@blanketbyte/wise-mcp 1.0.4 → 1.0.6

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.
@@ -183,7 +183,7 @@ function layout(title, body) {
183
183
  footer .ic { width:14px; height:14px; }
184
184
  footer a { color:var(--muted); }
185
185
  footer a.gh { display:inline-flex; align-items:center; gap:4px; }
186
- footer a.ver { color:var(--faint); text-decoration:none; font-variant-numeric:tabular-nums; }
186
+ footer a.ver { flex-basis:100%; text-align:center; margin-top:-2px; color:var(--faint); font-size:12px; text-decoration:none; font-variant-numeric:tabular-nums; }
187
187
  footer a.ver:hover { text-decoration:underline; }
188
188
  .dot { opacity:.5; }
189
189
  </style>
@@ -204,7 +204,7 @@ function card(inner, opts) {
204
204
  </div>
205
205
  ${inner}
206
206
  </div>
207
- <footer>${icon("lock")}<span>Token encrypted at rest</span><span class="dot">·</span><a href="/sessions">Manage sessions</a><span class="dot">·</span><a class="gh" href="${REPO_URL}" target="_blank" rel="noopener">${icon("github")}Source</a><span class="dot">·</span><a class="ver" href="${REPO_URL}/releases/tag/v${VERSION}" target="_blank" rel="noopener" title="Release notes">v${VERSION}</a></footer>
207
+ <footer>${icon("lock")}<span>Token encrypted at rest</span><span class="dot">·</span><a href="/sessions">Manage sessions</a><span class="dot">·</span><a class="gh" href="${REPO_URL}" target="_blank" rel="noopener">${icon("github")}Source</a><a class="ver" href="${REPO_URL}/releases/tag/v${VERSION}" target="_blank" rel="noopener" title="Release notes">v${VERSION}</a></footer>
208
208
  </main>`;
209
209
  }
210
210
  /** Red banner shown on every page served over plain HTTP (except localhost). */
package/dist/tools.js CHANGED
@@ -274,7 +274,15 @@ function registerWriteTools(server, wise, { sessionKey, connectionId }, safe) {
274
274
  (sourceCurrency !== recipient.currency ? `, rate ${quote.rate}` : "") +
275
275
  (details.reference ? `, reference "${details.reference}"` : "") +
276
276
  (quote.delivery ? `, ${stripHtml(quote.delivery)}` : "");
277
- const { code, expiresAt } = storePending(sessionKey, { kind: "transfer", profileId: profile.id, quoteId: quote.id, targetAccount: recipient.id, customerTransactionId: randomUUID(), details }, summary);
277
+ const { code, expiresAt } = storePending(sessionKey, {
278
+ kind: "transfer",
279
+ profileId: profile.id,
280
+ quoteId: quote.id,
281
+ targetAccount: recipient.id,
282
+ customerTransactionId: randomUUID(),
283
+ details,
284
+ expected: { amount: quote.source.amount, currency: sourceCurrency },
285
+ }, summary);
278
286
  await log("transfer_prepared", { code, summary });
279
287
  return ok({
280
288
  status: "awaiting_user_confirmation",
@@ -319,6 +327,27 @@ function registerWriteTools(server, wise, { sessionKey, connectionId }, safe) {
319
327
  try {
320
328
  if (a.kind === "transfer") {
321
329
  const t = await wise.createTransfer({ targetAccount: a.targetAccount, quoteId: a.quoteId, customerTransactionId: a.customerTransactionId, details: a.details });
330
+ // Wise has been seen creating a transfer for a different amount than its own quote. Never leave the
331
+ // user with a transfer they didn't approve: it's still unpaid, so cancel it and say what happened.
332
+ const got = { amount: Number(t.source.amount), currency: String(t.source.currency ?? "").toUpperCase() };
333
+ if (Math.abs(got.amount - a.expected.amount) > 0.005 || got.currency !== a.expected.currency.toUpperCase()) {
334
+ const cancelled = await wise.cancelTransfer(t.id).then((c) => c.status, (err) => `cancel failed: ${err instanceof Error ? err.message : err}`);
335
+ await log("transfer_amount_mismatch", {
336
+ code: confirmation_code,
337
+ summary: p.summary,
338
+ transfer_id: t.id,
339
+ quote_id: a.quoteId,
340
+ expected: a.expected,
341
+ got,
342
+ cancelled,
343
+ });
344
+ return fail(`Wise created transfer ${t.id} for ${money(got.amount, got.currency)} instead of the approved ` +
345
+ `${money(a.expected.amount, a.expected.currency)} (quote ${a.quoteId}). ` +
346
+ (cancelled === "cancelled"
347
+ ? "It was cancelled automatically, so nothing will be paid. "
348
+ : `Automatic cancellation didn't succeed (${cancelled}): tell the user to cancel it in the Wise app and NOT pay it. `) +
349
+ "Don't retry; tell the user this is a Wise-side mismatch.");
350
+ }
322
351
  await log("transfer_created", { code: confirmation_code, summary: p.summary, transfer_id: t.id, status: t.status });
323
352
  return ok({
324
353
  status: "created_awaiting_payment",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blanketbyte/wise-mcp",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "MCP server for Wise: read balances, card payments, transfers and recipients, and prepare transfers you approve twice. Runs locally (npx, for Claude, Codex, Cursor…) or as a remote connector for Claude and ChatGPT.",
5
5
  "mcpName": "io.github.BlanketByte/wise-mcp",
6
6
  "keywords": [