@@ -67,17 +67,27 @@ The Model Context Protocol allows applications to provide context for LLMs in a
6767
6868## Installation
6969
70- We recommend using [ uv] ( https://docs.astral.sh/uv/ ) to manage your Python projects:
70+ ### Adding MCP to your python project
71+
72+ We recommend using [ uv] ( https://docs.astral.sh/uv/ ) to manage your Python projects. In a uv managed python project, add mcp to dependencies by:
7173
7274``` bash
7375uv add " mcp[cli]"
7476```
7577
76- Alternatively:
78+ Alternatively, for projects using pip for dependencies :
7779``` bash
7880pip install mcp
7981```
8082
83+ ### Running the standalone MCP development tools
84+
85+ To run the mcp command with uv:
86+
87+ ``` bash
88+ uv run mcp
89+ ```
90+
8191## Quickstart
8292
8393Let's create a simple MCP server that exposes a calculator tool and some data:
@@ -89,12 +99,14 @@ from mcp.server.fastmcp import FastMCP
8999# Create an MCP server
90100mcp = FastMCP(" Demo" )
91101
102+
92103# Add an addition tool
93104@mcp.tool ()
94105def add (a : int , b : int ) -> int :
95106 """ Add two numbers"""
96107 return a + b
97108
109+
98110# Add a dynamic greeting resource
99111@mcp.resource (" greeting://{name} " )
100112def get_greeting (name : str ) -> str :
@@ -129,34 +141,42 @@ The FastMCP server is your core interface to the MCP protocol. It handles connec
129141
130142``` python
131143# Add lifespan support for startup/shutdown with strong typing
144+ from contextlib import asynccontextmanager
132145from dataclasses import dataclass
133146from typing import AsyncIterator
134- 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
135151
136152# Create a named server
137153mcp = FastMCP(" My App" )
138154
139155# Specify dependencies for deployment and development
140156mcp = FastMCP(" My App" , dependencies = [" pandas" , " numpy" ])
141157
158+
142159@dataclass
143160class AppContext :
144- db: Database # Replace with your actual DB type
161+ db: Database
162+
145163
146164@asynccontextmanager
147165async def app_lifespan (server : FastMCP) -> AsyncIterator[AppContext]:
148166 """ Manage application lifecycle with type-safe context"""
167+ # Initialize on startup
168+ db = await Database.connect()
149169 try :
150- # Initialize on startup
151- await db.connect()
152170 yield AppContext(db = db)
153171 finally :
154172 # Cleanup on shutdown
155173 await db.disconnect()
156174
175+
157176# Pass lifespan to server
158177mcp = FastMCP(" My App" , lifespan = app_lifespan)
159178
179+
160180# Access type-safe lifespan context in tools
161181@mcp.tool ()
162182def query_db (ctx : Context) -> str :
@@ -170,11 +190,17 @@ def query_db(ctx: Context) -> str:
170190Resources 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:
171191
172192``` python
193+ from mcp.server.fastmcp import FastMCP
194+
195+ mcp = FastMCP(" My App" )
196+
197+
173198@mcp.resource (" config://app" )
174199def get_config () -> str :
175200 """ Static configuration data"""
176201 return " App configuration here"
177202
203+
178204@mcp.resource (" users://{user_id} /profile" )
179205def get_user_profile (user_id : str ) -> str :
180206 """ Dynamic user data"""
@@ -186,10 +212,17 @@ def get_user_profile(user_id: str) -> str:
186212Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
187213
188214``` python
215+ import httpx
216+ from mcp.server.fastmcp import FastMCP
217+
218+ mcp = FastMCP(" My App" )
219+
220+
189221@mcp.tool ()
190222def calculate_bmi (weight_kg : float , height_m : float ) -> float :
191223 """ Calculate BMI given weight in kg and height in meters"""
192- return weight_kg / (height_m ** 2 )
224+ return weight_kg / (height_m** 2 )
225+
193226
194227@mcp.tool ()
195228async def fetch_weather (city : str ) -> str :
@@ -204,16 +237,22 @@ async def fetch_weather(city: str) -> str:
204237Prompts are reusable templates that help LLMs interact with your server effectively:
205238
206239``` python
240+ from mcp.server.fastmcp import FastMCP, types
241+
242+ mcp = FastMCP(" My App" )
243+
244+
207245@mcp.prompt ()
208246def review_code (code : str ) -> str :
209247 return f " Please review this code: \n\n { code} "
210248
249+
211250@mcp.prompt ()
212- def debug_error (error : str ) -> list[Message]:
251+ def debug_error (error : str ) -> list[types. Message]:
213252 return [
214- UserMessage(" I'm seeing this error:" ),
215- UserMessage(error),
216- 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?" ),
217256 ]
218257```
219258
@@ -225,6 +264,9 @@ FastMCP provides an `Image` class that automatically handles image data:
225264from mcp.server.fastmcp import FastMCP, Image
226265from PIL import Image as PILImage
227266
267+ mcp = FastMCP(" My App" )
268+
269+
228270@mcp.tool ()
229271def create_thumbnail (image_path : str ) -> Image:
230272 """ Create a thumbnail from an image"""
@@ -240,6 +282,9 @@ The Context object gives your tools and resources access to MCP capabilities:
240282``` python
241283from mcp.server.fastmcp import FastMCP, Context
242284
285+ mcp = FastMCP(" My App" )
286+
287+
243288@mcp.tool ()
244289async def long_task (files : list[str ], ctx : Context) -> str :
245290 """ Process multiple files with progress tracking"""
@@ -312,16 +357,19 @@ from mcp.server.fastmcp import FastMCP
312357
313358mcp = FastMCP(" Echo" )
314359
360+
315361@mcp.resource (" echo://{message} " )
316362def echo_resource (message : str ) -> str :
317363 """ Echo a message as a resource"""
318364 return f " Resource echo: { message} "
319365
366+
320367@mcp.tool ()
321368def echo_tool (message : str ) -> str :
322369 """ Echo a message as a tool"""
323370 return f " Tool echo: { message} "
324371
372+
325373@mcp.prompt ()
326374def echo_prompt (message : str ) -> str :
327375 """ Create an echo prompt"""
@@ -333,20 +381,21 @@ def echo_prompt(message: str) -> str:
333381A more complex example showing database integration:
334382
335383``` python
336- from mcp.server.fastmcp import FastMCP
337384import sqlite3
338385
386+ from mcp.server.fastmcp import FastMCP
387+
339388mcp = FastMCP(" SQLite Explorer" )
340389
390+
341391@mcp.resource (" schema://main" )
342392def get_schema () -> str :
343393 """ Provide the database schema as a resource"""
344394 conn = sqlite3.connect(" database.db" )
345- schema = conn.execute(
346- " SELECT sql FROM sqlite_master WHERE type='table'"
347- ).fetchall()
395+ schema = conn.execute(" SELECT sql FROM sqlite_master WHERE type='table'" ).fetchall()
348396 return " \n " .join(sql[0 ] for sql in schema if sql[0 ])
349397
398+
350399@mcp.tool ()
351400def query_data (sql : str ) -> str :
352401 """ Execute SQL queries safely"""
@@ -368,20 +417,27 @@ For more control, you can use the low-level server implementation directly. This
368417from contextlib import asynccontextmanager
369418from typing import AsyncIterator
370419
420+ from fake_database import Database # Replace with your actual DB type
421+
422+ from mcp.server import Server
423+
424+
371425@asynccontextmanager
372426async def server_lifespan (server : Server) -> AsyncIterator[dict ]:
373427 """ Manage server startup and shutdown lifecycle."""
428+ # Initialize resources on startup
429+ db = await Database.connect()
374430 try :
375- # Initialize resources on startup
376- await db.connect()
377431 yield {" db" : db}
378432 finally :
379433 # Clean up on shutdown
380434 await db.disconnect()
381435
436+
382437# Pass lifespan to server
383438server = Server(" example-server" , lifespan = server_lifespan)
384439
440+
385441# Access lifespan context in handlers
386442@server.call_tool ()
387443async def query_db (name : str , arguments : dict ) -> list :
@@ -396,14 +452,15 @@ The lifespan API provides:
396452- Type-safe context passing between lifespan and request handlers
397453
398454``` python
399- from mcp.server.lowlevel import Server, NotificationOptions
400- from mcp.server.models import InitializationOptions
401455import mcp.server.stdio
402456import mcp.types as types
457+ from mcp.server.lowlevel import NotificationOptions, Server
458+ from mcp.server.models import InitializationOptions
403459
404460# Create a server instance
405461server = Server(" example-server" )
406462
463+
407464@server.list_prompts ()
408465async def handle_list_prompts () -> list[types.Prompt]:
409466 return [
@@ -412,18 +469,16 @@ async def handle_list_prompts() -> list[types.Prompt]:
412469 description = " An example prompt template" ,
413470 arguments = [
414471 types.PromptArgument(
415- name = " arg1" ,
416- description = " Example argument" ,
417- required = True
472+ name = " arg1" , description = " Example argument" , required = True
418473 )
419- ]
474+ ],
420475 )
421476 ]
422477
478+
423479@server.get_prompt ()
424480async def handle_get_prompt (
425- name : str ,
426- arguments : dict[str , str ] | None
481+ name : str , arguments : dict[str , str ] | None
427482) -> types.GetPromptResult:
428483 if name != " example-prompt" :
429484 raise ValueError (f " Unknown prompt: { name} " )
@@ -433,14 +488,12 @@ async def handle_get_prompt(
433488 messages = [
434489 types.PromptMessage(
435490 role = " user" ,
436- content = types.TextContent(
437- type = " text" ,
438- text = " Example prompt text"
439- )
491+ content = types.TextContent(type = " text" , text = " Example prompt text" ),
440492 )
441- ]
493+ ],
442494 )
443495
496+
444497async def run ():
445498 async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
446499 await server.run(
@@ -452,12 +505,14 @@ async def run():
452505 capabilities = server.get_capabilities(
453506 notification_options = NotificationOptions(),
454507 experimental_capabilities = {},
455- )
456- )
508+ ),
509+ ),
457510 )
458511
512+
459513if __name__ == " __main__" :
460514 import asyncio
515+
461516 asyncio.run(run())
462517```
463518
@@ -466,18 +521,22 @@ if __name__ == "__main__":
466521The SDK provides a high-level client interface for connecting to MCP servers:
467522
468523``` python
469- from mcp import ClientSession, StdioServerParameters
524+ from mcp import ClientSession, StdioServerParameters, types
470525from mcp.client.stdio import stdio_client
471526
472527# Create server parameters for stdio connection
473528server_params = StdioServerParameters(
474- command = " python" , # Executable
475- args = [" example_server.py" ], # Optional command line arguments
476- env = None # Optional environment variables
529+ command = " python" , # Executable
530+ args = [" example_server.py" ], # Optional command line arguments
531+ env = None , # Optional environment variables
477532)
478533
534+
535+
479536# Optional: create a sampling callback
480- async def handle_sampling_message (message : types.CreateMessageRequestParams) -> types.CreateMessageResult:
537+ async def handle_sampling_message (
538+ message : types.CreateMessageRequestParams,
539+ ) -> types.CreateMessageResult:
481540 return types.CreateMessageResult(
482541 role = " assistant" ,
483542 content = types.TextContent(
@@ -488,17 +547,23 @@ async def handle_sampling_message(message: types.CreateMessageRequestParams) ->
488547 stopReason = " endTurn" ,
489548 )
490549
550+
551+
491552async def run ():
492553 async with stdio_client(server_params) as (read, write):
493- async with ClientSession(read, write, sampling_callback = handle_sampling_message) as session:
554+ async with ClientSession(
555+ read, write, sampling_callback = handle_sampling_message
556+ ) as session:
494557 # Initialize the connection
495558 await session.initialize()
496559
497560 # List available prompts
498561 prompts = await session.list_prompts()
499562
500563 # Get a prompt
501- prompt = await session.get_prompt(" example-prompt" , arguments = {" arg1" : " value" })
564+ prompt = await session.get_prompt(
565+ " example-prompt" , arguments = {" arg1" : " value" }
566+ )
502567
503568 # List available resources
504569 resources = await session.list_resources()
@@ -512,8 +577,10 @@ async def run():
512577 # Call a tool
513578 result = await session.call_tool(" tool-name" , arguments = {" arg1" : " value" })
514579
580+
515581if __name__ == " __main__" :
516582 import asyncio
583+
517584 asyncio.run(run())
518585```
519586
0 commit comments