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.