Back to AI App Dev Series

Anthropic SDK Track Part 7: MCP Servers & Built-In Tools

May 22, 2026 Wasil Zafar 45 min read

Connect Claude to external systems via the Model Context Protocol (MCP) — build and configure MCP servers, understand resource types (tools, prompts, resources), use built-in tools (Read, Write, Bash, WebFetch), and leverage transport layers for production deployments.

CCA Domain 2 · 23% Tasks 2.3, 2.4

Table of Contents

  1. MCP Architecture
  2. Built-In Tools
  3. Building MCP Servers
  4. Dynamic Tool Discovery
  5. Remote MCP & Tunnels
  6. Agent SDK: MCP & Tool Search
  7. Skills + MCP: The Knowledge Layer
What You’ll Learn: MCP (Model Context Protocol) lets your agent connect to external services — databases, APIs, file systems — through a standardized protocol. Instead of building custom tools for every service, you connect to MCP servers that expose capabilities. Think of it like USB: any device that speaks the protocol just works, no custom drivers needed.

1. MCP Architecture

The Model Context Protocol (MCP) is an open standard that connects AI agents to external systems. Instead of hard-coding tool implementations, MCP provides a standardized interface for tool discovery, invocation, and result handling. Claude Code uses MCP natively — every external integration (file I/O, web search, databases) is an MCP server.

MCP Architecture: Host → Client → Server
flowchart LR
    H["Host (Claude Code)"] --> C1["MCP Client"]
    C1 -->|"stdio"| S1["MCP Server: Filesystem"]
    C1 -->|"stdio"| S2["MCP Server: Database"]
    C1 -->|"HTTP"| S3["MCP Server: API Gateway"]
    S1 -->|"tools/list"| C1
    S2 -->|"tools/call"| C1
    S3 -->|"resources/read"| C1
                        

1.1 MCP Resource Types

MCP servers expose three types of capabilities:

TypePurposeExampleHow Agent Uses
ToolsActions the model can invokesearch_docs, create_fileModel calls via tool_use blocks
ResourcesRead-only data the model can accessFile contents, database schemasAttached as context in prompts
PromptsReusable prompt templatesCode review template, analysis frameworkUser selects; expands into messages
from mcp.server import Server
from mcp.types import Tool, Resource, TextContent

# MCP server exposing all three resource types
server = Server("my-tools")

# Tools — model invokes these autonomously
@server.tool()
async def search_knowledge_base(query: str, max_results: int = 5) -> str:
    """Search internal documentation by keyword query.
    Returns matching documents with titles and excerpts."""
    results = await db.search(query, limit=max_results)
    return "\n".join(f"- {r.title}: {r.excerpt}" for r in results)

# Resources — read-only context the model can access
@server.resource("schema://database/tables")
async def get_db_schema() -> str:
    """Returns the current database schema for context."""
    return await db.get_schema_ddl()

# Prompts — reusable templates
@server.prompt("code-review")
async def code_review_prompt(language: str, focus: str = "bugs") -> list:
    """Template for structured code review."""
    return [
        {"role": "user", "content": f"Review this {language} code for {focus}. "
         "Structure your review as: 1) Critical issues 2) Improvements 3) Positive patterns"}
    ]

1.2 Transport Layers

MCP supports two transport mechanisms for communication between clients and servers:

CCA Task 2.3: The exam tests understanding of MCP transport types: stdio (local process, JSON-RPC over stdin/stdout) and Streamable HTTP (remote server, HTTP-based JSON-RPC). Stdio is for local development; HTTP is for shared/production servers.
import json

# Transport 1: stdio (local process)
# - MCP server runs as a child process
# - Communication via stdin/stdout (JSON-RPC 2.0)
# - Best for: local development, single-user tools
# - Config: {"command": "python", "args": ["server.py"]}

