@blanketbyte/wise-mcp 1.0.5 → 1.0.7

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/dist/tools.js CHANGED
@@ -272,9 +272,18 @@ function registerWriteTools(server, wise, { sessionKey, connectionId }, safe) {
272
272
  `${recipient.name} (${recipient.account ?? recipient.type}) receives ${money(quote.target.amount, recipient.currency)}` +
273
273
  (quote.fee ? `, fee ${money(quote.fee.amount, sourceCurrency)}` : "") +
274
274
  (sourceCurrency !== recipient.currency ? `, rate ${quote.rate}` : "") +
275
+ (quote.pay_in && quote.pay_in !== "BALANCE" ? `, priced for paying by ${quote.pay_in}` : "") +
275
276
  (details.reference ? `, reference "${details.reference}"` : "") +
276
277
  (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);
278
+ const { code, expiresAt } = storePending(sessionKey, {
279
+ kind: "transfer",
280
+ profileId: profile.id,
281
+ quoteId: quote.id,
282
+ targetAccount: recipient.id,
283
+ customerTransactionId: randomUUID(),
284
+ details,
285
+ expected: { amount: quote.target.amount, currency: recipient.currency },
286
+ }, summary);
278
287
  await log("transfer_prepared", { code, summary });
279
288
  return ok({
280
289
  status: "awaiting_user_confirmation",
@@ -282,7 +291,7 @@ function registerWriteTools(server, wise, { sessionKey, connectionId }, safe) {
282
291
  quote,
283
292
  balance_check: available >= quote.source.amount
284
293
  ? `OK: ${money(available, sourceCurrency)} available`
285
- : `Not enough ${sourceCurrency} balance (${money(available, sourceCurrency)}); the user can still pay another way in the Wise app`,
294
+ : `Not enough ${sourceCurrency} balance (${money(available, sourceCurrency)}): the price assumes paying from the Wise balance, so tell the user to top it up first (paying another way in the app can cost more)`,
286
295
  confirmation_code: code,
287
296
  expires_at: expiresAt.toISOString(),
288
297
  next_step: "Show the summary. Only call confirm_action if the user explicitly approves. Afterwards they pay it in the Wise app.",
@@ -319,6 +328,28 @@ function registerWriteTools(server, wise, { sessionKey, connectionId }, safe) {
319
328
  try {
320
329
  if (a.kind === "transfer") {
321
330
  const t = await wise.createTransfer({ targetAccount: a.targetAccount, quoteId: a.quoteId, customerTransactionId: a.customerTransactionId, details: a.details });
331
+ // Never leave the user with a transfer they didn't approve (e.g. priced with another pay-in and a different
332
+ // fee): it's still unpaid, so cancel it and say what happened. Wise's transfer source amount excludes the
333
+ // fee, so compare what the recipient gets.
334
+ const got = { amount: Number(t.target.amount), currency: String(t.target.currency ?? "").toUpperCase() };
335
+ if (Math.abs(got.amount - a.expected.amount) > 0.005 || got.currency !== a.expected.currency.toUpperCase()) {
336
+ const cancelled = await wise.cancelTransfer(t.id).then((c) => c.status, (err) => `cancel failed: ${err instanceof Error ? err.message : err}`);
337
+ await log("transfer_amount_mismatch", {
338
+ code: confirmation_code,
339
+ summary: p.summary,
340
+ transfer_id: t.id,
341
+ quote_id: a.quoteId,
342
+ expected: a.expected,
343
+ got,
344
+ cancelled,
345
+ });
346
+ return fail(`Wise created transfer ${t.id} where the recipient gets ${money(got.amount, got.currency)} instead of the approved ` +
347
+ `${money(a.expected.amount, a.expected.currency)} (quote ${a.quoteId}), so the price differs from the summary. ` +
348
+ (cancelled === "cancelled"
349
+ ? "It was cancelled automatically, so nothing will be paid. "
350
+ : `Automatic cancellation didn't succeed (${cancelled}): tell the user to cancel it in the Wise app and NOT pay it. `) +
351
+ "Don't retry; tell the user.");
352
+ }
322
353
  await log("transfer_created", { code: confirmation_code, summary: p.summary, transfer_id: t.id, status: t.status });
323
354
  return ok({
324
355
  status: "created_awaiting_payment",
@@ -220,6 +220,9 @@ export class WiseService {
220
220
  sourceAmount: q.sourceAmount ?? null,
221
221
  targetAmount: q.targetAmount ?? null,
222
222
  targetAccount: q.targetAccount,
223
+ // Without this Wise prices the transfer with its default pay-in (often a bank transfer, with a fee), even
224
+ // though the user pays from their Wise balance in the app. BALANCE goes first and the transfer uses it.
225
+ preferredPayIn: "BALANCE",
223
226
  });
224
227
  return mapQuote(raw);
225
228
  }
@@ -319,8 +322,9 @@ function mapRecipient(r) {
319
322
  }
320
323
  function mapQuote(q) {
321
324
  const options = (q.paymentOptions ?? []).filter((o) => !o.disabled && (!q.payOut || o.payOut === q.payOut));
322
- // You pay in the Wise app, usually from your balance: show that option's price.
323
- const o = options.find((x) => x.payIn === "BALANCE") ?? options[0];
325
+ // Show the price of the pay-in Wise will use for the transfer: the quote's own payIn, otherwise the first option
326
+ // (createQuote asks for BALANCE first). Showing another option's price once hid a 0.95 EUR fee.
327
+ const o = options.find((x) => q.payIn && x.payIn === q.payIn) ?? options[0];
324
328
  return {
325
329
  id: String(q.id),
326
330
  source: { amount: o?.sourceAmount ?? q.sourceAmount, currency: q.sourceCurrency },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blanketbyte/wise-mcp",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
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": [