Skip to main content
Glama
tejasghalsasi

helcim-mcp

README.md
# helcim-mcp

> **Unofficial** community MCP server and developer toolkit for the Helcim API.
> Secure, typed, agent-friendly, and **read-only by default**.
> **Not affiliated with, sponsored by, maintained by, or endorsed by Helcim Inc.**

`helcim-mcp` is a production-quality, TypeScript monorepo that makes it safe
and easy for AI agents (and humans) to work with the Helcim payment platform.
It ships three things:

1. **`@helcim-mcp/server`** - an MCP server exposing **read-only** Helcim tools
   (customers, invoices, card transactions, card batches, recurring payment
   plans, subscriptions, connection test).
2. **`@helcim-mcp/core`** - a typed, idempotency-aware Helcim API client with
   normalized errors, rate-limit handling, and secret redaction.
3. **`@helcim-mcp/webhooks`** - a standalone Helcim webhook verifier
   (HMAC-SHA256 signature verification, timestamp validation, replay
   protection, typed events).

---

## Why would you use this?

- **You want an AI agent to answer questions about your Helcim data** - "what
  invoices are outstanding?", "show recent card transactions", "find the
  customer for this invoice", "which subscriptions need attention?" - **without
  ever risking a financial mutation**.
- **You want a clean, typed Helcim client** that handles the API's quirks
  (HTTP 200 ≠ success, `errors` object shapes, idempotency, rate limits,
  pagination) so you don't have to.
- **You want to verify Helcim webhooks securely** with constant-time signature
  comparison and replay protection, without re-inventing the HMAC scheme.

The MCP server is **read-only by default**. It physically cannot create,
update, delete, or move money - there are no such tools. Even a token with
full processing privileges cannot trigger a financial mutation through this
server.

---

## Quick start

### 1. Get a Helcim API token

Log in to your Helcim account (or a developer test account), go to
**All Tools → Integrations → API Access Configurations**, and create a
configuration. For read-only use, set **General: Read**, **Settings: Read**,
and **Transaction Processing: None**.

### 2. Run the MCP server

```bash
# From source
git clone https://github.com/tejasghalsasi/helcim-mcp.git
cd helcim-mcp
pnpm install
pnpm rebuild esbuild   # required: pnpm 11 blocks esbuild's postinstall by default
pnpm build

# Set your token (never commit it)
export HELCIM_API_TOKEN="your_token_here"

# Run over stdio
node packages/mcp/dist/index.js
```

### 2b. Run with Docker (optional)

```bash
docker build -t helcim-mcp .
docker run --rm -e HELCIM_API_TOKEN=your_token_here helcim-mcp
```

### 2c. Run via npx (once published)

```bash
npx @helcim-mcp/server
```

### 3. Connect it to an MCP client

Add this to your MCP client config (e.g. Claude Desktop, Cursor, or any MCP
client):

```json
{
  "mcpServers": {
    "helcim": {
      "command": "node",
      "args": ["/absolute/path/to/helcim-mcp/packages/mcp/dist/index.js"],
      "env": {
        "HELCIM_API_TOKEN": "your_token_here"
      }
    }
  }
}
```

### 4. Ask your agent

Once connected, your agent can call tools like:

- `connection_test` - confirm the token works.
- `list_invoices` with `status: "DUE"` - "what invoices are outstanding?"
- `list_card_transactions` - "show recent card transactions."
- `get_customer` - "find the customer for this invoice."
- `list_subscriptions` with `hasFailedPayments: true` - "which subscriptions need attention?"

---

## How read-only mode works

- The MCP server exposes **only** read tools. There are no payment, refund,
  capture, reversal, withdraw, settle, or delete tools.
- The core client exposes **no** write methods in v1.
- If a future version adds writes, it will require an explicit
  `HELCIM_ENABLE_WRITES=true` environment variable **and** a separate
  high-risk feature flag for financial mutations, with strong documentation
  and tests.
- HTTP 200 is **not** treated as success. Helcim explicitly warns that a 200
  response does not mean the requested action succeeded; the client surfaces
  `errors` in the body as typed errors.

## How credentials are protected

- The API token is read **only** from the `HELCIM_API_TOKEN` environment
  variable. Never hardcoded, never committed, never logged.
- All log lines and error messages pass through `redact()`. Token-like
  strings, card numbers, and F6L4 values are replaced with `<redacted-...>`.
- The token is never exposed to the model. The MCP server returns only
  redacted data and typed error codes.
- See [SECURITY.md](./SECURITY.md) for the full security model.

---

## Architecture

```mermaid
flowchart LR
    subgraph Client["MCP Client (LLM)"]
        A[Agent]
    end

    subgraph Server["@helcim-mcp/server"]
        M[MCP Server<br/>stdio transport]
        T[Read-only tools<br/>13 tools]
    end

    subgraph Core["@helcim-mcp/core"]
        C[HelcimClient]
        H[HelcimHttpClient<br/>auth, idempotency,<br/>rate-limit, redaction]
        E[Normalized errors]
    end

    subgraph Webhooks["@helcim-mcp/webhooks"]
        W[HelcimWebhookVerifier<br/>HMAC-SHA256, replay protection]
    end

    subgraph Helcim["Helcim API"]
        API[api.helcim.com/v2]
    end

    A -->|JSON-RPC over stdio| M
    M --> T
    T --> C
    C --> H
    H -->|HTTPS + api-token| API
    W -.->|verifies signed events| API
```

