A Model Context Protocol server that lets an AI agent create human‑in‑the‑loop tasks, fund them in TRX escrow, and later read the proof and accept or reject it — all over JSON‑RPC 2.0.
Tronopay connects autonomous AI clients with human operators. The agent publishes a task, locks a TRX budget in escrow, a human performs the task and uploads proof, then the agent inspects the proof and issues the final accept/reject decision that triggers payout or refund.
The MCP server exposes this flow as tools (actions), resources (read‑only data views) and prompts (curated instructions), so a capable LLM client can drive the whole lifecycle without hard-coding REST calls.
| Endpoint | POST /mcp (teardown: DELETE /mcp) |
|---|---|
| Transport | Streamable HTTP — one JSON‑RPC request per POST, JSON response |
| Content type | application/json |
| Protocol | JSON‑RPC 2.0, MCP version 2025-06-18 |
| Auth | None (open, like the REST API). Task actions are protected per-call by the task password. |
A single POST may also carry a JSON array to run a batch of requests; responses come back as an array in the same order. A batch of only notifications returns 202 Accepted with no body. The server is stateless, so DELETE /mcp teardown returns 200 OK (empty) and no session id is required; ping returns {} for a heartbeat. All timestamps are RFC 3339 UTC.
# client → server
POST /mcp
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
"protocolVersion":"2025-06-18",
"capabilities":{},
"clientInfo":{"name":"my-agent","version":"1.0"}
}}
// server → client
{"jsonrpc":"2.0","id":1,"result":{
"protocolVersion":"2025-06-18",
"capabilities":{
"tools":{"listChanged":false},
"resources":{"subscribe":false,"listChanged":false},
"prompts":{"listChanged":false}
},
"serverInfo":{"name":"tronopay-mcp","version":"1.0.0"},
"instructions":"Tronopay task-creation API over MCP. Tools create, cancel, accept and reject tasks; resources (task://…) expose task, invoice, result and file data."
}}
resources.subscribe is false, so the server never pushes updates — poll task://{id} to observe status changes.
Any Streamable HTTP MCP client can use the same-origin URL. Example configuration:
{
"mcpServers": {
"tronopay": {
"type": "http",
"url": "https://your-tronopay-host/mcp"
}
}
}
initialize, then (optional) the notification notifications/initialized. Notifications return HTTP 202 Accepted with an empty body.create_task with a statement, a password (8–64 chars) and a price in TRX.id and remember the password — every later action needs them.paymentAddress before paymentDeadlineAt.task://{id} until the status becomes processed.accept_task or reject_task with the task password.Discover them with tools/list. Every tool’s payload is wrapped as
content[0].text holding a JSON string; isError:true marks a failure.
// success envelope (tools/call)
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{…}"}],"isError":false}}
// failure envelope — the error object is JSON inside content[0].text
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text",
"text":"{\"error\":{\"code\":-32001,\"message\":\"The task password is missing or invalid.\"}}"}],"isError":true}}
create_taskCreate a task and open its escrow invoice. Required: statement, password. Optional: type, price, location, refund_address, webhook.
// request
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_task","arguments":{
"statement":"Photograph the storefront sign on Main St and confirm it is open.",
"password":"secret123",
"type":"photo",
"price":"30",
"location":"51.500675, -0.143105"
}}}
// result (content[0].text)
{"id":"01a13f81-…","status":"created","price":"30.000000",
"paymentAddress":"TWVQ3dz1U8YEDNaXe2V5Jet8mVuZszwkBF",
"hint":"Please deposit 30.000000 TRX to the address … within 1 hour; otherwise, the task will be cancelled."}
type ∈ mark verify classify compare photo video audio location delivery other (default other). price is TRX, minimum 10 (default 10). location is "latitude, longitude" or a GeoJSON Point.
list_tasksList tasks newest-first. No password required — only public fields are shown. Optional: page (default 1), per (1–100, default 30).
// result
{"tasks":[{"id":"01a13f81-…","statement":"…","status":"created","type":"photo",
"priceSun":30000000,"priceTrx":"30.000000","location":null,"refundAddress":null,
"webhook":null,"createdAt":"2026-10-15T12:19:53+00:00",
"updatedAt":"2026-10-15T12:19:54+00:00","paymentDeadlineAt":"2026-10-15T13:19:53+00:00"}],
"total":16,"page":1,"per":30,"totalPages":1}
cancel_task / accept_task / reject_taskFinalize a task. All take task_id and password and return the updated task JSON.
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"accept_task","arguments":{
"task_id":"01a13f81-…","password":"secret123"
}}}
// result: {"id":"…","status":"accepted","statement":"…","type":"…","priceTrx":"30.000000", …}
cancel_task — cancel a task that is still created (before deposit).accept_task — approve the proof of a processed task → escrow pays the worker.reject_task — reject the proof of a processed task → escrow refund. The refund_address must have been set when the task was created, otherwise the refund cannot be issued automatically.Discover them with resources/templates/list (tasks are runtime
entities, so the static resources/list is empty). Read one with resources/read
and a uri. The task password is passed as a query parameter.
| URI template | Auth | Returns |
|---|---|---|
task://{id} | none | Public task metadata (statement, status, type, price, timestamps). |
task://{id}/invoice?password={password} | password | Payment invoice: amount, address, status, hint. |
task://{id}/result?password={password} | password | Worker result: text content and a list of attached files. |
task://{id}/result/files/{fileId}?password={password} | password | One file as an inline base64 blob (its mimeType is preserved). |
// read a result (files are always inline base64, never a download URL)
{"jsonrpc":"2.0","id":3,"method":"resources/read","params":{
"uri":"task://01a13f81-…/result?password=secret123"
}}
// read one proof file as base64 — decode contents[0].blob to inspect it
{"jsonrpc":"2.0","id":4,"method":"resources/read","params":{
"uri":"task://01a13f81-…/result/files/{fileId}?password=secret123"
}}
Discover them with prompts/list, expand one with prompts/get.
create_task — turns a natural-language description and budget into a correct create_task call. Args: description, budget_trx, optional type, location.check_task — inspects a task via its resources and recommends the next step. Args: task_id, optional password.A task’s status advances through this machine:
created — you published it; escrow invoice is awaiting deposit.prepaid — TRX deposited; waiting for a worker.assigned — a human worker picked it up.processed — worker submitted proof; now you decide accept or reject.accepted/rejected — decision recorded; payout/refund is queued.finished — settlement completed.canceled — closed before payment.Protocol errors come back as JSON‑RPC error objects; domain errors come
back inside the tool result with isError:true (same codes).
| Code | Meaning |
|---|---|
-32700 | Parse error — malformed JSON body. |
-32600 | Invalid request. |
-32601 | Method not found (unknown JSON‑RPC method). |
-32602 | Invalid params (unknown tool/prompt, missing/invalid argument). |
-32603 | Internal error. |
-32000 | Task not found. |
-32001 | Task password missing or invalid. |
-32002 | Invalid status transition (e.g. accept a task that isn’t processed). |
-32003 | Validation failed (e.g. price below 10 TRX, unknown type). |
On -32003 the error carries data.violations — a list of human-readable messages (e.g. type: invalid value "…"). On isError:true tool results the same code/message/data live inside content[0].text under error.
Create → inspect invoice → (human works) → read result → accept.
# 1. initialize
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"1"}}}
# 2. create
→ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"create_task","arguments":{"statement":"Verify shop hours","password":"secret123","price":"25"}}}
# 3. read public status while waiting
→ {"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"task://01a13f81-…"}}
# 4. once status == "processed", read the proof
→ {"jsonrpc":"2.0","id":4,"method":"resources/read","params":{"uri":"task://01a13f81-…/result?password=secret123"}}
# 4.1 inspect each attached file — decode the base64 blob in contents[0].blob
→ {"jsonrpc":"2.0","id":5,"method":"resources/read","params":{"uri":"task://01a13f81-…/result/files/{fileId}?password=secret123"}}
# 5. approve
→ {"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"accept_task","arguments":{"task_id":"01a13f81-…","password":"secret123"}}}