> ## Documentation Index
> Fetch the complete documentation index at: https://docs.idem.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect AI Agents to Idem via the MCP Server

> Connect Claude Desktop, Claude Code, or any MCP-compatible agent to Idem's ledger tools — post transactions, query balances, reconcile, and roll back workflows under policy-guarded, HMAC-signed audit.

Idem exposes its agentic execution layer over the [Model Context Protocol](https://modelcontextprotocol.io/) as well as REST. Any MCP-compatible agent — Claude Desktop, Claude Code, or a custom runtime — can post transactions, query balances, list entries, reconcile settlements, and roll back workflows without a custom API integration. Every tool call goes through the same stack as a REST request: scope-checked authentication, **PolicyGuard** evaluation before any write, and an HMAC-signed, append-only audit entry recorded before execution.

<Note>
  MCP tool calls are subject to the same [policy rules](/guides/policy-rules) and [scopes](/concepts/scopes) as REST requests. An agent key that can `postTransaction` is not automatically permitted to `rollbackWorkflow` — see [Required scopes](#required-scopes) below.
</Note>

## How a tool call flows through Idem

```
AI agent (Claude Desktop / Claude Code / custom)
    │  MCP JSON-RPC 2.0 over SSE
    ▼
Session auth bridge  →  injects your tenant's authentication into the request
    │
Scope check  →  hasAuthority('AGENTS_EXECUTE' | 'AGENTS_ROLLBACK' | 'AGENTS_AUDIT_READ')
    │
MCP tool method
    │
PolicyGuard.evaluate()  ←  policy rules loaded per tenant + agent key prefix
    │
Ledger use case (execute / query / reconcile / rollback)
    │
PostgreSQL (journal_lines, transactions, accounts)
```

## Connect over SSE

The MCP server exposes two endpoints on your Idem instance:

| Endpoint        | Method | Purpose                                                                                       |
| --------------- | ------ | --------------------------------------------------------------------------------------------- |
| `/sse`          | `GET`  | Opens the SSE stream and returns a session ID                                                 |
| `/mcp/messages` | `POST` | Sends JSON-RPC 2.0 tool-call messages; requires `?sessionId=<uuid>` from the `/sse` handshake |

Authenticate the same way you would for REST — an `X-API-Key` header or `Authorization: Bearer` — on the initial `GET /sse` request. You don't need to resend the key on each `POST /mcp/messages` call; Idem associates your session with your authenticated tenant for the lifetime of the SSE connection.

## Required scopes

Most tools require `AGENTS_EXECUTE`. Rolling back a workflow and reading the audit log each require their own separate scope — an agent authorized to execute transactions cannot roll them back or read the audit trail unless explicitly granted those scopes too.

| Tool                                    | Required scope      |
| --------------------------------------- | ------------------- |
| [`postTransaction`](#posttransaction)   | `AGENTS_EXECUTE`    |
| [`getBalance`](#getbalance)             | `AGENTS_EXECUTE`    |
| [`listEntries`](#listentries)           | `AGENTS_EXECUTE`    |
| [`describeAccount`](#describeaccount)   | `AGENTS_EXECUTE`    |
| [`reconcileBatch`](#reconcilebatch)     | `AGENTS_EXECUTE`    |
| [`rollbackWorkflow`](#rollbackworkflow) | `AGENTS_ROLLBACK`   |
| [`getAgentAuditLog`](#getagentauditlog) | `AGENTS_AUDIT_READ` |

See [Scopes](/concepts/scopes) for the full scope reference and how to issue a least-privilege agent key.

## Tools

### `postTransaction`

Posts a balanced double-entry transaction as an AI agent.

```text theme={null}
postTransaction(
    entries: List<JournalLine>,   // journal lines — see below
    idempotencyKey: string,       // duplicate calls with the same key return the cached result
    intentDescription?: string,   // human-readable intent, recorded in the audit log
    agentId: string,              // agent identifier from your credentials
    sessionId: string,            // session grouping related agent actions
) → { workflowPlanId: string, status: string }
```

Each entry in `entries`:

| Field               | Required       | Notes                                 |
| ------------------- | -------------- | ------------------------------------- |
| `accountId`         | Yes            | UUID of the account                   |
| `entryType`         | Yes            | `DEBIT` or `CREDIT`                   |
| `monetaryEntryType` | Yes            | `FIAT` or `ON_CHAIN`                  |
| `amount`            | Yes            | Decimal string, e.g. `"1000.00"`      |
| `currency`          | FIAT only      | ISO 4217: `BRL`, `USD`, `MXN`, `EUR`  |
| `rail`              | FIAT only      | `ACH`, `WIRE`, `PIX`, `SWIFT`, `SEPA` |
| `bankReference`     | Optional       | Bank-issued reference                 |
| `token`             | ON\_CHAIN only | `USDC`, `USDT`, `BRZ`, `PYUSD`        |
| `chainId`           | ON\_CHAIN only | `EVM`, `SOLANA`, `TRON`               |
| `txHash`            | ON\_CHAIN only | On-chain transaction hash             |
| `blockNumber`       | ON\_CHAIN only | Block number                          |
| `walletAddress`     | ON\_CHAIN only | Receiving wallet address              |
| `tokenContract`     | ON\_CHAIN only | Token contract address                |

<Tip>
  `PolicyGuard` evaluates the effective [policy rules](/guides/policy-rules) for your tenant and agent key prefix **before** the transaction commits. If no rules are configured for an agent, the default is deny-all — configure at least one permissive rule before an agent can post a debit.
</Tip>

### `getBalance`

Returns the current balance for an account, optionally as of a point in time.

```text theme={null}
getBalance(
    accountId: string,   // account UUID
    asOf?: string,        // ISO-8601 instant, e.g. "2025-12-31T23:59:59Z"
) → { accountId: string, currency: string, amount: string, computedAt: string, onChainBalances: OnChainBalanceItem[] }
```

`onChainBalances` is a per-token breakdown of on-chain entries posted to the account, net across all chains for that token. Each `OnChainBalanceItem` is `{ token, amount }`. It is never combined with the fiat `amount` above — a token amount and a fiat amount are not fungible units. An account with only fiat entries returns `onChainBalances: []`.

### `listEntries`

Lists journal entries for an account, newest first, with time-range filtering and cursor-based pagination.

```text theme={null}
listEntries(
    accountId: string,
    from?: string,     // ISO-8601 inclusive lower bound
    to?: string,       // ISO-8601 inclusive upper bound
    limit?: number,    // 1–200, default 50
    cursor?: string,   // opaque cursor from a previous page's nextCursor
) → { accountId: string, entries: EntryItem[], nextCursor?: string }
```

Each `EntryItem` includes `id`, `transactionId`, `entryType`, `amount`, `currency` (ISO 4217 for fiat, token symbol for on-chain), `description`, and `createdAt`.

### `describeAccount`

Returns account metadata and current balance in a single call.

```text theme={null}
describeAccount(
    accountId: string,
) → {
      accountId: string, name: string, description?: string, currency: string,
      entryCount: number, lastActivityAt?: string,
      balanceCurrency: string, balanceAmount: string
    }
```

### `reconcileBatch`

Runs a reconciliation sweep over on-chain settlements within a time window, matching unmatched chain entries against pending journal lines by amount.

```text theme={null}
reconcileBatch(
    accountId?: string,          // optional account UUID to scope the sweep
    from: string,                // ISO-8601 lower bound on settlement timestamp
    to: string,                  // ISO-8601 upper bound on settlement timestamp
    tolerancePercent?: number,   // optional per-call override of the server default
) → { matched: number, unmatched: number, exceptions: string[], settlementIds: string[] }
```

Matching is exact by default. `tolerancePercent` allows a bounded amount difference for that call. Entries with no matching candidate within tolerance are reported in `exceptions` and left unsettled.

### `rollbackWorkflow`

Rolls back a committed or executing workflow using compensating transactions (the saga pattern) — each executed step is reversed in reverse order.

```text theme={null}
rollbackWorkflow(
    workflowPlanId: string,   // WorkflowPlan UUID to roll back
    reason: string,           // human-readable reason, recorded in the audit log
    agentId: string,
    sessionId: string,
) → { rollbackId: string, compensatedSteps: CompensatedStepItem[], status: string }
```

Each `CompensatedStepItem` includes `stepOrder`, `description`, and `compensatingTransactionId` where applicable.

<Warning>
  `rollbackWorkflow` requires the separate `AGENTS_ROLLBACK` scope — it is never granted implicitly by `AGENTS_EXECUTE`. Compensating transactions bypass `PolicyGuard` by design, since a rollback is a corrective action rather than a new agent-initiated debit. Grant `AGENTS_ROLLBACK` only to admin-tier keys or agents whose role is specifically to reverse workflows.
</Warning>

### `getAgentAuditLog`

Retrieves HMAC-signed audit events for agent actions, filterable by session and time range.

```text theme={null}
getAgentAuditLog(
    sessionId?: string,
    from?: string,
    to?: string,
    limit?: number,   // 1–200, default 50
) → { auditEvents: AuditEventItem[], total: number }
```

Each `AuditEventItem` includes `id`, `workflowPlanId`, `agentId`, `sessionId`, `eventType`, `intentPayload`, `status`, `occurredAt`, `completedAt`, and `hmacSignature`. Every mutating tool call writes a `PENDING` audit event **before** execution and a `COMPLETED` or `FAILED` event after — the audit log is append-only, and each event's `hmacSignature` can be independently re-verified against your tenant's audit HMAC secret.

## Connect Claude Desktop

<Steps>
  <Step title="Expose your Idem instance">
    If you're self-hosting locally, tunnel your instance so Claude Desktop can reach it (for example with `ngrok http 8081`). Skip this step if you're on the managed cloud tier.
  </Step>

  <Step title="Create an agent-scoped API key">
    Using an `ADMIN`-scoped key, create a key carrying `AGENTS_EXECUTE` (and any other scopes the agent needs):

    ```bash theme={null}
    curl -X POST https://api.your-domain.com/api/v1/api-keys \
      -H "Authorization: Bearer $IDEM_ADMIN_KEY" \
      -H "Content-Type: application/json" \
      -d '{"scopes":["AGENTS_EXECUTE","ACCOUNTS_READ"]}'
    ```

    Copy the `rawKey` value from the response — it's shown exactly once. See [Scopes](/concepts/scopes) for the full reference.
  </Step>

  <Step title="Configure Claude Desktop">
    Add an entry to `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`) using [`mcp-remote`](https://www.npmjs.com/package/mcp-remote):

    ```json theme={null}
    {
      "mcpServers": {
        "idem": {
          "command": "npx",
          "args": ["-y", "mcp-remote", "https://your-idem-instance/sse"],
          "env": {
            "MCP_HEADER_X_API_KEY": "sk_agent_..."
          }
        }
      }
    }
    ```
  </Step>

  <Step title="Restart Claude Desktop">
    The seven Idem tools appear in Claude's tool list, scoped to whatever the key you configured is authorized to do.
  </Step>
</Steps>

## Connect Claude Code

```bash theme={null}
claude mcp add idem -- npx -y mcp-remote https://your-idem-instance/sse --header "X-API-Key: sk_agent_..."
```

## Self-hosting: multi-replica deployments

<Warning>
  If you self-host Idem across multiple replicas, configure sticky sessions at your load balancer so that a given MCP session's `GET /sse` and subsequent `POST /mcp/messages` calls reach the same instance. Session state is held in-process per replica, not shared across a cluster.
</Warning>
