|
cMCP 0.4.1
Model Context Protocol library in pure C11
|
Written 2026-05-30, immediately after D1 dogfooding.
The Tier 6 cut at v0.5.0 hit an internal-quality bar: 22 test binaries green, 50k+ calls/s, Ajv-parity schema validator, threat model, packaging, retro-tags. None of those numbers measured the library against the seat of a host author actually trying to ship something.
The 2026-05-30 council session was convened to pick the next path. Five advisors converged on the same uncomfortable observation: cMCP has 2922 assertions and zero users. The peer reviewers added a council-wide blind-spot: no proposed path had a stated exit condition. Without one, Tier 7 (regression gates) would polish forever; Path B (deferred protocol work) would chase spec forever; any "spike" would manufacture urgency.
D1 produced evidence (see `dogfood-cragmcp.md`). This document is D2: the written exit condition for v0.6.0. It is the artefact every council reviewer flagged as missing.
v0.6.0 is the first release shaped by host-side evidence rather than spec-reading or internal-test green. It closes the API-ergonomics gaps F1–F4 from D1, nothing else.
cmcp_client.h gains the same typed shortcuts that cmcp_session.h already exposes, so a host talking to one server doesn't have to drop to raw JSON walking or wrap one server in a session just to get them.
Re-use the cmcp_session_tool_t / _resource_t / _prompt_t shapes that already exist. Pair each list helper with the existing cmcp_session_*_free to avoid a doubled-API surface. No client-side struct duplication.
tools/call carries two failure channels. A host should write one branch per outcome, not a dance through response.error then result.isError. v0.6.0 ships:
Exactly one of out_result / out_text / out_rpc_err is populated on return. Caller owns the populated one (free via the matching cmcp_json_free / free / cmcp_rpc_error_free).
include/cmcp_json.h's struct cmcp_json gains an explicit @warning block directing host code to use the typed accessors (cmcp_json_string(), cmcp_json_string_len(), cmcp_json_bool(), cmcp_json_array_at(), cmcp_json_array_len()) rather than poking the union directly.conformance/playbooks/crag-mcp.md T5 is updated to reflect the actual MCP 2025-11-25 convention (schema rejection surfaces as result.isError:true + content text, NOT as JSON-RPC -32602). Same sweep covers any other playbook still claiming -32602 for schema rejection.Nothing is removed. No struct layout change. No protocol change. SemVer-minor.
The release ships when all four conditions are simultaneously true. This is the council's missing exit condition.
tools/dogfood-crag-host/main.c is rewritten to use ONLY the new typed helpers from A1 + A2. After the rewrite:
cmcp_json_object_get(...) calls in the harness.cmcp_rpc_message_t declarations or cmcp_rpc_message_clear calls in the harness.switch (cmcp_client_tool_call(...)) per call site — no inspection of response.error and no inspection of result.isError from the host's seat.O1 is the only criterion that proves A1/A2 are well-shaped. Everything else (tests, conformance, fuzz) is hygiene.
| Gate | Command | Required state |
|---|---|---|
| Unit tests | make test | 22+ binaries pass, ≥2922 assertions |
| Valgrind | make valgrind | leak-clean |
| Sanitisers | make test-asan / make test-tsan | clean |
| Conformance | make conformance | green vs TS reference SDK |
| Schema conformance | make schema-conformance | ≥83/83 cMCP vs Ajv agreement |
| Replay | make replay | every tracked fixture passes |
| Fuzz smoke | make fuzz-smoke | no crashes (60s/harness × 4) |
New helpers ship with positive + negative tests in tests/test_client_* that mirror the cMCP test convention (one test_<name> function per behaviour). Aim for ≥30 new assertions.
conformance/fixtures/crag-mcp/dogfood/session-2026-06-XX.jsonl is re-captured by re-running make dogfood-crag-host after A1/A2 land. Registered in conformance/replay/fixtures.json. After that, the dogfood pass becomes a permanent regression gate via make replay.
CHANGELOG.md gains a v0.6.0 section above v0.5.0, naming A1/A2/A3 and citing the council session + docs/dogfood-cragmcp.md as the origin.README.md Status section flips: "v0.6 — first host-driven cut."docs/SEMVER.md retro-tag chain gains v0.6.0 (with the closing commit SHA pinned, per the post-Tier-6 fix).include/cmcp.h CMCP_VERSION advances to "0.6.0".v0.6.0 pushed to origin.The council-D1 findings F5 and F6, and the entire Tier 7 axis, are out of scope for this release. Each has a reason.
cmcp_json_new_string (verb-first) vs cmcp_json_object_set (subject-first) is a paper cut, not a barrier. Renaming is an ABI break — folds into v0.7.0.TODO.md. They become v0.7's tier. The principled reason: Tier 7 gates baselines, but until A1/A2 land, the baseline is the wrong shape — the regression fixtures Tier 7 would protect are the ones the rewritten dogfood harness produces. Sequencing is forced.| Axis | Estimate |
|---|---|
| A1 typed helpers (impl + tests) | ½ day |
| A2 flattener + outcome enum | ½ day |
| A3 doc updates | ½ day |
| O1 harness rewrite | ½ day |
| O3 fixture re-capture | ¼ day |
| O4 release-cut paperwork | ¼ day |
Total: ~2.5 days. Aim: v0.6.0 cuts ≤ 7 calendar days from v0.5.0 (2026-05-29) — so on or before 2026-06-05.
These let v0.6.0 ship without scope creep:
resource_read handles a binary blob content item): document the decision in include/cmcp_client.h near the declaration, choose the simplest semantics, ship. Do not add a second helper for the alternate case.docs/dogfood-cragmcp.md. Do not retroactively expand the v0.6.0 scope. A v0.6.x patch series is fine.nolint, --no-verify, or sanitiser-disable.v0.6.0 work stops and reverts to the design table if:
cmcp_session.h change. Indicates the typed shapes aren't reusable, which is a deeper question (probably v0.7.0 architecture-level).cmcp_json_object_get to zero. Indicates a missing helper. The right move is to identify which one and ship it inside A1's batch.The release is done when these are simultaneously true:
make replay).If you can answer "yes" to all five, cut v0.6.0. If you cannot, do not cut. There is no third option.