Agent Skills: MCP Operations

Model Context Protocol server development, tool design, resource handling, and transport configuration. Use for: mcp, model context protocol, mcp server, mcp tool, mcp resource, fastmcp, mcp transport, stdio, sse, streamable http, mcp inspector, tool handler, mcp prompt.

UncategorizedID: 0xdarkmatter/claude-mods/mcp-ops

Install this agent skill to your local

pnpm dlx add-skill https://github.com/0xDarkMatter/claude-mods/tree/HEAD/skills/mcp-ops

Skill Files

Browse the full folder contents for mcp-ops.

Download Skill

Loading file tree…

skills/mcp-ops/SKILL.md

Skill Metadata

Name
mcp-ops
Description
"Model Context Protocol server development, tool design, resource handling, and transport configuration. Use for: mcp, model context protocol, mcp server, mcp tool, mcp resource, fastmcp, mcp transport, stdio, sse, streamable http, mcp inspector, tool handler, mcp prompt."

MCP Operations

Comprehensive patterns for building, testing, and deploying Model Context Protocol servers in Python and TypeScript.

Ecosystem facts verified as of 2026-07-05 (standalone FastMCP at major 3).

MCP Architecture Quick Reference

┌─────────────────────────────────────────────────────────┐
│                     MCP Host                            │
│  (Claude Desktop, Claude Code, Custom App)              │
│                                                         │
│  ┌───────────┐   ┌───────────┐   ┌───────────┐        │
│  │  Client A  │   │  Client B  │   │  Client C  │       │
│  └─────┬─────┘   └─────┬─────┘   └─────┬─────┘        │
└────────┼───────────────┼───────────────┼────────────────┘
         │               │               │
    ┌────┴────┐     ┌────┴────┐     ┌────┴────┐
    │Transport│     │Transport│     │Transport│
    │ (stdio) │     │  (SSE)  │     │ (HTTP)  │
    └────┬────┘     └────┬────┘     └────┬────┘
         │               │               │
┌────────┴──┐     ┌──────┴────┐   ┌──────┴────┐
│  Server A  │     │  Server B  │   │  Server C  │
│            │     │            │   │            │
│ ┌────────┐ │     │ ┌────────┐ │   │ ┌────────┐ │
│ │ Tools  │ │     │ │Resources│ │   │ │Prompts │ │
│ └────────┘ │     │ └────────┘ │   │ └────────┘ │
│ ┌────────┐ │     │ ┌────────┐ │   │ ┌────────┐ │
│ │Resources│ │     │ │Prompts │ │   │ │ Tools  │ │
│ └────────┘ │     │ └────────┘ │   │ └────────┘ │
└────────────┘     └────────────┘   └────────────┘

Protocol: JSON-RPC 2.0 over chosen transport
Flow:     Client → request → Server → response → Client

Server Type Decision Tree

What transport does your MCP server need?
│
├─ Local CLI tool / single-user desktop integration?
│  └─ stdio
│     - Simplest setup, no networking
│     - Claude Desktop, Claude Code native support
│     - Process lifecycle managed by host
│
├─ Web dashboard / browser-based client?
│  └─ SSE (Server-Sent Events)
│     - HTTP-based, works through firewalls
│     - Persistent connection for server→client events
│     - Good for development and internal tools
│
└─ Production API / multi-tenant / cloud deployment?
   └─ Streamable HTTP
      - HTTP POST for requests, SSE for streaming responses
      - Supports stateless and stateful modes
      - Full auth support, load balancer friendly
      - Recommended for production deployments

Tool vs Resource vs Prompt Decision Tree

