The Model Context Protocol (MCP) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
Expose data through Resources (think of these sort of like GET endpoints; they are used to load information into the LLM’s context)
Provide functionality through Tools (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
Define interaction patterns through Prompts (reusable templates for LLM interactions)
# Add lifespan support for startup/shutdown with strong typingfromcontextlibimportasynccontextmanagerfromcollections.abcimportAsyncIteratorfromdataclassesimportdataclassfromfake_databaseimportDatabase# Replace with your actual DB typefrommcp.server.fastmcpimportContext,FastMCP# Create a named servermcp=FastMCP("My App")# Specify dependencies for deployment and developmentmcp=FastMCP("My App",dependencies=["pandas","numpy"])@dataclassclassAppContext:db:Database@asynccontextmanagerasyncdefapp_lifespan(server:FastMCP)->AsyncIterator[AppContext]:"""Manage application lifecycle with type-safe context"""# Initialize on startupdb=awaitDatabase.connect()try:yieldAppContext(db=db)finally:# Cleanup on shutdownawaitdb.disconnect()# Pass lifespan to servermcp=FastMCP("My App",lifespan=app_lifespan)# Access type-safe lifespan context in tools@mcp.tool()defquery_db(ctx:Context)->str:"""Tool that uses initialized resources"""db=ctx.request_context.lifespan_context["db"]returndb.query()
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
frommcp.server.fastmcpimportFastMCPmcp=FastMCP("My App")@mcp.resource("config://app")defget_config()->str:"""Static configuration data"""return"App configuration here"@mcp.resource("users://{user_id}/profile")defget_user_profile(user_id:str)->str:"""Dynamic user data"""returnf"Profile data for user {user_id}"
Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
importhttpxfrommcp.server.fastmcpimportFastMCPmcp=FastMCP("My App")@mcp.tool()defcalculate_bmi(weight_kg:float,height_m:float)->float:"""Calculate BMI given weight in kg and height in meters"""returnweight_kg/(height_m**2)@mcp.tool()asyncdeffetch_weather(city:str)->str:"""Fetch current weather for a city"""asyncwithhttpx.AsyncClient()asclient:response=awaitclient.get(f"https://api.weather.com/{city}")returnresponse.text
Prompts are reusable templates that help LLMs interact with your server effectively:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
frommcp.server.fastmcpimportFastMCPfrommcp.server.fastmcp.promptsimportbasemcp=FastMCP("My App")@mcp.prompt()defreview_code(code:str)->str:returnf"Please review this code:\n\n{code}"@mcp.prompt()defdebug_error(error:str)->list[base.Message]:return[base.UserMessage("I'm seeing this error:"),base.UserMessage(error),base.AssistantMessage("I'll help debug that. What have you tried so far?"),]
FastMCP provides an Image class that automatically handles image data:
1
2
3
4
5
6
7
8
9
10
11
12
frommcp.server.fastmcpimportFastMCP,ImagefromPILimportImageasPILImagemcp=FastMCP("My App")@mcp.tool()defcreate_thumbnail(image_path:str)->Image:"""Create a thumbnail from an image"""img=PILImage.open(image_path)img.thumbnail((100,100))returnImage(data=img.tobytes(),format="png")