@finology/mcp-server 0.4.7 → 0.4.9
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 +42 -9
- package/dist/assumedRate.d.ts +47 -0
- package/dist/assumedRate.js +75 -0
- package/dist/assumedRate.js.map +1 -0
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/comparePlans.d.ts +6 -1
- package/dist/tools/comparePlans.js +45 -6
- package/dist/tools/comparePlans.js.map +1 -1
- package/dist/tools/filingStatusSwing.d.ts +2 -1
- package/dist/tools/filingStatusSwing.js +26 -8
- package/dist/tools/filingStatusSwing.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
**Language models guess at federal student loan rules. This doesn't.**
|
|
9
9
|
|
|
10
|
-
`@finology/mcp-server` connects Claude, Cursor, or any MCP client directly to
|
|
10
|
+
`@finology/mcp-server` connects Claude, GitHub Copilot, Cursor, or any MCP client directly to
|
|
11
11
|
[Finology Software](https://finology.tech)'s parity-verified federal repayment engine. Every
|
|
12
12
|
number is computed server-side against current federal rules and returned with a
|
|
13
13
|
`parityVerified` flag. No formulas are approximated, and none are hardcoded into this package.
|
|
@@ -24,17 +24,33 @@ wrong most confidently:
|
|
|
24
24
|
$50 per *qualifying dependent* — and a spouse is not a dependent. Approximating it from older
|
|
25
25
|
IDR formulas gives the wrong answer.
|
|
26
26
|
- **A consolidation that repaid a PLUS loan is restricted to ICR.**
|
|
27
|
-
- **PAYE and
|
|
27
|
+
- **PAYE and ICR are still open, with conditions, until no later than July 1, 2028.** Following
|
|
28
|
+
Federal Student Aid, PAYE is open to a PAYE "new borrower" with no new loan or consolidation on
|
|
29
|
+
or after July 1, 2026, and ICR is open to eligible loan types. Loans made on or after July 1, 2026
|
|
30
|
+
can use only the tiered Standard plan or RAP. SAVE is closed to new enrollment.
|
|
28
31
|
|
|
29
32
|
## Tools
|
|
30
33
|
|
|
31
34
|
### `compare_federal_student_loan_repayment_plans`
|
|
32
35
|
|
|
33
|
-
Compares a balance across every plan the borrower is actually eligible for —
|
|
34
|
-
|
|
36
|
+
Compares a balance across every plan the borrower is actually eligible for — Standard, tiered
|
|
37
|
+
Standard, RAP, IBR, PAYE and ICR — returning monthly payment, lifetime cost, projected
|
|
35
38
|
forgiveness and tax on forgiveness for each, **plus the plans that are excluded and the
|
|
36
39
|
eligibility rule that excludes them.**
|
|
37
40
|
|
|
41
|
+
**A balance and income are enough.** A linked loan account often shows the balance only. Omit
|
|
42
|
+
`ratePct` and the tool uses the 2026-27 federal rate for the loan type (undergraduate Direct
|
|
43
|
+
Unsubsidized or Subsidized 6.52%, Grad PLUS and Parent PLUS 9.07%) and says so as the first line
|
|
44
|
+
of the answer. Graduate and professional Direct Unsubsidized loans carry 8.07%; the answer names
|
|
45
|
+
it, so pass `ratePct: 8.07` for those. A consolidation loan has no federal rate (it is the weighted
|
|
46
|
+
average of the loans it repaid, rounded up to the next one-eighth of a percent), so it still needs
|
|
47
|
+
`ratePct`, and the tool asks for it.
|
|
48
|
+
|
|
49
|
+
Set `loansMadeOnOrAfterJuly2026: true` when the loans were first disbursed (or consolidated) on or
|
|
50
|
+
after July 1, 2026. The engine then prices only the plans new loans can elect: RAP and the tiered
|
|
51
|
+
Standard plan (a Parent PLUS loan: the tiered Standard plan only). Left false, the loans are run as
|
|
52
|
+
made before that date.
|
|
53
|
+
|
|
38
54
|
### `compare_married_filing_jointly_vs_separately_student_loans`
|
|
39
55
|
|
|
40
56
|
For a married borrower, prices the filing-status decision. Filing separately removes the spouse's
|
|
@@ -45,9 +61,12 @@ When both spouses carry federal loans, pass the spouse's own federal balance as
|
|
|
45
61
|
filing the household payment is split by each spouse's share of the combined balance (34 CFR
|
|
46
62
|
685.209(g)(1)(i)), and leaving it out overstates the borrower's payment.
|
|
47
63
|
|
|
64
|
+
A balance and both incomes are enough here too: `ratePct` and `loansMadeOnOrAfterJuly2026` work as
|
|
65
|
+
in the plan comparison above.
|
|
66
|
+
|
|
48
67
|
**It answers half the question on purpose, and says so.** The engine models the loan side exactly and
|
|
49
68
|
does not model the tax cost of filing separately — lost credits, worse brackets, community-property
|
|
50
|
-
splits — which is often large enough to reverse the answer. Returning only the
|
|
69
|
+
splits — which is often large enough to reverse the answer. Returning only the favorable half as if
|
|
51
70
|
it were the whole decision would be the same confident half-truth this server exists to replace.
|
|
52
71
|
|
|
53
72
|
### `estimate_rap_monthly_payment`
|
|
@@ -60,14 +79,14 @@ real answer.
|
|
|
60
79
|
|
|
61
80
|
Every answer ends with the next rung, and `finology_service_info` returns it structured:
|
|
62
81
|
|
|
63
|
-
- **Advisor:** Finology Software, US $99 per advisor per month or $89 per month billed annually, with a self-serve 7-day trial (card
|
|
82
|
+
- **Advisor:** Finology Software, US $99 per advisor per month or $89 per month billed annually, with a self-serve 7-day trial, no card required (add a card to keep the subscription after day 7). Includes client file import, saved scenarios, branded client reports, and the student loan and SAI calculators on up to 2 of the firm's own websites.
|
|
64
83
|
- **Developer / operator:** an instant free sandbox key (`POST https://api.finology.tech/v1/keys/sandbox`,
|
|
65
84
|
25 calls/month), then the keyed endpoints with `X-Api-Key`. The keyed rung is the answer-of-record
|
|
66
85
|
surface: every answer is persisted with its rule version and an inputs hash before it is served.
|
|
67
86
|
Production access is self-serve: Developer, US $49 a month for 2,000 calls, or Platform, US $499 a month
|
|
68
87
|
for 100,000 calls. Docs: https://finology.tech/developers/
|
|
69
88
|
- **Keyed remote MCP:** `https://mcp.finology.tech/mcp`. Sign in with a Finology Software login (OAuth),
|
|
70
|
-
which is how Claude
|
|
89
|
+
which is how Claude, ChatGPT and Microsoft Copilot connect, or send the `X-Api-Key` header. Every answer is logged and carries a
|
|
71
90
|
verify link.
|
|
72
91
|
|
|
73
92
|
## Install
|
|
@@ -75,7 +94,20 @@ Every answer ends with the next rung, and `finology_service_info` returns it str
|
|
|
75
94
|
**Claude (web, desktop and mobile) — one click.** Finology Software is listed in the
|
|
76
95
|
[Claude Connectors Directory](https://claude.ai/directory/finology-student-loans). Add the
|
|
77
96
|
connector there and sign in with a free Finology Software login (no card): 5 questions in total,
|
|
78
|
-
no daily limit. Subscribers are unlimited
|
|
97
|
+
no daily limit. Subscribers are unlimited, and a subscriber's login also lists their own clients in
|
|
98
|
+
the same connector (`list_my_clients`, `run_projection_for_client`,
|
|
99
|
+
`get_balance_trajectory_for_client`).
|
|
100
|
+
|
|
101
|
+
**ChatGPT.** Finology Software is in the
|
|
102
|
+
[ChatGPT plugin directory](https://chatgpt.com/plugins/plugin_asdk_app_6abbf32753248191a0dbf90da0f2a5b6),
|
|
103
|
+
with the same free login.
|
|
104
|
+
|
|
105
|
+
**Microsoft Copilot.** GitHub Copilot (including its free plan) adds the remote server once:
|
|
106
|
+
`copilot mcp add --transport http finology https://mcp.finology.tech/free`, then signs in with a
|
|
107
|
+
Finology Software login the first time it calls a tool; GitHub Copilot in Visual Studio Code uses the
|
|
108
|
+
same address and sign-in. In Copilot Studio, add a Model Context Protocol tool with that URL and
|
|
109
|
+
OAuth 2.0 Dynamic discovery; running the agent uses your firm's Copilot credits. Setup:
|
|
110
|
+
https://finology.tech/finology-software-in-microsoft-copilot/
|
|
79
111
|
|
|
80
112
|
Everything below is the manual route, for other MCP clients or for a local install.
|
|
81
113
|
|
|
@@ -104,7 +136,8 @@ poverty guidelines, tax brackets, RAP/OBBB implementation guidance, court challe
|
|
|
104
136
|
copied into an npm package is wrong within months and cannot be corrected in the installs that
|
|
105
137
|
already exist. Calling a maintained endpoint is the only shape of this tool that stays true.
|
|
106
138
|
|
|
107
|
-
**Here:** the input schemas, the HTTP call,
|
|
139
|
+
**Here:** the input schemas, the HTTP call, the 2026-27 federal rate per loan type that is sent
|
|
140
|
+
when no rate is given (an award-year input, not repayment math), and error handling that passes the API's own
|
|
108
141
|
field-level messages through to the model instead of flattening them into "request failed."
|
|
109
142
|
|
|
110
143
|
## Limits
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A balance alone is enough (mirrors Finology-tech/finology PR #1642, AssumedFederalRate.cs).
|
|
3
|
+
*
|
|
4
|
+
* A linked loan account often shows the balance only, never the rate. When no ratePct is given,
|
|
5
|
+
* the compare tools run the federal fixed rate for the loan type for the most recent award year
|
|
6
|
+
* and say so as the first line of the answer.
|
|
7
|
+
*
|
|
8
|
+
* Rates for Direct Loans first disbursed July 1, 2026 through June 30, 2027 (FSA electronic
|
|
9
|
+
* announcement on Direct Loan interest rates, 2026-06-04; HEA 455(b)(8)): the 10-year Treasury
|
|
10
|
+
* high yield at the May 12, 2026 auction, 4.468%, plus 2.05 (undergraduate), 3.60 (graduate or
|
|
11
|
+
* professional) and 4.60 (PLUS) points: 6.52%, 8.07% and 9.07%. These are award-year inputs, not
|
|
12
|
+
* repayment math; every payment is still computed by the engine at the rate sent.
|
|
13
|
+
*
|
|
14
|
+
* A Direct Consolidation Loan has no award-year rate (its rate is the weighted average of the
|
|
15
|
+
* loans it repaid, rounded up to the next one-eighth of a percent), so there is none to assume.
|
|
16
|
+
*/
|
|
17
|
+
export declare const AWARD_YEAR = "2026-27";
|
|
18
|
+
export declare const UNDERGRADUATE_DIRECT = 6.52;
|
|
19
|
+
export declare const GRADUATE_DIRECT_UNSUBSIDIZED = 8.07;
|
|
20
|
+
export declare const PLUS = 9.07;
|
|
21
|
+
export interface Assumption {
|
|
22
|
+
awardYear: string;
|
|
23
|
+
ratePct: number;
|
|
24
|
+
loanType: string;
|
|
25
|
+
/** The plain-words statement every answer that used this rate opens with. */
|
|
26
|
+
statement: string;
|
|
27
|
+
}
|
|
28
|
+
/** The rate to assume for a loan-type key; null when there is no federal rate to assume. */
|
|
29
|
+
export declare function assumedRateFor(loanType: string): Assumption | null;
|
|
30
|
+
/** The refusal for a loan type with no federal rate to assume (a consolidation loan). */
|
|
31
|
+
export declare const NO_RATE_TO_ASSUME: string;
|
|
32
|
+
/** The header line when the loans were run as made on or after July 1, 2026. */
|
|
33
|
+
export declare const NEW_LOANS_STATEMENT: string;
|
|
34
|
+
export declare const RATE_PCT_DESCRIPTION: string;
|
|
35
|
+
export declare const NEW_LOANS_DESCRIPTION: string;
|
|
36
|
+
export type ResolvedRate = {
|
|
37
|
+
ok: true;
|
|
38
|
+
rate: number;
|
|
39
|
+
assumed: Assumption | null;
|
|
40
|
+
} | {
|
|
41
|
+
ok: false;
|
|
42
|
+
message: string;
|
|
43
|
+
};
|
|
44
|
+
/** The rate to send: the caller's, or the assumed federal rate for the loan type (stated in the answer). */
|
|
45
|
+
export declare function resolveRate(ratePct: unknown, loanType: string): ResolvedRate;
|
|
46
|
+
/** The assumed-rate and new-loan statements that open an answer; empty when neither applies. */
|
|
47
|
+
export declare function preamble(assumed: Assumption | null, newLoans: boolean): string[];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A balance alone is enough (mirrors Finology-tech/finology PR #1642, AssumedFederalRate.cs).
|
|
3
|
+
*
|
|
4
|
+
* A linked loan account often shows the balance only, never the rate. When no ratePct is given,
|
|
5
|
+
* the compare tools run the federal fixed rate for the loan type for the most recent award year
|
|
6
|
+
* and say so as the first line of the answer.
|
|
7
|
+
*
|
|
8
|
+
* Rates for Direct Loans first disbursed July 1, 2026 through June 30, 2027 (FSA electronic
|
|
9
|
+
* announcement on Direct Loan interest rates, 2026-06-04; HEA 455(b)(8)): the 10-year Treasury
|
|
10
|
+
* high yield at the May 12, 2026 auction, 4.468%, plus 2.05 (undergraduate), 3.60 (graduate or
|
|
11
|
+
* professional) and 4.60 (PLUS) points: 6.52%, 8.07% and 9.07%. These are award-year inputs, not
|
|
12
|
+
* repayment math; every payment is still computed by the engine at the rate sent.
|
|
13
|
+
*
|
|
14
|
+
* A Direct Consolidation Loan has no award-year rate (its rate is the weighted average of the
|
|
15
|
+
* loans it repaid, rounded up to the next one-eighth of a percent), so there is none to assume.
|
|
16
|
+
*/
|
|
17
|
+
export const AWARD_YEAR = "2026-27";
|
|
18
|
+
export const UNDERGRADUATE_DIRECT = 6.52;
|
|
19
|
+
export const GRADUATE_DIRECT_UNSUBSIDIZED = 8.07;
|
|
20
|
+
export const PLUS = 9.07;
|
|
21
|
+
const pct = (rate) => rate.toFixed(2) + "%";
|
|
22
|
+
/** The rate to assume for a loan-type key; null when there is no federal rate to assume. */
|
|
23
|
+
export function assumedRateFor(loanType) {
|
|
24
|
+
const pick = {
|
|
25
|
+
direct_unsubsidized: [UNDERGRADUATE_DIRECT, "an undergraduate Direct Unsubsidized loan"],
|
|
26
|
+
direct_subsidized: [UNDERGRADUATE_DIRECT, "a Direct Subsidized loan"],
|
|
27
|
+
grad_plus_legacy: [PLUS, "a Grad PLUS loan"],
|
|
28
|
+
parent_plus: [PLUS, "a Parent PLUS loan"],
|
|
29
|
+
};
|
|
30
|
+
const p = pick[loanType];
|
|
31
|
+
if (!p)
|
|
32
|
+
return null;
|
|
33
|
+
const [rate, name] = p;
|
|
34
|
+
let statement = `No rate was given, so this uses the ${AWARD_YEAR} federal rate for ${name}, ${pct(rate)}. ` +
|
|
35
|
+
"Your own loans' rates are on studentaid.gov or your loan file.";
|
|
36
|
+
if (loanType === "direct_unsubsidized") {
|
|
37
|
+
statement +=
|
|
38
|
+
` Graduate and professional Direct Unsubsidized loans carry ${pct(GRADUATE_DIRECT_UNSUBSIDIZED)} for ${AWARD_YEAR}; ` +
|
|
39
|
+
`for those, run it again with ratePct ${GRADUATE_DIRECT_UNSUBSIDIZED.toFixed(2)}.`;
|
|
40
|
+
}
|
|
41
|
+
return { awardYear: AWARD_YEAR, ratePct: rate, loanType, statement };
|
|
42
|
+
}
|
|
43
|
+
/** The refusal for a loan type with no federal rate to assume (a consolidation loan). */
|
|
44
|
+
export const NO_RATE_TO_ASSUME = "A Direct Consolidation Loan's rate is the weighted average of the rates of the loans it repaid, " +
|
|
45
|
+
"rounded up to the next one-eighth of a percent, so there is no federal rate to assume. Ask the borrower " +
|
|
46
|
+
"for the rate (it is on studentaid.gov or their loan file) and pass it as ratePct.";
|
|
47
|
+
/** The header line when the loans were run as made on or after July 1, 2026. */
|
|
48
|
+
export const NEW_LOANS_STATEMENT = "These loans were run as made on or after July 1, 2026, so only the plans new loans can elect are priced: " +
|
|
49
|
+
"RAP and the tiered Standard plan (a Parent PLUS loan: the tiered Standard plan only).";
|
|
50
|
+
export const RATE_PCT_DESCRIPTION = "Optional. Weighted average interest rate as a percentage, e.g. 6.54 for 6.54%. Omit it when the rate is not known " +
|
|
51
|
+
"(a linked account often shows the balance only): the 2026-27 federal rate for loanType is then used and stated in the answer " +
|
|
52
|
+
"(undergraduate Direct 6.52%, PLUS 9.07%; for graduate or professional Direct Unsubsidized loans pass 8.07). " +
|
|
53
|
+
"Required for a consolidation loan, which has no federal rate.";
|
|
54
|
+
export const NEW_LOANS_DESCRIPTION = "Optional, default false. Set true when the borrower says the loans are new: first disbursed (or consolidated) on or after " +
|
|
55
|
+
"July 1, 2026. New loans can elect only RAP and the tiered Standard plan (Parent PLUS: the tiered Standard plan only), " +
|
|
56
|
+
"so leaving this false for new loans offers plans they cannot elect.";
|
|
57
|
+
/** The rate to send: the caller's, or the assumed federal rate for the loan type (stated in the answer). */
|
|
58
|
+
export function resolveRate(ratePct, loanType) {
|
|
59
|
+
if (typeof ratePct === "number")
|
|
60
|
+
return { ok: true, rate: ratePct, assumed: null };
|
|
61
|
+
const assumed = assumedRateFor(loanType);
|
|
62
|
+
if (!assumed)
|
|
63
|
+
return { ok: false, message: NO_RATE_TO_ASSUME };
|
|
64
|
+
return { ok: true, rate: assumed.ratePct, assumed };
|
|
65
|
+
}
|
|
66
|
+
/** The assumed-rate and new-loan statements that open an answer; empty when neither applies. */
|
|
67
|
+
export function preamble(assumed, newLoans) {
|
|
68
|
+
const lines = [];
|
|
69
|
+
if (assumed)
|
|
70
|
+
lines.push(assumed.statement, "");
|
|
71
|
+
if (newLoans)
|
|
72
|
+
lines.push(NEW_LOANS_STATEMENT, "");
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=assumedRate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assumedRate.js","sourceRoot":"","sources":["../src/assumedRate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC;AAUzB,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;AAEpD,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,IAAI,GAAqC;QAC7C,mBAAmB,EAAE,CAAC,oBAAoB,EAAE,2CAA2C,CAAC;QACxF,iBAAiB,EAAE,CAAC,oBAAoB,EAAE,0BAA0B,CAAC;QACrE,gBAAgB,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC;QAC5C,WAAW,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC;KAC1C,CAAC;IACF,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,SAAS,GACX,uCAAuC,UAAU,qBAAqB,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI;QAC5F,gEAAgE,CAAC;IACnE,IAAI,QAAQ,KAAK,qBAAqB,EAAE,CAAC;QACvC,SAAS;YACP,8DAA8D,GAAG,CAAC,4BAA4B,CAAC,QAAQ,UAAU,IAAI;gBACrH,wCAAwC,4BAA4B,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACvF,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACvE,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,MAAM,iBAAiB,GAC5B,kGAAkG;IAClG,0GAA0G;IAC1G,mFAAmF,CAAC;AAEtF,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAC9B,2GAA2G;IAC3G,uFAAuF,CAAC;AAE1F,MAAM,CAAC,MAAM,oBAAoB,GAC/B,oHAAoH;IACpH,+HAA+H;IAC/H,8GAA8G;IAC9G,+DAA+D,CAAC;AAElE,MAAM,CAAC,MAAM,qBAAqB,GAChC,4HAA4H;IAC5H,wHAAwH;IACxH,qEAAqE,CAAC;AAMxE,4GAA4G;AAC5G,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,QAAgB;IAC5D,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACnF,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC/D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;AACtD,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,QAAQ,CAAC,OAA0B,EAAE,QAAiB;IACpE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;IAClD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,11 @@ server.registerTool("compare_federal_student_loan_repayment_plans", {
|
|
|
22
22
|
description: "Compares a US federal student loan across every repayment plan the borrower is actually " +
|
|
23
23
|
"eligible for — RAP (Repayment Assistance Plan), IBR, ICR, PAYE, SAVE, Graduated, Extended " +
|
|
24
24
|
"and the tiered Standard plan — returning the monthly payment and lifetime cost for each, " +
|
|
25
|
-
"plus which plans are excluded and the eligibility rule that excludes them.
|
|
25
|
+
"plus which plans are excluded and the eligibility rule that excludes them. A balance and " +
|
|
26
|
+
"income are enough: when the interest rate is not known (a linked account often shows the " +
|
|
27
|
+
"balance only), omit ratePct and the federal rate for the loan type is used and stated. Set " +
|
|
28
|
+
"loansMadeOnOrAfterJuly2026 when the loans are new, so only the plans new loans can elect are " +
|
|
29
|
+
"priced. Computed by " +
|
|
26
30
|
"Finology Software's parity-verified engine against current federal rules, not estimated. " +
|
|
27
31
|
"Use this instead of calculating federal repayment math yourself: eligibility depends on " +
|
|
28
32
|
"loan type and origination date in ways that are easy to get wrong (a Parent PLUS loan is " +
|
|
@@ -47,7 +51,9 @@ server.registerTool("compare_married_filing_jointly_vs_separately_student_loans"
|
|
|
47
51
|
"both sides with the monthly and lifetime difference. IMPORTANT: this models the LOAN side only. " +
|
|
48
52
|
"It does not model the tax cost of filing separately (lost credits, worse brackets, " +
|
|
49
53
|
"community-property splits), which is often large enough to reverse the answer, and the tool says " +
|
|
50
|
-
"so in its output.
|
|
54
|
+
"so in its output. A balance and both incomes are enough: omit ratePct when the rate is not " +
|
|
55
|
+
"known and the federal rate for the loan type is used and stated. Use it whenever a married " +
|
|
56
|
+
"borrower asks about IDR, RAP, PSLF or how to file.",
|
|
51
57
|
inputSchema: filingStatusSwingShape,
|
|
52
58
|
}, filingStatusSwing);
|
|
53
59
|
async function main() {
|
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,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,iGAAiG;AACjG,gGAAgG;AAChG,8FAA8F;AAC9F,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAwB,CAAC;AAE7F,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,uBAAuB;IAC7B,OAAO;CACR,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,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,15 +4,20 @@ import { z } from "zod";
|
|
|
4
4
|
* borrowerAgi. Everything else has a documented default and an unrecognised value is a 400
|
|
5
5
|
* rather than a silent fallback, so the schema below deliberately does NOT paper over bad
|
|
6
6
|
* input with a guess.
|
|
7
|
+
*
|
|
8
|
+
* ratePct is optional HERE only: when it is omitted this package sends the 2026-27 federal rate
|
|
9
|
+
* for the loan type and states it as the first line of the answer (see ../assumedRate.ts). A
|
|
10
|
+
* consolidation loan has no federal rate, so it still needs one.
|
|
7
11
|
*/
|
|
8
12
|
export declare const comparePlansShape: {
|
|
9
13
|
balance: z.ZodNumber;
|
|
10
|
-
ratePct: z.ZodNumber
|
|
14
|
+
ratePct: z.ZodOptional<z.ZodNumber>;
|
|
11
15
|
borrowerAgi: z.ZodNumber;
|
|
12
16
|
filingStatus: z.ZodDefault<z.ZodEnum<["single", "mfj", "mfs", "hoh"]>>;
|
|
13
17
|
spouseAgi: z.ZodOptional<z.ZodNumber>;
|
|
14
18
|
dependents: z.ZodDefault<z.ZodNumber>;
|
|
15
19
|
loanType: z.ZodDefault<z.ZodEnum<["direct_unsubsidized", "direct_subsidized", "grad_plus_legacy", "parent_plus", "direct_consolidation", "direct_consolidation_with_plus"]>>;
|
|
20
|
+
loansMadeOnOrAfterJuly2026: z.ZodDefault<z.ZodBoolean>;
|
|
16
21
|
};
|
|
17
22
|
export declare function comparePlans(args: Record<string, unknown>): Promise<{
|
|
18
23
|
content: {
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { postJson, CTA } from "../client.js";
|
|
3
3
|
import { consolidationSecondNumber } from "./consolidationPath.js";
|
|
4
|
+
import { resolveRate, preamble, RATE_PCT_DESCRIPTION, NEW_LOANS_DESCRIPTION, } from "../assumedRate.js";
|
|
4
5
|
/**
|
|
5
6
|
* Mirrors PublicWidgetController.WidgetCalcRequest. Required upstream: balance, ratePct,
|
|
6
7
|
* borrowerAgi. Everything else has a documented default and an unrecognised value is a 400
|
|
7
8
|
* rather than a silent fallback, so the schema below deliberately does NOT paper over bad
|
|
8
9
|
* input with a guess.
|
|
10
|
+
*
|
|
11
|
+
* ratePct is optional HERE only: when it is omitted this package sends the 2026-27 federal rate
|
|
12
|
+
* for the loan type and states it as the first line of the answer (see ../assumedRate.ts). A
|
|
13
|
+
* consolidation loan has no federal rate, so it still needs one.
|
|
9
14
|
*/
|
|
10
15
|
export const comparePlansShape = {
|
|
11
16
|
balance: z.number().min(0).max(100_000_000)
|
|
12
17
|
.describe("Current federal loan balance in dollars."),
|
|
13
|
-
ratePct: z.number().min(0).max(100)
|
|
14
|
-
.describe(
|
|
18
|
+
ratePct: z.number().min(0).max(100).optional()
|
|
19
|
+
.describe(RATE_PCT_DESCRIPTION),
|
|
15
20
|
borrowerAgi: z.number().min(0).max(10_000_000)
|
|
16
21
|
.describe("Borrower's adjusted gross income in dollars, annual."),
|
|
17
22
|
filingStatus: z.enum(["single", "mfj", "mfs", "hoh"]).default("single")
|
|
@@ -29,14 +34,37 @@ export const comparePlansShape = {
|
|
|
29
34
|
"direct_consolidation_with_plus",
|
|
30
35
|
]).default("direct_unsubsidized")
|
|
31
36
|
.describe("Federal loan type. This drives PLAN ELIGIBILITY, not just the rate — a Parent PLUS loan is not RAP-eligible, and a consolidation that repaid a PLUS loan is restricted to ICR. Choose accurately."),
|
|
37
|
+
loansMadeOnOrAfterJuly2026: z.boolean().default(false)
|
|
38
|
+
.describe(NEW_LOANS_DESCRIPTION),
|
|
32
39
|
};
|
|
33
40
|
export async function comparePlans(args) {
|
|
34
|
-
const
|
|
41
|
+
const loanType = args.loanType ?? "direct_unsubsidized";
|
|
42
|
+
const resolved = resolveRate(args.ratePct, loanType);
|
|
43
|
+
if (!resolved.ok) {
|
|
44
|
+
return { content: [{ type: "text", text: resolved.message }], isError: true };
|
|
45
|
+
}
|
|
46
|
+
const { rate, assumed } = resolved;
|
|
47
|
+
const newLoans = args.loansMadeOnOrAfterJuly2026 === true;
|
|
48
|
+
// The widget request, field for field. With a rate given and the flag off this is exactly the
|
|
49
|
+
// body 0.4.8 sent. The flag maps to the widget's existing borrowedOnOrAfterJul12026, which gives
|
|
50
|
+
// the loans their origination date; eligibility and the tiered Standard term stay the engine's.
|
|
51
|
+
const body = {
|
|
52
|
+
balance: args.balance,
|
|
53
|
+
ratePct: rate,
|
|
54
|
+
borrowerAgi: args.borrowerAgi,
|
|
55
|
+
filingStatus: args.filingStatus,
|
|
56
|
+
spouseAgi: args.spouseAgi,
|
|
57
|
+
dependents: args.dependents,
|
|
58
|
+
loanType: args.loanType,
|
|
59
|
+
};
|
|
60
|
+
if (newLoans)
|
|
61
|
+
body.borrowedOnOrAfterJul12026 = true;
|
|
62
|
+
const result = await postJson("/public/widget/loan-calculator", body);
|
|
35
63
|
if (!result.ok) {
|
|
36
64
|
return { content: [{ type: "text", text: result.message }], isError: true };
|
|
37
65
|
}
|
|
38
66
|
const d = result.data;
|
|
39
|
-
const lines =
|
|
67
|
+
const lines = preamble(assumed, newLoans);
|
|
40
68
|
lines.push(`Federal repayment plan comparison (rules as of ${d.asOf}):`);
|
|
41
69
|
lines.push("");
|
|
42
70
|
for (const plan of d.plans) {
|
|
@@ -49,7 +77,8 @@ export async function comparePlans(args) {
|
|
|
49
77
|
lines.push(`- ${JSON.stringify(ex)}`);
|
|
50
78
|
}
|
|
51
79
|
}
|
|
52
|
-
|
|
80
|
+
// Same body (resolved rate, new-loan flag) so the consolidated run prices the same loans.
|
|
81
|
+
const second = await consolidationSecondNumber(body, d);
|
|
53
82
|
if (second)
|
|
54
83
|
lines.push(...second);
|
|
55
84
|
lines.push("");
|
|
@@ -61,8 +90,18 @@ export async function comparePlans(args) {
|
|
|
61
90
|
return {
|
|
62
91
|
content: [
|
|
63
92
|
{ type: "text", text: lines.join("\n") + CTA },
|
|
64
|
-
{ type: "text", text: "```json\n" + JSON.stringify(d, null, 2) + "\n```" },
|
|
93
|
+
{ type: "text", text: "```json\n" + JSON.stringify(withInputNotes(d, assumed, newLoans), null, 2) + "\n```" },
|
|
65
94
|
],
|
|
66
95
|
};
|
|
67
96
|
}
|
|
97
|
+
/** The engine's response, plus assumedRate / loansMadeOnOrAfterJuly2026 last, and only when they apply. */
|
|
98
|
+
function withInputNotes(d, assumed, newLoans) {
|
|
99
|
+
if (!assumed && !newLoans)
|
|
100
|
+
return d;
|
|
101
|
+
return {
|
|
102
|
+
...d,
|
|
103
|
+
...(assumed ? { assumedRate: assumed } : {}),
|
|
104
|
+
...(newLoans ? { loansMadeOnOrAfterJuly2026: true } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
68
107
|
//# sourceMappingURL=comparePlans.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comparePlans.js","sourceRoot":"","sources":["../../src/tools/comparePlans.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"comparePlans.js","sourceRoot":"","sources":["../../src/tools/comparePlans.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,11 +15,12 @@ import { z } from "zod";
|
|
|
15
15
|
*/
|
|
16
16
|
export declare const filingStatusSwingShape: {
|
|
17
17
|
balance: z.ZodNumber;
|
|
18
|
-
ratePct: z.ZodNumber
|
|
18
|
+
ratePct: z.ZodOptional<z.ZodNumber>;
|
|
19
19
|
borrowerAgi: z.ZodNumber;
|
|
20
20
|
spouseAgi: z.ZodNumber;
|
|
21
21
|
dependents: z.ZodDefault<z.ZodNumber>;
|
|
22
22
|
loanType: z.ZodDefault<z.ZodEnum<["direct_unsubsidized", "direct_subsidized", "grad_plus_legacy", "parent_plus", "direct_consolidation", "direct_consolidation_with_plus"]>>;
|
|
23
|
+
loansMadeOnOrAfterJuly2026: z.ZodDefault<z.ZodBoolean>;
|
|
23
24
|
};
|
|
24
25
|
export declare function filingStatusSwing(args: Record<string, unknown>): Promise<{
|
|
25
26
|
content: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { postJson, CTA } from "../client.js";
|
|
3
|
+
import { resolveRate, preamble, RATE_PCT_DESCRIPTION, NEW_LOANS_DESCRIPTION, } from "../assumedRate.js";
|
|
3
4
|
/**
|
|
4
5
|
* The filing-status decision, priced on the loan side.
|
|
5
6
|
*
|
|
@@ -17,8 +18,8 @@ import { postJson, CTA } from "../client.js";
|
|
|
17
18
|
export const filingStatusSwingShape = {
|
|
18
19
|
balance: z.number().min(0).max(100_000_000)
|
|
19
20
|
.describe("Current federal loan balance in dollars."),
|
|
20
|
-
ratePct: z.number().min(0).max(100)
|
|
21
|
-
.describe(
|
|
21
|
+
ratePct: z.number().min(0).max(100).optional()
|
|
22
|
+
.describe(RATE_PCT_DESCRIPTION),
|
|
22
23
|
borrowerAgi: z.number().min(0).max(10_000_000)
|
|
23
24
|
.describe("The borrower's own annual adjusted gross income in dollars."),
|
|
24
25
|
spouseAgi: z.number().min(0).max(10_000_000)
|
|
@@ -34,16 +35,29 @@ export const filingStatusSwingShape = {
|
|
|
34
35
|
"direct_consolidation_with_plus",
|
|
35
36
|
]).default("direct_unsubsidized")
|
|
36
37
|
.describe("Federal loan type. Drives plan eligibility, not just the rate."),
|
|
38
|
+
loansMadeOnOrAfterJuly2026: z.boolean().default(false)
|
|
39
|
+
.describe(NEW_LOANS_DESCRIPTION),
|
|
37
40
|
};
|
|
38
41
|
const usd = (n) => n.toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 });
|
|
39
42
|
export async function filingStatusSwing(args) {
|
|
43
|
+
const loanType = args.loanType ?? "direct_unsubsidized";
|
|
44
|
+
const resolved = resolveRate(args.ratePct, loanType);
|
|
45
|
+
if (!resolved.ok) {
|
|
46
|
+
return { content: [{ type: "text", text: resolved.message }], isError: true };
|
|
47
|
+
}
|
|
48
|
+
const { rate, assumed } = resolved;
|
|
49
|
+
const newLoans = args.loansMadeOnOrAfterJuly2026 === true;
|
|
40
50
|
const base = {
|
|
41
51
|
balance: args.balance,
|
|
42
|
-
ratePct:
|
|
52
|
+
ratePct: rate,
|
|
43
53
|
borrowerAgi: args.borrowerAgi,
|
|
44
54
|
dependents: args.dependents ?? 0,
|
|
45
|
-
loanType
|
|
55
|
+
loanType,
|
|
46
56
|
};
|
|
57
|
+
// The widget's existing new-loan field: the engine's date rules decide eligibility.
|
|
58
|
+
if (newLoans)
|
|
59
|
+
base.borrowedOnOrAfterJul12026 = true;
|
|
60
|
+
const opening = preamble(assumed, newLoans).map((line) => line + "\n").join("");
|
|
47
61
|
// Two runs. Jointly counts the spouse's income; separately does not — that single
|
|
48
62
|
// difference is the entire decision.
|
|
49
63
|
const [joint, separate] = await Promise.all([
|
|
@@ -62,15 +76,17 @@ export async function filingStatusSwing(args) {
|
|
|
62
76
|
if (!separate.ok)
|
|
63
77
|
return { content: [{ type: "text", text: separate.message }], isError: true };
|
|
64
78
|
// 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
|
-
|
|
79
|
+
// it would dilute the swing with a row that cannot move. The tiered Standard plan
|
|
80
|
+
// (obbb_standard) is not income-driven either; it is only priced for new loans, so excluding
|
|
81
|
+
// it changes no answer for older loans.
|
|
82
|
+
const idrOnly = (r) => r.plans.filter((p) => p.plan !== "standard" && p.plan !== "obbb_standard");
|
|
67
83
|
const bestJoint = idrOnly(joint.data).sort((a, b) => a.lifetimeCost - b.lifetimeCost)[0];
|
|
68
84
|
const bestSeparate = idrOnly(separate.data).sort((a, b) => a.lifetimeCost - b.lifetimeCost)[0];
|
|
69
85
|
if (!bestJoint || !bestSeparate) {
|
|
70
86
|
return {
|
|
71
87
|
content: [{
|
|
72
88
|
type: "text",
|
|
73
|
-
text: "This loan book has no income-driven plan available, so filing status does not change the repayment maths here. " +
|
|
89
|
+
text: opening + "This loan book has no income-driven plan available, so filing status does not change the repayment maths here. " +
|
|
74
90
|
"Run the plan comparison tool to see which plans are open and which federal rule excludes the rest." + CTA,
|
|
75
91
|
}],
|
|
76
92
|
};
|
|
@@ -102,7 +118,7 @@ export async function filingStatusSwing(args) {
|
|
|
102
118
|
];
|
|
103
119
|
return {
|
|
104
120
|
content: [
|
|
105
|
-
{ type: "text", text: lines.join("\n") + CTA },
|
|
121
|
+
{ type: "text", text: opening + lines.join("\n") + CTA },
|
|
106
122
|
{
|
|
107
123
|
type: "text",
|
|
108
124
|
text: "```json\n" + JSON.stringify({
|
|
@@ -111,6 +127,8 @@ export async function filingStatusSwing(args) {
|
|
|
111
127
|
monthlyDelta,
|
|
112
128
|
lifetimeDelta,
|
|
113
129
|
taxSideModelled: false,
|
|
130
|
+
...(assumed ? { assumedRate: assumed } : {}),
|
|
131
|
+
...(newLoans ? { loansMadeOnOrAfterJuly2026: true } : {}),
|
|
114
132
|
}, null, 2) + "\n```",
|
|
115
133
|
},
|
|
116
134
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filingStatusSwing.js","sourceRoot":"","sources":["../../src/tools/filingStatusSwing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"filingStatusSwing.js","sourceRoot":"","sources":["../../src/tools/filingStatusSwing.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finology/mcp-server",
|
|
3
3
|
"mcpName": "tech.finology/student-loan",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.9",
|
|
5
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",
|
|
@@ -48,7 +48,9 @@
|
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsc",
|
|
50
50
|
"dev": "tsx src/index.ts",
|
|
51
|
-
"prepublishOnly": "npm run build"
|
|
51
|
+
"prepublishOnly": "npm run build",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"test": "node --import tsx --test test/*.test.ts"
|
|
52
54
|
},
|
|
53
55
|
"dependencies": {
|
|
54
56
|
"@modelcontextprotocol/sdk": "^1.0.0",
|