MCP (Model Context Protocol)
Use this skill to decide whether MCP is the right abstraction, configure existing servers in Claude Code or Codex, or build a narrow custom server when repeated agent workflows justify it.
Protocol baseline: 2026-07-28 is the current spec — it shipped on 2026-07-28 (https://modelcontextprotocol.io/specification/2026-07-28/changelog), superseding 2025-11-25. It is a fundamental redesign, not an increment:
- Stateless protocol. The
initialize/notifications/initializedhandshake and theMcp-Session-Idheader are removed (SEP-2567, SEP-2575). Every request carries its protocol version and client capabilities in_meta; a mandatoryserver/discoverRPC advertises supported versions and identity. Servers needing cross-call state mint explicit handles passed as ordinary tool arguments. - Roots, Sampling, and Logging are deprecated (SEP-2577) — still functional, but new implementations should not adopt them. Migrate to tool-parameter directories / resource URIs, direct LLM provider API calls, and stderr or OpenTelemetry respectively.
- Server-initiated requests are gone.
roots/list,sampling/createMessage, andelicitation/createare replaced by the Multi Round-Trip Requests (MRTR) pattern: the server returnsresultType: "input_required"withinputRequests, and the client retries the original request carryinginputResponses. - Transport changes. The HTTP GET endpoint and
resources/subscribe/unsubscribecollapse into a singlesubscriptions/listenstream; SSE resumability (Last-Event-ID) is removed, so a broken stream means re-issuing the request with a new ID.pingandlogging/setLevelare removed. HTTP+SSE transport is now formally Deprecated — migrate to Streamable HTTP.
The deprecation clock is the planning fact that matters. The spec adopted a formal feature lifecycle (Active / Deprecated / Removed) with a minimum twelve-month deprecation window (SEP-2596) and a published deprecated features registry. Anything you build on Roots, Sampling, or Logging today has a bounded, published lifetime — treat those as migration debt from the start rather than as supported surface. Verify the registry for current state before relying on a deprecated feature; the window is a floor, not a promise of a specific removal date.
TypeScript SDK v2 targets this spec; v1.x remains a supported production lane for a transition period. Confirm current SDK stability against the SDK repo before choosing a lane — that status moves independently of the spec.
Governance (factor into vendor-trust judgment, not just the tech): Anthropic donated MCP to the Agentic AI Foundation (AAIF), a directed fund under the Linux Foundation, effective 2025-12-09 (blog.modelcontextprotocol.io/posts/2025-12-09-mcp-joins-agentic-ai-foundation; AAIF founding members include AWS, Anthropic, Block, Bloomberg, Cloudflare, Google, Microsoft, and OpenAI). The existing maintainers keep full technical authority over the spec via the SEP process — the foundation explicitly "will not dictate the technical direction of MCP" — so day-to-day spec/SDK guidance in this skill is unaffected. What changes for your judgment: MCP is no longer a single-vendor bet, which lowers long-term protocol-abandonment risk and is a legitimate factor when a customer or security review asks "who owns this protocol" — cite the foundation, not Anthropic, when that question comes up.
Quick Reference
- use MCP when you need reuse, explicit permissions, discovery, or a stable tool contract across sessions
- prefer an existing official or vendor-maintained server before building custom infrastructure
- default to a low-cost health check before trusting a server
- build the narrowest possible tool surface and keep write access gated
When to Use MCP
- repeated database, filesystem, or SaaS-tool access for agents
- reusable internal API wrappers for agent workflows
- shared remote services that need a stable contract across clients
- memory, note-vault, or repo-context retrieval exposed behind explicit tools
Do not use MCP for one-off HTTP calls or for giant generic wrappers when a simpler direct tool will do.
Defaults
- local or private server:
stdio - remote shared server: Streamable HTTP (replaced SSE as the recommended remote transport in spec 2025-03-26)
- standalone SSE transport: compatibility fallback only
- registry discovery: official registry first
- authorization: optional by protocol; when implemented over HTTP it MUST use OAuth 2.1 with PKCE (S256), plus three things the spec also makes mandatory and that are easy to miss: Resource Indicators (RFC 8707) on every authorization and token request, server-side Protected Resource Metadata at
/.well-known/oauth-protected-resource(RFC 9728), and Client ID Metadata Documents as the primary client-registration mechanism (Dynamic Client Registration is now backwards-compat only). Plain OAuth 2.0 is not spec-compliant. See references/mcp-security.md. - SDK guidance: current stable docs first; treat preview SDKs as watchlist material
- deferred tool loading: on by default in Claude Code (unconditional, not threshold-gated — the
auto/auto:Nmodes are the ones that check a % threshold); off by default on Google Cloud's Agent Platform and behind non-first-partyANTHROPIC_BASE_URLproxies. Known gap: it does not reliably defer schemas from Streamable HTTP MCP servers (see Known Traps)
Quick Start
Claude Code
Prefer CLI setup over hand-editing config. All flags (--transport, --env, --scope, --header) must precede the server name; -- separates the server name from the stdio command.
# stdio (local) — postgres example
claude mcp add postgres \
--scope project \
--env POSTGRES_URL=postgresql://user:pass@localhost:5432/app \
-- npx -y @modelcontextprotocol/server-postgres
# Streamable HTTP (remote) — Linear example
claude mcp add --transport http linear-server https://mcp.linear.app/mcp
# Remote with bearer token header
claude mcp add --transport http \
--header "Authorization: Bearer ${SENTRY_TOKEN}" \
sentry https://mcp.sentry.io/mcp
claude mcp list
claude mcp get postgres
claude mcp remove postgres
Use .mcp.json for project-shared config. Scope options: local (default, user-only), project (.mcp.json), user (global ~/.claude/mcp.json).
Codex / OpenAI
The codex mcp add CLI supports both transports directly: stdio via --env + the -- separator, and Streamable HTTP via --url (added in openai/codex PR #4904, 2025-10-08 — confirmed against codex mcp add --help on codex-cli 0.144.1). There is still no --transport flag: transport is inferred from --url vs. a trailing -- <command>.
# stdio (local helper) — CLI path
codex mcp add repo-tools --env API_KEY=secret -- node ./dist/index.js
# Streamable HTTP (remote) — CLI path, bearer token via env var
codex mcp add figma --url https://mcp.figma.com/mcp --bearer-token-env-var FIGMA_OAUTH_TOKEN
codex mcp login figma # OAuth handshake for a server that needs it (or --oauth-client-id / --oauth-resource on add)
codex mcp list
Editing ~/.codex/config.toml directly is still the only path for fields the CLI doesn't expose yet — modular tool gating (enabled_tools/disabled_tools, default_tools_approval_mode), static http_headers, and OAuth callback ports:
# ~/.codex/config.toml — remote Streamable HTTP server, full field set
[mcp_servers.figma]
url = "https://mcp.figma.com/mcp"
bearer_token_env_var = "FIGMA_OAUTH_TOKEN"
startup_timeout_sec = 10 # default 10; startup_timeout_ms also accepted
Scopes mirror Claude Code: ~/.codex/config.toml is global; a project-root .codex/config.toml is project-scoped (trusted projects only). Full schema — env, tool gating, OAuth callback — in references/mcp-custom.md.
Claude Code ↔ Codex config parity
| Concern | Claude Code | Codex |
|---|---|---|
| Add stdio server (CLI) | claude mcp add NAME -- cmd args | codex mcp add NAME -- cmd args |
| Add remote HTTP server | claude mcp add --transport http NAME URL | codex mcp add NAME --url URL |
| Project-shared file | .mcp.json (mcpServers) | .codex/config.toml ([mcp_servers.NAME]) |
| Per-server env | --env K=V / JSON env | [mcp_servers.NAME.env] table |
| Remote auth header | --header "Authorization: Bearer …" | bearer_token_env_var or [mcp_servers.NAME.env_http_headers] |
| Per-tool approval | permission settings | default_tools_approval_mode, per-tool approval_mode |
Transport rule is identical for both: stdio for local helpers, Streamable HTTP for remote shared services.
Workflow
- Decide whether the problem really needs MCP.
- Search the official registry and prefer an existing server if it is well-scoped.
- Validate transport, auth, tool surface, and output size with one low-cost read.
- If custom work is justified, build the smallest server that solves the repeated workflow.
- Harden the server before broader rollout: least privilege, narrow scopes, output limits, logging, and approval controls for writes.
ASCII Flow
Agent tool need
-> Is reuse/discovery/permission boundary needed?
+-- No -> use direct API, script, or existing app tool
+-- Yes -> search registry and vendor servers
-> Choose transport
+-- local/private -> stdio
+-- shared/remote -> Streamable HTTP
-> Run health gate: list -> inspect config -> low-cost read
-> Harden scopes, output limits, auth, logging, and write approvals
Health Gate
Before relying on any MCP server:
- confirm the client can see it with
mcp list - inspect the effective config
- run one low-cost list or read tool
- tighten pagination or row limits at the server before raising client output caps
Auth Failure Rule
If auth fails:
- re-authenticate once
- retry once
- if it still fails, stop and report the exact transport, server, and error
Do not loop on auth failure.
Registry-First Discovery
Before building anything custom:
- search
https://registry.modelcontextprotocol.io - prefer provider-hosted or officially maintained servers
- record transport, auth model, write scope, maintainer, and whether the tool surface is narrow enough
- "officially maintained" now means the AAIF-governed MCP project, not Anthropic alone — a registry listing is a discovery signal, not a security review; still evaluate each server against the checklist below before adopting
Typical Scenarios
End-to-end recipes for the requests this skill actually receives. Each ends at a hardened, verified state — not a bare connection.
S1 — Connect an agent to a production database (read-only)
- Search the registry; prefer DBHub (zero-dep) or the official Postgres server over a generic SQL interpreter.
- Create a read-only DB role with row limits before connecting — enforce at the DB layer, not the prompt (
references/mcp-for-dwh.md, Layer 1). claude mcp add postgres --scope project --env POSTGRES_URL=… -- npx -y @modelcontextprotocol/server-postgres(Codex:[mcp_servers.postgres]stdio block +[mcp_servers.postgres.env]).- Health gate:
mcp list→mcp get→ one low-costSELECT … LIMIT 1. - If the workflow needs writes later, migrate to a custom thin server (Shape C) with per-tool approval — do not loosen the read-only role.
S2 — Wrap an internal REST API as a custom server
- Confirm reuse justifies it (repeated agent use, not a one-off fetch — see Build vs Use).
- Build the narrowest tool surface: one tool per action, strict
inputSchema,outputSchemafor structured returns. Python →mcp.server.fastmcp.FastMCP; TS →McpServer(references/mcp-custom.md,references/mcp-patterns.md). - Add retry, pagination, and server-side row caps from the start (
mcp-patterns.mdREST + pagination patterns). - Smoke-test with
npx @modelcontextprotocol/inspectorbefore wiring any client. - Local-only →
stdio; shared → Streamable HTTP bound to127.0.0.1withHost/Originvalidation.
S3 — Add a remote vendor SaaS server with OAuth
- Verify it in the registry/vendor docs; record transport, auth model, write scope, maintainer.
- Claude Code:
claude mcp add --transport http NAME URL(+--headerif it takes a bearer token). Codex:codex mcp add NAME --url URL --bearer-token-env-var VAR_NAME, thencodex mcp login NAMEif it needs OAuth. - Inspect granted scopes before accepting; reject write scopes with no approval boundary.
- On auth failure follow the Auth Failure Rule (re-auth once, retry once, stop) — most "protocol" failures here are stale local tokens.
S4 — Cut context bloat from too many servers
- Audit with
claude mcp list; each server costs ~1.5–2K tokens of definitions per message. - Keep deferred tool loading on (default); set
ENABLE_TOOL_SEARCH=autoif you want upfront-when-small behaviour. - If the fleet is behind Streamable HTTP (a gateway or a remote server with many tools), do not assume deferral is saving you tokens — check the actual context usage after connecting, since HTTP tool schemas are known not to defer reliably (see Known Traps). Splitting into fewer, narrower HTTP servers or moving to stdio helps more than tuning
ENABLE_TOOL_SEARCHin that case. - Disable unused toolsets in modular servers (Kubernetes, etc.); in Codex use
enabled_tools/disabled_tools. - Remove servers you no longer use; consider MCP Optimizer for semantic tool filtering only at large fleets.
S5 — Make an existing AWS Lambda / API Gateway estate agent-callable
- Do not hand-roll one MCP server per Lambda. Use AWS Bedrock AgentCore Gateway (see below) — configuration, not code.
- Tools exposed through Gateway speak MCP, so they are callable by any MCP client (Claude, Codex, Cursor), not just Bedrock agents.
- Choose a hand-rolled custom server only for non-AWS APIs, bespoke business rules, or multi-cloud portability.
S6 — Give an agent persistent repo / knowledge context
- Layer it: small session memory in repo files → searchable knowledge via MCP → an ingestion layer turning raw notes/transcripts into canonical artifacts.
- Use a docs/memory server (Context7 for live library docs; a code-graph memory server for repo context) — keep memory scoped by repo/tenant so unrelated context does not blend.
- Pick one source of truth; do not run a memory MCP that overlaps file-based memory without deciding which wins (
references/mcp-ecosystem-patterns.md).
High-Leverage Patterns
- documentation servers for up-to-date docs
- search and browser-backed research servers
- code and memory servers for reusable repo context
- note-vault and decision-log retrieval
- local toolchain wrappers for deterministic operational work
- multi-server hubs only when inventory and lifecycle management justify them
- plugin-sourced MCP servers should be manifest-declared, diffed on plugin reload, and reconnected with explicit host-owned precedence
For memory-heavy workflows, prefer:
- small session memory in repo files
- searchable knowledge via MCP
- an ingestion layer that turns raw notes or transcripts into canonical artifacts
Build vs Use Decision
- database, filesystem, browser, or vendor SaaS with a good existing server -> use an existing server
- internal API used repeatedly by agents -> build a custom server
- one-off fetch or ad hoc automation -> do not build MCP
- bespoke auth, approval flow, or business-rule enforcement -> custom server is often justified
- structured data store (Postgres, Snowflake, BigQuery, DuckDB, BI semantic layer) -> use references/mcp-for-dwh.md to pick the right shape and enforcement layers
- existing AWS Lambda + API Gateway estate that needs to become agent-callable -> use AWS Bedrock AgentCore Gateway instead of hand-rolling MCP servers (see below)
AWS Bedrock AgentCore Gateway
AgentCore Gateway is AWS's managed MCP-server surface — it turns existing AWS APIs, Lambda functions, and third-party MCP servers into MCP-compatible tools without writing a custom MCP server per service.
Two consequences:
- Existing AWS estates become agent-callable. A team with 200 Lambdas behind API Gateway exposes them via Gateway in a configuration layer.
- Portability via the protocol. Tools exposed through Gateway speak MCP, so they are callable by Claude (Anthropic MCP), GPT, Copilot, Cursor, and any MCP client — not just Bedrock agents. AWS becomes an MCP tool provider for the wider agent ecosystem.
When to pick over a hand-rolled MCP server:
| Need | Pick | |---|---| | Wrap many AWS Lambdas / API Gateway routes into MCP tools | AgentCore Gateway | | Custom auth flow, business rules, non-AWS API | Custom MCP server (this skill) | | Single non-AWS SaaS integration | Existing MCP server from the registry | | Multi-cloud team that may leave AWS in 12 months | Custom MCP server (portable) |
Deep dive: ../software-paas-hosting/references/aws-bedrock-agentcore.md — Gateway section.
Deferred Tool Loading (Tool Search)
Claude Code defers MCP tool schemas by default: only tool names and short descriptions load at session start, and full schemas load on demand (3-5 tools per search). This substantially reduces tool-definition token overhead for large server fleets — official docs frame the baseline as "50 tools can use 10-20K tokens" and note tool-selection accuracy degrades past 30-50 tools loaded at once; a "~85% / 77K→8.7K" figure circulates in third-party posts but is not the official figure — cite the mechanism, not that number. Tool search supports every Claude model except Haiku, and caps out at 10,000 tools in a catalog.
Configure behaviour via ENABLE_TOOL_SEARCH:
| Value | Behaviour |
|---|---|
| unset (default) | Tool search on unconditionally; falls back to loading upfront on Google Cloud's Agent Platform (pre-Sonnet 4.5/Opus 4.5) or a non-first-party ANTHROPIC_BASE_URL |
| true | Force tool search on even where the default would fall back — this can break requests on unsupported Agent Platform models or proxies that don't forward tool_reference blocks |
| auto | Threshold mode: load upfront if combined tool-definition size fits within ~10% of the context window, else activate tool search |
| auto:N | Same as auto with a custom percentage threshold N (0–100); lower activates sooner |
| false | Load all schemas upfront every turn (best only under ~10 tools total) |
Known gap (confirmed, not fixed): deferral does not reliably apply to tools from Streamable HTTP / remote MCP servers — a reported case with ~250 tools behind an HTTP gateway loaded ~120K tokens upfront (60% of a 200K context window) even with ENABLE_TOOL_SEARCH=auto:5 set; Anthropic closed the report as "not planned" (github.com/anthropics/claude-code issue #40314). Until this changes, treat large HTTP MCP fleets as upfront-loaded for budgeting purposes and prefer stdio or a smaller HTTP tool surface over relying on deferral to save context.
Codex equivalent: gate tool exposure per server with enabled_tools / disabled_tools and default_tools_approval_mode / per-tool approval_mode in config.toml rather than a global defer flag.
Design guidelines when building custom servers under deferred loading:
- keep tool
nameanddescriptionhighly precise — these are the only signals used for search-based discovery (e.g.search_slack_messagessurfaces for more queries thanquery_slack) - one tool per distinct action; never bundle multiple actions into a single overloaded tool
- keep
descriptionunder 120 characters for the discovery stub; put detail ininputSchemaannotations
Build a Custom Server
For new servers:
- TypeScript: prefer the higher-level
McpServerAPI. v2 targets the current2026-07-28spec; v1.x remains a supported production lane through the transition. Check the SDK repo for current release status before picking a lane — SDK stability tracks separately from the spec, so a claim about which lane is production-ready goes stale faster than the protocol itself - Python: prefer the high-level FastMCP API. Note two distinct things named "FastMCP":
from mcp.server.fastmcp import FastMCPships inside the officialpython-sdk(the default choice), while standalonejlowin/fastmcp(v3.x) is a separate, more feature-rich superset. Use the in-SDK one unless you specifically need a v3 feature, and say which you mean.
Build only the tools the workflow needs. Avoid giant generic CRUD surfaces.
Declare all four tool annotation hints (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) on every tool as part of the build, not as a later pass — see references/mcp-custom.md#tool-annotation-hints. Before broader rollout, author evaluation questions per references/mcp-evaluation.md.
Use:
Common Anti-Patterns
- Building an MCP server for a one-off HTTP call or a workflow that should stay inside normal application code.
- Exposing generic CRUD or shell-style tool surfaces when the workflow needs a narrow, policy-aware interface.
- Shipping a remote write-capable server with no explicit approval boundary or row-level restrictions.
- Treating client-side pagination as sufficient instead of enforcing limits server-side.
- Assuming every client supports the same transport and auth posture.
- Letting server output flow directly into privileged actions without validation because "the tool is trusted."
Known Traps
-
stdioand HTTP transport guidance mixed together in one recommendation with no runtime-specific separation. Resolution: Always declare the transport first, then give guidance specific to that transport. Use the transport-auth matrix inreferences/transport-auth-matrix.mdas the decision anchor. Never give a single config block that silently assumes one transport. -
OAuth or token caching issues that look like protocol failures but are really stale local auth state. Resolution: Follow the Auth Failure Rule above: re-authenticate once, retry once, then stop. Clear only the local cache — do not loop. If the error recurs, report the exact transport, server name, and HTTP status code before escalating.
-
Registry discovery done without recording write scope, maintainer, and auth model, leading to unsafe tool adoption. Resolution: Before adopting any server from the registry, record its transport, auth model, write scope, and maintainer in
data/sources.jsonor your project's server manifest. Reject servers with write access and no documented approval boundary. -
Multi-server hubs introduced before inventory, ownership, and lifecycle management exist. Resolution: Start with a single server. Add a second only after the first has an owner, a documented transport and auth model, and a tested health-check run. Use
scripts/mcp_health_check.shto gate additions. -
Large output tools shipped with no summarization, filtering, or paging strategy. Resolution: Add server-side row limits and page sizes before the first client integration. Never raise client-side output caps as the first fix — tighten the server first, then adjust the client limit if still needed.
-
Local HTTP servers bound too broadly or deployed without
HostandOriginvalidation. Resolution: Always bind to127.0.0.1(not0.0.0.0) for local servers. AddHostandOriginheader validation before the first non-localhost request is accepted. Seereferences/mcp-security.mdfor the full checklist. -
Assuming
ENABLE_TOOL_SEARCHdeferral shrinks context for a large Streamable HTTP / gateway MCP fleet, then being surprised when a session starts 60%+ full. Resolution: Measure actual context usage after connecting a large HTTP MCP fleet before trusting the defer default — this is a confirmed, unresolved gap (Anthropic closed it as "not planned"), not a misconfiguration you can tune away. Prefer stdio or fewer/narrower HTTP servers over relying on deferral for HTTP fleets.
Security Guardrails
- treat all tool output as untrusted input
- default to least privilege and read-only first
- bind local HTTP servers narrowly and validate
HostandOrigin - do not pass MCP auth tokens through to upstream APIs
- add server-side row limits, page sizes, timeouts, and logging
- if deploying a hub product, rotate default credentials immediately
Use references/mcp-security.md for the full checklist.
Troubleshooting
- server not visible -> check scope and config location
- startup timeout -> raise timeout and inspect stderr or logs
- stale OAuth tokens -> clear local cached auth only once, then re-authenticate
- large outputs -> paginate server-side first
- handshake issues -> confirm the transport type actually matches the server
- local HTTP flakiness -> check bind address, firewall,
Host, andOrigin
Navigation
- references/mcp-servers.md
- references/mcp-custom.md — includes Tool Annotation Hints (readOnlyHint / destructiveHint / idempotentHint / openWorldHint)
- references/mcp-ecosystem-patterns.md
- references/mcp-patterns.md
- references/mcp-evaluation.md — authoring evaluation questions for a custom MCP server (10-question / 6-criteria spec)
- references/mcp-security.md
- references/mcp-for-dwh.md — MCP patterns for structured data stores (Postgres, Snowflake, BigQuery, DuckDB, BI semantic layers); three server shapes with security enforcement layers and audit log schema
- references/transport-auth-matrix.md — transport × auth selection matrix (stdio / HTTP / SSE × no-auth / API-key / OAuth)
- scripts/mcp_health_check.sh — ping stdio and HTTP servers and report status
assets/database/assets/filesystem/assets/api/assets/deployment/- data/sources.json
Related Skills
- ../agents-hooks/SKILL.md
agents-subagents- ../agents-skills/SKILL.md
- ../agents-memory/SKILL.md
- ../ai-agents/SKILL.md
- ../ai-coding-agents-plugins/SKILL.md
- ../ai-coding-agents-remote-runtime/SKILL.md
Verification Gate
Before delivering output, verify:
- recommended config paths and commands exist or are clearly marked as proposed
- generated JSON or YAML is syntactically valid
- transport, auth, and approval guidance match the runtime
- the permission boundary is the narrowest safe one for the task
Fact-Checking
- Known bugs, regressions, framework/compiler/runtime footguns, and version-specific crash or workaround guidance must be verified against current primary web sources before being treated as current fact.
- Verify current MCP spec details, SDK guidance, transport defaults, and auth patterns against official sources before final answers.
- Prefer modelcontextprotocol.io and official SDK repos over blog posts.
- If web access is unavailable, mark transport and auth guidance as unverified.
Learnings Loop
Before applying this skill on a non-trivial task, read learnings.consolidated.md in this directory (and learnings.md if present).
After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to learnings.md via agents-skills-feedback-loop/scripts/append_learning.py. Do not modify SKILL.md itself.