Skip to main content
Glama

SingIt Agent Pay

Agents that pay each other, under human-set limits. SingIt Agent Pay is an MCP server that lets AI agents pay each other in USDC through escrow. A human sets the mandate (budget, per-transaction cap, allowed payees, expiry). The buyer agent locks funds; the seller agent captures what the work actually cost; the rest goes back. Only a human can settle a dispute, and every action, including every refused one, lands in an audit log.

Built at The FinTechers Hackathon, Prague, 19–20 October 2026: Track 2 "Stablecoins and new financial infrastructure", challenge "Agents that pay each other". Escrow runs on capture402 on Solana devnet, with a mock backend for local demos.

Demo dashboard: mandate, escrow holds, a dispute waiting for a human, and the live event stream with blocked attempts

Status

What

State

Real LLM agent paying on devnet (MVP-07)

Accepted on 20 October 2026 as an experimental devnet run. A real Codex CLI buyer went through quote → hold → purchase → retrieval with a separate merchant over authenticated MCP. One authorize locked 1 test USDC; one capture took 0.65 and released 0.35 in the same instruction. Both original transactions finalized with err=null, and nothing was left pending or UNKNOWN. The buyer got a useful report whose digest matched. Source pair: this repo 951ca06, capture402 208de48. Signatures and the model trace are kept private (release gate, #14)

Demo on the mock backend (default)

Runs without live wallets or RPC, labeled MOCK. Same server and safety rules, synthetic ledger

Earlier devnet runs

Scripted runs on older revisions: 4/4 direct operations and 13/13 dashboard → MCP → devnet steps finalized (historical)

Another live run

Needs its own owner authorization; the accepted run's authorization was single-use (runbook)

Still open

Event delivery (#15) and a wider pilot (#16)

Solana devnet and devnet USDC only, never mainnet. Experimental: no production claim and no human security audit.

Related MCP server: agent-insurance-mcp-server

Why

An agent with a wallet can overspend, pay the wrong party, pay twice after a timeout, or approve its own refund. SingIt puts deterministic rules between the agent and the money:

  • Mandates: total budget, per-transaction cap, allowed counterparties, expiry and devnet-USDC mint pinning, checked before any transaction is built.

  • Escrow, not transfers: hold locks funds; the seller captures part or all; the remainder is released back into the budget.

  • Roles from the transport: each agent connects with its own bearer token. The buyer cannot capture, the seller cannot spend, and neither can issue itself a bigger mandate.

  • A human in the loop: mandates are issued and disputes resolved only through a human route, with an HMAC approval token bound to the exact decision. No MCP tool can produce one.

  • Audit everything: every call and every refusal, with a stable code.

How it works

sequenceDiagram
    participant H as Human
    participant B as Buyer agent
    participant S as SingIt MCP
    participant M as Seller agent
    participant E as capture402 escrow
    H->>S: issue mandate (POST /human/mandate)
    B->>S: hold(mandateId, payee, amount, idempotencyKey)
    S->>S: check mandate, reserve budget, journal the intent
    S->>E: authorize (lock USDC)
    M->>S: capture(holdId, amount, releaseRemainder)
    S->>E: capture, return the remainder
    B->>S: open_dispute(holdId, reason)
    H->>S: approve or reject (human token)

The policy runs in the service; the money rules run on-chain. The capture402 program fixes the hold maximum and deadlines, and the payer can reclaim an unused hold after the capture deadline without the seller. A dispute is a rule of this service, not an on-chain freeze. See docs/ARCHITECTURE.md.

Payment safety

The agreed narrow workflow and ordered acceptance gates are in MVP-SCENARIO.md. Journal compatibility and read-only diagnosis when startup refuses are documented in JOURNAL.md.

Payments fail in messy ways: lost responses, timeouts, crashes between "sent" and "saved". The server is built so that none of these turns into a second payment.

  • One intent, one operation. Every write carries an idempotencyKey, which is required on devnet. A retry with the same key returns the original result and operationId, even after a full capture or a passed deadline. Two identical requests at once still send only once.

  • UNKNOWN is never guessed. If the outcome of a send is unproven, the reservation is kept, the server stops accepting new payments, and only the original transaction can settle it. No new salt, no resend, no automatic compensation.

  • Durable journal first. The reservation and its intent are journaled before anything is signed; a failed disk write means nothing is sent. On devnet, startup restores the journal and reconciles read-only before serving.

  • Fail closed. UNKNOWN, a failed journal write or shutdown closes economic writes. GET /ready reports why; GET /health only reports that the process is alive.

  • One runtime per set of wallets. A second server with the same program, payer and merchant refuses to start, even with another state directory.

Details: docs/INTERFACE.md ("Live safety policy", "Operation journal").

Responsible AI and compliance

  • The model never holds money or sets limits. LLM agents only call tools. Keys stay in the server process; budgets, caps, payees and deadlines are enforced by deterministic code before any transaction is built, and the escrow program enforces the hold maximum and deadlines on-chain.

  • Human authority stays human. Only a human issues mandates and decides disputes, through a separate route and an HMAC token bound to the exact decision. A prompt is never an authorization boundary: each agent's role comes from its own token.

  • Explainable by construction. Every call and every refusal is audited with the actor, a stable code and a reason. Each payment can be traced by its operationId.

  • Data handling. Demo data is synthetic and money is devnet test USDC; no personal data is processed. Signed transactions, journals and model traces stay private; public state and get_operation redact them.

  • Honest scope. Experimental devnet software, not audited. Moving real funds would need regulatory review (for example MiCA for e-money tokens such as USDC in the EU) and an independent security audit.

Quickstart (mock, no keys)

Requires Node 20, 22 or 24 (.nvmrc pins 22) on macOS or Linux.

npm ci
npm run demo:all

Open http://127.0.0.1:5173 and press ▶ Run demo story. In 13 steps a research agent buys from two sellers. Three bad payments are blocked: over the per-transaction cap, to an unknown seller, and over budget. The agent's attempt to approve its own refund is refused, and the story waits for a human to decide the dispute. Step by step: docs/DEMO.md. npm run demo plays the same story in the terminal.

URL

What

http://127.0.0.1:8787/mcp

MCP, streamable HTTP

http://127.0.0.1:8787/health

liveness

http://127.0.0.1:8787/ready

economic readiness (open on mock; human route secret on devnet)

http://127.0.0.1:8787/api/state

read-only public state

http://127.0.0.1:5173

demo dashboard

On first run demo:all writes random local secrets to singit-mcp/.env.local (gitignored).

Connect an agent

Start the server (npm run demo:all, or npm run start:http for the server only) and point an MCP client at http://127.0.0.1:8787/mcp.

On the mock backend without tokens, /mcp is open for local experiments. With agent tokens configured, as on devnet, each agent connects with its own token and the server derives its role from it:

# Claude Code as the buyer agent
claude mcp add --transport http singit-buyer http://127.0.0.1:8787/mcp \
  --header "Authorization: Bearer $SINGIT_BUYER_TOKEN"
// Cursor: .cursor/mcp.json
{ "mcpServers": { "singit-buyer": { "url": "http://127.0.0.1:8787/mcp", "headers": { "Authorization": "Bearer <buyer token>" } } } }

For stdio, the process environment fixes the role: SINGIT_MCP_PRINCIPAL=buyer or merchant, required on devnet. Never put the human secrets, keypairs or RPC credentials in an agent's config.

Tools

Amounts are USDC base units as decimal strings (2500000 = 2.5 USDC); times are Unix seconds. hold is the pay tool.

Tool

Who

What it does

hold

buyer

Lock up to amount for an allowed payee under a mandate

capture

seller

Capture part or all of a hold; releaseRemainder returns the rest in the same call

release

seller

Return everything still held to the payer

refund

seller

Refund captured funds from the seller's own balance (does not restore budget)

open_dispute

buyer

Dispute a hold; the service then refuses captures until a human decides

resolve_dispute

human

Refund or reject, only with a human approval token

create_mandate

human

Issue a mandate (agents are refused; humans use POST /human/mandate)

get_mandate, list_mandates

own

A mandate with its remaining budget and holds

list_disputes

own

Disputes by status or mandate

get_operation

own

Redacted status of one payment by operationId, after a lost response or UNKNOWN

list_audit_log

own

Every action and refusal on the caller's mandates

"Own" means the caller sees only mandates where it is the payer or an allowed payee. Economic writes accept idempotencyKey and return operationId and replay. Refusals come back as {ok: false, code, reason, operationId?}. Schemas and refusal codes: docs/INTERFACE.md.

Devnet (operators)

npm run live:all -- /absolute/path/to/private/devnet.env

This needs throwaway devnet wallets with devnet USDC and SOL, an approved HTTPS RPC, the confirmed program values, a private state directory, both human secrets and both agent tokens. The template is singit-mcp/.env.example. Live mode never falls back to mock. Every live run needs offline readiness and the owner's separate, single-use authorization (release gate).

The merchant and the real-LLM buyer used in the accepted run are in starPaw/capture402-solana under examples/singit-merchant.

Tests

npm test            # singit-mcp suites, then the dashboard e2e
npm run typecheck

The suites cover the mandate rules, the MCP transport with real clients, trusted principals, idempotent replay, concurrent and lost requests, crash boundaries, storage failures and the backend contract. The devnet contract suite is skipped without devnet configuration. Current results and the fault matrix: docs/RELEASE-GATE.md.

Repository

Path

What

singit-mcp/

MCP server: PaymentService (mandates, journal, reconciliation), transports, principals, human approval, mock and Capture402 backends, tests

demo-dashboard/

Web dashboard that plays the story against the MCP server; human console for disputes

docs/

Architecture, interface, demo story, live demo, devnet backend, MVP scenario, journal, offline recovery, release gate, runbook

Contributing rules (secrets, live authorization, no retries after UNKNOWN): CONTRIBUTING.md.

Built during the hackathon

Everything in this repository was written during the event. The first commit is tagged hackathon-start.

  • MCP server over stdio and streamable HTTP, 12 tools

  • Mandate engine and escrow lifecycle over a pluggable PaymentBackend (mock and capture402 devnet)

  • Trusted principals per agent, human-only mandates and dispute decisions

  • Durable operation journal (v2, with offline inspection and reviewed legacy disposition), idempotent replay, UNKNOWN handling, fail-closed admission and readiness

  • Audit log, demo dashboard, backend conformance suite, offline recovery scenarios, release gate and runbook

  • The accepted real-agent devnet run, with the merchant and LLM buyer in the capture402 repository (examples/singit-merchant, also written during the event)

Pre-existing (reused, not judged)

  • SingIt: singitai.app, github.com/bubon-ik/SingItAI. The team's existing product and x402 API; not part of this repository.

  • capture402 escrow: starPaw/capture402-solana, the Solana auth/capture escrow program the devnet backend targets. First published 19 October 2026, 10:46 CEST, before the 12:00 kickoff. Not part of this repository.

AI assistance

Disclosed as the hackathon rules ask: the team built this with AI coding assistants. Claude Code (Anthropic) and OpenAI Codex wrote and reviewed much of the code, tests and docs under the team's direction. In the accepted devnet run the buyer agent itself was the Codex CLI. Merges, the live run and funding were authorized by the team.

License

Apache-2.0, same as capture402. Third-party notices: NOTICE.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers