python-mcp-server

Instructions for building Model Context Protocol (MCP) servers using the Python SDK. Provides patterns, best practices, and code examples for FastMCP, tools, resources, prompts, and transports.

13 stars

Best use case

python-mcp-server is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Instructions for building Model Context Protocol (MCP) servers using the Python SDK. Provides patterns, best practices, and code examples for FastMCP, tools, resources, prompts, and transports.

Teams using python-mcp-server should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/python-mcp-server/SKILL.md --create-dirs "https://raw.githubusercontent.com/tae0y/python-project-template/main/.claude/skills.nouse/python-mcp-server/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/python-mcp-server/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How python-mcp-server Compares

Feature / Agentpython-mcp-serverStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Instructions for building Model Context Protocol (MCP) servers using the Python SDK. Provides patterns, best practices, and code examples for FastMCP, tools, resources, prompts, and transports.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Python MCP Server Development

## Instructions

- Use **uv** for project management: `uv init mcp-server-demo` and `uv add "mcp[cli]"`
- Import FastMCP from `mcp.server.fastmcp`: `from mcp.server.fastmcp import FastMCP`
- Use `@mcp.tool()`, `@mcp.resource()`, and `@mcp.prompt()` decorators for registration
- Type hints are mandatory - they're used for schema generation and validation
- Use Pydantic models, TypedDicts, or dataclasses for structured output
- Tools automatically return structured output when return types are compatible
- For stdio transport, use `mcp.run()` or `mcp.run(transport="stdio")`
- For HTTP servers, use `mcp.run(transport="streamable-http")` or mount to Starlette/FastAPI
- Use `Context` parameter in tools/resources to access MCP capabilities: `ctx: Context`
- Send logs with `await ctx.debug()`, `await ctx.info()`, `await ctx.warning()`, `await ctx.error()`
- Report progress with `await ctx.report_progress(progress, total, message)`
- Request user input with `await ctx.elicit(message, schema)`
- Use LLM sampling with `await ctx.session.create_message(messages, max_tokens)`
- Configure icons with `Icon(src="path", mimeType="image/png")` for server, tools, resources, prompts
- Use `Image` class for automatic image handling: `return Image(data=bytes, format="png")`
- Define resource templates with URI patterns: `@mcp.resource("greeting://{name}")`
- Implement completion support by accepting partial values and returning suggestions
- Use lifespan context managers for startup/shutdown with shared resources
- Access lifespan context in tools via `ctx.request_context.lifespan_context`
- For stateless HTTP servers, set `stateless_http=True` in FastMCP initialization
- Enable JSON responses for modern clients: `json_response=True`
- Test servers with: `uv run mcp dev server.py` (Inspector) or `uv run mcp install server.py` (Claude Desktop)
- Mount multiple servers in Starlette with different paths: `Mount("/path", mcp.streamable_http_app())`
- Configure CORS for browser clients: expose `Mcp-Session-Id` header
- Use low-level Server class for maximum control when FastMCP isn't sufficient

## Best Practices

- Always use type hints - they drive schema generation and validation
- Return Pydantic models or TypedDicts for structured tool outputs
- Keep tool functions focused on single responsibilities
- Provide clear docstrings - they become tool descriptions
- Use descriptive parameter names with type hints
- Validate inputs using Pydantic Field descriptions
- Implement proper error handling with try-except blocks
- Use async functions for I/O-bound operations
- Clean up resources in lifespan context managers
- Log to stderr to avoid interfering with stdio transport (when using stdio)
- Use environment variables for configuration
- Test tools independently before LLM integration
- Consider security when exposing file system or network access
- Use structured output for machine-readable data
- Provide both content and structured data for backward compatibility

## Common Patterns

### Basic Server Setup (stdio)
```python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("My Server")

@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
```python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("My HTTP Server")

@mcp.tool()
def hello(name: str = "World") -> str:
    """Greet someone"""
    return f"Hello, {name}!"

if __name__ == "__main__":
    mcp.run(transport="streamable-http")
```

### Tool with Structured Output
```python
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
```python
@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
```python
from mcp.server.fastmcp import Context
from mcp.server.session import ServerSession

@mcp.tool()
async def process_data(
    data: str,
    ctx: Context[ServerSession, None]
) -> str:
    """Process data with logging"""
    await ctx.info(f"Processing: {data}")
    await ctx.report_progress(0.5, 1.0, "Halfway done")
    return f"Processed: {data}"
```

### Tool with Sampling
```python
from mcp.server.fastmcp import Context
from mcp.server.session import ServerSession
from mcp.types import SamplingMessage, TextContent

