Skip to content

MCP tool reference

There is no separate REST API for Arxo. The only public interface is the Model Context Protocol — Connect an AI assistant (MCP) covers transport, corpus slices, and call order. This page reads through the same tools one at a time: what each one is for, what it takes, a minimal call, what comes back, and where it typically refuses.

The live tools/list response of the server is the only source of truth about the set — this page is a reading aid built from it, not a substitute for it. A tool’s description and parameters can change between formalization releases; if this page and a live call disagree, the live call wins.

Every tool is called the same way, over JSON-RPC tools/call, POST to /mcp. The full envelope — headers, the JSON-RPC wrapper — is in Connect an AI assistant (MCP), “Ask with one call”. To keep this page short, each tool below shows only the arguments object of params. For example

{"query": "feeding break"}

is short for

Terminal window
curl -s https://mcp.arxo.io/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
"name":"law_search","arguments":{"query":"feeding break"}}}'

Every reply carries a human line in content[].text and the same answer as a document in structuredContent — how to read the four fields that matter most (truthStatus, rulesApplied, provenance, the hashes) is How to read an answer.

Where a question starts: turn words into addresses, then take exact signatures and the official text behind them.

Semantic search over formalized law: a question or concept in your own words returns candidates with addresses — predicates with signatures, rules, source fragments. This is the discovery layer, not an answer: it does not establish a fact or a norm, and text similarity is not applicability. Ranking is a deterministic lexical embedder, not a language model.

  • Parameters: query (required, your words) · kinds (narrow to package/predicate/rule/norm/constraint/fragment) · package (search inside one act only) · limit (default 8, max 25).
  • Call: {"query": "feeding break for a nursing mother"}
  • Response: ranked candidates, each with next — the following call already filled in (usually law_packages or law_rules).
  • Typical limits: an empty result is an honest “not formalized”, not a server fault — do not invent a predicate name after it.
  • Used in: Find the norm.

The catalogue of formalized law: acts, predicates with signatures, and coverage bounds. This is where predicate names come from — do not guess them. Three modes, all paginated: no arguments gives an overview (one line per act); package gives the full signature list of one act; query filters signatures, labels, and constants across every act by a substring. package together with catalog lists a further inventory of one act: its declared question catalogue, its norms, or its constants.

  • Parameters: package (act key) · query (substring filter) · catalog (questions/norms/constants, only with package) · limit, offset (pagination; the unit is named by pageUnit in the reply).
  • Call: {"package": "kz-labour-code"}
  • Response: signatures with parameter types, constants, and — with catalog: "questions" — a template per supported question, each marked with what it does not answer and why.
  • Typical limits: do not start here to find an act — a title rarely matches the words of a question; start from law_search.
  • Used in: Find the norm.

Verbalized rules: exactly how a predicate is derived — premises and their source articles. Call this before sending facts to law_ask: a rule names the facts it needs, so there is nothing to guess. package lists a table of contents of an act’s rules; predicate gives the full text of the rules that derive it.

  • Parameters: predicate (rules deriving it) · package (all rules of an act, paginated) · lang (verbalization language, default ru) · limit, offset.
  • Call: {"predicate": "feeding_break_too_short"}
  • Response: each rule’s premises, its source article, and its strength (strict or defeasible).
  • Used in: Read the rules.

The official text behind a norm: the source law_ask and law_rules cite. By default, short excerpts (view: "summary"); view: "full" returns exact ranges of the pinned text, addressed by Unicode character offsets, with a contentHash of the full source (not of the excerpt).

  • Parameters: package (all fragments of an act) · fragment (a StableId from law_search/law_rules) · view (summary/full) · textOffset, textLimit, contentHash (continuing a full read) · limit, offset.
  • Call: {"fragment": "urn:...#fragment/Article-100"}
  • Response: the excerpt or exact text range, the hash that proves it matches the pinned publication, and — in summary — the address of the full text.
  • Used in: Read the rules, Why this can be trusted.

The law, asked directly, with the chain that produced the answer.

The core call: ask formalized law a question. Before the first call, get predicate names from law_search or law_packages — an invented name is refused. Five kinds: truth (is a fact established — one of four statuses), collect (list records matching a pattern), calc (compute a quantity), deadline (a period’s expiry on the official calendar), positions (which duties arose and their status). “Not established” and a tool error are the answer; do not replace them with general knowledge of another country’s law.

  • Parameters (required): predicate. Parameters (usual): kind (default truth) · package · args · facts ([{predicate, args, negated?}]) · legalTime. Kind-specific: calc (an expression tree) · days, after, deadlinePolicy (deadline) · proof (attach a proof graph, feeds law_explain).
  • Call: the example from Connect an AI assistant (MCP) — {"kind":"truth","package":"kz-labour-code","predicate":"feeding_break_too_short","args":["Айгуль","Работодатель"],"facts":[...],"legalTime":"2026-09-01"}
  • Response: the status, rulesApplied, provenance with hashes, and — on NEITHER — whyNot, naming the missing premise.
  • Typical limits: a name the corpus does not know is refused, not guessed at.
  • Used in: Connect an AI assistant (MCP), Ask the law, Reading silence.

