ADR 0001 — The MCP layer is the universal source of truth for agent conventions
- Status: Accepted
- Date: 2026-06-02
- Tracking: legacy tracker 1316 (agent self-sufficiency). This ADR closes the “decide first” sub-issue legacy tracker 1317.
- Amended: 2026-07-02 — the Tier taxonomy below is forked by ADR 0008 (legacy tracker 1693); see “ADR 0008 update” at the end of this document for what changed and what didn’t.
Context
madtea ships as both a CLI binary and a Model Context Protocol (MCP) server, and as a Claude Code plugin that bundles skills and PreToolUse hooks around that same binary. The plugin manifest advertises support for “Claude Code, OpenAI Codex CLI, and Google Gemini CLI”, and the README documents install paths for those plus Cursor, Windsurf, Cline, Zed, and “any MCP client”.
That breadth creates a hazard. Today an agent only drives the forge
correctly on the first try because the operator’s personal
CLAUDE.md (or AGENTS.md) already encodes the conventions: use madtea
for remote operations, never raw git push/fetch/clone, complete a
branch with madt_finish, use closes to close issues (branch-detected
issues are only referenced), batch IDs rather than looping. None of that
travels inside the package. For the public release the goal (legacy tracker 1316) is
self-sufficiency: a fresh install into a clean environment with an
empty CLAUDE.md/AGENTS.md must let the agent use the tools
correctly without the operator pasting anything into their own config.
The surfaces that could carry a convention differ sharply in reach:
- The MCP server
instructionsstring (serverInstructions()ininternal/mcp/server.go), the consolidated tool schemas (themadt_*tools registered acrossinternal/mcp/), and themadt_help(topic)manual are delivered by the MCP protocol itself. Every MCP client receives them — Claude Code, Cursor, Codex, Cline, Gemini, Zed, custom clients alike. - The Claude Code plugin’s skills (
skills/dev-workflow,skills/forge-workflow), its PreToolUse hooks (hooks/scripts/check-git-remote.sh,check-git-commit.sh,check-git-local.sh), and any slash commands are read only by Claude Code. The README already states this plainly: “Only Claude Code uses the plugin’s bundled skills and safety hooks; other clients get the MCP tools alone.”
If a safety rule or a correctness fact lives only in a skill or a hook, an agent on Cursor or Codex never sees it. A rule whose enforcement is a Claude Code hook silently does nothing on a non-Claude-Code client. So where each fact is authored is not a documentation-tidiness question; it decides whether a given agent is safe at all. This ADR settles that question once so the sibling issues (legacy tracker 1318–legacy tracker 1325) inherit a fixed target.
Decision
The MCP server layer is the universal source of truth for every agent-critical convention and safety rule. Specifically, the canonical home of any such fact is one of:
- the MCP server
instructions(when to reach for the tools + the non-negotiable safety rails), or - a tool/action schema description (how to call an action, with its primary gotcha stated inline), or
- a
madt_help(topic)entry (the on-demand manual for deeper procedure).
These three travel with every install on every MCP-capable client at
varying load costs — instructions is always loaded and cheap, schemas
load with the tool, madt_help costs nothing until fetched — so an agent
on any client can reach the full safety-and-usage picture without external
config.
Claude-Code-specific surfaces — skills, hooks, slash commands — are
enhancements only. They may make the workflow nicer, faster, or
mechanically enforced for Claude Code users (a hook that blocks a
credential-leaking git push before it runs is strictly better than a
written rule), but they layer on top of the universal facts. They never
introduce a fact that is absent from the MCP layer.
The invariant
No safety- or correctness-critical fact may live solely in a Claude-Code-specific surface (skill, hook, or slash command). Every such fact must also be present in the MCP layer — the server
instructions, a tool/action schema, or amadt_helptopic — so it reaches every MCP client.
A Claude Code enhancement may restate, enforce, or operationalize a
universal fact; it may not be the only place that fact exists. Concretely:
the check-git-remote.sh hook that blocks raw remote git is allowed and
encouraged, but the underlying rule (“use madtea for remote operations;
never raw git push/fetch”) must also be stated in the MCP
instructions, because a Cursor or Codex agent has the same credential-
leak exposure and no hook to catch it.
Agent portability tiers
Two supported tiers, named so the sibling issues can tag each surface they touch.
| Tier | Audience | Surfaces | Guarantee |
|---|---|---|---|
| Tier 1 — Universal (MCP) | Any MCP-capable client (Claude Code, Cursor, Cline, Codex, Gemini, Zed, custom) | Server instructions; consolidated tool/action schemas; madt_help(topic) | Full safety and usage. An agent gets the trigger conditions, the safety rails, and the how-to for every action with zero external config. |
| Tier 2 — Enhancement (Claude Code) | Claude Code only | Plugin skills (dev-workflow, forge-workflow); PreToolUse hooks (check-git-remote.sh, check-git-commit.sh, check-git-local.sh, check-foreign-primary.sh); slash commands | Ergonomics and mechanical enforcement on top of Tier 1. Never the sole home of a fact. May be entirely absent and Tier 1 still suffices for safe, correct use. |
Updated by ADR 0008 (legacy tracker 1697): within Tier 1, the tool/action schema is now the self-sufficient reference (action/op as JSON-Schema enums with per-value descriptions) and
madt_help(topic)is demoted to a thin, verb-aware compatibility surface — see “ADR 0008 update” at the end of this document. The Tier 1 / Tier 2 boundary itself, and the guarantee row above, are unchanged.
First-class target: any MCP-capable client, served by Tier 1. Claude Code is a superset experience (Tier 1 + Tier 2), not a separate first-class track — choosing “Claude Code only” was rejected because the package already ships to multiple clients and the safety exposure (credential leakage through raw git) is identical on all of them.
Two properties make the tiers usable as references:
- Tier 1 is self-sufficient. Removing all of Tier 2 must leave a
client able to use madtea safely and correctly. This is the
clean-room exit criterion of the epic (legacy tracker 1326): fresh install, empty
CLAUDE.md, representative tasks succeed first-try with no raw-git fallback. - Tier 2 is additive-only. Every Tier 2 surface either restates a Tier 1 fact (skills, as procedure) or enforces one (hooks). It adds no new fact.
A third, explicitly out-of-scope category exists and should be named
to avoid confusion: an operator’s hand-written CLAUDE.md/AGENTS.md is
Tier 0 — not a supported home for any convention. The whole point of
legacy tracker 1316 is to drain Tier 0. The migration matrix (legacy tracker 1318) must show no
row whose canonical home is the operator’s config.
How the sibling issues map onto the tiers
| Issue | Work | Tier it targets |
|---|---|---|
| legacy tracker 1318 | Inventory → migration matrix (canonical in-package home per convention) | Tier 1 (drives the placement of every fact) |
| legacy tracker 1319 | Tighten server instructions into the self-contained source of truth (≤ ~2KB) | Tier 1 |
| legacy tracker 1320 | Self-documenting tool/action descriptions + drift guard | Tier 1 |
| legacy tracker 1321 | Complete the madt_help manual (every workflow reachable by topic) | Tier 1 |
| legacy tracker 1324 | (Stretch) expose MCP resources + prompts — promoted from optional to first-class per-noun reference delivery by ADR 0008 (legacy tracker 1696); see “ADR 0008 update” below | Tier 1 |
| legacy tracker 1322 | Ship git-safety hook (block raw remote git → redirect to madtea) | Tier 2 (enforces a Tier 1 fact) |
| legacy tracker 1323 | Skills audit — procedures only; no sole-source critical facts | Tier 2 (must honor the invariant) |
| legacy tracker 1325 | Zero-config install docs (no required CLAUDE.md/AGENTS.md edits) | Tier 1 proof (drains Tier 0) |
| legacy tracker 1326 | Clean-room acceptance test (epic exit criterion) | Tier 1 guarantee |
Consequences
- Tier 1 carries a token budget. The MCP
instructionsreach every client on every session, so they must stay terse — legacy tracker 1319 caps them at ~2KB and pushes procedure intomadt_help. Tool descriptions similarly carry only their action’s primary gotcha inline (legacy tracker 1320), not a manual. Depth lives inmadt_help, which costs nothing until fetched. - Skills get trimmed, not deleted. The skills audit (legacy tracker 1323) keeps skill bodies as procedures (ordered workflows) and removes any safety/correctness fact that exists only there, after confirming the fact is present in the MCP layer. Skills remain valuable as Claude Code ergonomics; they stop being a second, divergent source of truth.
- Hooks stay, as enforcement. A PreToolUse hook that blocks credential-leaking git is a real safety gain for Claude Code users and is kept (legacy tracker 1322). It is explicitly redundant with a Tier 1 rule by design, not in place of one.
- One more axis to weigh when adding a fact. Any new agent-facing convention must declare its canonical Tier 1 home before (optionally) being mirrored into a Tier 2 enhancement. “Put it in the skill” is no longer a complete answer.
Verification and enforcement
- Drift guards (mechanical). madtea already enforces convention with
AST-scanning tests — the context-threading guard
(
internal/gitea/context_convention_test.go) and the mutation-confirmation guard (internal/parity/mutation_drift_test.go) are the precedent. legacy tracker 1320 extends that pattern: a test fails if a documented behavior/gotcha is missing from the corresponding tool description, keeping Tier 1 schemas honest in CI. - The invariant, audited. legacy tracker 1323 audits the bundled skills against the invariant — every safety/correctness rule a skill mentions must also be present in the MCP layer. Future skills/hooks are reviewed the same way.
- The end-to-end proof. legacy tracker 1326’s clean-room test is the standing check
that Tier 1 is genuinely self-sufficient: fresh install, empty
CLAUDE.md, representative tasks (file an issue, open a PR, finish a branch) succeed first-try with no fumbling and no raw-git fallback. If that test needs anything from Tier 2 to pass, the invariant has been violated and the fact must move down to Tier 1.
ADR 0008 update (legacy tracker 1693, legacy tracker 1697)
ADR 0008 (docs/adr/0008-operation-registry-source-of-record.md, Accepted,
2026-06-28) forks the Tier taxonomy defined above, as ADR 0008’s own
## Consequences states explicitly. This section is that update, made in
this ADR per that cross-reference, and records what changed and — just as
important — what the fork leaves untouched.
What changed, now shipped (legacy tracker 1698 registry, legacy tracker 1695 enums, legacy tracker 1696 resources, legacy tracker 1690 help resolver):
- The tool/action schema is now the self-sufficient Tier-1 reference,
not merely “how to call an action, with its primary gotcha stated inline”
(surface 2 above).
action=/op=parameters are JSON-Schemaenums with a per-value description sourced from the liveOperationSpec/ActionSpecregistry — the model sees every verb’s reference on every call, with no separate lookup required.
Updated by legacy tracker 2057 (2026-07-08): the per-value descriptions on the
action=/op=enums were removed — the enum now carries bare registry-derived values for machine validation and discoverability, and the toolDescription(buildToolDescription) is the sole carrier of every action/op summary (schema self-sufficiency unchanged). See the ADR 0015 legacy tracker 2057 amendment for the authoritative record.
- MCP Resources move from the “(Stretch)” legacy tracker 1324 row to first-class
reference delivery. Every noun is reachable as an MCP Resource
(
madtea://reference/{noun}), still a pointer/restatement of the same generated content the schema andmadt_helpcarry — never a new sole-sourced fact. madt_help(topic)(surface 3 above) is demoted to a thin, verb-aware compatibility surface. It still answers noun-topic lookups in full, and a verb lookup that used to dead-end (topic="import") now resolves to, or names, the owning noun instead — but it is no longer the primary place an agent learns an action’s shape; the schema enum is.madt_helpis kept, not deleted.
What did NOT change — the invariant above holds exactly as stated:
- Tier 1 remains self-sufficient without Tier 2 (skills/hooks), and, per
ADR 0008, also without Resources: the clean-room test (legacy tracker 1326) asserts
this on the credential-less server using only server instructions and the
enum-bearing tool schemas —
madt_helpand Resources are convenience, not a requirement. - The Tier 1 / Tier 2 boundary itself is untouched. The fork changes
which Tier 1 surface is primary for verb reference (schema over
madt_help); it does not move any fact onto a Claude-Code-only surface. - The safety-exposure boundary is unchanged. ADR 0008 unifies description text across CLI and MCP, never the exposed surface (CLI-only safety-withheld ops, gated domains, MCP-only fail-closed guards all stay exactly as this ADR and ADR 0008 both describe).
See ADR 0008 for the full decision, the OperationSpec registry shape, and
the safety non-goal in detail. docs/contributing/agent-onboarding.md row 6
and docs/architecture/mcp-registration.md reflect this fork.
Alternatives considered
- First-class target = Claude Code only. Rejected. The package already installs into Codex, Gemini, Cursor, Windsurf, Cline, and Zed, and the core safety exposure (a credential leaking through raw git in the agent’s shell) is identical on every one of them. Making safety Claude-Code-only would ship a knowingly unsafe default to the majority of advertised clients.
- Author conventions in the operator’s
CLAUDE.md(status quo). Rejected as the explicit anti-goal of legacy tracker 1316: it does not travel with the install, forces every operator to hand-maintain it, and silently fails for any agent whose operator left it empty. - Skills as the primary home, MCP as a thin pointer. Rejected: skills are Claude-Code-only, so this strands every other client. Skills are retained strictly as Tier 2 procedure.
Canonical source: docs/adr/0001-mcp-universal-source-of-truth.md in the madtea repo.