# Transport 2: Streamable HTTP (remote)
# - MCP server runs as an HTTP service
# - Communication via HTTP POST (JSON-RPC 2.0)
# - Best for: shared servers, production, multi-user
# - Config: {"url": "https://mcp.example.com/v1"}

# JSON-RPC message format (same for both transports)
request = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "search_knowledge_base",
        "arguments": {"query": "refund policy", "max_results": 3}
    }
}

response = {
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "content": [
            {"type": "text", "text": "- Refund Policy v3: Full refunds within 30 days..."}
        ]
    }
}

2. Built-In Tools

Claude Code ships with built-in tools that are always available without additional MCP server configuration. These cover the fundamental operations an agent needs for file manipulation, command execution, and web access.

2.1 Read & Write Tools

# Built-in Read tool — reads file contents
# Name: "Read"
# The agent uses this to inspect files before editing
read_input = {
    "file_path": "/path/to/file.py",
    "offset": 0,        # Start line (0-indexed)
    "limit": 100        # Number of lines to read
}
# Returns: file content as text

# Built-in Write tool — creates or overwrites files
# Name: "Write"
# The agent uses this after planning edits
write_input = {
    "file_path": "/path/to/file.py",
    "content": "import os\n\ndef main():\n    print('Hello')\n"
}
# Returns: confirmation of write

# Built-in Edit tool — surgical line-level edits
# Name: "Edit"
# Replaces specific lines without rewriting entire file
edit_input = {
    "file_path": "/path/to/file.py",
    "old_string": "def main():\n    print('Hello')",
    "new_string": "def main():\n    print('Hello, World!')"
}
# Returns: confirmation with diff preview

2.2 Bash Tool

# Built-in Bash tool — executes shell commands
# Name: "Bash"
# The agent uses this for: running tests, installing packages,
# git operations, compilation, and system inspection

bash_input = {
    "command": "cd /project && python -m pytest tests/ -v --tb=short",
    "timeout": 30000  # Timeout in ms (safety cap)
}
# Returns: stdout + stderr combined output

# Security considerations (CCA Task 2.4):
# - Commands run in a sandboxed environment
# - Network access may be restricted
# - File system access scoped to workspace
# - Destructive commands (rm -rf) require approval in interactive mode

2.3 WebFetch Tool

# Built-in WebFetch tool — retrieves web content
# Name: "WebFetch"
# The agent uses this for: reading documentation, checking APIs,
# verifying public information

fetch_input = {
    "url": "https://docs.anthropic.com/en/docs/agents",
    "prompt": "Extract the main concepts about agentic loops"
}
# Returns: extracted/summarized content from the URL

# Key behaviors:
# - Respects robots.txt
# - Returns text content (HTML stripped)
# - Can extract specific information via prompt parameter
# - Timeout limits prevent hanging on slow sites
CCA Task 2.4 — Tool Permissions: Built-in tools have permission levels. In Claude Code’s interactive mode: Read is always allowed, Write/Edit require user approval (first time), Bash requires approval per-command. In headless/CI mode: permissions are configured via allowedTools in settings. The exam tests understanding of when tools require approval vs. auto-execute.
Real-World Application

Enterprise Knowledge Base Agent

A consulting firm connected their Claude agent to MCP servers for Notion (documents), Slack (messages), and PostgreSQL (client data). The agent can answer questions like “What did we discuss with Client X last week?” by searching across all three systems through unified MCP tool calls. The unified protocol eliminated 3 months of custom integration work and enabled adding new data sources in hours instead of weeks.

Multi-Source SearchEnterpriseMCP Integration

3. Building MCP Servers

3.1 Python MCP Server

Building a custom MCP server lets you expose any backend system to Claude. Here is a complete example using the official mcp Python SDK:

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent
import asyncio
import json

# Create server instance
server = Server("customer-support")