Run a case already assembled as its own package (a “case package”): a fixed set of facts and pinned dependencies, asked through a declared case/query, or an evaluate expression. Preparation failures (missing pins, an unregistered case) come back as a coded error, not a silent empty answer.

  • Parameters (required): package (path to the case package). Usual: case (name, optional with one case) · query (a JSON question) or evaluate + queryId (a .lawtest-style expression).
  • Call: {"package": "examples/labour-cases/feeding-break", "queryId": "too-short"}
  • Response: the same evaluation document as law_ask, computed over the case’s own fixed snapshot.
  • Typical limits: for a one-off question about the general corpus, use law_ask instead — this tool is for a pre-registered case package.

Unfold a law_ask answer’s proof into a chain: rule ← premises → conclusion. The compact form re-runs the computation from question + resultHash + codeHash and refuses loudly if anything changed; or pass the evaluation document from law_ask(proof: true) directly.

  • Parameters: one of evaluation (a full evaluation document) / question + resultHash + codeHash / answerResource (a saved compact answer’s fullAnswer.uri). Optional: nodeId (one node and its premises only, capped at 24 KB).
  • Call: {"evaluation": {"...": "..."}}
  • Response: the proof tree, or a refusal if replay does not match the hashes you supplied.
  • Used in: Explain and doubt.

Whether a conclusion survives counter-argument, and how far the act can be pushed.

How robust is a conclusion: an argumentation map over the whole act (not just the rules that fired in a plain law_ask), including chains that lost. Four verdicts: SKEPTICALLY_ACCEPTED (survives every attack), CREDULOUSLY_ACCEPTED (defensible — there is a position for and against), REJECTED, NO_ARGUMENT.

  • Parameters (required): predicate. Usual: args, facts, package, legalTime · limits (caps on the search: maxArguments, maxExtensions, maxUndecided).
  • Call: {"predicate": "feeding_break_too_short", "facts": [...]}
  • Response: the verdict and the map of attacking/defending arguments.
  • Typical limits: a large act can refuse on its argument cap — narrow facts or raise limits.maxArguments.

Bounded search (dates and action sequences) for a path to a target outcome on which no step produces a violated duty. “Not found” is a statement inside the given depth and budget, not about the act in general; hitting a cap is reported explicitly (SPACE-TRUNCATED, BUDGET-EXHAUSTED), never silently.

  • Parameters (required): predicate (the goal). Usual: args, facts, package, legalTime, expect (default TRUE_ONLY) · depth, alphabetCap, datesCap, budget (search bounds).
  • Call: {"predicate": "some_goal", "depth": 3}
  • Response: a found sequence with its dates, or an explicit not-found verdict naming which cap ended the search.

The minimal mechanical edit to an act (drop a rule, drop a priority, narrow a window, add a priority, exclude one enum value) that yields a target outcome without moving a named list of regression questions on the same act. A candidate is accepted by execution, not by claim — the regression is checked byte-for-byte.

  • Parameters (required): predicate (the target question). Usual: args, facts, expect, package, legalTime · keep (regression questions that must not move) · maxEdits (default 2), budget.
  • Call: {"predicate": "some_goal", "expect": "TRUE_ONLY", "keep": [...]}
  • Response: the minimal edit found, with manifest.autoRegression proving the kept questions held.
  • Typical limits: it enumerates mechanical edits only — it does not author a new rule.

What changed between two dates, and how one case moves through a procedure.

What changed in an act between two legal dates: edition state, dating of norms, and a semantic diff between the two projections. NOT_DATED means there was nothing to date by, not “nothing changed”. For the outcome of one question on one date, use law_ask with legalTime instead.

  • Parameters (required): package, dateA, dateB.
  • Call: {"package": "kz-labour-code", "dateA": "2025-01-01", "dateB": "2026-09-01"}
  • Response: the diff between the two dates’ projections, article by article.

The stages a formalized process has, and whether its graph has a structural defect (an unreachable stage, a dead transition, a transition that leads nowhere). Without package, lists which acts have a process formalized at all. A stage without an outgoing transition is named but not flagged as a defect — the formalization does not say whether it is meant to be terminal.

  • Parameters: package (optional — omit for the list of acts with a process).
  • Call: {"package": "kz-tender-procedure"}
  • Response: stages, transitions, and their conditions, plus any detected structural defect.

