<!-- For developers and AI agents: the markdown version of https://mithril.law/agents -->

# For developers and AI agents

Every tool on mithril.law answers over MCP, REST or a single URL, with each step cited and the answer signed. When a matter needs a lawyer, your agent hands it to me. Anything a person can do on mithril.law, an agent can do here, at the same prices.

Standing mandates, which will let you retain Mithril Law for your user within limits they set, are not open yet. Until they are, hand the matter to me with the handoff below.

## Get an answer with one URL

Every tool without personal facts answers at one URL: fetch https://mithril.law/{area}/{tool}.md with the facts as query parameters and you get the answer as markdown, every step cited, with a receipt. If a fact is missing, the answer lists it and gives the URL to fetch next. Tools that need names or addresses say how to proceed instead.

For example:

https://mithril.law/corporate/incorporation-plan.md?operatesIn=ontario_only&founders=1&residentCanadianFounders=1&name=numbered&shareStructure=common_only&raisingMoneySoon=false&shareholderAgreementInPlace=false

The same as JSON: https://mithril.law/api/v1/operations/corporate.incorporation_plan/run?operatesIn=ontario_only&founders=1&residentCanadianFounders=1&name=numbered&shareStructure=common_only&raisingMoneySoon=false&shareholderAgreementInPlace=false

Every tool page (`/{area}/{tool}`) has a markdown version at `/{area}/{tool}.md` that opens with a working example URL. Facts go in as dotted query parameters (`pay.annualSalary=104000`), lists as repeated parameters, or all at once as `facts=<url-encoded JSON>`. Tools that need names or addresses never take them from a URL; their page says how to proceed. The index of every tool with an example URL is https://mithril.law/llms.txt.

## Connect over MCP

The server is at https://mithril.law/mcp (Streamable HTTP). It speaks protocol 2026-07-28 and, for older clients, 2025-11-25, 2025-06-18, 2025-03-26. Free, read-only tools need no sign-in.

- Claude: add a custom connector with the URL https://mithril.law/mcp.
- ChatGPT: add a connector (developer mode) with the same URL.
- Claude Code:

```sh
claude mcp add --transport http mithril https://mithril.law/mcp
```

- TypeScript (@modelcontextprotocol/client):

```ts
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";

const client = new Client({ name: "my-agent", version: "1.0.0" }, { versionNegotiation: { mode: "auto" } });
await client.connect(new StreamableHTTPClientTransport(new URL("https://mithril.law/mcp")));
const { tools } = await client.listTools();
const run = await client.callTool({ name: "corporate_incorporation_plan", arguments: {"operatesIn":"ontario_only","founders":1,"residentCanadianFounders":1,"name":"numbered","shareStructure":"common_only","raisingMoneySoon":false,"shareholderAgreementInPlace":false} });
console.log(run.structuredContent);
```

- Python (mcp):

```python
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async with streamablehttp_client("https://mithril.law/mcp") as (read, write, _):
    async with ClientSession(read, write) as session:
        await session.initialize()
        tools = await session.list_tools()
```

Tools: one per operation (the operation id with dots as underscores), plus `handoff_create`, `handoff_status`, `slots_list`, `booking_hold`, `receipt_verify`, `rules_search`, `rule_get` and `catalog`. Resources: `mithril://rules/{id}`, `mithril://sources/{id}`, `mithril://receipts/{id}`, `mithril://catalog`. Prompts: `i_was_let_go`, `i_got_an_n4`, `someone_was_arrested`, `starting_a_company`. The tools that are not operations also answer over REST at `POST /api/v1/tools/{name}` (the list and schemas: `GET /api/v1/tools`).

When facts are missing, a 2026-07-28 client that supports forms gets `input_required` with exactly the questions the law needs; an older client gets an error naming each missing fact. Ask the person; never fill in a fact yourself.

## Call the REST API

```sh
curl https://mithril.law/api/v1/operations

curl -X POST https://mithril.law/api/v1/operations/corporate.incorporation_plan/runs \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Mithril-Agent: my-agent/1.0" \
  -d '{"input": {"operatesIn":"ontario_only","founders":1,"residentCanadianFounders":1,"name":"numbered","shareStructure":"common_only","raisingMoneySoon":false,"shareholderAgreementInPlace":false}}'
```

Every run returns the answer (`result.output`), a summary you can repeat, the steps with their citations, the boundary, the law it relied on and a signed receipt. Errors look like this:

