@finology/mcp-server 0.4.8 → 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
@@ -38,6 +38,19 @@ Standard, RAP, IBR, PAYE and ICR — returning monthly payment, lifetime cost, p
38
38
  forgiveness and tax on forgiveness for each, **plus the plans that are excluded and the
39
39
  eligibility rule that excludes them.**
40
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
+
41
54
  ### `compare_married_filing_jointly_vs_separately_student_loans`
42
55
 
43
56
  For a married borrower, prices the filing-status decision. Filing separately removes the spouse's
@@ -48,6 +61,9 @@ When both spouses carry federal loans, pass the spouse's own federal balance as
48
61
  filing the household payment is split by each spouse's share of the combined balance (34 CFR
49
62
  685.209(g)(1)(i)), and leaving it out overstates the borrower's payment.
50
63
 
64
+ A balance and both incomes are enough here too: `ratePct` and `loansMadeOnOrAfterJuly2026` work as
65
+ in the plan comparison above.
66
+
51
67
  **It answers half the question on purpose, and says so.** The engine models the loan side exactly and
52
68
  does not model the tax cost of filing separately — lost credits, worse brackets, community-property
53
69
  splits — which is often large enough to reverse the answer. Returning only the favorable half as if
@@ -120,7 +136,8 @@ poverty guidelines, tax brackets, RAP/OBBB implementation guidance, court challe
120
136
  copied into an npm package is wrong within months and cannot be corrected in the installs that
121
137
  already exist. Calling a maintained endpoint is the only shape of this tool that stays true.
122
138
 
123
- **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
124
141
  field-level messages through to the model instead of flattening them into "request failed."
125
142
 
126
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":"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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("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.8",
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",