# Choose an integration pattern An agent can stand on an Arxo canon in four ways. They differ in two things: what the model sees, and what the answer carries back. Pick the pattern before you write instructions or tools, because it decides who pins the canon, who asks for the date of law, and whether every answer can be replayed. In three of the four patterns the model never sees Arxo at all. It calls tools named after your domain — "end of a period", "check the claim documents" — and your application answers them with the engine. The user sees your product; the canon is the foundation underneath. ## The four patterns | Pattern | What the model sees | Where the canon runs | What an answer carries | Use it when | |---|---|---|---|---| | **Domain tools over the local SDK** | your tools, e.g. `period_end` | in your process, `@arxo/law` with the engine in WebAssembly, offline after install | status, values, proof, sources, three hashes, the canonical document | you ship a product on one or a few canons | | **Domain tools over your own `law serve`** | your tools | a long-running HTTP service you host, with journals | the same answer, plus the exact embedded document bytes | several applications or languages share one pinned deployment | | **Domain tools over a generated module** | your tools | code printed from the canon by `law codegen` (TypeScript, Python, Go); no engine at run time | truth status, evaluation status, issues, judgment requests — no proof, no hashes | the engine cannot ship with the product and replay is not required | | **The agent explores the canon over MCP** | Arxo tools: `law_search`, `law_rules`, `law_ask` and the rest | the public Arxo host or your own MCP server | the host's answer projection | research assistants and open questions across many canons | The first agent of this section uses the first pattern; example A (one labour-law question) uses the fourth. ### Domain tools over the local SDK The application opens a pinned canon by name and version and keeps the handle for the life of the process: ```js import { open } from '@arxo/law'; const canon = await open('de.bgb.fristen@0.1.0'); // checked against its contentHash ``` Each domain tool is a small function: it maps the tool's arguments to typed facts, asks one question, and returns the status, the values and the grounds in the application's own words. The SDK rejects a fact that does not fit the canon — an unknown relation, a wrong arity or type — with `FactError` before the engine runs, so a model that invents a field cannot reach a result. Status: ran locally (`@arxo/law` 0.3.3, `de.bgb.fristen@0.1.0`); the complete tool is in [Build your first agent](/agent-engineering/first-agent/). ### Domain tools over your own `law serve` The same tools, but the application asks a service instead of an in-process engine: `open(name, { serve: { endpoint, token } })`. The service is a journaled HTTP deployment with `/v1/...` routes that you run and upgrade yourself; see [Private HTTP service](/operate/private-http-service/). Choose it when the pinned world must be one shared deployment rather than a copy inside each application. ### Domain tools over a generated module `law codegen` prints an installable TypeScript, Python or Go package from a canon installed in a project. The module evaluates without the engine and returns one answer envelope per question: truth status, evaluation status, issues and judgment requests, under the same status names. It carries no proof graph and no hashes, so an answer cannot be replayed or audited byte for byte, and a canon construct outside the generator's coverage is refused when the module is generated, not at run time. Status: NOT RUN in this section — there is no agent example on a generated module yet. The command accepts the targets `py`, `ts` and `go`; to try it, install a canon into a project and run `law codegen --target ts --name --version 0.1.0 --out lib-ts`. ### The agent explores the canon over MCP The model itself searches for a question, reads the rules' input contract (`law_rules` with `contract: true`), collects facts and calls `law_ask`. This is the right pattern when the question is not known in advance. An MCP profile narrows the tool surface and the packages a server exposes; it restricts what the agent can reach, it is not a different way of computing. [Connect and discover](/agent-engineering/connect-and-discover/) covers this pattern end to end. ## What stays the same in every pattern - **The engine decides.** Neither the model nor your application computes, rounds, compares against a threshold or fills a missing value. A domain tool returns what the engine returned. - **The date of law comes from a person.** A tool takes it as an argument; the agent asks for it and never defaults it to today. - **Statuses survive the translation.** A domain tool must pass the evaluation status through. An empty collection, `NEITHER` or a non-`COMPUTED` status is a stop, not "no" and not zero. The full reading is in [Read answers](/agent-engineering/handle-answers/). - **The contract applies to the whole system.** When the model cannot see a field, the obligation moves into your application code; see [The agent contract](/agent-engineering/agent-contract/). ## Choose 1. Is the question known when you build the product? If yes, write domain tools; if the agent must find the question, use MCP. 2. Must every answer be replayable and carry its proof? If yes, use the SDK or `law serve`; a generated module does not carry them. 3. One process or a shared deployment? One process: the SDK. Several applications on one pinned world: `law serve`. ## Failure case: the tool that drops the status A domain tool returns only `end_dates[0]`. On a case where the user did not give the period's length, the engine completes the evaluation with an empty collection; the tool returns `undefined`, and the model fills the gap with a date it counted itself. The fix is structural: return the status, the full value list and the grounds every time, and say in the tool result that an empty list is not an answer. The first agent's tool does exactly that. Status: labeled illustration; the empty-collection behavior itself ran locally and is recorded in [Build your first agent](/agent-engineering/first-agent/). ## How to verify - Open a canon with `@arxo/law` and ask one question; the answer has `evaluationStatus`, `value`, `proof` and `hashes`. - Run `law codegen --help` and read the target list. - Connect to an MCP server and read `tools/list`; it is the authority on which tools exist. Previous: [Agent Engineering with Arxo](/agent-engineering/) Next: [Build your first agent](/agent-engineering/first-agent/)