Token导航 LogoToken导航TokenDH.com
开发敏感数据github未标认证来源可访问许可证需确认审计异常

fastmcp-developmentfastmcp 开发

Agent Skill

fastmcp-development 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

10,607

周安装

291

GitHub Stars

11

下载量

3,707
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:fastmcp-development(fastmcp 开发)
来源仓库:https://github.com/enuno/claude-command-and-control
仓库路径:skills/fastmcp-development
安装命令:
npx skills add https://github.com/enuno/claude-command-and-control --skill 'FastMCP Development'
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/enuno/claude-command-and-control --skill 'FastMCP Development'

简介

fastmcp-development 提供 FastMCP 服务器开发的全流程指导,包括工具、资源与提示的实现模式。

  • 适用于在 Codex、Claude、Cursor、Gemini CLI 中为 LLM 构建可执行函数与数据访问接口。
  • 强调类型注解、输入验证、错误处理与安全沙箱,确保工具行为可控且可追溯。
  • 需根据项目需求选择 Python 或 TypeScript 实现,并遵循最小权限原则设计 URI 资源。
  • 开发完成后应进行本地测试与 schema 验证,避免因参数错误导致 LLM 调用失败。

SKILL.md

  1. Determine server purpose and required components (tools, resources, prompts)

Ask: What functionality does this MCP server provide? What external systems will it integrate with?

  1. Create FastMCP server file with basic structure

Use Quick Start template below. Choose Python or TypeScript based on project requirements.

  1. Implement tools for LLM-executable functions

Follow Tools section. Include type hints/annotations, validation, error handling.

  1. Add resources if data access needed

Follow Resources section. Use URI templates for dynamic resources. Include security validation.

  1. Add prompts if workflow guidance needed

Follow Prompts section. Use for multi-step workflows, best practices, templates.

  1. Configure Claude Desktop integration

Follow Claude Desktop Integration section. Use fastmcp CLI or manual config. Handle environment variables.

  1. Test server locally

Run server in STDIO mode. Test with FastMCP client or Claude Desktop locally.

  1. Add authentication for production

Follow Authentication section. Use OAuth for enterprise, token verification for custom auth.

  1. Deploy using appropriate transport

STDIO for local tools, HTTP/SSE for network access. Follow Deployment section.

  1. Verify integration end-to-end

Test in Claude Desktop. Verify tools appear, resources load, prompts work.

When to Use This Skill

Use this skill when:

  • Creating new MCP servers with FastMCP
  • Adding tools, resources, or prompts to existing servers
  • Integrating MCP servers with Claude Desktop
  • Implementing authentication for production MCP servers
  • Deploying MCP servers via STDIO, HTTP, or SSE transports
  • Migrating from FastMCP v2 to v3
  • Creating custom domain-specific MCP integrations

Do NOT use this skill when:

  • Building MCP servers in languages other than Python or TypeScript (use official SDK)
  • You need maximum control over MCP protocol implementation (use official SDK)
  • Creating simple command-line tools without LLM integration (FastMCP is overkill)

Quick Start

Minimal Python Example

from fastmcp import FastMCP

