paisa-mcp 0.1.0 → 0.2.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 +465 -14
- package/dist/index.js.map +6 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39147,6 +39147,220 @@ function validateAndWarnToolName(name) {
|
|
|
39147
39147
|
issueToolNameWarning(name, result.warnings);
|
|
39148
39148
|
return result.isValid;
|
|
39149
39149
|
}
|
|
39150
|
+
var MAX_TEMPLATE_LENGTH = 1e6;
|
|
39151
|
+
var MAX_VARIABLE_LENGTH = 1e6;
|
|
39152
|
+
var MAX_TEMPLATE_EXPRESSIONS = 1e4;
|
|
39153
|
+
var MAX_REGEX_LENGTH = 1e6;
|
|
39154
|
+
var UriTemplate = class UriTemplate2 {
|
|
39155
|
+
static isTemplate(str) {
|
|
39156
|
+
return /\{[^}\s]+\}/.test(str);
|
|
39157
|
+
}
|
|
39158
|
+
static validateLength(str, max, context) {
|
|
39159
|
+
if (str.length > max)
|
|
39160
|
+
throw new Error(`${context} exceeds maximum length of ${max} characters (got ${str.length})`);
|
|
39161
|
+
}
|
|
39162
|
+
template;
|
|
39163
|
+
parts;
|
|
39164
|
+
get variableNames() {
|
|
39165
|
+
return this.parts.flatMap((part) => typeof part === "string" ? [] : part.names);
|
|
39166
|
+
}
|
|
39167
|
+
constructor(template) {
|
|
39168
|
+
UriTemplate2.validateLength(template, MAX_TEMPLATE_LENGTH, "Template");
|
|
39169
|
+
this.template = template;
|
|
39170
|
+
this.parts = this.parse(template);
|
|
39171
|
+
}
|
|
39172
|
+
toString() {
|
|
39173
|
+
return this.template;
|
|
39174
|
+
}
|
|
39175
|
+
parse(template) {
|
|
39176
|
+
const parts = [];
|
|
39177
|
+
let currentText = "";
|
|
39178
|
+
let i = 0;
|
|
39179
|
+
let expressionCount = 0;
|
|
39180
|
+
while (i < template.length)
|
|
39181
|
+
if (template[i] === "{") {
|
|
39182
|
+
if (currentText) {
|
|
39183
|
+
parts.push(currentText);
|
|
39184
|
+
currentText = "";
|
|
39185
|
+
}
|
|
39186
|
+
const end = template.indexOf("}", i);
|
|
39187
|
+
if (end === -1)
|
|
39188
|
+
throw new Error("Unclosed template expression");
|
|
39189
|
+
expressionCount++;
|
|
39190
|
+
if (expressionCount > MAX_TEMPLATE_EXPRESSIONS)
|
|
39191
|
+
throw new Error(`Template contains too many expressions (max ${MAX_TEMPLATE_EXPRESSIONS})`);
|
|
39192
|
+
const expr = template.slice(i + 1, end);
|
|
39193
|
+
const operator = this.getOperator(expr);
|
|
39194
|
+
const exploded = expr.includes("*");
|
|
39195
|
+
const names = this.getNames(expr);
|
|
39196
|
+
const name = names[0];
|
|
39197
|
+
for (const name$1 of names)
|
|
39198
|
+
UriTemplate2.validateLength(name$1, MAX_VARIABLE_LENGTH, "Variable name");
|
|
39199
|
+
parts.push({
|
|
39200
|
+
name,
|
|
39201
|
+
operator,
|
|
39202
|
+
names,
|
|
39203
|
+
exploded
|
|
39204
|
+
});
|
|
39205
|
+
i = end + 1;
|
|
39206
|
+
} else {
|
|
39207
|
+
currentText += template[i];
|
|
39208
|
+
i++;
|
|
39209
|
+
}
|
|
39210
|
+
if (currentText)
|
|
39211
|
+
parts.push(currentText);
|
|
39212
|
+
return parts;
|
|
39213
|
+
}
|
|
39214
|
+
getOperator(expr) {
|
|
39215
|
+
return [
|
|
39216
|
+
"+",
|
|
39217
|
+
"#",
|
|
39218
|
+
".",
|
|
39219
|
+
"/",
|
|
39220
|
+
"?",
|
|
39221
|
+
"&"
|
|
39222
|
+
].find((op) => expr.startsWith(op)) || "";
|
|
39223
|
+
}
|
|
39224
|
+
getNames(expr) {
|
|
39225
|
+
const operator = this.getOperator(expr);
|
|
39226
|
+
return expr.slice(operator.length).split(",").map((name) => name.replace("*", "").trim()).filter((name) => name.length > 0);
|
|
39227
|
+
}
|
|
39228
|
+
encodeValue(value, operator) {
|
|
39229
|
+
UriTemplate2.validateLength(value, MAX_VARIABLE_LENGTH, "Variable value");
|
|
39230
|
+
if (operator === "+" || operator === "#")
|
|
39231
|
+
return encodeURI(value);
|
|
39232
|
+
return encodeURIComponent(value);
|
|
39233
|
+
}
|
|
39234
|
+
expandPart(part, variables) {
|
|
39235
|
+
if (part.operator === "?" || part.operator === "&") {
|
|
39236
|
+
const pairs = part.names.map((name) => {
|
|
39237
|
+
const value$1 = variables[name];
|
|
39238
|
+
if (value$1 === undefined)
|
|
39239
|
+
return "";
|
|
39240
|
+
return `${name}=${Array.isArray(value$1) ? value$1.map((v) => this.encodeValue(v, part.operator)).join(",") : this.encodeValue(value$1.toString(), part.operator)}`;
|
|
39241
|
+
}).filter((pair) => pair.length > 0);
|
|
39242
|
+
if (pairs.length === 0)
|
|
39243
|
+
return "";
|
|
39244
|
+
return (part.operator === "?" ? "?" : "&") + pairs.join("&");
|
|
39245
|
+
}
|
|
39246
|
+
if (part.names.length > 1) {
|
|
39247
|
+
const values = part.names.map((name) => variables[name]).filter((v) => v !== undefined);
|
|
39248
|
+
if (values.length === 0)
|
|
39249
|
+
return "";
|
|
39250
|
+
return values.map((v) => Array.isArray(v) ? v[0] : v).join(",");
|
|
39251
|
+
}
|
|
39252
|
+
const value = variables[part.name];
|
|
39253
|
+
if (value === undefined)
|
|
39254
|
+
return "";
|
|
39255
|
+
const encoded = (Array.isArray(value) ? value : [value]).map((v) => this.encodeValue(v, part.operator));
|
|
39256
|
+
switch (part.operator) {
|
|
39257
|
+
case "":
|
|
39258
|
+
return encoded.join(",");
|
|
39259
|
+
case "+":
|
|
39260
|
+
return encoded.join(",");
|
|
39261
|
+
case "#":
|
|
39262
|
+
return "#" + encoded.join(",");
|
|
39263
|
+
case ".":
|
|
39264
|
+
return "." + encoded.join(".");
|
|
39265
|
+
case "/":
|
|
39266
|
+
return "/" + encoded.join("/");
|
|
39267
|
+
default:
|
|
39268
|
+
return encoded.join(",");
|
|
39269
|
+
}
|
|
39270
|
+
}
|
|
39271
|
+
expand(variables) {
|
|
39272
|
+
let result = "";
|
|
39273
|
+
let hasQueryParam = false;
|
|
39274
|
+
for (const part of this.parts) {
|
|
39275
|
+
if (typeof part === "string") {
|
|
39276
|
+
result += part;
|
|
39277
|
+
continue;
|
|
39278
|
+
}
|
|
39279
|
+
const expanded = this.expandPart(part, variables);
|
|
39280
|
+
if (!expanded)
|
|
39281
|
+
continue;
|
|
39282
|
+
result += (part.operator === "?" || part.operator === "&") && hasQueryParam ? expanded.replace("?", "&") : expanded;
|
|
39283
|
+
if (part.operator === "?" || part.operator === "&")
|
|
39284
|
+
hasQueryParam = true;
|
|
39285
|
+
}
|
|
39286
|
+
return result;
|
|
39287
|
+
}
|
|
39288
|
+
escapeRegExp(str) {
|
|
39289
|
+
return str.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
39290
|
+
}
|
|
39291
|
+
partToRegExp(part) {
|
|
39292
|
+
const patterns = [];
|
|
39293
|
+
for (const name$1 of part.names)
|
|
39294
|
+
UriTemplate2.validateLength(name$1, MAX_VARIABLE_LENGTH, "Variable name");
|
|
39295
|
+
if (part.operator === "?" || part.operator === "&") {
|
|
39296
|
+
for (let i = 0;i < part.names.length; i++) {
|
|
39297
|
+
const name$1 = part.names[i];
|
|
39298
|
+
const prefix = i === 0 ? "\\" + part.operator : "&";
|
|
39299
|
+
patterns.push({
|
|
39300
|
+
pattern: prefix + this.escapeRegExp(name$1) + "=([^&]+)",
|
|
39301
|
+
name: name$1
|
|
39302
|
+
});
|
|
39303
|
+
}
|
|
39304
|
+
return patterns;
|
|
39305
|
+
}
|
|
39306
|
+
let pattern;
|
|
39307
|
+
const name = part.name;
|
|
39308
|
+
switch (part.operator) {
|
|
39309
|
+
case "":
|
|
39310
|
+
pattern = part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)";
|
|
39311
|
+
break;
|
|
39312
|
+
case "+":
|
|
39313
|
+
case "#":
|
|
39314
|
+
pattern = "(.+)";
|
|
39315
|
+
break;
|
|
39316
|
+
case ".":
|
|
39317
|
+
pattern = String.raw`\.([^/,]+)`;
|
|
39318
|
+
break;
|
|
39319
|
+
case "/":
|
|
39320
|
+
pattern = "/" + (part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)");
|
|
39321
|
+
break;
|
|
39322
|
+
default:
|
|
39323
|
+
pattern = "([^/]+)";
|
|
39324
|
+
}
|
|
39325
|
+
patterns.push({
|
|
39326
|
+
pattern,
|
|
39327
|
+
name
|
|
39328
|
+
});
|
|
39329
|
+
return patterns;
|
|
39330
|
+
}
|
|
39331
|
+
match(uri) {
|
|
39332
|
+
UriTemplate2.validateLength(uri, MAX_TEMPLATE_LENGTH, "URI");
|
|
39333
|
+
let pattern = "^";
|
|
39334
|
+
const names = [];
|
|
39335
|
+
for (const part of this.parts)
|
|
39336
|
+
if (typeof part === "string")
|
|
39337
|
+
pattern += this.escapeRegExp(part);
|
|
39338
|
+
else {
|
|
39339
|
+
const patterns = this.partToRegExp(part);
|
|
39340
|
+
for (const { pattern: partPattern2, name } of patterns) {
|
|
39341
|
+
pattern += partPattern2;
|
|
39342
|
+
names.push({
|
|
39343
|
+
name,
|
|
39344
|
+
exploded: part.exploded
|
|
39345
|
+
});
|
|
39346
|
+
}
|
|
39347
|
+
}
|
|
39348
|
+
pattern += "$";
|
|
39349
|
+
UriTemplate2.validateLength(pattern, MAX_REGEX_LENGTH, "Generated regex pattern");
|
|
39350
|
+
const regex = new RegExp(pattern);
|
|
39351
|
+
const match = uri.match(regex);
|
|
39352
|
+
if (!match)
|
|
39353
|
+
return null;
|
|
39354
|
+
const result = {};
|
|
39355
|
+
for (const [i, name_] of names.entries()) {
|
|
39356
|
+
const { name, exploded } = name_;
|
|
39357
|
+
const value = match[i + 1];
|
|
39358
|
+
const cleanName = name.replace("*", "");
|
|
39359
|
+
result[cleanName] = exploded && value.includes(",") ? value.split(",") : value;
|
|
39360
|
+
}
|
|
39361
|
+
return result;
|
|
39362
|
+
}
|
|
39363
|
+
};
|
|
39150
39364
|
function isZodV4Schema(v) {
|
|
39151
39365
|
return typeof v === "object" && v !== null && "_zod" in v;
|
|
39152
39366
|
}
|
|
@@ -48100,6 +48314,22 @@ var McpServer = class {
|
|
|
48100
48314
|
this.server.sendPromptListChanged();
|
|
48101
48315
|
}
|
|
48102
48316
|
};
|
|
48317
|
+
var ResourceTemplate = class {
|
|
48318
|
+
_uriTemplate;
|
|
48319
|
+
constructor(uriTemplate, _callbacks) {
|
|
48320
|
+
this._callbacks = _callbacks;
|
|
48321
|
+
this._uriTemplate = typeof uriTemplate === "string" ? new UriTemplate(uriTemplate) : uriTemplate;
|
|
48322
|
+
}
|
|
48323
|
+
get uriTemplate() {
|
|
48324
|
+
return this._uriTemplate;
|
|
48325
|
+
}
|
|
48326
|
+
get listCallback() {
|
|
48327
|
+
return this._callbacks.list;
|
|
48328
|
+
}
|
|
48329
|
+
completeCallback(variable) {
|
|
48330
|
+
return this._callbacks.complete?.[variable];
|
|
48331
|
+
}
|
|
48332
|
+
};
|
|
48103
48333
|
function createToolExecutor(inputSchema, handler) {
|
|
48104
48334
|
if (inputSchema) {
|
|
48105
48335
|
const callback$1 = handler;
|
|
@@ -53421,6 +53651,108 @@ function applyAnnotations(tools) {
|
|
|
53421
53651
|
});
|
|
53422
53652
|
}
|
|
53423
53653
|
|
|
53654
|
+
// src/prompts.ts
|
|
53655
|
+
var CONFIRM = "Ask me before any write; show what will change first (counts, not a dump).";
|
|
53656
|
+
var month = exports_external.string().regex(/^\d{4}-\d{2}$/).optional().describe("YYYY-MM; defaults to the last full month");
|
|
53657
|
+
function monthText(m) {
|
|
53658
|
+
if (!m)
|
|
53659
|
+
return "the last full month";
|
|
53660
|
+
const [y, mm] = m.split("-");
|
|
53661
|
+
return `${m} (month=${Number(mm)}, year=${y})`;
|
|
53662
|
+
}
|
|
53663
|
+
function rangeText(a) {
|
|
53664
|
+
if (a.startDate && a.endDate)
|
|
53665
|
+
return ` startDate=${a.startDate} endDate=${a.endDate}`;
|
|
53666
|
+
if (a.startDate)
|
|
53667
|
+
return ` startDate=${a.startDate}`;
|
|
53668
|
+
if (a.endDate)
|
|
53669
|
+
return ` endDate=${a.endDate}`;
|
|
53670
|
+
return "";
|
|
53671
|
+
}
|
|
53672
|
+
var PROMPTS = [
|
|
53673
|
+
{
|
|
53674
|
+
name: "monthly_review",
|
|
53675
|
+
title: "Monthly review",
|
|
53676
|
+
description: "Review one month: income, spending, savings rate, budgets, what's due next.",
|
|
53677
|
+
args: exports_external.object({ month }),
|
|
53678
|
+
text: (a) => [
|
|
53679
|
+
`Review my finances for ${monthText(a.month)}.`,
|
|
53680
|
+
"1. `check_alerts` — note anything urgent.",
|
|
53681
|
+
"2. `get_report` report='monthly' months=3 — income, expenses, savings rate vs the months before.",
|
|
53682
|
+
"3. `get_analytics` report='spending_trends' months=3 — category breakdown and budget vs actual (read that month's row).",
|
|
53683
|
+
"4. `get_transactions` for that month with amountMin to find the biggest items (limit 25).",
|
|
53684
|
+
"5. `get_report` report='upcoming' days=30 — EMIs, SIPs, renewals, card bills.",
|
|
53685
|
+
"Summarize: income, spend, savings rate, top categories, over-budget categories, unusual items, upcoming dues. Do not edit, import or delete anything.",
|
|
53686
|
+
"If I ask to save it, call `generate_monthly_report` with month and year."
|
|
53687
|
+
].join(`
|
|
53688
|
+
`)
|
|
53689
|
+
},
|
|
53690
|
+
{
|
|
53691
|
+
name: "import_statement",
|
|
53692
|
+
title: "Import a statement",
|
|
53693
|
+
description: "Preview, confirm, import and categorize a bank or card statement (PDF or CSV).",
|
|
53694
|
+
args: exports_external.object({
|
|
53695
|
+
filePath: exports_external.string().optional().describe("Local path of the statement file")
|
|
53696
|
+
}),
|
|
53697
|
+
text: (a) => [
|
|
53698
|
+
`Import the statement ${a.filePath ? `at ${a.filePath}` : "I give you (ask for the file path)"}.`,
|
|
53699
|
+
"1. `list_entities` entity='bank_accounts' — pick the account. Missing? Create it with `upsert_entity` entity='bank_account' only after I confirm name, bank, type, last4 and owner.",
|
|
53700
|
+
"2. `preview_import` — from='pdf' with filePath + account, or from='csv' with filePath. It writes nothing. Show me the new / already-in-Paisa / conflict counts and totals (CSV: the detected mapping).",
|
|
53701
|
+
"3. After I confirm, `import_statement` with the same args (CSV: the confirmed mapping). Never pass force unless I say so. CSV import is not available in private mode.",
|
|
53702
|
+
"4. Credit card statement: `get_settlement_candidates` with ccAccountId, then mark the matching bank debits isTransfer=true with `update_transaction` once I confirm.",
|
|
53703
|
+
"5. `sync_recurring`, then categorize what's left with `get_uncategorized_groups` → `categorize_groups` (see the categorize_uncategorized prompt).",
|
|
53704
|
+
CONFIRM
|
|
53705
|
+
].join(`
|
|
53706
|
+
`)
|
|
53707
|
+
},
|
|
53708
|
+
{
|
|
53709
|
+
name: "categorize_uncategorized",
|
|
53710
|
+
title: "Categorize uncategorized",
|
|
53711
|
+
description: "Group uncategorized transactions, propose categories, apply them and save learning rules.",
|
|
53712
|
+
args: exports_external.object({
|
|
53713
|
+
startDate: exports_external.string().optional().describe("YYYY-MM-DD"),
|
|
53714
|
+
endDate: exports_external.string().optional().describe("YYYY-MM-DD")
|
|
53715
|
+
}),
|
|
53716
|
+
text: (a) => [
|
|
53717
|
+
"Categorize my uncategorized transactions.",
|
|
53718
|
+
"1. `list_entities` entity='categories' (or the paisa://categories resource) for valid slugs.",
|
|
53719
|
+
`2. \`get_uncategorized_groups\`${rangeText(a)} — groups by pattern, biggest first.`,
|
|
53720
|
+
"3. Propose a category slug per group as a table (pattern, count, total, slug). Ask about groups you are unsure of instead of guessing.",
|
|
53721
|
+
`4. After I confirm, \`categorize_groups\`${rangeText(a)} with dryRun=true, show the counts, then run it again without dryRun. It also saves learning rules for future imports.`,
|
|
53722
|
+
"5. Transfers to/from individuals: `backfill_persons` with dryRun=true, then for real once I confirm.",
|
|
53723
|
+
"6. Tell me how many uncategorized groups remain.",
|
|
53724
|
+
CONFIRM
|
|
53725
|
+
].join(`
|
|
53726
|
+
`)
|
|
53727
|
+
},
|
|
53728
|
+
{
|
|
53729
|
+
name: "budget_check",
|
|
53730
|
+
title: "Budget check",
|
|
53731
|
+
description: "Compare one month's spending to its budgets and suggest changes.",
|
|
53732
|
+
args: exports_external.object({ month }),
|
|
53733
|
+
text: (a) => [
|
|
53734
|
+
`Check my budgets for ${monthText(a.month)}.`,
|
|
53735
|
+
"1. `list_entities` entity='budgets' with month and year.",
|
|
53736
|
+
"2. `get_analytics` report='spending_trends' months=3 — budget vs actual (read that month's row).",
|
|
53737
|
+
"3. Show a table: category, budget, spent, remaining, % used. Flag categories over 80% and over 100%, and big categories with no budget.",
|
|
53738
|
+
"4. Suggest changes. Call `set_budget` (categoryId from `list_entities` entity='categories') only for the changes I approve."
|
|
53739
|
+
].join(`
|
|
53740
|
+
`)
|
|
53741
|
+
}
|
|
53742
|
+
];
|
|
53743
|
+
function registerPrompts(server) {
|
|
53744
|
+
for (const p of PROMPTS) {
|
|
53745
|
+
const cb = (args) => ({
|
|
53746
|
+
messages: [{ role: "user", content: { type: "text", text: p.text(args) } }]
|
|
53747
|
+
});
|
|
53748
|
+
if (p.args) {
|
|
53749
|
+
server.registerPrompt(p.name, { title: p.title, description: p.description, argsSchema: p.args }, (args) => cb(args));
|
|
53750
|
+
} else {
|
|
53751
|
+
server.registerPrompt(p.name, { title: p.title, description: p.description }, () => cb({}));
|
|
53752
|
+
}
|
|
53753
|
+
}
|
|
53754
|
+
}
|
|
53755
|
+
|
|
53424
53756
|
// src/tools/alert-rules.ts
|
|
53425
53757
|
function alertRuleVariants(client) {
|
|
53426
53758
|
return {
|
|
@@ -57279,22 +57611,22 @@ function parseMonthDayYear(raw) {
|
|
|
57279
57611
|
const match = raw.trim().match(/^(\w+)\s+(\d{1,2}),?\s+(\d{4})$/);
|
|
57280
57612
|
if (!match)
|
|
57281
57613
|
return null;
|
|
57282
|
-
const
|
|
57283
|
-
if (!
|
|
57614
|
+
const month2 = MONTH_MAP[match[1].toLowerCase()];
|
|
57615
|
+
if (!month2)
|
|
57284
57616
|
return null;
|
|
57285
57617
|
const day = parseInt(match[2], 10);
|
|
57286
57618
|
const year = parseInt(match[3], 10);
|
|
57287
|
-
const iso = `${year}-${String(
|
|
57288
|
-
return { month, day, year, iso };
|
|
57619
|
+
const iso = `${year}-${String(month2).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
57620
|
+
return { month: month2, day, year, iso };
|
|
57289
57621
|
}
|
|
57290
57622
|
function parseMonthDay(raw) {
|
|
57291
57623
|
const match = raw.trim().match(/^(\w+)\s+(\d{1,2})$/);
|
|
57292
57624
|
if (!match)
|
|
57293
57625
|
return null;
|
|
57294
|
-
const
|
|
57295
|
-
if (!
|
|
57626
|
+
const month2 = MONTH_MAP[match[1].toLowerCase()];
|
|
57627
|
+
if (!month2)
|
|
57296
57628
|
return null;
|
|
57297
|
-
return { month, day: parseInt(match[2], 10) };
|
|
57629
|
+
return { month: month2, day: parseInt(match[2], 10) };
|
|
57298
57630
|
}
|
|
57299
57631
|
function parseAmexPdfDate(raw, meta3) {
|
|
57300
57632
|
const parsed = parseMonthDay(raw);
|
|
@@ -57697,10 +58029,10 @@ function parseMonthNameDate(raw) {
|
|
|
57697
58029
|
const match = raw.trim().match(/^(\d{1,2})\s+(\w{3})\w*,?\s+(\d{4})$/);
|
|
57698
58030
|
if (!match)
|
|
57699
58031
|
return null;
|
|
57700
|
-
const
|
|
57701
|
-
if (!
|
|
58032
|
+
const month2 = MONTH_NAMES[match[2].toLowerCase().slice(0, 3)];
|
|
58033
|
+
if (!month2)
|
|
57702
58034
|
return null;
|
|
57703
|
-
return `${match[3]}-${
|
|
58035
|
+
return `${match[3]}-${month2}-${match[1].padStart(2, "0")}`;
|
|
57704
58036
|
}
|
|
57705
58037
|
function parseDdMmYyyy(raw) {
|
|
57706
58038
|
const match = raw.trim().match(/^(\d{2})\/(\d{2})\/(\d{4})/);
|
|
@@ -57998,13 +58330,13 @@ function parseHdfcDate(raw) {
|
|
|
57998
58330
|
if (!match)
|
|
57999
58331
|
return null;
|
|
58000
58332
|
const day = match[1];
|
|
58001
|
-
const
|
|
58333
|
+
const month2 = match[2];
|
|
58002
58334
|
let year = match[3];
|
|
58003
58335
|
if (year.length === 2) {
|
|
58004
58336
|
const num = parseInt(year, 10);
|
|
58005
58337
|
year = num > 50 ? `19${year}` : `20${year}`;
|
|
58006
58338
|
}
|
|
58007
|
-
return `${year}-${
|
|
58339
|
+
return `${year}-${month2}-${day}`;
|
|
58008
58340
|
}
|
|
58009
58341
|
function parseAmount3(raw) {
|
|
58010
58342
|
if (!raw)
|
|
@@ -60263,10 +60595,127 @@ var REGISTRATIONS = [
|
|
|
60263
60595
|
{ title: "Settings", tools: settingsTools },
|
|
60264
60596
|
{ title: "Encryption & privacy", tools: authCryptoTools }
|
|
60265
60597
|
];
|
|
60598
|
+
|
|
60599
|
+
// src/resources.ts
|
|
60600
|
+
var MERCHANT_CAP = 500;
|
|
60601
|
+
var JSON_MIME = "application/json";
|
|
60602
|
+
var RESOURCES = [
|
|
60603
|
+
{
|
|
60604
|
+
name: "categories",
|
|
60605
|
+
uri: "paisa://categories",
|
|
60606
|
+
title: "Categories",
|
|
60607
|
+
description: "Category tree (id, slug, name, type, children). Same as list_entities categories.",
|
|
60608
|
+
tool: "list_entities",
|
|
60609
|
+
args: { entity: "categories" }
|
|
60610
|
+
},
|
|
60611
|
+
{
|
|
60612
|
+
name: "bank_accounts",
|
|
60613
|
+
uri: "paisa://bank-accounts",
|
|
60614
|
+
title: "Bank accounts",
|
|
60615
|
+
description: "Bank accounts and cards (id, name, bank, type, last4, owner).",
|
|
60616
|
+
tool: "list_entities",
|
|
60617
|
+
args: { entity: "bank_accounts" }
|
|
60618
|
+
},
|
|
60619
|
+
{
|
|
60620
|
+
name: "persons",
|
|
60621
|
+
uri: "paisa://persons",
|
|
60622
|
+
title: "Persons",
|
|
60623
|
+
description: "People you send/receive money from (decrypted in private mode).",
|
|
60624
|
+
tool: "list_entities",
|
|
60625
|
+
args: { entity: "persons" }
|
|
60626
|
+
},
|
|
60627
|
+
{
|
|
60628
|
+
name: "merchants",
|
|
60629
|
+
uri: "paisa://merchants",
|
|
60630
|
+
title: "Merchants",
|
|
60631
|
+
description: `Merchants with their category (first ${MERCHANT_CAP}; look others up with list_entities entity='merchants' rawId).`,
|
|
60632
|
+
tool: "list_entities",
|
|
60633
|
+
args: { entity: "merchants" },
|
|
60634
|
+
shape: capMerchants
|
|
60635
|
+
},
|
|
60636
|
+
{
|
|
60637
|
+
name: "household",
|
|
60638
|
+
uri: "paisa://household",
|
|
60639
|
+
title: "Household",
|
|
60640
|
+
description: "Household name, currency, locale, owner labels and members.",
|
|
60641
|
+
tool: "get_settings",
|
|
60642
|
+
args: { section: "household" }
|
|
60643
|
+
},
|
|
60644
|
+
{
|
|
60645
|
+
name: "profile",
|
|
60646
|
+
uri: "paisa://profile",
|
|
60647
|
+
title: "Profile",
|
|
60648
|
+
description: "Your name, email and owner slug.",
|
|
60649
|
+
tool: "get_settings",
|
|
60650
|
+
args: { section: "profile" }
|
|
60651
|
+
}
|
|
60652
|
+
];
|
|
60653
|
+
var TRANSACTION_TEMPLATE = "paisa://transactions/{id}";
|
|
60654
|
+
function capMerchants(value) {
|
|
60655
|
+
const v2 = value;
|
|
60656
|
+
if (!v2 || !Array.isArray(v2.data) || v2.data.length <= MERCHANT_CAP)
|
|
60657
|
+
return value;
|
|
60658
|
+
return {
|
|
60659
|
+
...v2,
|
|
60660
|
+
data: v2.data.slice(0, MERCHANT_CAP),
|
|
60661
|
+
truncated: {
|
|
60662
|
+
returned: MERCHANT_CAP,
|
|
60663
|
+
total: v2.data.length,
|
|
60664
|
+
hint: "Use list_entities entity='merchants' rawId=<UPI ID or name> to find one."
|
|
60665
|
+
}
|
|
60666
|
+
};
|
|
60667
|
+
}
|
|
60668
|
+
function scrubCiphertext(value) {
|
|
60669
|
+
const scrub = (row) => {
|
|
60670
|
+
if (row && typeof row === "object" && typeof row.cipher === "string") {
|
|
60671
|
+
Object.assign(row, { cipher: null, undecrypted: true });
|
|
60672
|
+
}
|
|
60673
|
+
};
|
|
60674
|
+
const data = value?.data;
|
|
60675
|
+
if (Array.isArray(data))
|
|
60676
|
+
data.forEach(scrub);
|
|
60677
|
+
else
|
|
60678
|
+
scrub(data);
|
|
60679
|
+
return value;
|
|
60680
|
+
}
|
|
60681
|
+
async function runTool(tool, args, mcp) {
|
|
60682
|
+
const input2 = tool.input ? await tool.input.parseAsync(args) : args;
|
|
60683
|
+
const ctx = { tool: tool.name, signal: mcp.mcpReq.signal, mcp };
|
|
60684
|
+
return tool.handler(input2, ctx);
|
|
60685
|
+
}
|
|
60686
|
+
function jsonContents(uri, value) {
|
|
60687
|
+
return { contents: [{ uri: uri.href, mimeType: JSON_MIME, text: JSON.stringify(value ?? null) }] };
|
|
60688
|
+
}
|
|
60689
|
+
function registerResources(server, tools) {
|
|
60690
|
+
const byName = new Map(tools.map((t2) => [t2.name, t2]));
|
|
60691
|
+
const need = (name) => {
|
|
60692
|
+
const t2 = byName.get(name);
|
|
60693
|
+
if (!t2)
|
|
60694
|
+
throw new Error(`MCP resources: tool ${name} is not registered`);
|
|
60695
|
+
return t2;
|
|
60696
|
+
};
|
|
60697
|
+
for (const r2 of RESOURCES) {
|
|
60698
|
+
const tool = need(r2.tool);
|
|
60699
|
+
server.registerResource(r2.name, r2.uri, { title: r2.title, description: r2.description, mimeType: JSON_MIME }, async (uri, mcp) => {
|
|
60700
|
+
const value = scrubCiphertext(await runTool(tool, r2.args, mcp));
|
|
60701
|
+
return jsonContents(uri, r2.shape ? r2.shape(value) : value);
|
|
60702
|
+
});
|
|
60703
|
+
}
|
|
60704
|
+
const getTransaction = need("get_transaction");
|
|
60705
|
+
server.registerResource("transaction", new ResourceTemplate(TRANSACTION_TEMPLATE, { list: undefined }), {
|
|
60706
|
+
title: "Transaction",
|
|
60707
|
+
description: "One transaction by UUID (same as get_transaction; decrypted in private mode).",
|
|
60708
|
+
mimeType: JSON_MIME
|
|
60709
|
+
}, async (uri, variables, mcp) => {
|
|
60710
|
+
const id = Array.isArray(variables.id) ? variables.id[0] : variables.id;
|
|
60711
|
+
const value = scrubCiphertext(await runTool(getTransaction, { id }, mcp));
|
|
60712
|
+
return jsonContents(uri, value);
|
|
60713
|
+
});
|
|
60714
|
+
}
|
|
60266
60715
|
// package.json
|
|
60267
60716
|
var package_default = {
|
|
60268
60717
|
name: "paisa-mcp",
|
|
60269
|
-
version: "0.
|
|
60718
|
+
version: "0.2.0",
|
|
60270
60719
|
repository: {
|
|
60271
60720
|
type: "git",
|
|
60272
60721
|
url: "git+https://github.com/nimit9/paisa.git",
|
|
@@ -60332,6 +60781,8 @@ function buildServer(client, crypto3) {
|
|
|
60332
60781
|
errorResult: paisaErrorResult
|
|
60333
60782
|
});
|
|
60334
60783
|
hideFromToolsList(server, new Set(tools.filter(isAlias).map((t2) => t2.name)));
|
|
60784
|
+
registerResources(server, tools);
|
|
60785
|
+
registerPrompts(server);
|
|
60335
60786
|
return server;
|
|
60336
60787
|
}
|
|
60337
60788
|
function hideFromToolsList(server, hidden) {
|
|
@@ -60368,5 +60819,5 @@ main().catch((err) => {
|
|
|
60368
60819
|
process.exit(1);
|
|
60369
60820
|
});
|
|
60370
60821
|
|
|
60371
|
-
//# debugId=
|
|
60822
|
+
//# debugId=1E3042577A7AFC5F64756E2164756E21
|
|
60372
60823
|
//# sourceMappingURL=index.js.map
|