@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 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 SAVE are closed to new enrollment.**
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 — RAP, IBR, ICR, PAYE,
34
- SAVE, Graduated, Extended, tiered Standard — returning monthly payment, lifetime cost, projected
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 favourable half as if
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 on file, nothing charged until it ends). 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.
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 connects, or send the `X-Api-Key` header. Every answer is logged and carries a
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, and error handling that passes the API's own
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. Computed by " +
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. Use it whenever a married borrower asks about IDR, RAP, PSLF or how to file.",
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":"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"}
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("Weighted average interest rate as a percentage, e.g. 6.54 for 6.54%."),
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 result = await postJson("/public/widget/loan-calculator", args);
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
- const second = await consolidationSecondNumber(args, d);
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":"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"}
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;AACnE,OAAO,EACL,WAAW,EACX,QAAQ,EACR,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC;SACxC,QAAQ,CAAC,0CAA0C,CAAC;IACvD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;SAC3C,QAAQ,CAAC,oBAAoB,CAAC;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC;SAC3C,QAAQ,CAAC,sDAAsD,CAAC;IACnE,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;SACpE,QAAQ,CAAC,oHAAoH,CAAC;IACjI,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE;SACpD,QAAQ,CAAC,0DAA0D,CAAC;IACvE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;SACnD,QAAQ,CAAC,+DAA+D,CAAC;IAC5E,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC;QACf,qBAAqB;QACrB,mBAAmB;QACnB,kBAAkB;QAClB,aAAa;QACb,sBAAsB;QACtB,gCAAgC;KACjC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC;SAC9B,QAAQ,CAAC,mMAAmM,CAAC;IAChN,0BAA0B,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;SACnD,QAAQ,CAAC,qBAAqB,CAAC;CACnC,CAAC;AAWF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAA6B;IAC9D,MAAM,QAAQ,GAAI,IAAI,CAAC,QAA+B,IAAI,qBAAqB,CAAC;IAChF,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACzF,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,0BAA0B,KAAK,IAAI,CAAC;IAE1D,8FAA8F;IAC9F,iGAAiG;IACjG,gGAAgG;IAChG,MAAM,IAAI,GAA4B;QACpC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;IACF,IAAI,QAAQ;QAAE,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAEpD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAuB,gCAAgC,EAAE,IAAI,CAAC,CAAC;IAE5F,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACvF,CAAC;IAED,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;IACtB,MAAM,KAAK,GAAa,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACpD,KAAK,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IACzE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,4FAA4F,CAAC,CAAC;QACzG,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,0FAA0F;IAC1F,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC,IAAI,EAAE,CAAU,CAAC,CAAC;IACjE,IAAI,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;IAElC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,IAAI,CAAC,CAAC,iBAAiB;QAAE,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACtF,IAAI,CAAC,CAAC,sBAAsB;QAAE,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,sBAAsB,EAAE,CAAC,CAAC;IAC9F,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAE3C,OAAO;QACL,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,EAAE;YACvD,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE;SACvH;KACF,CAAC;AACJ,CAAC;AAED,2GAA2G;AAC3G,SAAS,cAAc,CAAC,CAAuB,EAAE,OAAgB,EAAE,QAAiB;IAClF,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,OAAO;QACL,GAAG,CAAC;QACJ,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,0BAA0B,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1D,CAAC;AACJ,CAAC"}
@@ -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("Weighted average interest rate as a percentage, e.g. 6.54 for 6.54%."),
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: args.ratePct,
52
+ ratePct: rate,
43
53
  borrowerAgi: args.borrowerAgi,
44
54
  dependents: args.dependents ?? 0,
45
- loanType: args.loanType ?? "direct_unsubsidized",
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
- const idrOnly = (r) => r.plans.filter((p) => p.plan !== "standard");
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":"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"}
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.7",
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",