Skip to content

Connect an AI assistant (MCP)

Formalized law is delivered over Model Context Protocol: any MCP client — an agent, an editor, your own code — gets a set of tools, each of which returns a computed answer with a proof and provenance. An assistant connected this way does not paraphrase the statute: it asks, receives a document, and relays it. That is the public interface; there is no separate REST API.

The server is public — no key, no registration, no OAuth. Before connecting a client, see what one call returns.

The question comes from the Labour Code of Kazakhstan: a worker with one child under eighteen months is given a twenty-minute feeding break. Is that break shorter than the legal minimum? The corpus is in Russian, so the names of persons in the example are Russian too; the field names are English.

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_ask",
"arguments":{
"kind":"truth",
"package":"kz-labour-code",
"predicate":"feeding_break_too_short",
"args":["Айгуль","Работодатель"],
"facts":[
{"predicate":"child_feeding_break_minutes","args":["Айгуль","Работодатель",20]},
{"predicate":"children_under_eighteen_months","args":["Айгуль",1]}
],
"legalTime":"2026-09-01"}}}'

What is in the call: the kind of question (truth — is this fact established), the act (package), the question itself (predicate with its arguments), the facts of the case, and the date the law is projected on (legalTime). Every one of these is part of the answer, not a setting.

The reply has a human line in content[].text and the same answer as a document in structuredContent. Four fields carry the answer:

Field What it says In this example
answer.truthStatus one of four statuses: TRUE_ONLY, FALSE_ONLY, BOTH, NEITHER TRUE_ONLY — established: the break is shorter than the minimum
rulesApplied identifiers of the rules that fired the rule “for one child — not less than thirty minutes” among them
provenance which act answered, jurisdiction, legalTime, and the articles of the rules that fired the Labour Code, KZ, the date you passed
programHash, caseHash, resultHash (inside provenance) the law, the case, and the outcome as hashes repeat the call with the same inputs and the bytes match

That is the whole contract: a status, the rules, the address of each provision, and hashes that reproduce the answer. No sentence in the reply was written by a model.

Run the same call with "facts": []. The status becomes NEITHER — not established — and the answer carries whyNot: the candidate rules and the status of each of their premises. It names the fact that was missing. That is not “no”; it is “the law had nothing to read”. The difference between NEITHER and FALSE_ONLY is the first thing to learn about these answers: How to read an answer.

The same tools over MCP, for an agent or an editor:

Terminal window
claude mcp add --transport http law-dsl https://mcp.arxo.io/mcp

A client configured from a file needs three lines:

{ "mcpServers": { "law-dsl": { "type": "http", "url": "https://mcp.arxo.io/mcp" } } }

Transport is Streamable HTTP, stateless: no session is opened, Mcp-Session-Id is not required. Requests go POST to /mcp; GET answers 405, a batch answers 400.

From code — npm install @arxo/law-client, a method per tool: the same from code. An application that executes a canon itself, without a host, starts from the Quickstart.

The live list of tools is whatever the server returns on tools/list. That list is the only source of truth about the set. Do not copy a count from this page. A tool-by-tool reading, with parameters and a minimal call for each — MCP tool reference.

You do not need to know a predicate name in advance. law_search takes a question in your own words and returns addresses: predicates with signatures, rules, articles — each with the next call already filled in. Then law_rules says which facts the rule needs. The working session walks that path on the same question, from the first search to the proof: find the norm.

/mcp is the whole corpus. The same host has slices by legal order: each is a separate server with its own name, a jurisdiction in every answer, and a catalogue that does not contain foreign acts.

Terminal window
claude mcp add --transport http law-dsl-kz https://mcp.arxo.io/mcp/kz
claude mcp add --transport http law-dsl-islam https://mcp.arxo.io/mcp/islam
Path What it sees Notes
/mcp the whole corpus Kazakhstan is the main jurisdiction; other acts are named as their own
/mcp/kz law of the Republic of Kazakhstan official calendar; law_ask kind="deadline" works
/mcp/islam Islamic doctrine: fiqh, faraid, the Quran not the law of a state; no calendar; deadline refuses

