mathema mcp¶
mathema's library surface served as MCP tools, so an agent connects to the same machinery every other mode uses, the one resolver, the one gate, the reason-code registry, instead of shelling out and parsing tables.
pip install "mathema[mcp]"
mathema mcp serve [--root .]
serve runs the server on stdio. Without the optional extra installed
the command prints the install hint and exits 2. Set up mathema for a
coding agent has the configuration block for each
client and the PIN and policy that go with it.
Tools¶
| Tool | What it does |
|---|---|
resolve_target |
any target spelling (dotted name, module:function, file path) to its function keys and signatures |
adjudicate_target |
adjudicate one function, gated, with agent-shaped claim rows. include selects the row set: declared (default; what the surfaces actually state, which is what a re-check asks about), suggested, or all. Statements in claims are LINTED first, so a statement that does not parse comes back at parse cost with nothing adjudicated |
adjudicate_targets |
the same adjudication over SEVERAL targets in one call, column-oriented ({prefix, cols, rows}): the declared and verified stores are read once for the batch instead of once per target, and one envelope is returned instead of N |
verify_project |
the CI sweep: freshness, re-adjudication, record refresh, the gate; keys carries the sweep as structured per-key data (why, gate counts, claim rows) so nothing needs lines parsed |
describe_target |
one function's full detail view: identity, domains, claims with verdicts, tier ladder |
audit_targets |
the population report in the column-oriented compact shape; cols selects the columns, filter (explicit, never a default) keeps rows by derive_unlock class, claim status, or columnar substring (blocker~loop, ~mutual), same-dimension terms OR together, dimensions AND across, and a filter's column references are always computed so an empty result means "nothing matched", never "never looked" |
reason_code |
selective code lookup: names, numeric ids (2.18), or whole groups (loop, 2), singly or comma-separated; no argument returns the compact id/code/fixable index, never the full table, the machine twin of Reason codes |
claim_grammar |
the claim-language lexicon, exactly as the library states it |
parse_claim |
parse and validate one claim statement without adjudicating, the authoring-loop linter: resolved name/statement/relation/route/domain rendered back explicitly, or the grammar's own error. Pass target and it also checks the statement against that function's real signature (arity, parameter names, quantified names) by inspection |
suggest_claims |
candidate claims for one function, [name, statement, route, declared, aspect, section, reason] rows, declares, never verifies, and a suggestion never gates until adopted; identical columns to mathema claims --suggest --format json |
pending_decisions |
the human-decision queue, read-only: [key, claim, kind, detail] rows for pending supersessions, stale acceptances, stale intent acceptances, standing accepted risks, unaccepted unknowns and falsifications, locked functions whose body moved, and moved rows for a record whose key no longer resolves while an unrecorded function has its form hash (the detail carries the mathema accept NEW --as reconciled --from OLD command a human runs); acceptance and unlock themselves stay CLI-only |
lock_target |
pin a function's form hash so verify fails if the body changes; the safe direction, so agents may do it. There is deliberately no unlock tool: a person runs mathema unlock |
project_index |
the generated .mathema/index.yaml map: system/module/function intents with acceptance rungs, keys, a sed-ready span per function, verified-record paths, concepts; run mathema audit --index (or mathema docsync) first if it reports the index missing |
implementation_coverage |
per-function implementation coverage without running the test suite: mathema's own probes and proofs supply the probe and derive sources, as [key, percent, potential, sources, test_stale, traced, remedy] rows plus the repo score; run_tests=true re-runs the suite under coverage first (slow, and it runs your tests) |
badges |
by default scores nothing and returns only notice, the line saying badges now come from mathema-ci measure --out; with MATHEMA_BADGES=on in the server's environment, the three badges (implementation, intent, clarity) and their overall, scored in worker processes and kept in the badges cache, over a target set, or rootwide when it is omitted or empty, with [key, implementation, intent, clarity] rows, the ASCII triangle, and capped, the functions a step of which stopped at the wall-clock cap |
Which row set you get¶
adjudicate_target defaults to include="declared", the set a re-check
after editing a claim is asking about. include="all" adds the
suggestion battery, which is adjudicated in full, not only listed, so
the call is slower and its payload larger.
A function with no declared claims returns no rows plus a hint
naming include="suggested", rather than silently handing back a
different kind of row than the one you asked for.
The two agent payload shapes¶
Adjudication rows (adjudicate_target, and mathema check --format
compact) carry, per claim: stance (a closed four-value fold of the
open verdict, supported/refuted/undecided/blocked, to
branch on, with the verbatim verdict beside it for fidelity),
source (which authoring surface the claim came from: declared,
docstring, decorator, types, suggested, builtin, ad_hoc),
and gates (whether the row counts toward passed, a falsified
suggestion never does, and the flag makes that self-evident in the
payload). Envelope rule: counterexample is present iff the row is
refuted, blocked_by iff it is blocked; presence is the signal, so
a missing key can't be misread the way null or "" can.
The adjudication rows are also what mathema verify --format json
emits per key, so a CI artifact and an agent call carry the same
vocabulary.
Population output (audit_targets, and mathema audit --compact) is
column-oriented: {"prefix": ..., "cols": [...], "rows": [[...],
...]}, column names once, the shared dotted key prefix factored out
of every row, and span in the sed-ready start:endp form so the
follow-up read is targeted; it is a ready-made sed -n range, not a
line count. (verify_project separately returns report, the human
prose, which is a different thing.) The caller names the columns it wants
(cols); the resolved selection is echoed back either way, and
analyses no chosen column needs are never computed at all.
One null policy across every cell: null means the analysis was not
computed; a computed-but-empty result is its typed empty value;
[] for lists, 0 for counts, "" for the blocker of a derivable
function. blocker holds exactly a bare reason-code key (a real
enum over the code table), with its decorations split into
blocker_params (the domain parameters a resolvable branch names)
and blocker_more (the +N further-blocked-kinds count). typed and
doc_quality are [n, m] pairs, never "n/m" strings to parse;
docsync is the weighted 0-100 CDD-compliance percent (one int);
mutates lists the module-level names the function writes through
(the strongest scope relationship); tested is the closed enum
"yes"/"no"/"no-report"/"outdated" (a missing coverage report
is a different fact from an uncovered function, and a report older
than the function's own source file supports neither answer).
Resources and prompts¶
The server also registers reference material and procedures, not just tools. A tool answers a question the agent already knew to ask; a resource is what it reads when it does not, and a prompt is a procedure delivered at the moment it chooses what to do.
| Resource | Content |
|---|---|
mathema://reference/grammar |
the claim language by example, generated from lexicon.LEXICON |
mathema://reference/codes |
the reason-code table with each code's derive_unlock and hint |
mathema://reference/verdicts |
how to read a verdict: the stance fold, why skipped is not falsified, and why passed can be true beside a refuted row |
| Prompt | Bound to |
|---|---|
claim_this_function(target) |
the authoring procedure with that function's real signature and current candidates resolved into it |
triage_repository(targets) |
orient, then narrow to what is both risky and unevidenced |
diagnose_falsification(target, claim) |
the code-wrong / claim-wrong fork, and why the judgement is handed back |
Everything served is generated from what core already owns, so it
cannot drift from the code it describes. The rule for tools covers it too: no
resource or prompt takes a verdict/accepted/stance/gates/
pin/verified_by parameter, none is named to suggest accepting,
unlocking or PIN entry, and the material says plainly that acceptance,
unlocking and the PIN are human acts done in the CLI. A test
asserts that over everything registered, not only over TOOLS.
lock_target is the deliberate one-way door: an agent may lock, and
only a person may unlock.
Wire format¶
Tool payloads go over the wire as compact JSON, serialised at the server boundary without indentation. The tools themselves return dicts, so they stay directly callable from Python, and nothing is duplicated into a structured content block.
What is deliberately absent¶
No tool makes an LLM call. No tool accepts a caller-supplied verdict:
claims are statements mathema adjudicates, and every verdict in every
payload is mathema's own. And there is no accept tool at all,
accepting evidence, owning risk, or marking a discovery is a human act,
done in the CLI (mathema accept), never exposed to
agent tooling.
Plugins¶
A mathema plugin can contribute tools by exposing an entry point in
the mathema.mcp_tools group whose callable returns plain functions.
The hook is fail-soft: a plugin that errors contributes nothing and
never breaks the server.