Anthropic MCP: Claude Clients, Servers, and the Gateway Between
Anthropic MCP is the Claude side of the Model Context Protocol (/industry/mcp-protocol-message-format-explained). Claude Desktop and Claude Code are MCP clients; Anthropic introduced the protocol and publishes its specification and reference servers; and a client reaches a server over stdio when the server is a local process, or Streamable HTTP when it is hosted.
Short answer: Anthropic MCP is the Claude side of the Model Context Protocol. Claude Desktop and Claude Code are MCP clients; Anthropic introduced the protocol and publishes its specification and reference servers; and a client reaches a server over stdio when the server is a local process, or Streamable HTTP when it is hosted. What the protocol deliberately does not answer is who may call, what it costs and what is logged — that is the layer in front of a hosted server, and it is the part most integrations underestimate.
Key takeaways
- One protocol, two transports. Local servers speak stdio; hosted servers speak Streamable HTTP on a single endpoint that accepts POST. The older two-endpoint HTTP+SSE pair is legacy, and servers still answer its requests with a machine-readable migration body.
- The integration lives in a configuration file. A client entry is a name, a URL and an authorization header. If a host cannot read that file correctly, no protocol detail will save the connection.
- The protocol author is not the gateway. Anthropic ships the clients, the specification and the reference servers; it does not meter your tool calls, hold your budget or write your audit row.
- Attribution is cheap and worth having. Tagging each request by the surface it arrived on is three lines of code and turns an unexplained traffic number into a diagnosable one.
- Demand for this topic is modest and specific.
anthropic mcpmeasures about 1,600 monthly searches in the United States, so the page is written for the engineer who lands here mid-integration rather than for a general reader. - Do this next: open the configuration file your client loads, find the server entry, and write down the URL and the header it actually sends — that pair explains most first-connection failures before you read another line of the specification.
Where Anthropic MCP sits: clients, protocol, hosted servers
Three different things travel under the name Anthropic MCP, and separating them saves an afternoon. First there is the protocol: the Model Context Protocol, a JSON-RPC 2.0 interface for tools, resources and prompts, introduced by Anthropic and specified at modelcontextprotocol.io. If that sentence is carrying more than it should, the first-principles protocol guide builds the same definition from the ground up — the two problems that made one wire format worth agreeing on, and the three actors that appear in every session. Second there is the client side — Claude Desktop and Claude Code both speak MCP, as do Cursor, Windsurf, OpenClaw and any host that can keep a server entry in a configuration file. Third there is the server side, which is normally yours: a local process the host spawns, or a remote endpoint the host posts to.
This page is written for the engineer who searched for Anthropic's implementation and landed in the middle of an integration. The practical questions are narrow. Which transport does my client use? What belongs in the configuration entry? What happens when a server retires an endpoint? And who is responsible for the identity, the limit and the log once more than one person shares the server? The protocol answers none of the last three by design — it is a wire format with a capability negotiation, not an access-control system. the protocol walkthrough covers the actors and the lifecycle from the protocol's own side, and standing up an MCP server covers the server half. This page covers the seam between them: what a Claude-family client sends, and what has to sit in front of a hosted server before the calls mean anything operationally.
Model Context Protocol news: the transport migration every client made
The change that has caused the most integration traffic is not in the tool schemas; it is the transport. The original HTTP+SSE design used two endpoints — one event stream to receive on and one POST endpoint to send to. Streamable HTTP replaced it with a single endpoint that accepts POST and may answer with either one JSON body or an event stream, which removes a long-lived connection from the list of things a client must keep alive. A server that has to keep older clients working answers the retired shape with a machine-readable migration notice instead of a bare error:
# lib/connect/mcp-migration-response.ts — source lines 23–28 (legacyMcpMigrationResponse)
function legacyMcpMigrationResponse(
status: 405 | 410,
input: Parameters<typeof legacyMcpMigrationBody>[0],
): Response {
return Response.json(legacyMcpMigrationBody(input), { status });
}
Read the signature before the body. The function takes a status restricted to 405 or 410 — method not allowed, or gone — and returns the migration body with that status attached. The two statuses are a deliberate vocabulary rather than a shrug: 405 says the request reached the right place with a transport the endpoint no longer accepts, and 410 says that transport is retired and is not coming back. Either way the caller receives JSON it can parse instead of an HTML error page, which is the difference between a client that can print an upgrade instruction and one that reports a mystery failure. The payload itself is built by a second function, quoted below, so the notice has exactly one definition no matter which endpoint answers.
That pattern generalizes past MCP. When you retire a wire format, ship the retirement notice in the same commit as the code that consumes the replacement, return it with the same content type as the real responses, and put the new endpoint and the documentation path inside the notice. the transport chapter of the specification is the normative source, and the transport-level walkthrough shows what the two-transport decision looks like inside a deployment.
Model Context Protocol docs: reading the client configuration
Every hosted MCP server is reached through the same three pieces of information: a URL, a transport and a credential. The configuration file a client reads is where those three live, and it is worth reading it end to end before blaming the protocol. The preflight script in this codebase does exactly that, so a broken entry fails before an agent run instead of during one:
# backend/tools/run_mcp_preflight.py — source lines 24–29 (load_mcp_config)
def load_mcp_config() -> tuple[str, str]:
cfg = json.loads(MCP_JSON.read_text(encoding="utf-8"))
sg = cfg["mcpServers"]["smartgate"]
url = sg["url"].rstrip("/")
auth = sg["headers"]["Authorization"]
return url, auth.removeprefix("Bearer ").strip()
Six lines, and each one is a decision. The file is parsed from a constant path rather than from a command-line argument, so the script checks the configuration a host would really use. The lookup is by server name — the mcpServers object, then the entry called smartgate — which is the shape a Claude-family host expects when it opens a session with a server it knows by name. The URL has its trailing slash removed, because an endpoint with a trailing slash and one without are the same resource but not the same string, and the difference surfaces as a doubled slash in a request path at the least convenient moment. The Authorization header is read and then stripped of its bearer prefix, because what the caller needs is the token rather than the header syntax — and a prefix-removal helper does that job safely, returning a clean value even when the prefix was written differently.
What the function returns is a pair, endpoint and token, and the caller can immediately do something useful with it, such as asking for the tool list to prove the credential works. Two habits keep the pattern honest. Keep the credential in the client's own secret store rather than in a checked-in configuration file, and treat the pair as the unit of rotation: one key per client means revoking a lost laptop is a single edit. what the MCP acronym means and where the documentation lives are the two sibling pages that go deeper on naming and on the documentation set.
The MCP specification on the wire: Streamable HTTP, one POST path
A migration notice is only useful if it says what to do instead, and that is the second half of the pair:
# lib/connect/mcp-migration-response.ts — source lines 5–21 (legacyMcpMigrationBody)
function legacyMcpMigrationBody(input: {
origin: string;
error?: LegacyMcpMigrationError;
}) {
const base = input.origin.replace(/\/$/, "");
return {
error: input.error ?? "legacy_sse_removed",
message:
"SmartGate MCP uses Streamable HTTP (MCP 2025-03-26). POST JSON-RPC to this URL only.",
migrate: {
url: `${base}/api/mcp`,
transport: "streamable-http" as const,
method: "POST" as const,
docs: `${base}/docs/mcp-endpoint`,
},
};
}
Three details carry the weight in that returned object. The error code defaults to legacy_sse_removed when the caller did not supply one, so the ordinary migration path and an explicit error path share a single response shape. The message names the replacement in its first clause — Streamable HTTP, at a dated revision — and then states the one thing a client has to change: POST JSON-RPC to this URL, and only this URL. And the migrate object carries the new endpoint, the transport name, the HTTP method and a documentation path, which is the difference between a notice a developer reads and a notice a client can act on without a human. Because the base is derived from the request origin with a trailing slash trimmed, the same function serves every deployment: the caller gets its own host back with the path appended, and no environment string has to be threaded through.
The revision inside that message is a snapshot of when the text was written, not a promise about the current specification. Revisions are dated documents with their own versioning rules, so an integration should negotiate the revision during initialization rather than pin the one it saw in an error message. the specification walkthrough covers the lifecycle and the error model in more depth; the narrower point here is that a machine-readable migration is the cheapest deprecation you will ever ship, and the alternative is a support thread per client.
Model Context Protocol diagram: how an example reaches the page
Documentation for a protocol is mostly pictures made of text: a configuration block, a request and its response, a list of tools with their schemas. The component that renders those examples is small, and its most important line is the one that renders nothing at all:
# components/marketing/feature/feature-mcp-example.tsx — source lines 1–12 (FeatureMcpExample)
function FeatureMcpExample({ code }: { code: string }) {
if (!code.trim()) return null;
return (
<section className="mt-12">
<h2 className="font-heading text-lg font-semibold">MCP example</h2>
<pre className="mt-4 overflow-x-auto rounded-lg border bg-muted/30 p-4 font-mono text-xs leading-relaxed">
{code}
</pre>
</section>
);
}
The guard comes first. An empty or whitespace-only snippet returns nothing, so a page cannot ship a bordered empty box because a copy key went missing — the failure is invisible instead of embarrassing. When there is code, the component wraps it in one labelled block under one heading, with the snippet arriving as a prop. That prop is the architectural choice worth copying: the page does not keep its own copy of the example, so the snippet a reader copies and the snippet a test asserts cannot drift apart. The styling classes are incidental; the single-source rule is not.
Treat this as the template for every how-to page in a protocol cluster. A diagram section should hold exactly one artefact a reader can copy, be generated from the same source the product ships, and vanish cleanly when that source is absent. Pages that paste a second copy of a configuration block are the ones that age badly: the day the configuration changes, half the corpus is wrong and the other half contradicts it.
Model Context Protocol SDK: the tool surface a client sees
After the handshake, a client asks for the tool list and receives a schema per tool. The product side of that same list is what users meet first, and it is kept in one configuration module rather than typed into a page:
# components/marketing/home-sections.tsx — source lines 47–82 (McpBentoSection)
function McpBentoSection() {
const { label, title, subtitle, tools } = MARKETING_COPY.home.bento;
const [selected, setSelected] = useState<MarketingToolId>("smart_fetch");
const detail = MARKETING_COPY.tools[selected];
return (
<section className="py-16 sm:py-24">
<MaxWidthWrapper>
<HeaderSection label={label} title={title} subtitle={subtitle} />
<div className="mt-10 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{tools.map((tool) => (
<button
key={tool.id}
type="button"
onClick={() => setSelected(tool.id as MarketingToolId)}
className={cn(
"rounded-xl border bg-card p-4 text-left transition-colors hover:bg-muted/50",
selected === tool.id && "ring-2 ring-primary",
)}
>
<div className="font-mono text-xs text-muted-foreground">
{tool.id}
</div>
<div className="mt-1 font-semibold">{tool.title}</div>
<p className="mt-1 text-sm text-muted-foreground">{tool.desc}</p>
</button>
))}
</div>
<div className="mt-4 rounded-lg border border-dashed bg-muted/30 p-4">
<p className="font-mono text-xs text-muted-foreground">{selected}</p>
<p className="mt-2 text-sm">{detail}</p>
</div>
</MaxWidthWrapper>
</section>
);
}
The section is a bento — a label, a title and a subtitle drawn from a single copy module, a grid of tools, and one detail panel. Each tool contributes three fields: an identifier, a short title and a description; the panel shows the detail for whichever entry is selected, starting from smart_fetch because that is the call most sessions begin with. Two properties are worth carrying into an integration of your own. The identifiers in the grid are the same strings the server publishes in its tool list, so the marketing surface and the protocol surface cannot disagree about what the tools are called. And the default selection is a decision rather than an accident: the first thing a visitor reads about seven tools is the one that fetches a page and turns it into text, which is also the cheapest way to watch an audit row appear.
the seven tools in detail lists the parameters and annotations. The primitives beside tools — resources, prompts and sampling — are the other half of what an SDK exposes. Whichever way you build the surface, let one module own the names and descriptions and let both the documentation and the server read from it. A tool renamed in one place and not the other costs a support thread every time it happens.
Model Context Protocol, OpenAI clients, and REST: routing one endpoint
The last piece is attribution, and it is deliberately three lines long:
# backend/smartgate/core/audit_enrichment.py — source lines 29–32 (infer_transport)
def infer_transport(path: str) -> str:
if path.startswith("/mcp"):
return "mcp_sse"
return "rest"
A path that starts with the MCP prefix is labelled one way and everything else another. The label value is a historical accident worth naming: it reads mcp_sse, a transport this codebase no longer serves, because the column was added before Streamable HTTP replaced the two-endpoint pair and renaming a stored label would rewrite history in the audit table. Treat the label as an identifier rather than as a statement about the wire. The same gap between a name and the thing it names sits at the top of the stack, where the acronym and the expanded phrase are used on different surfaces: MCP naming and abbreviations works through which spelling belongs where, and why the two drifted apart in the first place. What the function buys is that every request carries a surface tag, so an operator can tell three different incidents apart — a spike in ordinary REST traffic is a client problem, a spike on the MCP path with a single tool name is a loop, and a drop to zero on the MCP path is a broken configuration rather than a quiet day.
That separation matters more as the client mix widens. OpenAI-compatible hosts, editors and other agent frameworks reach the same endpoint through their own clients, and some of them will use the plain REST surface that existed before MCP did. Classifying by path keeps those callers visible without asking anyone to change a configuration, and what MCP and A2A each carry is the page to read when a question moves from one request to a delegated task.
Model Context Protocol registry: how a client discovers a server
A registry answers a question the specification does not: which servers exist, and what each one is. The official registry (modelcontextprotocol.io/registry) is a metadata service — a row per server with the identity of its publisher, the transport it expects and how it is installed. Discovery is not trust, and the two are easy to conflate at the start of a project. A registered row says that a server exists and who claims to own it; it says nothing about whether the tools behind it are read-only, what a call costs, or whether the service will still answer next quarter.
The useful reading of a registry is as a comparison surface. When three servers expose the same capability, the metadata narrows the list to the ones that publish a transport you can serve and a package you can audit, and the rest is the ordinary work of reading code. On a client that expects one named entry in a configuration file, discovery matters even less than it looks, because you will name a single server and keep it there. The registry earns its place at the moment of choosing which server to name — and it is worth the same instinct you would apply to any package index: a listing is a starting point for a review, never a substitute for one.
Model Context Protocol servers on GitHub: judging one before you install it
The reference servers repository (github.com/modelcontextprotocol/servers) is where most first integrations begin, and it doubles as a benchmark for any server you are considering: the examples are small, each one declares its transport, and none of them pretend to be a platform. Five questions settle most candidates faster than reading the whole tree.
Does it declare a transport, or does it assume one? A server that only speaks stdio cannot be shared across machines, and a server that only speaks HTTP needs an identity story before it leaves your laptop. Does it require a credential per call, and where does that credential live? A server that reads a provider key from the environment hands every one of its clients the same key. Does it mark which tools are read-only? A schema without annotations asks the host to guess, and hosts guess generously. Who maintains it, and when was the last release? And finally: does it wrap a paid API, in which case the cost of a runaway loop is somebody else's invoice? None of those are protocol questions, and all of them decide whether the integration is pleasant or expensive.
Model Context Protocol inspector: debug the handshake before the tools
When a connection fails, the failure is almost never in the tool logic; it is in the handshake, the headers or the configuration. The inspector (github.com/modelcontextprotocol/inspector) exists to take those apart one at a time: point it at a server, watch the initialization exchange, list the tools, then call one. If the inspector succeeds and your host fails, the protocol is fine and the difference is in your configuration — a missing header, a trailing slash, a transport the host did not expect. debugging tools compared covers the wider set, including log-based approaches for servers you cannot drive interactively.
Two habits make the inspector pay for itself. Capture the initialization exchange once and keep it: the negotiated revision and the capability map are the reference you compare against when a client starts behaving oddly after an upgrade. And test with the credential your production client uses — a session opened with a personal token proves that the server works, not that the deployment does.
Model Context Protocol security: keys, sessions, and blast radius
Three mistakes account for most incidents in MCP deployments, and the specification's own guidance (security best practices) names them: treating a session identifier as authentication, passing a caller's token through to an upstream service, and giving every client the same credential. A session identifier is a routing hint that a server may or may not issue; it is not an authorization decision, and a server that accepts one as proof of identity will accept a guessed one. Token passthrough is the second: forwarding the credential you received to a third-party API turns one compromise into a chain and makes the audit row name the wrong principal. The third is the boring one — a single key shared by a laptop, a CI job and a staging host cannot be revoked for any of them without breaking the other two.
The specification's authorization chapter is written for HTTP servers that act on behalf of an end user, which is a larger problem than a single-tenant gateway has. The simpler version of the rule is the one to apply first: give every client its own credential, make the blast radius of that credential visible, and authentication for MCP traffic walks through the options if you need more than that. Whichever you choose, rotate the credential that appears in a configuration file, and record which key made each call.
How SmartGate compares
The choice is not between protocols; it is about where the identity, the limit and the log live.
| What it is | Where the credential lives | What it costs | |
|---|---|---|---|
| Claude Desktop, Claude Code | MCP clients that spawn local servers or post to remote ones | On each machine, in the client's configuration file | Nothing charged per call by the client |
| A self-hosted MCP server | Your tool surface, in your process or on your host | Wherever you decide to put it | Infrastructure plus whatever metering you build |
| A hand-rolled proxy | Routing, auth and logging glue in front of the server | In your code | Maintenance, and failure modes nobody has priced |
| SmartGate | A hosted MCP endpoint with seven tools, per-key rate limits, a hard budget guard and an audit row per call | One key per client, rotated on its own | Free tier: 2M tokens per month, all seven tools, no card required. Pro from $18/month, Teams from $55/month |
The reason to put a layer in front is that the protocol has no opinion about any of it. Limits that live in the gateway see the same traffic the meter sees, which is what stops a rate limit and a spend limit from disagreeing about who was calling, and the audit row is written on the way through rather than reconstructed from logs afterwards. what a gateway adds is the longer version of that argument.
How to get started
- Read the configuration you already have. Find the server entry your client loads, remove any trailing slash, and confirm the credential is the one you think it is. Most failed first connections end here.
- Point one client at a hosted endpoint. Add a server entry whose URL is the MCP path and whose header is a bearer token, then restart the host so it re-reads the file.
- Ask for the tool list. A successful tool listing is the proof that the credential and the transport are both right, before any tool logic runs.
- Call one tool and find the audit row. If the row is missing, the request never reached the gateway, which makes it a configuration problem rather than a tool problem.
- Set a limit and a budget before letting an agent loose. A per-key rate limit and a hard budget stop an unattended loop; nothing in the protocol will.
- Then read the rest of the cluster. The transport-level walkthrough and the specification walkthrough answer the next two questions.
Start on the free tier — 2 million tokens a month and all seven tools — with start free; the per-plan limits are on the pricing page, the endpoint shape is in the product docs, and contract traffic starts with the contact form.
Frequently Asked Questions
Limitations and what this does not do
- Six of ten planned sections carry code. The registry, GitHub-server, inspector and security sections matched no unique symbol in the codebase, so they are written from the published specification and the vendors' own documentation, with no quoted implementation. Where a section shows no fence, that is the reason and not an omission.
- A quoted revision string is a snapshot. The migration message names a dated revision; the specification may have moved since. Negotiate the revision during initialization rather than pinning the one an error message happened to show.
- One audit label is historically named. The transport classifier reports mcp_sse for MCP-path traffic even though that transport is retired. It is an identifier in a log table, not a claim about how the request arrived.
- The client-side code here is one host's preflight, not a client library. It reads a configuration file and returns a URL and a token; it does not implement JSON-RPC, sessions or retries, and it is not a substitute for a client that does.
- This page is not a replacement for the specification. It is the ecosystem view — what Anthropic ships, what a client sends, and what belongs in front of a hosted server. The lifecycle and the message shapes are documented in the specification itself.
Sources
- Anthropic — introducing the Model Context Protocol: https://www.anthropic.com/news/model-context-protocol
- Model Context Protocol — transports, lifecycle, tools, authorization and security best practices, under revision 2025-06-18: https://modelcontextprotocol.io/specification/2025-06-18/basic/transports · https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle · https://modelcontextprotocol.io/specification/2025-06-18/server/tools · https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization
- Model Context Protocol — the official registry: https://modelcontextprotocol.io/registry
- Reference servers and the inspector on GitHub: https://github.com/modelcontextprotocol/servers · https://github.com/modelcontextprotocol/inspector
- SmartGate — documentation, pricing and sales contact: https://smartgate.network/docs · https://smartgate.network/pricing · https://smartgate.network/contact
Method note
The code in this article is not transcribed. Each block was cut directly out of the slice body returned by the SmartGate slice API and then re-asserted byte-for-byte as a substring of that body before publication; the first line inside every fence records the file and the exact source lines. Symbols were pinned with whole-name containment (rule A level 2) and confirmed by the service's slot-proof endpoint — 6 of 10 planned sections pinned, no abstentions. Four sections (the registry, the GitHub server checklist, the inspector and the security review) matched no unique symbol and are written from the published specification with no code, as the abstain rule requires. The client-side view here is the gateway's own configuration and marketing path, not a third-party client implementation.
Slice provenance
| # | SERP keyword | Symbol | File | Source lines | How it was pinned | sha256(12) |
|---|---|---|---|---|---|---|
| 1 | model context protocol news | legacyMcpMigrationResponse |
lib/connect/mcp-migration-response.ts |
23–28 | rule A L2 → slot-proof | ba1823c46b64 |
| 2 | model context protocol docs | load_mcp_config |
backend/tools/run_mcp_preflight.py |
24–29 | rule A L2 → slot-proof | 273bbfcbd5d3 |
| 3 | model context protocol specification | legacyMcpMigrationBody |
lib/connect/mcp-migration-response.ts |
5–21 | rule A L2 → slot-proof | 617eba28a538 |
| 4 | model context protocol diagram | FeatureMcpExample |
components/marketing/feature/feature-mcp-example.tsx |
1–12 | rule A L2 → slot-proof | fefae265eee4 |
| 5 | model context protocol sdk | McpBentoSection |
components/marketing/home-sections.tsx |
47–82 | rule A L2 → slot-proof | d414563f9bf7 |
| 6 | model context protocol openai | infer_transport |
backend/smartgate/core/audit_enrichment.py |
29–32 | rule A L2 → slot-proof | fc93297c9232 |
Every fenced block above was cut from the slice body and re-asserted against it byte-for-byte before publication. 6 of 10 sections pinned, 0 abstentions, 4 misses.