```json
{ "error": { "code": "invalid_input", "message": "Some facts are missing or not in the right form.", "hint": "…", "details": [{ "path": "startDate", "message": "…" }] }, "requestId": "req_…" }
```

The full description is at https://mithril.law/openapi.json (OpenAPI 3.1).

## The boundary

- `automated`: Done by the software. Nothing waits on the person or a lawyer.
- `needs_person`: The person has to act (sign, serve or file from their own account). Pass the next steps to them.
- `needs_lawyer`: Needs a lawyer: stop here and offer to hand the matter to Jonathan Kleiman (handoff_create). Do not give legal advice yourself.

## Hand a matter to me

```sh
curl -X POST https://mithril.law/api/v1/handoffs -H "Content-Type: application/json" -d '{
  "area": "employment", "kind": "consultation",
  "summary": "Let go after 11 years; wants help negotiating.",
  "person": { "name": "…", "email": "…" },
  "parties": [{ "name": "…", "role": "employer" }],
  "runId": "run_…"
}'
```

The response carries a `consentUrl`. Give this link to the person and to no one else. Do not open it, fill it in or act on it yourself. The person confirms who they are, passes the conflict check, picks the time, signs and pays there. I check the names against my practice records and this site’s records; if there is a conflict, the person is referred elsewhere and you never learn why. Follow progress with `GET /api/v1/handoffs/{id}` or the `handoff.updated` webhook.

## Receipts

Every run returns a receipt signed with the site’s Ed25519 key (JWS, EdDSA). It records the operation, hashes of the facts and the answer, and each rule’s version and verification status. Verify it offline against /.well-known/jwks.json or with POST /api/v1/receipts/verify.

```sh
curl -X POST https://mithril.law/api/v1/receipts/verify -H "Content-Type: application/json" -d '{"jws": "eyJ…"}'
```

The answer tells you whether the signature is ours, which rules the answer relied on and whether each has changed since, and whether the run has been superseded. A superseded answer must not be repeated: run the operation again.

## Webhooks

Create an endpoint with an API key: `POST https://mithril.law/api/v1/webhooks` with `{ "url": "https://…", "events": ["run.completed", "handoff.updated"] }`. The response shows the endpoint’s secret once. Events:

- `run.completed`: A run you made finished. Carries the run id, operation, boundary and links.
- `run.superseded`: A rule a run of yours relied on changed after the run. Stop repeating that answer; run it again.
- `rule.changed`: A rule changed (amendment, practice direction or fee change) and is being re-verified.
- `handoff.updated`: A handoff you created changed status (booked, engaged, matter_open, referred…).
- `booking.updated`: A booking you held was confirmed, moved or cancelled.
- `matter.updated`: Delegated access only: the person's matter changed (new shared document, message, date).
- `document.ready`: A document you were waiting for (for example after payment) is ready to download.
- `journey.updated`: A supervised journey across several operations moved on: a step finished, a date arrived, or it needs the person.
- `consulting.proposal.sent`: Consulting: a proposal was sent to your organization.
- `consulting.proposal.accepted`: Consulting: your organization accepted a proposal.
- `consulting.project.updated`: Consulting: a project for your organization changed stage.
- `consulting.deployment.updated`: Consulting: your workspace deployment changed status.
- `principal_step.completed`: The person finished a step only they can take (identity, a signature, a decision). Carry on from where you left off.

Each delivery is a POST with `Mithril-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256>`, computed over `t.rawBody` with your secret, plus `Mithril-Event` and `Mithril-Delivery`. Reject signatures older than five minutes. Any 2xx acknowledges; anything else is retried with backoff (1, 2, 4 … 64 minutes), 8 attempts in all.

Node:

```js
import { createHmac, timingSafeEqual } from "node:crypto";

// secret: the whsec_… value shown once when you created the endpoint.
// header: the Mithril-Signature request header. body: the raw request body, as a string.
export function verifyMithrilSignature(secret, header, body, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=", 2)));
  const t = Number(parts.t);
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
  return header.split(",").map((p) => p.trim()).filter((p) => p.startsWith("v1=")).some((p) => {
    const given = Buffer.from(p.slice(3));
    return given.length === expected.length && timingSafeEqual(given, Buffer.from(expected));
  });
}
```

Python:

```python
import hmac, hashlib, time

def verify_mithril_signature(secret: str, header: str, body: bytes, tolerance: int = 300) -> bool:
    parts = dict(p.strip().split("=", 1) for p in header.split(","))
    t = int(parts.get("t", "0"))
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    given = [p.strip()[3:] for p in header.split(",") if p.strip().startswith("v1=")]
    return any(hmac.compare_digest(g, expected) for g in given)
```

