paisa-mcp 0.3.0 → 0.4.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/dist/index.js CHANGED
@@ -38033,6 +38033,44 @@ var inputRequired = Object.assign(buildInputRequired, {
38033
38033
  return { method: "roots/list" };
38034
38034
  }
38035
38035
  });
38036
+ function acceptedContent(responses, key, schema) {
38037
+ const view = inputResponse(responses, key);
38038
+ if (view.kind !== "elicit" || view.action !== "accept" || view.content === undefined)
38039
+ return;
38040
+ if (schema === undefined)
38041
+ return view.content;
38042
+ const outcome = schema["~standard"].validate(view.content);
38043
+ if (outcome instanceof Promise)
38044
+ throw new TypeError("acceptedContent(responses, key, schema) requires a synchronously-validating schema");
38045
+ return outcome.issues === undefined ? outcome.value : undefined;
38046
+ }
38047
+ function inputResponse(responses, key) {
38048
+ if (responses === undefined || typeof responses !== "object" || responses === null)
38049
+ return { kind: "missing" };
38050
+ const entry = responses[key];
38051
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry))
38052
+ return { kind: "missing" };
38053
+ const candidate = entry;
38054
+ if (candidate["action"] === "accept" || candidate["action"] === "decline" || candidate["action"] === "cancel") {
38055
+ const content = candidate["content"];
38056
+ return {
38057
+ kind: "elicit",
38058
+ action: candidate["action"],
38059
+ ...content !== null && typeof content === "object" && !Array.isArray(content) && { content }
38060
+ };
38061
+ }
38062
+ if (Array.isArray(candidate["roots"]))
38063
+ return {
38064
+ kind: "roots",
38065
+ roots: candidate["roots"]
38066
+ };
38067
+ if (typeof candidate["role"] === "string" && candidate["content"] !== undefined)
38068
+ return {
38069
+ kind: "sampling",
38070
+ result: candidate
38071
+ };
38072
+ return { kind: "missing" };
38073
+ }
38036
38074
  var REQUEST_STATE_ONLY_LEG_PACING_MS = 250;
38037
38075
  function inputRequiredRoundsExceededMessage(method, maxRounds) {
38038
38076
  return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
@@ -53145,6 +53183,90 @@ class PaisaApiClient {
53145
53183
  }
53146
53184
  }
53147
53185
 
53186
+ // ../../node_modules/.bun/@nimit9+signet-lib@0.1.15+d40099021b198cb0/node_modules/@nimit9/signet-lib/dist/crypto/index.js
53187
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
53188
+ async function decryptFields(input2, paths, { decrypt, isCiphertext }) {
53189
+ const undecrypted = [];
53190
+ const pending = [];
53191
+ const looksEncrypted = (value, path) => {
53192
+ if (!isCiphertext)
53193
+ return true;
53194
+ try {
53195
+ return isCiphertext(value, path) !== false;
53196
+ } catch {
53197
+ return true;
53198
+ }
53199
+ };
53200
+ const decryptInto = (target, key, value, path) => {
53201
+ pending.push((async () => {
53202
+ try {
53203
+ const plain = await decrypt(value, path);
53204
+ if (typeof plain !== "string")
53205
+ throw new TypeError("decrypt returned a non-string");
53206
+ target[key] = plain;
53207
+ } catch {
53208
+ target[key] = null;
53209
+ undecrypted.push(path);
53210
+ }
53211
+ })());
53212
+ };
53213
+ const copies = new Map;
53214
+ const mine = new WeakSet;
53215
+ const copyOf = (v) => {
53216
+ if (mine.has(v))
53217
+ return v;
53218
+ let c = copies.get(v);
53219
+ if (!c) {
53220
+ c = Array.isArray(v) ? [...v] : { ...v };
53221
+ copies.set(v, c);
53222
+ mine.add(c);
53223
+ }
53224
+ return c;
53225
+ };
53226
+ const walk = (node2, keys, at, set2) => {
53227
+ if (Array.isArray(node2)) {
53228
+ const arr = copyOf(node2);
53229
+ set2(arr);
53230
+ arr.forEach((item, i) => {
53231
+ walk(item, keys, at ? `${at}.${i}` : String(i), (v) => {
53232
+ arr[i] = v;
53233
+ });
53234
+ });
53235
+ return;
53236
+ }
53237
+ if (!isObj(node2) || keys.length === 0)
53238
+ return;
53239
+ const [key, ...rest] = keys;
53240
+ if (!Object.hasOwn(node2, key))
53241
+ return;
53242
+ const obj = copyOf(node2);
53243
+ set2(obj);
53244
+ const path = at ? `${at}.${key}` : key;
53245
+ const value = obj[key];
53246
+ if (rest.length > 0) {
53247
+ walk(value, rest, path, (v) => {
53248
+ obj[key] = v;
53249
+ });
53250
+ return;
53251
+ }
53252
+ if (typeof value === "string" && value !== "" && looksEncrypted(value, path)) {
53253
+ decryptInto(obj, key, value, path);
53254
+ }
53255
+ };
53256
+ let data = input2;
53257
+ for (const p of paths) {
53258
+ const keys = p.split(".").filter(Boolean);
53259
+ if (keys.length === 0)
53260
+ continue;
53261
+ walk(data, keys, "", (v) => {
53262
+ data = v;
53263
+ });
53264
+ }
53265
+ await Promise.all(pending);
53266
+ undecrypted.sort();
53267
+ return { data, undecrypted };
53268
+ }
53269
+
53148
53270
  // src/crypto.ts
