AgentStack
SKILL verified MIT Self-run

Fastmcp Mcp Builder

skill-allenli178-yuyan-skills-fastmcp-mcp-builder · by allenli178

Guide for creating high-quality MCP (Model Context Protocol) servers using FastMCP, the Pythonic MCP framework. Use when building MCP servers to integrate external APIs or services with tools, resources, and prompts using FastMCP decorators. Includes advanced features like structured outputs, tag-based filtering, dependency injection, custom routes, and CLI development workflows.

No reviews yet
0 installs
3 views
0.0% view→install

Install

$ agentstack add skill-allenli178-yuyan-skills-fastmcp-mcp-builder

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Fastmcp Mcp Builder? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

FastMCP Server Development Guide

Overview

Create MCP (Model Context Protocol) servers using FastMCP, the standard Pythonic framework for building MCP applications. FastMCP provides a clean, Pythonic API for registering tools, resources, and prompts, with automatic schema generation and transport management. FastMCP 1.0 was incorporated into the official MCP Python SDK in 2024, and today some version of FastMCP powers 70% of MCP servers across all languages.


Process

🚀 High-Level Workflow

Creating a high-quality FastMCP server involves four main phases:

Phase 1: Deep Research and Planning

1.1 Understand FastMCP Design

Three Pillars of FastMCP:

  • Servers: Wrap your Python functions into MCP-compliant tools, resources, and prompts
  • Clients: Connect to any server with full protocol support
  • Apps: Give your tools interactive UIs rendered directly in the conversation

Tools, Resources, and Prompts: FastMCP servers expose three types of capabilities:

  • Tools: Server-side functions that clients can execute with arguments
  • Resources: Data sources that clients can read (static or template-based)
  • Prompts: Reusable message templates that guide LLM interactions

Balance comprehensive API coverage with specialized workflow tools. When uncertain, prioritize comprehensive API coverage.