@server.tool()
async def get_customer(identifier: str) -> str:
    """Look up a customer by email or ID.
    Returns: customer profile with name, plan, status, and verification state.
    Use this FIRST before any order or refund operations."""

    # In production, this calls your actual customer database
    customer = await db.customers.find_one({"email": identifier})
    if not customer:
        return json.dumps({"error": "Customer not found", "type": "not_found"})

    return json.dumps({
        "customer_id": customer["id"],
        "name": customer["name"],
        "plan": customer["plan"],
        "status": customer["status"],
        "verified": True
    })

@server.tool()
async def process_refund(order_id: str, customer_id: str, amount: float, reason: str) -> str:
    """Process a refund for a verified customer's order.
    Maximum $500 without escalation. Returns transaction confirmation."""

    if amount > 500:
        return json.dumps({
            "error": "Amount exceeds limit",
            "type": "limit_exceeded",
            "suggestion": "Escalate to human for refunds over $500"
        })

    result = await payments.refund(order_id, amount, reason)
    return json.dumps({
        "success": True,
        "transaction_id": result.id,
        "amount_refunded": amount
    })

# Run with stdio transport
async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream)

if __name__ == "__main__":
    asyncio.run(main())

3.2 MCP Server Configuration

MCP servers are configured in Claude Code’s settings file (.claude/settings.json or project-level .mcp.json):

{
  "mcpServers": {
    "customer-support": {
      "command": "python",
      "args": ["./mcp-servers/customer-support/server.py"],
      "env": {
        "DATABASE_URL": "postgresql://localhost:5432/support"
      }
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "remote-api": {
      "url": "https://mcp.internal.company.com/v1",
      "headers": {
        "Authorization": "Bearer ${API_TOKEN}"
      }
    }
  }
}
# Installing community MCP servers
# GitHub server (official)
npx -y @modelcontextprotocol/server-github

# Filesystem server (official)
npx -y @modelcontextprotocol/server-filesystem /path/to/allowed/dir

# PostgreSQL server
npx -y @modelcontextprotocol/server-postgres postgresql://localhost/mydb

# Custom Python server
pip install mcp
python my_server.py

4. Dynamic Tool Discovery

MCP enables dynamic tool discovery — the agent learns what tools are available at runtime by querying connected MCP servers. This is fundamentally different from static tool lists hardcoded in the application.

import json

# MCP tool discovery protocol
# 1. Client sends tools/list request to each connected server
discovery_request = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
}

# 2. Server responds with available tools + schemas
discovery_response = {
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "tools": [
            {
                "name": "get_customer",
                "description": "Look up a customer by email or ID...",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "identifier": {"type": "string", "description": "Email or customer ID"}
                    },
                    "required": ["identifier"]
                }
            },
            {
                "name": "process_refund",
                "description": "Process a refund for a verified customer...",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "order_id": {"type": "string"},
                        "customer_id": {"type": "string"},
                        "amount": {"type": "number"},
                        "reason": {"type": "string"}
                    },
                    "required": ["order_id", "customer_id", "amount", "reason"]
                }
            }
        ]
    }
}

# 3. Agent's tool list is assembled dynamically from all connected servers
# This means: adding a new MCP server instantly gives the agent new capabilities
# without changing any agent code
CCA Exam Pattern

MCP Questions on the CCA

The exam tests: (1) which transport to use for local vs. remote servers, (2) the difference between tools, resources, and prompts, (3) how tool discovery works at runtime, (4) permission models for built-in tools. It does NOT test MCP server implementation details — focus on the architectural concepts.

CCA Task 2.3CCA Task 2.4
Try It Yourself: Set up a local MCP server using the filesystem server (npx @modelcontextprotocol/server-filesystem). Connect your Claude agent to it. Then have the agent: (1) list files in a directory, (2) read a specific file, (3) create a summary of all .py files found. Verify the agent correctly uses the MCP tools.

5. Remote MCP Servers & Tunnels (CCA 7.2)

Local MCP servers (stdio transport) are great for development, but production agents need to access tools hosted on remote infrastructure — APIs running in the cloud, shared services across teams, or enterprise tools behind firewalls. Remote MCP uses HTTP transport and tunnels to bridge these gaps.

