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

# MCP server

> Payman in claude.ai, ChatGPT, Grok, Claude Desktop, Cursor — a URL for hosted chats, a command for local ones.

The same money action, in an AI host instead of your app. Ask for a balance or a payment; it runs against your own Payman account.

There are two ways in. Your host picks which:

| Your host                                                      | Use                                         |
| -------------------------------------------------------------- | ------------------------------------------- |
| A hosted chat — claude.ai, ChatGPT, Grok                       | **A URL.** Add it as a custom connector.    |
| Launches local processes — Claude Desktop, Claude Code, Cursor | **A command.** `npx @paymanai/connect-mcp`. |

Either way there is **no credential to paste into the chat**. You sign in on Payman's own pages, your bank credential goes on Payman's own pages, and nothing that moves money happens without your approval there.

## Is this the page you want?

| You are                               | Use                                                                       |
| ------------------------------------- | ------------------------------------------------------------------------- |
| Building a product for your customers | [The SDK](/quickstart). One app key, `forUser(id)` per customer, your UI. |
| Using Payman yourself from an AI host | This page. One host, one account, no app to build.                        |

## The URL

```
https://api.paygent.payman.ai/mcp
```

Give your host that, as a custom connector — in Grok, **grok.com/connectors → New Connector → Custom**; in claude.ai, **Settings → Connectors → Add custom connector**; in ChatGPT, **Settings → Connectors**. The host does the rest: it registers itself with Payman, opens the consent page, and you sign in, pick which account to connect, and place your bank credential there. Then ask.

> what's my balance?

Anything that moves money pauses. The exact operation is frozen, a one-time code is emailed to you, and the assistant hands you a link to Payman's approval page — verbatim, to open yourself. Approve there. Then say so, or ask `payman_approval_status`; the platform finishes the frozen operation, never a reconstructed one.

A hosted connection serves three tools: the money task, the approval check, and the connection status. Connecting and disconnecting live in your host's own connector settings — reconnecting **is** re-consenting, and revoking the connection from your Payman account works from either side.

## The command

**Claude Desktop** — Settings → Developer → Edit Config, then quit and reopen it (config is not reloaded live):

```jsonc theme={null}
{
  "mcpServers": {
    "payman": {
      "command": "npx",
      "args": ["-y", "@paymanai/connect-mcp"]
    }
  }
}
```

That is the whole configuration — no key, no environment file. It starts in **sandbox**, which reaches the Durango demo bank where nothing is money; add `"--live"` to `args` for real institutions. Node 22.5 or newer.

**Claude Code** — the same block in `.mcp.json` at your project root. Approve it when the session asks.

<Note>
  If the host cannot find the server, give `command` an absolute path to `npx` (`which npx`). A GUI-launched host does not inherit your shell `PATH`, and a version manager's shim is the first thing missing when it does not.
</Note>

Ask for something. Payman's consent page opens, you sign in and choose which account to connect, and the answer comes back in the same turn — the question is replayed once the connection lands, so you do not ask twice. Approvals work exactly as above; the local server also waits about ninety seconds and returns the receipt if your approval lands inside that window.

## Why it needs no key

An app key is a secret, and a secret shipped to consumers is not one: it would be a single shared credential for every install, readable by anyone who looks.

So both ways in authenticate as **public clients** — OAuth 2.1 with PKCE. A fresh random `code_verifier` is minted per authorization and never leaves the client; whoever intercepts the authorization code cannot redeem it without the verifier. Same mechanism as a mobile banking app, for the same reason. The local server ships a published `client_id`; a hosted chat registers its own on first contact (RFC 7591, `/oauth/register`) — that registration is a name and a callback URL, never a capability, and every connection it will ever hold still passes the consent page you decide on.

<Note>
  Building a product for **your** customers? Keep your app key and use [the SDK](/quickstart) — a server can hold a secret, and a secret is a stronger credential than a public client. Public clients exist for software in other people's hands.
</Note>

## The tools

| Tool                       | What it does                                                                                                                    | Hosted          | Local |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----- |
| `payman_money_task`        | The action. Name, description and input schema are [served by the platform](/reference/http-api) and passed through unmodified. | ✓               | ✓     |
| `payman_approval_status`   | Did that payment go through yet?                                                                                                | ✓               | ✓     |
| `payman_connection_status` | Connected? To what? Nothing about your money.                                                                                   | ✓               | ✓     |
| `payman_connect`           | Connect or reconnect, on request.                                                                                               | — host settings | ✓     |
| `payman_disconnect`        | Forget the stored connection here.                                                                                              | — host settings | ✓     |

A few tools around one action, not forty. What your account can do differs by provider, and a hand-written `list_accounts` / `pay_invoice` surface would be a second, worse copy of it. Ask in plain language and let your own Payman Wallet say what it can do.

