Files
awesome-copilot/skills/python-mcp-server-generator/SKILL.md
T
Burak Kaygusuz 3fb8ae0f2c docs(python-mcp): update skill, instructions, agent, and plugin for MCP Python SDK v2 🤖🤖🤖 (#4737)
* 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
2026-10-09 12:00:23 +11:00

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 a src/ package layout, and the tests cannot import server.py.
  • uv init makes the .gitignore file. 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:

  1. Replace divide with the tools that the user needs.
  2. 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.
  3. Return a Pydantic model or a TypedDict when the client needs machine-readable data. The SDK makes the output schema from the return type.
  4. Use async def for I/O. The SDK runs a sync tool on a worker thread.
  5. Mark a read-only tool with @mcp.tool(annotations=ToolAnnotations(read_only_hint=True)) (from mcp.types). Use destructive_hint=True for a destructive tool.
  6. 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 Client needs no subprocess and no port. It connects with the 2026-07-28 revision.
  • The mcp package installs anyio, which supplies the anyio marker. Do not add pytest-asyncio.

Step 4: Run the tests until they pass

  1. Run uv run pytest.
  2. If a test fails, read the error. Fix the server or the test.
  3. Run uv run pytest again.
  4. 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.py starts the stdio server. The server waits for a host on stdin and prints nothing.
  • uv run mcp dev server.py opens 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 FastMCP and mcp.server.fastmcp. Import the server class with from mcp.server import MCPServer.
  • Import Context, Image, Audio, Resolve, Elicit, ElicitationResult, AcceptedElicitation, Message, UserMessage, and AssistantMessage from mcp.server.mcpserver.
  • Give the server name as the first MCPServer argument. 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) to run(), not to MCPServer(...).
  • The SDK v2 types use snake_case fields, for example read_only_hint, structured_content, and is_error.
  • Raise ToolError for an error that the model must read. For other exceptions, the model gets only a generic error message.
  • Do not raise MCPError for a tool failure. The client gets a protocol error, not a tool result with is_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 the logging module.
  • Do not use ctx.elicit(). It fails on a 2026-07-28 connection. For user input during a tool call, annotate a parameter with Resolve(fn). Return Elicit(message, Model) from fn.
  • Send list change notifications with await ctx.notify_tools_changed(). A 2026-07-28 connection drops ctx.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 with ctx.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 standard logging module.
    • Roots (ctx.session.list_roots()): Get paths from tool parameters, resource URIs, or the server configuration.
  • The 2026-07-28 revision has no SSE transport. Use Streamable HTTP.