## Try the handoff without a key

`POST https://mithril.law/agents/sandbox/handoffs` takes the same body as `POST https://mithril.law/api/v1/handoffs`, checks it against the same schema and returns a test handoff (`hof_test_…`) with a test consent link. No one is contacted. `POST https://mithril.law/agents/sandbox/handoffs/{id}/events` with `{ "handoff": <the test handoff> }` plays the person’s next step and returns the `handoff.updated` delivery a live endpoint would receive, signed with the published test secret `whsec_sandbox_mithril_test_only`, so you can try your signature check.

## Changes

Each operation has a version, and every receipt signs the version that answered. Each rule has a version and a hash; a changed rule needs verifying again before the tools that use it run. The register’s Atom feed lists every sign-off and every rule sent back for a new check: https://mithril.law/verify/feed.xml.

## Sign-in

- None, for free and read-only operations, the handoff and booking holds. `tools/list` over MCP never needs sign-in.
- API keys, for developers and organizations (metered use): create one at https://mithril.law/account/keys and send `Authorization: Bearer mk_…`. Runs are recorded to your account and metered.
- To act for a person, the simple way: a personal access token. The person makes an `mk_…` key for their own agent at https://mithril.law/account/keys, choosing its scopes, and gives it to the agent. It is sent the same way as an API key.
- Or OAuth 2.1 with PKCE. Metadata: https://mithril.law/.well-known/oauth-protected-resource. The person approves scopes in plain English and can revoke them at https://mithril.law/account/agents.

  - `runs:read`: See the runs made for you and their receipts.
  - `runs:write`: Run operations for you.
  - `handoffs:write`: Ask Mithril Law to take on your matter. You still confirm, sign and pay yourself.
  - `bookings:write`: Hold a consultation or call time for you to confirm.
  - `matter:read`: See your matter as the client portal shows it: status, dates, your tasks, shared documents and messages.
  - `matter:documents:write`: Upload documents to your matter.
  - `matter:messages:write`: Send messages to me on your matter.
  - `billing:read`: See invoices, payments and legal aid certificate status on your matter.

No scope ever grants access to Crown disclosure, notes, drafts, research, time entries or conflict data.

Tell me who you are: `Mithril-Agent: name/version`, `Mithril-Agent-Model: provider/model`, `Mithril-On-Behalf-Of: person | organization | self`.

## Prices and rate limits

- Anonymous, per IP, per minute: 120 reads, 30 runs, 10 writes.
- With an API key or OAuth, per minute: 1200 reads, 300 runs, 60 writes.
- API and agent runs: CAD 1 per run, first 100 free each month, plus HST. Every operation over the API or MCP. The first 100 runs each month are free.
- A paid step (a document package, for example) comes back with a `payment` object and a checkout link for the person. Fetching the locked document before payment answers HTTP 402 with the same object.

## Rules for agents

1. Never invent facts. Ask the person for every fact an operation needs. When a tool asks for missing facts, ask the person exactly those questions.
2. Consent links and checkout links go to the person, and only to the person. Never open them, fill them in, sign or pay through them yourself.
3. The boundary is an instruction. `automated` means the job is done. `needs_person` means the person must act (sign, serve, file from their own account). `needs_lawyer` means stop: do not answer the legal question yourself; offer the handoff.
4. Cite the receipt when you repeat an answer. If the answer is old, check the receipt: a superseded run means the law behind it changed.
5. Quote only the prices the catalog gives. Never promise an outcome.

The software provides legal information and document preparation, sold by Jonathan Kleiman. Legal advice starts at the lawyer boundary and is provided by Mithril Law under an engagement.

## Do this through the API

