Skip to main content
Glama
nziacbdc

movmint-fx-mcp

by nziacbdc
README.md
# movmint-fx-mcp

MCP server for the Movmint FX quoting and capture service. Exposes two tools (`get_fx_quote`, `capture_fx_quote`) that LLM clients can call to get live FX rates and execute trades.

---

## Architecture

### How it fits together

```
┌─────────────────────────────────────────────────────────────────┐
│  LLM Client (Claude Desktop / Open WebUI + Ollama)              │
└───────────────────────┬─────────────────────────────────────────┘
                        │
          ┌─────────────▼──────────────┐
          │  Transport layer           │
          │                            │
          │  Option A: stdio           │  ← Claude Desktop spawns
          │    (no port, no proxy)     │    the process directly
          │                            │
          │  Option B: HTTP via mcpo   │  ← Open WebUI / Ollama
          │    localhost:8008          │    connects as tool server
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │  MCP Server (Node.js)      │
          │  src/index.ts              │
          │                            │
          │  Tools:                    │
          │  • get_fx_quote            │
          │  • capture_fx_quote        │
          └─────────────┬──────────────┘
                        │  OAuth2 client_credentials
                        │  Bearer token (cached, auto-refreshed)
          ┌─────────────▼──────────────┐
          │  Movmint API               │
          │  api.nextgen.digitalfiat.app│
          │                            │
          │  POST /fx/quote            │
          │  POST /fx/quote/capture/:id│
          └────────────────────────────┘
```

### MCP Server (`src/index.ts`)

Written in TypeScript using the `@modelcontextprotocol/sdk`. Key behaviours:

- **Transport:** `StdioServerTransport` — communicates over stdin/stdout. Does not bind to a network port by itself.
- **Auth:** OAuth2 `client_credentials` flow. Tokens are cached in memory and refreshed 30 seconds before expiry.
- **Validation:** Tool inputs are validated with `zod` schemas before the API is called.

#### Tool: `get_fx_quote`

Calls `POST /fx/quote` on the Movmint API.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `from_currency` | string | Yes | Source currency code (e.g. `BSD`, `USD`) |
| `to_currency` | string | Yes | Target currency code (e.g. `USDC`, `EUR`) |
| `amount` | number | Yes | Amount in `from_currency` |
| `funding_method` | `IMMEDIATE` \| `DEFERRED` | No | Defaults to `IMMEDIATE` |
| `tx_configuration` | object | Yes | Client reference ID + source/target payment config |
| `participants` | array | No | KYC participants (ULTIMATE_ORIGINATOR, ULTIMATE_BENEFICIARY, etc.) |

Returns a `quote_id` valid for approximately 30 seconds.

#### Tool: `capture_fx_quote`

Calls `POST /fx/quote/capture/{quote_id}` on the Movmint API.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `quote_id` | string | Yes | The `quote_id` returned from `get_fx_quote` |

Returns transaction details including the deposit address for funding.

#### Payment configuration schemas

`tx_configuration` accepts `source_configuration` and `target_configuration`, each with a `source_type` and an `account_configuration` that is one of:

| Type | Fields |
|------|--------|
| `BANK_ACCOUNT` | `account_number`, `routing_number`, `account_type` (`CHECKING`/`SAVINGS`) |
| `DLT_WALLET` | `wallet_address`, `network` (`ethereum`/`solana`/`base`/`sanddollar`) |
| `CARD` | `card_number`, `expiry_date`, `cvv`, `cardholder_name`, `card_type` (`CreditCard`/`DebitCard`) |

---

## Environment variables

| Variable | Default | Required |
|----------|---------|----------|
| `CLIENT_ID` | — | **Yes** |
| `CLIENT_SECRET` | — | **Yes** |
| `MOVMINT_BASE_URL` | `https://api.nextgen.digitalfiat.app` | No |
| `MOVMINT_TOKEN_URL` | `https://api.nextgen.digitalfiat.app/oauth2/token` | No |
| `MOVMINT_TOKEN_AUDIENCE` | `fx-service` | No |

Store these in a `.env` file at the project root (never commit it — it is gitignored).

```
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret
MOVMINT_BASE_URL=https://api.nextgen.digitalfiat.app
MOVMINT_TOKEN_URL=https://api.nextgen.digitalfiat.app/oauth2/token
MOVMINT_TOKEN_AUDIENCE=fx-service
```

