Warrant MCP
Stop AI agents from acting without a signed human approval. Issue Ed25519-signed warrants, render them for user review, and verify them before any high-stakes action runs. No key. No account. No side-channel.
How it works
Three steps. The agent cannot forge or skip any of them.
Agent proposes an action
Before executing pay, transfer, delete, grant, accept, file, send, or execute, the agent calls warrant_issue with the verb and the exact values it intends to use.
User reads and signs
warrant_render surfaces a clear English summary: "Pay $1,200 to Acme Ltd on 2026-09-14." The user sees precisely what they are approving — not a prompt that could be reworded after the fact.
Agent verifies before acting
warrant_verify checks the Ed25519 signature against the payload. If the warrant is tampered with, the check fails. The agent proceeds only on a clean result.
40 tools — all free
No key, no account. Attach the server and call them.
warrant_issue
Issue a signed warrant. Provide verb and values; returns signed dict and rendering for approval. Verbs: pay, transfer, delete, grant, accept, file, send, execute.
warrant_verify
Verify a warrant. Checks the Ed25519 signature using the public key embedded in the warrant. Returns {valid:true} when the signature is intact; {valid:false, error:<reason>} on any failure.
warrant_render
Render a warrant as human-readable text for review. The rendering is injective — no two distinct payloads produce the same output. Returns {text, lines}.
warrant_revoke
Revoke a warrant by its warrant_id. After revocation, warrant_verify returns {valid:false} even if the signature is intact. Revocation is checked before expiry. Idempotent — revoking again is a no-op.
warrant_list
List warrants. Requires WARRANT_ISSUE_DB. Active by default; include_expired/include_revoked widen the result. Returns {warrants, total, limit, offset}. Paginate via limit and offset.
warrant_stats
Summary counts from the issuance store. Requires WARRANT_ISSUE_DB. Returns {total_issued, active, expired, revoked}. active+expired+revoked=total_issued.
warrant_get
Look up a single warrant by warrant_id. Requires WARRANT_ISSUE_DB. Returns {warrant_id, verb, summary, issued_at, expires_at, revoked}, or NOT_FOUND if the ID is absent from the store.
warrant_purge
Remove expired warrant records. Requires WARRANT_ISSUE_DB. Deletes expired on/before before_utc (default: now). Verb filter for one class. Returns {deleted, purged_before}. Idempotent.
warrant_reissue
Reissue a warrant with a fresh TTL, copying verb and values. Blocked if revoked. Returns new dict, rendering, and reissued_from (original warrant_id).
warrant_schema
Return the list of supported verbs and their field specs. No arguments required. Optional verb filter returns one verb. Returns {verbs: [{verb, fields: [{name, type, label}]}]}.
warrant_batch_revoke
Revoke multiple warrants in one call. Pass a list of warrant_ids; each is revoked independently. Idempotent — already-revoked IDs are counted but not re-stamped. Returns {revoked, already_revoked, results}.
warrant_batch_issue
Issue multiple warrants in one call. Pass a list of issue specs {verb, values, ttl_seconds?}; each is issued independently. Partial success — invalid specs return per-item errors without aborting valid ones. Returns {issued, errors, total, success_count, error_count}.
warrant_batch_verify
Verify multiple warrants in one call. Pass a list of warrant dicts; each is verified independently for signature validity, revocation and expiry. Partial results — all items are processed regardless of individual failures. Returns {verified, failed, results, total, success_count, error_count} where each result has {index, valid, error, details}.
warrant_search
Search issued warrants by summary text and optional date-range filters. Requires WARRANT_ISSUE_DB. query is a case-insensitive substring match against the human-readable summary (empty = all). Combine with verb, issued_after, issued_before to narrow results. Active warrants only by default; include_expired/include_revoked widen the set. Returns {warrants, total, limit, offset, query}.
warrant_count
Count issued warrants grouped by verb. Requires WARRANT_ISSUE_DB. Each row shows active, expired, and revoked counts for one verb; active+expired+revoked equals total for every row. totals aggregates across all verbs. Optional issued_after/issued_before narrow to a time window. Returns {by_verb: [{verb, active, expired, revoked, total}], totals: {active, expired, revoked, grand}}.
warrant_status
Get the lifecycle state of a warrant by ID. Requires WARRANT_ISSUE_DB. Reports the server's recorded state from the issuance and revocation stores without re-checking the cryptographic signature. Use warrant_verify for signature verification. Returns {warrant_id, verb, summary, state, issued_at, expires_at, revoked_at, time_remaining_seconds} where state is 'active', 'expired', or 'revoked'. time_remaining_seconds is 0 when expired or revoked.
warrant_expire_soon
List active warrants expiring within a given window (default 300 s). Requires WARRANT_ISSUE_DB. Excludes already-expired and revoked warrants. Results are sorted by expires_at ascending (soonest first). Useful for proactive renewal checks and alerting before a warrant lapses. Returns {warrants: [{warrant_id, verb, summary, expires_at, time_remaining_seconds}], count, within_seconds}.
warrant_extend
Extend the TTL of an active warrant. Parses the original warrant dict, checks it is not revoked or expired, then issues a new warrant with remaining_seconds + extend_by_seconds as its TTL. The old warrant is revoked so the caller can only use the new token. Returns {warrant, rendered, summary, previous_warrant_id, old_expires_at, new_expires_at, extended_by_seconds}. Requires an active (non-expired, non-revoked) warrant.
warrant_tag
Add, remove, or list tags on a warrant. Tags are short labels (max 64 chars) you attach to a warrant_id for organisation and filtering. Requires WARRANT_ISSUE_DB to be set. Pass add=[] and/or remove=[] to mutate; omit both to read current tags. Returns {warrant_id, tags, added, removed, tag_count}. Adding an existing tag or removing an absent tag are both silent no-ops.
warrant_tag_search
Search warrants by tag (requires WARRANT_ISSUE_DB). Pass one or more tags; by default returns warrants that have ANY of the specified tags. Set match_all=true to require ALL tags. Excludes expired and revoked warrants by default (set include_expired/include_revoked to override). Supports pagination via limit/offset. Returns {warrants: [{warrant_id, verb, summary, issued_at, expires_at, tags}], total, limit, offset, tags, match_all}.
warrant_tag_list
List all unique tags in the store with usage counts (requires WARRANT_ISSUE_DB). Useful for tag discovery before calling warrant_tag_search. Results are sorted by count descending then tag ascending. Optional prefix filters tags that begin with the given string. Supports pagination via limit/offset. Returns {tags: [{tag, count}], total, limit, offset, prefix}.
warrant_annotate
Add, list, or remove free-text annotations on a stored warrant (requires WARRANT_ISSUE_DB). Annotations let agents and humans attach reasoning or context notes to any warrant_id. action=add (default when note is given): attach a note; returns the new annotation. action=list (default when no note/annotation_id): list all annotations for a warrant. action=remove (default when annotation_id given): delete one annotation by annotation_id.
warrant_export
Export warrants as a structured snapshot for audit, backup, or compliance (requires WARRANT_ISSUE_DB). Accepts the same filters as warrant_list. format=json (default): returns a JSON array string of warrant records. format=csv: returns a CSV string with columns warrant_id,verb,summary,issued_at,expires_at,state (state = active | expired | revoked). limit=0 (default) exports all matching warrants; set limit>0 to cap output. Returns {format, count, data}.
warrant_import
Import warrant records into the issuance store from a JSON array (requires WARRANT_ISSUE_DB). Accepts the JSON output of warrant_export or any list of records with fields: warrant_id, verb, summary, issued_at, expires_at. Uses INSERT OR IGNORE — idempotent by warrant_id (re-importing the same records is safe). Useful for backup restore, migration, and audit seeding. Returns {imported, skipped, errors, total} where imported=new records added, skipped=already existed, errors=records with missing or invalid fields.
warrant_annotate_search
Search across all warrant annotations by note text (requires WARRANT_ISSUE_DB). query is a case-insensitive substring match against the note field (empty = return all). Narrow further with author (substring match) or warrant_id (exact match). Results are sorted by created_at descending (newest first). Supports pagination via limit/offset. Returns {annotations: [{annotation_id, warrant_id, note, author, created_at}], total, limit, offset, query}.
warrant_annotate_list
List all annotations for a specific warrant by warrant_id (requires WARRANT_ISSUE_DB). Returns annotations sorted oldest-first. Supports pagination via limit/offset. Use warrant_annotate_search to search across all warrants by note text or author. Returns {warrant_id, annotations: [{annotation_id, note, author, created_at}], total, limit, offset}.
warrant_annotate_delete
Delete a specific annotation by annotation_id (requires WARRANT_ISSUE_DB). Returns {deleted: true, annotation_id} on success or a NOT_FOUND error if the annotation does not exist. Use warrant_annotate_list to retrieve annotation_ids for a warrant.
warrant_audit_log
Query the warrant audit log for lifecycle events (issued, revoked, extended, reissued). Requires WARRANT_ISSUE_DB. Returns {events, total, limit, offset} where each event has {log_id, warrant_id, event_type, ts, actor, detail}. Filter by warrant_id, event_type, after (ISO timestamp), before (ISO timestamp). Pagination: limit (default 50, 0=all) and offset. Events are ordered oldest-first.
warrant_validate
Validate verb and values against the warrant schema without issuing a warrant. Returns {valid, verb, canonical_values, errors}. valid=true means the values are correct and would succeed in warrant_issue. valid=false means errors lists the schema violations. canonical_values contains the normalised form when valid=true (null otherwise). Use this as a pre-flight check before calling warrant_issue.
warrant_clone
Clone an existing warrant to issue a new one with the same verb and values, optionally overriding specific fields. Unlike warrant_reissue, clone works on revoked or expired warrants and accepts an overrides dict to mutate individual field values before issuing. Returns {warrant, rendered, summary, cloned_from, overrides_applied} where cloned_from is the source warrant_id and overrides_applied is the override dict used.
warrant_chain
Traverse the ancestry lineage of a warrant, following cloned_from, reissued_from, and extended_from links recorded in the audit log. Returns the chain from the given warrant back to its root, with each node's warrant_id, relationship, verb, summary, issued_at, expires_at, revoked, and depth. Requires WARRANT_ISSUE_DB to be set. Returns {warrant_id, chain, depth, root_warrant_id, truncated}.
warrant_diff
Compare two warrants field-by-field. Accepts the full warrant dicts returned by warrant_issue (not IDs). Returns same=true when both warrants are identical in verb, values, expiry, signer, and tags (if store available). When same=false, differences lists exactly what changed: verb, values (added/removed/changed keys), expires_at, signer, and tags. Returns {warrant_a_id, warrant_b_id, same, differences, summary, a, b}.
warrant_tree
Traverse all descendants of a warrant, following cloned_from, reissued_from, and extended_from links recorded in the audit log. This is the inverse of warrant_chain: chain goes UP to ancestors; tree goes DOWN to descendants. Returns a recursive tree structure where each node has warrant_id, relationship, verb, summary, issued_at, revoked, depth, and children. Requires WARRANT_ISSUE_DB. Accepts include_revoked=false to prune revoked branches. Returns {warrant_id, total_nodes, max_depth, truncated, tree}.
warrant_bulk_tag
Add or remove tags on all warrants that match a filter in one call. Requires WARRANT_ISSUE_DB. Filters: verb, query (summary substring), issued_after, issued_before, include_expired (default false), include_revoked (default false). At least one of add or remove must be provided. Safety cap: limit controls the maximum number of warrants processed (default 500, max 1000). Returns {updated, total_matched, add, remove} where updated is the count of warrants whose tag set actually changed.
warrant_bulk_extend
Extend the operational TTL of all active (non-expired, non-revoked) warrants that match a filter in one call. Requires WARRANT_ISSUE_DB. Updates the expires_at field in the issuance store for each matching warrant. The cryptographic signature in the warrant blob is unchanged — use warrant_list or warrant_status to see the updated operational expiry. Filters: verb, query (summary substring), issued_after, issued_before. extend_by_seconds is required and must be a positive integer. Safety cap: limit controls the maximum number of warrants processed (default 500, max 1000). Returns {extended, skipped_revoked, skipped_expired, total_matched, extend_by_seconds}.
warrant_bulk_revoke
Revoke all warrants that match a filter in one call. Requires WARRANT_ISSUE_DB. Skips warrants that are already in the revocation store. By default only active (non-expired) warrants are revoked; set include_expired=true to also revoke warrants that have already passed their expiry time. Filters: verb, query (summary substring), issued_after, issued_before. Safety cap: limit controls the maximum number of warrants processed (default 500, max 1000). Returns {revoked, skipped_already_revoked, total_matched}.
warrant_bulk_annotate
Add a note to many warrants at once. Requires WARRANT_ISSUE_DB. Each warrant in warrant_ids receives an independent annotation with the same note and author. warrant_ids must be a non-empty list of strings (max 1000). note is required; author is optional. Returns {annotated, errors, results} where each result has {warrant_id, status, annotation_id}.
warrant_bulk_reissue
Reissue many warrants in one call, rotating their IDs and resetting their TTLs. Accepts a list of full warrant dicts (as returned by warrant_issue or warrant_verify). Each non-revoked warrant is signed fresh under the server key, producing a new warrant_id and a new expires_at. Revoked warrants are skipped and reported. warrants must be a non-empty list (max 1000). ttl_seconds controls the new expiry for every reissued warrant (default 600). Returns {reissued, skipped_revoked, errors, results} where each result carries {original_id, new_warrant_id, status} on success or {original_id, status, error} on failure.
warrant_bulk_get
Fetch many warrants by ID in one call. Pass warrant_ids (list of warrant_id strings, max 1000); each ID is looked up in the issuance store. Found records include {warrant_id, verb, summary, issued_at, expires_at, state} where state is 'active', 'expired', or 'revoked'. IDs absent from the store are collected in not_found. Requires WARRANT_ISSUE_DB. Returns {found, not_found, found_count, not_found_count, total_requested}.
warrant_bulk_status
Get full status of many warrants by ID in one call. Like warrant_status for a single warrant, but accepts warrant_ids (list of warrant_id strings, max 1000). Each found entry includes {warrant_id, verb, summary, state, issued_at, expires_at, revoked_at, time_remaining_seconds} — the same shape as warrant_status. IDs absent from the store are collected in not_found. Requires WARRANT_ISSUE_DB. Returns {found, not_found, found_count, not_found_count, total_requested, checked_at}.
# Supported verbs pay · transfer · delete · grant · accept · file · send · execute
Endpoint config
Add this to your Claude Desktop or Cursor MCP settings file. No key needed.
{
"mcpServers": {
"warrant": {
"url": "https://warrant.hatchloop.dev/mcp"
}
}
}Why cryptographic warrants?
Prompts can be reworded
A prompt saying "you approved the payment" can say anything the model inferred. A warrant is a frozen, signed structure — the approved values cannot be changed after the user signs.
No server trust required
The public key is embedded in every warrant. Any party — agent, auditor, or court — can verify the signature offline. There is no authority to phone home to.
Injective rendering
warrant_render produces output that uniquely identifies the warrant. Two different approvals cannot produce the same text, so a user reading the summary sees exactly what they are approving.
Works with any agent stack
Standard MCP transport over HTTP. Works with Claude Desktop, Cursor, any MCP-compatible agent framework, or a raw HTTP call. No SDK, no vendor lock-in.
Start in two minutes
Add one URL to your MCP config. No sign-up. No credits.