Analogy: Local MCP is like having tools on your desk — fast and private. Remote MCP is like calling a specialist in another office — you need a phone line (HTTP) or a secure tunnel (for firewalled services).

5.1 Transport Types: stdio vs HTTP

import json

# Transport 1: stdio (LOCAL)
# - MCP server runs as a child process on the same machine
# - Communication: stdin/stdout with JSON-RPC 2.0
# - Best for: development, single-user, tools that need local filesystem access
# - Config example:
stdio_config = {
    "my-local-tool": {
        "command": "python",
        "args": ["mcp_server.py"],
        "env": {"DATABASE_URL": "sqlite:///local.db"}
    }
}

# Transport 2: Streamable HTTP (REMOTE)
# - MCP server runs as an HTTP service (anywhere: cloud, kubernetes, another machine)
# - Communication: HTTP POST with JSON-RPC 2.0
# - Best for: shared tools, production, multi-user, enterprise
# - Config example:
http_config = {
    "shared-crm-tool": {
        "url": "https://mcp.internal.company.com/crm",
        "headers": {
            "Authorization": "Bearer ${MCP_AUTH_TOKEN}"
        }
    }
}

# When to use which:
# stdio: development, filesystem tools, single-user
# HTTP: production, shared services, team tools, enterprise APIs

print("stdio: local process, fast, private, needs local access")
print("HTTP: remote service, shareable, scalable, needs network")

5.2 MCP Tunnels (Firewall Bridging)

import json

# MCP Tunnels solve: "My MCP server is behind a firewall/VPN,
# but my agent (or Anthropic's hosted agent) needs to reach it."

# A tunnel creates a secure connection FROM your internal network
# TO Anthropic's infrastructure — no inbound firewall rules needed.

# Tunnel setup (conceptual — actual commands vary by deployment):

# 1. QUICKSTART (Docker)
# Run the tunnel agent on any machine inside your network:
docker_command = """
docker run -d \\
  --name mcp-tunnel \\
  -e TUNNEL_TOKEN=tun_abc123... \\
  -e MCP_SERVER_URL=http://internal-crm:8080 \\
  anthropic/mcp-tunnel:latest
"""

# 2. HELM (Kubernetes)
helm_values = """
# values.yaml for MCP tunnel Helm chart
tunnel:
  token: tun_abc123...
  targets:
    - name: internal-crm
      url: http://crm-service.default.svc:8080
    - name: internal-jira
      url: http://jira.internal:8080
  replicas: 2  # HA: run multiple tunnel instances
"""

# 3. DOCKER COMPOSE (multi-service)
compose_config = """
services:
  mcp-tunnel:
    image: anthropic/mcp-tunnel:latest
    environment:
      TUNNEL_TOKEN: ${TUNNEL_TOKEN}
    extra_hosts:
      - "internal-api:host-gateway"
    restart: unless-stopped
"""

# How tunnels work:
# 1. Tunnel agent connects OUTBOUND to Anthropic (no firewall changes)
# 2. Anthropic's agent infrastructure routes tool calls through the tunnel
# 3. Tunnel agent forwards requests to your internal MCP server
# 4. Response travels back through the same tunnel

# Security:
# - All traffic is TLS encrypted
# - Tunnel tokens are scoped to specific MCP servers
# - No inbound ports needed (outbound-only connection)
# - Tunnel certificates can be pinned for zero-trust environments

print("Tunnels: outbound-only connections, no firewall changes needed")
print("Deploy via: Docker, Helm, Docker Compose, or standalone binary")

5.3 MCP Configuration: Project vs User Scope (CCA 7.3)

import json

# MCP server configuration has TWO scopes:
# 1. Project scope (.mcp.json) — committed to repo, shared with team
# 2. User scope (~/.claude.json) — personal, not committed

