The interface is localized. Skill descriptions and documents are machine-translated by AI into your language; code, commands, paths and technical terms stay in the original, and the author's document governs.
Explore skills
Development · SKILL.md

mcp-protocol

mcp-protocol

Implements Model Context Protocol (MCP) servers and clients using the mcp Python SDK (FastMCP, resources, tools, prompts, transports) for LLM tool integration.

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill documentation

GitHub

name: mcp-protocol description: Implements Model Context Protocol (MCP) servers and clients using the mcp Python SDK (FastMCP, resources, tools, prompts, transports) for LLM tool integration. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: mcp, model context protocol, mcp server, fastmcp, mcp tools, mcp resources, how do i build an mcp server, mcp python sdk archetypes: - tactical - generation anti_triggers: - brainstorming - vague ideation - code golf - over-engineering response_profile: verbosity: low directive_strength: high abstraction_level: operational role: implementation scope: implementation output-format: code content-types: [code, guidance, do-dont, examples] related-skills: coding-anthropic-api, coding-langchain, coding-openai-api


Model Context Protocol (MCP) Integration

Implements MCP (Model Context Protocol) servers and clients using the mcp Python SDK. When loaded, this skill makes the model build production-grade MCP servers exposing tools, resources, prompts, and notifications to LLM applications using FastMCP, with support for stdio, SSE, Streamable HTTP, and WebSocket transports.

TL;DR Checklist

  • Define tool schemas with strict JSON Schema parameter validation
  • Implement proper error handling with MCP error codes (-32601, -32603, etc.)
  • Support at least stdio transport for local integrations
  • Add resource definitions with URI templates and MIME type negotiation
  • Implement prompt templates for reusable LLM interaction patterns
  • Handle connection lifecycle (initialize, ping, close) properly
  • Add logging for all tool calls and resource accesses

When to Use

Use this skill when:

  • Building MCP servers to expose tools/resources/prompts to LLM applications (Claude Desktop, VS Code, Cursor, other AI IDEs)
  • Creating MCP clients that connect to remote servers and consume external tools
  • Integrating external APIs (REST, GraphQL, databases) as MCP tools for agent access
  • Exposing database schemas, file contents, or API responses as MCP resources with URI templates
  • Defining reusable prompt templates for common LLM interaction patterns
  • Deploying MCP servers with Streamable HTTP transport for remote access in production
  • Building custom tool integrations for Claude Desktop or other MCP-compatible hosts
  • Implementing MCP server-to-server communication via notifications

When NOT to Use

Avoid this skill for:

  • Simple one-off API calls — use direct HTTP requests instead of building an MCP server
  • Real-time data streaming with sub-millisecond latency — MCP has transport overhead
  • Non-LLM tool integrations (CI/CD, DevOps) — use standard APIs or CLIs directly