Slices do not carry the author workbench (law_draft_*, law_pin, law_measure, law_check) or adversarial analytics (law_loophole, law_amend): they are absent from tools/list, and a direct call refuses with a reason. The server instructions name what is missing so an agent does not plan those calls.

A slice is a selector over acts plus the closure of their imports: an act that a fiqh formalization cites from Kazakhstani law is present in the islam slice and named in the server instructions as borrowed.

The tools are not peers: one of them is the entry, and starting with another yields silence that is easy to take for absence of a norm.

Step Call Why
1 law_search the question in your own words → addresses: predicates with signatures, rules, articles
2 law_packages exact signatures and constants of the act you found
3 law_rules which facts the rule needs — instead of guessing
4 law_ask the question itself: truth, collect, calc, deadline, positions

Do not start from the catalogue. Act titles rarely match the words of the question (“Newton’s laws of motion” do not contain the word “physics”), and an empty catalogue is a property of search by title, not a conclusion about the corpus. An empty law_search result is an honest “not formalized”; inventing predicate names after it is forbidden — the server will refuse.

A law_ask answer can be published to Arxo Lens and embedded on any page as a card. The result, its statuses and the snapshot hash come from the saved answer; “How to cite” on the answer page gives a citation pinned to that hash.

Grouped by role — again, tools/list is authoritative:

Group Tools What they do
Discovery law_search, law_packages, law_rules, law_sources find an address, take a signature, read a rule, get official text and its hash
Question law_ask, law_case_ask, law_explain ask the law; unfold the proof into the chain “rule ← premises → conclusion”
Robustness and bounds law_argue, law_loophole, law_amend did the conclusion stand against counter-arguments; is there a sequence of acts to a goal with no single violation; what minimal amendment yields the target outcome
Time and process law_editions, law_process, law_process_run what changed between two legal dates; what stages a process has; how a particular case will run from a log
Structure and completeness law_query, law_measure, law_check, law_screen a LawQL query of the formalization; both completeness measures; a compiler check of a .law file; a population screen
Draft workbench law_pin, law_draft_check, law_draft_eval, law_draft_verbalize, law_draft_differential, law_draft_impact pin document bytes and take a draft act through layers of checking
Passports and reports law_passport, law_report read a package passport; file a report that the formalization diverges from the source
Published answers law_prepare_answer, law_publish_answer, law_revoke_answer freeze, publish, and revoke a reproducible HTML answer

The workbench is a sandbox. Drafts are not law, they do not enter the corpus, and they are not visible to law_ask. That contour is for whoever formalizes an act, not for whoever asks the law.

What Value Why
Rate 3 requests per second per IP, burst 10 an open computer; law_draft_* execute submitted drafts
Request body 1 MB mirror of the server’s own body limit
Timeout 120 s per request a pathological request refuses with an honest error instead of computing for minutes

Exceeding the rate yields 429, not a silent queue. Limits sit in front of the server, on the transport layer, and do not affect the semantics of an answer: the same question through your own deployment yields the same bytes.

The tools mutate nothing: the engines are deterministic, file input is isolated, execution has its own limits. Openness costs only server CPU — hence the limits, and the absence of registration.

The public server is convenient, not mandatory. The same code runs locally. The public host is a Rust binary (law-mcp-server); Python is no longer the production MCP host.

Terminal window
cargo run --quiet --manifest-path engines/lawc/Cargo.toml \
--package law-mcp --bin law-mcp-server

That is stdio transport — exactly what .mcp.json in this repository declares. The network variant adds -- --http, default 127.0.0.1:8722. Binding off loopback requires an explicit decision: either LAW_MCP_TOKEN (Bearer on every request) or LAW_MCP_PUBLIC=1. A public address without authentication does not come up by silent configuration — that is checked, not implied.

An MCP answer is not text to paraphrase. It is a document with fields. How to read it — How to read an answer. Short, for an agent:

  • “not established” and an error are passed to the user as they are and are not replaced with general knowledge of another country’s law;
  • on “not established”, look at whyNot — it names the fact that was missing;
  • jurisdiction and legal time are named in the answer; do not paraphrase an answer without them.

Writing .law packages is a different door: Connect: the editor.

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

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