8.2 MCP Servers: Primitives and Transports
8.2.1 What MCP Is: A USB-C Port for AI
Custom tools in the previous lesson live inside a single application — you define them, and only that app's Claude calls them. The Model Context Protocol (MCP) exists for the case where that's not good enough: a capability that should be usable from Claude Desktop, Claude Code, and your own custom app alike, built once and maintained in exactly one place. Anthropic's own description of MCP is memorable for a reason: "a USB-C port for AI" — a single standardized connector that any MCP-compatible client can plug into, rather than every application inventing its own bespoke integration.
An MCP server is the thing that exposes the capability. It advertises what it can do, and handles the calls that come in. An MCP client lives inside the AI application itself and is the piece that connects out to one or more of these servers. The value proposition is entirely about reuse and independent maintenance: fix a bug or add a feature in the server once, and every client connected to it benefits immediately, with zero changes needed in any of the applications using it.
The one idea to hold onto
MCP is an open standard — "a USB-C port for AI" — for connecting AI applications to external systems. Build a capability once as an MCP server and any MCP-compatible client can reuse it, maintained independently of any single app.
8.2.2 Three Core Primitives: Tools, Resources, and Prompts
It's a common misconception to assume MCP is just a fancier way to define tools. It isn't — an MCP server can expose three distinct kinds of capability, and the exam will test whether you can tell them apart rather than lumping everything under "tools."
| Primitive | What it is | Example |
|---|---|---|
| Tools | Model-callable functions that take an action | Look up inventory, send an email, create a ticket |
| Resources | Readable data or content the client can load into context | A file, a database record, a support ticket's contents |
| Prompts | Reusable, parameterized prompt templates the user or client can invoke | A "summarize this quarter's incidents" template with slots for date range |
MCP servers can expose all three primitives at once — they aren't mutually exclusive, and a single server commonly offers a mix.
Notice how this maps onto what an MCP server is actually good for beyond mere function calling: it isn't limited to letting Claude take actions (tools). It can also hand the client readable content to load directly into context (resources), and it can ship ready-made, parameterized prompt templates that a user or client invokes by name instead of writing from scratch (prompts). That's a distinctly richer surface than a plain custom tool, which only ever offers the "action" half of this picture.
8.2.2 — Exam Trap
Exam trap: a question that asks "which of these is NOT an MCP primitive" and lists a plausible fourth option (model weights, a fine-tuned checkpoint, an API key). Only tools, resources, and prompts are the three core MCP primitives.
Resources themselves come in two shapes, and telling them apart is its own small exam-relevant skill. A direct resource has a fixed address that takes no parameters -- a specific file, a fixed database record, a static list of available documents. A templated resource has a parameterized address instead -- a URI template that takes an argument, such as a document identifier or a date range, so a single resource shape can address many different underlying records.
8.2.2 -- Exam Trap
Don't assume every MCP client automatically injects a resource directly into context without a tool call -- support for that pull-it-in-directly behavior varies by client. Verify the specific client you're targeting actually supports resource injection before designing a workflow that depends on it.
8.2.3 When to Build an MCP Server — and the Anti-Pattern It Replaces
The canonical trigger for building an MCP server is a specific combination of two requirements showing up together: the capability must be reusable across multiple Claude applications, AND it must be maintained independently of any single one of them. An internal inventory REST API that three different internal tools all need to query is the textbook case — wrap it once as an MCP server exposing inventory-lookup tools, and every app that needs it connects to the same server instead of re-implementing the integration.
The anti-pattern this replaces is hard-coding the integration logic into each application's system prompt or app-local tool definitions. That approach fails on both axes at once: it isn't reusable (every app duplicates the same logic), and it isn't independently maintainable (a change to the underlying API means hunting down and updating every copy). Pasting a snapshot of current inventory data into context on every request fails even harder — it's not live, and it doesn't scale as the data grows.
- •Reusable across multiple Claude applications — not just used by one.
- •Maintained independently — a fix or feature ships once, in the server, not once per consuming app.
- •Anti-pattern: hard-coding the integration into each app's prompt duplicates logic and multiplies maintenance cost.
- •Anti-pattern: pasting a data snapshot into context isn't live and doesn't scale.
8.2.4 Transports: stdio vs. Streamable HTTP/Sockets
Once you know you're building an MCP server, the next design decision is how the client talks to it, and there are two transport families to choose between. **stdio** runs the server as a local subprocess that the client launches directly, communicating over standard input and output. This fits a local, single-user integration — think a developer's own Claude Code session spinning up a small local server on their own machine. **Streamable HTTP** (or sockets) is a network transport instead: the server runs somewhere reachable over the network and can serve many clients — multiple users, multiple applications — concurrently.
stdio suits a local subprocess owned by one client; Streamable HTTP/sockets suit a remote server shared by many clients. The deployment shape decides the transport, not preference.
8.2.4 — Key Concept
stdio: local subprocess, ideal for local single-user integrations. Streamable HTTP/sockets: network transport for a remote server shared across multiple clients. Match the transport to the deployment shape described in the scenario — don't default to one option.
8.2.5 The API MCP Connector: mcp_toolset, defer_loading, and enabled
The previous note covered which transport an MCP server uses. This note is about a specific API-level mechanism for attaching a remote server directly to a Messages API request -- the API MCP Connector -- which is a distinctly different concern from transports in general. The connector lets you attach a remote MCP server directly through the Messages API using an mcp_toolset object placed in the request's tools array. That object carries a default_config block (settings applied to every tool on the server unless overridden) plus an optional configs object, keyed by individual tool name, for per-tool overrides.
{
"type": "mcp_toolset",
"mcp_server_url": "https://mcp.example-inventory.com",
"default_config": { "enabled": true },
"configs": {
"delete_record": { "enabled": false },
"bulk_export": { "defer_loading": true }
}
}| Control | What it governs | Category |
|---|---|---|
| defer_loading | Delays loading a tool's definition into context until the model actually needs it | Context-cost / scope mechanism |
| enabled | Turns an individual tool fully on or off | Governance mechanism |
Both are per-tool booleans, and it's easy to conflate them -- but they solve two different problems. A tool can be enabled: true with defer_loading: true at the same time: available to the model, just not loaded into context until it's actually reached for.
8.2.5 -- Exam Trap
Exam trap: a question describing 'reduce context cost from a large connected tool list' points to defer_loading. A question describing 'expose only these specific tools from a connected server' points to enabled. Conflating 'reduce context cost' with 'restrict what the model can do' is the exact mistake this note exists to prevent -- they are independent settings solving independent problems.
Using the API MCP Connector -- the mcp_toolset object, default_config, and configs -- requires the mcp-client-2025-11-20 beta header on the request. Without it, the connector configuration does not take effect as described. And the connector has a hard limit worth remembering on its own: it supports only remote, HTTP-based MCP servers. It cannot reach a local stdio server. If the server you need runs as a local subprocess, the API Connector cannot connect to it -- you need a client that manages that connection directly, such as Claude Desktop or Claude Code, or a self-managed MCP client connection built with an SDK.
8.2.6 MCP Configuration Scope and the Secrets-in-Config Anti-Pattern
An MCP server's configuration lives at one of four scope levels, and each answers a different question about who sees it and where it's stored.
| Scope | Config location | Shared? | Fits |
|---|---|---|---|
| Local | ~/.claude.json, per-project entry | Not shared, not committed | A server tied to one project's context you're not ready to commit to the repo |
| User | Personal settings, applied across all your projects | Personal -- not shared with teammates | A personal utility you use regardless of which codebase you're in |
| Project | .mcp.json at the repository root | Committed to VCS, shared with every clone | A server the whole team needs, traveling with the code |
| Enterprise | Admin-managed, org-wide | Pushed to everyone by an administrator | Shared internal services or security tooling that must be present org-wide |
A subtlety on Project scope: committing .mcp.json shares the configuration, not a running server. For a stdio server, each teammate's clone still spawns its own local subprocess on their own machine -- every teammate needs the same local runtime (e.g., Node.js for an npx-launched server) installed.
Here's a worked scenario showing exactly how the secrets side of this goes wrong. A developer connects to a data-warehouse MCP server and, to get it working quickly, places the server's auth token inline in .mcp.json -- intending to swap it for an environment variable later. But the file gets pushed to the repo first, 'just so the team can keep moving.' Two days later, that same token is sitting in four or more separate places: the original machine, the repo's commit history (a later commit that removes the value doesn't erase it from history), every teammate's clone the moment they pull, and whatever CI runner checked the repo out.
// Before (do not use) -- credential committed directly into version control
{
"type": "http",
"url": "https://warehouse.internal/mcp",
"headers": {
"Authorization": "Bearer sk-abc123..."
}
}
// After (correct) -- config holds only a reference; the value lives in the environment
{
"type": "http",
"url": "https://warehouse.internal/mcp",
"headers": {
"Authorization": "Bearer ${WAREHOUSE_MCP_TOKEN}"
}
}Rotating the now-compromised key is the correct remediation once the exposure is discovered, but it's expensive: every other service still configured with the old key breaks the moment the key rotates, and each of those has to be found and updated separately -- exactly what happened here, costing hours of unplanned work.
8.2.6 -- Key Concept
Removing a secret from .mcp.json in a later commit does not remove it from exposure. Prior commits still carry the plaintext value in history, and the credential must be treated as compromised and rotated regardless of the follow-up commit. The fix is to never let the secret enter version control in the first place: reference an environment variable in the config, not the value.
8.2.7 Putting It Together: Worked Scenarios
The exam tends to describe a concrete situation and ask you to identify either the right primitive or the right transport. Reading the deployment shape and the kind of capability being exposed out of the scenario, rather than pattern-matching on buzzwords, is the actual skill being tested.
| Scenario | Right choice | Why |
|---|---|---|
| A developer wants a personal local server that reads files from their own machine into Claude Code's context | MCP server, stdio transport, resources primitive | Local, single-user, subprocess-owned, and it's readable content being loaded — not an action |
| A company-wide inventory API needs to be reachable from Claude Desktop, Claude Code, and a custom internal app, maintained by one platform team | MCP server, Streamable HTTP transport, tools primitive | Reused across multiple apps, maintained independently, and it performs actions (lookups) — needs to serve many clients remotely |
| A team wants users to invoke a standardized "summarize this incident" template with a couple of fill-in-the-blank slots | MCP server, prompts primitive | A reusable, parameterized template is exactly what the prompts primitive is for, distinct from an action (tool) or raw content (resource) |
Read the deployment shape (local/single-user vs. remote/multi-client) for the transport, and the nature of the capability (action, content, or template) for the primitive.
8.2.5 — Exam Strategy
Where this shows up on the exam: a scenario names a deployment shape (one developer's machine vs. a shared company-wide service) and/or a kind of capability (an action, a piece of readable content, a template). Map the shape to the transport and the capability to the primitive independently — they're two separate questions, not one.
Key Takeaways
- ✓MCP is an open standard — "a USB-C port for AI" — letting a capability be built once as an MCP server and reused by any MCP-compatible client, maintained independently of any single application.
- ✓An MCP server exposes three core primitives: tools (model-callable actions), resources (readable data/content loaded into context), and prompts (reusable, parameterized templates) — not just tools.
- ✓The client lives inside the AI application and connects to one or more servers; each server advertises and handles its own tools, resources, and prompts.
- ✓Build an MCP server when a capability must be reusable across multiple apps AND maintained independently — that specific combination is the canonical use case.
- ✓Hard-coding integration logic into each app's prompt, or pasting a data snapshot into context, are the anti-patterns MCP servers replace — neither is reusable, maintainable, or live.
- ✓stdio is the local-subprocess transport for single-user integrations; Streamable HTTP/sockets is the network transport for a remote server serving multiple clients — match transport to deployment shape.
- ✓Resources come in two shapes: direct (fixed, parameterless address, e.g. a specific file) and templated (parameterized address, e.g. a URI template taking a document ID or date range); client support for injecting a resource directly into context without a tool call varies by client.
- ✓The API MCP Connector attaches a remote MCP server via an mcp_toolset object (default_config plus per-tool configs) and requires the mcp-client-2025-11-20 beta header; it supports only remote/HTTP servers, never local stdio ones.
- ✓defer_loading delays a tool's definition entering context until needed (a context-cost/scope control); enabled turns a tool fully on or off (a governance control) -- two different per-tool booleans solving two different problems.
- ✓MCP configuration has four scope levels -- Local (~/.claude.json, not shared), User (personal, across projects), Project (.mcp.json, committed, shared with clones), and Enterprise (admin-managed, org-wide) -- and secrets must never be placed inline in .mcp.json; reference an environment variable instead, since a committed secret is compromised the moment it enters history, even if a later commit removes it.
Check Your Understanding
Test what you learned in this lesson.
Q1.Which of the following is NOT one of MCP's core primitives?
Q2.A platform team needs an internal REST API to be callable from three separate Claude applications, with the integration logic maintained in one place going forward. What is the best approach?
Q3.A developer wants to run a small MCP server as a subprocess on their own laptop, used only by their own local Claude Code session. Which transport fits?
Q4.A team wants to give users a standardized, parameterized "generate a release summary" template they can invoke with different date ranges, rather than an action that changes anything. Which MCP primitive fits best?
Q5.A team connects an MCP server through the API MCP Connector and wants to (1) keep a rarely-used tool's schema out of context until the model actually needs it, and (2) make sure a destructive delete_record tool is never available to the model at all. Which settings accomplish these two goals respectively?
Q6.A developer commits a data-warehouse MCP server's auth token directly inline in .mcp.json to unblock the team quickly, planning to move it to an environment variable later. Two days later, three teammates have cloned the repo and a CI pipeline has also checked it out. What is true about the exposure at this point?
Practice This Lesson