mirror of
https://github.com/github/awesome-copilot.git
synced 2026-10-13 15:24:25 +00:00
* docs(python-mcp-server-generator): rewrite skill for MCP Python SDK v2 - Target MCP Python SDK v2 (MCPServer) and MCP specification 2026-07-28 - Replace FastMCP imports with `from mcp.server import MCPServer` - Rewrite the description with explicit triggers and exclusions - Add the compatibility field (uv, Python 3.10+, npx for the Inspector) - Add a five-step workflow checklist: create, write, test, validate, configure - Pin the SDK with `uv add "mcp[cli]>=2,<3"` and use a flat uv app layout - Add server and in-memory Client test templates - Add a test-fix-retest validation loop with `uv run pytest` - Add a Gotchas section for SDK v1 to v2 changes and deprecated features - Move Streamable HTTP guidance to references/streamable-http.md - Remove generic guidance that the model already knows * docs(python-mcp-server): update instructions for MCP Python SDK v2 - Target MCP Python SDK v2 (MCPServer) and MCP specification 2026-07-28 - Replace FastMCP imports with MCPServer and mcp.server.mcpserver imports - Move transport options from the constructor to run() and streamable_http_app() - Use Resolve and Elicit for user input instead of ctx.elicit() - Use ctx.notify_tools_changed() for list change notifications - Explain ToolError and MCPError behavior for tool failures - Mark sampling, protocol logging, and roots as deprecated - Add transport security, CORS, and multi-worker guidance - Add an in-memory Client test pattern - Rewrite the text in ASD-STE100 Simplified Technical English - Use single-quoted frontmatter values * docs(python-mcp-expert): update agent for MCP Python SDK v2 - Target MCP Python SDK v2 (MCPServer) and MCP specification 2026-07-28 - Replace the retired GPT-4.1 model with GPT-6 Sol - Replace FastMCP guidance with MCPServer guidance - Add guidance for transport options, Resolve and Elicit, and notify_tools_changed() - Explain ToolError and MCPError behavior for tool failures - Mark sampling, protocol logging, and roots as deprecated - Add SDK v1 to v2 migration as a supported scenario - Rewrite the text in ASD-STE100 Simplified Technical English - Use single-quoted frontmatter values * chore(python-mcp-development): update plugin metadata for MCP Python SDK v2 - Remove FastMCP from the plugin description and README - Describe the actual plugin contents: one skill and one agent - Replace the fastmcp keyword with mcp-python-sdk - Bump the plugin version to 2.0.0 - Regenerate marketplace.json and docs/README.plugins.md * fix(python-mcp-server-generator): correct transport security, lifespan, and SSE notes * fix(python-mcp-server): remove invalid structured_output advice and align with skill * fix(python-mcp-expert): add missing SDK v2 rules and remove repeated guidance * fix(python-mcp-expert): remove the retired model from frontmatter * fix(python-mcp-server-generator): state Node.js as the MCP Inspector prerequisite
9.7 KiB
9.7 KiB
description, applyTo
| description | applyTo |
|---|---|
| Instructions for building Model Context Protocol (MCP) servers using the Python SDK v2 | **/*.py, **/pyproject.toml, **/requirements.txt |
Python MCP Server Development
These instructions apply to the MCP Python SDK v2 and the MCP specification revision 2026-07-28.
Instructions
- Use uv for project management:
uv init --app --no-package mcp-server-demoanduv add "mcp[cli]>=2,<3". - Import the server class from
mcp.server:from mcp.server import MCPServer. - Do not use
FastMCPormcp.server.fastmcp. SDK v2 removed this import path. - Import
Context,Image,Audio,Resolve,Elicit, and the prompt message classes frommcp.server.mcpserver. - Use the
@mcp.tool(),@mcp.resource(), and@mcp.prompt()decorators for registration. - Add type hints to all parameters. The SDK makes the schemas from the type hints and validates the arguments.
- Use Pydantic models, TypedDicts, or dataclasses for structured output.
- A tool returns structured output when its return type is compatible.
- For stdio transport, use
mcp.run()ormcp.run(transport="stdio"). - For HTTP servers, use
mcp.run(transport="streamable-http"), or mountmcp.streamable_http_app()in Starlette or FastAPI. - Give transport options (
host,port,json_response,stateless_http,transport_security) torun(), not toMCPServer(...). - When you mount the server, give the same options to
streamable_http_app(). This method has noportoption. - Give the server name as the first
MCPServerargument. Give all other constructor arguments as keyword arguments. - Set
versiononMCPServer. If you do not set it, the server reports an empty version. - The SDK v2 types use snake_case fields, for example
read_only_hint,structured_content, andis_error. - Add a
ctx: Contextparameter to a tool, resource, or prompt to get the request context. - Report progress with
await ctx.report_progress(progress, total, message). - Get user input with a
Resolve(fn)parameter. ReturnElicit(message, Model)fromfn. This works with clients of all protocol revisions. - Do not use
ctx.elicit(). It fails on a 2026-07-28 connection. - Log with the standard
loggingmodule. Protocol logging (ctx.info()and similar methods) is deprecated. - Do not use sampling (
ctx.session.create_message()). It is deprecated. Call the LLM provider API directly. - Do not use roots (
ctx.session.list_roots()). It is deprecated. Get paths from tool parameters or the server configuration. - Configure icons with
Icon(src="https://...", mime_type="image/png")frommcp.types. Server, tools, resources, and prompts accepticons=[...]. - Use the
Imageclass to return images:return Image(data=png_bytes, format="png")orreturn Image(path=file_path). - Define resource templates with RFC 6570 URI templates:
@mcp.resource("greeting://{name}"). - Add completion support with the
@mcp.completion()decorator. - Use a lifespan context manager for startup and shutdown of shared resources. For stdio and HTTP, the lifespan runs one time for the server. Each in-memory
Client(mcp)runs it again. - Get the lifespan object in tools from
ctx.request_context.lifespan_context. UseContext[AppContext]for a typed result. - Raise
ToolError(frommcp.server.mcpserver.exceptions) for an error that the model must read. - Do not raise
MCPErrorfor a tool failure. The client gets a protocol error, not a tool result withis_error. Many hosts do not show this error to the model. - Send list change notifications with
await ctx.notify_tools_changed(). Do not usectx.session.send_tool_list_changed(). A 2026-07-28 connection drops it. - On the 2026-07-28 revision, Streamable HTTP requests have no session.
stateless_http=Truechanges only how the server serves clients of earlier revisions. json_response=Truesends one JSON body for each request. In this mode, the server cannot send progress notifications during the request.- Test servers with
uv run mcp dev server.py(MCP Inspector) oruv run mcp install server.py(Claude Desktop). - Write tests with the in-memory client:
async with Client(mcp) as client:(from mcp import Client). - Mount more than one server in Starlette with different paths:
Mount("/path", app=mcp.streamable_http_app()). The lifespan of the host app must entermcp.session_manager.run()for each server. - For browser clients, configure CORS. Allow the
Mcp-*request headers. Expose theMcp-Session-Idresponse header. - If you do not set
transport_security, the SDK checks theHostandOriginheaders only when the host is127.0.0.1,localhost, or::1. For all other hosts, settransport_security=TransportSecuritySettings(allowed_hosts=[...], allowed_origins=[...])(frommcp.server.transport_security). - For more than one worker, set
request_state_security=RequestStateSecurity(keys=[...])onMCPServer. - Use the low-level
Serverclass (frommcp.server) only whenMCPServerdoes not give sufficient control.
Best Practices
- Give each tool function one responsibility.
- Write clear docstrings. They become the tool descriptions.
- Use descriptive parameter names with type hints.
- Validate inputs with Pydantic
Fielddescriptions. - Use async functions for I/O-bound operations. The SDK runs sync functions on a worker thread.
- Log to stderr. Do not use
print()in a stdio server, because stdout is the protocol channel. - Use environment variables for configuration.
- Be careful when a tool gives access to the file system or the network.
Common Patterns
Basic Server Setup (stdio)
from mcp.server import MCPServer
mcp = MCPServer("My Server", version="0.1.0")
@mcp.tool()
def calculate(a: int, b: int, op: str) -> int:
"""Perform calculation"""
if op == "add":
return a + b
return a - b
if __name__ == "__main__":
mcp.run() # stdio by default
HTTP Server
from mcp.server import MCPServer
mcp = MCPServer("My HTTP Server", version="0.1.0")
@mcp.tool()
def hello(name: str = "World") -> str:
"""Greet someone"""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="127.0.0.1", port=8000)
Tool with Structured Output
from pydantic import BaseModel, Field
class WeatherData(BaseModel):
temperature: float = Field(description="Temperature in Celsius")
condition: str
humidity: float
@mcp.tool()
def get_weather(city: str) -> WeatherData:
"""Get weather for a city"""
return WeatherData(
temperature=22.5,
condition="sunny",
humidity=65.0
)
Dynamic Resource
@mcp.resource("users://{user_id}")
def get_user(user_id: str) -> str:
"""Get user profile data"""
return f"User {user_id} profile data"
Tool with Context
import logging
from mcp.server.mcpserver import Context
logger = logging.getLogger(__name__)
@mcp.tool()
async def process_data(data: str, ctx: Context) -> str:
"""Process data with progress reports"""
logger.info("Processing: %s", data)
await ctx.report_progress(0.5, 1.0, "Halfway done")
return f"Processed: {data}"
Tool with User Input
from typing import Annotated
from pydantic import BaseModel
from mcp.server.mcpserver import AcceptedElicitation, Elicit, ElicitationResult, Resolve
class Quantity(BaseModel):
copies: int
async def ask_quantity() -> Elicit[Quantity]:
"""Ask the user how many copies to reserve."""
return Elicit("How many copies?", Quantity)
@mcp.tool()
async def reserve(
title: str,
quantity: Annotated[ElicitationResult[Quantity], Resolve(ask_quantity)],
) -> str:
"""Reserve copies of a book"""
if isinstance(quantity, AcceptedElicitation):
return f"Reserved {quantity.data.copies} of {title!r}."
return "Nothing reserved."
Lifespan Management
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from dataclasses import dataclass
from mcp.server import MCPServer
from mcp.server.mcpserver import Context
@dataclass
class AppContext:
db: Database # Your database client
@asynccontextmanager
async def app_lifespan(server: MCPServer) -> AsyncIterator[AppContext]:
db = await Database.connect()
try:
yield AppContext(db=db)
finally:
await db.disconnect()
mcp = MCPServer("My App", version="0.1.0", lifespan=app_lifespan)
@mcp.tool()
def query(sql: str, ctx: Context[AppContext]) -> str:
"""Query database"""
db = ctx.request_context.lifespan_context.db
return db.execute(sql)
Prompt with Messages
from mcp.server.mcpserver import AssistantMessage, Message, UserMessage
@mcp.prompt(title="Code Review")
def review_code(code: str) -> list[Message]:
"""Create code review prompt"""
return [
UserMessage("Review this code:"),
UserMessage(code),
AssistantMessage("I'll review the code for you.")
]
Error Handling
from mcp.server.mcpserver.exceptions import ToolError
@mcp.tool()
async def risky_operation(input: str) -> str:
"""Operation that might fail"""
if not input:
raise ToolError("Input must not be empty.")
result = await perform_operation(input) # Your operation
return f"Success: {result}"
In-Memory Test
import pytest
from mcp import Client
from server import mcp # The HTTP Server example
@pytest.mark.anyio
async def test_hello():
async with Client(mcp) as client:
result = await client.call_tool("hello", {"name": "MCP"})
assert result.structured_content == {"result": "Hello, MCP!"}