Learn-skills.dev mcp-ops
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.
install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/0xdarkmatter/claude-mods/mcp-ops" ~/.claude/skills/neversight-learn-skills-dev-mcp-ops && rm -rf "$T"
manifest:
data/skills-md/0xdarkmatter/claude-mods/mcp-ops/SKILL.mdsource content
MCP Operations
Comprehensive patterns for building, testing, and deploying Model Context Protocol servers in Python and TypeScript.
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
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 | has invalid JSON Schema | Validate schema with jsonschema library; use Pydantic/Zod to generate |
| Tool returns raw object | Results must be list with typed items | Always return |
| 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 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 |
| Tool name collision | Two servers register same tool name | Namespace tools: not just |
| Resource URI too generic | is ambiguous | Use specific schemes: , |
missing on handler | FastMCP tools can be sync or async, but I/O should be async | Use for any handler doing network/file I/O |
| Server works locally, fails in Claude Desktop | Different working directory or PATH | Use absolute paths; log on startup |
Reference Files
| File | Lines | Content |
|---|---|---|
| ~700 | Server lifecycle, FastMCP/TS SDK setup, capabilities, middleware, error handling |
| ~650 | Schema design, validation, return types, composition, side effects, examples |
| ~550 | Resource URIs, static/dynamic resources, templates, prompts, subscriptions |
| ~550 | stdio/SSE/HTTP transports, session management, OAuth2, rate limiting, TLS |
| ~550 | MCP Inspector, unit/integration testing, protocol debugging, CI, performance |
See Also
- MCP Specification: https://spec.modelcontextprotocol.io
- 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:
(hook into Claude Code),claude-code-hooks
(debug Claude Code issues)claude-code-debug