The monorepo layout:

```
helcim-mcp/
├── packages/
│   ├── core/       # Typed Helcim API client (read-safe)
│   ├── mcp/        # MCP server (read-only tools)
│   ├── webhooks/   # Webhook verifier
│   └── fixtures/   # Deterministic mock responses + test vectors
├── examples/       # Copy-paste usage examples
├── docs/           # Architecture, env reference, troubleshooting
└── scripts/        # Smoke test, CI helpers
```

---

## Example interaction

> **Agent:** "What invoices are currently outstanding?"

```
list_invoices(status: "DUE")
→ { count: 2, invoices: [
    { invoiceId: 28658838, invoiceNumber: "INV1000", status: "DUE", currency: "CAD", customerId: 2488717 },
    { invoiceId: 28658839, invoiceNumber: "INV1001", status: "DUE", currency: "USD", customerId: 2488718 }
  ] }
```

> **Agent:** "Show recent card transactions."

```
list_card_transactions(limit: 5)
→ { count: 2, transactions: [
    { transactionId: 25557533, status: "APPROVED", type: "purchase", amount: 100.99, currency: "CAD", cardType: "MC", customerCode: "CST1000" },
    { transactionId: 25557534, status: "DECLINED", type: "purchase", amount: 250.00, currency: "CAD", cardType: "VI", customerCode: "CST1001" }
  ] }
```

> **Agent:** "Find the customer associated with this invoice."

```
get_invoice(invoiceId: 28658838) → { customerId: 2488717, ... }
get_customer(customerId: 2488717) → { customerCode: "CST1000", businessName: "Acme Widgets Ltd", ... }
```

> **Agent:** "Show subscriptions requiring attention."

```
list_subscriptions(hasFailedPayments: true)
→ { count: 1, subscriptions: [ { id: 42, status: "ACTIVE", hasFailedPayments: true, customerCode: "CST1000", ... } ] }
```

> **Agent:** "Process a refund for transaction 25557533."

```
→ Error: Unknown tool: process_refund
```

The agent **cannot** move money. There is no such tool.

---

## Webhook verification

```ts
import { HelcimWebhookVerifier } from '@helcim-mcp/webhooks';

const verifier = new HelcimWebhookVerifier(process.env.HELCIM_VERIFIER_TOKEN!);

// In your webhook handler (e.g. Next.js route handler):
export async function POST(req: Request) {
  const body = await req.text();
  const headers = Object.fromEntries(req.headers.entries());
  try {
    const verified = verifier.verify(headers, body);
    // verified.event.type === 'cardTransaction' | 'terminalCancel'
    return new Response('ok', { status: 200 });
  } catch (err) {
    return new Response('invalid signature', { status: 401 });
  }
}
```

See [examples/webhook-nextjs.md](./examples/webhook-nextjs.md) for a full
Next.js example.

---

## Environment variables

| Variable                | Required         | Description                                              |
| ----------------------- | ---------------- | -------------------------------------------------------- |
| `HELCIM_API_TOKEN`      | Yes (for server) | Your Helcim API token.                                   |
| `HELCIM_BASE_URL`       | No               | Override base URL (default `https://api.helcim.com/v2`). |
| `HELCIM_DEBUG`          | No               | `true` to enable redacted request logging.               |
| `HELCIM_TIMEOUT_MS`     | No               | Request timeout in ms (default `15000`).                 |
| `HELCIM_VERIFIER_TOKEN` | For webhooks     | Your Helcim webhook verifier token.                      |

See [docs/environment.md](./docs/environment.md) for the full reference.

## Reference

- [Supported tools & API matrix](./docs/api-matrix.md) - every MCP tool and
  core client method mapped to its Helcim endpoint.
- [Architecture](./docs/architecture.md) - design decisions and data flow.
- [Troubleshooting](./docs/troubleshooting.md) - common issues and fixes.
- [Environment reference](./docs/environment.md) - all env vars.
- [Release process](./docs/release.md) - how to cut a release.

---

## Development

```bash
pnpm install
pnpm rebuild esbuild  # pnpm 11 blocks esbuild's postinstall by default
pnpm build        # build all packages
pnpm test         # run all tests
pnpm typecheck    # type-check all packages
pnpm lint         # prettier check
pnpm smoke        # verify the built server exposes only read-only tools
```

## License

MIT. See [LICENSE](./LICENSE).

## Disclaimer

This is an independent community project. It is **not** affiliated with,
sponsored by, maintained by, or endorsed by Helcim Inc. "Helcim" is a
trademark of Helcim Inc. and is used here only to describe API compatibility.
This project does not use Helcim logos or branding.