deploy pending · free · no key

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.

1

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.

2

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.

3

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.

free

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.

free

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.

free

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}.

free

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.

free

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.

free

warrant_stats

Summary counts from the issuance store. Requires WARRANT_ISSUE_DB. Returns {total_issued, active, expired, revoked}. active+expired+revoked=total_issued.

free

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.

free

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.

free

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).

free

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}]}]}.

free

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}.

free

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}.

free

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}.

free

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}.

free

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}}.

free

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.

free

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}.

free

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.

free

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.

free

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}.

free

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}.

free

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.

free

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}.

free

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.

free

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}.

free

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}.

free

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.

free

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.

free

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.

free

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.

free

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}.

free

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}.

free

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}.

free

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.

free

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}.

free

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}.

free

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}.

free

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.

free

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}.

free

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
# 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.

mcp configJSON
{
  "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.