# PROJECT SCOPE: .mcp.json (in project root)
# Shared with all team members via version control
project_mcp = {
    "servers": {
        "project-db": {
            "command": "python",
            "args": ["tools/db_server.py"],
            "env": {
                "DATABASE_URL": "${DATABASE_URL}"  # Env var expansion!
            }
        },
        "shared-api": {
            "url": "https://mcp.team-tools.internal/api",
            "headers": {
                "Authorization": "Bearer ${TEAM_API_TOKEN}"
            }
        }
    }
}

# USER SCOPE: ~/.claude.json (personal machine only)
# For tools only YOU need (personal assistants, private APIs)
user_mcp = {
    "servers": {
        "my-notes": {
            "command": "node",
            "args": ["/home/user/tools/notes-server.js"]
        },
        "personal-calendar": {
            "url": "https://my-calendar-mcp.vercel.app",
            "headers": {
                "Authorization": "Bearer ${MY_CALENDAR_TOKEN}"
            }
        }
    }
}

# KEY FEATURES:
# 1. Environment variable expansion: ${VAR_NAME} is replaced at runtime
# 2. Multi-server simultaneous access: agent sees tools from ALL configured servers
# 3. Server isolation: each server's tools are namespaced (no name collisions)
# 4. Project + User merge: both scopes active simultaneously

# PRECEDENCE:
# If same server name in both scopes: project scope wins
# Tools from all servers are merged into one tool list for the agent

# Community vs Custom servers:
# Community: pre-built servers for common services (GitHub, Slack, Jira, etc.)
# Custom: your own servers for internal tools (build with mcp SDK)

print("Project scope (.mcp.json): shared with team, version controlled")
print("User scope (~/.claude.json): personal tools, not committed")
print("Both active simultaneously — agent sees merged tool list")
CCA Exam Pattern (7.2, 7.3): Questions test: (1) Tunnels use outbound-only connections (no firewall changes). (2) Project scope (.mcp.json) is shared via git; user scope (~/.claude.json) is personal. (3) ${VAR_NAME} syntax enables environment variable expansion in configs. (4) Multi-server access: all servers merge into one tool list. (5) HTTP transport for production/shared; stdio for local/development.

6. Agent SDK: MCP Connection & Tool Search

In Sections 3–5, you built and configured MCP servers externally. The Agent SDK provides two ways to connect to them programmatically: (1) passing server configs to query() via mcp_servers, and (2) using tool search for deferred loading when you have many tools.

6.1 Connecting MCP Servers via query()

Pass external MCP servers directly to the Agent SDK. The SDK starts the server process, discovers its tools, and registers them alongside built-in tools:

# Connecting External MCP Servers to the Agent SDK
# Requires: pip install claude-agent-sdk
# Pre-requisite: MCP servers available as commands (npm packages or local scripts)

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage


async def main():
    """Connect multiple MCP servers to an Agent SDK query."""

    async for message in query(
        prompt="Find all open GitHub issues labeled 'bug' and check if any have related Jira tickets.",
        options=ClaudeAgentOptions(
            # Connect external MCP servers by command
            mcp_servers={
                # stdio transport: SDK starts this process and connects via stdin/stdout
                "github": {
                    "command": "npx",
                    "args": ["-y", "@modelcontextprotocol/server-github"],
                    "env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"},  # From env
                },
                # Another server — tools from all servers are merged
                "jira": {
                    "command": "npx",
                    "args": ["-y", "@modelcontextprotocol/server-jira"],
                    "env": {"JIRA_TOKEN": "${JIRA_TOKEN}"},
                },
            },
            # Pre-approve specific tools from these servers
            # Format: mcp____
            allowed_tools=[
                "mcp__github__list_issues",
                "mcp__github__get_issue",
                "mcp__jira__search_issues",
            ],
        ),
    ):
        if isinstance(message, ResultMessage) and message.subtype == "success":
            print(message.result)