53149
53271
  var import_hash_wasm = __toESM(require_index_umd(), 1);
53150
53272
  var ENCRYPTED_TXN_FIELDS = ["description", "referenceNumber", "notes"];
@@ -53207,16 +53329,6 @@ async function decryptField(encrypted, key) {
53207
53329
  const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
53208
53330
  return new TextDecoder().decode(plaintext);
53209
53331
  }
53210
- async function decryptObjectFields(obj, fields, key) {
53211
- for (const field of fields) {
53212
- const val = obj[field];
53213
- if (typeof val !== "string" || !val)
53214
- continue;
53215
- try {
53216
- obj[field] = await decryptField(val, key);
53217
- } catch {}
53218
- }
53219
- }
53220
53332
  async function encryptObjectFields(obj, fields, key) {
53221
53333
  for (const field of fields) {
53222
53334
  const val = obj[field];
@@ -53225,25 +53337,55 @@ async function encryptObjectFields(obj, fields, key) {
53225
53337
  obj[field] = await encryptField(val, key);
53226
53338
  }
53227
53339
  }
53340
+ var CIPHERTEXT_CHARSET_RE = /^[A-Za-z0-9+/]+={0,2}$/;
53341
+ var MIN_CIPHERTEXT_B64_LEN = 40;
53342
+ function looksLikeCiphertext(value) {
53343
+ return value.length >= MIN_CIPHERTEXT_B64_LEN && CIPHERTEXT_CHARSET_RE.test(value);
53344
+ }
53345
+ async function decryptFieldsFailClosed(input2, paths, key) {
53346
+ return decryptFields(input2, paths, {
53347
+ decrypt: (ciphertext) => decryptField(ciphertext, key),
53348
+ isCiphertext: (value) => looksLikeCiphertext(value)
53349
+ });
53350
+ }
53351
+ var TXN_DECRYPT_PATHS = [
53352
+ ...ENCRYPTED_TXN_FIELDS,
53353
+ ...ENCRYPTED_MERCHANT_FIELDS.map((f) => `merchant.${f}`)
53354
+ ];
53228
53355
  async function decryptTransactionFields(items, key) {
53229
- for (const item of items) {
53230
- await decryptObjectFields(item, ENCRYPTED_TXN_FIELDS, key);
53231
- const merchant = item.merchant;
53232
- if (merchant && typeof merchant === "object") {
53233
- await decryptObjectFields(merchant, ENCRYPTED_MERCHANT_FIELDS, key);
53234
- }
53235
- }
53356
+ return decryptFieldsFailClosed(items, TXN_DECRYPT_PATHS, key);
53236
53357
  }
53237
53358
  async function decryptMerchantFields(items, key) {
53238
- for (const item of items) {
53239
- await decryptObjectFields(item, ENCRYPTED_MERCHANT_FIELDS, key);
53240
- }
53359
+ return decryptFieldsFailClosed(items, ENCRYPTED_MERCHANT_FIELDS, key);
53241
53360
  }
53242
53361
  async function encryptTransactionFields(items, key) {
53243
53362
  for (const item of items) {
53244
53363
  await encryptObjectFields(item, ENCRYPTED_TXN_FIELDS, key);
53245
53364
  }
53246
53365
  }
53366
+ async function decryptPersonCiphers(input2, key) {
53367
+ const { data, undecrypted } = await decryptFields(input2, ["cipher"], {
53368
+ decrypt: async (ciphertext) => {
53369
+ const json2 = await decryptField(ciphertext, key);
53370
+ JSON.parse(json2);
53371
+ return json2;
53372
+ }
53373
+ });
53374
+ const merge3 = (row) => {
53375
+ if (!row || typeof row !== "object")
53376
+ return;
53377
+ const r = row;
53378
+ if (typeof r.cipher !== "string")
53379
+ return;
53380
+ const fields = JSON.parse(r.cipher);
53381
+ Object.assign(r, fields, { cipher: null });
53382
+ };
53383
+ if (Array.isArray(data))
53384
+ data.forEach(merge3);
53385
+ else
53386
+ merge3(data);
53387
+ return { data, undecrypted };
53388
+ }
53247
53389
  var VERIFIER_PLAINTEXT = "paisa-verify-v1";
53248
53390
  async function checkVerifier(key, encryptedVerifier) {
53249
53391
  try {
@@ -53552,6 +53694,145 @@ function applyAnnotations(tools) {
53552
53694
  return { ...tool, title: meta3.title, annotations: meta3.annotations };
53553
53695
  });
53554
53696
  }
53697
+
53698
+ // src/lib/confirm.ts
53699
+ import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
53700
+ var ANSWER_KEY = "confirm";
53701
+ function clientCanConfirm(mcp, initializeCapabilities) {
53702
+ const envelope = mcp.mcpReq.envelope;
53703
+ const caps = envelope?.[CLIENT_CAPABILITIES_META_KEY] ?? initializeCapabilities();
53704
+ return Boolean(caps?.elicitation);
53705
+ }
53706
+ function planFingerprint(plan) {
53707
+ const canonical = (v) => Array.isArray(v) ? v.map(canonical) : v && typeof v === "object" ? Object.fromEntries(Object.entries(v).filter(([, x]) => x !== undefined).sort(([a], [b]) => a.localeCompare(b)).map(([k, x]) => [k, canonical(x)])) : v;
53708
+ return createHash("sha256").update(JSON.stringify(canonical(plan))).digest("hex");
53709
+ }
53710
+ var STATE_KEY = randomBytes(32);
53711
+ function sign(nonce, fingerprint) {
53712
+ return createHmac("sha256", STATE_KEY).update(`${nonce}.${fingerprint}`).digest("hex");
53713
+ }
53714
+ function mintState(fingerprint) {
53715
+ const nonce = randomBytes(16).toString("hex");
53716
+ return `${nonce}.${sign(nonce, fingerprint)}`;
53717
+ }
53718
+ function verifyState(state, fingerprint) {
53719
+ if (typeof state !== "string")
53720
+ return false;
53721
+ const [nonce, mac3, ...rest] = state.split(".");
53722
+ if (!nonce || !mac3 || rest.length)
53723
+ return false;
53724
+ const expected = Buffer.from(sign(nonce, fingerprint), "hex");
53725
+ const given = Buffer.from(mac3, "hex");
53726
+ return given.length === expected.length && timingSafeEqual(given, expected);
53727
+ }
53728
+ function confirmPlan(mcp, message, plan, initializeCapabilities = () => {
53729
+ return;
53730
+ }) {
53731
+ if (!clientCanConfirm(mcp, initializeCapabilities))
53732
+ return { kind: "unsupported" };
53733
+ const fingerprint = planFingerprint(plan);
53734
+ const responses = mcp.mcpReq.inputResponses;
53735
+ const answered = responses !== undefined && ANSWER_KEY in responses;
53736
+ const echoed = mcp.mcpReq.requestState?.();
53737
+ if (answered && verifyState(echoed, fingerprint)) {
53738
+ const content = acceptedContent(responses, ANSWER_KEY);
53739
+ if (content?.confirm === true)
53740
+ return { kind: "confirmed" };
53741
+ return {
53742
+ kind: "declined",
53743
+ result: { cancelled: true, message: "Cancelled by the user. Nothing was changed." }
53744
+ };
53745
+ }
53746
+ return {
53747
+ kind: "ask",
53748
+ result: inputRequired({
53749
+ inputRequests: {
53750
+ [ANSWER_KEY]: inputRequired.elicit({
53751
+ message,
53752
+ requestedSchema: {
53753
+ type: "object",
53754
+ properties: {
53755
+ confirm: { type: "boolean", title: "Confirm", description: "This can't be undone." }
53756
+ },
53757
+ required: ["confirm"]
53758
+ }
53759
+ })
53760
+ },
53761
+ requestState: mintState(fingerprint)
53762
+ })
53763
+ };
53764
+ }
53765
+ function withConfirmation(tool, describe3, initializeCapabilities) {
53766
+ const run = tool.handler;
53767
+ return {
53768
+ ...tool,
53769
+ handler: (args, ctx) => {
53770
+ const message = describe3(args);
53771
+ if (message === null || !ctx?.mcp)
53772
+ return run(args, ctx);
53773
+ const outcome = confirmPlan(ctx.mcp, message, { tool: tool.name, args }, initializeCapabilities);
53774
+ if (outcome.kind === "confirmed" || outcome.kind === "unsupported")
53775
+ return run(args, ctx);
53776
+ return outcome.result;
53777
+ }
53778
+ };
53779
+ }
53780
+ var safeDate = (v) => typeof v === "string" && /^\d{4}-\d{2}-\d{2}$/.test(v) ? v : v === undefined ? "any" : "a date";
53781
+ var safeAmount = (v) => typeof v === "string" && /^\d+(\.\d{1,2})?$/.test(v) || typeof v === "number" ? String(v) : null;
53782
+ var plural2 = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
53783
+ function describeTransactionFilters(f) {
53784
+ const parts = [];
53785
+ if (f.startDate || f.endDate)
53786
+ parts.push(`dated ${safeDate(f.startDate)} to ${safeDate(f.endDate)}`);
53787
+ if (f.type)
53788
+ parts.push(`${f.type}s only`);
53789
+ if (f.categoryType)
53790
+ parts.push(`category type ${f.categoryType}`);
53791
+ if (f.categoryId)
53792
+ parts.push("in one category");
53793
+ if (f.uncategorized)
53794
+ parts.push("uncategorized");
53795
+ if (f.amountMin !== undefined || f.amountMax !== undefined)
53796
+ parts.push(`amount ₹${f.amountMin ?? 0} to ${f.amountMax === undefined ? "any" : `₹${f.amountMax}`}`);
53797
+ if (f.owner)
53798
+ parts.push("one owner");
53799
+ if (f.search)
53800
+ parts.push("matching a text search");
53801
+ return parts.length ? parts.join(", ") : "with no filters (ALL transactions)";
53802
+ }
53803
+ var describeDeletes = {
53804
+ transactions: (a) => {
53805
+ if (a.id)
53806
+ return "Permanently delete 1 transaction?";
53807
+ if (Array.isArray(a.ids))
53808
+ return `Permanently delete ${plural2(a.ids.length, "transaction")}?`;
53809
+ return `Permanently delete every transaction ${describeTransactionFilters(a.filters ?? {})}?`;
53810
+ },
53811
+ entity: (a) => {
53812
+ const entity = String(a.entity ?? "item").replace(/_/g, " ");
53813
+ const n = Array.isArray(a.ids) ? a.ids.length : 1;
53814
+ const extra = a.entity === "merchant" ? " Their transactions are kept but unlinked." : a.entity === "debt" ? " Its history is lost; settling keeps it." : "";
53815
+ return `Permanently delete ${plural2(n, entity)}?${extra}`;
53816
+ },
53817
+ mergeMerchants: (a) => {
53818
+ const n = Array.isArray(a.duplicateIds) ? a.duplicateIds.length : 0;
53819
+ return `Merge ${plural2(n, "duplicate merchant")} into the one you're keeping? Their transactions move over and the duplicates are deleted.`;
53820
+ },
53821
+ encryptionMode: (a) => `Switch encryption mode to ${a.encryptionMode === "private" ? "private" : "standard"}?`,
53822
+ settleDebt: (a) => {
53823
+ if (a.action !== "settle")
53824
+ return null;
53825
+ const amount = a.settledAmount === undefined ? "0" : safeAmount(a.settledAmount);
53826
+ return amount === null ? "Settle this debt? It will be closed with the remaining balance you gave." : `Settle this debt? It will be closed with a remaining balance of ₹${amount}.`;
53827
+ }
53828
+ };
53829
+ var CONFIRMED_TOOLS = {
53830
+ delete_transactions: describeDeletes.transactions,
53831
+ delete_entity: describeDeletes.entity,
53832
+ merge_merchants: describeDeletes.mergeMerchants,
53833
+ set_encryption_mode: describeDeletes.encryptionMode,
53834
+ manage_debt: describeDeletes.settleDebt
53835
+ };
53555
53836
  // src/lib/union-tool.ts
53556
53837
  function variant(input2, run) {
53557
53838
  return { input: input2, run };
@@ -53862,7 +54143,8 @@ var dashboardOutputSchema = exports_external.object({
53862
54143
  recentTransactions: exports_external.array(exports_external.record(exports_external.string(), exports_external.unknown())),
53863
54144
  netWorth: exports_external.record(exports_external.string(), exports_external.unknown()).nullable(),
53864
54145
  debtSummary: exports_external.object({ totalOutstanding: exports_external.number(), totalEmi: exports_external.number(), count: exports_external.number() }).nullable(),
53865
- budgetVsActual: exports_external.array(budgetVsActualEntrySchema)
54146
+ budgetVsActual: exports_external.array(budgetVsActualEntrySchema),
54147
+ undecrypted: exports_external.array(exports_external.string()).optional()
53866
54148
  });
53867
54149
  var spendingTrendsSchema = exports_external.object({
53868
54150
  success: exports_external.literal(true),
@@ -53897,7 +54179,8 @@ var spendingTrendsSchema = exports_external.object({
53897
54179
  total: exports_external.number(),
53898
54180
  count: exports_external.number()
53899
54181
  })),
53900
- budgetVsActual: exports_external.array(budgetVsActualEntrySchema)
54182
+ budgetVsActual: exports_external.array(budgetVsActualEntrySchema),
54183
+ undecrypted: exports_external.array(exports_external.string()).optional()
53901
54184
  });
53902
54185
  var categoryAnalyticsSchema = exports_external.object({
53903
54186
  success: exports_external.literal(true),
@@ -53912,7 +54195,7 @@ var categoryAnalyticsSchema = exports_external.object({
53912
54195
  recentTransactions: exports_external.array(exports_external.object({
53913
54196
  id: exports_external.string(),
53914
54197
  date: exports_external.string(),
53915
- description: exports_external.string(),
54198
+ description: exports_external.string().nullable(),
53916
54199
  amount: exports_external.number(),
53917
54200
  type: exports_external.string(),
53918
54201
  owner: exports_external.string(),
@@ -53926,7 +54209,8 @@ var categoryAnalyticsSchema = exports_external.object({
53926
54209
  total: exports_external.number(),
53927
54210
  count: exports_external.number()
53928
54211
  })),
53929
- stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() })
54212
+ stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() }),
54213
+ undecrypted: exports_external.array(exports_external.string()).optional()
53930
54214
  });
53931
54215
  var merchantAnalyticsSchema = exports_external.object({
53932
54216
  success: exports_external.literal(true),
@@ -53941,7 +54225,7 @@ var merchantAnalyticsSchema = exports_external.object({
53941
54225
  recentTransactions: exports_external.array(exports_external.object({
53942
54226
  id: exports_external.string(),
53943
54227
  date: exports_external.string(),
53944
- description: exports_external.string(),
54228
+ description: exports_external.string().nullable(),
53945
54229
  amount: exports_external.number(),
53946
54230
  type: exports_external.string(),
53947
54231
  owner: exports_external.string(),
@@ -53956,7 +54240,8 @@ var merchantAnalyticsSchema = exports_external.object({
53956
54240
  total: exports_external.number(),
53957
54241
  count: exports_external.number()
53958
54242
  })),
53959
- stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() })
54243
+ stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() }),
54244
+ undecrypted: exports_external.array(exports_external.string()).optional()
53960
54245
  });
53961
54246
  var analyticsOutputSchema = exports_external.union([
53962
54247
  spendingTrendsSchema,
@@ -53965,37 +54250,17 @@ var analyticsOutputSchema = exports_external.union([
53965
54250
  ]);
53966
54251
 
53967
54252
  // src/tools/analytics.ts
53968
- async function dec(value, key) {
53969
- if (typeof value !== "string" || !value)
53970
- return value;
53971
- try {
53972
- return await decryptField(value, key);
53973
- } catch {
53974
- return value;
53975
- }
53976
- }
53977
54253
  async function decryptAnalytics(data, key) {
53978
54254
  if (!data || typeof data !== "object")
53979
- return;
53980
- const d = data;
53981
- const topMerchants = d.topMerchants;
53982
- if (Array.isArray(topMerchants)) {
53983
- for (const m of topMerchants)
53984
- m.name = await dec(m.name, key);
53985
- }
53986
- const recent = d.recentTransactions;
53987
- if (Array.isArray(recent)) {
53988
- for (const t of recent) {
53989
- t.description = await dec(t.description, key);
53990
- if ("merchantName" in t)
53991
- t.merchantName = await dec(t.merchantName, key);
53992
- }
53993
- }
53994
- const merchant = d.merchant;
53995
- if (merchant && typeof merchant === "object") {
53996
- merchant.cleanName = await dec(merchant.cleanName, key);
53997
- merchant.rawId = await dec(merchant.rawId, key);
53998
- }
54255
+ return data;
54256
+ const { data: decrypted, undecrypted } = await decryptFieldsFailClosed(data, [
54257
+ "topMerchants.name",
54258
+ "recentTransactions.description",
54259
+ "recentTransactions.merchantName",
54260
+ "merchant.cleanName",
54261
+ "merchant.rawId"
54262
+ ], key);
54263
+ return undecrypted.length ? { ...decrypted, undecrypted } : decrypted;
53999
54264
  }
54000
54265
  function analyticsTools(client, crypto3) {
54001
54266
  return [
@@ -54023,7 +54288,7 @@ function analyticsTools(client, crypto3) {
54023
54288
  break;
54024
54289
  }
54025
54290
  if (crypto3)
54026
- await decryptAnalytics(data, crypto3.key);
54291
+ data = await decryptAnalytics(data, crypto3.key);
54027
54292
  return data;
54028
54293
  }
54029
54294
  })
@@ -54091,7 +54356,10 @@ function dashboardTools(client, crypto3) {
54091
54356
  handler: async (params) => {
54092
54357
  const data = await client.get("/api/dashboard", params.months ? { months: params.months } : undefined);
54093
54358
  if (crypto3 && Array.isArray(data?.recentTransactions)) {
54094
- await decryptTransactionFields(data.recentTransactions, crypto3.key);
54359
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.recentTransactions, crypto3.key);
54360
+ data.recentTransactions = decrypted;
54361
+ if (undecrypted.length)
54362
+ return { ...data, undecrypted };
54095
54363
  }
54096
54364
  return data;
54097
54365
  }
@@ -54643,13 +54911,17 @@ function merchantVariants(client, crypto3) {
54643
54911
  return {
54644
54912
  list: variant(exports_external.object({ rawId: exports_external.string().optional().describe("merchants: find one by UPI ID or name") }), async ({ rawId }) => {
54645
54913
  const data = await client.get("/api/merchants");
54646
- if (crypto3 && data.data)
54647
- await decryptMerchantFields(data.data, crypto3.key);
54914
+ let undecrypted = [];
54915
+ if (crypto3 && data.data) {
54916
+ const dec = await decryptMerchantFields(data.data, crypto3.key);
54917
+ data.data = dec.data;
54918
+ undecrypted = dec.undecrypted;
54919
+ }
54648
54920
  if (!rawId)
54649
- return data;
54921
+ return undecrypted.length ? { ...data, undecrypted } : data;
54650
54922
  const needle = rawId.toLowerCase();
54651
54923
  const merchants = (data.data ?? []).filter((m) => String(m.rawId ?? "").toLowerCase() === needle || String(m.cleanName ?? "").toLowerCase().includes(needle));
54652
- return { ...data, data: merchants };
54924
+ return { ...data, data: merchants, ...undecrypted.length ? { undecrypted } : {} };
54653
54925
  }),
54654
54926
  upsert: variant(exports_external.object({
54655
54927
  id: exports_external.string().optional().describe("Merchant UUID — pass to update, omit to create"),
@@ -54758,22 +55030,19 @@ function personVariants(client, crypto3) {
54758
55030
  if (id) {
54759
55031
  const data2 = await client.get(`/api/persons/${id}`);
54760
55032
  if (crypto3 && data2.data.cipher) {
54761
- const fields = await decryptPersonCipher(data2.data.cipher, crypto3.key);
54762
- Object.assign(data2.data, fields, { cipher: null });
55033
+ const { data: decrypted, undecrypted } = await decryptPersonCiphers(data2.data, crypto3.key);
55034
+ data2.data = decrypted;
55035
+ if (undecrypted.length)
55036
+ return { ...data2, undecrypted };
54763
55037
  }
54764
55038
  return data2;
54765
55039
  }
54766
55040
  const data = await client.get("/api/persons");
54767
55041
  if (crypto3 && data.data) {
54768
- for (const person of data.data) {
54769
- const cipher = person.cipher;
54770
- if (cipher) {
54771
- try {
54772
- const fields = await decryptPersonCipher(cipher, crypto3.key);
54773
- Object.assign(person, fields, { cipher: null });
54774
- } catch {}
54775
- }
54776
- }
55042
+ const { data: decrypted, undecrypted } = await decryptPersonCiphers(data.data, crypto3.key);
55043
+ data.data = decrypted;
55044
+ if (undecrypted.length)
55045
+ return { ...data, undecrypted };
54777
55046
  }
54778
55047
  return data;
54779
55048
  }),
@@ -58567,9 +58836,8 @@ async function fetchAllRowsPaged(client, crypto3, params = {}, maxPages = 100) {
58567
58836
  if (cursor)
58568
58837
  q2.cursor = cursor;
58569
58838
  const res = await client.get("/api/transactions", q2);
58570
- const batch = res.data ?? [];
58571
- if (crypto3)
58572
- await decryptTransactionFields(batch, crypto3.key);
58839
+ const batch0 = res.data ?? [];
58840
+ const batch = crypto3 ? (await decryptTransactionFields(batch0, crypto3.key)).data : batch0;
58573
58841
  out.push(...batch.map(toTxnRow));
58574
58842
  if (!res.hasMore || !res.nextCursor || batch.length === 0 || res.nextCursor === cursor)
58575
58843
  break;
@@ -59034,9 +59302,8 @@ async function runRescan(client, crypto3) {
59034
59302
  bag = (await loadBag(client)).bag;
59035
59303
  } catch {}
59036
59304
  const merchantsRes = await client.get("/api/merchants");
59037
- const allMerchants = merchantsRes.data ?? [];
59038
- if (crypto3)
59039
- await decryptMerchantFields(allMerchants, crypto3.key);
59305
+ const allMerchants0 = merchantsRes.data ?? [];
59306
+ const allMerchants = crypto3 ? (await decryptMerchantFields(allMerchants0, crypto3.key)).data : allMerchants0;
59040
59307
  const globalMerchants = allMerchants.filter((m2) => !m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
59041
59308
  const localMerchants = allMerchants.filter((m2) => m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
59042
59309
  const merchantCategoryById = new Map(allMerchants.filter((m2) => m2.categoryId).map((m2) => [m2.id, m2.categoryId]));
@@ -59133,9 +59400,13 @@ function transactionTools(client, crypto3) {
59133
59400
  if (!full)
59134
59401
  apiParams.view = "lean";
59135
59402
  const data = await client.get("/api/transactions", Object.fromEntries(Object.entries(apiParams).filter(([, v2]) => v2 !== undefined).map(([k2, v2]) => [k2, String(v2)])));
59136
- const rows = data.data ?? [];
59137
- if (crypto3)
59138
- await decryptTransactionFields(rows, crypto3.key);
59403
+ let rows = data.data ?? [];
59404
+ let undecrypted = [];
59405
+ if (crypto3) {
59406
+ const dec = await decryptTransactionFields(rows, crypto3.key);
59407
+ rows = dec.data;
59408
+ undecrypted = dec.undecrypted;
59409
+ }
59139
59410
  const personNames = new Map;
59140
59411
  if (rows.some((r2) => r2.personId)) {
59141
59412
  for (const p2 of await loadDecryptedPersons(client, crypto3))
@@ -59147,12 +59418,14 @@ function transactionTools(client, crypto3) {
59147
59418
  if (person && personNames.has(person.id))
59148
59419
  person.name = personNames.get(person.id) ?? "";
59149
59420
  }
59150
- return data;
59421
+ data.data = rows;
59422
+ return undecrypted.length ? { ...data, undecrypted } : data;
59151
59423
  }
59152
59424
  const out = {
59153
59425
  rows: rows.map((r2) => toLeanRow(r2, personNames)),
59154
59426
  ...data.hasMore ? { more: true, cursor: data.nextCursor } : {},
59155
- ...data.summary ? { spent: data.summary.spent, income: data.summary.income } : {}
59427
+ ...data.summary ? { spent: data.summary.spent, income: data.summary.income } : {},
59428
+ ...undecrypted.length ? { undecrypted } : {}
59156
59429
  };
59157
59430
  return out;
59158
59431
  }
@@ -59278,8 +59551,11 @@ function transactionTools(client, crypto3) {
59278
59551
  }
59279
59552
  if (action === "list") {
59280
59553
  const data2 = await client.get("/api/transactions/duplicates");
59281
- if (crypto3 && data2.data)
59282
- await decryptTransactionFields(data2.data, crypto3.key);
59554
+ if (crypto3 && data2.data) {
59555
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data2.data, crypto3.key);
59556
+ data2.data = decrypted;
59557
+ return withTruncationWarning(undecrypted.length ? { ...data2, undecrypted } : data2);
59558
+ }
59283
59559
  return withTruncationWarning(data2);
59284
59560
  }
59285
59561
  const data = await client.post("/api/transactions/duplicates/resolve", {
@@ -59299,8 +59575,11 @@ function transactionTools(client, crypto3) {
59299
59575
  }),
59300
59576
  handler: async ({ accountId, startDate, endDate }) => {
59301
59577
  const data = await client.get("/api/transactions/reconciliation-context", { accountId, startDate, endDate });
59302
- if (crypto3 && data.data)
59303
- await decryptTransactionFields(data.data, crypto3.key);
59578
+ if (crypto3 && data.data) {
59579
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto3.key);
59580
+ data.data = decrypted;
59581
+ return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
59582
+ }
59304
59583
  return withTruncationWarning(data);
59305
59584
  }
59306
59585
  }),
@@ -59392,8 +59671,11 @@ function transactionTools(client, crypto3) {
59392
59671
  input: exports_external.object({ ccAccountId: exports_external.string().uuid() }),
59393
59672
  handler: async ({ ccAccountId }) => {
59394
59673
  const data = await client.get("/api/transactions/settlement-candidates", { ccAccountId });
59395
- if (crypto3 && data.data)
59396
- await decryptTransactionFields(data.data, crypto3.key);
59674
+ if (crypto3 && data.data) {
59675
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto3.key);
59676
+ data.data = decrypted;
59677
+ return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
59678
+ }
59397
59679
  return withTruncationWarning(data);
59398
59680
  }
59399
59681
  }),
@@ -59403,11 +59685,15 @@ function transactionTools(client, crypto3) {
59403
59685
  input: exports_external.object({ id: exports_external.string().uuid() }),
59404
59686
  handler: async ({ id }) => {
59405
59687
  const res = await client.get(`/api/transactions/${id}`);
59406
- const row = res.data;
59688
+ let row = res.data;
59407
59689
  if (!row)
59408
59690
  return res;
59409
- if (crypto3)
59410
- await decryptTransactionFields([row], crypto3.key);
59691
+ let undecrypted = [];
59692
+ if (crypto3) {
59693
+ const dec = await decryptTransactionFields([row], crypto3.key);
59694
+ row = dec.data[0];
59695
+ undecrypted = dec.undecrypted;
59696
+ }
59411
59697
  const lean = Object.fromEntries([
59412
59698
  "id",
59413
59699
  "date",
@@ -59423,7 +59709,7 @@ function transactionTools(client, crypto3) {
59423
59709
  "isDuplicate",
59424
59710
  "source"
59425
59711
  ].filter((k2) => row[k2] !== undefined).map((k2) => [k2, row[k2]]));
59426
- return lean;
59712
+ return undecrypted.length ? { ...lean, undecrypted } : lean;
59427
59713
  }
59428
59714
  })
59429
59715
  ];
@@ -59448,7 +59734,9 @@ function exportTools(client, crypto3) {
59448
59734
  format: "json"
59449
59735
  });
59450
59736
  if (crypto3 && Array.isArray(data?.transactions)) {
59451
- await decryptTransactionFields(data.transactions, crypto3.key);
59737
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.transactions, crypto3.key);
59738
+ data.transactions = decrypted;
59739
+ return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
59452
59740
  }
59453
59741
  return withTruncationWarning(data);
59454
59742
  }