How one concrete case moves through a procedure: a log of {date, attempt?, facts?} steps, each evaluated under the law of its own date. The reply says which stages were entered, which attempted transition did not produce its legal effect and why, which duties are violated, and what is available next.

  • Parameters (required): package, instance (what is going through the procedure), steps (the log). Usual: legalTime, procedure, probeTime (to inspect the next available steps).
  • Call: {"package": "kz-tender-procedure", "instance": "lot-42", "steps": [{"date": "2026-01-10", "facts": [...]}]}
  • Response: stages entered, failed attempts and why, active violations, and what remains to be done.

Querying the formalization itself, measuring how much of an act is executable, and checking a file.

A deterministic LawQL query over the compiled intermediate representation (CLIR): tables like nodes, edges, rules, norms, positions, labels, imports. This is orchestration over the existing engine, not a new source of legal semantics.

  • Parameters (required): query (inline LawQL, or a query …over "pkg" block). Usual: corpus (a package.name glob for a structural snapshot) · rowLimit (default 20, max 5000), rowOffset · case (for query … over with a case in place of a case clause).
  • Call: {"query": "from n in nodes where n.kind == \"rule\" select n.id as id", "corpus": "kz-labour-code"}
  • Response: matching rows, with warnings: ["ROWS_TRUNCATED"] and a next cursor if the page was cut.
  • Typical limits: row truncation is reported explicitly, never silent.

Both completeness measures at once: article coverage (by convention — “Article N”, “N-бап” — and markers of executable content) and depth (EXECUTABLE/ANCHORED/SOURCE_ONLY per article, with byte coverage of the pinned text). Neither measure alone is enough: “every article is covered” and “nothing computes” can both be true together. Two input modes: a draft (source + document) or a corpus package (package, using the committed CLIR and pinned documents).

  • Parameters: package (corpus act) — or source + document + declaration (a draft).
  • Call: {"package": "kz-labour-code"}
  • Response: per-document article coverage and per-article depth.
  • Used in: Package passports.

Compile a .law file from the repository: syntax, symbol declarations, arity, types, and the hashes of official texts it cites.

  • Parameters (required): path (repository-relative).
  • Call: {"path": "packs/examples/tutorial/archive/archive.law"}
  • Response: compiler diagnostics, or a clean pass.

Build a necessary-condition superset for pre-screening rows of an external data source. This is not a legal answer and not SQL — it is a typed tree plus a manifest of extensions to TRUE. Every row that passes still has to be fully evaluated by the engine (law_ask) with its own proof, on the same data snapshot.

  • Parameters (required): package, goal (a StableId or short name), binding (a law.screen.binding/0.1 artifact), sourceSchemaContentHash. Usual: legalTime.
  • Call: {"package": "kz-labour-code", "goal": "feeding_break_too_short", "binding": {"...": "..."}, "sourceSchemaContentHash": "sha256:..."}
  • Response: the screening tree and manifest; rows still need law_ask.
  • Typical limits: the hash in binding must match the source schema exactly, or the call refuses loudly.

Author-facing tools for taking a .law draft from pinned bytes to a checked, measured, differential-tested candidate. Drafts are a sandbox: they never become law, never enter the corpus, and are invisible to law_ask.

Phase 0: pin a document’s bytes — sha256 and a ready-made publication block. No network access — the client brings the document; retrieved_at is passed as an argument, the server does not invent a time.

  • Parameters: text or html (the document) · uri, local_path, retrieved_at.
  • Call: {"text": "...", "uri": "https://...", "retrieved_at": "2026-09-22T00:00:00Z"}
  • Response: the hash and a publication block ready for sources.law.

Layers 1–2: whether a draft parses, and — if it parses — whether every node survives lowering into executable form. A node that drops out is a verdict, not a log line: “a rule that can never fire is indistinguishable from silence”.

  • Parameters (required): source (the draft text). Usual: sources (pinned companion files, for source-citation checks).
  • Call: {"source": "package tutorial.archive version \"0.1.0\" { ... }"}
  • Response: parse result, dropped nodes (if any), citation checks.

Layer 3: execute the draft in a sandbox. A clean law_draft_check does not mean the rules derive anything — not_known, classical negation in an open world, and arithmetic kind errors only show up on execution. kind: "positions" reports duty/power statuses instead of a plain predicate.

  • Parameters (required): source. Usual: predicate, args, facts, kind (truth/collect/positions), legalTime, sources.
  • Call: {"source": "...", "predicate": "eligible", "facts": [...]}
  • Response: the computed result and any issue above info severity, including dead rules named individually.