---

## Bootstrap: running locally

### Prerequisites

- Node.js 20+
- `uvx` (ships with `uv`) — for running `mcpo` without a permanent install

### 1. Install dependencies and build

```bash
cd /path/to/movmint-fx-mcp
npm install
npm run build        # compiles src/ → dist/
```

### 2. Option A — Claude Desktop (stdio, no proxy needed)

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "movmint-fx": {
      "command": "node",
      "args": ["/absolute/path/to/movmint-fx-mcp/dist/index.js"],
      "env": {
        "CLIENT_ID": "your-client-id",
        "CLIENT_SECRET": "your-client-secret"
      }
    }
  }
}
```

Restart Claude Desktop. The tools appear automatically in the tool picker.

### 2. Option B — Open WebUI + Ollama (HTTP via mcpo)

`mcpo` is a proxy that wraps any stdio MCP server and exposes it as an OpenAI-compatible HTTP tool server. Open WebUI can then connect to it as a tool source for any hosted model including Ollama models.

#### Port map (confirmed on this machine)

| Service | Port |
|---------|------|
| Ollama | 11434 |
| Open WebUI | 8080 |
| mcpo (this server) | **8008** |

#### Step 1 — Create `mcpo-config.json`

Create `mcpo-config.json` at the project root (it is gitignored):

```json
{
  "mcpServers": {
    "movmint-fx": {
      "command": "node",
      "args": ["/absolute/path/to/movmint-fx-mcp/dist/index.js"],
      "env": {
        "CLIENT_ID": "your-client-id",
        "CLIENT_SECRET": "your-client-secret",
        "MOVMINT_BASE_URL": "https://api.nextgen.digitalfiat.app",
        "MOVMINT_TOKEN_URL": "https://api.nextgen.digitalfiat.app/oauth2/token",
        "MOVMINT_TOKEN_AUDIENCE": "fx-service"
      }
    }
  }
}
```

#### Step 2 — Start mcpo

```bash
uvx mcpo --port 8008 --config /absolute/path/to/movmint-fx-mcp/mcpo-config.json
```

Verify it is running:

```bash
curl http://localhost:8008/docs        # OpenAPI/Swagger UI
curl http://localhost:8008/openapi.json
```

#### Step 3 — Connect Open WebUI

1. Open WebUI → **Admin Panel → Settings → Tools**
2. Add tool server URL: `http://localhost:8008`
3. Open WebUI discovers `get_fx_quote` and `capture_fx_quote` automatically via the OpenAPI spec
4. Enable the tools when starting a chat with any Ollama model

### 3. Development mode (watch + rebuild)

```bash
npm run dev      # tsc --watch, rebuilds dist/ on every save
```

Run mcpo in a separate terminal; it will pick up the rebuilt `dist/index.js` on the next tool invocation (the Node process is spawned fresh per mcpo request).

---

## Project structure

```
movmint-fx-mcp/
├── src/
│   └── index.ts          # MCP server — tools, auth, API client
├── dist/                 # Compiled output (gitignored)
├── interactions/
│   └── SD-CAD_SD-USD Collection.postman_collection.json
├── mcpo-config.json      # mcpo proxy config with credentials (gitignored)
├── package.json
├── tsconfig.json
├── .env                  # Local credentials (gitignored)
└── .gitignore
```

---

## Security notes

- `mcpo-config.json` and `.env` both contain credentials and are gitignored. Do not commit either file.
- The MCP server logs the token request URL and body to `stderr` for debugging. Avoid piping stderr to shared log aggregators in production.
- The `quote_id` returned by `get_fx_quote` expires in ~30 seconds. Do not store or reuse stale quote IDs.

TDQS

A4.1/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one generates a quote, the other executes it. No overlap or ambiguity.

Naming Consistency5/5

Both tools follow a consistent verb_noun pattern with snake_case: get_fx_quote and capture_fx_quote.

Tool Count4/5

With only 2 tools, the set is minimal but appropriate for a focused FX quote-capture workflow. Slightly thin but not unreasonable.

Completeness3/5

The core flow of getting and capturing a quote is covered, but missing operations like quote status, history, or cancellation create notable gaps.

Maintenance

ActivityStale
ResponsivenessNo issues