## Whose name the consent page shows

The host's — "Claude wants to act on your behalf", not a generic Payman label. The two ways in earn the name differently: a hosted chat registers under the name it gives; a local install maps the name the host reports at `initialize` onto a per-host registered client.

Four local hosts are recognised, matched on a normalised substring so versions, platforms and rebrands still match:

| The host's name contains       | Registered as |
| ------------------------------ | ------------- |
| `claude`                       | Claude        |
| `chatgpt`, `openai`            | ChatGPT       |
| `cursor`                       | Cursor        |
| `vscode`, `visual studio code` | VS Code       |

Anything else, including a call that arrives before `initialize`, gets the generic client, whose registered name is **"An AI assistant"**.

That has a second effect worth knowing: Claude and ChatGPT are different clients, so connecting one no longer disconnects the other. Two installs of the **same** host still replace each other — one live connection per host, per account, per provider.

<Warning>
  The name is a label, not a proof — a self-registered or published client identifier is exactly that, and the consent page says so. What you can rely on is where you are standing: consent and approval happen on Payman's own origin, behind your own sign-in, and the code goes to your own inbox. Approve a connection only when you just asked for it yourself.
</Warning>

## Where links go

A tool result is model context, and a model that has seen one consent link will reconstruct a plausible-looking one when it next needs one. So links stay out of results wherever the host offers anywhere else to put them:

| Rung                | When                                      | What you see                                                                   |
| ------------------- | ----------------------------------------- | ------------------------------------------------------------------------------ |
| URL elicitation     | The host supports it                      | The host shows the link itself                                                 |
| Local browser       | Local server, no elicitation              | The page simply opens                                                          |
| The reply, labelled | Hosted connector, and last resort locally | The exact URL, marked to be handed over character for character, never retyped |

On the last rung, open the link yourself and never accept a retyped one. Whatever the rung, the wait can run out before you finish — the connection or approval still lands; say so and ask again.

## Local configuration

Nothing is required. These apply to `@paymanai/connect-mcp` only — a hosted connector has no flags, which is the point of it.

<Warning>
  A redirect URI is compared byte for byte, so the port is part of the registration. Five are
  registered, `8899` through `8903`, all on `/payman/callback`, and the server takes the first
  one free: several assistants on one machine do not fight over a port. `--callback-port` pins
  one instead, and a port outside that set is **not** registered, so authorize refuses it
  unless you registered it yourself.
</Warning>

| Flag                   | Env                           | Default                                |
| ---------------------- | ----------------------------- | -------------------------------------- |
| `--live`               | `PAYMAN_ENVIRONMENT=live`     | sandbox                                |
| `--app-key`            | `PAYMAN_APP_KEY`              | none — public client                   |
| `--client-id`          | `PAYMAN_CLIENT_ID`            | picked from the host at `initialize`   |
| `--base-url`           | `PAYMAN_BASE_URL`             | production                             |
| `--callback-port`      | `PAYMAN_MCP_CALLBACK_PORT`    | the first free of `8899`–`8903`        |
| `--callback-path`      | `PAYMAN_MCP_CALLBACK_PATH`    | `/payman/callback`                     |
| `--data-dir`           | `PAYMAN_MCP_DATA_DIR`         | `~/.payman/connect-mcp`                |
| `--user-id`            | `PAYMAN_MCP_USER_ID`          | a stable local id                      |
| —                      | `PAYMAN_CONSOLE_ORIGIN`       | from the app's own registration        |
| —                      | `PAYMAN_MCP_APPROVAL_WAIT_MS` | `90000`                                |
| —                      | `PAYMAN_MCP_CONSENT_WAIT_MS`  | `300000`                               |
| `--no-browser`         | `PAYMAN_MCP_NO_BROWSER=1`     | browser allowed                        |
| `--print-redirect-uri` | —                             | prints every registered URI, and exits |
| `-h`, `--help`         | —                             | prints usage, and exits                |
| `-h`, `--help`         | —                             | prints usage, and exits                |

`--app-key` switches to the confidential path if you would rather run your own registered Connect app; it is mutually exclusive with the public client, because a client that has a secret must use it. The local connection store is encrypted under a random per-installation secret generated on first run — never the `client_id`, which is published and identical for every install.

## What it will not do

* **Run a payment twice.** One request runs the action at most once against a live connection, and a replay into an approved ticket completes that ticket rather than paying again.
* **Hold your approval code.** Approval happens on Payman's page, in your own session, with a code emailed to you. Nothing here can ask for it or carry it — an assistant that asks for the code is wrong, and Payman will refuse it anyway.
* **Tell you a payment landed when Payman cannot confirm it.** An unconfirmed outcome is returned unretryable, with instructions not to offer a retry — a retry there risks paying twice.
