Documentation
MCP

Confirmed actions

How FloPay MCP runs mutating tools, from a preview that changes nothing to explicit confirmation, safe retries, and what to do when an outcome is unclear.

Confirmed actions

A mutating tool never changes anything on its first call. FloPay checks the request, returns a preview of exactly what would happen, and waits. The action runs only when your client sends back the operator's explicit acceptance, and FloPay checks everything again before it writes.

Each tool's class, risk, and retry policy are in the Tool reference. Reviewed high- and critical-risk actions use the same preview and explicit confirmation flow, with stronger operation-specific warnings and the restricted rate-limit tier. Unavailable operations lists reviewed exclusions and the surfaces outside the merchant MCP boundary.

Which clients see mutating tools

A mutating tool appears in tools/list and search results, and can be called, only when all of these hold for the request:

  • The access token carries the mcp:write scope.
  • The operator's current merchant role appears in the tool's Roles policy. Some actions allow member; others require admin or owner.
  • The request declares form elicitation in io.modelcontextprotocol/clientCapabilities: "elicitation": { "form": {} }, or an elicitation object that lists no mode. A client that declares only URL elicitation stays read-only.

Otherwise the tool is hidden, and calling it returns JSON-RPC error -32602 with Unknown or unavailable tool. Declare the same capabilities on the preview call and the confirmation call.

Preview

Call the tool with its arguments, exactly as you call a read-only tool, using the headers from Connect any MCP client with Mcp-Name: ProductBrand_create:

{
  "jsonrpc": "2.0",
  "id": 10,
  "method": "tools/call",
  "params": {
    "name": "ProductBrand_create",
    "arguments": { "code": "summer-range", "name": "Summer range" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": { "elicitation": { "form": {} } }
    }
  }
}

FloPay runs the same validation and permission checks the action itself would run, then stops before writing, so the preview does not change anything. FloPay MCP answers with an input_required result that holds a confirmation form:

{
  "jsonrpc": "2.0",
  "id": 10,
  "result": {
    "inputRequests": {
      "flopay/confirmation": {
        "method": "elicitation/create",
        "params": {
          "mode": "form",
          "message": "Review the authoritative FloPay preview and explicitly accept to execute: {\"operationId\":\"ProductBrand_create\",\"inputHash\":\"<input hash>\",\"actionPrincipal\":{\"actorId\":\"3c6f1e2a-8b4d-4f7e-9a1c-5d2e8b7f4a3c\",\"authMethod\":\"oauth-bearer\",\"clientApplicationId\":\"7d0c6c2e-4b1f-4f7a-9c1e-2f8b7a1d0e5c\",\"merchantId\":\"0f8e4c1a-6d2b-4e7f-8a9c-3b5d7e1f2a4c\",\"role\":\"admin\",\"scopes\":[\"mcp:read\",\"mcp:write\"]},\"authorization\":{\"role\":\"admin\",\"scopes\":[\"mcp:read\",\"mcp:write\"],\"permissions\":{\"roles\":[\"owner\",\"admin\"],\"scopes\":[\"mcp:write\"]},\"policyHash\":\"<policy hash>\"},\"targets\":[{\"type\":\"product-brand-code\",\"id\":\"summer-range\",\"version\":\"absent\"}],\"effects\":[{\"type\":\"create-product-brand\",\"targetId\":\"summer-range\",\"details\":{\"before\":null,\"after\":{\"code\":\"summer-range\",\"name\":\"Summer range\",\"description\":null}}}],\"warnings\":[\"Product brand id and timestamps are assigned only when the confirmed action executes.\"],\"normalizedProposedValues\":{\"code\":\"summer-range\",\"name\":\"Summer range\",\"description\":null},\"validationOutcome\":{\"valid\":true,\"checks\":[\"dto\",\"product-brand-code-available\"]},\"expectedResult\":{\"responseType\":\"ProductBrandDto\",\"projected\":{\"code\":\"summer-range\",\"name\":\"Summer range\",\"description\":null},\"serverAssignedFields\":[\"id\",\"createdAt\",\"updatedAt\"]},\"expiresAt\":\"2026-09-14T15:05:00.000Z\"}",
          "requestedSchema": {
            "type": "object",
            "properties": {
              "idempotencyKey": {
                "type": "string",
                "title": "Idempotency key",
                "description": "A client-generated retry identity for this action."
              }
            },
            "required": ["idempotencyKey"]
          }
        }
      }
    },
    "requestState": "<confirmation state>",
    "_meta": {
      "flopay/contractRevision": "<contract revision>",
      "flopay/httpStatus": 201,
      "flopay/confirmationPhase": "preview",
      "io.modelcontextprotocol/serverInfo": { "name": "flopay-mcp", "version": "<server version>" }
    },
    "resultType": "input_required"
  }
}

