Skip to main content

mcp-server-fastmcp-python

Implements FastMCP v3.x/4.0.0b1 server development using Python SDK v2 with Pydantic validation, transport selection, resource/tool/prompt management, and production-grade error handling for building MCP servers.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
paulpas/agent-skill-router
آخر نشاط في المصدر
٢٣ سبتمبر ٢٠٢٦ في ٢١:٥٩
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٦
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
mcp-server-fastmcp-python
description
Implements FastMCP v3.x/4.0.0b1 server development using Python SDK v2 with Pydantic validation, transport selection, resource/tool/prompt management, and production-grade error handling for building MCP servers.
license
MIT
compatibility
opencode
metadata
{"version":"1.0.0","domain":"coding","triggers":"fastmcp, mcp server python, how do i create an mcp server, tool registration, pydantic validation, mcp transport, streamable http","role":"implementation","scope":"implementation","output-format":"code","related-skills":"ai-llm-agentic-tooling-mcp, mcp-client-integration, mcp-tool-design-patterns","archetypes":"tactical, generation","anti_triggers":"brainstorming, vague ideation","response_profile":{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"}}
# FastMCP Server Implementation FastMCP is a Python framework that simplifies MCP (Model Context Protocol) server development. Loading this skill makes you capable of building fully-featured MCP servers with Pydantic-validated tools, dynamic resources, prompt templates, and production-grade error handling using the FastMCP v3.x/4.0.0b1 SDK with Python 3.10+. ## TL;DR Checklist - [ ] Install: `pip install fastmcp pydantic` - [ ] Create server with `@mcp.tool` and `@mcp.resource` decorators - [ ] Validate inputs with Pydantic models, not raw strings - [ ] Use `McpError` for structured error responses, never raise bare `Exception` - [ ] Choose transport: stdio (local CLI tools), HTTP/Streamable (integrations) - [ ] Never `print()` on stdio transport — use logging module - [ ] Test with `mcp dev` CLI before deploying to production --- ## When to Use Use this skill when: - Building a new MCP server from scratch in Python - Integrating existing Python functions as MCP tools - Exposing APIs or resources through the MCP protocol - Creating reusable prompt templates for LLM clients - Implementing production-grade tool registration with validation - Debugging or fixing errors in an existing FastMCP server - Choosing between stdio vs HTTP transport for your server --- ## When NOT to Use Avoid this skill for: - Building MCP clients (use `mcp-client-integration` instead) - Designing tool interfaces (use `mcp-tool-design-patterns` instead) - Security and authorization policies (use `mcp-security-authorization` instead) - General MCP protocol questions without Python context - Non-Python MCP servers (JavaScript/Rust/Go have different SDKs) --- ## Core Workflow 1. **Define Server Class and Transport** — Create a FastMCP server instance with transport selection (stdio for local, HTTP/Streamable for remote). **Checkpoint:** Verify `mcp` object is properly initialized. 2. **Design Tool Signatures** — Sketch tool names, input parameters as Pydantic models, and expected outputs. Define constraints (required fields, ranges, validation rules) in Pydantic `Field()` definitions. **Checkpoint:** Validate each tool solves ONE specific problem. 3. **Register Tools with Decorators** — Use `@mcp.tool()` decorator with docstring describing behavior. Input validation is automatic via Pydantic. **Checkpoint:** Verify all tool parameters are type-hinted. 4. **Implement Resource Management** — Register static URIs (read-only data) and dynamic URIs (computed on-demand) with `@mcp.resource()` decorators. Define content MIME types. **Checkpoint:** Test resource retrieval with sample URIs. 5. **Add Prompt Templates** — Create reusable prompt templates with `@mcp.prompt()` decorator for common LLM use cases. Include template arguments as Pydantic models. **Checkpoint:** Verify all templates are self-documenting. 6. **Implement Error Handling** — Catch errors from tool execution, convert to `McpError` with proper error codes, never expose raw tracebacks. **Checkpoint:** Test error paths; verify all errors return diagnostic messages. 7. **Run and Validate Server** — Start server with `mcp.run()`, test with `mcp dev` CLI, verify tools/resources/prompts are discoverable. **Checkpoint:** Confirm server starts without stderr pollution on stdio transport. --- ## Implementation Patterns ### Pattern 1: Tool Registration with Pydantic Validation **Problem:** Tool inputs must be validated before execution. Raw string parameters are unsafe. **Solution:** Use Pydantic models for all tool inputs. FastMCP automatically converts JSON to Pydantic instances and validates constraints. ```python from fastmcp import FastMCP from pydantic import BaseModel, Field from typing import Optional # Initialize server with stdio transport (for local CLI usage) mcp = FastMCP(name="weather-service", version="1.0.0") # Define input schema as Pydantic model class WeatherQuery(BaseModel): """Request schema for weather lookup.""" location: str = Field( ..., description="City name or coordinates (e.g., 'San Francisco' or '37.7749,-122.4194')", min_length=1, max_length=200, ) units: str = Field( default="celsius", description="Temperature units: celsius, fahrenheit, kelvin", pattern="^(celsius|fahrenheit|kelvin)$", ) forecast_days: int = Field( default=1, description="Number of forecast days (1-14)", ge=1, le=14, ) @mcp.tool() def get_weather(query: WeatherQuery) -> dict: """Fetch current weather and forecast for a location. Args: query: WeatherQuery containing location, units, and forecast days. Returns: Dictionary with current conditions and forecast array. Raises: McpError: If location is not found or API fails. """ # At this point, query is already validated by Pydantic # location is guaranteed to be 1-200 characters # units matches the regex pattern # forecast_days is between 1-14 try: # Simulate API call if query.location.lower() == "invalid": from mcp.types import McpError, ErrorCode raise McpError( code=ErrorCode.INVALID_PARAMS, message=f"Location '{query.location}' not found in weather database", ) current = { "location": query.location, "temperature": 22.5, "units": query.units, "conditions": "Partly cloudy", "humidity": 65, } forecast = [ {"day": i + 1, "high": 24 + i, "low": 18 - i} for i in range(query.forecast_days) ] return { "current": current, "forecast": forecast, } except Exception as e: # Convert any internal error to McpError from mcp.types import McpError, ErrorCode raise McpError( code=ErrorCode.INTERNAL_ERROR, message=f"Weather service error: {str(e)}", ) if __name__ == "__main__": mcp.run() ``` **Key Points:** - Pydantic `Field()` provides automatic validation and documentation - `min_length`, `max_length`, `ge`, `le`, `pattern` enforce constraints - Type hints (`location: str`, `forecast_days: int`) are required - Docstrings appear in LLM client UIs - Errors are structured as `McpError` with proper error codes --- ### Pattern 2: Resource Management (Static + Dynamic URIs) **Problem:** Resources (read-only data) need both static lookup and dynamic generation. **Solution:** Use `@mcp.resource()` decorator for static URIs and `@mcp.resource_list()` for discovering available resources. ```python from fastmcp import FastMCP from mcp.types import McpError, ErrorCode from pydantic import BaseModel, Field from typing import Optional import json mcp = FastMCP(name="config-manager", version="1.0.0") # Static configuration resources CONFIG_STORE = { "app:production": {"database": "prod-db.example.com", "log_level": "INFO"}, "app:staging": {"database": "staging-db.example.com", "log_level": "DEBUG"}, "app:development": {"database": "localhost", "log_level": "TRACE"}, } @mcp.resource(uri_template="config://app/{env}") def get_app_config(env: str) -> str: """Retrieve application configuration for an environment. URI format: config://app/{env} Example: config://app/production """ if env not in CONFIG_STORE: raise McpError( code=ErrorCode.RESOURCE_NOT_FOUND, message=f"Environment '{env}' not found. Available: {', '.join(CONFIG_STORE.keys())}", ) return json.dumps(CONFIG_STORE[env], indent=2) @mcp.resource_list() async def list_configs() -> list[dict]: """List all available configuration resources.""" return [ { "uri": f"config://app/{env}", "name": f"Config for {env}", "mimeType": "application/json", } for env in CONFIG_STORE.keys() ] # Dynamic resource generation @mcp.resource(uri_template="stats://process/{pid}") def get_process_stats(pid: str) -> str: """Get runtime statistics for a process. URI format: stats://process/{pid} Example: stats://process/12345 """ try: pid_int = int(pid) except ValueError: raise McpError( code=ErrorCode.INVALID_PARAMS, message=f"Invalid PID: '{pid}' must be a number", ) # Simulate process stats lookup if pid_int < 1: raise McpError( code=ErrorCode.RESOURCE_NOT_FOUND, message=f"Process {pid_int} not running", ) stats = { "pid": pid_int, "memory_mb": 128.5 + pid_int, "cpu_percent": 25.3, "threads": 4, } return json.dumps(stats, indent=2) if __name__ == "__main__": mcp.run() ``` **Key Points:** - `uri_template` defines parameterized URIs (e.g., `config://app/{env}`) - Parameters are extracted automatically and passed as function arguments - `@mcp.resource_list()` returns discoverable resources for LLM clients - Always return `str` (JSON-serializable) from resource handlers - Use `McpError` for not-found or invalid resource cases --- ### Pattern 3: Complete Server (Tools + Resources + Prompts + Error Handling) **Problem:** Real-world servers need tools, resources, prompts, and robust error handling all together. **Solution:** Build a multi-capability server with all features integrated. ```python from fastmcp import FastMCP from mcp.types import McpError, ErrorCode from pydantic import BaseModel, Field from typing import Optional import json import logging from datetime import datetime # Configure logging (never print to stdout on stdio transport) logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) # Initialize server mcp = FastMCP( name="data-analysis-server", version="1.0.0", ) # ============================================================================ # TOOLS # ============================================================================ class DatasetQuery(BaseModel): """Query schema for dataset analysis.""" dataset_id: str = Field( ..., description="Dataset identifier", min_length=1, max_length=50, pattern="^[a-zA-Z0-9_-]+$", ) operation: str = Field( default="summary", description="Analysis operation: summary, schema, sample, stats", pattern="^(summary|schema|sample|stats)$", ) limit: int = Field( default=10, description="Number of rows to return for sample operation", ge=1, le=1000, ) @mcp.tool() def analyze_dataset(query: DatasetQuery) -> dict: """Analyze a dataset and return statistics or samples. Supports multiple analysis operations: - summary: Basic metadata about the dataset - schema: Column names and types - sample: First N rows of data - stats: Statistical summary of numeric columns """ logger.info(f"Analyzing dataset: {query.dataset_id}, operation: {query.operation}") try: # Simulate dataset lookup datasets = { "sales-2024": { "rows": 50000, "columns": ["date", "product_id", "quantity", "price"], "types": ["timestamp", "string", "integer", "float"], }, "users-active": { "rows": 12500, "columns": ["user_id", "signup_date", "last_login", "country"], "types": ["string", "timestamp", "timestamp", "string"], }, } if query.dataset_id not in datasets: logger.warning(f"Dataset not found: {query.dataset_id}") raise McpError( code=ErrorCode.RESOURCE_NOT_FOUND, message=f"Dataset '{query.dataset_id}' not found. Available: {', '.join(datasets.keys())}", ) dataset_info = datasets[query.dataset_id] # Handle different operations if query.operation == "summary": result = { "dataset_id": query.dataset_id, "row_count": dataset_info["rows"], "column_count": len(dataset_info["columns"]), "columns": dataset_info["columns"], } elif query.operation == "schema": result = { "dataset_id": query.dataset_id, "schema": { col: col_type for col, col_type in zip(dataset_info["columns"], dataset_info["types"]) }, }
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub