
AMR (Agent Mandate Registry)
io.github.JC7333Published by GitHub account JC7333v0.1.1Updated Nov 7, 2026
Records who authorized an AI agent to do what, until when; signed tokens, hash-chained journal.
Overview
Local registry that records who authorized an AI agent to do what, until when, with signed tokens and a hash-chained audit journal.
- What you can do
- Create and delegate mandates: register principal, agent, scope, legal basis, duration, action perimeter, spending ceilings and risk classes
- Verify mandates and issue signed action tokens, including re-checking a token with the registry before an irreversible step
- Log agent actions into a hash-chained journal, revoke mandates, and contest individual past actions with a response deadline
- Produce audit output: proof packs, signed mandate attestations, the registry public key, and signed dossiers for insurers or auditors
- Prepare mandate payloads and PDFs for the principal to sign, and register principal Ed25519 keys
- When to use it
- Use it when an assistant acts on someone's behalf and you need a local, verifiable record of the authorization: agent governance, AI Act-style documentation, spending limits, revocation, and audit evidence for third parties.
- Requirements
- Runs locally over stdio with uv (uvx mandatia); no account or network call by default. Set AMR_DB_PATH, AMR_SIGNING_KEY_PATH and AMR_PUBLIC_KEY_PATH to absolute paths, since defaults write the database and signing key into the client's current directory. Optional AMR_ISSUE_TOKEN and AMR_DID_WEB_RESOLVE enable the two network features.
Installation
In SourceWeft
- Open AMR (Agent Mandate Registry) in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
Agent Mandate Registry (AMR)
The legal mandate layer for AI agents.
AMR is an open-source MCP server that registers, verifies, and audits legal mandates authorizing AI agents to act on behalf of principals. It provides a cryptographically chained audit trail of who authorized which agent to do what, until when: evidence that supports the human-oversight and record-keeping duties of deployers under EU AI Act Article 26, with eIDAS 2.0 electronic mandates in mind. AMR is a technical tool: it does not by itself make a system compliant.
Quickstart for agents
This section is written so that an AI agent (Claude Code, Cursor, any MCP client) can
install AMR and reach a first verified mandate on its own: no account, no email, no
person to ask. Everything runs on your machine; AMR makes no network call (two opt-in
exceptions, both off by default: report_issue once the operator sets AMR_ISSUE_TOKEN,
and the did:web key check once the operator sets AMR_DID_WEB_RESOLVE=1).
1. Run the server (needs uv).
The PyPI package is named mandatia; the Python module and the amr command keep their
names. uvx amr installs a different, unrelated project: do not use it. To run the
latest code of the repository instead:
AMR runs with version 1 and version 2 of the MCP Python SDK (mcp>=1.2,<3); no extra
version pin is needed.
Or clone the repository and run uv sync && uv run amr.
2. Declare it in your MCP client.
Claude Code:
Cursor (.cursor/mcp.json) and other clients that read an mcpServers object:
To run the repository code instead, the arguments are
["--from", "git+https://github.com/JC7333/amr", "amr"].
Set the three paths: by default AMR writes its database and its signing key in the
current directory of the client. Keep the .pem private key out of version control.
3. Reach a first verified mandate. The tools, in the order an agent uses them:
create_mandate: register who authorizes what (principal_id,agent_id,scope,legal_basis_regulation,legal_basis_article,duration_hours; addallowed_actions/forbidden_actionsfor a perimeter; onlyissue_action_tokenenforces it).verify_mandate(agent_id, action):authorized: truewhile the agent has an active mandate. It does not checkactionagainst the perimeter;issue_action_tokendoes.log_action(mandate_id, action): append an entry to the hash-chained journal.issue_mandate_attestation+get_registry_public_key: a signed document a third party checks offline withpython -m amr.verify_attestation.revoke_mandate(mandate_id, reason): the stop button;verify_mandatethen answersauthorized: false. Tokens and attestations already issued stay valid until they expire, except for a receiver that re-checks the token withcheck_action_token.get_proof(mandate_id): the full audit bundle (mandate, actions, integrity). If the agent already did something it should not have,contest_action(mandate_id, action_id, reason, response_days)records that the act is disputed, andlist_action_contestsreminds the deadline the operator chose.report_issue(title, description, version, context): report a problem with AMR itself. WithoutAMR_ISSUE_TOKENit makes no network call and returns a pre-filled link to open; with it, it files the issue on GitHub. Seedocs/report-issue-spec.md.
The same flow, runnable and checked by the test suite, is in
examples/quickstart_agent.py:
Machine-readable summaries for agents: AGENTS.md and llms.txt.
To report a problem, call report_issue or open an issue at
https://github.com/JC7333/amr/issues (never paste real mandate contents or personal data
into it).
Why AMR?
Modern AI agent infrastructure covers many concerns — but the legal mandate layer is missing:
EU AI Act Article 26 (Regulation (EU) 2024/1689) requires deployers of high-risk AI systems to assign human oversight and to keep the logs the system generates, to the extent they are under their control, for at least six months (Art. 26(6)). It applies from 2 December 2027 for Annex III systems and 2 August 2028 for Annex I systems (Regulation (EU) 2026/1744). Non-compliance with Article 26 can be fined up to €15 million or 3% of worldwide annual turnover, whichever is higher (Art. 99(4); for SMEs and start-ups, whichever is lower, Art. 99(6)). AMR records who authorized an agent, for what and until when, and what it did, in a tamper-evident form that this oversight can rely on; it complements the system's own logs, it does not replace them.
Structural hard-stop enforcement
AMR does not only record mandates — it enforces them. Agent runtimes must obtain a signed action token from AMR before performing any action. The token is issued if and only if a valid, non-expired, non-revoked mandate authorizes that specific action for that specific agent. No mandate → no token → no action.
Tokens are JWS (RFC 7515) signed with Ed25519 (EdDSA), include replay prevention
via the jti claim and the issued_tokens audit table, and carry the exact
action hash and scope digest they authorize. Consumers verify offline with the
AMR public key and MUST check aud, exp, and amr_action_hash.
Perimeter enforcement and delegation control
A mandate can carry a structured perimeter: the action types the agent may
request (allowed_actions), the ones it may never request (forbidden_actions,
which always win and also reach sub-types: forbidding agent.create forbids
agent.create.batch), whether it may delegate (delegation_allowed), and an
optional amount ceiling (max_amount with currency, applied to amount_actions). The token issuer then
checks the requested action against the perimeter, not only the existence of
the mandate:
- an action type outside
allowed_actions, or matchingforbidden_actions, gets no token (reason: "action_out_of_scope", with adetailand the mandate that refused it inrefused_by); - a sub-mandate (
parent_mandate_id) is created only if the parent is active, allows delegation, and was granted to the agent now delegating; the sub-mandate must stay inside the parent's allowed actions and limit, inherits the parent's prohibitions, and cannot outlive the parent; - every token issued under a sub-mandate re-checks the whole chain: revoke
(
revoke_mandate) or let expire the parent, and every sub-mandate below it stops getting tokens; - an optional cumulative ceiling (
period_max_amountover a rolling window ofperiod_daysdays, samecurrencyandamount_actions) caps the total of the amounts of the tokens issued under the mandate and every sub-mandate below it in the window: for the actions that carry an amount (those reached byamount_actions, every action when it is empty, and any action that declares anamount), an agent cannot split a large purchase into small ones, nor spend the same budget again through a sub-agent (detail: "period_amount_above_limit:<max> <CUR>/<days>d", with the mandate whose budget is spent inrefused_by). A token counts as soon as it is issued, used or not; a refused token counts for nothing. A sub-mandate's own ceiling may not exceed the parent's. The parent's ceiling is not copied into the sub-mandate's perimeter (it binds the sub-mandate all the same, at issuance): read the whole chain, asget_proofand the attestation do, to see every ceiling that applies. A sub-mandate that sets a ceiling under a parent with a per-action limit binds the union of bothamount_actions(an empty list on either side means every action); - optional risk classes (
risk_classes) bound how long a token stays usable, by what is at stake: each class names action types (reached with their sub-types, likeforbidden_actions) and amax_token_secondsfrom 1 to 86400, for example{"name": "irreversible", "actions": ["invoice.pay"], "max_token_seconds": 30}. A token for an action reached by a class lives at most that long; the shortest class wins, in the mandate and in every mandate above it in the delegation chain, so a sub-mandate can shorten the window, never lengthen it: it inherits the parent's classes (shown in its own perimeter) and may reuse a class name only with a shorter or equalmax_token_seconds(elselimit_exceeds_parent). The matched class names are sealed in the token asamr_risk_classes. A class never lengthens a token beyondAMR_TOKEN_LIFETIME_SECONDS. Mandates without classes keep their exact hash and tokens. A class may also set"registry_check": true: its tokens then carryamr_registry_check: true, and the receiver re-checks them withcheck_action_tokenat the moment of acting, so a revocation in the meantime is seen. A sub-mandate inherits the flag and cannot drop it. Classes without it keep their exact form and hash.
Example: an assistant agent told never to create sub-agents.
Root mandates without a perimeter keep working as before (free-text scope,
existence and validity checks only, same hashes). Perimeters without a cumulative
ceiling keep their exact canonical form, so their hashes, perimeter digests and
principal signatures are unchanged; schema v7 only adds the token_amounts table.
Limits of the cumulative ceiling: tokens issued before schema v7 carry no recorded
amount and are not counted (an older AMR cannot issue tokens on a capped branch: it
refuses the unknown perimeter fields); the ceiling binds one mandate and its branch,
not every mandate of the same principal. Two changes are deliberate and
breaking for sub-mandates: a new sub-mandate requires a parent that allows
delegation (so a free-text parent can no longer delegate), and tokens under any
sub-mandate, including ones created before v1.1, now require every parent to be
active and unexpired.
Deployment rule. These controls hold only if agents cannot register their own mandates.
create_mandateandregister_principal_keyare principal-side tools: expose them to the principal (or its back office), never to the agents it governs. An agent that can callcreate_mandatecan mint itself a fresh root mandate for any principal without a key, and one that can callregister_principal_keycan claim a principal that has not registered a key yet. Principal signatures (below) close the first gap once each principal has registered its key; binding keys to a verified identity is on the roadmap.
Principal signatures: the principal signs the mandate
A principal can register its own Ed25519 public key once (register_principal_key).
From then on, the registry accepts a mandate in its name only if the principal signed
it: prepare_mandate_signature returns the exact bytes to sign, and create_mandate
takes principal_signature and signed_at. The signature is sealed into the mandate's
chain_hash, cannot be used twice in the same registry, and is kept in the Proof Pack
with the public key and the signed bytes, so anyone can re-check it offline. Set
AMR_REQUIRE_PRINCIPAL_SIGNATURE=true to refuse every unsigned mandate.
What a valid signature proves: the mandate was signed with the key first registered
for that principal_id. For an ordinary principal_id the registry does not check who
registers a key (first registration wins), so register each principal's key at
onboarding, through the principal's own channel. An organisation can instead use a
did:web:<its domain> identifier: the key is then accepted only if the domain publishes
it in https://<domain>/.well-known/did.json, and the SHA-256 of that document is
sealed with the key (opt-in network access, AMR_DID_WEB_RESOLVE=1). This proves
control of the domain at registration time, not a qualified signature. Consumers that
rely on a did:web principal must check did_web_unverified: false in the Proof Pack,
the attestation or the insurer dossier.
Behaviour change (did:web keys): with the default configuration, registering a key
for a did:web: identifier is now refused (did_web_resolution_disabled) instead of
"first registration wins"; keys already registered stay valid and are reported as
did_web_unverified. A scheme:method: prefix written with non-ASCII look-alike
characters is refused (principal_id_lookalike). Mandates of principals without a key behave as before (same
hashes; the result gains "principal_signature": null); mandates created before a key
was registered stay valid. See docs/principal-signature-spec.md, including its
known limits.
See docs/token-issuance-spec.md for the full specification.
Sector templates
A template is a ready-made, versioned perimeter for one kind of agent, with a catalog
that says in plain words what each action type means. The first one,
agent-sante-rgpd, covers an assistant agent in a medical practice: it may read the
record, handle the schedule, draft letters and patient messages for a human to send,
and order supplies under a ceiling; it may never prescribe, diagnose, change or export
the record, contact a patient itself, or create a sub-agent.
The mandate records template_id, template_version and template_digest in its
metadata. That metadata is a label, not sealed into the chain hash; the perimeter is
sealed and is what the token issuer enforces. Templates only fill in existing
create_mandate arguments: existing mandates and their hashes are unchanged. They are a
Python API for now (no MCP tool lists them yet). Like every perimeter, the template
holds only if the agent cannot call create_mandate itself (see the deployment rule
above). See docs/templates/agent-sante-rgpd.md.
When a body (committee, board) authorized the agent, the mandate can record its decision
(organ_decision: body, title, date, optional reference, designated persons or roles);
the health template accepts it, create_mandate seals it into the chain hash (root
mandates with a closed list of allowed actions only), and verify_mandate and
get_proof return it. Mandates without it keep their exact hash.
Third-party verification: the receiver checks the mandate alone
An agent can show whoever receives it (a seller, a hospital, an insurer) a signed
mandate attestation (issue_mandate_attestation). The receiver checks it alone,
offline, with the registry's public key fetched once (get_registry_public_key): no
account, no call to the registry. The attestation states that the mandate exists, who
granted it (and whether the principal signed it; with include_principal_payload the
receiver re-checks that signature itself), the agent, the scope and structured perimeter, the expiry, and that the
mandate and every mandate above it were active when the attestation was issued.
What it does not prove: that the mandate is still active now. An attestation lives
at most AMR_ATTESTATION_LIFETIME_SECONDS (default 15 minutes) and never outlives the
mandate or a parent; a revocation made after issuance reaches an offline receiver only
when the attestation expires. It is a bearer document: the receiver must make sure it
is dealing with the agent named in agent_id. Pass audience to address it to one
receiver. Mandate metadata is not disclosed unless include_principal_payload is
set (it is inside the signed bytes). The registry trusts its own database: protect the
file. Existing mandates and their hashes are unchanged; issuances are logged in a
new append-only table, issued_attestations (schema v5). See
docs/mandate-attestation-spec.md.
Public registry: online verification without an account
AMR runs over stdio next to each operator, so nobody else can query it. The public registry is a separate, read-only verification point: the operator exports a signed snapshot of the registry, and a small WSGI application (standard library only) serves it. A receiver checks a mandate identifier or pastes an attestation, in a browser (HTML) or from a script (JSON), with no account.
The snapshot holds, per mandate, the agent, a SHA-256 reference of the principal (never
the identifier in clear), the perimeter, dates, recorded status, revocation date and
chain hash; never the scope text, legal basis, metadata or revocation reason. The
application verifies the snapshot with the pinned registry key before serving it
(and serves it raw at /snapshot for receivers who verify it themselves; trust in
the JSON answers is trust in whoever runs the app),
has no write path, keeps no log and never reads the client's address. Every result
gives the snapshot date and the time the verification took. Limit: a revocation made
after the snapshot shows only at the next export; set
AMR_PUBLIC_SNAPSHOT_MAX_AGE_SECONDS to refuse answers from an older snapshot. Deployment and domain are the
operator's step. Existing mandates and their hashes are unchanged; no schema change.
See docs/public-registry-spec.md.
Vision: from registry to enforcement
AMR started as a mandate registry — it stores, verifies, and audits mandates. This is necessary but not sufficient.
The problem: if the mandate check is a voluntary call, agents skip it. The system degrades to advisory compliance within weeks of real-world deployment.
AMR now provides structural enforcement: it is the layer that issues execution authorizations (issue_action_token). No valid mandate → no authorization → no action possible. The system refuses to work without the mandate — not because someone recommends checking, but because the architecture makes it impossible to bypass.
The open-source registry stays free. The enforcement layer is the commercial product.
Quick Start
Claude Desktop Integration
Add AMR to your Claude Desktop config file:
Location: %APPDATA%\Claude\claude_desktop_config.json
Restart Claude Desktop. All AMR tools will be available immediately.
Tools
create_mandate
Register a new legal mandate authorizing an AI agent to act on behalf of a principal.
Parameters:
Returns:
perimeter holds the structured perimeter when one was given (it is sealed into
chain_hash). Perimeter and delegation errors return {"error": "...", "code": "..."}
with a stable code: perimeter_invalid, parent_mandate_not_found,
parent_mandate_<status>, parent_mandate_expired, delegation_not_allowed,
delegator_mismatch, sub_mandate_requires_perimeter, perimeter_exceeds_parent,
limit_exceeds_parent, expires_after_parent. A broken chain above the parent
returns parent_mandate_<status>, parent_mandate_expired or
delegation_chain_too_deep (more than 16 levels).
principal_signature is {"key_id", "signed_at"} for a signed mandate. Signature
errors: principal_signature_required, principal_signature_incomplete,
principal_key_not_registered, signed_at_invalid, principal_signature_stale,
principal_signature_invalid, principal_signature_replayed.
register_principal_key
Register a principal's Ed25519 public key, once, with a proof of possession.
Returns {"status": "registered", "principal_id", "key_id", "registered_at", "chain_hash", "domain_proof"}. domain_proof is null, except for a did:web:<domain> principal: its key
is accepted only if https://<domain>/.well-known/did.json lists it (needs
AMR_DID_WEB_RESOLVE=1), and domain_proof holds did_document_url,
did_document_sha256 and verified_at. See docs/principal-signature-spec.md,
section 1 bis.
prepare_mandate_signature
Same arguments as create_mandate (plus optional signed_at, default now). Writes
nothing; returns {"payload", "payload_b64url", "signed_at"}: the exact bytes the
principal signs before calling create_mandate.
prepare_mandate_pdf
Same arguments as prepare_mandate_signature. Writes nothing; returns the mandate as a
PDF for the principal to read and sign with its own signature tool (for example a PAdES
signature): {"pdf_base64", "filename", "pdf_sha256", "attachment_name", "payload_sha256", "payload", "payload_b64url", "signed_at"}. The PDF carries the exact
payload of prepare_mandate_signature as the attached file amr-mandate.json and prints
its SHA-256 on every page. The same arguments and signed_at always give the same PDF.
The registry does not sign this PDF and, in this version, does not check a signature
made on it: the mandate is still accepted on the principal's Ed25519 signature of the
same payload, with signed_at within AMR_PRINCIPAL_SIGNATURE_MAX_AGE_SECONDS
(default 900 seconds) of the registry clock; generate, sign and register within that
window (see docs/principal-signature-spec.md, section 2 bis).
verify_mandate
Check whether an AI agent has a currently active, non-expired mandate (for a
sub-mandate, every mandate above it must be active and unexpired too). It does not
check the action against the perimeter: issue_action_token does.
Parameters:
Returns:
log_action
Log an agent action under an active mandate. Cryptographically chained to previous actions.
Parameters:
Returns:
The mandate must be active, not revoked and not expired. For a sub-mandate, so must
every mandate above it: revoke or let expire a parent and its whole branch stops
logging. The refusal then says why and which mandate broke the chain:
{"error": "...", "reason": "parent_mandate_revoked", "refused_by": "<parent id>"}
(also parent_mandate_expired, parent_mandate_<status>, parent_mandate_not_found,
delegation_chain_too_deep).
revoke_mandate
The principal's stop button. Records a final revocation of a mandate, with the
reason the principal gives. Append-only: a chained row in mandate_revocations and a
revoked mandate event; the mandate row is not modified and its hash does not change.
Parameters:
Only the mandate's own principal revokes it (for a sub-mandate, the agent that
delegated it). Same signature policy as create_mandate; build the exact bytes to sign
with prepare_revocation_signature(mandate_id, reason, signed_at=None).
Effect, as soon as it returns: issue_action_token and issue_mandate_attestation
refuse the mandate (mandate_status_revoked) and every sub-mandate below it
(parent_mandate_revoked);
verify_mandate no longer returns it; no sub-mandate can be created under it;
log_action refuses it and every sub-mandate below it; get_proof and export_insurer_dossier report it. Tokens already issued cannot be
called back and stay valid until their exp: the response gives their number and the
last expiry, unless the receiver re-checks the token with check_action_token when it
acts (see below). Mandate attestations already issued also stay valid until their exp
(15 minutes by default) for a receiver that checks them offline: the response gives
their number and last expiry too, for the mandate and its sub-mandates.
Returns:
A token lives at most AMR_TOKEN_LIFETIME_SECONDS (300 s by default) and never past the
expiry of its mandate or of any mandate above it in the delegation chain, nor beyond
the shortest risk class (risk_classes) that reaches its action type in that chain.
The caller's lifetime_override_seconds can only shorten it. Breaking change for callers that asked
for a token longer than the default: they now get the default. After a revocation, the
window in which an already issued token still works is therefore at most that lifetime,
whatever the depth of the chain. See docs/revocation-spec.md.
contest_action
The agent has already acted, and the principal disputes the act. Revoking the mandate
stops what comes next; contest_action records that one past act, logged with
log_action, is contested, when, why, and by whom. Append-only: a chained row in
action_contests (one per action; it cannot be withdrawn or edited) and an
action_contested mandate event. The action log, the mandate and every hash already
recorded stay as they are.
Parameters:
contested_by may be the mandate's principal or the principal of any mandate above it
in the delegation chain (an organization disputes what a sub-agent did), whatever the
status of those mandates. Same signature policy as create_mandate and
revoke_mandate; build the bytes with prepare_contest_signature (same arguments,
plus signed_at=None).
respond_by = contested_at + response_days is the operator's own deadline, for
example the delay its counsel gives before keeping or using what was ordered could be
read as accepting it. AMR computes no legal deadline and asserts none. A contest
does not undo the act and does not revoke the mandate: call revoke_mandate for that.
Returns:
Errors (code): invalid_input, mandate_not_found, action_not_found (also when
the action belongs to another mandate), contester_not_in_chain,
action_already_contested, and the principal signature codes of revoke_mandate.
Reminder: list_action_contests(mandate_id=None, contested_by=None, due_within_days=None) (read-only; give mandate_id, contested_by or both) returns
the contests, nearest respond_by first, each with reminder.days_left (negative once
passed) and reminder.deadline_passed. Call it on a schedule, for example each morning
with due_within_days=3. get_proof lists the contests of a mandate in
action_contests and re-checks them in chain_integrity.contests_intact. See
docs/action-contest-spec.md.
check_action_token
For the receiver of an agent's action: re-check a token with the registry at the moment
of acting, before an irreversible step. Required when the token carries
amr_registry_check: true (a risk class with registry_check); possible for any token.
Read only.
It verifies the signature (EdDSA only, this registry's key), iss, aud, exp, nbf,
that the token is one the registry issued, then that the mandate and every mandate
above it are still active, not revoked and not expired.
The registry opens its database read-only and writes nothing for a check, and the
answer is not signed: it is not evidence that anyone checked. Call the registry over
your own channel; never accept a check result relayed by the agent whose token it is.
Once a mandate with registry_check exists, an older AMR version cannot read its
perimeter (it fails closed). See docs/token-issuance-spec.md, Registry Check at
Execution.
get_proof
Generate a complete Proof Pack — a full audit bundle for a mandate including all actions and cryptographic chain verification.
Parameters:
Returns:
mandate.principal_signature holds the principal's signature, public key, the key's
proof of possession and the signed bytes (or null); principal_signature_valid
re-checks them against the stored mandate. It is false if the signature does not
match, or if it is missing although the principal had a key when the mandate was
created; null for a mandate whose principal had no key then.
Once revoke_mandate has run, mandate.status reads "revoked" (the stored value
stays in mandate.stored_status) and mandate.revocation holds revoked_at, reason, the principal's signature
of the revocation (or null) and its chain hash; revocation_signature_valid re-checks
that signature against the principal's key (null when there is nothing to check).
delegation_chain says whether the chain above a sub-mandate still holds:
{"broken": true, "reason": "parent_mandate_revoked", "refused_by": "<parent id>"}
when a mandate above it is revoked, expired, not active or missing (the sub-mandate's
own mandate.status does not change: the cut is above it);
{"broken": false, "reason": null, "refused_by": null} otherwise, and always for a root
mandate.
issue_mandate_attestation
Returns {"attestation", "jti", "issued_at", "expires_at", "key_id", "issuer"}, or
{"denied": true, "reason", "mandate_id"}, for example mandate_not_found,
agent_mismatch, mandate_status_<status>, mandate_status_revoked, mandate_expired,
parent_mandate_<status>, parent_mandate_revoked, principal_signature_invalid, with refused_by when a
parent is the cause (full list in docs/mandate-attestation-spec.md).
get_registry_public_key
No parameters. Returns {"issuer", "key_id", "algorithm": "EdDSA", "public_key_pem", "public_key_jwk"}: what a receiver needs to verify attestations offline.
export_insurer_dossier
Export a signed dossier of one mandate over a period: the piece an insurer or an auditor asks for about an AI agent. Read-only; facts from the registry only.
Parameters:
The dossier holds: the mandate (principal, agent, scope, declared legal basis,
dates); its perimeter (allowed and forbidden action types, right to delegate, amount
limit); whether it is in force at generation time, and why not (mandate_revoked,
mandate_expired, or a broken chain above it such as parent_mandate_revoked); its
sub-mandates, each with depth, perimeter, in-force status and the number of actions it
logged in the period (a cut mandate cuts its whole branch); the actions the mandate's
own agent logged in the period; the status events and action tokens of the period, for
the mandate and its sub-mandates; the integrity checks (action log chain, principal
signature, last chain hash); and a fixed list of the dossier's limits (for example:
refused token requests are not recorded, so the dossier lists issued tokens, not
blocked actions).
Returns:
The signature is a JWS (RFC 7515, EdDSA / Ed25519, same registry key as action tokens) whose payload is the canonical JSON of the dossier. Verify it offline:
The export never creates a signing key: on a registry without one it returns an error.
Get the registry public key out of band: public_key_pem in the export is a
convenience, and a dossier that carries its own key proves nothing about who signed
it. The HTML page is a self-contained print view (no external resources; print to PDF
from the browser) that shows the dossier's SHA-256 and the key id; only the JSON
signature is proof. See docs/insurer-dossier-spec.md.
How It Works
Security
-
Pydantic v2 validation — All tool inputs are validated before any DB interaction. Unknown fields are rejected (
extra="forbid"). Duration, string lengths, and UUID formats are enforced at the model layer. -
Parameterized SQL only — Every database query uses
?placeholders. No string interpolation or f-strings in SQL. Enforced byruffS608 rule on every commit. -
Append-only tables — The
mandatesandaction_logstables are neverUPDATEd orDELETEd. The audit trail is immutable by design. Status changes use a separatemandate_eventstable. -
SHA-256 cryptographic chain — Each mandate and each action log entry is linked to the previous one via a SHA-256 hash (git-style). Any tampering invalidates all subsequent hashes, making the trail tamper-evident.
-
stdio transport only — AMR runs as a local MCP server over stdio. No listening port, no inbound network exposure, no outbound HTTP from tools, with one opt-in exception:
report_issuesends one HTTPS request to the GitHub API, and only when the operator has setAMR_ISSUE_TOKEN. The attack surface is minimal.
Regulatory Context
Roadmap
Development
Project Structure
Environment Variables
License
Apache License 2.0 — See LICENSE for full terms.
Free to use in commercial products. Compatible with EU AI Act open-source provisions.
⚠️ Disclaimer: AMR is a technical infrastructure tool. It does not constitute legal advice. Compliance with EU AI Act, eIDAS 2.0, or any other regulation remains the responsibility of the operator. Consult qualified legal counsel for your specific regulatory obligations.
Source: README.md at commit aa073aa
Tools
0Version history
1- v0.1.1LatestNov 7, 2026
Score
40% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts
Declare a license in the repository or the registry entry.