The form message carries the preview from the FloPay API. Show the operator what it says before asking for a decision:

Preview fieldWhat it tells the operator
effectsEach change, with the values before and after it.
targetsThe records the action touches, with the version the preview saw.
warningsAnything the operator should know first.
validationOutcomeWhether the input passed validation, and which checks ran.
expectedResultThe kind of record the action returns, and which fields FloPay assigns when it runs.
expiresAtWhen the confirmation stops working.
actionPrincipal, authorizationWho the action runs as, and the role and scopes that allow it.

requestState is an opaque confirmation token. Keep it as it is and never show it to the model or the operator. If FloPay rejects the preview, because the arguments are invalid or the operator can no longer run the action, FloPay MCP answers JSON-RPC error -32603 with Backend action preview is invalid. Check the arguments against the tool's entry in the Tool reference before trying again.

Confirm or decline

To run the action, call the same tool again with the same name and arguments, echo requestState, and send the operator's decision in inputResponses:

{
  "jsonrpc": "2.0",
  "id": 11,
  "method": "tools/call",
  "params": {
    "name": "ProductBrand_create",
    "arguments": { "code": "summer-range", "name": "Summer range" },
    "requestState": "<confirmation state>",
    "inputResponses": {
      "flopay/confirmation": {
        "action": "accept",
        "content": { "idempotencyKey": "5b7e2c90-3d4f-4a8b-9c1d-6e2f7a8b9c0d" }
      }
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": { "elicitation": { "form": {} } }
    }
  }
}

Every confirmed action requires an idempotency key that your client generates once for this intended action, such as a UUID. The result is the FloPay API response for the action, in the shape the tool's entry in the reference documents.

If the operator declines or dismisses the form, send decline or cancel as the action instead. FloPay MCP answers FloPay action was not executed. and does not contact the FloPay API.

A confirmation:

  • expires five minutes after the preview,
  • works for one execution only,
  • is bound to the operator, the merchant, the client application, the tool, the exact arguments, the operator's role and scopes, and the version of every record the preview targeted.

If anything the confirmation is bound to changes after the preview, for example someone else edits the same record, FloPay refuses to run the action. Call the tool again for a fresh preview.

An action that reaches the FloPay API but does not complete returns a tool result with isError: true, the status in flopay/httpStatus, and the API response in structuredContent:

StatusMessageWhat to do
409MCP action confirmation is expired, changed, forged, or already used.Call the tool again for a new preview, and ask the operator to confirm it.
409Idempotency-Key was already used for a different MCP action binding.The key belongs to another action. Generate a new key for this one.
409The MCP action outcome is not yet authoritative.An earlier attempt with this key has not finished or did not complete. Read the target record before doing anything else.
503The MCP action outcome is ambiguous.FloPay cannot tell whether the change was written. Read the target record to find out.
503The MCP action completed without a durable authoritative result record.The action ran, but FloPay could not keep its result for retries. Read the target record instead of retrying.
403The live action principal does not satisfy the reviewed MCP action policy.The operator's role or the token's scopes changed since the preview. Reconnect with the access the action needs.

The responses about unclear outcomes also carry a reconciliation field with the same instruction: do not retry automatically, and check the record first.

Retry an action

Start with the tool's Retry policy in the Tool reference. Do not retry automatically after a timeout, lost connection, or ambiguous result. First read the canonical target record and use the response's reconciliation guidance.

  • same-idempotency-key. An exact replay uses the same confirmation, arguments, and idempotency key. If the first attempt completed, FloPay returns its original result instead of running the action twice. FloPay keeps that result for at least 24 hours. If FloPay says the outcome is not authoritative, reconcile instead of replaying again.
  • manual-reconciliation. Never resubmit automatically after execution may have started. Read the target through its canonical read tool, then let the operator decide whether to request a new preview.
  • A confirmed failure before any write. When FloPay can prove no write began, it returns that failure normally. Correct the input or permissions, then start again with a new preview and a new key.

An idempotency key can be up to 200 characters. Never reuse a key for a different action: FloPay refuses it with 409.

The following tools declare manual-reconciliation because their outcome cannot be safely repeated once FloPay starts writing. A failure after writing starts answers 503 with The MCP action outcome is ambiguous., and you must read the record before deciding anything:

Tools with the same-idempotency-key policy can be retried with the same idempotency key as described above. Do not retry automatically when FloPay says a result is not yet authoritative.

On this page