Layer 5: verbalize the draft — a side-by-side “official text ↔ deterministic sentence” pair for meaning review. The server does not judge whether the formalization is correct; it makes the comparison possible, and names constructs it cannot verbalize rather than skipping them.

  • Parameters (required): source. Usual: sources.
  • Call: {"source": "..."}
  • Response: paired official-text and generated-sentence entries per rule.

The final gate: ask the same question of the draft through both implementations (the Python oracle and the Rust engine) and compare the canonical output bytes. A divergence is a blocker that must be classified (a defect in one implementation or in the specification) — never silently patched over.

  • Parameters (required): source, predicate. Usual: args, facts, legalTime, sources.
  • Call: {"source": "...", "predicate": "eligible", "facts": [...]}
  • Response: a byte-identical result from both engines, or a named divergence.

What a draft changes against a corpus package: a semantic diff (norms added, dropped, changed) and coverage targets. The draft must declare the same package as the comparison base — this tool compares editions of one act, not two different acts. It does not check whether the draft’s rules fire on a scenario; that is law_draft_eval.

  • Parameters (required): package (comparison base), source (the new edition’s draft). Usual: sources.
  • Call: {"package": "kz-labour-code", "source": "..."}
  • Response: the semantic diff between the draft and the corpus edition.

What is released, under what name; and a channel to flag a mismatch with the source.

The passport of an exact release: purpose, interface, bounds, checks, and performance. The reports section carries the history of measurements, including failures. Paginated by JSON Pointer, with next continuing the read.

  • Parameters (required): name, version, contentHash. Usual: sections (narrow to one or more of description, interface, semantics, sources, composition, dependencies, checks, evidence, reports) · limit, offset.
  • Call: {"name": "kz-labour-code", "version": "0.3.0", "contentHash": "sha256:..."}
  • Response: the requested passport sections.
  • Used in: Package passports.

Report a divergence between the formalization and its source: the answer contradicts the pinned text, a norm is not expressed as a rule, a label disagrees with the text, or an address does not resolve. This is a triage hypothesis, not a correction — it does not change the law, does not appear in other tools’ answers, and “not derived” does not become “derived” because a report was filed.

  • Parameters (required): package, category (missing-norm/wrong-outcome/label-mismatch/broken-address/source-text/other), expected, observed. Usual: fragment (a StableId), predicate, callId (the X-Law-Call-Id of the disputed answer).
  • Call: {"package": "kz-labour-code", "category": "wrong-outcome", "expected": "...", "observed": "..."}
  • Response: confirmation that the report was logged for triage.

Freeze a set of prior law_ask/law_process_run calls into a reproducible, shareable page.

By explicit user request, replay 1–12 prior law_ask or law_process_run calls, strictly verify their replayControl, and prepare an unpublished, frozen document for 24 hours. The client never hands over a pre-computed answer — the server only accepts what its own fresh execution produces; any mismatch in input, rights, resource, MCP code, or result rejects the whole document.

  • Parameters (required): question · one of calculations (with request + replayControl per entry) or producerRuns. Usual: assistant (a summary and per-calculation explanations), documentVersion, language.
  • Response: a preparationId for law_publish_answer, or a rejection naming what did not replay.

Publish a prepared document. Calling it again with the same preparationId is idempotent — it returns the same URL and the same separate revoke secret.

  • Parameters (required): preparationId.
  • Response: the public URL and a revoke secret (shown once — store it to be able to revoke later).

Revoke a published document using its separate secret. The public id alone grants no management rights; revocation closes the HTML page, the API, and the download.

  • Parameters (required): publicId, revokeSecret.
  • Response: confirmation that the page, API, and download are closed.

A machine-readable matrix of which answer producers, replay/projection checks, profiles, limits, and confirming contract tests are actually installed on this server. declared_only marks a schema without an executable witness behind it yet.

  • Parameters: none.
  • Response: the capability matrix.

Run a registered producer and capture a producerControl/0.2 record for a later verifiable law_prepare_answer call. The producer must be one of the names the server’s own registry lists — it is not an arbitrary plugin point.

  • Parameters (required): producer (a registered name), request.
  • Response: the captured control record for use in law_prepare_answer.

There is no separate REST endpoint per tool. Every call above goes through the same JSON-RPC tools/call method on /mcp — see Connect an AI assistant (MCP) for the transport, rate limits, and corpus slices where some of these tools (the draft workbench and the adversarial analytics group) are absent entirely.

This page does not replace tools/list. Parameter names, required fields, and even the presence of a tool can change between releases; the server’s own answer to tools/list is what an agent should read programmatically, not this page.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.