Template L · Concept · D2 Tool Design + Integration

Model Context Protocol.

8 min read·10 sections·Tier A

MCP is a communication standard that lets Claude access pre-built tools, resources, and prompts from specialized servers without you writing integration code. Connect to a GitHub MCP server instead of writing GitHub API tools yourself. Servers are configured in .mcp.json (project) or ~/.claude.json (user).

Infrastructure standardDomain 23 primitives
Model Context Protocol — hero illustration featuring Loop mascot in a warm gallery scene.
Domain D2Tool Design + Integration · 18%
On this page
01 · Summary

TLDR

MCP is a communication standard that lets Claude access pre-built tools, resources, and prompts from specialized servers without you writing integration code. Connect to a GitHub MCP server instead of writing GitHub API tools yourself. Servers are configured in .mcp.json (project) or ~/.claude.json (user).

3
Primitives
2
Config levels
3
Transports
D2
Exam domain
18+
Pre-built sets
02 · Definition

What it is

Model Context Protocol (MCP) is a standardized interface for connecting Claude to external systems: files, APIs, databases, web services. Think of it as the USB-C of AI integrations. Instead of each application building custom tools, MCP defines a protocol that any tool provider implements once, and any MCP-enabled client uses immediately. A .mcp.json configuration file lists servers; each server exposes tools, resources, and prompts that Claude can invoke.

Two server categories exist: installed (community-maintained or custom, running locally) and cloud-hosted (managed by the vendor, authenticated via env vars). A local server might expose Grep, Read, Bash in the same process as your app. A cloud server (e.g. GitHub MCP) runs on the vendor's infra and authenticates via your token. Both appear in Claude's tool list identically; the distinction is who maintains the code.

The architecture is asymmetric: Claude (the client) sends tool_use blocks; the MCP server receives the request, executes the tool, and returns a result. Claude never talks directly to an external API, the server acts as a proxy. This isolation prevents token leakage (Claude never sees raw credentials), enables caching (server-side response cache), and allows request validation (the server can reject malformed calls before forwarding).

Production failures stem from stale `.mcp.json` configuration, mismatched env vars, vague tool descriptions inherited from old integrations, and forgetting that the server owns error handling. The JSON file is version-controlled; if a teammate updates GitHub's API and doesn't re-run the MCP setup, their tools silently fail. Errors must bubble to Claude as structured data, not be swallowed by the server.

03 · Mechanics

How it works

At startup, your app loads .mcp.json. Each entry specifies a server: installed (local executable) or cloud (managed remote). Installed servers are spawned as child processes; cloud servers are contacted via HTTP with credential headers. Each server is interrogated: "what tools do you expose?" The server responds with names, descriptions, and JSON schemas. All tools merge into a single namespace; Claude sees them as a unified set.

When Claude's loop produces a tool_use block (e.g. {name: "Grep", input: {query: "..."}}), the MCP framework routes the request to the server that owns that tool. The server's code runs: validates input, executes the operation, returns a result. The result is wrapped in a tool_result block and appended to the message list. Claude never knows where the tool ran or what authentication was used.

Each server can expose resources: catalogs of data Claude can query without invoking a tool. A GitHub MCP server might expose a resource listing all open issues; Claude inspects that catalog to decide which tools to call. Resources reduce exploratory tool calls: instead of calling list_issues 20 times, Claude reads the resource once and calls targeted tools. Resources are read-only caches; tools are the write path.

Error handling flows back to Claude via tool_result. If the server hits an API error (timeout, 403, invalid request), it returns a structured message in content: {"error": "github_api_timeout", "hint": "retry after 30s"}. Claude reads the error and adjusts. Silent error suppression (returning empty as success) is the #1 production failure: Claude doesn't know the tool failed and re-requests indefinitely.

Model Context Protocol mechanics, painterly diagram featuring Loop mascot.
04 · In production

Where you'll see it

Team-shared GitHub integration

Project uses GitHub MCP server: PR queries, issue lists, code search, all pre-built. Configure in .mcp.json (committed to repo). Team members run claude mcp sync once. Replaces hand-rolled tool definitions and auth wrappers; tokens come from team env vars.

Custom MCP server for legacy billing

Company has a Unix-only billing API with no SDK. Build a Python MCP server wrapping the API; expose via stdio in .mcp.json. Agent calls standard MCP tools; the server hides the legacy weirdness. New team members inherit the integration automatically.

Resources for upfront schema discovery

Database MCP exposes a resources endpoint listing all tables and columns. Agent reads the resource ONCE at session start instead of calling list_tables / describe_table N times. Cuts exploratory tool calls from 8 → 0 per session.

05 · Implementation

Code examples

.mcp.json, team-shared MCP configurationjson
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "postgres": {
      "command": "uvx",
      "args": ["mcp-server-postgres", "--readonly"],
      "env": {
        "DATABASE_URL": "${DATABASE_URL}"
      }
    },
    "billing-legacy": {
      "command": "python3",
      "args": ["./mcp-servers/billing.py"],
      "env": {
        "BILLING_API_HOST": "${BILLING_API_HOST}",
        "BILLING_API_KEY": "${BILLING_API_KEY}"
      }
    }
  }
}
Env-var expansion (${VAR}) keeps secrets out of the committed file. Team members run claude mcp sync to install all servers consistently.
Custom MCP server skeleton (FastMCP)python
# Minimal MCP server using the FastMCP Python library.
# Run: python billing_server.py
# Configure in .mcp.json with command: python3, args: [path/to/billing_server.py]

from mcp.server.fastmcp import FastMCP
import os