asyncio.run(main())

When you have 50+ tools across multiple servers, loading them all upfront wastes context window space. Tool search solves this: only tool names are loaded initially; full JSON schemas are fetched on demand when Claude determines it needs a specific tool. Tool search is enabled by default in the Agent SDK — no explicit opt-in required.

Common Mistake: Tool search is NOT a tool you add to allowed_tools. There is no "ToolSearch" tool name to include. It’s an automatic background behavior controlled by the ENABLE_TOOL_SEARCH environment variable. When active, Claude automatically searches your tool catalog before calling MCP tools it hasn’t loaded yet.
# Tool Search — Automatic Deferred Loading (ON by default)
# Requires: pip install claude-agent-sdk
# Tool search works transparently: Claude discovers tools when it needs them.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage


async def main():
    """Tool search is ON by default — no explicit config needed.
    Claude sees tool NAMES from connected servers, and fetches full
    schemas on-demand when a task requires a tool it hasn't loaded yet.
    """

    options = ClaudeAgentOptions(
        mcp_servers={
            "crm": {
                "command": "python",
                "args": ["./mcp_servers/crm_server.py"],
            },
            "analytics": {
                "command": "python",
                "args": ["./mcp_servers/analytics_server.py"],
            },
        },
        # Pre-approve all tools from both servers via wildcards
        # (required — without this, Claude sees tools but can't call them)
        allowed_tools=[
            "mcp__crm__*",          # All tools from CRM server
            "mcp__analytics__*",    # All tools from analytics server
        ],
        # Tool search is ON by default. To customize:
        env={
            # "ENABLE_TOOL_SEARCH": "true"    # Always on (default behavior)
            # "ENABLE_TOOL_SEARCH": "auto:5"  # Activate when tools exceed 5% of context
            # "ENABLE_TOOL_SEARCH": "false"   # Off: load all schemas upfront (faster for <10 tools)
        },
    )

    async for message in query(
        prompt="Find the customer's recent orders and check their loyalty points.",
        options=options,
    ):
        if isinstance(message, ResultMessage) and message.subtype == "success":
            print(message.result)


asyncio.run(main())
When to Use Tool Search: When you have >30 tools across multiple servers, keep tool search on (default) to save context. When you have <10 tools, set ENABLE_TOOL_SEARCH=false — loading everything upfront avoids the extra round-trip. The auto mode activates when tool definitions exceed 10% of the context window. Tool search requires Claude Sonnet 4+ or Opus 4+ (no Haiku support).

6.3 Wildcard Permissions for MCP Tools

Instead of listing every tool individually, use wildcards to approve all tools from a trusted server:

# Wildcard Permissions — Approve All Tools from a Server
# Requires: pip install claude-agent-sdk

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage


async def main():
    async for message in query(
        prompt="Generate a weekly report from our analytics data.",
        options=ClaudeAgentOptions(
            mcp_servers={
                "analytics": {
                    "command": "python",
                    "args": ["./mcp_servers/analytics_server.py"],
                },
            },
            # Wildcard: approve ALL tools from the analytics server
            # Use when: you trust the server completely and want all its tools available
            allowed_tools=[
                "mcp__analytics__*",     # All tools from analytics server
                "Read",                  # Plus built-in Read
            ],
            # Alternative: approve all MCP tools from all servers
            # allowed_tools=["mcp__*"],  # Very permissive — use with caution
        ),
    ):
        if isinstance(message, ResultMessage) and message.subtype == "success":
            print(message.result)


asyncio.run(main())
Security Note: Wildcards like mcp__* bypass per-tool approval. Only use with fully trusted servers. For third-party MCP servers, always list individual tools explicitly.

Next in the SDK Track

In Part 8: CLAUDE.md, Rules & Skills, we shift to CCA Domain 3 — configuring agent behavior via CLAUDE.md files, project-level rules, skill definitions, and allowedTools restrictions. Covers CCA Tasks 3.1 and 3.2.

