paisa-mcp 0.4.0 → 0.4.1

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/index.js CHANGED
@@ -53442,7 +53442,24 @@ async function initCrypto(client, pin, userId) {
53442
53442
  // ../../node_modules/.bun/@modelcontextprotocol+server@2.1.0/node_modules/@modelcontextprotocol/server/dist/index.mjs
53443
53443
  var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
53444
53444
 
53445
- // ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/http/status-codes.js
53445
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.6+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/interaction.js
53446
+ async function progress(ctx, done, total, message) {
53447
+ const token = ctx.mcp.mcpReq._meta?.progressToken;
53448
+ if (token === undefined)
53449
+ return;
53450
+ try {
53451
+ await ctx.mcp.mcpReq.notify({
53452
+ method: "notifications/progress",
53453
+ params: {
53454
+ progressToken: token,
53455
+ progress: done,
53456
+ ...total !== undefined ? { total } : {},
53457
+ ...message ? { message } : {}
53458
+ }
53459
+ });
53460
+ } catch {}
53461
+ }
53462
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.6+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/http/status-codes.js
53446
53463
  var STATUS_CODES = {
53447
53464
  400: "BAD_REQUEST",
53448
53465
  401: "UNAUTHORIZED",
@@ -53456,14 +53473,7 @@ var STATUS_CODES = {
53456
53473
  429: "RATE_LIMITED"
53457
53474
  };
53458
53475
 
53459
- // ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/server.js
53460
- var RAW = Symbol.for("signet.mcp.rawResult");
53461
- function toolResult(result) {
53462
- return Object.defineProperty({ ...result }, RAW, { value: true, enumerable: false });
53463
- }
53464
- function defineTool(def) {
53465
- return def;
53466
- }
53476
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.6+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/errors.js
53467
53477
  function isZodError(err) {
53468
53478
  return err instanceof Error && err.name === "ZodError" && Array.isArray(err.issues);
53469
53479
  }
@@ -53512,10 +53522,10 @@ function toolErrorResult(info) {
53512
53522
  };
53513
53523
  }
53514
53524
  var INTERNAL_ERROR2 = { code: "INTERNAL_ERROR", message: "Internal error" };
53515
- function report(opts, err, tool, c) {
53525
+ function report(opts, err, meta3, c) {
53516
53526
  let pending;
53517
53527
  try {
53518
- pending = opts.onError?.(err, { tool });
53528
+ pending = opts.onError?.(err, meta3);
53519
53529
  } catch {
53520
53530
  return;
53521
53531
  }
@@ -53526,9 +53536,162 @@ function report(opts, err, tool, c) {
53526
53536
  c?.executionCtx.waitUntil(settled);
53527
53537
  } catch {}
53528
53538
  }
53529
- function toResult(value, structured) {
53539
+
53540
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.6+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/primitives.js
53541
+ var RAW = Symbol.for("signet.mcp.rawResult");
53542
+ function protocolError(err, deps, meta3) {
53543
+ if (ProtocolError.isInstance(err))
53544
+ return err;
53545
+ const info = deps.options.mapError?.(err) ?? describeToolError(err);
53546
+ if (!info) {
53547
+ report(deps.options, err, meta3, deps.context.c);
53548
+ return new ProtocolError(ProtocolErrorCode.InternalError, "Internal error");
53549
+ }
53550
+ const status = isHttpException(err) ? err.status : isZodError(err) ? 400 : undefined;
53551
+ const code = status !== undefined && status >= 500 ? ProtocolErrorCode.InternalError : ProtocolErrorCode.InvalidParams;
53552
+ return new ProtocolError(code, `${info.code}: ${info.message}`, {
53553
+ success: false,
53554
+ error: info.message,
53555
+ code: info.code,
53556
+ ...info.details ? { details: info.details } : {}
53557
+ });
53558
+ }
53559
+ function checkScopes(scopes, token) {
53560
+ if (!scopes?.length)
53561
+ return;
53562
+ const missing = scopes.filter((s) => !token?.scopes.includes(s));
53563
+ if (!missing.length)
53564
+ return;
53565
+ const message = `This token is missing the scope: ${missing.join(" ")}. Request a new token with it and try again.`;
53566
+ throw new ProtocolError(ProtocolErrorCode.InvalidRequest, `insufficient_scope: ${message}`, {
53567
+ success: false,
53568
+ error: message,
53569
+ code: "insufficient_scope"
53570
+ });
53571
+ }
53572
+ function baseContext(mcp, deps) {
53573
+ const { c, token } = deps.context;
53574
+ return { signal: mcp.mcpReq.signal, mcp, ...c ? { c } : {}, ...token ? { token } : {} };
53575
+ }
53576
+ function toContents(uri, value, mimeType) {
53530
53577
  if (value !== null && typeof value === "object" && RAW in value)
53531
53578
  return value;
53579
+ if (typeof value === "string")
53580
+ return { contents: [{ uri: uri.href, mimeType: mimeType ?? "text/plain", text: value }] };
53581
+ if (value instanceof Uint8Array) {
53582
+ let bin = "";
53583
+ for (const b of value)
53584
+ bin += String.fromCharCode(b);
53585
+ return {
53586
+ contents: [
53587
+ { uri: uri.href, mimeType: mimeType ?? "application/octet-stream", blob: btoa(bin) }
53588
+ ]
53589
+ };
53590
+ }
53591
+ return {
53592
+ contents: [
53593
+ { uri: uri.href, mimeType: mimeType ?? "application/json", text: JSON.stringify(value) }
53594
+ ]
53595
+ };
53596
+ }
53597
+ function registerResources(server, resources, deps) {
53598
+ const register2 = server.registerResource.bind(server);
53599
+ for (const r of resources) {
53600
+ const meta3 = { tool: r.name, kind: "resource" };
53601
+ const ctxOf = (mcp) => ({
53602
+ resource: r.name,
53603
+ ...baseContext(mcp, deps)
53604
+ });
53605
+ const run = async (uri, variables, mcp) => {
53606
+ checkScopes(r.scopes, deps.context.token);
53607
+ let value;
53608
+ try {
53609
+ const read = r.read;
53610
+ value = await read(r.uri !== undefined ? { uri } : { uri, variables }, ctxOf(mcp));
53611
+ } catch (err) {
53612
+ throw protocolError(err, deps, meta3);
53613
+ }
53614
+ if (value === null || value === undefined)
53615
+ throw new ResourceNotFoundError(uri.href);
53616
+ if (isInputRequiredResult(value))
53617
+ return value;
53618
+ return toContents(uri, value, r.mimeType);
53619
+ };
53620
+ const config2 = {
53621
+ ...r.title ? { title: r.title } : {},
53622
+ ...r.description ? { description: r.description } : {},
53623
+ ...r.mimeType ? { mimeType: r.mimeType } : {}
53624
+ };
53625
+ if (r.uri !== undefined) {
53626
+ register2(r.name, r.uri, config2, (uri, mcp) => run(uri, {}, mcp));
53627
+ continue;
53628
+ }
53629
+ const list = r.list;
53630
+ const template = new ResourceTemplate(r.template, {
53631
+ list: list ? async (mcp) => {
53632
+ try {
53633
+ checkScopes(r.scopes, deps.context.token);
53634
+ } catch {
53635
+ return { resources: [] };
53636
+ }
53637
+ try {
53638
+ return { resources: await list(ctxOf(mcp)) };
53639
+ } catch (err) {
53640
+ throw protocolError(err, deps, meta3);
53641
+ }
53642
+ } : undefined
53643
+ });
53644
+ register2(r.name, template, config2, (uri, vars, mcp) => {
53645
+ const variables = {};
53646
+ for (const [k, v] of Object.entries(vars ?? {}))
53647
+ variables[k] = Array.isArray(v) ? v[0] ?? "" : v;
53648
+ return run(uri, variables, mcp);
53649
+ });
53650
+ }
53651
+ }
53652
+ function toPromptResult(value) {
53653
+ if (isInputRequiredResult(value))
53654
+ return value;
53655
+ if (typeof value === "string")
53656
+ return { messages: [{ role: "user", content: { type: "text", text: value } }] };
53657
+ if (Array.isArray(value))
53658
+ return {
53659
+ messages: value.map((m) => typeof m === "string" ? { role: "user", content: { type: "text", text: m } } : typeof m.content === "string" ? { role: m.role, content: { type: "text", text: m.content } } : m)
53660
+ };
53661
+ return value;
53662
+ }
53663
+ function registerPrompts(server, prompts, deps) {
53664
+ const register2 = server.registerPrompt.bind(server);
53665
+ for (const p of prompts) {
53666
+ const meta3 = { tool: p.name, kind: "prompt" };
53667
+ const run = async (args, mcp) => {
53668
+ checkScopes(p.scopes, deps.context.token);
53669
+ try {
53670
+ const render = p.render;
53671
+ return toPromptResult(await render(args, { prompt: p.name, ...baseContext(mcp, deps) }));
53672
+ } catch (err) {
53673
+ throw protocolError(err, deps, meta3);
53674
+ }
53675
+ };
53676
+ register2(p.name, {
53677
+ ...p.title ? { title: p.title } : {},
53678
+ ...p.description ? { description: p.description } : {},
53679
+ ...p.args ? { argsSchema: p.args } : {}
53680
+ }, p.args ? (args, mcp) => run(args, mcp) : (mcp) => run({}, mcp));
53681
+ }
53682
+ }
53683
+
53684
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.6+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/server.js
53685
+ var RAW2 = Symbol.for("signet.mcp.rawResult");
53686
+ function toolResult(result) {
53687
+ return Object.defineProperty({ ...result }, RAW2, { value: true, enumerable: false });
53688
+ }
53689
+ function defineTool(def) {
53690
+ return def;
53691
+ }
53692
+ function toResult(value, structured) {
53693
+ if (value !== null && typeof value === "object" && RAW2 in value)
53694
+ return value;
53532
53695
  if (isInputRequiredResult(value))
53533
53696
  return value;
53534
53697
  const text = typeof value === "string" ? value : JSON.stringify(value ?? null) ?? "null";
@@ -53541,7 +53704,11 @@ function toResult(value, structured) {
53541
53704
  }
53542
53705
  function createMcpServer(options) {
53543
53706
  const unlisted = new Set(options.tools.filter((t) => t.listed === false).map((t) => t.name));
53544
- const toolsCapability = { tools: { listChanged: false } };
53707
+ const toolsCapability = {
53708
+ tools: { listChanged: false },
53709
+ ...options.resources?.length ? { resources: { listChanged: false } } : {},
53710
+ ...options.prompts?.length ? { prompts: { listChanged: false } } : {}
53711
+ };
53545
53712
  const server = new McpServer({ name: options.name, version: options.version }, {
53546
53713
  ...unlisted.size ? {} : { capabilities: toolsCapability },
53547
53714
  ...options.instructions ? { instructions: options.instructions } : {}
@@ -53581,7 +53748,7 @@ function createMcpServer(options) {
53581
53748
  } catch {}
53582
53749
  const info = options.mapError?.(err) ?? describeToolError(err);
53583
53750
  if (!info)
53584
- report(options, err, tool.name, c);
53751
+ report(options, err, { tool: tool.name }, c);
53585
53752
  if (custom2)
53586
53753
  return custom2;
53587
53754
  return toolErrorResult(info ?? INTERNAL_ERROR2);
@@ -53597,6 +53764,11 @@ function createMcpServer(options) {
53597
53764
  restore?.();
53598
53765
  restore = undefined;
53599
53766
  }
53767
+ const deps = { options, context: { ...c ? { c } : {}, ...token ? { token } : {} } };
53768
+ if (options.resources?.length)
53769
+ registerResources(server, options.resources, deps);
53770
+ if (options.prompts?.length)
53771
+ registerPrompts(server, options.prompts, deps);
53600
53772
  return server;
53601
53773
  }
53602
53774
  function hideUnlisted(server, unlisted) {
@@ -53618,7 +53790,6 @@ function hideUnlisted(server, unlisted) {
53618
53790
  throw new Error("signet mcp: could not hide unlisted tools; @modelcontextprotocol/server no longer installs tools/list through setRequestHandler");
53619
53791
  };
53620
53792
  }
53621
-
53622
53793
  // src/annotations.ts
53623
53794
  var RO = { readOnlyHint: true, openWorldHint: false };
53624
53795
  var WI = {
@@ -53833,6 +54004,51 @@ var CONFIRMED_TOOLS = {
53833
54004
  set_encryption_mode: describeDeletes.encryptionMode,
53834
54005
  manage_debt: describeDeletes.settleDebt
53835
54006
  };
54007
+ var BULK_CONFIRM_THRESHOLD = 20;
54008
+ var sum = (rows, key) => Array.isArray(rows) ? rows.reduce((n, r) => n + (Number(r?.[key]) || 0), 0) : 0;
54009
+ var fromDryRun = (listKey, countKey) => async (args, dryRun) => {
54010
+ const r = await dryRun({ ...args, dryRun: true });
54011
+ return {
54012
+ n: sum(r?.[listKey], countKey),
54013
+ planHash: typeof r?.planHash === "string" ? r.planHash : undefined
54014
+ };
54015
+ };
54016
+ var BULK_CONFIRMED_TOOLS = {
54017
+ categorize_groups: fromDryRun("applied", "rows"),
54018
+ backfill_persons: fromDryRun("plan", "txnCount"),
54019
+ bulk_categorize_transactions: async (args) => ({
54020
+ n: Array.isArray(args.links) ? args.links.length : 0
54021
+ }),
54022
+ bulk_link_entity: async (args) => ({ n: Array.isArray(args.links) ? args.links.length : 0 })
54023
+ };
54024
+ var PLAN_CHANGED = {
54025
+ planChanged: true,
54026
+ message: "The transactions changed after they were counted, so nothing was written. Run it again to see the new count."
54027
+ };
54028
+ function withBulkConfirmation(tool, count, initializeCapabilities) {
54029
+ const run = tool.handler;
54030
+ const runPlanned = async (args, ctx, hash2) => {
54031
+ const result = await run(hash2 === undefined ? args : { ...args, expectedPlanHash: hash2 }, ctx);
54032
+ return result?.planChanged ? PLAN_CHANGED : result;
54033
+ };
54034
+ return {
54035
+ ...tool,
54036
+ handler: async (args, ctx) => {
54037
+ if (args.dryRun === true || !ctx?.mcp || !clientCanConfirm(ctx.mcp, initializeCapabilities))
54038
+ return run(args, ctx);
54039
+ const { n, planHash: hash2 } = await count(args, async (a) => run(a, ctx));
54040
+ if (n <= BULK_CONFIRM_THRESHOLD)
54041
+ return runPlanned(args, ctx, hash2);
54042
+ const message = `Update ${plural2(n, "transaction")} in one go?`;
54043
+ const outcome = confirmPlan(ctx.mcp, message, { tool: tool.name, args, n, planHash: hash2 }, initializeCapabilities);
54044
+ if (outcome.kind === "confirmed")
54045
+ return runPlanned(args, ctx, hash2);
54046
+ if (outcome.kind === "unsupported")
54047
+ return run(args, ctx);
54048
+ return outcome.result;
54049
+ }
54050
+ };
54051
+ }
53836
54052
  // src/lib/union-tool.ts
53837
54053
  function variant(input2, run) {
53838
54054
  return { input: input2, run };
@@ -54025,15 +54241,15 @@ var PROMPTS = [
54025
54241
  `)
54026
54242
  }
54027
54243
  ];
54028
- function registerPrompts(server) {
54244
+ function registerPrompts2(server2) {
54029
54245
  for (const p of PROMPTS) {
54030
54246
  const cb = (args) => ({
54031
54247
  messages: [{ role: "user", content: { type: "text", text: p.text(args) } }]
54032
54248
  });
54033
54249
  if (p.args) {
54034
- server.registerPrompt(p.name, { title: p.title, description: p.description, argsSchema: p.args }, (args) => cb(args));
54250
+ server2.registerPrompt(p.name, { title: p.title, description: p.description, argsSchema: p.args }, (args) => cb(args));
54035
54251
  } else {
54036
- server.registerPrompt(p.name, { title: p.title, description: p.description }, () => cb({}));
54252
+ server2.registerPrompt(p.name, { title: p.title, description: p.description }, () => cb({}));
54037
54253
  }
54038
54254
  }
54039
54255
  }
@@ -58808,6 +59024,12 @@ function parseAmount4(raw) {
58808
59024
  const num = parseFloat(cleaned);
58809
59025
  return Number.isNaN(num) ? 0 : Math.abs(num);
58810
59026
  }
59027
+ // src/lib/plan-hash.ts
59028
+ import { createHash as createHash2 } from "node:crypto";
59029
+ function planHash(ids) {
59030
+ return createHash2("sha256").update([...ids].sort().join(",")).digest("hex");
59031
+ }
59032
+
58811
59033
  // src/lib/txn-rows.ts
58812
59034
  function toTxnRow(r2) {
58813
59035
  return {
@@ -58976,6 +59198,20 @@ async function backfillPersons(client, crypto3, opts = {}) {
58976
59198
  else
58977
59199
  newGroups.push({ norm, displayName: g2.displayName, upiHandles });
58978
59200
  }
59201
+ const hash2 = planHash([...groups.values()].flatMap((g2) => g2.txnIds));
59202
+ if (!opts.dryRun && opts.expectedPlanHash !== undefined && opts.expectedPlanHash !== hash2) {
59203
+ return {
59204
+ dryRun: false,
59205
+ truncated,
59206
+ candidatesScanned: candidates.length,
59207
+ created: 0,
59208
+ reused: 0,
59209
+ linked: 0,
59210
+ plan,
59211
+ planHash: hash2,
59212
+ planChanged: true
59213
+ };
59214
+ }
58979
59215
  if (!opts.dryRun && newGroups.length > 0) {
58980
59216
  const bodies = await Promise.all(newGroups.map(async ({ displayName, upiHandles }) => {
58981
59217
  const fields = {
@@ -59009,6 +59245,7 @@ async function backfillPersons(client, crypto3, opts = {}) {
59009
59245
  const CHUNK = 25;
59010
59246
  for (let i2 = 0;i2 < links.length; i2 += CHUNK) {
59011
59247
  await client.post("/api/transactions/bulk-link-entity", { links: links.slice(i2, i2 + CHUNK) });
59248
+ await opts.onProgress?.(Math.min(i2 + CHUNK, links.length), links.length, "Linking transactions to people");
59012
59249
  }
59013
59250
  }
59014
59251
  return {
@@ -59018,7 +59255,8 @@ async function backfillPersons(client, crypto3, opts = {}) {
59018
59255
  created,
59019
59256
  reused,
59020
59257
  linked: links.length,
59021
- plan
59258
+ plan,
59259
+ planHash: hash2
59022
59260
  };
59023
59261
  }
59024
59262
 
@@ -59145,6 +59383,10 @@ async function applyGroupAssignments(client, crypto3, assignments, opts = {}) {
59145
59383
  plan.push({ group: g2, categoryId: cat.id, slug: cat.slug, learn: a2.learn !== false });
59146
59384
  }
59147
59385
  }
59386
+ result.planHash = planHash(plan.flatMap((p2) => p2.group.rows.map((r2) => r2.id)));
59387
+ if (!opts.dryRun && opts.expectedPlanHash !== undefined && opts.expectedPlanHash !== result.planHash) {
59388
+ return { ...result, planChanged: true };
59389
+ }
59148
59390
  if (opts.dryRun) {
59149
59391
  result.applied = plan.map((p2) => ({
59150
59392
  pattern: p2.group.pattern,
@@ -59232,7 +59474,11 @@ async function applyGroupAssignments(client, crypto3, assignments, opts = {}) {
59232
59474
  }
59233
59475
  if (result.rulesSaved > 0)
59234
59476
  await saveBag(client, bag, version2);
59477
+ const total = links.length + direct.reduce((n2, d2) => n2 + d2.ids.length, 0);
59478
+ let done = 0;
59235
59479
  await postLinks(client, links);
59480
+ done += links.length;
59481
+ await opts.onProgress?.(done, total, "Categorizing transactions");
59236
59482
  for (const { ids, categoryId } of direct) {
59237
59483
  const categoryType = catIndex.byId.get(categoryId)?.type;
59238
59484
  for (let i2 = 0;i2 < ids.length; i2 += 200) {
@@ -59241,11 +59487,20 @@ async function applyGroupAssignments(client, crypto3, assignments, opts = {}) {
59241
59487
  categoryId,
59242
59488
  ...categoryType ? { categoryType } : {}
59243
59489
  });
59490
+ done += Math.min(200, ids.length - i2);
59491
+ await opts.onProgress?.(done, total, "Categorizing transactions");
59244
59492
  }
59245
59493
  }
59246
59494
  return result;
59247
59495
  }
59248
59496
 
59497
+ // src/lib/progress.ts
59498
+ function progressFor(ctx) {
59499
+ if (!ctx?.mcp)
59500
+ return;
59501
+ return (done, total, message) => progress(ctx, done, total, message);
59502
+ }
59503
+
59249
59504
  // src/tools/transactions.ts
59250
59505
  function dedupeKey(date5, description, amount, type) {
59251
59506
  return `${date5}|${description}|${amount.toFixed(2)}|${type}`;
@@ -59293,6 +59548,7 @@ async function importTransactions(client, crypto3, opts) {
59293
59548
  transactions: rows.slice(i2, i2 + CHUNK)
59294
59549
  });
59295
59550
  imported += res.imported ?? 0;
59551
+ await opts.onProgress?.(Math.min(i2 + CHUNK, rows.length), rows.length, "Importing transactions");
59296
59552
  }
59297
59553
  return { imported, duplicatesSkipped, duplicatesFlagged };
59298
59554
  }
@@ -59473,17 +59729,27 @@ function transactionTools(client, crypto3) {
59473
59729
  endDate: exports_external.string().optional(),
59474
59730
  dryRun: exports_external.boolean().optional()
59475
59731
  }),
59476
- handler: ({ assignments, startDate, endDate, dryRun }) => applyGroupAssignments(client, crypto3, assignments, {
59477
- dryRun,
59478
- startDate,
59479
- endDate
59480
- })
59732
+ handler: (args, ctx) => {
59733
+ const { assignments, startDate, endDate, dryRun } = args;
59734
+ const { expectedPlanHash } = args;
59735
+ return applyGroupAssignments(client, crypto3, assignments, {
59736
+ dryRun,
59737
+ startDate,
59738
+ endDate,
59739
+ expectedPlanHash,
59740
+ onProgress: progressFor(ctx)
59741
+ });
59742
+ }
59481
59743
  }),
59482
59744
  defineTool({
59483
59745
  name: "backfill_persons",
59484
59746
  description: "Create persons from UPI/IMPS counterparties in unlinked transactions (deduped by name) and link those transactions to them. Idempotent. dryRun previews.",
59485
59747
  input: exports_external.object({ dryRun: exports_external.boolean().optional() }),
59486
- handler: ({ dryRun }) => backfillPersons(client, crypto3, { dryRun })
59748
+ handler: (args, ctx) => backfillPersons(client, crypto3, {
59749
+ onProgress: progressFor(ctx),
59750
+ dryRun: args.dryRun,
59751
+ expectedPlanHash: args.expectedPlanHash
59752
+ })
59487
59753
  }),
59488
59754
  defineTool({
59489
59755
  name: "update_transaction",
@@ -60210,7 +60476,7 @@ async function fetchReconciliationContext(client, accountId, start, end, budget
60210
60476
  const seen = new Set;
60211
60477
  return [...left, ...right].filter((r2) => r2.id == null ? true : !seen.has(r2.id) && seen.add(r2.id));
60212
60478
  }
60213
- var sum = (rows) => Math.round(rows.reduce((n2, r2) => n2 + r2.amount, 0));
60479
+ var sum2 = (rows) => Math.round(rows.reduce((n2, r2) => n2 + r2.amount, 0));
60214
60480
  async function runStatementImport(deps, args) {
60215
60481
  const { client, crypto: crypto3 } = deps;
60216
60482
  const accounts = (await client.get("/api/accounts")).data ?? [];
@@ -60252,8 +60518,8 @@ async function runStatementImport(deps, args) {
60252
60518
  conflictCount: result.conflicts.length,
60253
60519
  new: {
60254
60520
  n: fresh.length,
60255
- debits: { n: debits.length, total: sum(debits) },
60256
- credits: { n: credits.length, total: sum(credits) }
60521
+ debits: { n: debits.length, total: sum2(debits) },
60522
+ credits: { n: credits.length, total: sum2(credits) }
60257
60523
  }
60258
60524
  };
60259
60525
  if (!args.confirm) {
@@ -60273,6 +60539,7 @@ async function runStatementImport(deps, args) {
60273
60539
  return { ...summary, dryRun: false, imported: 0 };
60274
60540
  const savings = account.type !== "credit_card";
60275
60541
  const imported = await importTransactions(client, crypto3, {
60542
+ onProgress: deps.onProgress,
60276
60543
  accountId: account.id,
60277
60544
  source: savings ? "bank_statement" : "cc_statement",
60278
60545
  granularity: savings ? 1 : 2,
@@ -60321,7 +60588,13 @@ var statementSource = exports_external.enum([
60321
60588
  ]);
60322
60589
  var granularity = exports_external.number().int().min(1).max(5).describe("1=bank, 2=CC, 3=manual, 4=app export, 5=telegram/AI");
60323
60590
  function statementImportTools(client, crypto3) {
60324
- const pdf = (confirm) => variant(pdfInput, (args) => runStatementImport({ client, crypto: crypto3, readFile: readFileAsArrayBuffer, parsers: realParsers }, confirm ? { ...args, confirm: true } : args));
60591
+ const pdf = (confirm2) => variant(pdfInput, (args, ctx) => runStatementImport({
60592
+ client,
60593
+ crypto: crypto3,
60594
+ readFile: readFileAsArrayBuffer,
60595
+ parsers: realParsers,
60596
+ onProgress: progressFor(ctx)
60597
+ }, confirm2 ? { ...args, confirm: true } : args));
60325
60598
  const csvParse = variant(exports_external.object({ filePath: exports_external.string() }), async ({ filePath }) => {
60326
60599
  if (crypto3)
60327
60600
  return CSV_PRIVATE_MODE_ERROR;
@@ -60382,8 +60655,9 @@ function statementImportTools(client, crypto3) {
60382
60655
  description: exports_external.string(),
60383
60656
  referenceNumber: exports_external.string().optional()
60384
60657
  }))
60385
- }), async ({ accountId, source, granularity: granularity2, transactions: txns }) => {
60658
+ }), async ({ accountId, source, granularity: granularity2, transactions: txns }, ctx) => {
60386
60659
  const data = await importTransactions(client, crypto3, {
60660
+ onProgress: progressFor(ctx),
60387
60661
  accountId,
60388
60662
  source,
60389
60663
  granularity: granularity2,
@@ -60397,7 +60671,7 @@ function statementImportTools(client, crypto3) {
60397
60671
  source: statementSource,
60398
60672
  granularity,
60399
60673
  filePath: exports_external.string()
60400
- }), async ({ accountId, source, granularity: granularity2, filePath }) => {
60674
+ }), async ({ accountId, source, granularity: granularity2, filePath }, ctx) => {
60401
60675
  let txns;
60402
60676
  try {
60403
60677
  const raw = await readFile2(filePath, "utf8");
@@ -60414,6 +60688,7 @@ function statementImportTools(client, crypto3) {
60414
60688
  throw new Error(`Failed to read/parse ${filePath}: ${e2 instanceof Error ? e2.message : String(e2)}`);
60415
60689
  }
60416
60690
  const data = await importTransactions(client, crypto3, {
60691
+ onProgress: progressFor(ctx),
60417
60692
  accountId,
60418
60693
  source,
60419
60694
  granularity: granularity2,
@@ -60540,7 +60815,7 @@ async function runTool(tool, args, mcp) {
60540
60815
  function jsonContents(uri, value) {
60541
60816
  return { contents: [{ uri: uri.href, mimeType: JSON_MIME, text: JSON.stringify(value ?? null) }] };
60542
60817
  }
60543
- function registerResources(server, tools) {
60818
+ function registerResources2(server2, tools) {
60544
60819
  const byName = new Map(tools.map((t2) => [t2.name, t2]));
60545
60820
  const need = (name) => {
60546
60821
  const t2 = byName.get(name);
@@ -60550,13 +60825,13 @@ function registerResources(server, tools) {
60550
60825
  };
60551
60826
  for (const r2 of RESOURCES) {
60552
60827
  const tool = need(r2.tool);
60553
- server.registerResource(r2.name, r2.uri, { title: r2.title, description: r2.description, mimeType: JSON_MIME }, async (uri, mcp) => {
60828
+ server2.registerResource(r2.name, r2.uri, { title: r2.title, description: r2.description, mimeType: JSON_MIME }, async (uri, mcp) => {
60554
60829
  const value = scrubCiphertext(await runTool(tool, r2.args, mcp));
60555
60830
  return jsonContents(uri, r2.shape ? r2.shape(value) : value);
60556
60831
  });
60557
60832
  }
60558
60833
  const getTransaction = need("get_transaction");
60559
- server.registerResource("transaction", new ResourceTemplate(TRANSACTION_TEMPLATE, { list: undefined }), {
60834
+ server2.registerResource("transaction", new ResourceTemplate(TRANSACTION_TEMPLATE, { list: undefined }), {
60560
60835
  title: "Transaction",
60561
60836
  description: "One transaction by UUID (same as get_transaction; decrypted in private mode).",
60562
60837
  mimeType: JSON_MIME
@@ -60569,7 +60844,7 @@ function registerResources(server, tools) {
60569
60844
  // package.json
60570
60845
  var package_default = {
60571
60846
  name: "paisa-mcp",
60572
- version: "0.4.0",
60847
+ version: "0.4.1",
60573
60848
  repository: {
60574
60849
  type: "git",
60575
60850
  url: "git+https://github.com/nimit9/paisa.git",
@@ -60598,7 +60873,7 @@ var package_default = {
60598
60873
  "@modelcontextprotocol/server": "^2.1.0",
60599
60874
  "@nimit9/signet-ai": "^0.1.11",
60600
60875
  "@nimit9/signet-lib": "^0.1.15",
60601
- "@nimit9/signet-server": "^0.2.2",
60876
+ "@nimit9/signet-server": "^0.2.6",
60602
60877
  "@paisa/parsers": "workspace:*",
60603
60878
  "@paisa/reconciliation": "workspace:*",
60604
60879
  "@paisa/types": "workspace:*",
@@ -60629,22 +60904,26 @@ function buildServer(client, crypto3) {
60629
60904
  return;
60630
60905
  };
60631
60906
  const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto3)).map((t2) => {
60907
+ const caps = () => initializeCapabilities();
60632
60908
  const describe3 = CONFIRMED_TOOLS[t2.name];
60633
- return describe3 ? withConfirmation(t2, describe3, () => initializeCapabilities()) : t2;
60909
+ if (describe3)
60910
+ return withConfirmation(t2, describe3, caps);
60911
+ const count = BULK_CONFIRMED_TOOLS[t2.name];
60912
+ return count ? withBulkConfirmation(t2, count, caps) : t2;
60634
60913
  }));
60635
60914
  for (const t2 of tools)
60636
60915
  if (t2.input)
60637
60916
  withCompactJsonSchema(t2.input);
60638
- const server = createMcpServer({
60917
+ const server2 = createMcpServer({
60639
60918
  name: "paisa",
60640
60919
  version: VERSION2,
60641
60920
  tools,
60642
60921
  errorResult: paisaErrorResult
60643
60922
  });
60644
- initializeCapabilities = () => server.server.getClientCapabilities();
60645
- registerResources(server, tools);
60646
- registerPrompts(server);
60647
- return server;
60923
+ initializeCapabilities = () => server2.server.getClientCapabilities();
60924
+ registerResources2(server2, tools);
60925
+ registerPrompts2(server2);
60926
+ return server2;
60648
60927
  }
60649
60928
 
60650
60929
  // src/index.ts
@@ -60668,5 +60947,5 @@ main().catch((err) => {
60668
60947
  process.exit(1);
60669
60948
  });
60670
60949
 
60671
- //# debugId=F56B7BEACF2C363564756E2164756E21
60950
+ //# debugId=882108748FF621AF64756E2164756E21
60672
60951
  //# sourceMappingURL=index.js.map