# Guide: Build a Class Profiles MCP Server

*Implementation guide. This draft ships fixture definitions but no official runner. Within the experiment, the authoritative sources are the selected `profile.json`, [`spec/02`](../spec/02-descriptor.md), and [`spec/03`](../spec/03-conformance.md).*

## Why implement this

Not so your server becomes interchangeable with a competitor's. **So that a model already knows how to call it.**

An agent meeting an unfamiliar server has to work out what its tools mean: read the prose, infer the argument shapes, guess whether a retry is safe, discover by failing which jobs are unsupported. That inference is the tax of being unfamiliar, and it is paid on every turn, by every agent, forever. It also produces the tickets you cannot debug — the invented identifier, the argument you would have defaulted, the duplicate write after a dropped stream. The failure happened in someone else's inference.

A profile is a language. Implementing one means an agent that has learned that language does not have to work anything out about *you*.

Nothing here enforces anything. The table below is not a set of guards; it is what a fluent caller already knows, so the wrong call is never formed:

| Without a shared language, an agent must work out… | With one, it already knows |
|---|---|
| whether an identifier can be constructed or parsed | identifiers are opaque and are only ever echoed back ([`05 §1`](../spec/05-kernel.md#identity)) |
| whether an empty-looking answer is real | a result is never fabricated to avoid an error ([`05 §5`](../spec/05-kernel.md#errors)) |
| which arguments are actually needed | omitted optionals take declared defaults ([`02 §6`](../spec/02-descriptor.md#defaults)) |
| what a long description is really saying | the summary is ≤100 characters and restates nothing ([`02 §3`](../spec/02-descriptor.md#summary)) |
| how a nested schema composes | advertised schemas are shallow and standalone ([`02 §4`](../spec/02-descriptor.md#tool)) |
| which jobs this backend supports | absent facets are declared, with what is lost ([`02 §8`](../spec/02-descriptor.md#degrades-to)) |
| whether re-issuing after a dropped stream is safe | the re-issue disposition is declared ([`05 §4.1`](../spec/05-kernel.md#reissue)) |
| whether an act is visible to other people | `socially_irreversible` says so ([`05 §4`](../spec/05-kernel.md#effects)) |
| whether it has seen every result | an absent cursor proves the view complete ([`05 §6`](../spec/05-kernel.md#pagination)) |

Three things worth knowing before you start.

**The agent learns the class, not your server.** One profile is read once and applies to every implementation of it — including yours, including ones written later. `SKILL.md` is that manual. You are not competing for space in a model's attention with a vendor-specific document; you are inheriting one that is already there.

**You benefit unilaterally, and partially.** None of this requires the calling agent to know what a Class Profile is — a conformant server is, to any ordinary MCP client, just a server whose surface is unusually easy to read correctly. And the benefit is a gradient, not a gate: profiled verbs are cheap to call, and the rest of your surface costs what it always did. You can profile six verbs and keep forty vendor tools.

**The fixtures are a test suite you did not have to write.** The Calendar package ships 15 scenarios pinning half-open range boundaries, pagination disjointness, read-only proof by snapshot comparison, and repeated-cancellation behavior — the cases where real implementations diverge. Run them against your server today regardless of what you decide about the rest of this proposal.

MCP revision `2026-07-28` additionally requires clients to treat tool annotations as untrusted unless they come from a trusted server. Fixtures are how your `readOnlyHint` and `idempotentHint` become checkable claims rather than assertions a client is told to discount.

## Contract of this guide

Input: a profile package, a target language or framework, and a backing store or upstream API.

Output: an ordinary MCP server whose declared tools implement the selected baseline and optional facets.

Definition of done: the advertised surface is generated from the descriptor, behavior is exercised against every applicable fixture by an independent harness, and limitations are reported without a conformance Claim. An official Claim becomes possible only when an official runner binds a passing run to published artifact digests.

## Step 0 — Load the authoritative inputs

1. [`profiles/calendar/profile.json`](../profiles/calendar/profile.json) — the machine-readable contract.
2. [`spec/03-conformance.md`](../spec/03-conformance.md) — fixture and runner semantics.
3. This guide.

Do not implement from `PROFILE.md`; it is a human view. The private contract may be large because it is build and validation material, not resident model context.

## Step 1 — Generate the baseline surface

For each `verbs[]` entry whose `tier` is `baseline`:

- register an MCP tool named exactly by `name`, such as `calendar.create_event`;
- copy `tool.inputSchema` verbatim to MCP `inputSchema`;
- copy `summary` verbatim to the MCP description;
- omit MCP `outputSchema`;
- validate returned `structuredContent` privately against `contract.structuredContent`;
- emit MCP annotations only where the member's `risk` differs from MCP defaults; and
- apply every declared input default when its argument is omitted.

Return sparse structured content: omit nulls, empty optional collections, default-valued status fields, echoed arguments, and absent cursors. When a contract requires media, use native MCP content blocks.

Then select only facets the implementation can honor completely. Inject each claimed property's `tool` fragment into its declared request attachment and its private `contract` into every declared attachment. Unclaimed properties remain absent. This is deterministic compilation from the descriptor, not local schema design.

## Step 2 — Implement the Calendar semantics

The common failure modes are contractual, not stylistic:

1. **Cancellation leaves a tombstone.** Repeated cancellation preserves the same terminal state, and `calendar.get_event` returns `state: "cancelled"`, not a fabricated object or silent success. The profile does not claim MCP idempotence because duplicate attendee notifications are not observable through this surface.
2. **Update is a patch.** Merge only provided fields. A title patch must not change location, start, or end. Explicit `null` clears only fields whose patch schema admits it; cleared values are omitted from results.
3. **Timestamps retain explicit offsets.** Store them however the backend requires, but emit RFC 3339 with an offset and honor the event's IANA timezone.
4. **Unknown identifiers fail loudly.** Return JSON-RPC `-32602`; never invent an object to avoid an error.
5. **Free-time search is correctness-first.** Every returned slot fits the requested duration, stays inside the window, and overlaps no confirmed event.

## Step 3 — Provide an isolated sandbox

The descriptor's `sandbox_contract` requires disposable state. A local implementation might enable it with `CLASSPROFILES_SANDBOX=1`, but the mechanism is implementation-defined. Before every fixture scenario, it must isolate all operations from user data and reset to an otherwise-empty calendar in the reference week. The sole exception is the RSVP facet scenario, whose reset contains exactly the declared invitation addressed to the sandbox principal.

Do not add privileged behavior unavailable through the public MCP surface. Fixtures call the same registered tools as an ordinary consumer.

## Step 4 — Exercise every applicable fixture

This draft contains the fixture documents and runner semantics, but no official executable runner. Until one exists:

- use an independent harness that speaks plain MCP;
- record the harness version and exact draft commit;
- run the complete baseline suite, plus every declared facet suite;
- treat skipped unmet facet requirements as skips, never passes; and
- report results as implementation feedback, not an official receipt or Claim.

A partial baseline result is evidence for debugging, not support for the baseline. If a fixture appears wrong, report an ambiguity against the profile instead of weakening it locally.

## Step 5 — Describe the implementation honestly

A future portable card will name the exact baseline, its digest, facets, provenance, and receipt as defined in [`spec/01 §6`](../spec/01-concepts.md#card). Because Draft artifacts are undigested and have no official runner, a server may make a clearly labelled prototype declaration for experimentation but MUST NOT attach a passing Claim.

Record at least:

- implementation identity and version;
- exact Class Profiles draft commit;
- baseline and facets attempted;
- native or projected provenance; and
- fixture outcomes and known limitations.

## Step 6 — Report like a colleague

Tell reviewers which behaviors were exercised, which facets were attempted, what failed, and what ambiguity was resolved conservatively. Include reproducible commands for the independent harness. Do not compress “mostly passes” into “conformant.”
