haia-trace-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
haia-trace-mcp
An MCP server that lets an AI agent read its own Haia Trace Operation Receipts and check, before paying again, whether earlier paid operations actually completed.
A wallet confirms funds left. A payment protocol confirms the payment was
accepted. A chain confirms settlement. None of them confirms that the
operation as a whole finished, so an agent can keep spending against outcomes
that never arrived. Haia Trace already produces a per-operation verdict
(full or partial) from the events an agent recorded on its own side. This
project puts that verdict in front of the agent through MCP, and adds a small
advisory pre-payment guard whose review outcomes go to a human.
Independent, MIT-licensed, built on Haia's published @usehaia/* packages.
Not affiliated with or endorsed by Haia.

The demo, recorded from a real run with
examples/demo/record.sh. The money is simulated; the x402 client, the
recorder, the receipts and the MCP tools are real.
What it does
Tool | Reads | Writes | Returns |
| runs | — | one row per receipt, newest first: |
| one run | — | the full Receipt exactly as |
| all runs, policy, review log | one |
|
| one run | — | a deterministic plain-language paragraph about one receipt |
| review log | — |
|
There is no tool to approve a review. That is the design, not an omission: a human approves or rejects from the command line, and the agent can only read the result.
agent ──MCP──▶ haia-trace-mcp ──reads──▶ .trace/events/*.ndjson (written by the trace-x402 recorder)
│ ──reads──▶ .trace/templates, bundled templates
│ ──appends─▶ .trace/guard/decisions.ndjson
human ──CLI──▶ haia-trace-mcp review approve|reject ──appends─▶ .trace/guard/decisions.ndjsonRelated MCP server: agenticrail-mcp
Quickstart (60 seconds)
Requires Node 22 and pnpm.
git clone <this repo> && cd haia-trace-mcp
pnpm install && pnpm build
sh examples/demo/run.shThe demo pays two simulated sellers, one of which never settles, builds the
receipts with haia-trace, asks check_before_pay before paying the second
seller again, gets review, rejects it from the CLI, and has the agent read
the decision and the explanation. See examples/demo.
To serve a real Trace directory to a host:
node dist/cli.js --dir /path/to/.trace --guard-config /path/to/haia-guard.config.jsonClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"haia-trace": {
"command": "node",
"args": [
"/abs/path/haia-trace-mcp/dist/cli.js",
"--dir",
"/abs/path/to/.trace"
]
}
}
}Tool reference
Identity: an operation is (run_id, operation_id). A run is one recorder
session, .trace/events/<run_id>.ndjson; context_id (the operation id) is
only unique within a run, so this server never merges runs and never looks an
operation up by id alone.
list_operations
{
"template": "x402-buyer",
"run": "latest",
"completeness": "partial",
"limit": 20
}
// run: "latest", a run id, or omit for all runs. limit ≤ 100, default 50.Returns { operations: [{ run_id, operation_id, template, completeness, missing_stages, exception_count, last_event_at }], runs_scanned }.
get_receipt
{
"run_id": "1794165874559",
"operation_id": "e4cc6413-…",
"template": "x402-buyer"
}Returns the Receipt from @usehaia/trace-core (operation, completeness,
stages, missing, exceptions, events) plus run_id. The receipt is
byte-for-byte what haia-trace build --json produces for the same run and
template; test/receipts.parity.test.ts checks this on every shipped fixture.
check_before_pay
{
"counterparty": "0xB0B0…0002",
"amount": "0.25",
"asset": "USDC",
"template": "x402-buyer"
}
// counterparty: the seller's pay_to address, or the service host / URL.
// amount: a decimal string in display units. Never a float.
// asset: registry key, symbol or token address; default: the policy's asset.Returns { decision, reasons: [{ rule, detail, evidence?: [{ run_id, operation_id }] }], review_id? }.
Rules, in order; the first block wins, any review makes the decision
review, otherwise allow. reasons always ends with a history line.
# | rule | fires when |
1 |
| the counterparty is on the denylist → block |
2 |
| the allowlist is non-empty and the counterparty is not on it |
3 |
| more than |
— |
| the amount cannot be interpreted in the policy's asset; no money math is attempted |
4 |
| spend to this counterparty inside |
5 |
| the amount exceeds |
"Open partial" is deliberately narrow: a partial receipt where no payment was ever made (the agent saw a 402 and walked away) is not a risk to the counterparty and does not count. A partial with a fault event does count when the money stage closed, because from the buyer's side a reported failure and a silently settled payment cannot be told apart.
Idempotency: the same (counterparty, amount, asset) within 10 minutes
returns the existing pending review_id instead of opening another review.
explain
Same input as get_receipt. Returns { run_id, operation_id, template, completeness, explanation }.
The paragraph is built by fixed rules from the receipt, so the same receipt
always yields the same text. It states what was observed and what was not,
including the buyer-side blind spot: a payment whose settlement response
never arrived and one that settled silently look identical from the buyer's
side, and the text says so rather than guessing.
get_review_status
{ "review_id": "49185038-…" }Returns { review_id, status, created_at, decided_at?, note?, counterparty, amount, asset, template?, reasons }.
Guard policy: haia-guard.config.json
{
"allowlist": [],
"denylist": [],
"perCounterpartyLimit": { "amount": "5.00", "windowHours": 24 },
"maxOpenPartialPerCounterparty": 0,
"reviewAbove": "1.00",
"defaultAsset": "eip155:8453/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"assets": {
"eip155:8453/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": {
"symbol": "USDC",
"decimals": 6
}
}
}Every key is optional; the values above are the defaults (USDC on Base is
known without configuration, as is USDC on Base Sepolia). Unknown keys are
rejected. Amounts are decimal strings converted once, at load, to atomic
units of defaultAsset; the config is never re-read while the server runs,
so nothing in a tool call or an event payload can change the policy.
Allowlist and denylist entries are 0x… addresses or service hosts.
The x402 payload carries the asset's contract address but not its decimals,
so any asset the guard should reason about must be in assets. An unknown
asset sends the payment to review rather than guessing a scale.
Human review
haia-trace-mcp review list [--dir .trace]
haia-trace-mcp review approve <review_id> [--note "…"]
haia-trace-mcp review reject <review_id> [--note "…"].trace/guard/decisions.ndjson is append-only: check_before_pay appends an
opened line, the CLI appends a decided line, and the state of a review is
the fold of its lines. Nothing is edited, so the file is also the audit trail.
A review can be decided once.
Boundaries
Advisory only.
check_before_paynever moves funds, signs, or calls a payment API. It returns a decision; acting on it is the agent's job, and nothing stops an agent that ignores it. Enforcement belongs in the wallet or the payment path.The agent cannot approve its own request. There is no approve tool on the MCP surface, by design.
Read-only over
.trace/events. The only file this server writes is.trace/guard/decisions.ndjson.No outbound network. The Control Plane sink is not used; nothing is fetched, not even a JWKS. The optional HTTP transport only listens.
Limits
Buyer-side blindness. Receipts are built from the buyer's own events. An unsettled payment and a silently settled one produce the same partial receipt. The guard treats both as "possibly paid, outcome unconfirmed" and
explainsays so; neither can know more than the buyer observed.x402 only, for money fields. Counterparty, amount and asset are read from the trace-x402 payload (
requirements.pay_to,requirements.amount,requirements.asset/network). Theescrow-arctemplate is listed and explained but its receipts carry no x402 money fields, socheck_before_paycannot attribute them to a counterparty.Stage roles are a table. Which stage means "paid" and which "settled" is known for the four shipped templates (
x402-buyer,x402-seller,x402-facilitator,escrow-arc). A custom template is judged coarsely: any partial counts as open, and thehistoryreason says so.Hard links into
.trace/eventscannot be detected. Symlinks are refused.Pre-1.0 upstream.
@usehaia/*contracts may change before 1.0; the version is pinned exactly and the parity test will fail if receipts drift.Local-first. No multi-user review queue. The HTTP transport below authenticates callers but does not authorize them differently: every valid token sees the same tools over the same Trace root.
HTTP transport (optional)
Stdio is the default. For a host that reaches the server over the network,
--http serves Streamable HTTP as an OAuth 2.1 resource server, in the
direction Haia Rooms describes: PKCE-issued, resource-bound bearer tokens,
verified locally. This project does not ship an authorization server; it
trusts one the operator already runs.
node dist/cli.js --dir .trace \
--http 127.0.0.1:3000 \
--auth-issuer https://auth.example \
--auth-jwks ./auth-example-jwks.json \
--auth-scope trace:readGET /.well-known/oauth-protected-resource/mcpserves RFC 9728 metadata naming the issuer, so a client knows where to get a token.POST /mcprequiresAuthorization: Bearer <jwt>. The token must be signed by a key in the JWKS (RS256 or ES256) or by the HS256 secret file (--auth-hmac-secret, for a local authorization server only), carryissequal to--auth-issuer,audequal to the resource URL (http://host:port/mcp, or--public-urlbehind TLS termination), anexp, and every--auth-scope. Failures answer401/403with aWWW-Authenticatechallenge pointing at the metadata.Verification is dependency-free (
node:crypto) and makes no network call; the JWKS is a file the operator provides.Binding is loopback-only unless
--http-allow-remoteis passed, and--http-insecure-no-auth(for the MCP Inspector) is refused off loopback. Host and Origin headers are validated against DNS rebinding.
Security
The Trace root is resolved to its real path once. Every run file, template and the review log must resolve inside it; symlinks are refused with
O_NOFOLLOWand a real-path check.--templates-dirgets the same rules.run_idandoperation_idare looked up in what exists, never joined onto a path. Template names must be bare slugs.Every event line is validated against the Event Contract envelope before the assembler sees it; every payload field is validated where it is read.
Payloads are already redacted by the adapters (allowlisted fields, never signatures or keys). This server adds no fields and logs no payloads.
stdout is the protocol channel under stdio; all diagnostics go to stderr, and Biome's
noConsolerule enforces it.
Development
pnpm install
pnpm build # tsc → dist/
pnpm test # vitest: ~190 tests, including live parity against haia-trace build
pnpm check-types # tsc --noEmit over src and tests
pnpm lint # biomeCI runs all four on Node 22. Commits follow Conventional Commits.
Pinned upstream: @usehaia/trace-core 0.0.10 (runtime),
@usehaia/trace-cli 0.0.10 (dev: parity oracle and template/fixture drift
check), @usehaia/trace-x402 0.0.10 and @x402/core 2.20.0 (dev: demo).
MCP: @modelcontextprotocol/server 2.4.0 and @modelcontextprotocol/node 2.1.2. The four bundled templates and the
four sample runs are verbatim copies from @usehaia/trace-cli 0.0.10
(templates/SOURCE.md, test/fixtures/SOURCE.md); a test fails if they
drift from the installed package.
License
MIT © Muhammad Jon Raza. Haia Trace is MIT © Haia. This project is not affiliated with Haia Finance.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
Related MCP Connectors
Supervised API-write gateway for AI agents with policy, human approval and execution receipts.
Authorize AI agent tool calls before they run: policy-as-code, human approval, signed receipts.
Advisory policy preflight for AI-agent spend requests; never executes payments or accesses wallets.
AI agent infrastructure for discovery, authorization, execution, identity, and signed receipts.
Related MCP Servers
AlicenseAqualityDmaintenanceEnables AI agents to submit, check, and manage human attestation requests for AI actions through Legitify's approval receipt system.6MIT- AlicenseNot gradedqualityCmaintenanceProvides tools to evaluate an AI agent's step before execution, returning signed ALLOW/DENY receipts, and to verify receipt chain integrity.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to verify proposed payments against their assigned task, budgets, permitted categories, and counterparties, returning an ALLOW or DENY decision with a tamper-evident audit trail.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to inspect, check, and audit signed offline spending authorizations, and to issue hash-chained receipts for each action taken against them. Enforces per-transaction, total, expiry, merchant, and human-approval constraints, returning allow, needs_human, or deny with the specific failing constraint.Apache 2.0