Loading...
Loading...
This skill should be used when the user asks to "build an MCP server", "create an MCP tool", "expose resources with MCP", "write an MCP client", or needs guidance on the Model Context Protocol Python SDK best practices, transports, server primitives, or LLM context integration.
npx skill4agent add the-perfect-developer/the-perfect-opencode python-mcpmcpuv add "mcp[cli]"
# or
pip install "mcp[cli]"[cli]mcp| Primitive | Analogy | Purpose |
|---|---|---|
| Resources | GET endpoint | Load data into LLM context (read-only) |
| Tools | POST endpoint | Execute actions, produce side effects |
| Prompts | Template | Reusable interaction patterns for LLMs |
FastMCPfrom mcp.server.fastmcp import FastMCP
mcp = FastMCP(
"MyServer",
stateless_http=True, # recommended for production HTTP
json_response=True, # recommended for scalability
)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, unit: str = "celsius") -> WeatherData:
"""Get current weather for a city.
Returns structured weather data validated against WeatherData schema.
"""
# Implementation calls a real weather API
return WeatherData(temperature=22.5, condition="sunny", humidity=45.0)BaseModelTypedDictdataclassasync defctx: Context@mcp.resource("file://documents/{name}")
def read_document(name: str) -> str:
"""Read a document by name from the document store."""
# Read from disk, DB, or cache
return f"Content of {name}"
@mcp.resource("config://settings")
def get_settings() -> str:
"""Return current application settings as JSON."""
return '{"theme": "dark", "debug": false}'{param}from mcp.server.fastmcp.prompts import base
@mcp.prompt(title="Code Review")
def review_code(code: str, language: str = "python") -> list[base.Message]:
"""Generate a structured code review prompt."""
return [
base.UserMessage(f"Please review this {language} code:"),
base.UserMessage(f"```{language}\n{code}\n```"),
base.AssistantMessage("I'll analyze the code for correctness, style, and potential issues."),
]ctx: Contextfrom mcp.server.fastmcp import Context, FastMCP
from mcp.server.session import ServerSession
@mcp.tool()
async def long_running_task(
task_name: str,
steps: int,
ctx: Context[ServerSession, None],
) -> str:
"""Run a multi-step task with progress reporting."""
await ctx.info(f"Starting task: {task_name}")
for i in range(steps):
await ctx.report_progress(
progress=(i + 1) / steps,
total=1.0,
message=f"Step {i + 1} of {steps}",
)
await ctx.info("Task complete")
return f"Completed {task_name}"| Method | Purpose |
|---|---|
| Send info log to client |
| Send debug log |
| Send warning log |
| Send error log |
| Report numeric progress |
| Read another resource from within a tool |
| Request structured input from the user |
| Unique ID for current request |
| Access server instance metadata |
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from dataclasses import dataclass
import httpx
from mcp.server.fastmcp import Context, FastMCP
@dataclass
class AppState:
http_client: httpx.AsyncClient
@asynccontextmanager
async def lifespan(server: FastMCP) -> AsyncIterator[AppState]:
async with httpx.AsyncClient() as client:
yield AppState(http_client=client)
mcp = FastMCP("MyServer", lifespan=lifespan)
@mcp.tool()
async def fetch_url(url: str, ctx: Context) -> str:
"""Fetch content from a URL using the shared HTTP client."""
state: AppState = ctx.request_context.lifespan_context
response = await state.http_client.get(url)
return response.text@dataclassTypedDict@mcp.tool()
def divide(a: float, b: float) -> float:
"""Divide a by b."""
if b == 0:
raise ValueError("Division by zero is not allowed")
return a / bCallToolResultfrom mcp.types import CallToolResult, TextContent
@mcp.tool()
def safe_parse(data: str) -> CallToolResult:
"""Parse data, returning errors inline rather than raising."""
try:
result = parse(data)
return CallToolResult(
content=[TextContent(type="text", text=str(result))]
)
except ParseError as exc:
return CallToolResult(
content=[TextContent(type="text", text=f"Parse failed: {exc}")],
isError=True,
)isError=True# Start dev server with MCP Inspector
uv run mcp dev server.py
# Install to Claude Desktop
uv run mcp install server.py --name "My Server"
# Run with extra dependencies
uv run mcp dev server.py --with pandas --with numpy
# Run production HTTP server (uvicorn)
uvicorn server:mcp.streamable_http_app --host 0.0.0.0 --port 8000| Pattern | Recommendation |
|---|---|
| Transport (production) | Streamable HTTP with |
| Transport (local/stdio) | stdio via |
| I/O tools | Use |
| Shared state | Use lifespan context |
| Structured output | Return Pydantic |
| Progress reporting | Use |
| Secrets/config | Pass via environment variables, not hardcoded |
references/server-patterns.mdreferences/transports-and-deployment.md