Core Workflow

  1. Define Tool Schemas — Create function definitions with strict JSON Schema parameter validation. Each tool must have: unique name, description, input schema, and return type. Checkpoint: Validate all schemas against the JSON Schema draft-07 specification before registration.

  2. Choose Transport Layer — Select transport based on deployment model: stdio for local CLI tools, SSE for long-lived server connections, Streamable HTTP for remote access with REST semantics. Checkpoint: Match transport to expected client type and network topology.

  3. Implement Resources — Define resource URIs with templates (e.g., file://{path}, db://schema/{table}), MIME type negotiation, and optional metadata. Checkpoint: Each resource must support contents array with string or blob payloads.

  4. Add Prompt Templates — Create named prompt templates with mandatory/optional message parameters for reusable LLM interaction patterns (summaries, translations, code reviews). Checkpoint: Prompts must render to valid Message[] arrays.

  5. Handle Connection Lifecycle — Implement initialize handshake, ping/keepalive, notification routing, and graceful shutdown. Checkpoint: Server must respond to initialize with proper protocol version and capabilities before accepting other requests.

  6. Test with MCP Host — Validate server against an MCP host (Claude Desktop, VS Code extension, or custom client). Checkpoint: All tools must be discoverable, callable, and return structured responses.


Implementation Patterns

Pattern 1: FastMCP Server with Tools

"""Production MCP server using FastMCP with tool registration."""
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
from typing import Optional

mcp = FastMCP("my-server")

class SearchParams(BaseModel):
    query: str = Field(description="Search query string", min_length=1)
    max_results: int = Field(default=10, ge=1, le=100)
    filter: Optional[str] = Field(default=None, description="Optional result filter")

@mcp.tool()
def search_documents(params: SearchParams) -> dict:
    """Search indexed documents by query with optional filtering."""
    # Implementation calls your search engine / database
    return {
        "results": [{"title": f"Doc {i}", "snippet": "..."} for i in range(params.max_results)],
        "total_count": 42,
        "query": params.query,
    }

# Usage: mcp.run() starts the server on stdio transport by default

Pattern 2: Resource with URI Templates and MIME Negotiation

"""MCP resources exposing dynamic content via URI templates."""
from mcp.server.fastmcp import FastMCP
from mcp.types import Resource, TextResourceContents

mcp = FastMCP("resource-server")

@mcp.resource("db://schema/{table}")
def get_table_schema(table: str) -> str:
    """Return the database schema for a given table as text/SQL."""
    schemas = {
        "users": "CREATE TABLE users (id INT PRIMARY KEY, name TEXT, email TEXT UNIQUE)",
        "orders": "CREATE TABLE orders (id INT PRIMARY KEY, user_id INT, total DECIMAL)",
    }
    schema = schemas.get(table)
    if schema is None:
        raise ValueError(f"Unknown table: {table}")
    return schema

@mcp.resource("file://{path:path}")
def read_file(path: str) -> dict:
    """Read file contents with MIME type negotiation."""
    import mimetypes
    content_type = mimetypes.guess_type(path)[0] or "application/octet-stream"
    try:
        with open(path, "rb") as f:
            data = f.read()
        return TextResourceContents(
            uri=f"file://{path}",
            text=data.decode("utf-8", errors="replace"),
            mime_type=content_type if content_type.startswith("text/") else None,
        )
    except FileNotFoundError:
        raise ValueError(f"File not found: {path}")

@mcp.resource_template("api://{service}/{endpoint}")
def api_proxy(service: str, endpoint: str) -> str:
    """Proxy external API responses as MCP resources for caching."""
    import requests
    url = f"https://{service}.example.com/{endpoint}"
    resp = requests.get(url, timeout=10)
    resp.raise_for_status()
    return resp.json()

Pattern 3: Prompt Templates for LLM Interaction Patterns

"""MCP prompt templates for reusable LLM conversation patterns."""
from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent, ImageContent, EmbeddedResource

mcp = FastMCP("prompt-server")

@mcp.prompt(
    name="code-review",
    description="Review code changes and provide structured feedback",
)
def code_review(pr_url: str, review_focus: str = "all") -> list[dict]:
    """Generate a code review prompt with context."""
    return [
        {
            "role": "system",
            "content": (
                "You are an expert code reviewer. Review the PR at "
                f"{pr_url} with focus on: {review_focus}. "
                "Provide structured feedback organized by severity."
            ),
        },
        {
            "role": "user",
            "content": f"Please review this pull request: {pr_url}",
        },
    ]

@mcp.prompt(
    name="summarize-thread",
    description="Summarize a long conversation thread into key decisions and action items",
)
def summarize_thread(thread_id: str, max_length: int = 500) -> list[dict]:
    """Generate a conversation summarization prompt."""
    return [
        {
            "role": "system",
            "content": (
                f"Summarize the conversation thread '{thread_id}' into key decisions "
                f"and action items. Keep it under {max_length} characters."
            ),
        },
    ]

Pattern 4: MCP Client for Consuming Remote Tools

"""MCP client implementation for consuming remote server tools."""
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.types import CallToolResult, TextContent as ServerTextContent

async def consume_mcp_tools(server_url: str = "localhost", server_port: int = 8080):
    """Connect to an MCP server and call its tools via Streamable HTTP."""
    # For stdio transport (local server)
    params = StdioServerParameters(
        command="python",
        args=["-m", "my_mcp_server"],
        env=None,
    )
    
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            # Initialize handshake
            await session.initialize()
            
            # List available tools
            tools = await session.list_tools()
            for tool in tools.tools:
                print(f"Tool: {tool.name} — {tool.description}")
            
            # Call a specific tool
            result = await session.call_tool("search_documents", arguments={
                "query": "production deployment issues",
                "max_results": 5,
            })
            
            for content in result.content:
                if isinstance(content, ServerTextContent):
                    print(content.text)

# For remote Streamable HTTP clients, use the httpx-based transport:
# async with httpx_streamable_client("http://server:8080/mcp") as session:
#     ...

Pattern 5: Error Handling and MCP Standard Error Codes

"""MCP error handling following the protocol specification."""
from mcp.server import Server, NotificationOptions
from mcp.types import (
    ErrorData,
    McpError,
    INITIALIZE_ERROR_CODE,
    TOOL_ERROR_CODE,
    INTERNAL_ERROR_CODE,
)

class MCPErrorHandler:
    """Centralized error handling for MCP server operations."""
    
    MCP_ERROR_CODES = {
        "PARSE_ERROR": -32700,
        "INVALID_REQUEST": -32600,
        "METHOD_NOT_FOUND": -32601,
        "INVALID_PARAMS": -32602,
        "INTERNAL_ERROR": -32603,
        "TOOL_ERROR": -32001,  # Custom range for tool execution errors
    }
    
    @staticmethod
    def handle_tool_error(error: Exception, tool_name: str) -> ErrorData:
        """Convert Python exceptions to MCP error responses."""
        if isinstance(error, ValueError):
            return ErrorData(
                code=-32602,  # INVALID_PARAMS
                message=f"Invalid arguments for tool '{tool_name}': {error}",
            )
        elif isinstance(error, FileNotFoundError):
            return ErrorData(
                code=-32602,
                message=f"Resource not found: {error}",
            )
        else:
            return ErrorData(
                code=INTERNAL_ERROR_CODE,
                message=f"Internal error in tool '{tool_name}': {type(error).__name__}: {error}",
            )
    
    @staticmethod
    def validate_json_schema(schema: dict) -> list[str]:
        """Pre-flight validation of JSON schemas before tool registration."""
        import jsonschema
        errors = list(jsonschema.Draft7Validator(schema).iter_errors({}))
        return [f"Schema error: {e.message}" for e in errors]

Constraints

MUST DO

  • Always define strict JSON Schema input parameters for every tool — never use untyped **kwargs
  • Implement proper initialize handshake before accepting any requests from clients
  • Use FastMCP's built-in parameter validation (Pydantic models) instead of manual validation
  • Handle MCP error codes per the specification: -32601 (method not found), -32602 (invalid params), -32603 (internal error)
  • Support at minimum stdio transport for all local integrations; add HTTP/SSE for remote access
  • Log all tool calls with arguments, execution time, and result size for observability
  • Implement graceful shutdown (SIGINT/SIGTERM handlers) that flushes pending operations
  • Version your MCP server capabilities in the initialize response

MUST NOT DO

  • Expose sensitive data (secrets, PII, internal URLs) through resources or tools without access control
  • Use synchronous HTTP calls inside async tool implementations — always use asyncio or run in executor
  • Bypass JSON Schema validation by accepting raw dicts from clients — all parameters must be typed
  • Implement long-running blocking operations inside tool handlers — delegate to background tasks
  • Skip the initialize handshake — clients may send arbitrary requests before initialization

Output Template

When implementing MCP servers/clients, output should contain:

  1. Server/Client Definition — Complete FastMCP or client code with transport configuration
  2. Tool Definitions — Typed Pydantic models for all tool inputs, with descriptions
  3. Resource Definitions — URI templates, MIME types, and content handlers
  4. Error Handling — MCP-standard error codes and Python exception mapping
  5. Connection Lifecycle — Initialize, ping/keepalive, shutdown handlers

Related Skills

SkillPurpose
coding-anthropic-apiIntegrates Claude API with MCP connector for agent tool access
coding-langchainLangChain framework integration that can use MCP tools as LangChain tools
coding-openai-apiOpenAI API patterns including function calling compatible with MCP tool schemas

Live References

Saved skills