7. Skills + MCP: The Knowledge Layer

MCP gives Claude the ability to access your service. Skills give Claude the ability to use it correctly. Together they are complete — MCP without skills leaves users wondering what to do next; skills without MCP are instructions without any real-world data.

MCP vs Skills: Complementary Layers
flowchart LR
    subgraph MCP["MCP — Connectivity Layer"]
        direction TB
        A1["Real-time data access"]
        A2["Tool invocation"]
        A3["What Claude can do"]
    end
    subgraph Skills["Skills — Knowledge Layer"]
        direction TB
        B1["Workflow best practices"]
        B2["Domain expertise"]
        B3["How Claude should do it"]
    end
    MCP <-->|"Together"| Skills
    Skills --> C["Reliable, consistent agent"]
    MCP --> C
                        

7.1 Why Skills Complete MCP

Without skills paired with your MCP server, users face a cold-start problem:

Without SkillsWith Skills
Users connect MCP but don’t know what to do nextPre-built workflows activate automatically when needed
Support tickets: “How do I do X with your integration?”Consistent, reliable tool usage out of the box
Each conversation starts from scratchBest practices embedded in every interaction
Inconsistent results because users prompt differently each timeLower learning curve for your integration

The kitchen analogy: MCP provides the professional kitchen — access to tools, ingredients, and equipment. Skills provide the recipes — step-by-step instructions on how to create something valuable. Together they enable users to accomplish complex tasks without figuring out every step themselves.

7.2 MCP Enhancement Patterns

Three skill categories work well with MCP servers:

Category 3: MCP Enhancement Skills

Used for: Workflow guidance that enhances the tool access an MCP server provides.

Real example: sentry-code-review skill (from Sentry) — “Automatically analyzes and fixes detected bugs in GitHub Pull Requests using Sentry’s error monitoring data via their MCP server.”

Key techniques for MCP enhancement skills:

  • Coordinate multiple MCP calls in sequence (fetch issue → analyze → create fix → comment on PR)
  • Embed domain expertise users would otherwise need to specify each time
  • Provide context so Claude uses the right MCP tools in the right order
  • Include error handling for common MCP issues (connection failures, auth expiry, rate limits)
MCP EnhancementWorkflow GuidanceDomain Expertise
# Example: MCP Enhancement SKILL.md
# This skill wraps your MCP server with workflow knowledge

---
name: linear-sprint-planner
description: >
  Plans Linear project sprints using team velocity and task prioritization.
  Use when user mentions "sprint", "sprint planning", "Linear tasks",
  or asks to "create sprint" or "plan this sprint".
metadata:
  mcp-server: linear
  author: Your Company
  version: 1.0.0
---

## Sprint Planning Workflow

When the user asks to plan a sprint:

### Step 1: Gather Context
1. Use `linear_get_team` to fetch team details and velocity
2. Use `linear_list_issues` to get unplanned backlog items
3. Use `linear_get_cycles` to check current sprint capacity

### Step 2: Analyze & Prioritize
- Sort by: priority (urgent first), then effort (small tasks for end of sprint)
- Check for blockers: mark items with unresolved dependencies as blocked
- Match capacity: sum estimated points to not exceed team velocity

### Step 3: Create Sprint
1. Use `linear_create_cycle` with: title, start date, end date
2. Use `linear_add_issues_to_cycle` for each prioritized item
3. Use `linear_update_issue` to assign owners

### Output
Report: sprint name, total points, # tasks, owner breakdown
Highlight any items that didn't fit (backlog for next sprint)
Distribution Tip: When distributing an MCP server, bundle a companion skill with it. Host the skill on GitHub alongside your MCP server documentation. Describe it in outcome terms: “The Linear skill enables teams to plan complete sprints in seconds, instead of 15 minutes of manual task selection” — not in technical terms (“a folder containing YAML frontmatter”). See Part 8 for full skill authoring reference.