--- url: https://mcp.klappay.com/getting-started.md --- # Getting started ## 1. Store a test key `@klappay/mcp` reads the same `~/.klap/config.json` the Klap CLI writes, so the key never has to appear in your MCP client's config file: ```sh npx @klappay/cli login --api-key - --base-url https://api.klap.example ``` Paste a `klap_test_...` key on stdin and press Ctrl-D. Reading it from stdin (`--api-key -`) keeps it out of your shell history; setting `KLAP_API_KEY` before running the command works too. `--base-url` is required. The file is created with `0600` permissions. You can store a live key the same way later; both slots live side by side. ## 2. Add the server Claude Code: ```sh claude mcp add klap-test -- npx -y @klappay/mcp@1.0.0 ``` Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`): ```json { "mcpServers": { "klap-test": { "command": "npx", "args": ["-y", "@klappay/mcp@1.0.0"] } } } ``` Restart the client. Its MCP log should show `klap-mcp: test → `. If the server refuses to start, the same log has a one-line reason; see [Configuration](./configuration.md#refusals). ## 3. Try it Ask the assistant things like: * "Which Klap environment are you connected to?" (`klap_status`) * "Create a 25 USD test charge payable in USDC on Base, expiring in 15 minutes." (`charges_create`) * "Mark that charge as confirmed in the sandbox, then show me its timeline." (`sandbox_trigger`, `charges_timeline`) * "Which of my webhooks failed deliveries recently?" (`webhooks_list`, `webhooks_list_deliveries`) ## 4. Add live (optional) Add a second, separate server with `KLAP_ENV=live`. It is read-only: ```sh claude mcp add klap-live -e KLAP_ENV=live -- npx -y @klappay/mcp@1.0.0 ``` Only add `KLAP_MCP_ALLOW_LIVE_WRITES=1` if you really want the assistant to create live charges or re-send live webhooks; read [Security](./security.md) first. ## Where to go next * [Configuration](./configuration.md): every variable and refusal * [Tools](./tools.md): what each tool takes and returns * [Security](./security.md): what is excluded and why ## For LLMs and agents This site publishes [`llms.txt`](https://mcp.klappay.com/llms.txt), a link index of every doc page, and [`llms-full.txt`](https://mcp.klappay.com/llms-full.txt), the full content of every doc page in one plain-text file. Point an agent or RAG pipeline at either to give it these docs without scraping HTML. Both regenerate on every deploy, so they never drift from the pages here. --- --- url: https://mcp.klappay.com/configuration.md --- # Configuration The server reads four environment variables. An empty string counts as unset; any other value, including whitespace, is taken literally. | Variable | Values | |---|---| | `KLAP_ENV` | unset (means `test`), `test` or `live` | | `KLAP_API_KEY` | a `klap_test_...` or `klap_live_...` key | | `KLAP_BASE_URL` | the Klap API base URL | | `KLAP_MCP_ALLOW_LIVE_WRITES` | `1` to enable live writes; anything else leaves them off | ## How the key is chosen 1. `KLAP_ENV` picks the environment. It must be exactly `test` or `live` (`LIVE`, ` live` or `production` all refuse). 2. If `KLAP_API_KEY` is set, it is the only key source. `~/.klap/config.json` is not read at all. `KLAP_BASE_URL` is required, and the key's prefix must match the environment: a `klap_test_` key with `KLAP_ENV=live` refuses to start, and so does a `klap_live_` key with `KLAP_ENV` unset. 3. Otherwise the key comes from `~/.klap/config.json`, the slot for the chosen environment. When `KLAP_ENV` is unset the test slot is used. Unlike the CLI, the server never picks live on its own: if only a live key is stored, set `KLAP_ENV=live`. The base URL is the one stored with the key. If `KLAP_BASE_URL` is also set it must point to the same place (compared on origin and path, ignoring a trailing slash). ## Base URL rules Whichever base URL ends up in use must: * parse as a URL; * use `https://`, or `http://` only for `localhost`, `127.0.0.1` or `[::1]`; * carry no username or password. ## Live writes In `live`, only read tools are registered. `KLAP_MCP_ALLOW_LIVE_WRITES=1` adds `charges_create`, `charges_check` and `webhooks_retry_delivery`. `sandbox_trigger` is never available in live. In `test` the variable has no effect; every tool is available. ## Refusals When the configuration is unsafe or incomplete the server prints one line to stderr and exits with status 1. Messages never contain key material. | Situation | What to do | |---|---| | `KLAP_ENV` is not `test`/`live` | Fix the value | | `KLAP_API_KEY` set, `KLAP_BASE_URL` missing | Set both | | `KLAP_API_KEY` prefix unknown | Use a `klap_test_`/`klap_live_` key | | Key environment differs from `KLAP_ENV` | Use the matching key or change `KLAP_ENV` | | No `~/.klap/config.json` and no `KLAP_API_KEY` | Run `klap login --api-key - --base-url ` | | Stored config has no key for the environment | `klap login` with that key, or change `KLAP_ENV` | | Stored config is corrupted or has a key in the wrong slot | Run `klap logout`, then `klap login` again | | `~/.klap` or `~/.klap/config.json` is a symlink | Remove the link, then run `klap login` | | `KLAP_BASE_URL` differs from the stored base URL | Unset it, or pass `KLAP_API_KEY` too | | Base URL is `http://` on a non-loopback host, or has credentials | Use `https://` without credentials | ## Using a key without `klap login` For CI or a throwaway setup you can pass the key directly. It then sits in your MCP client's config file, so prefer `klap login` on a workstation: ```json { "mcpServers": { "klap-local": { "command": "npx", "args": ["-y", "@klappay/mcp@1.0.0"], "env": { "KLAP_API_KEY": "klap_test_...", "KLAP_BASE_URL": "http://localhost:3000" } } } } ``` --- --- url: https://mcp.klappay.com/tools.md --- # Tools Every result is a JSON object with an `environment` field (`test` or `live`). It is returned both as text and as `structuredContent`. API responses are parsed through the matching `@klappay/types` schema first: fields the schema doesn't know are dropped, and a response that doesn't match at all becomes an `unexpected_response` error instead of being passed through. Ids are checked before any request is sent: charge ids look like `ch_...`, webhook ids `wh_...`, delivery ids `ev_...`, followed by letters and digits only. ## Errors A failed call returns `isError: true` with: ```json { "environment": "test", "error": { "code": "charge_not_found", "status": 404, "message": "Charge not found" } } ``` `code` and `status` come straight from the Klap API. Locally detected problems use `validation_error` (invalid input), `unexpected_response` (the API answered with something the schema rejects) or `unexpected_error` (anything else; details go to the server's stderr only). Input that fails the tool's schema is rejected by the MCP SDK before the tool runs. ## Read tools Available in every environment, annotated `readOnlyHint: true`. ### `klap_status` No input. Returns `{ environment, host }`, where `host` is the API host (and port) only. Makes no API call. ### `charges_get` | Input | | |---|---| | `id` | charge id | | `includeMetadata` | boolean, default `false` | Returns `{ charge }` (`ChargeSchema`). The charge's `metadata` object (free-form merchant data, which may contain customer details) is left out unless `includeMetadata` is `true`. ### `charges_timeline` Input `{ id }`. Returns `{ chargeId, events }` (`TimelineEventSchema[]`). ### `webhooks_list` No input. Returns `{ webhooks }` (`WebhookListItemSchema[]`). Each `url` is reduced to origin + path: any username/password, query string and fragment are removed. Secrets are never returned, only the display `hint`. ### `webhooks_list_deliveries` | Input | | |---|---| | `webhookId` | webhook id | | `limit` | 1–100, default 20 | | `cursor` | the previous page's `nextCursor` | Returns `{ webhookId, data, nextCursor, hasMore }` (`PaginatedWebhookDeliveriesSchema`). ### `networks_get` No input. Returns `{ acceptedPayments }` (`CapabilitiesSchema`): the `(token, network)` pairs this environment accepts for new charges. ### `metrics_query` Input `{ query }`, where `query` is a `MetricsQuerySchema` request (resource, metrics, `dateRange`, optional filters/groupBy/limit). Returns the `MetricsQueryResultSchema` fields (`data` rows and `meta`). ## Write tools Available in `test`, and in `live` only with `KLAP_MCP_ALLOW_LIVE_WRITES=1`. Annotated `readOnlyHint: false`. ### `charges_create` Input is `CreateChargeSchema` without `escrow` and `redirectUrl`; sending either is rejected. Pass `idempotencyKey` to make a retry safe; without it every call creates a new charge. Returns `{ charge }` without `metadata` (including `amount`, `acceptedPayments` and `splitRecipients`). Annotations: `destructiveHint: false`, `idempotentHint: false`. ### `charges_check` | Input | | |---|---| | `id` | charge id | | `txHash` | optional `0x` + 64 hex transaction hash | | `network` | required together with `txHash` | Asks the API to re-check the charge on-chain now. Returns `{ charge }` (`CheckChargeResponseSchema` without `metadata`). Annotations: `destructiveHint: false`, `idempotentHint: true`. ### `webhooks_retry_delivery` Input `{ webhookId, deliveryId }`. Re-sends one delivery to its endpoint; the receiver processes that event again. Returns `{ webhookId, deliveryId, retried: true }`. Annotations: `destructiveHint: true`. ## Sandbox tool ### `sandbox_trigger` Test environment only, never registered in live. | Input | | |---|---| | `chargeId` | charge id | | `event` | `charge.confirmed`, `charge.partially_paid`, `charge.overpaid`, `charge.expired`, `charge.underpaid`, `charge.settled` or `charge.settlement_failed` | | `amount` | optional, for `charge.partially_paid`/`charge.overpaid` | Returns `{ charge }` without `metadata`. Annotations: `destructiveHint: true` (the state change can't be undone on that charge). --- --- url: https://mcp.klappay.com/security.md --- # Security An MCP server hands an AI model the ability to call an API with your key. The model can be wrong, and text it reads (a charge's metadata, a webhook URL, an API error) can try to steer it. This server is built so that a confused or manipulated assistant can do as little damage as possible. ## Where the key goes * The key is read from `~/.klap/config.json` (via `@klappay/cli/credentials`, which refuses symlinked paths and keys stored in the wrong slot) or from `KLAP_API_KEY`. It is sent only as the `Authorization` header to the configured base URL. * A stored key is bound to the base URL stored with it. Setting a different `KLAP_BASE_URL` refuses to start rather than sending that key to a new host. To use another host, pass `KLAP_API_KEY` and `KLAP_BASE_URL` together. * `http://` is only accepted for loopback hosts; base URLs with embedded credentials are refused. * The key prefix must match `KLAP_ENV`, so a live key can't be used by a server you think is in test, or the other way round. * The key never appears in tool results, startup output or refusal messages. Request logging (`debug`) is never enabled. ## Live is read-only by default In `live` only read tools are registered. The model can't call a write tool that doesn't exist, whatever it is told. `KLAP_MCP_ALLOW_LIVE_WRITES=1` (exactly `1`) adds `charges_create`, `charges_check` and `webhooks_retry_delivery`. `sandbox_trigger` is never available in live. Run test and live as separate servers so you can enable or disable live independently. ## Excluded on purpose | Not exposed | Why | |---|---| | Escrow `release` / `refund` | They move funds out of a charge. That stays a deliberate human action. | | Webhook create / delete / rotate secret | Pointing webhooks somewhere else, or rotating a secret, can silently break or redirect your integration. | | Recipient (payout) changes | They decide where money goes. | | Live event streams (`watch`) | Long-lived streams don't fit a request/response tool. | | QR codes and swap quotes | Payer-facing features with no use to an assistant. | ## What the model sees * Responses are parsed through `@klappay/types` schemas, dropping any field the schema doesn't define. A response that doesn't fit the schema is reported as `unexpected_response`, not forwarded. * A charge's `metadata` (free-form merchant data, possibly customer details) is only included when `charges_get` is called with `includeMetadata: true`. * Webhook URLs are reduced to origin + path, removing credentials and tokens people often put in a URL's userinfo or query string. * Unexpected errors return a generic message; details go to stderr for you, not to the model. ## stdout stdout carries only MCP JSON-RPC messages. `console.log`, `console.info` and `console.debug` are redirected to stderr before anything else loads, so no dependency can corrupt the protocol stream. ## Reporting a problem Report security issues privately to the Klappay team rather than in a public issue.