mcp = FastMCP("billing-legacy")

@mcp.tool()
def get_invoice(invoice_id: str) -> dict:
    """Fetch invoice by ID from the legacy billing API.

    Use when a customer asks for invoice details. Returns
    {invoice_id, customer_id, amount, status, line_items}.
    """
    api_host = os.environ["BILLING_API_HOST"]
    # ... call legacy API, return result
    return {"invoice_id": invoice_id, "amount": 247.83, "status": "paid"}

@mcp.tool()
def issue_credit(invoice_id: str, amount: float, reason: str) -> dict:
    """Issue a credit note against an invoice. Max amount $500
    without manager approval. Returns {credit_id, status}.
    """
    if amount > 500:
        return {"status": "blocked", "reason": "exceeds_policy"}
    # ... call legacy API
    return {"credit_id": "CR-9999", "status": "issued"}

@mcp.resource("billing://schema")
def get_schema() -> str:
    """Returns the billing data dictionary so the agent doesn't
    need exploratory tool calls. Read once per session."""
    return open("billing_schema.md").read()

if __name__ == "__main__":
    mcp.run()
@mcp.tool decorators expose functions as MCP tools. @mcp.resource exposes read-only data so the agent loads schema once instead of calling list_* repeatedly.
06 · Distractor patterns

Looks right, isn't

Each row pairs a plausible-looking pattern with the failure it actually creates. These are the shapes exam distractors are built from.

Looks right

MCP and tool-use are competing approaches; pick one.

Actually wrong

MCP is infrastructure (who provides tools); tool-use is the mechanism (Claude calls them). They're complementary, every MCP tool fires through the tool-use protocol.

Looks right

Hardcode API tokens in .mcp.json so the team has a single source of truth.

Actually wrong

.mcp.json is committed to git. Hardcoded secrets leak. Use ${ENV_VAR} expansion; secrets stay in CI/CD or local .env.local files.

Looks right

Expose 30 tools in one MCP server for completeness.

Actually wrong

Same 18-tool degradation applies. Scope MCP servers to focused domains (one per service). Use 'resources' to expose data upfront and avoid forcing N exploratory tool calls.

07 · Compare

Side-by-side

AspectMCP serverCustom in-app toolsSDK built-ins
Setup effortLow (config in .mcp.json)Medium (write tool defs in code)Zero (Read/Edit/Bash)
ReusabilityHigh (any Claude client)Per-appBuilt into Claude Code
Best forStandard integrationsBespoke business logicFile ops + shell
AuthEnv-var expansionPer-app configInherits user permissions
08 · When to use

Decision tree

01

Is this integration with a known service (GitHub, Slack, Postgres, etc.)?

YesSearch for an existing MCP server. Almost always saves work over a custom build.
NoGo to next question.
02

Does the team need to share this integration?

YesPut config in .mcp.json (project root, committed). Use ${ENV_VAR} for secrets.
NoUse ~/.claude.json (personal, not committed).
03

Will the agent need exploratory schema/data lookups?

YesExpose a 'resources' endpoint on your MCP server. Read once, save N exploratory tool calls.
NoTools alone are sufficient.
09 · On the exam

Question patterns

Model Context Protocol exam trap — painterly cautionary scene featuring Loop mascot.
You add a new MCP server to `.mcp.json` but Claude Code doesn't see its tools. What did you forget?
Restart the Claude Code session. The framework caches the tool list at startup. Changes to .mcp.json require a restart, or a manual mcp restart command in newer versions.
Two MCP servers expose tools with the same name (`Read`). What happens?
The config is invalid. Tool names must be globally unique across all MCP servers. Prefix tools by server name (GithubRead, FileRead) or disable one of the conflicting servers.
A cloud MCP server returns `{error: "rate_limited"}`. Your agent retries 5 times and exhausts budget. Better approach?
Server should return structured rate-limit info: {error: "rate_limited", retry_after: 30}. Your harness reads this and waits before retrying. Don't hammer; respect the hint.
You hardcoded a GitHub token in `.mcp.json`. A teammate clones the repo and your token leaks. Fix?
Use ${GITHUB_TOKEN} env-var expansion. Each developer sets their own token in their environment. The .mcp.json is committed; the token is not.
An MCP server resource lists 1000 items. Claude calls a tool 1000 times, one per item. Why?
The resource is a catalog (read-only data Claude can scan), not a list of pre-staged actions. Reduce tool calls by structuring the resource so Claude can scan and pick targeted items, not iterate.
Your custom MCP server crashes when Claude calls a tool with malformed JSON. What's the right error handling?
Wrap tool execution in try/except. Return {error: "invalid_input", detail: "..."} as the tool_result. Claude reads it and retries with corrected input. Crashes leave the framework hanging.
You want to add a custom internal API. Should you build a custom MCP server or expose tools directly via the SDK?
MCP for shared/team use (multiple developers, multiple Claude sessions). Direct SDK tools for app-specific custom logic. MCP adds a small protocol overhead but enables reuse.
An MCP server's tool description is auto-generated from the underlying API and reads like docstring noise. The model misroutes. Fix?
Override the description in your MCP server config. The vendor's auto-generated description may be too generic. Write a 4-line description with what/when/edge-cases/boundaries.
10 · FAQ

Frequently asked

Do I have to use MCP?
No. Write tools yourself if you prefer. MCP saves work when pre-built servers exist for the service you need.
Can I create my own MCP server?
Yes. Anthropic ships Python and Node.js SDKs; community implementations cover other languages.
What languages can MCP servers be written in?
Any. Python and Node.js are official; community SDKs cover the rest.