Component Naming:

  • Tools: snake_case with service prefix (e.g., github_create_issue, slack_send_message)
  • Resources: URI-based with template parameters (e.g., user://{user_id}/profile)
  • Prompts: snake_case function names (e.g., analyze_data)

Context Management: FastMCP provides a Context object for logging, progress reporting, resource access, and LLM sampling. Use it to enhance tool capabilities.

Actionable Error Messages: Error messages should guide agents toward solutions with specific suggestions and next steps.

1.2 Study FastMCP Documentation

Key Resources: FastMCP documentation is available in multiple LLM-friendly formats:

  • MCP Server: Accessible at https://gofastmcp.com/mcp
  • Text Formats: llms.txt (sitemap), llms-full.txt (complete docs)
  • Markdown: Any page can be accessed by appending .md (e.g., https://gofastmcp.com/getting-started/welcome.md)

Load these documentation pages as needed:

  • Welcome & Quickstart: Use WebFetch to load https://gofastmcp.com/getting-started/welcome and https://gofastmcp.com/getting-started/quickstart
  • Server Guide: https://gofastmcp.com/servers/server
  • Tools: https://gofastmcp.com/servers/tools
  • Resources: https://gofastmcp.com/servers/resources
  • Prompts: https://gofastmcp.com/servers/prompts
  • Running Servers: https://gofastmcp.com/deployment/running-server
1.3 Plan Your Implementation

Understand the API: Review the service's API documentation to identify key endpoints, authentication requirements, and data models. Use web search and WebFetch as needed.

Capability Selection: Prioritize comprehensive API coverage. List which endpoints to implement as tools, which data to expose as resources, and what prompt templates would be useful.


Phase 2: Implementation

2.1 Set Up Project Structure

Create the following structure for FastMCP servers:

{service}_mcp/
├── {service}_mcp.py    # Main entry point with FastMCP initialization
├── requirements.txt     # Dependencies (fastmcp)
└── README.md            # Documentation

For larger projects, you can organize into modules:

{service}_mcp/
├── __init__.py
├── main.py              # FastMCP server initialization
├── tools/               # Tool implementations
│   ├── __init__.py
│   ├── users.py
│   └── projects.py
├── resources/           # Resource definitions
│   ├── __init__.py
│   └── data.py
├── prompts/             # Prompt templates
│   ├── __init__.py
│   └── analysis.py
├── requirements.txt
└── README.md
2.2 Implement Core Infrastructure
Server Initialization
from fastmcp import FastMCP

# Basic initialization
mcp = FastMCP(name="service_name_mcp")

# With instructions for clients
mcp = FastMCP(
    name="HelpfulAssistant",
    instructions="""
        This server provides data analysis tools.
        Call get_average() to analyze numerical data.
    """,
)

# With security and validation settings
mcp = FastMCP(
    name="SecureServer",
    strict_input_validation=False,  # Default: flexible type coercion
    mask_error_details=True,        # Mask internal error details
    on_duplicate_tools="error",     # "warn", "error", "replace", "ignore"
    on_duplicate_resources="error"  # Same options as tools
)

# Add tools, resources, and prompts here...

if __name__ == "__main__":
    mcp.run()  # Uses stdio transport by default
Tag-Based Filtering

New in version 2.8.0, tags let you categorize components and selectively expose them:

from fastmcp import FastMCP

mcp = FastMCP()

@mcp.tool(tags={"public", "utility"})
def public_tool() -> str:
    return "This tool is public"

@mcp.tool(tags={"internal", "admin"})
def admin_tool() -> str:
    return "This tool is for admins only"

# Only expose components tagged with "public"
mcp.enable(tags={"public"}, only=True)

# Hide components tagged as "internal" or "deprecated"
mcp.disable(tags={"internal", "deprecated"})

# Combine both: show admin tools but hide deprecated ones
mcp.enable(tags={"admin"}, only=True).disable(tags={"deprecated"})
Shared Utilities

Create shared utilities for common operations:

  • API client with authentication
  • Error handling helpers
  • Response formatting
  • Pagination support
2.3 Implement Tools

Tools are Python functions decorated with @mcp.tool. FastMCP automatically:

  • Uses the function name as the tool name
  • Uses the function's docstring as the tool description
  • Generates an input schema from function parameters and type annotations
  • Handles parameter validation and error reporting

Basic Tool Pattern:

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

Tool with Custom Name and Annotations:

@mcp.tool(
    name="service_search_users",
    description="Search for users in the service by name or email",
    tags={"users", "search"},
    meta={"version": "1.2", "author": "product-team"},
    annotations={
        "title": "Search Users",
        "readOnlyHint": True,
        "destructiveHint": False,
        "idempotentHint": True,
        "openWorldHint": True
    },
    timeout=30.0  # New in 3.0.0: Tool timeout in seconds
)
async def search_users(query: str, limit: int = 20) -> list[dict]:
    """Search for users with pagination support."""
    # Implementation here
    pass

Simple Parameter Descriptions (New in 2.11.0):

from typing import Annotated

@mcp.tool
def process_image(
    image_url: Annotated[str, "URL of the image to process"],
    resize: Annotated[bool, "Whether to resize the image"] = False,
    width: Annotated[int, "Target width in pixels"] = 800,
    format: Annotated[str, "Output image format"] = "jpeg"
) -> dict:
    """Process an image with optional resizing."""
    # Implementation...
    pass

Advanced Metadata with Field:

from typing import Annotated
from pydantic import Field
from typing import Literal

@mcp.tool
def process_image(
    image_url: Annotated[str, Field(description="URL of the image to process")],
    resize: Annotated[bool, Field(description="Whether to resize the image")] = False,
    width: Annotated[int, Field(description="Target width in pixels", ge=1, le=2000)] = 800,
    format: Annotated[
        Literal["jpeg", "png", "webp"], 
        Field(description="Output image format")
    ] = "jpeg"
) -> dict:
    """Process an image with optional resizing."""
    # Implementation...
    pass

Dependency Injection (New in 2.14.0):

from fastmcp import FastMCP
from fastmcp.dependencies import Depends

mcp = FastMCP()

def get_user_id() -> str:
    return "user_123"  # Injected at runtime

@mcp.tool
def get_user_details(user_id: str = Depends(get_user_id)) -> str:
    # user_id is injected by the server, not provided by the LLM
    return f"Details for {user_id}"

Tool with Context:

from fastmcp import FastMCP, Context

@mcp.tool
async def process_data(data_uri: str, ctx: Context) -> dict:
    """Process data from a resource with progress reporting."""
    await ctx.info(f"Processing data from {data_uri}")
    
    # Read a resource
    resource = await ctx.read_resource(data_uri)
    data = resource[0].content if resource else ""
    
    # Report progress
    await ctx.report_progress(progress=50, total=100)
    
    # Example request to the client's LLM for help
    summary = await ctx.sample(f"Summarize this in 10 words: {data[:200]}")
    
    await ctx.report_progress(progress=100, total=100)
    return {
        "length": len(data),
        "summary": summary.text
    }

Structured Output (New in 2.10.0):

FastMCP automatically creates structured outputs when you add return type annotations:

from dataclasses import dataclass

@dataclass
class UserData:
    id: str
    name: str
    email: str

@mcp.tool
def get_user(user_id: str) -> UserData:
    """Get user data with structured output."""
    return UserData(id=user_id, name="Alice", email="alice@example.com")

ToolResult for Full Control:

from fastmcp.tools.tool import ToolResult
from mcp.types import TextContent

@mcp.tool
def advanced_tool() -> ToolResult:
    """Tool with full control over output."""
    return ToolResult(
        content=[TextContent(type="text", text="Human-readable summary")],
        structured_content={"data": "value", "count": 42},
        meta={"execution_time_ms": 145}
    )

Error Handling:

from fastmcp import FastMCP
from fastmcp.exceptions import ToolError

mcp = FastMCP(name="SecureServer", mask_error_details=True)

@mcp.tool
def divide(a: float, b: float) -> float:
    """Divide a by b."""
    if b == 0:
        # Error messages from ToolError are always sent to clients
        raise ToolError("Division by zero is not allowed.")
    return a / b

Using with Methods:

from fastmcp import FastMCP
from fastmcp.tools import tool

class Calculator:
    def __init__(self, multiplier: int):
        self.multiplier = multiplier

    @tool()
    def multiply(self, x: int) -> int:
        """Multiply x by the instance multiplier."""
        return x * self.multiplier

calc = Calculator(multiplier=3)
mcp = FastMCP()
mcp.add_tool(calc.multiply)  # Registers with correct schema

Tool Decorator Parameters:

  • name: Explicit tool name (overrides function name)
  • description: Explicit description (overrides docstring)
  • tags: Set of strings for categorizing tools
  • meta: Custom metadata dictionary
  • annotations: Tool behavior hints (readOnlyHint, destructiveHint, idempotentHint, openWorldHint, title)
  • timeout: Tool timeout in seconds (new in 3.0.0)
  • output_schema: Custom JSON schema for output
  • version: Component version for versioning
2.4 Implement Resources

Resources are data sources exposed via URIs. Use the @mcp.resource decorator.

Static Resource:

@mcp.resource("config://app_settings")
def get_app_settings() -> dict:
    """Returns application configuration."""
    return {"version": "1.0.0", "theme": "dark"}

Resource with Custom Configuration:

@mcp.resource(
    uri="data://app-status",
    name="ApplicationStatus",
    description="Provides the current status of the application.",
    mime_type="application/json",
    tags={"monitoring", "status"},
    meta={"version": "2.1", "team": "infrastructure"},
    annotations={
        "readOnlyHint": True,
        "idempotentHint": True
    }
)
def get_application_status() -> str:
    """Provides application status."""
    return json.dumps({"status": "ok", "uptime": 12345})

Resource Template (Dynamic):

@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> dict:
    """Retrieves a user's profile by ID."""
    return {
        "id": user_id,
        "name": f"User {user_id}",
        "status": "active"
    }

Multiple Parameters with Annotations:

@mcp.resource(
    "repos://{owner}/{repo}/info",
    annotations={
        "readOnlyHint": True,
        "idempotentHint": True
    }
)
def get_repo_info(owner: str, repo: str) -> dict:
    """Retrieves information about a repository."""
    return {
        "owner": owner,
        "name": repo,
        "full_name": f"{owner}/{repo}"
    }

Wildcard Parameters (New in 2.2.4):

@mcp.resource("path://{filepath*}")
def get_path_content(filepath: str) -> str:
    """Retrieves content at a specific path."""
    # Can match path://docs/server/resources.mdx
    return f"Content at path: {filepath}"

Query Parameters (New in 2.13.0):

@mcp.resource("data://{id}{?format}")
def get_data(id: str, format: str = "json") -> str:
    """Retrieve data in specified format."""
    if format == "xml":
        return f""
    return f'{{"id": "{id}"}}'

@mcp.resource("api://{endpoint}{?version,limit,offset}")
def call_api(endpoint: str, version: int = 1, limit: int = 10, offset: int = 0) -> dict:
    """Call API endpoint with pagination."""
    return {
        "endpoint": endpoint,
        "version": version,
        "limit": limit,
        "offset": offset
    }

ResourceResult for Full Control (New in 3.0.0):

from fastmcp import FastMCP
from fastmcp.resources import ResourceResult, ResourceContent

@mcp.resource("data://users")
def get_users() -> ResourceResult:
    return ResourceResult(
        contents=[
            ResourceContent(content='[{"id": 1}]', mime_type="application/json"),
            ResourceContent(content="# Users\n...", mime_type="text/markdown"),
        ],
        meta={"total": 1}
    )

Resource Classes:

from pathlib import Path
from fastmcp import FastMCP
from fastmcp.resources import FileResource, TextResource, DirectoryResource

mcp = FastMCP(name="DataServer")

# Exposing a static file directly
readme_path = Path("./README.md").resolve()
if readme_path.exists():
    readme_resource = FileResource(
        uri=f"file://{readme_path.as_posix()}",
        path=readme_path,
        name="README File",
        description="The project's README.",
        mime_type="text/markdown",
        tags={"documentation"}
    )
    mcp.add_resource(readme_resource)

# Exposing simple, predefined text
notice_resource = TextResource(
    uri="resource://notice",
    name="Important Notice",
    text="System maintenance scheduled for Sunday.",
    tags={"notification"}
)
mcp.add_resource(notice_resource)

Resource Decorator Parameters:

  • uri: Required URI (e.g., "resource://my-resource" or "resource://{param}")
  • name: Optional name
  • description: Optional description
  • mime_type: Optional MIME type
  • tags: Optional tags
  • annotations: Optional behavior hints
  • meta: Optional metadata
2.5 Implement Prompts

Prompts are reusable message templates. Use the @mcp.prompt decorator.

Basic Prompt Returning String:

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

Prompt with Custom Configuration:

@mcp.prompt(
    name="data_analysis_request",
    description="Generates a prompt for analyzing numerical data",
    tags={"analysis", "data"}
)
def analyze_data_prompt(data_points: list[float], context: str = "") -> str:
    """Generates a data analysis request."""
    formatted_data = ", ".join(str(point) for point in data_points)
    prompt = f"Please analyze these data poi

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [allenli178](https://github.com/allenli178)
- **Source:** [allenli178/yuyan-skills](https://github.com/allenli178/yuyan-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.