@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
package/README.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>@cyanheads/college-scorecard-mcp-server</h1>
|
|
3
|
+
<p><b>Search, compare, and analyze U.S. college data — costs, earnings, programs, and outcomes — via MCP. STDIO or Streamable HTTP.</b>
|
|
4
|
+
<div>9 Tools • 2 Resources • 1 Prompt</div>
|
|
5
|
+
</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div align="center">
|
|
9
|
+
|
|
10
|
+
[](./CHANGELOG.md) [](./LICENSE) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Tools
|
|
19
|
+
|
|
20
|
+
9 tools for working with U.S. Department of Education College Scorecard data — institution search, program-level earnings, cost and outcomes analysis, and structured comparison:
|
|
21
|
+
|
|
22
|
+
| Tool | Description |
|
|
23
|
+
|:---|:---|
|
|
24
|
+
| `scorecard_search_schools` | Search and filter institutions by name, location, type, size, and acceptance rate range. Returns core identity and cost metrics. |
|
|
25
|
+
| `scorecard_get_school` | Full institutional profile for one or more school IDs — costs, admissions, outcomes, aid, demographics, and completion rates. |
|
|
26
|
+
| `scorecard_compare_schools` | Normalized side-by-side comparison of 2–5 schools on a named topic. Returns percentile-ranked rows and relative deltas within the result set. |
|
|
27
|
+
| `scorecard_get_programs` | All field-of-study programs at one school: 1-year post-graduation earnings (P25/median/P75), debt at graduation, and enrollment figures. |
|
|
28
|
+
| `scorecard_search_programs` | Find programs by CIP code or keyword across all institutions, ranked by median earnings. Accepts school-side filters (state, ownership, max cost). |
|
|
29
|
+
| `scorecard_get_earnings` | Institution-level post-graduation earnings for one school — median and percentiles at 6, 8, and 10 years after entry, with optional gender breakdown. |
|
|
30
|
+
| `scorecard_value_analysis` | Workflow tool: parallel-fetches cost, debt, repayment, and earnings data, then computes ROI metrics — debt-to-earnings ratio, net price by income bracket, and peer comparisons. |
|
|
31
|
+
| `scorecard_lookup_cip` | Search Classification of Instructional Programs (CIP) codes by keyword or partial name. Served from embedded static data — no API call or rate-limit impact. |
|
|
32
|
+
| `scorecard_list_fields` | Search the Scorecard field catalog by keyword. Returns matching field paths, descriptions, data types, and sort support. Use before passing custom `fields` parameters. |
|
|
33
|
+
|
|
34
|
+
### `scorecard_search_schools`
|
|
35
|
+
|
|
36
|
+
Search for institutions using name, location, and institutional filters.
|
|
37
|
+
|
|
38
|
+
- Free-text name search plus typed filters: state, ownership (public/private nonprofit/private for-profit), degree level, size range, acceptance rate range
|
|
39
|
+
- Geographic proximity filtering by U.S. zip code and distance (miles or km)
|
|
40
|
+
- CIP code filter to find schools offering a specific program family
|
|
41
|
+
- Pagination (`per_page` up to 100, zero-indexed `page`)
|
|
42
|
+
- Returns core identity and cost metrics for quick scanning
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
### `scorecard_get_school`
|
|
47
|
+
|
|
48
|
+
Fetch a full institutional profile by school ID.
|
|
49
|
+
|
|
50
|
+
- Accepts a single ID or an array of IDs (batch fetch up to 100 per page)
|
|
51
|
+
- Covers costs, admissions, outcomes, financial aid, demographics, and completion rates
|
|
52
|
+
- Optional `fields` override for callers who need a narrower or broader field set
|
|
53
|
+
- For side-by-side comparison on a specific dimension, use `scorecard_compare_schools`
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### `scorecard_compare_schools`
|
|
58
|
+
|
|
59
|
+
Normalized comparison across 2–5 institutions on a named topic.
|
|
60
|
+
|
|
61
|
+
- Four topics: `costs`, `admissions`, `outcomes`, `aid` — each pulls a curated topic-specific field set
|
|
62
|
+
- Computes within-set percentile ranks and relative deltas — structured output an agent cannot reconstruct from raw profiles
|
|
63
|
+
- Single API call for all schools; normalization applied post-fetch
|
|
64
|
+
- Distinct from `scorecard_get_school` multi-ID: output shape is rows, not profiles
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### `scorecard_get_programs`
|
|
69
|
+
|
|
70
|
+
List all field-of-study programs at one school with earnings and debt data.
|
|
71
|
+
|
|
72
|
+
- Returns P25/median/P75 earnings 1 year after graduation, median debt at graduation, and enrollment figures per program
|
|
73
|
+
- Filter by CIP code to return only matching programs
|
|
74
|
+
- Filter by `credential_level` (certificate, associate's, bachelor's) and minimum earnings threshold
|
|
75
|
+
- Primary source for program-level earnings — institution-level earnings at 6/8/10 years are available via `scorecard_get_earnings`
|
|
76
|
+
- FERPA suppression surfaced as structured `suppressed: true` flag with `suppression_note`, not bare null
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### `scorecard_search_programs`
|
|
81
|
+
|
|
82
|
+
Find programs by CIP code or name across all institutions, ranked by median earnings.
|
|
83
|
+
|
|
84
|
+
- Program-centric: "which schools in Washington have CS programs with median earnings over $80k?"
|
|
85
|
+
- Accepts school-side filters: state, ownership, max net price
|
|
86
|
+
- Earnings and debt thresholds for filtering results
|
|
87
|
+
- Returns school name, school ID, and unit ID alongside program metrics for follow-up chaining
|
|
88
|
+
- Sorting applied post-fetch where earnings fields are not API-indexed
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### `scorecard_get_earnings`
|
|
93
|
+
|
|
94
|
+
Institution-level post-graduation earnings for one school.
|
|
95
|
+
|
|
96
|
+
- Median and P25/P75 earnings at 6, 8, and 10 years after entry
|
|
97
|
+
- Optional gender breakdown when available
|
|
98
|
+
- `years` parameter for time-series analysis; defaults to `latest.*` for current-state queries
|
|
99
|
+
- Reflects outcomes across all graduates, not broken down by program
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### `scorecard_value_analysis`
|
|
104
|
+
|
|
105
|
+
Workflow tool: "Is this school worth it?"
|
|
106
|
+
|
|
107
|
+
- Parallel-fetches cost/debt/repayment and earnings data in two concurrent requests
|
|
108
|
+
- Computes ROI metrics the API does not pre-calculate: debt-to-earnings ratio (median debt / 6-year earnings), net price to first-year earnings ratio, and 3-year loan repayment rate
|
|
109
|
+
- `family_income` parameter selects the applicable net price bracket
|
|
110
|
+
- Fetches peer school identifiers (same Carnegie category and ownership) for comparative median values
|
|
111
|
+
- Returns all source figures alongside derived metrics — callers can audit the arithmetic
|
|
112
|
+
- `data_notes` flags any suppressed or null fields with structured explanations
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### `scorecard_lookup_cip`
|
|
117
|
+
|
|
118
|
+
Search CIP codes by keyword or partial name.
|
|
119
|
+
|
|
120
|
+
- Covers the full ~2,400-code CIP taxonomy embedded as static data
|
|
121
|
+
- No API call required — zero rate-limit impact, works offline
|
|
122
|
+
- Required before using CIP-based filters when the caller knows a program by name but not code
|
|
123
|
+
- Returns matching codes with standard titles
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### `scorecard_list_fields`
|
|
128
|
+
|
|
129
|
+
Search the Scorecard field catalog by keyword.
|
|
130
|
+
|
|
131
|
+
- ~2,800 field entries from the data dictionary, embedded as static data
|
|
132
|
+
- Returns field paths, descriptions, data types, and whether the field supports API-side sorting
|
|
133
|
+
- No API call required — zero rate-limit impact
|
|
134
|
+
- Use before passing custom `fields` parameters to search/get tools
|
|
135
|
+
|
|
136
|
+
## Resources and prompts
|
|
137
|
+
|
|
138
|
+
| Type | Name | Description |
|
|
139
|
+
|:---|:---|:---|
|
|
140
|
+
| Resource | `scorecard://school/{id}` | Institutional profile by unit ID — injectable context for school-specific conversations |
|
|
141
|
+
| Resource | `scorecard://programs/{id}` | Program-level outcomes for a school |
|
|
142
|
+
| Prompt | `scorecard_compare_prompt` | Structures a multi-school comparison analysis using Scorecard data |
|
|
143
|
+
|
|
144
|
+
All resource data is also reachable via tools. Use `scorecard_search_schools` or `scorecard_get_school` to discover school IDs before constructing resource URIs.
|
|
145
|
+
|
|
146
|
+
## Features
|
|
147
|
+
|
|
148
|
+
Built on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):
|
|
149
|
+
|
|
150
|
+
- Declarative tool, resource, and prompt definitions — single file per primitive, framework handles registration and validation
|
|
151
|
+
- Unified error handling — handlers throw, framework catches, classifies, and formats
|
|
152
|
+
- Pluggable auth: `none`, `jwt`, `oauth`
|
|
153
|
+
- Swappable storage backends: `in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`
|
|
154
|
+
- Structured logging with optional OpenTelemetry tracing
|
|
155
|
+
- STDIO and Streamable HTTP transports
|
|
156
|
+
|
|
157
|
+
College Scorecard-specific:
|
|
158
|
+
|
|
159
|
+
- Full College Scorecard API coverage: ~6,500 Title IV institutions, ~2,800 data fields spanning costs, outcomes, demographics, financial aid, and field-of-study earnings
|
|
160
|
+
- Program-level post-graduation earnings: actual median earnings 1 year after graduation for ~6,500 school × CIP code combinations
|
|
161
|
+
- Field pre-selection per tool — curated ~10–20 field sets appropriate to each tool's purpose; optional `fields` override for custom queries
|
|
162
|
+
- Embedded CIP code taxonomy and field catalog: both served as static data with zero API calls and zero rate-limit impact
|
|
163
|
+
- Geographic filtering via U.S. zip code + distance radius
|
|
164
|
+
- `scorecard_value_analysis` workflow tool computes ROI metrics (debt-to-earnings ratio, net price to first-year earnings) that require multiple API round-trips and post-processing arithmetic
|
|
165
|
+
|
|
166
|
+
Agent-friendly output:
|
|
167
|
+
|
|
168
|
+
- FERPA suppression surfaced as structured `suppressed: true` flag with `suppression_note` — prevents hallucination of missing earnings data at selective schools with small cohorts
|
|
169
|
+
- Derived metrics alongside source figures in `scorecard_value_analysis` — agents can verify arithmetic and branch on computed values, not raw numbers
|
|
170
|
+
- Percentile ranks and relative deltas in `scorecard_compare_schools` — structured output an agent cannot reconstruct from raw profiles without knowing the full comparison set
|
|
171
|
+
- Post-fetch sorting documented and handled transparently — callers never hit API errors on non-indexed sort fields
|
|
172
|
+
|
|
173
|
+
## Getting started
|
|
174
|
+
|
|
175
|
+
Add the following to your MCP client configuration file. See [api.data.gov/signup](https://api.data.gov/signup/) for a free API key.
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"mcpServers": {
|
|
180
|
+
"college-scorecard": {
|
|
181
|
+
"type": "stdio",
|
|
182
|
+
"command": "bunx",
|
|
183
|
+
"args": ["@cyanheads/college-scorecard-mcp-server@latest"],
|
|
184
|
+
"env": {
|
|
185
|
+
"MCP_TRANSPORT_TYPE": "stdio",
|
|
186
|
+
"MCP_LOG_LEVEL": "info",
|
|
187
|
+
"SCORECARD_API_KEY": "your-api-key"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Or with npx (no Bun required):
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"mcpServers": {
|
|
199
|
+
"college-scorecard": {
|
|
200
|
+
"type": "stdio",
|
|
201
|
+
"command": "npx",
|
|
202
|
+
"args": ["-y", "@cyanheads/college-scorecard-mcp-server@latest"],
|
|
203
|
+
"env": {
|
|
204
|
+
"MCP_TRANSPORT_TYPE": "stdio",
|
|
205
|
+
"MCP_LOG_LEVEL": "info",
|
|
206
|
+
"SCORECARD_API_KEY": "your-api-key"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Or with Docker:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"mcpServers": {
|
|
218
|
+
"college-scorecard": {
|
|
219
|
+
"type": "stdio",
|
|
220
|
+
"command": "docker",
|
|
221
|
+
"args": [
|
|
222
|
+
"run", "-i", "--rm",
|
|
223
|
+
"-e", "MCP_TRANSPORT_TYPE=stdio",
|
|
224
|
+
"-e", "SCORECARD_API_KEY=your-api-key",
|
|
225
|
+
"ghcr.io/cyanheads/college-scorecard-mcp-server:latest"
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
For Streamable HTTP, set the transport and start the server:
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 SCORECARD_API_KEY=... bun run start:http
|
|
236
|
+
# Server listens at http://localhost:3010/mcp
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Prerequisites
|
|
240
|
+
|
|
241
|
+
- [Bun v1.3.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
242
|
+
- A College Scorecard API key — free registration at [api.data.gov/signup](https://api.data.gov/signup/). Rate limit: 1,000 requests/hour per key.
|
|
243
|
+
|
|
244
|
+
### Installation
|
|
245
|
+
|
|
246
|
+
1. **Clone the repository:**
|
|
247
|
+
|
|
248
|
+
```sh
|
|
249
|
+
git clone https://github.com/cyanheads/college-scorecard-mcp-server.git
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
2. **Navigate into the directory:**
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
cd college-scorecard-mcp-server
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
3. **Install dependencies:**
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
bun install
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
4. **Configure environment:**
|
|
265
|
+
|
|
266
|
+
```sh
|
|
267
|
+
cp .env.example .env
|
|
268
|
+
# edit .env and set SCORECARD_API_KEY
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Configuration
|
|
272
|
+
|
|
273
|
+
All configuration is validated at startup via Zod schemas in `src/config/server-config.ts`. Key environment variables:
|
|
274
|
+
|
|
275
|
+
| Variable | Description | Default |
|
|
276
|
+
|:---|:---|:---|
|
|
277
|
+
| `SCORECARD_API_KEY` | **Required.** API key from [api.data.gov](https://api.data.gov/signup/). 1,000 req/hour rate limit. | — |
|
|
278
|
+
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http` | `stdio` |
|
|
279
|
+
| `MCP_HTTP_PORT` | HTTP server port | `3010` |
|
|
280
|
+
| `MCP_HTTP_ENDPOINT_PATH` | HTTP endpoint path where the MCP server is mounted | `/mcp` |
|
|
281
|
+
| `MCP_PUBLIC_URL` | Public origin override for TLS-terminating reverse-proxy deployments | none |
|
|
282
|
+
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth` | `none` |
|
|
283
|
+
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.) | `info` |
|
|
284
|
+
| `MCP_GC_PRESSURE_INTERVAL_MS` | Opt-in forced-GC pressure loop (ms, Bun only). Try `60000` if heap growth is observed under sustained HTTP load. | `0` (disabled) |
|
|
285
|
+
| `LOGS_DIR` | Directory for log files (Node.js only) | `<project-root>/logs` |
|
|
286
|
+
| `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1` | `in-memory` |
|
|
287
|
+
| `OTEL_ENABLED` | Enable [OpenTelemetry instrumentation](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry) | `false` |
|
|
288
|
+
|
|
289
|
+
See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
290
|
+
|
|
291
|
+
## Running the server
|
|
292
|
+
|
|
293
|
+
### Local development
|
|
294
|
+
|
|
295
|
+
- **Build and run:**
|
|
296
|
+
|
|
297
|
+
```sh
|
|
298
|
+
# One-time build
|
|
299
|
+
bun run rebuild
|
|
300
|
+
|
|
301
|
+
# Run the built server
|
|
302
|
+
bun run start:stdio
|
|
303
|
+
# or
|
|
304
|
+
bun run start:http
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
- **Run checks and tests:**
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
bun run devcheck # Lint, format, typecheck, security
|
|
311
|
+
bun run test # Vitest test suite
|
|
312
|
+
bun run lint:mcp # Validate MCP definitions against spec
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Docker
|
|
316
|
+
|
|
317
|
+
```sh
|
|
318
|
+
docker build -t college-scorecard-mcp-server .
|
|
319
|
+
docker run --rm -e SCORECARD_API_KEY=your-key -p 3010:3010 college-scorecard-mcp-server
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/college-scorecard-mcp-server`. OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them.
|
|
323
|
+
|
|
324
|
+
## Project structure
|
|
325
|
+
|
|
326
|
+
| Directory | Purpose |
|
|
327
|
+
|:---|:---|
|
|
328
|
+
| `src/index.ts` | `createApp()` entry point — registers tools/resources/prompts and inits services. |
|
|
329
|
+
| `src/config` | Server-specific environment variable parsing and validation with Zod. |
|
|
330
|
+
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Nine tools across search, profile, programs, earnings, and analysis. |
|
|
331
|
+
| `src/mcp-server/resources` | Resource definitions. School profile and program outcomes resources. |
|
|
332
|
+
| `src/mcp-server/prompts` | Prompt definitions. Multi-school comparison prompt. |
|
|
333
|
+
| `src/services` | `ScorecardService` — fetch wrapper with retry, field selection, and pagination against the College Scorecard API. |
|
|
334
|
+
| `tests/` | Unit and integration tests mirroring `src/`. |
|
|
335
|
+
|
|
336
|
+
## Development guide
|
|
337
|
+
|
|
338
|
+
See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
|
|
339
|
+
|
|
340
|
+
- Handlers throw, framework catches — no `try/catch` in tool logic
|
|
341
|
+
- Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage
|
|
342
|
+
- Register new tools and resources via the barrels in `src/mcp-server/*/definitions/index.ts`
|
|
343
|
+
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
|
|
344
|
+
|
|
345
|
+
## Contributing
|
|
346
|
+
|
|
347
|
+
Issues and pull requests are welcome. Run checks and tests before submitting:
|
|
348
|
+
|
|
349
|
+
```sh
|
|
350
|
+
bun run devcheck
|
|
351
|
+
bun run test
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
Apache-2.0 — see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Initial release — 9 College Scorecard tools (institution search, program earnings, school comparison, value analysis), 2 resources, 1 prompt, embedded CIP taxonomy and field catalog."
|
|
3
|
+
breaking: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0.1.0 — 2026-05-25
|
|
7
|
+
|
|
8
|
+
## Added
|
|
9
|
+
|
|
10
|
+
- **`ScorecardService`**: College Scorecard API client (`api.data.gov/ed/collegescorecard/v1`) with retry, field pre-selection, and pagination.
|
|
11
|
+
- **9 tools**:
|
|
12
|
+
- `scorecard_search_schools` — search institutions by name, location, type, size, and acceptance rate range
|
|
13
|
+
- `scorecard_get_school` — full institutional profile for one or more school IDs (costs, admissions, outcomes, aid, demographics, completion)
|
|
14
|
+
- `scorecard_compare_schools` — normalized side-by-side comparison of 2–5 schools on a named topic with percentile ranks and relative deltas
|
|
15
|
+
- `scorecard_get_programs` — program-level earnings (P25/median/P75), debt, and enrollment for one school
|
|
16
|
+
- `scorecard_search_programs` — cross-institution program search by CIP code or keyword, ranked by median earnings
|
|
17
|
+
- `scorecard_get_earnings` — institution-level earnings at 6, 8, and 10 years after entry with optional gender breakdown
|
|
18
|
+
- `scorecard_value_analysis` — workflow tool: parallel-fetches cost and earnings data, computes debt-to-earnings ratio and net price to first-year earnings with peer comparisons
|
|
19
|
+
- `scorecard_lookup_cip` — keyword search over the embedded CIP code taxonomy (~2,400 codes, static — no API call)
|
|
20
|
+
- `scorecard_list_fields` — keyword search over the embedded field catalog (~2,800 entries, static — no API call)
|
|
21
|
+
- **2 resources**: `scorecard://school/{id}`, `scorecard://programs/{id}`
|
|
22
|
+
- **1 prompt**: `scorecard_compare_prompt` — structures a multi-school comparison analysis
|
|
23
|
+
- **FERPA suppression handling**: `suppressed: true` flag and `suppression_note` on program/earnings records where cohort size triggers privacy protection, preventing bare null from reaching callers
|
|
24
|
+
- **Embedded static data**: CIP code taxonomy and Scorecard field catalog bundled at build time; `scorecard_lookup_cip` and `scorecard_list_fields` make zero API calls
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Field-test fixes — CIP code filtering, repayment rate scaling, credential level path, and format output cleanup."
|
|
3
|
+
breaking: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0.1.1 — 2026-05-25
|
|
7
|
+
|
|
8
|
+
## Fixed
|
|
9
|
+
|
|
10
|
+
- **CIP code filtering**: strip the dot separator from CIP codes before sending to the API (`11.0701` → `110701`) — the Scorecard API rejects dotted codes; `scorecard_search_schools`, `scorecard_get_programs`, and `scorecard_search_programs` were all affected.
|
|
11
|
+
- **Repayment rate scaling**: `latest.repayment.3_yr_repayment.overall` is returned as a scaled integer (e.g. `576` = 57.6%) — divide by 1000 before use. Fixed in `scorecard_get_school`, `scorecard_compare_schools`, and `scorecard_value_analysis`.
|
|
12
|
+
- **Credential level field path**: `RawProgram.credential_level` → `credential.level` to match the actual API response shape; fixes `scorecard_get_programs` filtering and `scorecard://programs/{id}` resource output.
|
|
13
|
+
- **`format()` output in `scorecard_get_earnings`**: removed internal field path names from rendered text (e.g. `earnings_6yr_median`) — labels now read as "6-Year Median", "6-Year P25/P75", etc.
|
|
14
|
+
- **`format()` output in `scorecard_compare_schools`**: rank display changed from `value: X rank: Y` to `X (#Y)` for readability.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
# FORMAT REFERENCE — do not edit. Copy this file to
|
|
3
|
+
# `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.8.x/0.8.6.md`)
|
|
4
|
+
# to author a new release. Set that file's H1 to `# <version> — YYYY-MM-DD`
|
|
5
|
+
# with a concrete date.
|
|
6
|
+
|
|
7
|
+
# Required. One-line GitHub Release-style headline. 350 character cap.
|
|
8
|
+
# Default short and scannable. Don't pad, don't stitch unrelated changes with
|
|
9
|
+
# semicolons — pick the headline. Quotes required: unquoted YAML treats `: `
|
|
10
|
+
# inside the value as a key separator and fails GitHub's strict parser.
|
|
11
|
+
summary: ""
|
|
12
|
+
|
|
13
|
+
# Set `true` when consumers must change code to upgrade: API removals,
|
|
14
|
+
# signature changes, config renames, behavior changes that break existing
|
|
15
|
+
# usage. Flagged as `Breaking` in the rollup.
|
|
16
|
+
breaking: false
|
|
17
|
+
|
|
18
|
+
# Set `true` if this release contains any security fix. Pairs with the
|
|
19
|
+
# `## Security` section below. Flagged as `Security` in the rollup so
|
|
20
|
+
# users can triage upgrade urgency at a glance.
|
|
21
|
+
security: false
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# <version> — YYYY-MM-DD
|
|
25
|
+
|
|
26
|
+
<!--
|
|
27
|
+
AUTHORING GUIDE — applies to the new per-version file you create from this
|
|
28
|
+
template.
|
|
29
|
+
|
|
30
|
+
Audience: someone scanning release notes to decide what affects them. Lead
|
|
31
|
+
each bullet with the symbol or concept name in **bold** so they can skip
|
|
32
|
+
what's irrelevant and zoom in on what's not.
|
|
33
|
+
|
|
34
|
+
Tone: terse, fact-dense, not verbose. Default to one sentence per bullet —
|
|
35
|
+
name the symbol, state what changed, stop. Use a second sentence only when
|
|
36
|
+
it carries weight. If a bullet feels long, it is.
|
|
37
|
+
|
|
38
|
+
Cut: mechanism walkthroughs (those belong in JSDoc, AGENTS.md, or the
|
|
39
|
+
relevant skill), ceremonial framings ("This release introduces…",
|
|
40
|
+
backwards-compat paragraphs), file-by-file test enumerations, internal
|
|
41
|
+
implementation notes. Prefer code/symbol names over English re-explanations.
|
|
42
|
+
|
|
43
|
+
Narrative intro: skip by default. Add one short sentence only when the
|
|
44
|
+
release theme genuinely needs framing the bullets can't carry.
|
|
45
|
+
|
|
46
|
+
Sections: Keep a Changelog order — Added, Changed, Deprecated, Removed,
|
|
47
|
+
Fixed, Security. Include only sections with entries; delete the rest
|
|
48
|
+
(including the commented-out scaffolding below). Don't ship empty headers.
|
|
49
|
+
|
|
50
|
+
Include: every distinct fact a reader needs to adopt or audit the release —
|
|
51
|
+
new exports, signatures, lint rule IDs, env vars, breaking changes, version
|
|
52
|
+
bumps on shipped skills. Nothing more.
|
|
53
|
+
|
|
54
|
+
Links: link issues, PRs, docs, or skills where they help a reader jump to
|
|
55
|
+
context. Once per item per entry — don't re-link the same issue in summary,
|
|
56
|
+
narrative, and bullet. Skip links for inline symbol names; code spans speak
|
|
57
|
+
for themselves.
|
|
58
|
+
|
|
59
|
+
Issue/PR URLs: use full URLs. GitHub's bare `#NN` auto-link only resolves
|
|
60
|
+
inside its own UI, not in npm reads or local editors.
|
|
61
|
+
|
|
62
|
+
[#38](https://github.com/cyanheads/mcp-ts-core/issues/38) ← issue
|
|
63
|
+
[#42](https://github.com/cyanheads/mcp-ts-core/pull/42) ← PR
|
|
64
|
+
|
|
65
|
+
Verify numbers exist before linking (`gh issue view NN`, `gh pr view NN`).
|
|
66
|
+
Never speculate on a future number — `#42` for an upcoming PR silently
|
|
67
|
+
resolves to whatever real item already owns 42, and timeline previews pull
|
|
68
|
+
in that unrelated item's metadata.
|
|
69
|
+
|
|
70
|
+
TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
|
|
71
|
+
via `gh release create --notes-from-tag`. The tag is a derivative of this
|
|
72
|
+
changelog entry — a condensed, scannable version, not a copy. Format:
|
|
73
|
+
|
|
74
|
+
<theme — omit version number, GitHub prepends it>
|
|
75
|
+
← blank line
|
|
76
|
+
<1-2 sentence context: what this release does>
|
|
77
|
+
← blank line
|
|
78
|
+
Dependency bumps: ← section header
|
|
79
|
+
← blank line
|
|
80
|
+
- `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
|
|
81
|
+
← blank line
|
|
82
|
+
Changed: ← only sections with entries
|
|
83
|
+
← blank line
|
|
84
|
+
- `format()` output includes `query` in text mode
|
|
85
|
+
← blank line
|
|
86
|
+
Added:
|
|
87
|
+
← blank line
|
|
88
|
+
- `manifest.json` scaffolded for MCPB bundle support
|
|
89
|
+
- Install badges (Claude Desktop, Cursor, VS Code)
|
|
90
|
+
← blank line
|
|
91
|
+
<N> tests pass; `bun run devcheck` clean. ← footer
|
|
92
|
+
|
|
93
|
+
Never a flat comma-separated string. Always structured markdown with
|
|
94
|
+
sections. The tag must scan well as a rendered GitHub Release page.
|
|
95
|
+
-->
|
|
96
|
+
|
|
97
|
+
## Added
|
|
98
|
+
|
|
99
|
+
-
|
|
100
|
+
|
|
101
|
+
## Changed
|
|
102
|
+
|
|
103
|
+
-
|
|
104
|
+
|
|
105
|
+
<!-- ## Deprecated
|
|
106
|
+
|
|
107
|
+
- -->
|
|
108
|
+
|
|
109
|
+
<!-- ## Removed
|
|
110
|
+
|
|
111
|
+
- -->
|
|
112
|
+
|
|
113
|
+
## Fixed
|
|
114
|
+
|
|
115
|
+
-
|
|
116
|
+
|
|
117
|
+
<!-- ## Security
|
|
118
|
+
|
|
119
|
+
- -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-specific environment variable config for college-scorecard-mcp-server.
|
|
3
|
+
* @module config/server-config
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
declare const ServerConfigSchema: z.ZodObject<{
|
|
7
|
+
apiKey: z.ZodString;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type ServerConfig = z.infer<typeof ServerConfigSchema>;
|
|
10
|
+
export declare function getServerConfig(): ServerConfig;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=server-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;iBAEtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAK9C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-specific environment variable config for college-scorecard-mcp-server.
|
|
3
|
+
* @module config/server-config
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';
|
|
7
|
+
const ServerConfigSchema = z.object({
|
|
8
|
+
apiKey: z.string().min(1).describe('API key from api.data.gov (free registration)'),
|
|
9
|
+
});
|
|
10
|
+
let _config;
|
|
11
|
+
export function getServerConfig() {
|
|
12
|
+
_config ??= parseEnvConfig(ServerConfigSchema, {
|
|
13
|
+
apiKey: 'SCORECARD_API_KEY',
|
|
14
|
+
});
|
|
15
|
+
return _config;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=server-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,+CAA+C,CAAC;CACpF,CAAC,CAAC;AAIH,IAAI,OAAiC,CAAC;AAEtC,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,cAAc,CAAC,kBAAkB,EAAE;QAC7C,MAAM,EAAE,mBAAmB;KAC5B,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Embedded CIP (Classification of Instructional Programs) code taxonomy.
|
|
3
|
+
* Derived from NCES CIP 2020 taxonomy. Static data — no API call required.
|
|
4
|
+
* @module data/cip-codes
|
|
5
|
+
*/
|
|
6
|
+
export interface CipEntry {
|
|
7
|
+
/** 4-digit CIP code (e.g. "11.07") */
|
|
8
|
+
code: string;
|
|
9
|
+
/** Standard title for the program (e.g. "Computer Science") */
|
|
10
|
+
title: string;
|
|
11
|
+
/** 2-digit family code (e.g. "11") */
|
|
12
|
+
family: string;
|
|
13
|
+
/** Family name (e.g. "Computer And Information Sciences And Support Services") */
|
|
14
|
+
familyTitle: string;
|
|
15
|
+
}
|
|
16
|
+
/** 4-digit CIP code taxonomy — common programs for college/career exploration. */
|
|
17
|
+
export declare const CIP_CODES: CipEntry[];
|
|
18
|
+
/**
|
|
19
|
+
* Search CIP codes by keyword — matches against code, title, family title.
|
|
20
|
+
* Returns up to `limit` results (default 20).
|
|
21
|
+
*/
|
|
22
|
+
export declare function searchCipCodes(query: string, limit?: number): CipEntry[];
|
|
23
|
+
//# sourceMappingURL=cip-codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cip-codes.d.ts","sourceRoot":"","sources":["../../src/data/cip-codes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,QAAQ;IACvB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,kFAAkF;AAClF,eAAO,MAAM,SAAS,EAAE,QAAQ,EA4Q/B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,QAAQ,EAAE,CAuBpE"}
|