* 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
6.7 KiB
name, description, compatibility
| name | description | compatibility |
|---|---|---|
| python-mcp-server-generator | Use this skill when the user wants to create a new Model Context Protocol (MCP) server in Python. Also use it to expose Python functions, an API, or a database to AI clients as MCP tools. The skill makes a uv project with the MCP Python SDK v2 (MCPServer), typed tools, in-memory tests, and client configuration. Do not use it for an MCP client or for a server in another language. | Requires uv, Python 3.10 or later, and network access to install packages. The MCP Inspector also requires Node.js. |
Python MCP Server Generator
Create a Model Context Protocol (MCP) server in Python with the official MCP Python SDK v2. SDK v2 supports the MCP specification revision 2026-07-28. The same server also serves clients that use earlier revisions.
Workflow
Copy this checklist and track your progress:
- Step 1: Create the project.
- Step 2: Write the server.
- Step 3: Write the tests.
- Step 4: Run the tests until they pass.
- Step 5: Configure the client.
Step 1: Create the project
Run these commands:
uv init --app --no-package project-name
cd project-name
uv add "mcp[cli]>=2,<3"
uv add --dev pytest
Then delete main.py. The server goes in server.py.
- Without
--no-package, uv makes asrc/package layout, and the tests cannot importserver.py. uv initmakes the.gitignorefile. Do not write a different one.
Step 2: Write the server
Start from this template:
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
mcp = MCPServer("Demo", version="0.1.0")
@mcp.tool()
def divide(a: float, b: float) -> float:
"""Divide a by b."""
if b == 0:
raise ToolError("b must not be zero.")
return a / b
if __name__ == "__main__":
mcp.run() # stdio is the default transport
Then change the template for the request of the user:
- Replace
dividewith the tools that the user needs. - Give each tool full type hints and a docstring. The SDK makes the input schema from the type hints. The docstring becomes the tool description.
- Return a Pydantic model or a
TypedDictwhen the client needs machine-readable data. The SDK makes the output schema from the return type. - Use
async deffor I/O. The SDK runs a sync tool on a worker thread. - Mark a read-only tool with
@mcp.tool(annotations=ToolAnnotations(read_only_hint=True))(frommcp.types). Usedestructive_hint=Truefor a destructive tool. - Add resources (
@mcp.resource("users://{user_id}")) and prompts (@mcp.prompt()) only when the user asks for them.
Use stdio by default. Read references/streamable-http.md only when the user asks for a remote server or an HTTP server.
Step 3: Write the tests
Put test_server.py next to server.py. Write a test for each tool and for each ToolError:
import pytest
from mcp import Client
from server import mcp
@pytest.mark.anyio
async def test_divide():
async with Client(mcp) as client:
result = await client.call_tool("divide", {"a": 6, "b": 3})
assert result.structured_content == {"result": 2.0}
@pytest.mark.anyio
async def test_divide_by_zero():
async with Client(mcp) as client:
result = await client.call_tool("divide", {"a": 1, "b": 0})
assert result.is_error
assert "must not be zero" in result.content[0].text
- The in-memory
Clientneeds no subprocess and no port. It connects with the 2026-07-28 revision. - The
mcppackage installsanyio, which supplies theanyiomarker. Do not addpytest-asyncio.
Step 4: Run the tests until they pass
- Run
uv run pytest. - If a test fails, read the error. Fix the server or the test.
- Run
uv run pytestagain. - Continue only when all tests pass.
Step 5: Configure the client
For VS Code, write .vscode/mcp.json:
{
"servers": {
"demo": {
"type": "stdio",
"command": "uv",
"args": ["--directory", "/absolute/path/to/project", "run", "server.py"]
}
}
}
For Claude Desktop, run uv run mcp install server.py.
Tell the user these commands:
uv run server.pystarts the stdio server. The server waits for a host on stdin and prints nothing.uv run mcp dev server.pyopens the MCP Inspector. The Inspector needs Node.js.
Gotchas
SDK v2 and the 2026-07-28 revision changed many SDK v1 patterns. Do not copy SDK v1 examples.
- SDK v2 removed
FastMCPandmcp.server.fastmcp. Import the server class withfrom mcp.server import MCPServer. - Import
Context,Image,Audio,Resolve,Elicit,ElicitationResult,AcceptedElicitation,Message,UserMessage, andAssistantMessagefrommcp.server.mcpserver. - Give the server name as the first
MCPServerargument. Give all other constructor arguments as keyword arguments. Their positional order changed in SDK v2. - Set
version. If you do not set it, the server reports an empty version. - Give the transport options (
transport,host,port,json_response,stateless_http,transport_security) torun(), not toMCPServer(...). - The SDK v2 types use snake_case fields, for example
read_only_hint,structured_content, andis_error. - Raise
ToolErrorfor an error that the model must read. For other exceptions, the model gets only a generic error message. - 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. - In a stdio server, stdout is the protocol channel. Do not call
print(). Log to stderr with theloggingmodule. - Do not use
ctx.elicit(). It fails on a 2026-07-28 connection. For user input during a tool call, annotate a parameter withResolve(fn). ReturnElicit(message, Model)fromfn. - Send list change notifications with
await ctx.notify_tools_changed(). A 2026-07-28 connection dropsctx.session.send_tool_list_changed(). - For stdio and HTTP, the lifespan runs one time, when the server starts. Each in-memory
Client(mcp)in a test runs the lifespan again. Read its object withctx.request_context.lifespan_context. - The 2026-07-28 revision deprecates these features (SEP-2577). Do not add them to a new server:
- Sampling (
ctx.session.create_message()): Call the LLM provider API directly. - Protocol logging (
ctx.log(),ctx.info(), and similar methods): Use the standardloggingmodule. - Roots (
ctx.session.list_roots()): Get paths from tool parameters, resource URIs, or the server configuration.
- Sampling (
- The 2026-07-28 revision has no SSE transport. Use Streamable HTTP.