mirror of
https://github.com/github/awesome-copilot.git
synced 2026-10-13 07:14:24 +00:00
chore: publish from main
This commit is contained in:
@@ -1,105 +1,151 @@
|
||||
---
|
||||
name: python-mcp-server-generator
|
||||
description: 'Generate a complete MCP server project in Python with tools, resources, and proper configuration'
|
||||
description: '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.'
|
||||
compatibility: 'Requires uv, Python 3.10 or later, and network access to install packages. The MCP Inspector also requires Node.js.'
|
||||
---
|
||||
|
||||
# Generate Python MCP Server
|
||||
# Python MCP Server Generator
|
||||
|
||||
Create a complete Model Context Protocol (MCP) server in Python with the following specifications:
|
||||
Create a Model Context Protocol (MCP) server in Python with the official [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) v2. SDK v2 supports the MCP specification revision [2026-07-28](https://modelcontextprotocol.io/specification/2026-07-28). The same server also serves clients that use earlier revisions.
|
||||
|
||||
## Requirements
|
||||
## Workflow
|
||||
|
||||
1. **Project Structure**: Create a new Python project with proper structure using uv
|
||||
2. **Dependencies**: Include mcp[cli] package with uv
|
||||
3. **Transport Type**: Choose between stdio (for local) or streamable-http (for remote)
|
||||
4. **Tools**: Create at least one useful tool with proper type hints
|
||||
5. **Error Handling**: Include comprehensive error handling and validation
|
||||
Copy this checklist and track your progress:
|
||||
|
||||
## Implementation Details
|
||||
- [ ] 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.
|
||||
|
||||
### Project Setup
|
||||
- Initialize with `uv init project-name`
|
||||
- Add MCP SDK: `uv add "mcp[cli]"`
|
||||
- Create main server file (e.g., `server.py`)
|
||||
- Add `.gitignore` for Python projects
|
||||
- Configure for direct execution with `if __name__ == "__main__"`
|
||||
### Step 1: Create the project
|
||||
|
||||
### Server Configuration
|
||||
- Use `FastMCP` class from `mcp.server.fastmcp`
|
||||
- Set server name and optional instructions
|
||||
- Choose transport: stdio (default) or streamable-http
|
||||
- For HTTP: optionally configure host, port, and stateless mode
|
||||
Run these commands:
|
||||
|
||||
### Tool Implementation
|
||||
- Use `@mcp.tool()` decorator on functions
|
||||
- Always include type hints - they generate schemas automatically
|
||||
- Write clear docstrings - they become tool descriptions
|
||||
- Use Pydantic models or TypedDicts for structured outputs
|
||||
- Support async operations for I/O-bound tasks
|
||||
- Include proper error handling
|
||||
```bash
|
||||
uv init --app --no-package project-name
|
||||
cd project-name
|
||||
uv add "mcp[cli]>=2,<3"
|
||||
uv add --dev pytest
|
||||
```
|
||||
|
||||
### Resource/Prompt Setup (Optional)
|
||||
- Add resources with `@mcp.resource()` decorator
|
||||
- Use URI templates for dynamic resources: `"resource://{param}"`
|
||||
- Add prompts with `@mcp.prompt()` decorator
|
||||
- Return strings or Message lists from prompts
|
||||
Then delete `main.py`. The server goes in `server.py`.
|
||||
|
||||
### Code Quality
|
||||
- Use type hints for all function parameters and returns
|
||||
- Write docstrings for tools, resources, and prompts
|
||||
- Follow PEP 8 style guidelines
|
||||
- Use async/await for asynchronous operations
|
||||
- Implement context managers for resource cleanup
|
||||
- Add inline comments for complex logic
|
||||
- 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.
|
||||
|
||||
## Example Tool Types to Consider
|
||||
- Data processing and transformation
|
||||
- File system operations (read, analyze, search)
|
||||
- External API integrations
|
||||
- Database queries
|
||||
- Text analysis or generation (with sampling)
|
||||
- System information retrieval
|
||||
- Math or scientific calculations
|
||||
### Step 2: Write the server
|
||||
|
||||
## Configuration Options
|
||||
- **For stdio Servers**:
|
||||
- Simple direct execution
|
||||
- Test with `uv run mcp dev server.py`
|
||||
- Install to Claude: `uv run mcp install server.py`
|
||||
|
||||
- **For HTTP Servers**:
|
||||
- Port configuration via environment variables
|
||||
- Stateless mode for scalability: `stateless_http=True`
|
||||
- JSON response mode: `json_response=True`
|
||||
- CORS configuration for browser clients
|
||||
- Mounting to existing ASGI servers (Starlette/FastAPI)
|
||||
Start from this template:
|
||||
|
||||
## Testing Guidance
|
||||
- Explain how to run the server:
|
||||
- stdio: `python server.py` or `uv run server.py`
|
||||
- HTTP: `python server.py` then connect to `http://localhost:PORT/mcp`
|
||||
- Test with MCP Inspector: `uv run mcp dev server.py`
|
||||
- Install to Claude Desktop: `uv run mcp install server.py`
|
||||
- Include example tool invocations
|
||||
- Add troubleshooting tips
|
||||
```python
|
||||
from mcp.server import MCPServer
|
||||
from mcp.server.mcpserver.exceptions import ToolError
|
||||
|
||||
## Additional Features to Consider
|
||||
- Context usage for logging, progress, and notifications
|
||||
- LLM sampling for AI-powered tools
|
||||
- User input elicitation for interactive workflows
|
||||
- Lifespan management for shared resources (databases, connections)
|
||||
- Structured output with Pydantic models
|
||||
- Icons for UI display
|
||||
- Image handling with Image class
|
||||
- Completion support for better UX
|
||||
mcp = MCPServer("Demo", version="0.1.0")
|
||||
|
||||
## Best Practices
|
||||
- Use type hints everywhere - they're not optional
|
||||
- Return structured data when possible
|
||||
- Log to stderr (or use Context logging) to avoid stdout pollution
|
||||
- Clean up resources properly
|
||||
- Validate inputs early
|
||||
- Provide clear error messages
|
||||
- Test tools independently before LLM integration
|
||||
|
||||
Generate a complete, production-ready MCP server with type safety, proper error handling, and comprehensive documentation.
|
||||
@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](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`:
|
||||
|
||||
```python
|
||||
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`:
|
||||
|
||||
```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](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/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.
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Streamable HTTP Servers
|
||||
|
||||
Use this transport only for a remote server. A local server uses stdio.
|
||||
|
||||
## Run the server
|
||||
|
||||
Replace `mcp.run()` in `server.py`:
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(
|
||||
transport="streamable-http",
|
||||
host=os.environ.get("HOST", "127.0.0.1"),
|
||||
port=int(os.environ.get("PORT", "8000")),
|
||||
)
|
||||
```
|
||||
|
||||
Clients connect to `http://HOST:PORT/mcp`. For VS Code, write `.vscode/mcp.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"demo": {
|
||||
"type": "http",
|
||||
"url": "http://localhost:8000/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Mount the server in an ASGI app
|
||||
|
||||
1. Use `mcp.streamable_http_app()` to mount the server in a Starlette or FastAPI app.
|
||||
2. Give the transport options to `streamable_http_app()`. This method has no `port` option.
|
||||
3. Make the lifespan of the host app enter `mcp.session_manager.run()`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Requests on the 2026-07-28 revision have no session. Any replica can answer a request.
|
||||
- `stateless_http=True` changes only how the server serves clients of earlier revisions.
|
||||
- `json_response=True` sends one JSON body for each request. In this mode, the server cannot send progress notifications during the request.
|
||||
- If you do not set `transport_security`, the SDK checks the `Host` and `Origin` headers only when the host is `127.0.0.1`, `localhost`, or `::1`. For all other hosts, for example `0.0.0.0` or a public host name, the server does no check. For these hosts, give `transport_security=TransportSecuritySettings(allowed_hosts=[...], allowed_origins=[...])` (from `mcp.server.transport_security`) to `run()` or `streamable_http_app()`.
|
||||
- For browser clients, add CORS to the host app. Allow the `Mcp-*` request headers. Expose the `Mcp-Session-Id` response header.
|
||||
- For more than one worker, set `request_state_security=RequestStateSecurity(keys=[...])` (from `mcp.server.mcpserver`) on `MCPServer`. Use the same keys and the same server name on all workers.
|
||||
Reference in New Issue
Block a user