@@ -99,12 +99,14 @@ from mcp.server.fastmcp import FastMCP
99
99
# Create an MCP server
100
100
mcp = FastMCP(" Demo" )
101
101
102
+
102
103
# Add an addition tool
103
104
@mcp.tool ()
104
105
def add (a : int , b : int ) -> int :
105
106
""" Add two numbers"""
106
107
return a + b
107
108
109
+
108
110
# Add a dynamic greeting resource
109
111
@mcp.resource (" greeting://{name} " )
110
112
def get_greeting (name : str ) -> str :
@@ -139,34 +141,42 @@ The FastMCP server is your core interface to the MCP protocol. It handles connec
139
141
140
142
``` python
141
143
# Add lifespan support for startup/shutdown with strong typing
144
+ from contextlib import asynccontextmanager
142
145
from dataclasses import dataclass
143
146
from typing import AsyncIterator
144
- from mcp.server.fastmcp import FastMCP
147
+
148
+ from fake_database import Database # Replace with your actual DB type
149
+
150
+ from mcp.server.fastmcp import Context, FastMCP
145
151
146
152
# Create a named server
147
153
mcp = FastMCP(" My App" )
148
154
149
155
# Specify dependencies for deployment and development
150
156
mcp = FastMCP(" My App" , dependencies = [" pandas" , " numpy" ])
151
157
158
+
152
159
@dataclass
153
160
class AppContext :
154
- db: Database # Replace with your actual DB type
161
+ db: Database
162
+
155
163
156
164
@asynccontextmanager
157
165
async def app_lifespan (server : FastMCP) -> AsyncIterator[AppContext]:
158
166
""" Manage application lifecycle with type-safe context"""
167
+ # Initialize on startup
168
+ db = await Database.connect()
159
169
try :
160
- # Initialize on startup
161
- await db.connect()
162
170
yield AppContext(db = db)
163
171
finally :
164
172
# Cleanup on shutdown
165
173
await db.disconnect()
166
174
175
+
167
176
# Pass lifespan to server
168
177
mcp = FastMCP(" My App" , lifespan = app_lifespan)
169
178
179
+
170
180
# Access type-safe lifespan context in tools
171
181
@mcp.tool ()
172
182
def query_db (ctx : Context) -> str :
@@ -180,11 +190,17 @@ def query_db(ctx: Context) -> str:
180
190
Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:
181
191
182
192
``` python
193
+ from mcp.server.fastmcp import FastMCP
194
+
195
+ mcp = FastMCP(" My App" )
196
+
197
+
183
198
@mcp.resource (" config://app" )
184
199
def get_config () -> str :
185
200
""" Static configuration data"""
186
201
return " App configuration here"
187
202
203
+
188
204
@mcp.resource (" users://{user_id} /profile" )
189
205
def get_user_profile (user_id : str ) -> str :
190
206
""" Dynamic user data"""
@@ -196,10 +212,17 @@ def get_user_profile(user_id: str) -> str:
196
212
Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
197
213
198
214
``` python
215
+ import httpx
216
+ from mcp.server.fastmcp import FastMCP
217
+
218
+ mcp = FastMCP(" My App" )
219
+
220
+
199
221
@mcp.tool ()
200
222
def calculate_bmi (weight_kg : float , height_m : float ) -> float :
201
223
""" Calculate BMI given weight in kg and height in meters"""
202
- return weight_kg / (height_m ** 2 )
224
+ return weight_kg / (height_m** 2 )
225
+
203
226
204
227
@mcp.tool ()
205
228
async def fetch_weather (city : str ) -> str :
@@ -214,16 +237,22 @@ async def fetch_weather(city: str) -> str:
214
237
Prompts are reusable templates that help LLMs interact with your server effectively:
215
238
216
239
``` python
240
+ from mcp.server.fastmcp import FastMCP, types
241
+
242
+ mcp = FastMCP(" My App" )
243
+
244
+
217
245
@mcp.prompt ()
218
246
def review_code (code : str ) -> str :
219
247
return f " Please review this code: \n\n { code} "
220
248
249
+
221
250
@mcp.prompt ()
222
- def debug_error (error : str ) -> list[Message]:
251
+ def debug_error (error : str ) -> list[types. Message]:
223
252
return [
224
- UserMessage(" I'm seeing this error:" ),
225
- UserMessage(error),
226
- AssistantMessage(" I'll help debug that. What have you tried so far?" )
253
+ types. UserMessage(" I'm seeing this error:" ),
254
+ types. UserMessage(error),
255
+ types. AssistantMessage(" I'll help debug that. What have you tried so far?" ),
227
256
]
228
257
```
229
258
@@ -235,6 +264,9 @@ FastMCP provides an `Image` class that automatically handles image data:
235
264
from mcp.server.fastmcp import FastMCP, Image
236
265
from PIL import Image as PILImage
237
266
267
+ mcp = FastMCP(" My App" )
268
+
269
+
238
270
@mcp.tool ()
239
271
def create_thumbnail (image_path : str ) -> Image:
240
272
""" Create a thumbnail from an image"""
@@ -250,6 +282,9 @@ The Context object gives your tools and resources access to MCP capabilities:
250
282
``` python
251
283
from mcp.server.fastmcp import FastMCP, Context
252
284
285
+ mcp = FastMCP(" My App" )
286
+
287
+
253
288
@mcp.tool ()
254
289
async def long_task (files : list[str ], ctx : Context) -> str :
255
290
""" Process multiple files with progress tracking"""
@@ -322,16 +357,19 @@ from mcp.server.fastmcp import FastMCP
322
357
323
358
mcp = FastMCP(" Echo" )
324
359
360
+
325
361
@mcp.resource (" echo://{message} " )
326
362
def echo_resource (message : str ) -> str :
327
363
""" Echo a message as a resource"""
328
364
return f " Resource echo: { message} "
329
365
366
+
330
367
@mcp.tool ()
331
368
def echo_tool (message : str ) -> str :
332
369
""" Echo a message as a tool"""
333
370
return f " Tool echo: { message} "
334
371
372
+
335
373
@mcp.prompt ()
336
374
def echo_prompt (message : str ) -> str :
337
375
""" Create an echo prompt"""
@@ -343,20 +381,21 @@ def echo_prompt(message: str) -> str:
343
381
A more complex example showing database integration:
344
382
345
383
``` python
346
- from mcp.server.fastmcp import FastMCP
347
384
import sqlite3
348
385
386
+ from mcp.server.fastmcp import FastMCP
387
+
349
388
mcp = FastMCP(" SQLite Explorer" )
350
389
390
+
351
391
@mcp.resource (" schema://main" )
352
392
def get_schema () -> str :
353
393
""" Provide the database schema as a resource"""
354
394
conn = sqlite3.connect(" database.db" )
355
- schema = conn.execute(
356
- " SELECT sql FROM sqlite_master WHERE type='table'"
357
- ).fetchall()
395
+ schema = conn.execute(" SELECT sql FROM sqlite_master WHERE type='table'" ).fetchall()
358
396
return " \n " .join(sql[0 ] for sql in schema if sql[0 ])
359
397
398
+
360
399
@mcp.tool ()
361
400
def query_data (sql : str ) -> str :
362
401
""" Execute SQL queries safely"""
@@ -378,20 +417,27 @@ For more control, you can use the low-level server implementation directly. This
378
417
from contextlib import asynccontextmanager
379
418
from typing import AsyncIterator
380
419
420
+ from fake_database import Database # Replace with your actual DB type
421
+
422
+ from mcp.server import Server
423
+
424
+
381
425
@asynccontextmanager
382
426
async def server_lifespan (server : Server) -> AsyncIterator[dict ]:
383
427
""" Manage server startup and shutdown lifecycle."""
428
+ # Initialize resources on startup
429
+ db = await Database.connect()
384
430
try :
385
- # Initialize resources on startup
386
- await db.connect()
387
431
yield {" db" : db}
388
432
finally :
389
433
# Clean up on shutdown
390
434
await db.disconnect()
391
435
436
+
392
437
# Pass lifespan to server
393
438
server = Server(" example-server" , lifespan = server_lifespan)
394
439
440
+
395
441
# Access lifespan context in handlers
396
442
@server.call_tool ()
397
443
async def query_db (name : str , arguments : dict ) -> list :
@@ -406,14 +452,15 @@ The lifespan API provides:
406
452
- Type-safe context passing between lifespan and request handlers
407
453
408
454
``` python
409
- from mcp.server.lowlevel import Server, NotificationOptions
410
- from mcp.server.models import InitializationOptions
411
455
import mcp.server.stdio
412
456
import mcp.types as types
457
+ from mcp.server.lowlevel import NotificationOptions, Server
458
+ from mcp.server.models import InitializationOptions
413
459
414
460
# Create a server instance
415
461
server = Server(" example-server" )
416
462
463
+
417
464
@server.list_prompts ()
418
465
async def handle_list_prompts () -> list[types.Prompt]:
419
466
return [
@@ -422,18 +469,16 @@ async def handle_list_prompts() -> list[types.Prompt]:
422
469
description = " An example prompt template" ,
423
470
arguments = [
424
471
types.PromptArgument(
425
- name = " arg1" ,
426
- description = " Example argument" ,
427
- required = True
472
+ name = " arg1" , description = " Example argument" , required = True
428
473
)
429
- ]
474
+ ],
430
475
)
431
476
]
432
477
478
+
433
479
@server.get_prompt ()
434
480
async def handle_get_prompt (
435
- name : str ,
436
- arguments : dict[str , str ] | None
481
+ name : str , arguments : dict[str , str ] | None
437
482
) -> types.GetPromptResult:
438
483
if name != " example-prompt" :
439
484
raise ValueError (f " Unknown prompt: { name} " )
@@ -443,14 +488,12 @@ async def handle_get_prompt(
443
488
messages = [
444
489
types.PromptMessage(
445
490
role = " user" ,
446
- content = types.TextContent(
447
- type = " text" ,
448
- text = " Example prompt text"
449
- )
491
+ content = types.TextContent(type = " text" , text = " Example prompt text" ),
450
492
)
451
- ]
493
+ ],
452
494
)
453
495
496
+
454
497
async def run ():
455
498
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
456
499
await server.run(
@@ -462,12 +505,14 @@ async def run():
462
505
capabilities = server.get_capabilities(
463
506
notification_options = NotificationOptions(),
464
507
experimental_capabilities = {},
465
- )
466
- )
508
+ ),
509
+ ),
467
510
)
468
511
512
+
469
513
if __name__ == " __main__" :
470
514
import asyncio
515
+
471
516
asyncio.run(run())
472
517
```
473
518
@@ -476,18 +521,21 @@ if __name__ == "__main__":
476
521
The SDK provides a high-level client interface for connecting to MCP servers:
477
522
478
523
``` python
479
- from mcp import ClientSession, StdioServerParameters
524
+ from mcp import ClientSession, StdioServerParameters, types
480
525
from mcp.client.stdio import stdio_client
481
526
482
527
# Create server parameters for stdio connection
483
528
server_params = StdioServerParameters(
484
- command = " python" , # Executable
485
- args = [" example_server.py" ], # Optional command line arguments
486
- env = None # Optional environment variables
529
+ command = " python" , # Executable
530
+ args = [" example_server.py" ], # Optional command line arguments
531
+ env = None , # Optional environment variables
487
532
)
488
533
534
+
489
535
# Optional: create a sampling callback
490
- async def handle_sampling_message (message : types.CreateMessageRequestParams) -> types.CreateMessageResult:
536
+ async def handle_sampling_message (
537
+ message : types.CreateMessageRequestParams,
538
+ ) -> types.CreateMessageResult:
491
539
return types.CreateMessageResult(
492
540
role = " assistant" ,
493
541
content = types.TextContent(
@@ -498,17 +546,22 @@ async def handle_sampling_message(message: types.CreateMessageRequestParams) ->
498
546
stopReason = " endTurn" ,
499
547
)
500
548
549
+
501
550
async def run ():
502
551
async with stdio_client(server_params) as (read, write):
503
- async with ClientSession(read, write, sampling_callback = handle_sampling_message) as session:
552
+ async with ClientSession(
553
+ read, write, sampling_callback = handle_sampling_message
554
+ ) as session:
504
555
# Initialize the connection
505
556
await session.initialize()
506
557
507
558
# List available prompts
508
559
prompts = await session.list_prompts()
509
560
510
561
# Get a prompt
511
- prompt = await session.get_prompt(" example-prompt" , arguments = {" arg1" : " value" })
562
+ prompt = await session.get_prompt(
563
+ " example-prompt" , arguments = {" arg1" : " value" }
564
+ )
512
565
513
566
# List available resources
514
567
resources = await session.list_resources()
@@ -522,8 +575,10 @@ async def run():
522
575
# Call a tool
523
576
result = await session.call_tool(" tool-name" , arguments = {" arg1" : " value" })
524
577
578
+
525
579
if __name__ == " __main__" :
526
580
import asyncio
581
+
527
582
asyncio.run(run())
528
583
```
529
584
0 commit comments