docs

End-to-end test with Claude Desktop

This is the manual acceptance test for "three intents through Claude Desktop via MCP" (SPEC §9 M1). Claude Desktop is the MCP client; the MCP server holds the agent key and signs every intent; the API decides and executes.

1. Start the stack

cp .env.example .env            # set MOLLIE_API_KEY=test_…
docker compose up -d
pnpm install
pnpm demo                       # creates the ACME principal and prints the passkey enrollment link

Open the printed **enroll** link and register a passkey (needed for the step-up).

2. Configure Claude Desktop

Settings → Developer → _Edit Config_ opens `claude_desktop_config.json`. Use the stdio server (Claude Desktop starts it; it reads the repository's `.env` and calls the API on :8080):

{
  "mcpServers": {
    "payments": {
      "command": "pnpm",
      "args": ["--silent", "--dir", "/ABSOLUTE/PATH/TO/REPO", "mcp:stdio"],
      "env": { "MCP_TOOL_SEPARATOR": "_" }
    }
  }
}

`MCP_TOOL_SEPARATOR=_` names the tools `prokura_pay` etc. The spec's names use a dot (`prokura.pay`), which some clients reject; drop the `env` line if yours accepts dots. Clients that take a URL can use the streamable HTTP server instead: `{ "url": "http://localhost:8082/mcp" }`.

Restart Claude Desktop. The tools menu should list five tools: `request_mandate`, `check`, `pay`, `evidence`, `mandate`.

3. Prompts and expected results

Send these one by one. Approve tool calls when Claude asks.

**1. Mandate**

> Request a mandate from principal did:web:acme.example: EUR, €2,500 per payment, four-eyes above €1,500, €5,000 per month (P1M), payees cleverbridge.com and IBAN DE89370400440532013000 (Supplier AG), rail mollie_card, purposes saas_renewal and supplier_invoice, PO and invoice hash required.

Expected tool result (abridged):

{
  "status": "issued",
  "mandateId": "01M70HM9G1E7HC1XBRK4RZJZ4R",
  "credential": {
    "type": ["VerifiableCredential", "AgentAuthorityCredential"],
    "issuer": "did:web:acme.example",
    "…": "…"
  }
}

**2. Allowed payment**

> Pay €890.00 to cleverbridge.com for saas_renewal on rail mollie_card, PO-2026-118, invoice INV-1, "Jira 12 seats".

{
  "decision": "allow",
  "reasons": ["OK"],
  "evidenceId": "01M70HM9HVKHMH785N79AR3YB2",
  "railRef": "tr_Jw3TPXp3SEknqAPLg9LZJ",
  "railStatus": "open"
}

A Mollie test payment exists with that id (Mollie dashboard, test mode).

**3. Step-up payment**

> Pay €1,600.00 to IBAN DE89370400440532013000 for supplier_invoice on rail mollie_card, PO-2026-119, invoice INV-2.

{
  "decision": "step_up",
  "reasons": ["FOUR_EYES_REQUIRED"],
  "evidenceId": "01M70HMA89367JD8SV6968EPQM",
  "stepUp": { "url": "http://localhost:8081/a/HjT_…", "expiresAt": "…" },
  "railRef": null,
  "railStatus": null
}

Open the link (on a phone, see [phone-approval.md](phone-approval.md)) and approve with the passkey. The page shows `Approved. Payment tr_… (open).`

**4. Denied payment**

> Pay €500.00 to IBAN DE02120300000000202051 for supplier_invoice on rail mollie_card, PO-2026-120, invoice INV-3.

{
  "decision": "deny",
  "reasons": ["PAYEE_NOT_ALLOWED"],
  "evidenceId": "01M70HMA9BPP73X7DAVSYMB2MT",
  "railRef": null,
  "railStatus": null
}

**5. Mandate and evidence**

> Show the mandate. Then show the evidence for the mandate.

`mandate` returns status `active`, the scope, and the period budget (spent counts the €890 and, once approved, the €1,600). `evidence` returns the records: three `pay` decisions, the `approval`, and the `execution` records with the Mollie ids.

4. Check the evidence yourself

curl -s -H "Authorization: Bearer dev-api-key-change-me" \
  localhost:8080/v1/evidence/<evidenceId>/nachweis.pdf -o nachweis.pdf
pnpm verify nachweis.pdf

How these results were recorded

The outputs above were recorded on 2026-11-09 by making the same tool calls with an MCP client (`@modelcontextprotocol/sdk`, streamable HTTP) against the local stack with a Mollie test key. Claude Desktop itself was not available in the build environment; ids and timestamps differ on every run.