@cyanheads/college-scorecard-mcp-server 0.1.1
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/CLAUDE.md +405 -0
- package/Dockerfile +98 -0
- package/README.md +356 -0
- package/changelog/0.1.x/0.1.0.md +24 -0
- package/changelog/0.1.x/0.1.1.md +14 -0
- package/changelog/template.md +119 -0
- package/dist/config/server-config.d.ts +12 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +17 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/data/cip-codes.d.ts +23 -0
- package/dist/data/cip-codes.d.ts.map +1 -0
- package/dist/data/cip-codes.js +274 -0
- package/dist/data/cip-codes.js.map +1 -0
- package/dist/data/field-catalog.d.ts +25 -0
- package/dist/data/field-catalog.d.ts.map +1 -0
- package/dist/data/field-catalog.js +122 -0
- package/dist/data/field-catalog.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/format-helpers.d.ts +9 -0
- package/dist/mcp-server/format-helpers.d.ts.map +1 -0
- package/dist/mcp-server/format-helpers.js +24 -0
- package/dist/mcp-server/format-helpers.js.map +1 -0
- package/dist/mcp-server/prompts/definitions/compare.prompt.d.ts +15 -0
- package/dist/mcp-server/prompts/definitions/compare.prompt.d.ts.map +1 -0
- package/dist/mcp-server/prompts/definitions/compare.prompt.js +55 -0
- package/dist/mcp-server/prompts/definitions/compare.prompt.js.map +1 -0
- package/dist/mcp-server/resources/definitions/programs.resource.d.ts +10 -0
- package/dist/mcp-server/resources/definitions/programs.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/programs.resource.js +42 -0
- package/dist/mcp-server/resources/definitions/programs.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/school.resource.d.ts +10 -0
- package/dist/mcp-server/resources/definitions/school.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/school.resource.js +43 -0
- package/dist/mcp-server/resources/definitions/school.resource.js.map +1 -0
- package/dist/mcp-server/tools/definitions/compare-schools.tool.d.ts +46 -0
- package/dist/mcp-server/tools/definitions/compare-schools.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/compare-schools.tool.js +367 -0
- package/dist/mcp-server/tools/definitions/compare-schools.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-earnings.tool.d.ts +43 -0
- package/dist/mcp-server/tools/definitions/get-earnings.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-earnings.tool.js +183 -0
- package/dist/mcp-server/tools/definitions/get-earnings.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-programs.tool.d.ts +46 -0
- package/dist/mcp-server/tools/definitions/get-programs.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-programs.tool.js +194 -0
- package/dist/mcp-server/tools/definitions/get-programs.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-school.tool.d.ts +67 -0
- package/dist/mcp-server/tools/definitions/get-school.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-school.tool.js +324 -0
- package/dist/mcp-server/tools/definitions/get-school.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/list-fields.tool.d.ts +28 -0
- package/dist/mcp-server/tools/definitions/list-fields.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/list-fields.tool.js +92 -0
- package/dist/mcp-server/tools/definitions/list-fields.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/lookup-cip.tool.d.ts +26 -0
- package/dist/mcp-server/tools/definitions/lookup-cip.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/lookup-cip.tool.js +71 -0
- package/dist/mcp-server/tools/definitions/lookup-cip.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-programs.tool.d.ts +49 -0
- package/dist/mcp-server/tools/definitions/search-programs.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-programs.tool.js +188 -0
- package/dist/mcp-server/tools/definitions/search-programs.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-schools.tool.d.ts +57 -0
- package/dist/mcp-server/tools/definitions/search-schools.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-schools.tool.js +223 -0
- package/dist/mcp-server/tools/definitions/search-schools.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/value-analysis.tool.d.ts +37 -0
- package/dist/mcp-server/tools/definitions/value-analysis.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/value-analysis.tool.js +227 -0
- package/dist/mcp-server/tools/definitions/value-analysis.tool.js.map +1 -0
- package/dist/services/scorecard/scorecard-service.d.ts +44 -0
- package/dist/services/scorecard/scorecard-service.d.ts.map +1 -0
- package/dist/services/scorecard/scorecard-service.js +376 -0
- package/dist/services/scorecard/scorecard-service.js.map +1 -0
- package/dist/services/scorecard/types.d.ts +131 -0
- package/dist/services/scorecard/types.d.ts.map +1 -0
- package/dist/services/scorecard/types.js +6 -0
- package/dist/services/scorecard/types.js.map +1 -0
- package/package.json +80 -0
- package/server.json +99 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview School comparison tool. Returns normalized side-by-side comparison
|
|
3
|
+
* of 2–5 schools with percentile ranks and relative deltas within the result set.
|
|
4
|
+
* @module mcp-server/tools/definitions/compare-schools.tool
|
|
5
|
+
*/
|
|
6
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
8
|
+
import { getScorecardService } from '../../../services/scorecard/scorecard-service.js';
|
|
9
|
+
/** Compute rank within an array of values (0-indexed, higher is better for most metrics) */
|
|
10
|
+
function computeRanks(values, higherIsBetter = true) {
|
|
11
|
+
const nonNull = values
|
|
12
|
+
.map((v, i) => ({ v: v, i }))
|
|
13
|
+
.filter((x) => x.v != null)
|
|
14
|
+
.sort((a, b) => (higherIsBetter ? b.v - a.v : a.v - b.v));
|
|
15
|
+
const rankMap = new Map();
|
|
16
|
+
for (const [rankIdx, { i }] of nonNull.entries()) {
|
|
17
|
+
rankMap.set(i, rankIdx + 1);
|
|
18
|
+
}
|
|
19
|
+
return values.map((_, i) => rankMap.get(i) ?? null);
|
|
20
|
+
}
|
|
21
|
+
const ComparisonRowSchema = z.object({
|
|
22
|
+
metric: z.string().describe('Metric name.'),
|
|
23
|
+
values: z
|
|
24
|
+
.array(z
|
|
25
|
+
.object({
|
|
26
|
+
school_id: z.number().describe('School unit ID.'),
|
|
27
|
+
value: z.number().optional().describe('Raw metric value. Omitted when not available.'),
|
|
28
|
+
rank: z.number().optional().describe('Rank within this comparison set (1=best).'),
|
|
29
|
+
suppressed: z.boolean().describe('True when data is unavailable for this school/metric.'),
|
|
30
|
+
})
|
|
31
|
+
.describe('Per-school data point for this metric.'))
|
|
32
|
+
.describe('Per-school values for this metric.'),
|
|
33
|
+
unit: z.string().optional().describe('Unit of measurement (e.g. "$", "%", "students").'),
|
|
34
|
+
note: z.string().optional().describe('Contextual note about this metric.'),
|
|
35
|
+
});
|
|
36
|
+
export const compareSchoolsTool = tool('scorecard_compare_schools', {
|
|
37
|
+
title: 'Compare Schools',
|
|
38
|
+
description: 'Normalized side-by-side comparison of 2–5 schools on a named topic. Returns percentile-ranked rows and relative deltas within the result set — structured output an agent cannot reconstruct from raw profiles. Topics: costs (tuition, net price by income bracket, debt), admissions (acceptance rate, SAT/ACT, enrollment), outcomes (graduation rate, earnings, repayment), aid (Pell grants, federal loans, debt, repayment). This is different from scorecard_get_school with multiple IDs — compare_schools adds within-set normalization.',
|
|
39
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
40
|
+
input: z.object({
|
|
41
|
+
ids: z
|
|
42
|
+
.array(z
|
|
43
|
+
.union([
|
|
44
|
+
z.string().describe('School unit ID as string.'),
|
|
45
|
+
z.number().describe('School unit ID as number.'),
|
|
46
|
+
])
|
|
47
|
+
.describe('School unit ID (string or number).'))
|
|
48
|
+
.min(2)
|
|
49
|
+
.max(5)
|
|
50
|
+
.describe('Array of 2–5 school unit IDs to compare.'),
|
|
51
|
+
topic: z
|
|
52
|
+
.enum(['costs', 'admissions', 'outcomes', 'aid'])
|
|
53
|
+
.describe('Comparison topic: costs, admissions, outcomes, or aid.'),
|
|
54
|
+
}),
|
|
55
|
+
output: z.object({
|
|
56
|
+
schools: z
|
|
57
|
+
.array(z
|
|
58
|
+
.object({
|
|
59
|
+
id: z.number().describe('School unit ID.'),
|
|
60
|
+
name: z.string().describe('Institution name.'),
|
|
61
|
+
})
|
|
62
|
+
.describe('School identity record.'))
|
|
63
|
+
.describe('Schools in the comparison set, in the same order as the ids input.'),
|
|
64
|
+
topic: z.string().describe('The comparison topic used.'),
|
|
65
|
+
rows: z
|
|
66
|
+
.array(ComparisonRowSchema.describe('Comparison row for one metric.'))
|
|
67
|
+
.describe('Comparison rows — one per metric, with per-school values and ranks.'),
|
|
68
|
+
summary: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('Brief interpretive summary highlighting the most notable relative differences.'),
|
|
72
|
+
notice: z.string().optional().describe('Warning if some requested IDs returned no record.'),
|
|
73
|
+
}),
|
|
74
|
+
errors: [
|
|
75
|
+
{
|
|
76
|
+
reason: 'schools_not_found',
|
|
77
|
+
code: JsonRpcErrorCode.NotFound,
|
|
78
|
+
when: 'None of the requested school IDs returned records.',
|
|
79
|
+
recovery: 'Verify school IDs with scorecard_search_schools and retry with valid IDs.',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
reason: 'api_error',
|
|
83
|
+
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
84
|
+
when: 'The College Scorecard API returned an error.',
|
|
85
|
+
recovery: 'Check SCORECARD_API_KEY validity and retry after a brief delay.',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
async handler(input, ctx) {
|
|
89
|
+
ctx.log.info('Comparing schools', { ids: input.ids, topic: input.topic });
|
|
90
|
+
const service = getScorecardService();
|
|
91
|
+
const response = await service.getComparisonData(input.ids, input.topic, ctx);
|
|
92
|
+
if (response.results.length === 0) {
|
|
93
|
+
throw ctx.fail('schools_not_found', `No records found for the requested school IDs.`, {
|
|
94
|
+
ids: input.ids,
|
|
95
|
+
recovery: { hint: 'Use scorecard_search_schools to find valid school IDs first.' },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// Build a lookup by ID for consistent ordering
|
|
99
|
+
const byId = new Map();
|
|
100
|
+
for (const r of response.results) {
|
|
101
|
+
if (r.id != null)
|
|
102
|
+
byId.set(String(r.id), r);
|
|
103
|
+
}
|
|
104
|
+
const schools = input.ids.map((id) => {
|
|
105
|
+
const r = byId.get(String(id));
|
|
106
|
+
return { id: Number(id), name: r?.['school.name'] ?? '(Not found)' };
|
|
107
|
+
});
|
|
108
|
+
const found = schools.filter((s) => byId.has(String(s.id)));
|
|
109
|
+
const missing = schools.filter((s) => !byId.has(String(s.id)));
|
|
110
|
+
const notice = missing.length > 0
|
|
111
|
+
? `${missing.length} of ${input.ids.length} requested IDs returned no record: ${missing.map((s) => s.id).join(', ')}`
|
|
112
|
+
: undefined;
|
|
113
|
+
const metricsByTopic = {
|
|
114
|
+
costs: [
|
|
115
|
+
{
|
|
116
|
+
metric: 'In-State Tuition',
|
|
117
|
+
key: 'latest.cost.tuition.in_state',
|
|
118
|
+
unit: '$',
|
|
119
|
+
higherIsBetter: false,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
metric: 'Out-of-State Tuition',
|
|
123
|
+
key: 'latest.cost.tuition.out_of_state',
|
|
124
|
+
unit: '$',
|
|
125
|
+
higherIsBetter: false,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
metric: 'Avg Net Price (overall)',
|
|
129
|
+
key: 'latest.cost.avg_net_price.overall',
|
|
130
|
+
unit: '$',
|
|
131
|
+
higherIsBetter: false,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
metric: 'Net Price ($0–30k income)',
|
|
135
|
+
key: 'latest.cost.avg_net_price.by_income.0-30000',
|
|
136
|
+
unit: '$',
|
|
137
|
+
higherIsBetter: false,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
metric: 'Net Price ($30k–48k income)',
|
|
141
|
+
key: 'latest.cost.avg_net_price.by_income.30001-48000',
|
|
142
|
+
unit: '$',
|
|
143
|
+
higherIsBetter: false,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
metric: 'Net Price ($48k–75k income)',
|
|
147
|
+
key: 'latest.cost.avg_net_price.by_income.48001-75000',
|
|
148
|
+
unit: '$',
|
|
149
|
+
higherIsBetter: false,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
metric: 'Net Price ($75k–110k income)',
|
|
153
|
+
key: 'latest.cost.avg_net_price.by_income.75001-110000',
|
|
154
|
+
unit: '$',
|
|
155
|
+
higherIsBetter: false,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
metric: 'Net Price ($110k+ income)',
|
|
159
|
+
key: 'latest.cost.avg_net_price.by_income.110001-plus',
|
|
160
|
+
unit: '$',
|
|
161
|
+
higherIsBetter: false,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
metric: 'Median Debt at Graduation',
|
|
165
|
+
key: 'latest.aid.median_debt.completers.overall',
|
|
166
|
+
unit: '$',
|
|
167
|
+
higherIsBetter: false,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
admissions: [
|
|
171
|
+
{
|
|
172
|
+
metric: 'Acceptance Rate',
|
|
173
|
+
key: 'latest.admissions.admission_rate.overall',
|
|
174
|
+
unit: '%',
|
|
175
|
+
higherIsBetter: false,
|
|
176
|
+
note: 'Lower may indicate more selectivity',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
metric: 'SAT Average',
|
|
180
|
+
key: 'latest.admissions.sat_scores.average.overall',
|
|
181
|
+
higherIsBetter: true,
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
metric: 'SAT Math 25th Pct',
|
|
185
|
+
key: 'latest.admissions.sat_scores.25th_percentile.math',
|
|
186
|
+
higherIsBetter: true,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
metric: 'SAT Math 75th Pct',
|
|
190
|
+
key: 'latest.admissions.sat_scores.75th_percentile.math',
|
|
191
|
+
higherIsBetter: true,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
metric: 'ACT 25th Pct',
|
|
195
|
+
key: 'latest.admissions.act_scores.25th_percentile.cumulative',
|
|
196
|
+
higherIsBetter: true,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
metric: 'ACT 75th Pct',
|
|
200
|
+
key: 'latest.admissions.act_scores.75th_percentile.cumulative',
|
|
201
|
+
higherIsBetter: true,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
metric: 'Enrollment',
|
|
205
|
+
key: 'latest.student.size',
|
|
206
|
+
unit: 'students',
|
|
207
|
+
higherIsBetter: true,
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
outcomes: [
|
|
211
|
+
{
|
|
212
|
+
metric: 'Completion Rate',
|
|
213
|
+
key: 'latest.completion.rate_suppressed.overall',
|
|
214
|
+
unit: '%',
|
|
215
|
+
higherIsBetter: true,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
metric: 'Median Earnings (6yr)',
|
|
219
|
+
key: 'latest.earnings.6_yrs_after_entry.median',
|
|
220
|
+
unit: '$',
|
|
221
|
+
higherIsBetter: true,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
metric: 'Median Earnings (10yr)',
|
|
225
|
+
key: 'latest.earnings.10_yrs_after_entry.median',
|
|
226
|
+
unit: '$',
|
|
227
|
+
higherIsBetter: true,
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
metric: '3-Year Repayment Rate',
|
|
231
|
+
key: 'latest.repayment.3_yr_repayment.overall',
|
|
232
|
+
unit: '%',
|
|
233
|
+
higherIsBetter: true,
|
|
234
|
+
scale: 1 / 1000,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
metric: 'Median Debt at Graduation',
|
|
238
|
+
key: 'latest.aid.median_debt.completers.overall',
|
|
239
|
+
unit: '$',
|
|
240
|
+
higherIsBetter: false,
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
aid: [
|
|
244
|
+
{
|
|
245
|
+
metric: 'Pell Grant Rate',
|
|
246
|
+
key: 'latest.aid.pell_grant_rate',
|
|
247
|
+
unit: '%',
|
|
248
|
+
higherIsBetter: true,
|
|
249
|
+
note: 'Share of students receiving Pell grants',
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
metric: 'Federal Loan Rate',
|
|
253
|
+
key: 'latest.aid.federal_loan_rate',
|
|
254
|
+
unit: '%',
|
|
255
|
+
higherIsBetter: false,
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
metric: 'Median Debt at Graduation',
|
|
259
|
+
key: 'latest.aid.median_debt.completers.overall',
|
|
260
|
+
unit: '$',
|
|
261
|
+
higherIsBetter: false,
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
metric: '3-Year Repayment Rate',
|
|
265
|
+
key: 'latest.repayment.3_yr_repayment.overall',
|
|
266
|
+
unit: '%',
|
|
267
|
+
higherIsBetter: true,
|
|
268
|
+
scale: 1 / 1000,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
metric: 'Avg Net Price (overall)',
|
|
272
|
+
key: 'latest.cost.avg_net_price.overall',
|
|
273
|
+
unit: '$',
|
|
274
|
+
higherIsBetter: false,
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
const metrics = metricsByTopic[input.topic] ?? [];
|
|
279
|
+
const rows = metrics.map((m) => {
|
|
280
|
+
const rawValues = found.map((s) => {
|
|
281
|
+
const r = byId.get(String(s.id));
|
|
282
|
+
if (!r)
|
|
283
|
+
return null;
|
|
284
|
+
const raw = r[m.key];
|
|
285
|
+
if (raw == null)
|
|
286
|
+
return null;
|
|
287
|
+
return m.scale != null ? raw * m.scale : raw;
|
|
288
|
+
});
|
|
289
|
+
const ranks = computeRanks(rawValues, m.higherIsBetter);
|
|
290
|
+
return {
|
|
291
|
+
metric: m.metric,
|
|
292
|
+
values: found.map((s, i) => {
|
|
293
|
+
const rv = rawValues[i];
|
|
294
|
+
const rk = ranks[i];
|
|
295
|
+
return {
|
|
296
|
+
school_id: s.id,
|
|
297
|
+
...(rv != null ? { value: rv } : {}),
|
|
298
|
+
...(rk != null ? { rank: rk } : {}),
|
|
299
|
+
suppressed: rv == null,
|
|
300
|
+
};
|
|
301
|
+
}),
|
|
302
|
+
...(m.unit && { unit: m.unit }),
|
|
303
|
+
...(m.note && { note: m.note }),
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
// Build a brief summary of the most notable differences
|
|
307
|
+
const bestByMetric = rows
|
|
308
|
+
.filter((r) => r.values.some((v) => !v.suppressed))
|
|
309
|
+
.slice(0, 3)
|
|
310
|
+
.map((r) => {
|
|
311
|
+
const best = r.values.find((v) => v.rank === 1 && !v.suppressed);
|
|
312
|
+
if (!best)
|
|
313
|
+
return null;
|
|
314
|
+
const school = schools.find((s) => s.id === best.school_id);
|
|
315
|
+
return `${school?.name ?? best.school_id} leads on ${r.metric}`;
|
|
316
|
+
})
|
|
317
|
+
.filter(Boolean);
|
|
318
|
+
const summary = bestByMetric.length > 0 ? `${bestByMetric.join('; ')}.` : undefined;
|
|
319
|
+
return {
|
|
320
|
+
schools,
|
|
321
|
+
topic: input.topic,
|
|
322
|
+
rows,
|
|
323
|
+
...(summary && { summary }),
|
|
324
|
+
...(notice && { notice }),
|
|
325
|
+
};
|
|
326
|
+
},
|
|
327
|
+
format: (result) => {
|
|
328
|
+
// Build a lookup: school_id → school name for readable cell headers
|
|
329
|
+
const schoolById = new Map(result.schools.map((s) => [s.id, s.name]));
|
|
330
|
+
const lines = [
|
|
331
|
+
`## School Comparison: ${result.topic}`,
|
|
332
|
+
`**Schools:** ${result.schools.map((s) => `${s.name} (ID: ${s.id})`).join(', ')}`,
|
|
333
|
+
];
|
|
334
|
+
if (result.notice)
|
|
335
|
+
lines.push(`\n> **Note:** ${result.notice}`);
|
|
336
|
+
if (result.summary)
|
|
337
|
+
lines.push(`\n> **Summary:** ${result.summary}`);
|
|
338
|
+
for (const row of result.rows) {
|
|
339
|
+
const unitNote = row.unit ? ` (${row.unit})` : '';
|
|
340
|
+
const noteText = row.note ? ` — ${row.note}` : '';
|
|
341
|
+
lines.push(`\n**${row.metric}${unitNote}${noteText}**`);
|
|
342
|
+
for (const v of row.values) {
|
|
343
|
+
const schoolName = schoolById.get(v.school_id) ?? `School ${v.school_id}`;
|
|
344
|
+
let valueDisplay;
|
|
345
|
+
if (v.value != null) {
|
|
346
|
+
const val = v.value;
|
|
347
|
+
if (row.unit === '$')
|
|
348
|
+
valueDisplay = `$${val.toLocaleString()}`;
|
|
349
|
+
else if (row.unit === '%')
|
|
350
|
+
valueDisplay = `${(val * 100).toFixed(1)}%`;
|
|
351
|
+
else if (row.unit === 'students')
|
|
352
|
+
valueDisplay = val.toLocaleString();
|
|
353
|
+
else
|
|
354
|
+
valueDisplay = String(val);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
valueDisplay = 'N/A';
|
|
358
|
+
}
|
|
359
|
+
const rankDisplay = v.rank != null ? `#${v.rank}` : 'N/A';
|
|
360
|
+
const suppressedLabel = v.suppressed ? ' (suppressed)' : '';
|
|
361
|
+
lines.push(` ${schoolName}: ${valueDisplay} (${rankDisplay})${suppressedLabel}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
//# sourceMappingURL=compare-schools.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-schools.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/compare-schools.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Earnings tool. Returns institution-level post-graduation earnings
|
|
3
|
+
* for one school at 6, 8, and 10 years after entry, with optional gender breakdown.
|
|
4
|
+
* @module mcp-server/tools/definitions/get-earnings.tool
|
|
5
|
+
*/
|
|
6
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
8
|
+
export declare const getEarningsTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
9
|
+
id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
10
|
+
years: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
11
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
12
|
+
school_id: z.ZodNumber;
|
|
13
|
+
school_name: z.ZodString;
|
|
14
|
+
current: z.ZodObject<{
|
|
15
|
+
earnings_6yr_median: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
earnings_6yr_p25: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
earnings_6yr_p75: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
earnings_8yr_median: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
earnings_10yr_median: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
earnings_10yr_p25: z.ZodOptional<z.ZodNumber>;
|
|
21
|
+
earnings_10yr_p75: z.ZodOptional<z.ZodNumber>;
|
|
22
|
+
earnings_6yr_female_median: z.ZodOptional<z.ZodNumber>;
|
|
23
|
+
earnings_6yr_male_median: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
trend: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
26
|
+
year: z.ZodNumber;
|
|
27
|
+
earnings_6yr_median: z.ZodOptional<z.ZodNumber>;
|
|
28
|
+
earnings_10yr_median: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
}, z.core.$strip>>>;
|
|
30
|
+
suppressed: z.ZodBoolean;
|
|
31
|
+
suppression_note: z.ZodOptional<z.ZodString>;
|
|
32
|
+
}, z.core.$strip>, readonly [{
|
|
33
|
+
readonly reason: "school_not_found";
|
|
34
|
+
readonly code: JsonRpcErrorCode.NotFound;
|
|
35
|
+
readonly when: "No school record found for the requested ID.";
|
|
36
|
+
readonly recovery: "Verify the school ID using scorecard_search_schools and retry.";
|
|
37
|
+
}, {
|
|
38
|
+
readonly reason: "api_error";
|
|
39
|
+
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
40
|
+
readonly when: "The College Scorecard API returned an error.";
|
|
41
|
+
readonly recovery: "Check SCORECARD_API_KEY validity and retry after a brief delay.";
|
|
42
|
+
}]>;
|
|
43
|
+
//# sourceMappingURL=get-earnings.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-earnings.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-earnings.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmM1B,CAAC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Earnings tool. Returns institution-level post-graduation earnings
|
|
3
|
+
* for one school at 6, 8, and 10 years after entry, with optional gender breakdown.
|
|
4
|
+
* @module mcp-server/tools/definitions/get-earnings.tool
|
|
5
|
+
*/
|
|
6
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
8
|
+
import { getScorecardService } from '../../../services/scorecard/scorecard-service.js';
|
|
9
|
+
export const getEarningsTool = tool('scorecard_get_earnings', {
|
|
10
|
+
title: 'Get School Earnings',
|
|
11
|
+
description: 'Institution-level post-graduation earnings for one school — median and percentiles at 6, 8, and 10 years after entry, with optional gender breakdown. This reflects outcomes across all graduates, not broken down by program. For program-specific earnings use scorecard_get_programs. For ROI analysis combining cost and debt data use scorecard_value_analysis. The years parameter accepts a list of cohort entry years for historical trend queries — omit for current snapshot only.',
|
|
12
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
13
|
+
input: z.object({
|
|
14
|
+
id: z
|
|
15
|
+
.union([
|
|
16
|
+
z.string().describe('School unit ID as string.'),
|
|
17
|
+
z.number().describe('School unit ID as number.'),
|
|
18
|
+
])
|
|
19
|
+
.describe('School unit ID from scorecard_search_schools.'),
|
|
20
|
+
years: z
|
|
21
|
+
.array(z.number().int())
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Optional list of cohort entry years for historical trend data (e.g. [2011, 2012, 2013]). Omit for current snapshot.'),
|
|
24
|
+
}),
|
|
25
|
+
output: z.object({
|
|
26
|
+
school_id: z.number().describe('School unit ID.'),
|
|
27
|
+
school_name: z.string().describe('Institution name.'),
|
|
28
|
+
current: z
|
|
29
|
+
.object({
|
|
30
|
+
earnings_6yr_median: z.number().optional().describe('Median earnings 6 years after entry.'),
|
|
31
|
+
earnings_6yr_p25: z
|
|
32
|
+
.number()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('25th percentile earnings 6 years after entry.'),
|
|
35
|
+
earnings_6yr_p75: z
|
|
36
|
+
.number()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe('75th percentile earnings 6 years after entry.'),
|
|
39
|
+
earnings_8yr_median: z.number().optional().describe('Median earnings 8 years after entry.'),
|
|
40
|
+
earnings_10yr_median: z
|
|
41
|
+
.number()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('Median earnings 10 years after entry.'),
|
|
44
|
+
earnings_10yr_p25: z
|
|
45
|
+
.number()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe('25th percentile earnings 10 years after entry.'),
|
|
48
|
+
earnings_10yr_p75: z
|
|
49
|
+
.number()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('75th percentile earnings 10 years after entry.'),
|
|
52
|
+
earnings_6yr_female_median: z
|
|
53
|
+
.number()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe('Median earnings 6 years after entry for female students.'),
|
|
56
|
+
earnings_6yr_male_median: z
|
|
57
|
+
.number()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe('Median earnings 6 years after entry for male students.'),
|
|
60
|
+
})
|
|
61
|
+
.describe('Most recent available earnings snapshot.'),
|
|
62
|
+
trend: z
|
|
63
|
+
.array(z
|
|
64
|
+
.object({
|
|
65
|
+
year: z.number().describe('Cohort entry year.'),
|
|
66
|
+
earnings_6yr_median: z
|
|
67
|
+
.number()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe('Median earnings 6 years after entry for this cohort.'),
|
|
70
|
+
earnings_10yr_median: z
|
|
71
|
+
.number()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe('Median earnings 10 years after entry for this cohort.'),
|
|
74
|
+
})
|
|
75
|
+
.describe('Earnings data point for one cohort entry year.'))
|
|
76
|
+
.optional()
|
|
77
|
+
.describe('Historical trend rows by cohort entry year. Present only when years parameter was supplied.'),
|
|
78
|
+
suppressed: z
|
|
79
|
+
.boolean()
|
|
80
|
+
.describe('True when earnings data is suppressed across all time points.'),
|
|
81
|
+
suppression_note: z.string().optional().describe('Explains suppression when suppressed=true.'),
|
|
82
|
+
}),
|
|
83
|
+
errors: [
|
|
84
|
+
{
|
|
85
|
+
reason: 'school_not_found',
|
|
86
|
+
code: JsonRpcErrorCode.NotFound,
|
|
87
|
+
when: 'No school record found for the requested ID.',
|
|
88
|
+
recovery: 'Verify the school ID using scorecard_search_schools and retry.',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
reason: 'api_error',
|
|
92
|
+
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
93
|
+
when: 'The College Scorecard API returned an error.',
|
|
94
|
+
recovery: 'Check SCORECARD_API_KEY validity and retry after a brief delay.',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
async handler(input, ctx) {
|
|
98
|
+
ctx.log.info('Fetching school earnings', { id: input.id, years: input.years });
|
|
99
|
+
const service = getScorecardService();
|
|
100
|
+
const response = await service.getSchoolEarnings(input.id, input.years ?? [], ctx);
|
|
101
|
+
const r = response.results[0];
|
|
102
|
+
if (!r) {
|
|
103
|
+
throw ctx.fail('school_not_found', `No school record found for ID ${input.id}.`, {
|
|
104
|
+
id: input.id,
|
|
105
|
+
recovery: { hint: 'Search for valid school IDs with scorecard_search_schools.' },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const v6med = r['latest.earnings.6_yrs_after_entry.median'];
|
|
109
|
+
const v6p25 = r['latest.earnings.6_yrs_after_entry.percentile25'];
|
|
110
|
+
const v6p75 = r['latest.earnings.6_yrs_after_entry.percentile75'];
|
|
111
|
+
const v8med = r['latest.earnings.8_yrs_after_entry.median_earnings'];
|
|
112
|
+
const v10med = r['latest.earnings.10_yrs_after_entry.median'];
|
|
113
|
+
const v10p25 = r['latest.earnings.10_yrs_after_entry.percentile25'];
|
|
114
|
+
const v10p75 = r['latest.earnings.10_yrs_after_entry.percentile75'];
|
|
115
|
+
const v6fem = r['latest.earnings.6_yrs_after_entry.female_students.median_earnings'];
|
|
116
|
+
const v6mal = r['latest.earnings.6_yrs_after_entry.male_students.median_earnings'];
|
|
117
|
+
const current = {
|
|
118
|
+
...(v6med != null && { earnings_6yr_median: v6med }),
|
|
119
|
+
...(v6p25 != null && { earnings_6yr_p25: v6p25 }),
|
|
120
|
+
...(v6p75 != null && { earnings_6yr_p75: v6p75 }),
|
|
121
|
+
...(v8med != null && { earnings_8yr_median: v8med }),
|
|
122
|
+
...(v10med != null && { earnings_10yr_median: v10med }),
|
|
123
|
+
...(v10p25 != null && { earnings_10yr_p25: v10p25 }),
|
|
124
|
+
...(v10p75 != null && { earnings_10yr_p75: v10p75 }),
|
|
125
|
+
...(v6fem != null && { earnings_6yr_female_median: v6fem }),
|
|
126
|
+
...(v6mal != null && { earnings_6yr_male_median: v6mal }),
|
|
127
|
+
};
|
|
128
|
+
const suppressed = Object.keys(current).length === 0;
|
|
129
|
+
const suppression_note = suppressed
|
|
130
|
+
? 'Earnings data not available — likely suppressed due to small cohort size (FERPA).'
|
|
131
|
+
: undefined;
|
|
132
|
+
// Build trend rows for requested years
|
|
133
|
+
const trend = input.years && input.years.length > 0
|
|
134
|
+
? input.years.map((year) => {
|
|
135
|
+
const row = { year };
|
|
136
|
+
const k6 = `${year}.earnings.6_yrs_after_entry.median`;
|
|
137
|
+
const k10 = `${year}.earnings.10_yrs_after_entry.median`;
|
|
138
|
+
if (r[k6] != null)
|
|
139
|
+
row.earnings_6yr_median = r[k6];
|
|
140
|
+
if (r[k10] != null)
|
|
141
|
+
row.earnings_10yr_median = r[k10];
|
|
142
|
+
return row;
|
|
143
|
+
})
|
|
144
|
+
: undefined;
|
|
145
|
+
return {
|
|
146
|
+
school_id: r.id ?? Number(input.id),
|
|
147
|
+
school_name: r['school.name'] ?? 'Unknown',
|
|
148
|
+
current,
|
|
149
|
+
...(trend && { trend }),
|
|
150
|
+
suppressed,
|
|
151
|
+
...(suppression_note && { suppression_note }),
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
format: (result) => {
|
|
155
|
+
const fmt = (n) => (n != null ? `$${n.toLocaleString()}` : 'Not available');
|
|
156
|
+
const lines = [`## Earnings: ${result.school_name} (ID: ${result.school_id})`];
|
|
157
|
+
lines.push('\n**Current Earnings Snapshot**');
|
|
158
|
+
const c = result.current;
|
|
159
|
+
if (result.suppressed) {
|
|
160
|
+
lines.push(`> **Suppressed:** ${result.suppression_note ?? 'Earnings data not available.'}`);
|
|
161
|
+
}
|
|
162
|
+
lines.push(`6-Year Median: ${fmt(c.earnings_6yr_median)}`);
|
|
163
|
+
lines.push(`6-Year P25/P75: ${fmt(c.earnings_6yr_p25)} / ${fmt(c.earnings_6yr_p75)}`);
|
|
164
|
+
lines.push(`8-Year Median: ${fmt(c.earnings_8yr_median)}`);
|
|
165
|
+
lines.push(`10-Year Median: ${fmt(c.earnings_10yr_median)}`);
|
|
166
|
+
lines.push(`10-Year P25/P75: ${fmt(c.earnings_10yr_p25)} / ${fmt(c.earnings_10yr_p75)}`);
|
|
167
|
+
lines.push(`6-Year Female Median: ${fmt(c.earnings_6yr_female_median)}`);
|
|
168
|
+
lines.push(`6-Year Male Median: ${fmt(c.earnings_6yr_male_median)}`);
|
|
169
|
+
if (result.trend && result.trend.length > 0) {
|
|
170
|
+
lines.push('\n**Historical Trend (by Entry Year)**');
|
|
171
|
+
for (const row of result.trend) {
|
|
172
|
+
const parts = [`Year: ${row.year}`];
|
|
173
|
+
if (row.earnings_6yr_median != null)
|
|
174
|
+
parts.push(`6yr: $${row.earnings_6yr_median.toLocaleString()}`);
|
|
175
|
+
if (row.earnings_10yr_median != null)
|
|
176
|
+
parts.push(`10yr: $${row.earnings_10yr_median.toLocaleString()}`);
|
|
177
|
+
lines.push(parts.join(' | '));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=get-earnings.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-earnings.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-earnings.tool.ts"],"names":[],"mappings":"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"}
|