@@ -59456,7 +59744,7 @@ function exportTools(client, crypto3) {
59456
59744
  ];
59457
59745
  }
59458
59746
 
59459
- // ../../node_modules/.bun/@nimit9+signet-ai@0.1.11+a8a356da5edd5fb9/node_modules/@nimit9/signet-ai/dist/jev.js
59747
+ // ../../node_modules/.bun/@nimit9+signet-ai@0.1.11+53d0ad1559459462/node_modules/@nimit9/signet-ai/dist/jev.js
59460
59748
  var JEV_DEFAULT_MODEL = "jev-1.13.0";
59461
59749
  var JEV_DEFAULT_MIN_CONFIDENCE = 0.95;
59462
59750
  var JEV_DEFAULT_TIMEOUT_MS = 8000;
@@ -59942,9 +60230,8 @@ async function runStatementImport(deps, args) {
59942
60230
  throw new Error(`${basename(args.filePath)} parsed to 0 transactions (format ${format}). If it is a statement with real activity, the layout may not be supported.`);
59943
60231
  }
59944
60232
  const dates = txns.map((t2) => t2.date).sort();
59945
- const existing = await fetchReconciliationContext(client, account.id, dates[0], dates[dates.length - 1]);
59946
- if (crypto3)
59947
- await decryptTransactionFields(existing, crypto3.key);
60233
+ const existing0 = await fetchReconciliationContext(client, account.id, dates[0], dates[dates.length - 1]);
60234
+ const existing = crypto3 ? (await decryptTransactionFields(existing0, crypto3.key)).data : existing0;
59948
60235
  const present = existing.map((r2) => ({ ...r2, isDuplicate: false, isIgnored: false }));
