@finology/mcp-server 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/README.md +11 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/filingStatusSwing.d.ts +36 -0
- package/dist/tools/filingStatusSwing.js +119 -0
- package/dist/tools/filingStatusSwing.js.map +1 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -30,6 +30,17 @@ SAVE, Graduated, Extended, tiered Standard — returning monthly payment, lifeti
|
|
|
30
30
|
forgiveness and tax on forgiveness for each, **plus the plans that are excluded and the
|
|
31
31
|
eligibility rule that excludes them.**
|
|
32
32
|
|
|
33
|
+
### `compare_married_filing_jointly_vs_separately_student_loans`
|
|
34
|
+
|
|
35
|
+
For a married borrower, prices the filing-status decision. Filing separately removes the spouse's
|
|
36
|
+
income from the income-driven payment calculation, which routinely moves the lifetime loan cost by
|
|
37
|
+
tens of thousands of dollars.
|
|
38
|
+
|
|
39
|
+
**It answers half the question on purpose, and says so.** The engine models the loan side exactly and
|
|
40
|
+
does not model the tax cost of filing separately — lost credits, worse brackets, community-property
|
|
41
|
+
splits — which is often large enough to reverse the answer. Returning only the favourable half as if
|
|
42
|
+
it were the whole decision would be the same confident half-truth this server exists to replace.
|
|
43
|
+
|
|
33
44
|
### `estimate_rap_monthly_payment`
|
|
34
45
|
|
|
35
46
|
The RAP monthly payment alone, for the common case. Requires the borrower's AGI and refuses to
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { comparePlansShape, comparePlans } from "./tools/comparePlans.js";
|
|
5
5
|
import { rapPaymentShape, rapPayment } from "./tools/rapPayment.js";
|
|
6
|
+
import { filingStatusSwingShape, filingStatusSwing } from "./tools/filingStatusSwing.js";
|
|
6
7
|
import { BASE_URL } from "./client.js";
|
|
7
8
|
const server = new McpServer({
|
|
8
9
|
name: "finology-student-loan",
|
|
@@ -33,6 +34,17 @@ server.registerTool("estimate_rap_monthly_payment", {
|
|
|
33
34
|
"assumed zero returns the $10 floor and reads like a real answer.",
|
|
34
35
|
inputSchema: rapPaymentShape,
|
|
35
36
|
}, rapPayment);
|
|
37
|
+
server.registerTool("compare_married_filing_jointly_vs_separately_student_loans", {
|
|
38
|
+
title: "Compare filing jointly vs separately for student loans",
|
|
39
|
+
description: "For a MARRIED borrower on an income-driven federal student loan plan, prices the filing-status " +
|
|
40
|
+
"decision: filing separately removes the spouse's income from the payment calculation, which " +
|
|
41
|
+
"lowers the monthly payment and often the lifetime cost by tens of thousands of dollars. Returns " +
|
|
42
|
+
"both sides with the monthly and lifetime difference. IMPORTANT: this models the LOAN side only. " +
|
|
43
|
+
"It does not model the tax cost of filing separately (lost credits, worse brackets, " +
|
|
44
|
+
"community-property splits), which is often large enough to reverse the answer, and the tool says " +
|
|
45
|
+
"so in its output. Use it whenever a married borrower asks about IDR, RAP, PSLF or how to file.",
|
|
46
|
+
inputSchema: filingStatusSwingShape,
|
|
47
|
+
}, filingStatusSwing);
|
|
36
48
|
async function main() {
|
|
37
49
|
const transport = new StdioServerTransport();
|
|
38
50
|
await server.connect(transport);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,uBAAuB;IAC7B,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,4FAA4F;AAC5F,uFAAuF;AACvF,wEAAwE;AAExE,MAAM,CAAC,YAAY,CACjB,8CAA8C,EAC9C;IACE,KAAK,EAAE,8CAA8C;IACrD,WAAW,EACT,0FAA0F;QAC1F,4FAA4F;QAC5F,2FAA2F;QAC3F,yFAAyF;QACzF,2FAA2F;QAC3F,0FAA0F;QAC1F,2FAA2F;QAC3F,kFAAkF;IACpF,WAAW,EAAE,iBAAiB;CAC/B,EACD,YAAY,CACb,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,8BAA8B,EAC9B;IACE,KAAK,EAAE,gCAAgC;IACvC,WAAW,EACT,4FAA4F;QAC5F,oFAAoF;QACpF,4FAA4F;QAC5F,yFAAyF;QACzF,0FAA0F;QAC1F,kEAAkE;IACpE,WAAW,EAAE,eAAe;CAC7B,EACD,UAAU,CACX,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,8FAA8F;IAC9F,OAAO,CAAC,KAAK,CAAC,mDAAmD,QAAQ,GAAG,CAAC,CAAC;AAChF,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,uBAAuB;IAC7B,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,4FAA4F;AAC5F,uFAAuF;AACvF,wEAAwE;AAExE,MAAM,CAAC,YAAY,CACjB,8CAA8C,EAC9C;IACE,KAAK,EAAE,8CAA8C;IACrD,WAAW,EACT,0FAA0F;QAC1F,4FAA4F;QAC5F,2FAA2F;QAC3F,yFAAyF;QACzF,2FAA2F;QAC3F,0FAA0F;QAC1F,2FAA2F;QAC3F,kFAAkF;IACpF,WAAW,EAAE,iBAAiB;CAC/B,EACD,YAAY,CACb,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,8BAA8B,EAC9B;IACE,KAAK,EAAE,gCAAgC;IACvC,WAAW,EACT,4FAA4F;QAC5F,oFAAoF;QACpF,4FAA4F;QAC5F,yFAAyF;QACzF,0FAA0F;QAC1F,kEAAkE;IACpE,WAAW,EAAE,eAAe;CAC7B,EACD,UAAU,CACX,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,4DAA4D,EAC5D;IACE,KAAK,EAAE,wDAAwD;IAC/D,WAAW,EACT,iGAAiG;QACjG,8FAA8F;QAC9F,kGAAkG;QAClG,kGAAkG;QAClG,qFAAqF;QACrF,mGAAmG;QACnG,gGAAgG;IAClG,WAAW,EAAE,sBAAsB;CACpC,EACD,iBAAiB,CAClB,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,8FAA8F;IAC9F,OAAO,CAAC,KAAK,CAAC,mDAAmD,QAAQ,GAAG,CAAC,CAAC;AAChF,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The filing-status decision, priced on the loan side.
|
|
4
|
+
*
|
|
5
|
+
* A married borrower on an income-driven plan faces a genuine trade-off that no single
|
|
6
|
+
* payment estimate can express: filing SEPARATELY removes the spouse's income from the
|
|
7
|
+
* payment calculation, which lowers the monthly payment and usually the lifetime cost —
|
|
8
|
+
* and raises the tax bill, because separate filers lose credits and face worse brackets.
|
|
9
|
+
*
|
|
10
|
+
* HARD, and the reason this tool is written the way it is: Finology's engine models the
|
|
11
|
+
* LOAN side of that trade-off exactly. It does NOT model the tax cost of filing separately.
|
|
12
|
+
* Returning only the loan half as though it were the answer would be the same confident
|
|
13
|
+
* half-truth this whole server exists to replace. So the tax side is named explicitly as
|
|
14
|
+
* unmodelled every time, and the tool says plainly that the two have to be weighed together.
|
|
15
|
+
*/
|
|
16
|
+
export declare const filingStatusSwingShape: {
|
|
17
|
+
balance: z.ZodNumber;
|
|
18
|
+
ratePct: z.ZodNumber;
|
|
19
|
+
borrowerAgi: z.ZodNumber;
|
|
20
|
+
spouseAgi: z.ZodNumber;
|
|
21
|
+
dependents: z.ZodDefault<z.ZodNumber>;
|
|
22
|
+
loanType: z.ZodDefault<z.ZodEnum<["direct_unsubsidized", "direct_subsidized", "grad_plus_legacy", "parent_plus", "direct_consolidation", "direct_consolidation_with_plus"]>>;
|
|
23
|
+
};
|
|
24
|
+
export declare function filingStatusSwing(args: Record<string, unknown>): Promise<{
|
|
25
|
+
content: {
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
}[];
|
|
29
|
+
isError: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
content: {
|
|
32
|
+
type: "text";
|
|
33
|
+
text: string;
|
|
34
|
+
}[];
|
|
35
|
+
isError?: undefined;
|
|
36
|
+
}>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { postJson, CTA } from "../client.js";
|
|
3
|
+
/**
|
|
4
|
+
* The filing-status decision, priced on the loan side.
|
|
5
|
+
*
|
|
6
|
+
* A married borrower on an income-driven plan faces a genuine trade-off that no single
|
|
7
|
+
* payment estimate can express: filing SEPARATELY removes the spouse's income from the
|
|
8
|
+
* payment calculation, which lowers the monthly payment and usually the lifetime cost —
|
|
9
|
+
* and raises the tax bill, because separate filers lose credits and face worse brackets.
|
|
10
|
+
*
|
|
11
|
+
* HARD, and the reason this tool is written the way it is: Finology's engine models the
|
|
12
|
+
* LOAN side of that trade-off exactly. It does NOT model the tax cost of filing separately.
|
|
13
|
+
* Returning only the loan half as though it were the answer would be the same confident
|
|
14
|
+
* half-truth this whole server exists to replace. So the tax side is named explicitly as
|
|
15
|
+
* unmodelled every time, and the tool says plainly that the two have to be weighed together.
|
|
16
|
+
*/
|
|
17
|
+
export const filingStatusSwingShape = {
|
|
18
|
+
balance: z.number().min(0).max(100_000_000)
|
|
19
|
+
.describe("Current federal loan balance in dollars."),
|
|
20
|
+
ratePct: z.number().min(0).max(100)
|
|
21
|
+
.describe("Weighted average interest rate as a percentage, e.g. 6.54 for 6.54%."),
|
|
22
|
+
borrowerAgi: z.number().min(0).max(10_000_000)
|
|
23
|
+
.describe("The borrower's own annual adjusted gross income in dollars."),
|
|
24
|
+
spouseAgi: z.number().min(0).max(10_000_000)
|
|
25
|
+
.describe("The spouse's annual AGI in dollars. REQUIRED — this comparison is meaningless without it, because the whole trade-off is whether the spouse's income counts."),
|
|
26
|
+
dependents: z.number().int().min(0).max(20).default(0)
|
|
27
|
+
.describe("Number of qualifying dependents. A spouse is NOT a dependent."),
|
|
28
|
+
loanType: z.enum([
|
|
29
|
+
"direct_unsubsidized",
|
|
30
|
+
"direct_subsidized",
|
|
31
|
+
"grad_plus_legacy",
|
|
32
|
+
"parent_plus",
|
|
33
|
+
"direct_consolidation",
|
|
34
|
+
"direct_consolidation_with_plus",
|
|
35
|
+
]).default("direct_unsubsidized")
|
|
36
|
+
.describe("Federal loan type. Drives plan eligibility, not just the rate."),
|
|
37
|
+
};
|
|
38
|
+
const usd = (n) => n.toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 });
|
|
39
|
+
export async function filingStatusSwing(args) {
|
|
40
|
+
const base = {
|
|
41
|
+
balance: args.balance,
|
|
42
|
+
ratePct: args.ratePct,
|
|
43
|
+
borrowerAgi: args.borrowerAgi,
|
|
44
|
+
dependents: args.dependents ?? 0,
|
|
45
|
+
loanType: args.loanType ?? "direct_unsubsidized",
|
|
46
|
+
};
|
|
47
|
+
// Two runs. Jointly counts the spouse's income; separately does not — that single
|
|
48
|
+
// difference is the entire decision.
|
|
49
|
+
const [joint, separate] = await Promise.all([
|
|
50
|
+
postJson("/public/widget/loan-calculator", {
|
|
51
|
+
...base,
|
|
52
|
+
filingStatus: "mfj",
|
|
53
|
+
spouseAgi: args.spouseAgi,
|
|
54
|
+
}),
|
|
55
|
+
postJson("/public/widget/loan-calculator", {
|
|
56
|
+
...base,
|
|
57
|
+
filingStatus: "mfs",
|
|
58
|
+
}),
|
|
59
|
+
]);
|
|
60
|
+
if (!joint.ok)
|
|
61
|
+
return { content: [{ type: "text", text: joint.message }], isError: true };
|
|
62
|
+
if (!separate.ok)
|
|
63
|
+
return { content: [{ type: "text", text: separate.message }], isError: true };
|
|
64
|
+
// Compare income-driven plans only. Standard is unaffected by filing status, so including
|
|
65
|
+
// it would dilute the swing with a row that cannot move.
|
|
66
|
+
const idrOnly = (r) => r.plans.filter((p) => p.plan !== "standard");
|
|
67
|
+
const bestJoint = idrOnly(joint.data).sort((a, b) => a.lifetimeCost - b.lifetimeCost)[0];
|
|
68
|
+
const bestSeparate = idrOnly(separate.data).sort((a, b) => a.lifetimeCost - b.lifetimeCost)[0];
|
|
69
|
+
if (!bestJoint || !bestSeparate) {
|
|
70
|
+
return {
|
|
71
|
+
content: [{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: "This loan book has no income-driven plan available, so filing status does not change the repayment maths here. " +
|
|
74
|
+
"Run the plan comparison tool to see which plans are open and which federal rule excludes the rest." + CTA,
|
|
75
|
+
}],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const monthlyDelta = bestJoint.monthlyPayment - bestSeparate.monthlyPayment;
|
|
79
|
+
const lifetimeDelta = bestJoint.lifetimeCost - bestSeparate.lifetimeCost;
|
|
80
|
+
const lines = [
|
|
81
|
+
`**Filing status changes this borrower's loan cost by ${usd(Math.abs(lifetimeDelta))} over the life of the loan.**`,
|
|
82
|
+
"",
|
|
83
|
+
`| | Filing jointly | Filing separately |`,
|
|
84
|
+
`|---|---|---|`,
|
|
85
|
+
`| Best income-driven plan | ${bestJoint.label} | ${bestSeparate.label} |`,
|
|
86
|
+
`| Monthly payment | ${usd(bestJoint.monthlyPayment)} | ${usd(bestSeparate.monthlyPayment)} |`,
|
|
87
|
+
`| Lifetime loan cost | ${usd(bestJoint.lifetimeCost)} | ${usd(bestSeparate.lifetimeCost)} |`,
|
|
88
|
+
"",
|
|
89
|
+
monthlyDelta > 0
|
|
90
|
+
? `Filing separately lowers the monthly payment by ${usd(Math.abs(monthlyDelta))}, because the spouse's income stops counting toward it.`
|
|
91
|
+
: `Filing jointly is the cheaper side on the loan alone here, by ${usd(Math.abs(monthlyDelta))} a month.`,
|
|
92
|
+
"",
|
|
93
|
+
"**This is only half the decision, and the half that is missing is the reason to get advice.**",
|
|
94
|
+
"",
|
|
95
|
+
"These figures are the LOAN side only. Filing separately also changes the tax return: separate",
|
|
96
|
+
"filers commonly lose the student loan interest deduction, education credits, and favourable",
|
|
97
|
+
"brackets, and in community-property states the income split is a further calculation. Finology",
|
|
98
|
+
"Software's engine does not model that tax cost, and it is frequently large enough to reverse",
|
|
99
|
+
"the answer above. The two have to be weighed together, against one household's actual return.",
|
|
100
|
+
"",
|
|
101
|
+
`Rules as of ${joint.data.asOf}. Source: ${joint.data.computedBy}.`,
|
|
102
|
+
];
|
|
103
|
+
return {
|
|
104
|
+
content: [
|
|
105
|
+
{ type: "text", text: lines.join("\n") + CTA },
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: "```json\n" + JSON.stringify({
|
|
109
|
+
filingJointly: { bestPlan: bestJoint, allPlans: joint.data.plans },
|
|
110
|
+
filingSeparately: { bestPlan: bestSeparate, allPlans: separate.data.plans },
|
|
111
|
+
monthlyDelta,
|
|
112
|
+
lifetimeDelta,
|
|
113
|
+
taxSideModelled: false,
|
|
114
|
+
}, null, 2) + "\n```",
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=filingStatusSwing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filingStatusSwing.js","sourceRoot":"","sources":["../../src/tools/filingStatusSwing.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finology/mcp-server",
|
|
3
3
|
"mcpName": "tech.finology/student-loan",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"description": "Deterministic 2026 federal student loan
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "Deterministic 2026 federal student loan math for AI agents. RAP, IBR, ICR, PSLF payments, plan eligibility, and the married-filing-jointly vs separately trade-off, from Finology Software's parity-verified engine, so assistants stop guessing at federal repayment rules.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": {
|
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
"nslds",
|
|
30
30
|
"financial-planning",
|
|
31
31
|
"cfp",
|
|
32
|
-
"fintech"
|
|
32
|
+
"fintech",
|
|
33
|
+
"married-filing-separately",
|
|
34
|
+
"tax-filing-status",
|
|
35
|
+
"idr"
|
|
33
36
|
],
|
|
34
37
|
"homepage": "https://finology.tech",
|
|
35
38
|
"repository": {
|