@mcp.tool()
async def summarize(
    text: str,
    ctx: Context[ServerSession, None]
) -> str:
    """Summarize text using LLM"""
    result = await ctx.session.create_message(
        messages=[SamplingMessage(
            role="user",
            content=TextContent(type="text", text=f"Summarize: {text}")
        )],
        max_tokens=100
    )
    return result.content.text if result.content.type == "text" else ""
```

### Lifespan Management
```python
from contextlib import asynccontextmanager
from dataclasses import dataclass
from mcp.server.fastmcp import FastMCP, Context

@dataclass
class AppContext:
    db: Database

@asynccontextmanager
async def app_lifespan(server: FastMCP):
    db = await Database.connect()
    try:
        yield AppContext(db=db)
    finally:
        await db.disconnect()

mcp = FastMCP("My App", lifespan=app_lifespan)

@mcp.tool()
def query(sql: str, ctx: Context) -> str:
    """Query database"""
    db = ctx.request_context.lifespan_context.db
    return db.execute(sql)
```

### Prompt with Messages
```python
from mcp.server.fastmcp.prompts import base

@mcp.prompt(title="Code Review")
def review_code(code: str) -> list[base.Message]:
    """Create code review prompt"""
    return [
        base.UserMessage("Review this code:"),
        base.UserMessage(code),
        base.AssistantMessage("I'll review the code for you.")
    ]
```

### Error Handling
```python
@mcp.tool()
async def risky_operation(input: str) -> str:
    """Operation that might fail"""
    try:
        result = await perform_operation(input)
        return f"Success: {result}"
    except Exception as e:
        return f"Error: {str(e)}"
```

Related Skills

python-mcp-expert

13
from tae0y/python-project-template

Expert assistant for developing Model Context Protocol (MCP) servers in Python. Use when creating, debugging, or optimizing MCP servers with FastMCP, tools, resources, and prompts.

python-conventions

13
from tae0y/python-project-template

Python coding conventions and guidelines including PEP 8, type hints, docstrings, and testing standards. Automatically applied when working with Python files.

python-mcp-server-generator

13
from tae0y/python-project-template

Generate a complete MCP server project in Python with tools, resources, and proper configuration. Use when scaffolding a new MCP server from scratch.

playwright-python

13
from tae0y/python-project-template

Playwright Python AI test generation instructions based on official documentation. Use when writing or reviewing Playwright tests in Python.

microsoft-agent-framework-python

13
from tae0y/python-project-template

Create, update, refactor, explain or work with code using the Python version of Microsoft Agent Framework. Use when working with AI agents, workflows, or migrating from Semantic Kernel or AutoGen.

worklog

13
from tae0y/python-project-template

Update worklog files by moving tasks between todo/doing/done states. Use when recording task progress, starting new work, or marking tasks complete. Requires explicit arguments: worklog [done|doing|todo] [description].

template-proposal-review

13
from tae0y/python-project-template

Review proposal files in localdocs/localdocs/proposals/ and decide whether to apply them to the upstream python-project-template repository. Use when evaluating proposals generated by template-upstream, deciding which ones to accept or reject, and applying accepted ones to the template repo. Triggers on: "proposal 검토", "제안 검토", "template에 반영할지", "proposals 리뷰", "upstream 반영 결정".

template-downstream

13
from tae0y/python-project-template

Update .claude/, .mcp.json, and .pre-commit-config.yaml from the upstream python-project-template repository into the current project. Only files that exist upstream are updated — project-only files are never deleted. Use when pulling latest tooling, rules, hooks, or skills from the canonical template into this project. Triggers on: "템플릿 최신화", "template sync", "upstream 반영", "template 업데이트".

template-broadcast

13
from tae0y/python-project-template

Apply template-downstream to all registered downstream projects in bulk, one project at a time. Use when you want to push the latest .claude/, .mcp.json, or .pre-commit-config.yaml to every project at once. Triggers on: "일괄 배포", "전체 프로젝트에 내려보내기", "모든 프로젝트 업데이트", "broadcast template", "bulk sync".

tdd

13
from tae0y/python-project-template

Test-Driven Development workflow. Use for ALL code changes - features, bug fixes, refactoring. TDD is non-negotiable.

sparks-create

13
from tae0y/python-project-template

Apply the 13 thinking tools from "Sparks of Genius" (생각의 탄생) to reframe any problem through creative, cross-disciplinary lenses. Use when conventional analysis falls short, when you need a fresh creative angle, or when the problem spans multiple domains.

skill-creator

13
from tae0y/python-project-template

Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.