@blanketbyte/wise-mcp 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +354 -2
- package/dist/auth/pages.js +298 -0
- package/dist/auth/provider.js +222 -0
- package/dist/auth/store.js +105 -0
- package/dist/cli.js +35 -0
- package/dist/config.js +55 -0
- package/dist/crypto.js +37 -0
- package/dist/fsutil.js +16 -0
- package/dist/index.js +198 -0
- package/dist/meta.js +10 -0
- package/dist/pending.js +34 -0
- package/dist/stdio.js +50 -0
- package/dist/tools.js +358 -0
- package/dist/wise/client.js +116 -0
- package/dist/wise/connections.js +76 -0
- package/dist/wise/service.js +350 -0
- package/package.json +69 -4
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/icon.png +0 -0
- package/public/logo.png +0 -0
- package/public/logo.svg +7 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BlanketByte (https://blanketbyte.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,355 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="public/logo.svg" alt="Wise MCP" width="120" height="120">
|
|
4
|
+
|
|
5
|
+
# Wise MCP
|
|
6
|
+
|
|
7
|
+
**Ask Claude or ChatGPT about your Wise money.**
|
|
8
|
+
The simplest, most complete MCP server for your Wise balances, card payments, transfers and recipients that can also prepare transfers you approve twice.
|
|
9
|
+
Run it locally with one `npx` command (Claude Desktop, Claude Code, Codex, Cursor…), or host it as a custom connector for Claude and ChatGPT.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
> *"How much did I spend on groceries in September?"*
|
|
23
|
+
> *"What's left in my jars, in EUR?"*
|
|
24
|
+
> *"Show the transfers I sent to Mario this year and the rate I got."*
|
|
25
|
+
> *"Send Mario 120 EUR for dinner."*
|
|
26
|
+
|
|
27
|
+
Give it a Wise API token once and your assistant can answer questions like these across all your Wise profiles, personal and business. It can also prepare transfers, new recipients and cancellations. Every change needs your explicit approval in chat, and a transfer only moves money once **you pay it in the Wise app**.
|
|
28
|
+
|
|
29
|
+
### Why this one
|
|
30
|
+
|
|
31
|
+
The simplest way to connect Wise to an AI assistant, and the most feature-rich Wise MCP server we know of (October 2026):
|
|
32
|
+
|
|
33
|
+
- **One token, no partner account.** Other servers need Wise partner OAuth credentials or stop at read-only basics. Here a personal API token is all you need, locally or hosted.
|
|
34
|
+
- **Your real spending.** The activity feed includes **card payments** with clean merchant names and amounts, plus transfers, conversions, cashback and Jars, across personal and business profiles.
|
|
35
|
+
- **Writes you can trust.** Quotes, new recipients, transfers and cancellations, all two-step and approved in chat; transfers are paid by you in the Wise app.
|
|
36
|
+
- **Everywhere.** `npx` for Claude Desktop, Claude Code, Codex and Cursor, a one-click Claude Desktop extension, or a self-hosted connector for Claude and ChatGPT on web and mobile.
|
|
37
|
+
- **Built for real accounts.** 14 tools, encrypted token storage, session management, an audit log, and end-to-end tests for both modes.
|
|
38
|
+
|
|
39
|
+
## Choose how to run it
|
|
40
|
+
|
|
41
|
+
| | **Local** | **Remote (self-hosted)** |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| Works with | Claude Desktop, Claude Code, Codex, Cursor, VS Code, any MCP client | Claude (web, desktop, mobile) and ChatGPT custom connectors |
|
|
44
|
+
| Setup | One `npx` command or a one-click Claude Desktop extension | Deploy a container behind HTTPS (Dokploy, Easypanel, Docker Compose) |
|
|
45
|
+
| Token | In your app's MCP config, on your machine | Pasted on a login page, stored encrypted on your server |
|
|
46
|
+
| Guide | [Run locally](#run-locally) | [Deploy](#deploy) |
|
|
47
|
+
|
|
48
|
+
## Contents
|
|
49
|
+
|
|
50
|
+
- [Why this one](#why-this-one)
|
|
51
|
+
- [Choose how to run it](#choose-how-to-run-it)
|
|
52
|
+
- [Tools](#tools)
|
|
53
|
+
- [How it works](#how-it-works)
|
|
54
|
+
- [Security model](#security-model)
|
|
55
|
+
- [Run locally](#run-locally)
|
|
56
|
+
- [Deploy](#deploy)
|
|
57
|
+
- [Your server's pages](#your-servers-pages)
|
|
58
|
+
- [Configuration](#configuration)
|
|
59
|
+
- [Development](#development)
|
|
60
|
+
- [Limitations](#limitations)
|
|
61
|
+
- [Contributing](#contributing)
|
|
62
|
+
- [License](#license)
|
|
63
|
+
|
|
64
|
+
## Tools
|
|
65
|
+
|
|
66
|
+
### Read
|
|
67
|
+
|
|
68
|
+
| Tool | What it returns |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `list_profiles` | Every Wise profile the token can see (personal, business) |
|
|
71
|
+
| `list_balances` | Balances in every currency plus **Jars**, across all profiles by default |
|
|
72
|
+
| `get_transactions` | Activity feed, newest first: **card payments**, transfers, conversions, cashback, direct debits. Clean merchant names, numeric amounts, `in` / `out` / `neutral` direction. Filter by type, status, date range and text; cursor pagination |
|
|
73
|
+
| `list_transfers` | Transfers newest first, with both currencies, reference, status and recipient name |
|
|
74
|
+
| `get_transfer` | One transfer in detail: rate, amounts, reference, recipient |
|
|
75
|
+
| `list_recipients` | Saved recipients with account number / IBAN, filterable by currency and text |
|
|
76
|
+
| `get_exchange_rate` | Wise mid-market rate, now or at a past date |
|
|
77
|
+
|
|
78
|
+
### Write: always two-step
|
|
79
|
+
|
|
80
|
+
| Tool | What it does |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `get_quote` | Prices a transfer or conversion: rate, fee, amount received, delivery estimate. Changes nothing |
|
|
83
|
+
| `get_recipient_requirements` | The bank details Wise needs for a recipient in a currency (IBAN, sort code, …) |
|
|
84
|
+
| `prepare_transfer` | Quotes a transfer to a saved recipient, checks Wise's route requirements and your balance, returns a summary + one-time code |
|
|
85
|
+
| `prepare_recipient` | Validates a new recipient and returns a summary + one-time code |
|
|
86
|
+
| `prepare_cancel_transfer` | Prepares cancelling a transfer you haven't paid yet |
|
|
87
|
+
| `confirm_action` | Performs a prepared action. Only after you approve the exact summary |
|
|
88
|
+
| `discard_action` | Throws a prepared action away |
|
|
89
|
+
|
|
90
|
+
Profiles are discovered from the token at runtime. Every tool takes an optional `profile`: an id, `personal`, `business`, or part of the profile name.
|
|
91
|
+
|
|
92
|
+
## How it works
|
|
93
|
+
|
|
94
|
+
**Locally**, the server runs on your machine as a child process of your MCP client and reads the token from `WISE_API_TOKEN`. Nothing is hosted and there's no login.
|
|
95
|
+
|
|
96
|
+
**Remotely**, Wise doesn't offer OAuth to individuals, so the server is its own OAuth 2.1 authorization server for Claude and ChatGPT, and the "login" is a Wise personal API token you paste once.
|
|
97
|
+
|
|
98
|
+
```mermaid
|
|
99
|
+
sequenceDiagram
|
|
100
|
+
autonumber
|
|
101
|
+
participant C as Claude / ChatGPT
|
|
102
|
+
participant S as Wise MCP
|
|
103
|
+
participant W as Wise API
|
|
104
|
+
C->>S: Dynamic client registration (trusted redirect hosts only)
|
|
105
|
+
C->>S: /authorize (PKCE)
|
|
106
|
+
S-->>C: Login page: token + "stay connected for"
|
|
107
|
+
Note over C,S: You paste a Wise API token, pick 5 / 15 / 30 / 90 days or until revoked
|
|
108
|
+
S->>W: GET /v1/me
|
|
109
|
+
W-->>S: Wise user id
|
|
110
|
+
S->>S: Allowlist check, token sealed with AES-256-GCM
|
|
111
|
+
S-->>C: Authorization code → access + refresh tokens
|
|
112
|
+
C->>S: MCP tool calls (Bearer, 1 h tokens, rotating refresh)
|
|
113
|
+
S->>W: Reads, and writes only after confirm_action
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- **Session length** is chosen at each login. Access tokens last one hour; refresh tokens rotate on every use and live exactly as long as the session.
|
|
117
|
+
- **`/sessions`** lists every connected session for your Wise account and lets you revoke one or all. You sign in by pasting a Wise token again; it's only used to check who you are and is never stored. Access lasts 15 minutes in an HTTP-only cookie.
|
|
118
|
+
- **Revoking in Wise works too**: delete the token in Wise → Settings → API tokens. The next call fails with a clear message, and the server deletes the stored token and every session that used it.
|
|
119
|
+
|
|
120
|
+
### Two confirmations for every write
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
prepare_* ──▶ summary + code (TX-…, RCP-…, CXL-…) ──▶ you approve in chat ──▶ confirm_action
|
|
124
|
+
nothing written to Wise yet ① created in Wise
|
|
125
|
+
│
|
|
126
|
+
transfers only: you pay it in the Wise app ◀──────────┘
|
|
127
|
+
② money moves only now
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- Codes are single-use, expire after 10 minutes, and only work from the session that prepared them.
|
|
131
|
+
- Transfers are created **unpaid** (`incoming_payment_waiting`). Wise doesn't let personal tokens fund transfers for EU/UK accounts anyway, so the final "Pay" is always yours. Until then you can cancel the transfer with `prepare_cancel_transfer` or in the app.
|
|
132
|
+
- Every prepare, confirm, discard and failure is appended to `audit.log` (`~/.wise-mcp/` locally, the data volume when hosted).
|
|
133
|
+
|
|
134
|
+
## Security model
|
|
135
|
+
|
|
136
|
+
| | |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| ✋ **Two-step writes** | Every write is prepare → your approval in chat → confirm with a one-time code. Transfers then also need you to pay them in the Wise app. |
|
|
139
|
+
| 🔒 **Read-only option** | `WRITES_ENABLED=false` removes all write tools. With a Wise **Read only** token, write tools fail with a clear message and reading keeps working. |
|
|
140
|
+
| 💶 **Limits** | Optional `MAX_TRANSFER_AMOUNT` per transfer. Wise's own route checks (purpose, source of funds) are enforced before a code is issued. |
|
|
141
|
+
| 🧾 **Allowlist** | Only Wise user ids in `ALLOWED_WISE_USERS` can connect. Any other token is refused and nothing about it is stored. Removing an id cuts off that user's existing sessions too. |
|
|
142
|
+
| 🗝️ **Encryption at rest** | Wise tokens are sealed with AES-256-GCM under `TOKEN_ENCRYPTION_KEY`. OAuth tokens are stored only as SHA-256 hashes. State files and the audit log are owner-only (`0600`) and written atomically. |
|
|
143
|
+
| 🧹 **Minimal retention** | A Wise token is deleted as soon as no live session uses it: after revocation, session expiry, or when Wise rejects it. |
|
|
144
|
+
| 🔁 **OAuth hardening** | PKCE (S256), single-use login and authorization codes, rotating refresh tokens, redirect hosts limited to `claude.ai`, `claude.com`, `chatgpt.com` and localhost, so authorization codes can only reach apps you trust. |
|
|
145
|
+
| 🚦 **Rate limiting** | The token forms (`/login`, `/sessions/login`) allow 20 attempts per IP every 15 minutes; OAuth endpoints use the MCP SDK's limits. |
|
|
146
|
+
| 🛡️ **Pages** | Strict CSP (`default-src 'none'`, `form-action` limited to the server and the client's own redirect origin), `X-Frame-Options: DENY`, `no-store`, no referrer. |
|
|
147
|
+
|
|
148
|
+
> [!WARNING]
|
|
149
|
+
> Keep `TOKEN_ENCRYPTION_KEY` out of backups of the data volume. Changing it makes stored tokens unreadable: just reconnect.
|
|
150
|
+
|
|
151
|
+
## Run locally
|
|
152
|
+
|
|
153
|
+
Needs Node.js 20 or newer. Create a token first: Wise → **Settings → API tokens → Add new token** (**Full access** to prepare transfers and recipients, **Read only** for answers only).
|
|
154
|
+
|
|
155
|
+
### Claude Desktop extension (one click)
|
|
156
|
+
|
|
157
|
+
Download `wise-mcp-<version>.mcpb` from the [latest release](https://github.com/BlanketByte/wise-mcp/releases/latest) and open it. Claude Desktop asks for your token (stored securely, never in a plain config file) and whether to allow preparing transfers.
|
|
158
|
+
|
|
159
|
+
### Claude Code
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
claude mcp add wise -e WISE_API_TOKEN=your-token -- npx -y @blanketbyte/wise-mcp
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Codex
|
|
166
|
+
|
|
167
|
+
Add this to `~/.codex/config.toml`:
|
|
168
|
+
|
|
169
|
+
```toml
|
|
170
|
+
[mcp_servers.wise]
|
|
171
|
+
command = "npx"
|
|
172
|
+
args = ["-y", "@blanketbyte/wise-mcp"]
|
|
173
|
+
env = { WISE_API_TOKEN = "your-token" }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Claude Desktop, Cursor, VS Code and other MCP clients
|
|
177
|
+
|
|
178
|
+
Add this to the client's MCP config (for Claude Desktop: **Settings → Developer → Edit config**):
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"mcpServers": {
|
|
183
|
+
"wise": {
|
|
184
|
+
"command": "npx",
|
|
185
|
+
"args": ["-y", "@blanketbyte/wise-mcp"],
|
|
186
|
+
"env": { "WISE_API_TOKEN": "your-token" }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Optional env: `WRITES_ENABLED=false` for read-only, `MAX_TRANSFER_AMOUNT` for a per-transfer limit. Run `npx @blanketbyte/wise-mcp --help` for all options.
|
|
193
|
+
|
|
194
|
+
> [!NOTE]
|
|
195
|
+
> ChatGPT only connects to remote servers, so for ChatGPT use the [self-hosted](#deploy) setup.
|
|
196
|
+
|
|
197
|
+
## Deploy
|
|
198
|
+
|
|
199
|
+
The container serves plain HTTP on port **3000**; put it behind anything that terminates HTTPS. State (sealed tokens, sessions) lives in **`/data`**.
|
|
200
|
+
|
|
201
|
+
### Dokploy
|
|
202
|
+
|
|
203
|
+
1. **DNS**: point a hostname (e.g. `wise.example.com`) at your Dokploy server.
|
|
204
|
+
2. **Application**: *Create Service → Application*, source = this repository, branch `main`, build type **Dockerfile**, context `.`.
|
|
205
|
+
3. **Domain**: your hostname, container port **3000**, HTTPS on, certificate **Let's Encrypt**.
|
|
206
|
+
4. **Volume**: a named volume mounted at **`/data`** (the container runs as uid 1000).
|
|
207
|
+
5. **Environment**:
|
|
208
|
+
```env
|
|
209
|
+
PUBLIC_URL=https://wise.example.com
|
|
210
|
+
ALLOWED_WISE_USERS= # empty on the first deploy, see step 7
|
|
211
|
+
TOKEN_ENCRYPTION_KEY= # openssl rand -base64 32
|
|
212
|
+
```
|
|
213
|
+
6. **Deploy**, then check `https://wise.example.com/health`.
|
|
214
|
+
7. **Find your Wise user id**: connect from Claude once. The *Not allowed* page shows your id: set `ALLOWED_WISE_USERS=<id>`, redeploy and connect again.
|
|
215
|
+
|
|
216
|
+
### Easypanel
|
|
217
|
+
|
|
218
|
+
1. **DNS**: point a hostname (e.g. `wise.example.com`) at your Easypanel server.
|
|
219
|
+
2. **Service**: in a project, **+ Service → App**, name it `wise-mcp`.
|
|
220
|
+
3. **Source**: **GitHub**, owner/repository of this repo, branch `main`, build path `/`. (Or **Git** with the repository URL.)
|
|
221
|
+
4. **Build**: **Dockerfile**, file `Dockerfile`.
|
|
222
|
+
5. **Environment**:
|
|
223
|
+
```env
|
|
224
|
+
PUBLIC_URL=https://wise.example.com
|
|
225
|
+
ALLOWED_WISE_USERS= # empty on the first deploy, see step 9
|
|
226
|
+
TOKEN_ENCRYPTION_KEY= # openssl rand -base64 32
|
|
227
|
+
```
|
|
228
|
+
6. **Domains**: add `wise.example.com`, HTTPS on, port **3000**. Easypanel issues the Let's Encrypt certificate.
|
|
229
|
+
7. **Mounts**: add a **volume mount** named `data` with mount path **`/data`**. Without it, sessions and tokens are lost on every redeploy.
|
|
230
|
+
8. **Deploy**, then check `https://wise.example.com/health`.
|
|
231
|
+
9. **Find your Wise user id**: connect once from Claude or ChatGPT. The *Not allowed* page shows your id: set `ALLOWED_WISE_USERS=<id>`, deploy again and reconnect.
|
|
232
|
+
|
|
233
|
+
To redeploy automatically on every push, enable **Auto Deploy** on the service (it adds a GitHub webhook).
|
|
234
|
+
|
|
235
|
+
### Docker Compose
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
cp .env.example .env # fill it in
|
|
239
|
+
mkdir -p data && sudo chown 1000:1000 data
|
|
240
|
+
docker compose up -d --build # listens on 127.0.0.1:3000
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Connect Claude
|
|
244
|
+
|
|
245
|
+
1. In Wise: **Settings → API tokens → Add new token**. Choose **Full access** to prepare transfers and recipients, or **Read only** if you only want answers.
|
|
246
|
+
2. In Claude: **Settings → Connectors → Add custom connector**:
|
|
247
|
+
```
|
|
248
|
+
https://wise.example.com/mcp
|
|
249
|
+
```
|
|
250
|
+
3. Paste the token, choose how long to stay connected, done.
|
|
251
|
+
|
|
252
|
+
**ChatGPT:** **Settings → Apps & Connectors**, turn on **Developer mode** (under Advanced), then **Create** a connector with the same `/mcp` address and **OAuth** authentication. The same login page opens.
|
|
253
|
+
|
|
254
|
+
## Your server's pages
|
|
255
|
+
|
|
256
|
+
Once deployed, your server has three pages you can open in a browser. Replace `wise.example.com` with your own address.
|
|
257
|
+
|
|
258
|
+
| Page | Address | What it's for |
|
|
259
|
+
| --- | --- | --- |
|
|
260
|
+
| 🏠 **Home** | `https://wise.example.com/` | Shows the connector address and step-by-step instructions for Claude and ChatGPT. Open it to check the server is up. |
|
|
261
|
+
| 🔑 **Login** | *opens by itself* | Appears when you add the connector in Claude or ChatGPT. Paste your Wise API token, choose how long to stay connected, and press **Connect Wise**. You don't need to open it yourself. |
|
|
262
|
+
| 🧾 **Manage sessions** | `https://wise.example.com/sessions` | Lists every app connected to your Wise account (Claude, ChatGPT, on any device) with when it was connected, last used and when it expires. Revoke one, or all at once. To sign in, paste a Wise API token for your account; it's only used to check who you are and is never stored. |
|
|
263
|
+
|
|
264
|
+
**Common situations**
|
|
265
|
+
|
|
266
|
+
- *"I want to disconnect a device or app."* Open **Manage sessions** and press **Revoke** next to it.
|
|
267
|
+
- *"I think my token leaked."* Delete it in Wise → Settings → API tokens. Every session that used it stops working immediately, and the server deletes its copy. Then reconnect with a new token.
|
|
268
|
+
- *"Claude or ChatGPT says to reconnect."* Your session expired or was revoked. Remove and add the connector again; the **Login** page will open.
|
|
269
|
+
- *"The login page says Not allowed."* Your Wise account isn't on this server's allowlist. The page shows your Wise user id: add it to `ALLOWED_WISE_USERS` and redeploy.
|
|
270
|
+
|
|
271
|
+
For monitoring there's also `https://wise.example.com/health`, which returns `{"ok":true}` when the server is running.
|
|
272
|
+
|
|
273
|
+
## Configuration
|
|
274
|
+
|
|
275
|
+
| Variable | Mode | Default | |
|
|
276
|
+
| --- | --- | --- | --- |
|
|
277
|
+
| `WISE_API_TOKEN` | local, **required** | | Wise personal API token |
|
|
278
|
+
| `WRITES_ENABLED` | both | `true` | `false` removes every write tool |
|
|
279
|
+
| `MAX_TRANSFER_AMOUNT` | both | *(no limit)* | Max per transfer, in the source currency |
|
|
280
|
+
| `DATA_DIR` | both | `~/.wise-mcp` locally, `/data` in Docker | Audit log (and, hosted, sealed tokens and sessions) |
|
|
281
|
+
| `PUBLIC_URL` | remote, **required** | | Public HTTPS origin of the server |
|
|
282
|
+
| `TOKEN_ENCRYPTION_KEY` | remote, **required** | | ≥ 32 chars; encrypts stored Wise tokens |
|
|
283
|
+
| `ALLOWED_WISE_USERS` | remote | *(nobody)* | Comma-separated Wise user ids |
|
|
284
|
+
| `ALLOWED_REDIRECT_HOSTS` | remote | `claude.ai,claude.com,chatgpt.com,localhost,127.0.0.1` | Hosts OAuth clients may redirect to |
|
|
285
|
+
| `PORT` | remote | `3000` | |
|
|
286
|
+
|
|
287
|
+
At startup the server logs the allowed users and a short SHA-256 fingerprint of the encryption key, so you can tell which key is deployed without revealing it.
|
|
288
|
+
|
|
289
|
+
## Development
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
npm install
|
|
293
|
+
npm run dev # remote server with tsx watch (needs the remote env vars)
|
|
294
|
+
npm test # typecheck + both end-to-end suites
|
|
295
|
+
npm run e2e # remote server against a local mock of the Wise API
|
|
296
|
+
npm run e2e:stdio # local mode: spawns the CLI like an MCP client would
|
|
297
|
+
npm run mcpb # builds release/wise-mcp-<version>.mcpb
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The remote end-to-end test boots the server next to a mock Wise API and drives everything Claude does: client registration, PKCE login, allowlist, session lengths, refresh rotation, encryption at rest, every tool (amount parsing, pagination, filters), the two-step writes (single-use codes, cross-session isolation, route requirements, cancellation, audit log, read-only tokens), `/sessions`, and revocation in Wise. The stdio suite covers the CLI, token errors, every tool over stdio, two-step writes, the audit log and read-only mode.
|
|
301
|
+
|
|
302
|
+
### Releasing
|
|
303
|
+
|
|
304
|
+
Push a tag matching `package.json` (e.g. `v1.1.0`). The release workflow runs the tests, publishes `@blanketbyte/wise-mcp` to npm with provenance (needs an `NPM_TOKEN` secret), lists it in the [official MCP Registry](https://registry.modelcontextprotocol.io) from `server.json`, and attaches the `.mcpb` to a GitHub release. Before the first release, remove `"private": true` from `package.json`.
|
|
305
|
+
|
|
306
|
+
<details>
|
|
307
|
+
<summary>Project layout</summary>
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
src/
|
|
311
|
+
cli.ts Entry point: `wise-mcp` (stdio) or `wise-mcp serve` (remote)
|
|
312
|
+
stdio.ts Local mode over stdin/stdout
|
|
313
|
+
index.ts Remote server: icons, OAuth routes, /login, /sessions, /mcp
|
|
314
|
+
config.ts Environment validation (shared and remote-only settings)
|
|
315
|
+
meta.ts Version, paths, links
|
|
316
|
+
crypto.ts AES-256-GCM sealing, cookie signing
|
|
317
|
+
tools.ts MCP tools (read + two-step write)
|
|
318
|
+
pending.ts Prepared actions and one-time confirmation codes
|
|
319
|
+
wise/client.ts Wise API client (retries, idempotent transfers, revocation detection)
|
|
320
|
+
wise/service.ts Profiles, balances, activities, transfers, recipients, rates
|
|
321
|
+
wise/connections.ts Encrypted per-token connections
|
|
322
|
+
auth/provider.ts OAuth 2.1 provider for Claude, token-paste login
|
|
323
|
+
auth/store.ts Clients, sessions, hashed tokens
|
|
324
|
+
auth/pages.ts Login, /sessions and message pages
|
|
325
|
+
public/ Logo, icons, favicon
|
|
326
|
+
mcpb/manifest.json Claude Desktop extension manifest
|
|
327
|
+
scripts/e2e.ts Remote end-to-end test with a mock Wise API
|
|
328
|
+
scripts/e2e-stdio.ts Local (stdio) end-to-end test
|
|
329
|
+
scripts/build-mcpb.sh Builds the .mcpb bundle
|
|
330
|
+
.github/workflows/ CI, and release to npm + GitHub
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
</details>
|
|
334
|
+
|
|
335
|
+
## Limitations
|
|
336
|
+
|
|
337
|
+
Personal API tokens can't use every Wise endpoint. In particular, for EU/UK accounts PSD2 rules block **balance statements** (so there's no running balance per transaction) and the **card list**. The activity feed covers card payments, so day-to-day spending questions work fine.
|
|
338
|
+
|
|
339
|
+
On the write side, personal tokens can't **fund** transfers for EU/UK accounts (you pay them in the Wise app, which this server relies on anyway), and Wise rejects API transfers on business accounts that have **payment approval rules**.
|
|
340
|
+
|
|
341
|
+
## Contributing
|
|
342
|
+
|
|
343
|
+
Issues and pull requests are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md). Please report security problems privately as described in [SECURITY.md](SECURITY.md).
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
|
|
347
|
+
[MIT](LICENSE) © [BlanketByte](https://blanketbyte.com)
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
<div align="center">
|
|
352
|
+
<img src="public/logo.svg" alt="" width="40" height="40">
|
|
353
|
+
<br>
|
|
354
|
+
<sub>Made by <a href="https://blanketbyte.com">BlanketByte</a>. Independent project, not affiliated with or endorsed by Wise.<br>Wise and the Wise flag are trademarks of Wise Payments Limited; the flag is shown unaltered to indicate compatibility.</sub>
|
|
355
|
+
</div>
|