|
cMCP 0.4.1
Model Context Protocol library in pure C11
|
cMCP follows Semantic Versioning 2.0.0, with the clarifications and conventions below. This document is the contract: what a downstream consumer can rely on from one cmcp X.Y.Z to the next.
| Bump | When |
|---|---|
| MAJOR | Any change to the public surface that breaks the ABI or API. |
| MINOR | New functions, new fields in tagged-extensible structs, new env |
| variables. No removal, no signature change, no struct-layout | |
| change. | |
| PATCH | Bug fixes, performance improvements, doc/test changes. No |
| observable change to the public surface. |
CMCP_PROTOCOL_VERSION (the MCP wire protocol revision cMCP speaks) is independent of the cMCP package version (CMCP_VERSION) — see Protocol version vs package version below.
The public surface is exactly the set of identifiers declared in:
Plus the documented set of environment variables (see README.md and the table in CLAUDE.md for the in-tree mirror).
Everything in src/*.h, src/*.c, tools/, tests/, fuzz/, conformance/, examples/, and bench/ is internal. It may change in any release, including patch releases. Downstream consumers must not include it directly or link against it as a stable surface. The reference binaries cmcp-inspect, filesystem-mcp, and cmcp-tee are installable but their CLI flags are not covered by this policy — their job is to be useful, not to provide a stable scriptable surface.
A MAJOR bump (e.g. 1.x.y → 2.0.0, or eventually 0.x.y → 1.0.0) is required if any of the following changes occur in the public surface:
typedef struct foo foo_t; with no members exposed) can grow freely.A MINOR bump (e.g. 1.4.0 → 1.5.0) covers additions only:
cmcp_err_t, never inserted in the middle — that would change the numeric encoding of existing values and bump MAJOR).cmcp_server_capabilities_t and cmcp_client_capabilities_t are designed for this; growing them remains MINOR).Bug fixes, performance work, internal refactoring, documentation, tests. Nothing observable from the public surface changes. PATCH bumps are always ABI-compatible and source-compatible with the same MAJOR.MINOR.
cMCP is in 0.x.y. Per SemVer's own clause 4: *"Anything MAY change at
any time. The public API SHOULD NOT be considered stable."* In practice we still follow the policy above during 0.x — additions go in MINOR, breaks force MINOR (since MAJOR is pinned at 0). Treat every 0.x → 0.x+1 minor bump as potentially ABI-incompatible; treat 0.x.y → 0.x.y+1 patch as ABI-stable.
When cMCP cuts 1.0.0, the policy above becomes binding — no breaking change without a MAJOR bump.
The installed cmcpConfigVersion.cmake exposes SameMinorVersion compatibility while we are 0.x — find_package(cmcp 0.5) matches any installed 0.5.z but rejects 0.6.z or 0.4.z. Once we hit 1.0.0, this becomes SameMajorVersion.
pkg-config has no built-in compatibility relation. The installed cmcp-core.pc exposes Version: $VERSION and downstream Requires: should pin a range (e.g. Requires: cmcp-core >= 0.5.0, cmcp-core < 0.6.0) — the equivalent of the CMake SameMinorVersion rule, expressed by hand at the consumer site.
cMCP carries two version numbers, deliberately separate:
| Macro | What it identifies |
|---|---|
CMCP_VERSION | The cMCP package release (this SemVer policy). |
CMCP_PROTOCOL_VERSION | The MCP wire protocol revision (e.g. 2025-11-25). |
A package release can bump CMCP_PROTOCOL_VERSION without changing the public C surface — that goes in a MINOR (new behavior added) or even PATCH if the protocol bump is a tightening of an existing behavior. A package release can also change the public C surface without touching CMCP_PROTOCOL_VERSION — that goes by the rules above.
The two version numbers move on independent timelines. The spec-version-upgrade workflow documents how CMCP_PROTOCOL_VERSION is bumped; this file is silent on it.
Every release lands as an annotated git tag named v$VERSION (e.g. v0.4.1). The tag message is the corresponding CHANGELOG section so git show v0.4.1 reproduces what shipped without leaving the repo.
To retro-tag the pre-policy releases, the maintainer runs the commands at the bottom of this file once.
These are not part of the SemVer contract — they may change in any release:
src/*.h. Tagged-internal even when they look API-shaped.CMCP_LOG_JSON=1) keep their key set stable within a MAJOR but add new keys freely.cmcp-inspect --help is honest; cmcp-inspect itself is not a script-stable surface.make targets are additive; the set of CFLAGS/LDFLAGS we accept may grow.Pre-policy releases 0.1.0 through 0.4.1 shipped without git tags. To attach annotated tags pointing at the closing commit for each tier, the maintainer runs once:
Each SHA above was verified against the matching CHANGELOG.md release date: the commit's author-date matches the date stamped on the release section, and no later commit changes anything user-visible between the tag point and the next release. If the history is ever rewritten such that one of these SHAs no longer exists, regenerate the list from git log --oneline --reverse rather than re-running a grep-based search.
v0.5.0 is the first post-policy release: it gets its tag at release time (not retro), pointing at the "cut v0.5.0" commit:
v0.6.0 is the first host-driven cut — sized by the dogfood findings F1–F4 against a real consumer (tools/crag-mcp/ driven by tools/dogfood-crag-host/) rather than by a spec axis. The tag is applied at release time against the "cut v0.6.0" commit (the paperwork roll-up that lands CHANGELOG, README, cmcp.h CMCP_VERSION bump, and this file):
v0.7.0 bundles the host-API extensions that closed the two v0.7-candidate findings the v0.6.0 dogfood rewrite surfaced (A4 async typed tools/call pair; A5 content-shortcut) with the first Tier 7 ops axis (schema-conformance Ajv corpus 83 → 500). The four remaining Tier 7 axes (perf-regression CI gate, nightly fuzz, nightly soak, coverage delta) are deferred — each gates on an infrastructure/policy decision and the work that's ready ships rather than waiting. The tag is applied at release time against the "cut v0.7.0" commit (the paperwork roll-up that lands CHANGELOG, README, cmcp.h CMCP_VERSION bump, and this file):
v0.8.0 closes Tier 7: the four axes deferred at v0.7.0 land together as regression gates over the Tier 6 baselines. 7.1 perf-regression CI gate (median-of-11 vs bench/baseline.json), 7.2 nightly fuzz baselines (6h × 4 harnesses, cron), 7.3 nightly soak runs (6h stdio + 6h HTTP, local-cron orchestrator), 7.4 coverage delta gate (1.0pp band on lines + functions, via actions cache). Additive only — no public-surface change, no protocol bump, no struct layout change. The tag is applied at release time against the "cut v0.8.0" commit (the paperwork roll-up that lands CHANGELOG, README, cmcp.h CMCP_VERSION bump, and this file):
Going forward, every CHANGELOG release section ships with its tag at release time — no retro work needed.