- `corporate.annual_maintenance`: The year’s resolutions and annual return. REST `POST https://mithril.law/api/v1/operations/corporate.annual_maintenance/runs`; MCP tool `corporate_annual_maintenance`.
- `corporate.incorporation_plan`: Where to incorporate, and what it costs. REST `POST https://mithril.law/api/v1/operations/corporate.incorporation_plan/runs`; MCP tool `corporate_incorporation_plan`.
- `corporate.incorporation_prepare`: Incorporation package. REST `POST https://mithril.law/api/v1/operations/corporate.incorporation_prepare/runs`; MCP tool `corporate_incorporation_prepare`.
- `corporate.isc_register`: Register of individuals with significant control. REST `POST https://mithril.law/api/v1/operations/corporate.isc_register/runs`; MCP tool `corporate_isc_register`.
- `corporate.share_issuance`: Issue shares. REST `POST https://mithril.law/api/v1/operations/corporate.share_issuance/runs`; MCP tool `corporate_share_issuance`.
- `criminal.intake_triage`: How urgent is a criminal matter. REST `POST https://mithril.law/api/v1/operations/criminal.intake_triage/runs`; MCP tool `criminal_intake_triage`.
- `criminal.legal_aid_path`: Getting a legal aid lawyer for a criminal charge. REST `POST https://mithril.law/api/v1/operations/criminal.legal_aid_path/runs`; MCP tool `criminal_legal_aid_path`.
- `criminal.what_happens_next`: Someone was arrested or charged: what happens next. REST `POST https://mithril.law/api/v1/operations/criminal.what_happens_next/runs`; MCP tool `criminal_what_happens_next`.
- `employment.demand_letter`: Demand letter. REST `POST https://mithril.law/api/v1/operations/employment.demand_letter/runs`; MCP tool `employment_demand_letter`.
- `employment.limitation_dates`: Deadlines after a job ends. REST `POST https://mithril.law/api/v1/operations/employment.limitation_dates/runs`; MCP tool `employment_limitation_dates`.
- `employment.severance_check`: Severance check. REST `POST https://mithril.law/api/v1/operations/employment.severance_check/runs`; MCP tool `employment_severance_check`.
- `employment.termination_clause_check`: Termination clause check. REST `POST https://mithril.law/api/v1/operations/employment.termination_clause_check/runs`; MCP tool `employment_termination_clause_check`.
- `general.limitation_period`: How long you have to sue in Ontario. REST `POST https://mithril.law/api/v1/operations/general.limitation_period/runs`; MCP tool `general_limitation_period`.
- `general.triage`: Where to start with a legal problem in Ontario. REST `POST https://mithril.law/api/v1/operations/general.triage/runs`; MCP tool `general_triage`.
- `ltb.l1_prepare`: Prepare an L1 to evict for non-payment of rent. REST `POST https://mithril.law/api/v1/operations/ltb.l1_prepare/runs`; MCP tool `ltb_l1_prepare`.
- `ltb.n4_bulk`: An N4 for every unit in arrears. REST `POST https://mithril.law/api/v1/operations/ltb.n4_bulk/runs`; MCP tool `ltb_n4_bulk`.
- `ltb.n4_prepare`: Prepare an N4 for non-payment of rent. REST `POST https://mithril.law/api/v1/operations/ltb.n4_prepare/runs`; MCP tool `ltb_n4_prepare`.
- `ltb.outcomes`: LTB orders by application. REST `POST https://mithril.law/api/v1/operations/ltb.outcomes/runs`; MCP tool `ltb_outcomes`.
- `ltb.rent_increase_check`: Is this rent increase lawful?. REST `POST https://mithril.law/api/v1/operations/ltb.rent_increase_check/runs`; MCP tool `ltb_rent_increase_check`.
- `ltb.tenant_path`: Which LTB application fits a tenant’s problem. REST `POST https://mithril.law/api/v1/operations/ltb.tenant_path/runs`; MCP tool `ltb_tenant_path`.
- `ltb.termination_date`: Termination date for a notice. REST `POST https://mithril.law/api/v1/operations/ltb.termination_date/runs`; MCP tool `ltb_termination_date`.
- `small_claims.route`: Small claims: is it the right court, and where to go. REST `POST https://mithril.law/api/v1/operations/small_claims.route/runs`; MCP tool `small_claims_route`.
- `small_claims.start_claim`: Open a small claims case at makethempay.ca from an invoice. REST `POST https://mithril.law/api/v1/operations/small_claims.start_claim/runs`; MCP tool `small_claims_start_claim`.
- `handoff_create` (REST `POST /api/v1/handoffs`): hand the matter to me with the person’s consent.
- `slots_list` (REST `GET /api/v1/slots`) and `booking_hold` (REST `POST /api/v1/bookings`): offer and hold a time.
- MCP server: https://mithril.law/mcp (Streamable HTTP, protocol 2026-07-28; 2025-era clients also work).
- REST: https://mithril.law/api/v1 ([OpenAPI](https://mithril.law/openapi.json)).
- For agents: [llms.txt](https://mithril.law/llms.txt) and the [developer docs](https://mithril.law/agents.md).