59949
60236
  const result = reconcile(present, { ...statement, transactions: txns });
59950
60237
  const fresh = result.unmatched;
@@ -60282,7 +60569,7 @@ function registerResources(server, tools) {
60282
60569
  // package.json
60283
60570
  var package_default = {
60284
60571
  name: "paisa-mcp",
60285
- version: "0.3.0",
60572
+ version: "0.4.0",
60286
60573
  repository: {
60287
60574
  type: "git",
60288
60575
  url: "git+https://github.com/nimit9/paisa.git",
@@ -60310,7 +60597,7 @@ var package_default = {
60310
60597
  "@modelcontextprotocol/client": "^2.1.0",
60311
60598
  "@modelcontextprotocol/server": "^2.1.0",
60312
60599
  "@nimit9/signet-ai": "^0.1.11",
60313
- "@nimit9/signet-lib": "^0.1.14",
60600
+ "@nimit9/signet-lib": "^0.1.15",
60314
60601
  "@nimit9/signet-server": "^0.2.2",
60315
60602
  "@paisa/parsers": "workspace:*",
60316
60603
  "@paisa/reconciliation": "workspace:*",
@@ -60338,7 +60625,13 @@ function paisaErrorResult(err) {
60338
60625
  });
60339
60626
  }
60340
60627
  function buildServer(client, crypto3) {
60341
- const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto3)));
60628
+ let initializeCapabilities = () => {
60629
+ return;
60630
+ };
60631
+ const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto3)).map((t2) => {
60632
+ const describe3 = CONFIRMED_TOOLS[t2.name];
60633
+ return describe3 ? withConfirmation(t2, describe3, () => initializeCapabilities()) : t2;
60634
+ }));
60342
60635
  for (const t2 of tools)
60343
60636
  if (t2.input)
60344
60637
  withCompactJsonSchema(t2.input);
@@ -60348,6 +60641,7 @@ function buildServer(client, crypto3) {
60348
60641
  tools,
60349
60642
  errorResult: paisaErrorResult
60350
60643
  });
60644
+ initializeCapabilities = () => server.server.getClientCapabilities();
60351
60645
  registerResources(server, tools);
60352
60646
  registerPrompts(server);
60353
60647
  return server;
@@ -60374,5 +60668,5 @@ main().catch((err) => {
60374
60668
  process.exit(1);
60375
60669
  });
60376
60670
 
60377
- //# debugId=C64445D8B55EDCFA64756E2164756E21
60671
+ //# debugId=F56B7BEACF2C363564756E2164756E21
60378
60672
  //# sourceMappingURL=index.js.map