What does the LLM need to do?
│
├─ Perform an action or computation?
│  └─ TOOL
│     - Has side effects (API calls, file writes, DB mutations)
│     - Accepts structured input, returns results
│     - Examples: run_query, create_issue, send_email
│
├─ Read data or context?
│  └─ RESOURCE
│     - Read-only data retrieval
│     - Identified by URI (file://, db://, api://)
│     - Examples: config://app, schema://users, file://readme.md
│
└─ Guide the LLM's behavior or workflow?
   └─ PROMPT
      - Templated instructions with arguments
      - Suggests conversation starters or workflows
      - Examples: code_review(language, file), summarize(topic)

Python SDK Quick Start

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-server")

@mcp.tool()
def search_docs(query: str) -> str:
    """Search documentation by keyword."""
    results = perform_search(query)
    return "\n".join(f"- {r.title}: {r.snippet}" for r in results)

@mcp.tool()
def create_ticket(title: str, body: str, priority: str = "medium") -> str:
    """Create a support ticket."""
    ticket = api.create(title=title, body=body, priority=priority)
    return f"Created ticket #{ticket.id}: {ticket.url}"

@mcp.resource("config://app")
def get_config() -> str:
    """Return current application configuration."""
    return json.dumps(load_config(), indent=2)

@mcp.resource("schema://db/{table}")
def get_table_schema(table: str) -> str:
    """Return the schema for a database table."""
    return json.dumps(get_schema(table), indent=2)

@mcp.prompt()
def code_review(language: str, filepath: str) -> str:
    """Generate a code review prompt for the given file."""
    return f"Review this {language} code in {filepath} for bugs, style issues, and performance."

if __name__ == "__main__":
    mcp.run()  # Defaults to stdio transport

Install and run:

uv init my-mcp-server && cd my-mcp-server
uv add mcp[cli]
# Run with: uv run python server.py
# Or:       uv run mcp run server.py

Two Python FastMCPs — know which you're on. The official mcp SDK bundles a frozen 1.x-era FastMCP (from mcp.server.fastmcp import FastMCP, used in the samples above — stable, minimal). The standalone fastmcp package (gofastmcp.com) is where active development happens and is at major 3: same decorator surface, plus auth, proxying, OpenAPI generation, and a test client. To use it:

uv add fastmcp
from fastmcp import FastMCP   # standalone FastMCP 3 — not mcp.server.fastmcp

mcp = FastMCP("my-server")    # v3: constructor is identity/behaviour only;
                              # transport config moved to run()/serve time

FastMCP 3 breaking changes (from 2.x): 16 deprecated constructor kwargs removed (transport settings now passed at serve time), ui= replaced by app=, ctx.set_state()/ctx.get_state() are now async with session-scoped persistence, and the metadata namespace changed from _fastmcp to fastmcp.

TypeScript SDK Quick Start

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({
  name: "my-server",
  version: "1.0.0",
});

// Register a tool
server.tool(
  "search_docs",
  "Search documentation by keyword",
  { query: z.string().describe("Search query") },
  async ({ query }) => {
    const results = await performSearch(query);
    return {
      content: [{ type: "text", text: results.join("\n") }],
    };
  }
);

// Register a resource
server.resource(
  "config",
  "config://app",
  { description: "Current application configuration" },
  async (uri) => ({
    contents: [{
      uri: uri.href,
      mimeType: "application/json",
      text: JSON.stringify(loadConfig(), null, 2),
    }],
  })
);

// Register a prompt
server.prompt(
  "code_review",
  "Generate a code review prompt",
  { language: z.string(), filepath: z.string() },
  async ({ language, filepath }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Review this ${language} code in ${filepath} for bugs and style issues.`,
      },
    }],
  })
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}
main().catch(console.error);

Install and run:

npm init -y
npm install @modelcontextprotocol/sdk zod
npx tsx server.ts

Transport Selection Matrix

| Feature | stdio | SSE | Streamable HTTP | |---------|-------|-----|-----------------| | Use case | Local CLI tools, desktop | Web dashboards, dev | Production APIs | | Protocol | stdin/stdout pipes | HTTP + EventSource | HTTP POST + SSE | | Auth support | Env vars only | Bearer tokens | Full OAuth2/PKCE | | Deployment | Local process | Single server | Load balanced | | Reconnection | Process restart | Auto-reconnect | Stateless resilient | | Multi-client | 1:1 only | Multiple clients | Horizontally scalable | | Firewall | N/A (local) | HTTP-friendly | HTTP-friendly | | State | Process lifetime | Connection lifetime | Session or stateless | | Best for | Claude Desktop/Code | Internal tools | Cloud/enterprise |

Authentication Patterns Quick Reference

# Pattern 1: API keys from environment
import os
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("api-server")

@mcp.tool()
def call_api(endpoint: str) -> str:
    """Call external API with configured credentials."""
    api_key = os.environ["MY_API_KEY"]  # Set in client config
    resp = httpx.get(f"https://api.example.com/{endpoint}",
                     headers={"Authorization": f"Bearer {api_key}"})
    return resp.text
# Pattern 2: OAuth2 token refresh (in-memory cache)
import time

_token_cache: dict = {}

async def get_valid_token() -> str:
    if _token_cache.get("expires_at", 0) > time.time() + 60:
        return _token_cache["access_token"]
    resp = await httpx.AsyncClient().post("https://auth.example.com/token", data={
        "grant_type": "refresh_token",
        "refresh_token": os.environ["REFRESH_TOKEN"],
        "client_id": os.environ["CLIENT_ID"],
    })
    data = resp.json()
    _token_cache.update({
        "access_token": data["access_token"],
        "expires_at": time.time() + data["expires_in"],
    })
    return data["access_token"]
// Claude Desktop config with env vars
{
  "mcpServers": {
    "my-server": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/server", "python", "server.py"],
      "env": {
        "MY_API_KEY": "sk-...",
        "DATABASE_URL": "postgresql://..."
      }
    }
  }
}

Common Gotchas

| Gotcha | Why | Fix | |--------|-----|-----| | Tool not appearing in client | inputSchema has invalid JSON Schema | Validate schema with jsonschema library; use Pydantic/Zod to generate | | Tool returns raw object | Results must be content list with typed items | Always return {"content": [{"type": "text", "text": "..."}]} | | Timeout on long operations | Default client timeout is often 30-60s | Add progress notifications; break into smaller operations | | Concurrent requests fail | Tool handler uses shared mutable state | Use asyncio locks, or make handlers stateless | | Large response crashes client | MCP messages have practical size limits | Paginate results; return summaries with detail-fetch tools | | Error swallowed silently | Exception in handler returns generic error | Set isError: true in response; include error message in content | | SSE connection drops | No keep-alive or reconnection logic | Implement heartbeat; client auto-reconnects on SSE | | Client ignores new tools | Capabilities not updated after tool change | Call server.request_context.session.send_resource_list_changed() | | Tool name collision | Two servers register same tool name | Namespace tools: myserver_search not just search | | Resource URI too generic | data://info is ambiguous | Use specific schemes: db://myapp/users, config://myapp/settings | | async def missing on handler | FastMCP tools can be sync or async, but I/O should be async | Use async def for any handler doing network/file I/O | | Server works locally, fails in Claude Desktop | Different working directory or PATH | Use absolute paths; log os.getcwd() on startup |

Reference Files

| File | Lines | Content | |------|-------|---------| | references/server-architecture.md | ~700 | Server lifecycle, FastMCP/TS SDK setup, capabilities, middleware, error handling | | references/tool-handlers.md | ~650 | Schema design, validation, return types, composition, side effects, examples | | references/resources-prompts.md | ~550 | Resource URIs, static/dynamic resources, templates, prompts, subscriptions | | references/transport-auth.md | ~550 | stdio/SSE/HTTP transports, session management, OAuth2, rate limiting, TLS | | references/testing-debugging.md | ~550 | MCP Inspector, unit/integration testing, protocol debugging, CI, performance |

Staleness verifier

This skill encodes fast-moving facts (the MCP SDK package names + spec URL). scripts/check-mcp-facts.py guards them against silent drift:

# Structural (PR CI, no network): every catalogued package's prose_token is
# still named in this skill's prose, the spec URL is still cited, and the
# currency note still carries a year.
python scripts/check-mcp-facts.py --offline        # exit 0 consistent, 10 drift

# Live (freshness job, never blocks a PR): each SDK still resolves on
# npm/PyPI, no tracked major has moved off the sampled major, spec URL 200.
python scripts/check-mcp-facts.py --live            # exit 10 drift, 7 registries unreachable

The canonical fact set lives in assets/mcp-facts.json; when you add or drop a package, update it to match or --offline fails CI.

See Also

  • MCP Specification: https://modelcontextprotocol.io/specification/latest (the old spec.modelcontextprotocol.io subdomain no longer resolves)
  • Python SDK: https://github.com/modelcontextprotocol/python-sdk
  • TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
  • Official MCP Servers: https://github.com/modelcontextprotocol/servers
  • MCP Inspector: npx @modelcontextprotocol/inspector
  • FastMCP Documentation: https://gofastmcp.com
  • Related skills: claude-code-hooks (hook into Claude Code), claude-code-debug (debug Claude Code issues)