« Phase 02 · Warmup · Track Overview

Hitchhiker's Guide — MCP & the Tool Estate

The 30-second mental model

MCP turns M×N integrations into M+N: one protocol between an agent host and a tool provider. JSON-RPC 2.0, a capability-negotiating handshake, three primitives.

Then it stops. No authorization, no tenancy, no versioning, no tool identity, no idempotency, no audit. Those are the platform's, and they are the actual job. "We use MCP" is a transport decision, not a tool strategy.

The numbers and codes

ThingValue
Parse error−32700
Invalid request−32600
Method not found (incl. unentitled tool)−32601
Invalid params (incl. schema violation)−32602
Internal error−32603
Server-defined range−32000 … −32099
Protocol version formata date, e.g. 2025-06-18
Clients per serverexactly 1
60 tools × 200-token descriptions12 000 tokens before the user speaks

The three primitives, by who controls them

Controlled byRisk
Toolthe modelhighest — needs schema, scope, side-effect class
Resourcethe applicationthe choice is trusted; the content never is
Promptthe userlowest

The who-breaks rule

Callers break when the contract gets stricter.

MAJORMINOR
add a required propertyadd an optional property
remove a propertyremove a required property
change a type
narrow an enumwiden an enum
tighten a boundloosen a bound
close additionalProperties

One-liners

  • Notification — no id, never answered. Half of all JSON-RPC bugs live here.
  • Lenient negotiation — an unknown protocol version offers a fallback; it never fails the handshake. A skew must degrade one connection, not the host.
  • Filter before you listtools/list is a query with a principal, not a catalogue.
  • Unentitled == nonexistent — same error, same shape, or your estate is enumerable by diffing messages.
  • Protocol error vs tool error — a schema violation is -32602 and never reaches the tool; a downstream 503 is a successful result with isError: true, so the model can adapt.
  • Repair syntax, never semantics — coerce "42"42; never invent an account number.
  • Immutable versions — a republish makes every pin and every reproducibility claim false.
  • Deprecated ≠ retired — deprecated stays resolvable by pin. That is what a migration window is.
  • listChanged or the window is theatre — clients cache; without the notification they call retired tools.
  • Side-effect class is required, with no default — a default of read makes a payment tool retryable.

Vocabulary

Host / client / server · the app, one connection object per server, the tool provider. Capability negotiation · both sides declare what they support at initialize. Sampling · server asks the client's model for a completion. Elicitation · server asks the user for input. inputSchema · the tool's JSON Schema contract. Prompt surface · text the model reads every turn — descriptions are this. Pin · a caller's version constraint (^1.2.0). Deprecation window · the period a pinned old version keeps working.

War stories

The Tuesday that broke four teams. payments.lookup gained a required as_of argument, released as 1.1.0 "because it's just one field." Every caller that omitted it started failing within the hour. Three controls would each have stopped it: classification at publish, pins, and listChanged.

The enumerable estate. Unentitled tools returned 403 forbidden; nonexistent ones returned 404. A red-team exercise mapped the entire tool catalogue — names, and therefore capabilities — by diffing responses, without ever calling anything successfully.

The 12 000-token preamble. Every agent got every tool. Cost per turn was dominated by tool schemas, and selection accuracy was poor because the model had sixty choices. Filtering discovery by scope and task cut context 90% and raised task success more than a model upgrade had.

The retryable payment. side_effect defaulted to read. A payments tool was registered without setting it. The gateway's retry-on-timeout logic did exactly what it was told.

The helpful server. A third-party MCP server's tool description contained instructions addressed to the model. It was, by construction, a prompt injection on every turn — and it was "just documentation," so nobody reviewed it.

Beginner mistakes

  1. Replying to a notification.
  2. tools/list as a static list.
  3. Filtering in the client after the model has seen the list.
  4. Different errors for unentitled vs nonexistent.
  5. Platform metadata in the description.
  6. Breaking change as a minor bump.
  7. Mutating a published version.
  8. No listChanged.
  9. isinstance(v, int) accepting True for an amount.
  10. Repairing semantics — inventing values.
  11. Failing the handshake on an unknown version.
  12. Assuming "application-controlled" resources are safe content.

What "good" sounds like

"MCP is the transport. The estate is the work: immutable semver'd tools in a registry that classifies every schema change and refuses an under-bumped breaking one, discovery filtered by scope, tenant and classification before the list is built, an unentitled tool returning the same error as a nonexistent one so the catalogue isn't probeable, and every tool carrying a required side-effect class the platform derives retry policy from. Schema violations are protocol errors that never reach the tool and get one deterministic repair pass first; downstream failures come back as results with isError so the model can adapt. And I'd want workload identity on the servers themselves, because nothing in the protocol tells me this server is really the payments team's."