Polivor

MCP server

If your agent talks to tools through the Model Context Protocol (MCP), it can ask Polivor for authority without any REST code. The MCP server offers the same two operations as the REST API, on the same keys, roles and limits:

ToolSame asWhat it does
authorizePOST /v1/authorizeAsk for authority for one payment. Returns ALLOW with an authorization, DENY with reasons, or REQUIRE_APPROVAL.
get_decisionGET /v1/decisions/{id}Read how an earlier decision stands, for example after an approval.

Admin and simulation operations are not offered as tools: an agent should not be able to change its own mandate or policy.

Connect

  • URL: https://api.staging.polivor.com/mcp in the sandbox.
  • Authentication: an agent API key, sent as Authorization: Bearer <key> on every request. Any other role is refused with 403.
  • Transport: MCP over HTTP (the Streamable HTTP transport), answered as plain JSON. There are no sessions and no event stream, so GET /mcp returns 405. Protocol versions 2025-06-18, 2025-03-26 and 2024-11-05 are supported.
  • Browsers are refused. A request with an Origin header returns 403: MCP clients are programs, and this keeps a web page from driving an agent's key.

MCP clients differ in how they configure a remote server and custom headers. The two things every client needs are the URL and the Authorization header; some clients need a small bridge program to send that header.

{
  "mcpServers": {
    "polivor": {
      "url": "https://api.staging.polivor.com/mcp",
      "headers": { "Authorization": "Bearer pv_test_..." }
    }
  }
}

What a session looks like

The client sends JSON-RPC 2.0 messages with POST /mcp. First it introduces itself:

{ "jsonrpc": "2.0", "id": 1, "method": "initialize",
  "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "my-agent", "version": "1.0" } } }

Then it can list the tools (tools/list) and call them. Calling authorize takes the fields of the authorize request and one more, a required idempotency_key:

{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
  "params": { "name": "authorize",
    "arguments": {
      "idempotency_key": "6f1d2c1e-5d0a-4a63-9a0c-8d9f5a0b7c11",
      "agent_version": "3.7",
      "mandate_id": "2d1b3b44-5e0c-4a3c-9a7e-6f1f7a9c0a11",
      "action": {
        "type": "PayInvoice", "amount_minor": 420000, "currency": "EUR",
        "debtor_account": "ES9121000418450200051332", "payment_type": "SEPA_CT",
        "beneficiary": { "id": "lopez-sl", "iban": "ES7620770024003102575766" },
        "reference": "INV-104"
      } } } }

The answer carries the decision twice, as the JSON text in content and as structuredContent; it is exactly the body of the REST response, including outcome and, on ALLOW, the authorization to hand to the payment system.

Errors

  • A problem with the call itself (unknown tool, a missing idempotency_key, a malformed decision_id) is a JSON-RPC error with code -32602. Unknown methods return -32601, a body that is not JSON returns -32700 with HTTP 400.
  • A business error (a validation failure, an idempotency conflict, a decision that does not exist) is a tool result with isError: true whose content is the usual problem details, so the model can read it and react.
  • Missing or wrong keys return HTTP 401; over the rate limit, 429 with Retry-After.

Rules for the agent

The tool descriptions tell the model, and your agent's instructions should too: ask before every payment, pay only on ALLOW, hand the authorization to the payment system and never pay on a DENY or while waiting for approval, reuse the idempotency_key only to retry the same payment, and do not try to change facts such as the beneficiary's IBAN to get a different answer: Polivor computes them itself. See Enforcement points for the system that releases the money.