mcp = FastMCP("Demo Server 🚀")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers together"""
    return a + b

@mcp.resource("greeting://hello")
def get_greeting() -> str:
    """Get a friendly greeting"""
    return "Hello from FastMCP!"

if __name__ == "__main__":
    mcp.run()

Run it:

python server.py

Minimal TypeScript Example

import { FastMCP } from "@fastmcp/server";

const mcp = new FastMCP("Demo Server 🚀");

mcp.tool({
  name: "add",
  description: "Add two numbers together",
  parameters: {
    a: { type: "number", description: "First number" },
    b: { type: "number", description: "Second number" }
  },
  execute: async ({ a, b }) => a + b
});

mcp.resource({
  uri: "greeting://hello",
  name: "Greeting",
  description: "Get a friendly greeting",
  read: async () => "Hello from FastMCP!"
});

mcp.run();

Run it:

npm install @fastmcp/server
node server.js

Claude Desktop Installation

Using fastmcp CLI (Recommended):

fastmcp install claude-desktop server.py

Manual config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "my-server": {
      "command": "uv",
      "args": ["run", "--with", "fastmcp", "fastmcp", "run", "/absolute/path/to/server.py"],
      "env": {}
    }
  }
}

Verify: Restart Claude Desktop, look for hammer icon (🔨) in input box.


Core Concepts

Tools - LLM-Executable Functions

What are tools? Python/TypeScript functions that LLMs can execute to interact with external systems, run code, and access data.

Python Tool Implementation

from fastmcp import FastMCP, Context
from typing import Annotated
from pydantic import Field

mcp = FastMCP("Data Server")

@mcp.tool(
    description="Analyze dataset statistics",
    tags={"analysis", "statistics"},
    timeout=60.0,
    annotations={"readOnlyHint": True}  # Enables caching, skips confirmation
)
async def analyze_dataset(
    data: Annotated[list[float], Field(description="Numbers to analyze")],
    percentiles: Annotated[list[int], Field(ge=0, le=100)] = [25, 50, 75],
    ctx: Context = None  # Injected automatically
) -> dict:
    """Compute statistical analysis of numeric data."""
    await ctx.info(f"Analyzing {len(data)} data points")
    await ctx.report_progress(progress=50, total=100)

    import statistics
    return {
        "mean": statistics.mean(data),
        "median": statistics.median(data),
        "stdev": statistics.stdev(data) if len(data) > 1 else 0,
        "percentiles": {p: sorted(data)[int(len(data) * p / 100)] for p in percentiles}
    }

TypeScript Tool Implementation

import { FastMCP, Context } from "@fastmcp/server";

const mcp = new FastMCP("Data Server");

mcp.tool({
  name: "analyze_dataset",
  description: "Compute statistical analysis of numeric data",
  parameters: {
    data: {
      type: "array",
      items: { type: "number" },
      description: "Numbers to analyze"
    },
    percentiles: {
      type: "array",
      items: { type: "number", minimum: 0, maximum: 100 },
      default: [25, 50, 75],
      description: "Percentiles to calculate"
    }
  },
  annotations: { readOnlyHint: true },
  execute: async ({ data, percentiles }, ctx: Context) => {
    await ctx.info(`Analyzing ${data.length} data points`);
    await ctx.reportProgress(50, 100);

    const sorted = [...data].sort((a, b) => a - b);
    const mean = data.reduce((a, b) => a + b) / data.length;
    const median = sorted[Math.floor(sorted.length / 2)];

    return {
      mean,
      median,
      stdev: calculateStdev(data, mean),
      percentiles: Object.fromEntries(
        percentiles.map(p => [p, sorted[Math.floor(sorted.length * p / 100)]])
      )
    };
  }
});

Tool Annotations for Client Behavior

annotations={
    "readOnlyHint": True,      # Skip confirmation, enable caching
    "destructiveHint": True,   # Warn users before execution
    "idempotentHint": True,    # Safe to retry
    "openWorldHint": True      # Accesses unpredictable external data
}

Tool Error Handling

from fastmcp import ToolError

@mcp.tool()
def query_database(sql: str) -> list[dict]:
    """Execute read-only SQL queries"""
    if not sql.upper().startswith("SELECT"):
        raise ToolError("Only SELECT queries allowed")

    try:
        return execute_query(sql)
    except DatabaseError as e:
        raise ToolError(f"Query failed: {str(e)}")

Resources - Read-Only Data Access

What are resources? Data or files that MCP clients can read - like GET endpoints in REST APIs, providing data without side effects.

Static Python Resource

from fastmcp.resources import TextResource

mcp.add_resource(
    TextResource(
        uri="config://database",
        name="Database Config",
        description="Current database configuration",
        text='{"host": "localhost", "port": 5432}'
    )
)

Dynamic Python Resource with URI Templates

from fastmcp.resources import ResourceResult, ResourceContent

# Simple parameter (single path segment)
@mcp.resource("weather://{city}/current")
async def get_weather(city: str) -> str:
    """Get current weather for a city"""
    if not is_valid_city(city):
        raise ResourceError(f"Invalid city: {city}")

    data = await fetch_weather(city)
    return json.dumps({"city": city, "temp": data.temp, "conditions": data.conditions})

# Wildcard parameter (multiple segments)
@mcp.resource("path://{filepath*}")
async def get_file_content(filepath: str, ctx: Context) -> str:
    """Read file contents with security validation"""
    # Matches: path://docs/server/resources.mdx
    full_path = os.path.abspath(filepath)

    # Security: Prevent directory traversal
    if not full_path.startswith(ALLOWED_BASE_PATH):
        raise ResourceError("Access denied: path outside allowed directory")

    if not os.path.exists(full_path):
        raise ResourceError(f"File not found: {filepath}")

    await ctx.info(f"Reading file: {filepath}")

    async with aiofiles.open(full_path, 'r') as f:
        content = await f.read()

    return content

# Query parameters (optional configuration)
@mcp.resource("data://{id}{?format}")
def get_data(id: str, format: str = "json") -> str:
    """Get data in specified format"""
    # Matches: data://123?format=xml
    data = fetch_data(id)
    if format == "xml":
        return convert_to_xml(data)
    return json.dumps(data)

TypeScript Resource with URI Templates

mcp.resource({
  uri: "weather://{city}/current",
  name: "Current Weather",
  description: "Get current weather for a city",
  read: async ({ city }, ctx) => {
    if (!isValidCity(city)) {
      throw new ResourceError(`Invalid city: ${city}`);
    }

    const data = await fetchWeather(city);
    return JSON.stringify({ city, temp: data.temp, conditions: data.conditions });
  }
});

// File system resource with security
mcp.resource({
  uri: "path://{filepath*}",
  name: "File Content",
  description: "Read file contents",
  read: async ({ filepath }, ctx) => {
    const fullPath = path.resolve(filepath);

    // Security: Prevent directory traversal
    if (!fullPath.startsWith(ALLOWED_BASE_PATH)) {
      throw new ResourceError("Access denied");
    }

    await ctx.info(`Reading file: ${filepath}`);
    return await fs.promises.readFile(fullPath, 'utf-8');
  }
});

Multiple Content Types

@mcp.resource("data://users")
def get_users() -> ResourceResult:
    """Get user data in multiple formats"""
    return ResourceResult(
        contents=[
            ResourceContent(
                content='[{"id": 1, "name": "Alice"}]',
                mime_type="application/json"
            ),
            ResourceContent(
                content="# Users\nTotal: 1 user",
                mime_type="text/markdown"
            ),
        ],
        meta={"total": 1, "cached": True}
    )

Prompts - Reusable Message Templates

What are prompts? Message templates that help LLMs generate structured, purposeful responses - "best practices encoded into your server."

Basic Python Prompt

from fastmcp import FastMCP
from fastmcp.prompts import Message, PromptResult

mcp = FastMCP("Prompt Server")

@mcp.prompt()
def ask_about_topic(topic: str) -> str:
    """Generate a user message asking for explanation"""
    return f"Can you please explain the concept of '{topic}' in simple terms?"

Advanced Python Prompt with Multi-Message Conversation

@mcp.prompt(
    name="code_review_workflow",
    description="Complete code review with security analysis",
    tags={"security", "code-quality"}
)
def code_review(code: str, language: str = "python") -> PromptResult:
    """Security-focused code review workflow"""
    return PromptResult(
        messages=[
            Message(
                role="user",
                content=f"Review this {language} code for security issues:\n```{language}\n{code}\n```"
            ),
            Message(
                role="assistant",
                content="I'll analyze this systematically for security vulnerabilities."
            ),
            Message(
                role="user",
                content="Focus especially on SQL injection, XSS, and authentication bypass."
            )
        ],
        description="Security-focused code review with systematic analysis",
        meta={"priority": "high", "review_type": "security"}
    )

TypeScript Prompt Implementation

mcp.prompt({
  name: "code_review_workflow",
  description: "Complete code review with security analysis",
  parameters: {
    code: { type: "string", description: "Code to review" },
    language: { type: "string", default: "python" }
  },
  execute: async ({ code, language }) => {
    return {
      messages: [
        {
          role: "user",
          content: `Review this ${language} code for security issues:\n\`\`\`${language}\n${code}\n\`\`\``
        },
        {
          role: "assistant",
          content: "I'll analyze this systematically for security vulnerabilities."
        },
        {
          role: "user",
          content: "Focus especially on SQL injection, XSS, and authentication bypass."
        }
      ],
      description: "Security-focused code review",
      meta: { priority: "high", review_type: "security" }
    };
  }
});

Context - MCP Capabilities Access

What is Context? Dependency-injected object providing access to logging, progress tracking, resource/prompt management, LLM operations, and request metadata.

Context Capabilities

CategoryMethodsPurpose
Loggingdebug(), info(), warning(), error()Send log messages to clients
Progressreport_progress(progress, total)Update clients on long-running ops
Resourceslist_resources(), read_resource(uri)Access other resources
Promptslist_prompts(), get_prompt(name, args)Retrieve prompt templates
LLMsample(prompt, temperature)Request client LLM generation
User Inputelicit(question, response_type)Request structured user input
Stateset_state(), get_state(), delete_state()Persist data across requests
Metadatarequest_id, client_id, session_idRequest context information

Python Context Example

from fastmcp import FastMCP, Context
from fastmcp.dependencies import CurrentContext

@mcp.tool()
async def process_data(data_uri: str, ctx: Context = CurrentContext()) -> dict:
    """Process data with full context capabilities"""
    await ctx.info(f"Processing {data_uri}")

    # Read another resource
    resources = await ctx.read_resource(data_uri)
    content = resources[0].text

    # Report progress
    await ctx.report_progress(progress=25, total=100)

    # Use LLM for analysis
    summary = await ctx.sample(f"Summarize this data concisely: {content[:500]}")

    await ctx.report_progress(progress=75, total=100)

    # Store state for next request
    await ctx.set_state("last_processed", data_uri)

    await ctx.report_progress(progress=100, total=100)

    return {
        "result": summary.text,
        "request_id": ctx.request_id,
        "timestamp": ctx.timestamp
    }

TypeScript Context Example

mcp.tool({
  name: "process_data",
  description: "Process data with full context capabilities",
  parameters: {
    data_uri: { type: "string", description: "URI of data to process" }
  },
  execute: async ({ data_uri }, ctx) => {
    await ctx.info(`Processing ${data_uri}`);

    // Read resource
    const resources = await ctx.readResource(data_uri);
    const content = resources[0].text;

    // Report progress
    await ctx.reportProgress(25, 100);

    // Use LLM
    const summary = await ctx.sample(`Summarize this data: ${content.slice(0, 500)}`);

    await ctx.reportProgress(75, 100);

    // Store state
    await ctx.setState("last_processed", data_uri);

    await ctx.reportProgress(100, 100);

    return {
      result: summary.text,
      request_id: ctx.requestId,
      timestamp: ctx.timestamp
    };
  }
});

Critical: Context is scoped to a single request. State persists across requests, but context object itself is recreated per request.


Claude Desktop Integration

Installation Methods

Method 1: FastMCP CLI (Recommended)

# Basic installation
fastmcp install claude-desktop server.py

# With dependencies
fastmcp install claude-desktop server.py \
    --with pandas \
    --with requests \
    --env API_KEY=your_key

# With requirements file
fastmcp install claude-desktop server.py \
    --with-requirements requirements.txt \
    --env-file .env

Method 2: Manual Configuration

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json Linux: ~/.config/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "my-server": {
      "command": "uv",
      "args": [
        "run",
        "--with", "fastmcp",
        "--with", "pandas",
        "fastmcp",
        "run",
        "/absolute/path/to/server.py"
      ],
      "env": {
        "API_KEY": "your_value",
        "DATABASE_URL": "postgresql://localhost/mydb"
      }
    }
  }
}

TypeScript/Node.js config:

{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["/absolute/path/to/server.js"],
      "env": {
        "NODE_ENV": "production"
      }
    }
  }
}

Critical Requirements

⚠️ Environment Isolation: Claude Desktop runs servers in completely isolated environment with no access to your shell environment

⚠️ Must explicitly pass environment variables via --env or --env-file

⚠️ Absolute paths required for server files (relative paths will fail)

⚠️ uv must be installed and available in PATH for Python servers

Verification

  1. Save config file
  2. Restart Claude Desktop completely (quit and reopen)
  3. Look for hammer icon (🔨) in input box
  4. Type a message to see if tools appear in suggestions

Troubleshooting

No hammer icon appears:

  • Check config file syntax (use JSON validator)
  • Verify absolute path to server file
  • Check uv is installed: which uv
  • Look at Claude Desktop logs (Help → View Logs)

Hammer icon appears but tools don't work:

  • Check environment variables are passed correctly
  • Verify dependencies are installed
  • Check server logs for errors
  • Test server standalone: python server.py

Environment variables not working:

  • Don't rely on shell environment (it won't be loaded)
  • Pass ALL required env vars explicitly in config
  • Use --env-file to load from .env file

Authentication and Security

Default is Insecure

⚠️ FastMCP defaults to HTTP with no authentication or encryption

🔒 For production: ALWAYS use HTTPS/TLS and require authentication

OAuth Authentication (Python)

from fastmcp import FastMCP
from fastmcp.auth import GoogleOAuth, GitHubOAuth, AzureOAuth

# Google OAuth
mcp = FastMCP(
    name="Secure Server",
    auth=GoogleOAuth(
        client_id="your-client-id.apps.googleusercontent.com",
        client_secret="your-client-secret"
    )
)

# GitHub OAuth
mcp = FastMCP(
    name="Secure Server",
    auth=GitHubOAuth(
        client_id="your-client-id",
        client_secret="your-client-secret"
    )
)

# Azure OAuth
mcp = FastMCP(
    name="Secure Server",
    auth=AzureOAuth(
        client_id="your-client-id",
        client_secret="your-client-secret",
        tenant_id="your-tenant-id"
    )
)

Token Verification (Python)

from fastmcp.auth import TokenVerifier

def verify_jwt(token: str) -> bool:
    """Verify JWT token against your auth system"""
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return payload.get("authorized") == True
    except jwt.InvalidTokenError:
        return False

mcp = FastMCP(
    name="Secure Server",
    auth=TokenVerifier(verify_token=verify_jwt)
)

TypeScript Authentication

import { FastMCP } from "@fastmcp/server";
import { GoogleOAuth } from "@fastmcp/auth";

const mcp = new FastMCP({
  name: "Secure Server",
  auth: new GoogleOAuth({
    clientId: "your-client-id",
    clientSecret: "your-client-secret"
  })
});

Security Best Practices

mcp = FastMCP(
    name="Production Server",
    auth=GoogleOAuth(...),
    mask_error_details=True,  # Don't leak internal errors to clients
    allowed_origins=["https://your-domain.com"],  # CORS restrictions
    rate_limit={"requests_per_minute": 100}  # Rate limiting
)

@mcp.tool()
def query_database(sql: str) -> list[dict]:
    """Secure database query with validation"""
    # Input validation
    if not sql.upper().startswith("SELECT"):
        raise ToolError("Only SELECT queries allowed")

    # SQL injection prevention
    if any(keyword in sql.upper() for keyword in ["DROP", "DELETE", "UPDATE", "INSERT"]):
        raise ToolError("Destructive SQL keywords not allowed")

    # Parameterized queries
    return execute_query(sql, use_prepared=True)

@mcp.resource("file://{path*}")
def read_file(path: str) -> str:
    """Secure file access with path validation"""
    # Prevent directory traversal
    abs_path = os.path.abspath(path)
    if not abs_path.startswith(ALLOWED_BASE_DIR):
        raise ResourceError("Access denied: path outside allowed directory")

    # Check permissions
    if not os.access(abs_path, os.R_OK):
        raise ResourceError("Access denied: insufficient permissions")

    return open(abs_path).read()

Advanced Patterns

Dependency Injection

from fastmcp.dependencies import Depends

def get_user_id() -> str:
    """Hidden from LLM schema, injected at runtime"""
    return "user_123"

def get_db_connection():
    """Dependency injection for database"""
    return DatabaseConnection()

@mcp.tool()
def get_user_details(
    user_id: str = Depends(get_user_id),
    db = Depends(get_db_connection)
) -> str:
    """Tool receives injected dependencies transparently"""
    return db.fetch_user(user_id)

Server Composition

from fastmcp import FastMCP

# Combine multiple servers
weather_server = FastMCP("Weather")
database_server = FastMCP("Database")

main = FastMCP("Unified Server")
main.add_server(weather_server, prefix="weather")
main.add_server(database_server, prefix="db")

# Exposes: weather:get_forecast, db:query_users
if __name__ == "__main__":
    main.run()

Remote Server Proxying

from fastmcp.server import create_proxy
from fastmcp.auth import BearerAuth

# Create proxy to remote MCP server
proxy = create_proxy(
    "https://api.example.com/mcp/sse",
    name="Remote Server Proxy",
    auth=BearerAuth(token="your-api-token")
)

if __name__ == "__main__":
    proxy.run()

Dynamic Component Management

# Add/remove components at runtime
mcp.add_tool(my_function)
mcp.remove_tool("tool_name")

# Control visibility
mcp.disable(tags={"admin"})  # Hide admin tools
mcp.enable(tags={"public"}, only=True)  # Allowlist mode

# Clients automatically notified via notifications/tools/list_changed

Testing with Client

import asyncio
from fastmcp import FastMCP, Client

mcp = FastMCP("Test Server")

@mcp.tool()
def add(a: int, b: int) -> int:
    return a + b

# Test your server
async def test_server():
    async with Client(mcp) as client:
        result = await client.call_tool("add", {"a": 5, "b": 3})
        assert result == 8
        print("✅ Test passed")

asyncio.run(test_server())

Common Pitfalls

1. Environment Isolation in Claude Desktop

Problem: Server can't find API keys or dependencies

# ❌ BAD: Relies on shell environment
api_key = os.getenv("API_KEY")  # Will be None in Claude Desktop!

Solution: Explicitly pass environment variables in config

{
  "mcpServers": {
    "my-server": {
      "command": "uv",
      "args": ["run", "--with", "fastmcp", "fastmcp", "run", "server.py"],
      "env": {
        "API_KEY": "actual_value_here"
      }
    }
  }
}

2. Relative Paths in Config

Problem: Server file not found

// ❌ BAD: Relative path
"args": ["fastmcp", "run", "./server.py"]

Solution: Use absolute paths

// ✅ GOOD: Absolute path
"args": ["fastmcp", "run", "/Users/alice/projects/mcp-server/server.py"]

3. Functions with *args or **kwargs

Problem: FastMCP can't generate schema

# ❌ BAD: Can't extract parameter schema
@mcp.tool()
def process(*args, **kwargs):
    pass

Solution: Use explicit parameters

# ✅ GOOD: Explicit parameters with types
@mcp.tool()
def process(data: list[str], options: dict[str, any] = {}) -> dict:
    pass

4. Context Scoped to Single Request

Problem: Expecting context to persist

# ❌ BAD: Context won't persist across requests
@mcp.tool()
async def step1(ctx: Context):
    ctx.user_data = "some value"  # Lost after request ends

@mcp.tool()
async def step2(ctx: Context):
    return ctx.user_data  # Will fail - different context instance

Solution: Use context state methods

# ✅ GOOD: Use state persistence
@mcp.tool()
async def step1(ctx: Context):
    await ctx.set_state("user_data", "some value")

@mcp.tool()
async def step2(ctx: Context):
    return await ctx.get_state("user_data")

5. Default Security is Insecure

Problem: Production server with no authentication

# ❌ BAD: No auth, HTTP only
mcp = FastMCP("Production Server")
mcp.run(transport="http", port=8000)

Solution: Always use auth and HTTPS

# ✅ GOOD: OAuth with HTTPS
mcp = FastMCP(
    "Production Server",
    auth=GoogleOAuth(client_id="...", client_secret="...")
)
mcp.run(transport="http", port=8443, ssl_certfile="cert.pem", ssl_keyfile="key.pem")

6. Async/Sync Confusion

Problem: Mixing async/sync incorrectly

# ❌ BAD: Blocking I/O in async function
@mcp.tool()
async def fetch_data(url: str) -> str:
    return requests.get(url).text  # Blocks event loop!

Solution: Use async libraries or sync tools

# ✅ GOOD: Async I/O
@mcp.tool()
async def fetch_data(url: str) -> str:
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

# ✅ ALSO GOOD: Sync tool (FastMCP runs in thread pool)
@mcp.tool()
def fetch_data_sync(url: str) -> str:
    return requests.get(url).text  # FastMCP handles threading

7. Not Handling Tool Errors

Problem: Unhandled exceptions leak to clients

# ❌ BAD: Raw exception
@mcp.tool()
def divide(a: int, b: int) -> float:
    return a / b  # ZeroDivisionError leaks

Solution: Catch and raise ToolError

# ✅ GOOD: Clean error handling
from fastmcp import ToolError

@mcp.tool()
def divide(a: int, b: int) -> float:
    if b == 0:
        raise ToolError("Cannot divide by zero")
    return a / b

Deployment

Transport Options

STDIO (Default) - For Claude Desktop and Local Tools

# Python
if __name__ == "__main__":
    mcp.run()  # Defaults to STDIO
# Run directly
python server.py

Use when:

  • Integrating with Claude Desktop
  • Building local command-line tools
  • Single-user scenarios

HTTP - For Network Access

# Python
if __name__ == "__main__":
    mcp.run(transport="http", host="0.0.0.0", port=8000)
// TypeScript
mcp.run({ transport: "http", host: "0.0.0.0", port: 8000 });

Use when:

  • Multiple clients need access
  • Deploying to cloud services
  • Need RESTful interface

SSE (Server-Sent Events) - For Streaming

if __name__ == "__main__":
    mcp.run(transport="sse", host="0.0.0.0", port=8000)

Use when:

  • Need long-lived connections
  • Real-time updates/streaming
  • Better than HTTP for persistent connections

Production Deployment Checklist

  • Authentication enabled (OAuth or token verification)
  • HTTPS/TLS configured (SSL certificates)
  • Error masking enabled (mask_error_details=True)
  • Input validation on all tools and resources
  • Rate limiting configured
  • Logging configured to monitoring system
  • Health check endpoint implemented
  • Environment variables managed securely (not in code)
  • Dependencies pinned to specific versions
  • CORS configured if web clients will connect
  • Resource limits set (memory, CPU, timeout)
  • Monitoring and alerting configured

Production Server Template (Python)

from fastmcp import FastMCP
from fastmcp.auth import GoogleOAuth
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Production server with security
mcp = FastMCP(
    name="Production API Server",
    auth=GoogleOAuth(
        client_id=os.getenv("GOOGLE_CLIENT_ID"),
        client_secret=os.getenv("GOOGLE_CLIENT_SECRET")
    ),
    mask_error_details=True,
    rate_limit={"requests_per_minute": 100},
    allowed_origins=["https://app.example.com"]
)

@mcp.tool(timeout=30.0)
async def secure_operation(data: str, ctx: Context) -> dict:
    """Production-ready tool with full security"""
    try:
        # Log request
        logger.info(f"Request {ctx.request_id}: processing {len(data)} bytes")

        # Validate input
        if len(data) > 10000:
            raise ToolError("Data too large (max 10KB)")

        # Process
        result = await process_data(data)

        # Log success
        logger.info(f"Request {ctx.request_id}: success")

        return {"result": result, "request_id": ctx.request_id}

    except Exception as e:
        logger.error(f"Request {ctx.request_id}: error - {str(e)}")
        raise ToolError("Processing failed")

if __name__ == "__main__":
    mcp.run(
        transport="http",
        host="0.0.0.0",
        port=int(os.getenv("PORT", 8443)),
        ssl_certfile=os.getenv("SSL_CERT"),
        ssl_keyfile=os.getenv("SSL_KEY")
    )

Production Server Template (TypeScript)

import { FastMCP } from "@fastmcp/server";
import { GoogleOAuth } from "@fastmcp/auth";

const mcp = new FastMCP({
  name: "Production API Server",
  auth: new GoogleOAuth({
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!
  }),
  maskErrorDetails: true,
  rateLimit: { requestsPerMinute: 100 },
  allowedOrigins: ["https://app.example.com"]
});

mcp.tool({
  name: "secure_operation",
  description: "Production-ready tool",
  parameters: { data: { type: "string" } },
  timeout: 30000,
  execute: async ({ data }, ctx) => {
    try {
      console.log(`Request ${ctx.requestId}: processing ${data.length} bytes`);

      if (data.length > 10000) {
        throw new Error("Data too large");
      }

      const result = await processData(data);

      console.log(`Request ${ctx.requestId}: success`);

      return { result, request_id: ctx.requestId };
    } catch (e) {
      console.error(`Request ${ctx.requestId}: error - ${e}`);
      throw new Error("Processing failed");
    }
  }
});

mcp.run({
  transport: "http",
  host: "0.0.0.0",
  port: parseInt(process.env.PORT || "8443"),
  sslCert: process.env.SSL_CERT,
  sslKey: process.env.SSL_KEY
});

FastMCP v3 Beta Features

Note: FastMCP v3 is in beta as of January 2026. For production systems, pin to v2.x.x stable release.

What's New in v3

1. Improved Type System

# v3: Better type inference
from fastmcp import FastMCP
from typing import Literal

@mcp.tool()
def process(mode: Literal["fast", "accurate", "balanced"]) -> dict:
    # v3 automatically generates enum schema
    pass

2. Enhanced Context API

# v3: New context methods
@mcp.tool()
async def advanced_tool(ctx: Context) -> dict:
    # Structured logging with levels
    await ctx.log(level="INFO", message="Starting", tags={"module": "processor"})

    # Batch operations
    resources = await ctx.batch_read_resources(["uri1", "uri2", "uri3"])

    # Enhanced state with TTL
    await ctx.set_state("cache", data, ttl=3600)  # Expire after 1 hour

3. Middleware Support

# v3: Request/response middleware
async def auth_middleware(request, call_next):
    if not validate_token(request.headers.get("Authorization")):
        raise UnauthorizedError()
    return await call_next(request)

mcp = FastMCP("Server", middleware=[auth_middleware])

4. Plugin System

# v3: Plugin architecture
from fastmcp.plugins import MetricsPlugin, CachePlugin

mcp = FastMCP(
    "Server",
    plugins=[
        MetricsPlugin(export_to="prometheus"),
        CachePlugin(backend="redis", ttl=300)
    ]
)

Breaking Changes from v2

Featurev2v3
Import pathfrom fastmcp import FastMCPSame (no change)
Tool decorator@mcp.tool()@mcp.tool() (signature changed)
Context injectionctx: Context = Nonectx: Context (required if used)
Error classesToolError, ResourceErrorSame + UnauthorizedError
Auth configurationauth parameterauth + authorization

Migration Guide: v2 → v3

Step 1: Update Dependencies

# Pin to v2 (stable)
pip install fastmcp~=2.11.0

# Upgrade to v3 (beta)
pip install fastmcp~=3.0.0-beta

Step 2: Update Tool Signatures

# v2
@mcp.tool()
def my_tool(param: str, ctx: Context = None) -> str:
    pass

# v3: Context must be explicitly typed (no default None)
@mcp.tool()
def my_tool(param: str, ctx: Context) -> str:
    pass

Step 3: Update Context Method Calls

# v2
await ctx.report_progress(50, 100)

# v3: Same syntax (no change)
await ctx.report_progress(50, 100)

Step 4: Update Authentication

# v2
from fastmcp.auth import GoogleOAuth
mcp = FastMCP("Server", auth=GoogleOAuth(...))

# v3: Enhanced auth configuration
from fastmcp.auth import GoogleOAuth, RoleBasedAuth
mcp = FastMCP(
    "Server",
    auth=GoogleOAuth(...),
    authorization=RoleBasedAuth(roles=["admin", "user"])
)

Step 5: Test Thoroughly

# Run full test suite before deploying v3
pytest tests/

Version Compatibility Matrix

Featurev2.11.xv3.0.0-betaStatus
Basic toolsStable
ResourcesStable
PromptsStable
Context API✅ EnhancedEnhanced in v3
OAuth✅ EnhancedEnhanced in v3
MiddlewareNew in v3
PluginsNew in v3
Batch operationsNew in v3

Recommendation: Use v2.11.x for production, evaluate v3 beta in staging environments.


Integration with Creating-Skills Workflow

This FastMCP skill composes with the creating-skills workflow for creating custom domain-specific MCP skills.

When to Combine Both Skills

Use FastMCP skill alone when:

  • Building one-off MCP servers
  • Integrating specific APIs or data sources
  • Quick prototyping

Use both skills together when:

  • Creating reusable MCP patterns for your team
  • Building domain-specific skill templates (e.g., "Database MCP Skill", "API Wrapper MCP Skill")
  • Packaging MCP servers as distributable skills

Workflow: Creating Custom MCP-Based Skill

  1. User requests custom skill: "Create a skill for working with our company's API using MCP"
  2. Creating-skills skill activates:

- Gathers requirements about the API - Determines skill structure - Creates skill directory

  1. Creating-skills delegates to FastMCP skill:

- FastMCP skill provides MCP server implementation - Creates tools for API endpoints - Creates resources for data access - Adds authentication

  1. Creating-skills wraps as reusable skill:

- Packages MCP server as SKILL.md - Adds checklist for using the skill - Documents activation triggers - Creates usage examples

  1. Result: Custom skill that other team members can use to work with the API via MCP

Example: Creating "Company CRM MCP Skill"

# Input to creating-skills
"Create a skill for working with our Salesforce CRM using MCP"

# Creating-skills output (using FastMCP skill for implementation)
.claude/skills/salesforce-crm/
├── SKILL.md                    # Skill instructions
├── server.py                   # FastMCP server (from this skill)
└── config.json                 # Claude Desktop config

# server.py (generated using FastMCP skill patterns)
from fastmcp import FastMCP, Context
from salesforce_api import SalesforceClient

mcp = FastMCP("Salesforce CRM")

@mcp.tool()
async def search_contacts(query: str) -> list[dict]:
    """Search contacts in Salesforce"""
    client = SalesforceClient(os.getenv("SALESFORCE_TOKEN"))
    return await client.search_contacts(query)

@mcp.resource("crm://contacts/{contact_id}")
async def get_contact(contact_id: str) -> str:
    """Get contact details"""
    client = SalesforceClient(os.getenv("SALESFORCE_TOKEN"))
    contact = await client.get_contact(contact_id)
    return json.dumps(contact)

if __name__ == "__main__":
    mcp.run()

# SKILL.md (created by creating-skills, references FastMCP patterns)
---
name: Salesforce CRM
description: Work with Salesforce CRM via MCP tools and resources
---

<required>
1. Install FastMCP skill server in Claude Desktop
2. Configure SALESFORCE_TOKEN environment variable
3. Use tools: search_contacts, get_contact
4. Use resources: crm://contacts/{id}
</required>

Reference Pattern

When creating-skills needs MCP implementation, it should reference this skill:

# In creating-skills SKILL.md
For MCP server implementation, follow patterns from FastMCP skill:
- Tools: /home/elvis/.claude/skills/fastmcp/SKILL.md#tools
- Resources: /home/elvis/.claude/skills/fastmcp/SKILL.md#resources
- Authentication: /home/elvis/.claude/skills/fastmcp/SKILL.md#authentication

Complete Examples

Example 1: Database Integration Server (Python)

from fastmcp import FastMCP, Context, ToolError
from fastmcp.auth import GoogleOAuth
import asyncpg
import os

mcp = FastMCP(
    "Database Server",
    auth=GoogleOAuth(
        client_id=os.getenv("GOOGLE_CLIENT_ID"),
        client_secret=os.getenv("GOOGLE_CLIENT_SECRET")
    )
)

async def get_db_pool():
    """Dependency: Database connection pool"""
    return await asyncpg.create_pool(os.getenv("DATABASE_URL"))

@mcp.tool(
    description="Execute read-only SQL queries",
    annotations={"readOnlyHint": True}
)
async def query(sql: str, ctx: Context) -> list[dict]:
    """Execute SELECT queries with security validation"""
    # Validate SQL
    sql_upper = sql.upper().strip()
    if not sql_upper.startswith("SELECT"):
        raise ToolError("Only SELECT queries allowed")

    if any(keyword in sql_upper for keyword in ["DROP", "DELETE", "UPDATE", "INSERT"]):
        raise ToolError("Destructive SQL not allowed")

    await ctx.info(f"Executing query: {sql[:50]}...")

    # Execute with connection pool
    pool = await get_db_pool()
    try:
        async with pool.acquire() as conn:
            rows = await conn.fetch(sql)
            return [dict(row) for row in rows]
    except Exception as e:
        await ctx.error(f"Query failed: {str(e)}")
        raise ToolError(f"Query execution failed: {str(e)}")

@mcp.resource("db://tables")
async def list_tables(ctx: Context) -> str:
    """List all database tables"""
    pool = await get_db_pool()
    async with pool.acquire() as conn:
        rows = await conn.fetch("""
            SELECT table_name
            FROM information_schema.tables
            WHERE table_schema = 'public'
            ORDER BY table_name
        """)
        tables = [row["table_name"] for row in rows]
        return json.dumps({"tables": tables})

@mcp.resource("db://tables/{table_name}/schema")
async def get_schema(table_name: str, ctx: Context) -> str:
    """Get table schema information"""
    # Prevent SQL injection in table name
    if not table_name.replace("_", "").isalnum():
        raise ResourceError("Invalid table name")

    pool = await get_db_pool()
    async with pool.acquire() as conn:
        rows = await conn.fetch("""
            SELECT column_name, data_type, is_nullable
            FROM information_schema.columns
            WHERE table_name = $1
            ORDER BY ordinal_position
        """, table_name)

        schema = [
            {
                "column": row["column_name"],
                "type": row["data_type"],
                "nullable": row["is_nullable"] == "YES"
            }
            for row in rows
        ]
        return json.dumps({"table": table_name, "schema": schema})

if __name__ == "__main__":
    mcp.run()

Example 2: File System Server (TypeScript)

import { FastMCP, Context, ToolError, ResourceError } from "@fastmcp/server";
import { GoogleOAuth } from "@fastmcp/auth";
import * as fs from "fs/promises";
import * as path from "path";

const ALLOWED_BASE = process.env.ALLOWED_DIR || "/home/user/documents";

const mcp = new FastMCP({
  name: "File System Server",
  auth: new GoogleOAuth({
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!
  })
});

// Tool: List directory
mcp.tool({
  name: "list_directory",
  description: "List files and directories",
  parameters: {
    path: { type: "string", description: "Directory path", default: "." }
  },
  execute: async ({ path: dirPath }, ctx) => {
    const fullPath = path.resolve(ALLOWED_BASE, dirPath);

    // Security: Prevent directory traversal
    if (!fullPath.startsWith(ALLOWED_BASE)) {
      throw new ToolError("Access denied");
    }

    await ctx.info(`Listing directory: ${dirPath}`);

    const entries = await fs.readdir(fullPath, { withFileTypes: true });

    return {
      path: dirPath,
      entries: entries.map(e => ({
        name: e.name,
        type: e.isDirectory() ? "directory" : "file"
      }))
    };
  }
});

// Tool: Search files
mcp.tool({
  name: "search_files",
  description: "Search for files by name pattern",
  parameters: {
    pattern: { type: "string", description: "Glob pattern (e.g., *.txt)" },
    directory: { type: "string", default: ".", description: "Directory to search" }
  },
  execute: async ({ pattern, directory }, ctx) => {
    await ctx.info(`Searching for ${pattern} in ${directory}`);

    const fullPath = path.resolve(ALLOWED_BASE, directory);

    if (!fullPath.startsWith(ALLOWED_BASE)) {
      throw new ToolError("Access denied");
    }

    const results = await searchFiles(fullPath, pattern);

    return {
      pattern,
      directory,
      matches: results.length,
      files: results
    };
  }
});

// Resource: Read file contents
mcp.resource({
  uri: "file://{filepath*}",
  name: "File Content",
  description: "Read file contents",
  read: async ({ filepath }, ctx) => {
    const fullPath = path.resolve(ALLOWED_BASE, filepath);

    // Security validation
    if (!fullPath.startsWith(ALLOWED_BASE)) {
      throw new ResourceError("Access denied");
    }

    try {
      await ctx.info(`Reading file: ${filepath}`);
      const content = await fs.readFile(fullPath, "utf-8");
      return content;
    } catch (e) {
      throw new ResourceError(`Failed to read file: ${e.message}`);
    }
  }
});

// Prompt: File analysis workflow
mcp.prompt({
  name: "analyze_file",
  description: "Analyze file content and generate report",
  parameters: {
    filepath: { type: "string", description: "Path to file" }
  },
  execute: async ({ filepath }) => {
    return {
      messages: [
        {
          role: "user",
          content: `Analyze the file at ${filepath} and provide:`
        },
        {
          role: "user",
          content: `1. File size and format\n2. Content summary\n3. Key findings\n4. Recommendations`
        }
      ]
    };
  }
});

mcp.run();

Templates

Basic Tool Template (Python)

from fastmcp import FastMCP, Context, ToolError

@mcp.tool(
    description="[What this tool does]",
    annotations={"readOnlyHint": True}  # or destructiveHint, etc.
)
async def tool_name(
    param1: str,  # Required parameter
    param2: int = 10,  # Optional with default
    ctx: Context = None  # Context injection
) -> dict:
    """[Detailed docstring for LLM]"""
    try:
        # 1. Validate inputs
        if not param1:
            raise ToolError("param1 is required")

        # 2. Log operation
        await ctx.info(f"Processing: {param1}")

        # 3. Report progress for long operations
        await ctx.report_progress(50, 100)

        # 4. Execute logic
        result = do_something(param1, param2)

        # 5. Return structured data
        return {"result": result, "param1": param1}

    except Exception as e:
        await ctx.error(f"Tool failed: {str(e)}")
        raise ToolError(f"Operation failed: {str(e)}")

Basic Resource Template (Python)

from fastmcp import ResourceError

@mcp.resource("namespace://{param}/path")
async def resource_name(param: str, ctx: Context) -> str:
    """[Docstring describing what data this returns]"""
    try:
        # 1. Validate parameters
        if not is_valid(param):
            raise ResourceError(f"Invalid parameter: {param}")

        # 2. Security checks
        if not has_permission(param):
            raise ResourceError("Access denied")

        # 3. Fetch data
        data = fetch_data(param)

        # 4. Return as string (JSON for structured data)
        return json.dumps(data)

    except Exception as e:
        raise ResourceError(f"Failed to fetch data: {str(e)}")

Production Server Template (Python)

#!/usr/bin/env python3
from fastmcp import FastMCP, Context, ToolError
from fastmcp.auth import GoogleOAuth
import logging
import os

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Create secure server
mcp = FastMCP(
    name=os.getenv("SERVER_NAME", "Production Server"),
    auth=GoogleOAuth(
        client_id=os.getenv("GOOGLE_CLIENT_ID"),
        client_secret=os.getenv("GOOGLE_CLIENT_SECRET")
    ),
    mask_error_details=True,
    rate_limit={"requests_per_minute": int(os.getenv("RATE_LIMIT", "100"))},
)

# Add your tools, resources, prompts here

if __name__ == "__main__":
    port = int(os.getenv("PORT", "8443"))
    transport = os.getenv("TRANSPORT", "http")

    logger.info(f"Starting server on {transport}:{port}")

    mcp.run(
        transport=transport,
        host="0.0.0.0",
        port=port,
        ssl_certfile=os.getenv("SSL_CERT"),
        ssl_keyfile=os.getenv("SSL_KEY")
    )

References

Official Documentation

Integration Guides

Community

Learning Resources


Version Compatibility

ComponentMinimum VersionRecommended
Python3.8+3.11+
Node.js16+20+ (LTS)
FastMCP (Python)2.11.02.11.x (stable)
FastMCP (TypeScript)2.0.02.x (stable)
uv0.1.0+Latest
Claude DesktopAnyLatest

Production Recommendation: Pin FastMCP to specific minor version (e.g., fastmcp~=2.11.0) to avoid breaking changes.


Skill Metadata

Created: January 2026 FastMCP Version: v2.11.0 / v3.0.0-beta Languages: Python, TypeScript Integration: Claude Desktop, FastMCP Cloud Status: Production Ready

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

35.53%
按下载量换算1,317

Claude

29.42%
按下载量换算1,091

Cursor

20.66%
按下载量换算766

Gemini CLI

9.19%
按下载量换算341

安全审计

Gen Agent Trust Hub

未通过

Socket

未通过

Snyk

未通过

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills