@cyanheads/congressgov-mcp-server 0.8.1 → 0.9.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/AGENTS.md +44 -17
- package/CLAUDE.md +44 -17
- package/Dockerfile +4 -0
- package/README.md +35 -124
- package/changelog/0.9.x/0.9.0.md +25 -0
- package/changelog/0.9.x/0.9.1.md +31 -0
- package/dist/config/server-config.d.ts +0 -7
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +1 -9
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/bill-lookup.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/bill-lookup.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/bill-summaries.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/bill-summaries.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/committee-lookup.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/committee-lookup.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/committee-reports.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/committee-reports.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/crs-reports.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/crs-reports.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/daily-record.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/daily-record.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/enacted-laws.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/enacted-laws.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/member-lookup.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/member-lookup.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/roll-votes.tool.d.ts +9 -2
- package/dist/mcp-server/tools/definitions/roll-votes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/roll-votes.tool.js +3 -2
- package/dist/mcp-server/tools/definitions/roll-votes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/senate-nominations.tool.d.ts +49 -4
- package/dist/mcp-server/tools/definitions/senate-nominations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/senate-nominations.tool.js +102 -5
- package/dist/mcp-server/tools/definitions/senate-nominations.tool.js.map +1 -1
- package/dist/mcp-server/tools/format-helpers.d.ts.map +1 -1
- package/dist/mcp-server/tools/format-helpers.js +37 -8
- package/dist/mcp-server/tools/format-helpers.js.map +1 -1
- package/dist/mcp-server/tools/tool-helpers.d.ts +11 -4
- package/dist/mcp-server/tools/tool-helpers.d.ts.map +1 -1
- package/dist/mcp-server/tools/tool-helpers.js +12 -4
- package/dist/mcp-server/tools/tool-helpers.js.map +1 -1
- package/dist/services/call-budget.d.ts +77 -0
- package/dist/services/call-budget.d.ts.map +1 -0
- package/dist/services/call-budget.js +118 -0
- package/dist/services/call-budget.js.map +1 -0
- package/dist/services/congress-api/congress-api-service.d.ts +4 -0
- package/dist/services/congress-api/congress-api-service.d.ts.map +1 -1
- package/dist/services/congress-api/congress-api-service.js +60 -32
- package/dist/services/congress-api/congress-api-service.js.map +1 -1
- package/dist/services/congress-documents/congress-documents-service.d.ts +46 -15
- package/dist/services/congress-documents/congress-documents-service.d.ts.map +1 -1
- package/dist/services/congress-documents/congress-documents-service.js +95 -24
- package/dist/services/congress-documents/congress-documents-service.js.map +1 -1
- package/dist/services/congress-documents/document-body-cache.d.ts +66 -0
- package/dist/services/congress-documents/document-body-cache.d.ts.map +1 -0
- package/dist/services/congress-documents/document-body-cache.js +122 -0
- package/dist/services/congress-documents/document-body-cache.js.map +1 -0
- package/dist/services/congress-mirror/ingest.d.ts +13 -7
- package/dist/services/congress-mirror/ingest.d.ts.map +1 -1
- package/dist/services/congress-mirror/ingest.js +33 -10
- package/dist/services/congress-mirror/ingest.js.map +1 -1
- package/dist/services/congress-mirror/schema.d.ts.map +1 -1
- package/dist/services/congress-mirror/schema.js +15 -1
- package/dist/services/congress-mirror/schema.js.map +1 -1
- package/dist/services/senate-lis/parse.d.ts +23 -0
- package/dist/services/senate-lis/parse.d.ts.map +1 -1
- package/dist/services/senate-lis/parse.js +49 -5
- package/dist/services/senate-lis/parse.js.map +1 -1
- package/dist/services/senate-lis/senate-vote-service.d.ts.map +1 -1
- package/dist/services/senate-lis/senate-vote-service.js +32 -25
- package/dist/services/senate-lis/senate-vote-service.js.map +1 -1
- package/dist/utils/congress-calendar.d.ts +15 -0
- package/dist/utils/congress-calendar.d.ts.map +1 -0
- package/dist/utils/congress-calendar.js +33 -0
- package/dist/utils/congress-calendar.js.map +1 -0
- package/package.json +7 -6
- package/scripts/_mirror-context.ts +15 -9
- package/scripts/congress-mirror-init.ts +1 -1
- package/scripts/congress-mirror-refresh.ts +1 -1
- package/scripts/congress-mirror-verify.ts +5 -4
- package/server.json +3 -3
- package/skills/congressgov-guide/SKILL.md +50 -0
package/AGENTS.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** congressgov-mcp-server
|
|
4
|
-
**Version:** 0.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 0.9.1
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/server` ^2.
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` ^2.4.0
|
|
8
8
|
**Zod:** ^4.6.7
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
@@ -74,7 +74,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
74
74
|
| `congressgov_member_lookup` | Discover members by state/district/congress, retrieve legislative portfolios |
|
|
75
75
|
| `congressgov_committee_lookup` | Browse committees and retrieve legislation, reports, nominations |
|
|
76
76
|
| `congressgov_roll_votes` | Retrieve House and Senate roll call votes and member voting positions |
|
|
77
|
-
| `congressgov_senate_nominations` | Browse presidential nominations, track Senate confirmation pipeline |
|
|
77
|
+
| `congressgov_senate_nominations` | Browse presidential nominations, track Senate confirmation pipeline; an action citing its own Senate record vote (`actions` rows, `latestAction` on `list`/`get`) carries `rollVote`, the `congressgov_roll_votes` arguments for it, from the 101st Congress on |
|
|
78
78
|
| `congressgov_bill_summaries` | Browse recent CRS bill summaries — the "what's happening" feed, bounded by the shared summary page budget and per-row text window |
|
|
79
79
|
| `congressgov_crs_reports` | Browse and retrieve nonpartisan CRS policy analysis reports |
|
|
80
80
|
| `congressgov_committee_reports` | Browse and retrieve committee reports accompanying legislation + bounded report text via `content` |
|
|
@@ -83,7 +83,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
83
83
|
|
|
84
84
|
**Summary bounding** (`src/mcp-server/tools/summary-window.ts`, constants in `tool-helpers.ts`). Both summary surfaces enforce two limits. `SUMMARY_PAGE_CHARACTERS` stops a page once its serialized rows reach the budget — the first row always returns, `pagination.nextOffset` counts the rows actually returned, and an enrichment notice discloses the stop. `DEFAULT_SUMMARY_TEXT_CHARACTERS` bounds one row's upstream `text`; a windowed row gains `textTotalCharacters` / `textTruncated` / `textNextOffset`, and its window end is retracted off any HTML tag or character reference it would otherwise split (unless the construct is wider than the whole window, where the requested limit wins). A row inside both limits is returned untouched, so an unbounded page stays byte-identical. `congressgov_bill_lookup` `summaries` overrides the row window with `characterOffset`/`characterLimit` and selects one version with `versionCode` — Congress.gov publishes no version filter, so selection reads the bill's whole summary list and matches client-side.
|
|
85
85
|
|
|
86
|
-
### Resources (
|
|
86
|
+
### Resources (6)
|
|
87
87
|
|
|
88
88
|
| URI Template | Description |
|
|
89
89
|
|:-------------|:------------|
|
|
@@ -92,6 +92,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
92
92
|
| `congress://member/{bioguideId}` | Member profile |
|
|
93
93
|
| `congress://bill/{congress}/{billType}/{billNumber}` | Bill detail |
|
|
94
94
|
| `congress://committee/{committeeCode}` | Committee detail |
|
|
95
|
+
| `skill://congressgov-guide/SKILL.md` | Published legislative research guide |
|
|
95
96
|
|
|
96
97
|
### Prompts (2)
|
|
97
98
|
|
|
@@ -110,11 +111,13 @@ src/
|
|
|
110
111
|
config/
|
|
111
112
|
server-config.ts # CONGRESS_API_KEY, base URL (Zod schema)
|
|
112
113
|
services/
|
|
114
|
+
call-budget.ts # One 50 s upstream budget per tool call / resource read, shared by the three HTTP services
|
|
113
115
|
congress-api/
|
|
114
116
|
congress-api-service.ts # API client — auth, pagination, rate limiting
|
|
115
117
|
types.ts # API response types
|
|
116
118
|
congress-documents/
|
|
117
|
-
congress-documents-service.ts # www.congress.gov document fetch — host allowlist, byte ceiling, character window
|
|
119
|
+
congress-documents-service.ts # www.congress.gov document fetch — host allowlist, byte ceiling, call budget, character window
|
|
120
|
+
document-body-cache.ts # Process-local LRU of decompressed bodies — one fetch per document per TTL
|
|
118
121
|
document-formats.ts # Format label → URL resolution over upstream format lists
|
|
119
122
|
extract-text.ts # GPO `<pre>` / XML body → deterministic plain text
|
|
120
123
|
extract-text-stream.ts # Incremental twin of extract-text.ts — one character window, constant memory
|
|
@@ -127,7 +130,7 @@ src/
|
|
|
127
130
|
types.ts # Mirror row, search filters, search page types
|
|
128
131
|
senate-lis/
|
|
129
132
|
senate-vote-service.ts # Senate LIS XML client — fetch, retry, not-found
|
|
130
|
-
parse.ts # pure XML → domain parsers (fast-xml-parser)
|
|
133
|
+
parse.ts # pure XML → domain parsers (fast-xml-parser); Senate vote coverage, session for a date
|
|
131
134
|
types.ts # Senate vote domain types
|
|
132
135
|
mcp-server/
|
|
133
136
|
tools/definitions/
|
|
@@ -153,6 +156,7 @@ src/
|
|
|
153
156
|
legislative-research.prompt.ts # congressgov_legislative_research
|
|
154
157
|
utils/
|
|
155
158
|
character-references.ts # Single-pass HTML/XML character-reference decoding
|
|
159
|
+
congress-calendar.ts # ordinal(), congressFirstYear(), currentCongressNumber() — congress numbering arithmetic
|
|
156
160
|
scripts/
|
|
157
161
|
_mirror-context.ts # Shared bootstrap for the mirror lifecycle CLI scripts
|
|
158
162
|
congress-mirror-init.ts # mirror:init — full out-of-band mirror build
|
|
@@ -166,6 +170,12 @@ scripts/
|
|
|
166
170
|
|
|
167
171
|
Four services. `CongressApiService` backs nine tools and the House branch of `congressgov_roll_votes`; `SenateVoteService` backs only the Senate branch of `congressgov_roll_votes` (the Congress.gov API exposes no Senate vote namespace); `CongressMirrorService` backs `congressgov_search_bills` only; `CongressDocumentsService` backs the `content` operation on `congressgov_bill_lookup`, `congressgov_committee_reports`, and `congressgov_daily_record`.
|
|
168
172
|
|
|
173
|
+
**Call budget** (`call-budget.ts`, `withCallBudget`) — every upstream request a tool call or resource read makes through `CongressApiService`, `SenateVoteService`, or `CongressDocumentsService` spends one 50 s budget (`CALL_BUDGET_MS`) counted from the call's start (`ctx.timestamp`), so chained and parallel requests (a roster paged four times, a `content` lookup and then its document) end together before the 60 s default MCP client timeout:
|
|
174
|
+
- Each request's retry ladder gets what is left as `withRetry` `deadlineMs`; each attempt's timeout (10 s API, 12 s Senate, 30 s document) is clamped to the remainder and its I/O carries the budget's signal
|
|
175
|
+
- A backoff the remainder cannot hold fails at once as the expiry; a `Retry-After` wait it cannot hold fails at once with the upstream's own error and `retryAfter`; a request the spent budget cannot cover is never sent
|
|
176
|
+
- Running out is `Timeout`, reason `upstream_timeout`, with `budgetMs` and `retryAttempts`. A caller abort stays `RequestCancelled`, and a 429 is still never retried
|
|
177
|
+
- Services read the budget from the handler's own `ctx` — pass it to every service call, never a context derived from it (that carries a later timestamp). A request with no `ctx` — the mirror ingester and the `mirror:*` scripts — runs the plain per-request ladder, unbudgeted
|
|
178
|
+
|
|
169
179
|
**`CongressApiService`** — wraps the Congress.gov REST API v3:
|
|
170
180
|
- API key via `X-Api-Key` header (never logged; kept out of the URL so it can't leak in upstream error messages)
|
|
171
181
|
- Pagination: `offset` + `limit` query params, max 250 per request
|
|
@@ -178,18 +188,22 @@ Four services. `CongressApiService` backs nine tools and the House branch of `co
|
|
|
178
188
|
- The whole session menu is one file; each vote's roster ships inline — pagination is client-side
|
|
179
189
|
- The host returns HTTP 200 with an HTML page for unknown congress/session/vote, so "not found" is detected from the body, not the status code
|
|
180
190
|
- Party totals are derived from the roster (the feed publishes none)
|
|
191
|
+
- senate.gov's firewall answers automated clients with HTTP 403, and a retry meets the same refusal: a 403 fails on the first attempt as `upstream_error` with `retryable: false` and a hint that House votes are unaffected, while a 5xx or timeout is retried within the call budget
|
|
181
192
|
|
|
182
193
|
**`CongressDocumentsService`** — reads the document bodies behind Congress.gov's format URLs (`www.congress.gov`):
|
|
183
194
|
- A second host, so a sibling service rather than a method on `CongressApiService` — no API key, no JSON, and enrolled omnibus bills that run to ten megabytes
|
|
184
195
|
- URLs are resolved from upstream format metadata and checked against a `www.congress.gov` allowlist; a caller never supplies one
|
|
185
|
-
- The fetch is bounded: a 25 MB ceiling (refused on `Content-Length`, else mid-stream), a
|
|
186
|
-
- The body is extracted as it streams and only the requested window is
|
|
196
|
+
- The fetch is bounded: a 25 MB ceiling (refused on `Content-Length`, else mid-stream), a content-type allowlist, and the call budget (above) — the api.congress.gov format lookup the handler makes first spends from it, and the document attempts and backoff share what is left
|
|
197
|
+
- The body is extracted as it streams and only the requested window is extracted into memory; the ceiling bounds how long a response may run, not how much of one fits in a buffer (transient allocation still scales with the body — peak RSS runs above a buffered read)
|
|
198
|
+
- A document that runs past its window keeps its decompressed body in `DocumentBodyCache` — process-local, one cache for every tenant (each body is public and fetched without credentials), 64 MB LRU, 16 MB per body (a larger one is captured until it passes that and then dropped), 10-minute TTL from insert — and later windows replay those bytes through the same extractor in 128 KiB slices with a macrotask between them: one fetch per document per TTL, and a replay never spends the budget. A window that reaches the end of its document caches nothing, a cancelled read caches nothing, and concurrent cold reads each fetch (no single-flight)
|
|
187
199
|
- The structured response is bounded by an exact character window — `structuredContent.content.text` and its offsets index the extracted plain text and are never snapped to section breaks, so feeding `nextOffset` back walks a document with no overlap and no gap; `content[]` wraps that unchanged window in a dynamically safe Markdown fence for presentation
|
|
188
200
|
- `extract-text.ts` unwraps GPO's `<pre>` print output verbatim (whitespace is the document's structure), while no-`<pre>` XML gains one space only at an otherwise-unseparated sibling close→open element boundary; both readings decode character references through `src/utils/character-references.ts` — the one decoder every HTML/XML → text path in the server shares, single-pass so an escaped reference stays escaped — and `extract-text-stream.ts` is their incremental twin, pinned byte-for-byte against the whole-string extractor
|
|
189
201
|
|
|
190
202
|
**`CongressMirrorService`** — local SQLite FTS5 mirror of bill title + CRS summary text, backing `congressgov_search_bills` only:
|
|
191
203
|
- Opt-in via `CONGRESS_MIRROR_ENABLED` (off by default); built out-of-band via the `mirror:init`/`mirror:refresh` scripts, never on server startup
|
|
192
204
|
- No live-API fallback — ingests through the existing `CongressApiService`, and a mirror that hasn't finished its initial build returns an empty result with a notice, not an error
|
|
205
|
+
- Every sync re-walks each congress's whole summaries list, requested from January 1 of the congress's first year with no end date (without `fromDateTime`, Congress.gov returns only the last day's summaries); the `updateDate` checkpoint is persisted only once the whole walk finishes; store spec `version: 2` migrates an older store by clearing its checkpoint, so the next sync backfills
|
|
206
|
+
- The `mirror:*` scripts initialize the framework logger: log lines go to stderr as JSON, stdout carries only the script's own output
|
|
193
207
|
- `createApp({ teardown })` stops the refresh schedule, cancels and awaits an active refresh, then closes the SQLite mirror. With `MCP_SESSION_MODE` unset, HTTP defaults to stateless; an explicit `MCP_SESSION_MODE` value still overrides it. No handler uses `ctx.requestInput`.
|
|
194
208
|
|
|
195
209
|
**Usage in tools:**
|
|
@@ -198,7 +212,7 @@ import { getCongressApi } from '@/services/congress-api/congress-api-service.js'
|
|
|
198
212
|
|
|
199
213
|
async handler(input, ctx) {
|
|
200
214
|
const api = getCongressApi();
|
|
201
|
-
const bills = await api.listBills({ congress: input.congress, limit: input.limit });
|
|
215
|
+
const bills = await api.listBills({ congress: input.congress, limit: input.limit }, ctx);
|
|
202
216
|
ctx.log.info('Bills fetched', { congress: input.congress, count: bills.bills.length });
|
|
203
217
|
return bills;
|
|
204
218
|
}
|
|
@@ -215,11 +229,13 @@ All tools share these patterns. The service layer handles them uniformly:
|
|
|
215
229
|
| Condition | Error |
|
|
216
230
|
|:----------|:------|
|
|
217
231
|
| Rate limit hit | `rateLimited(...)` with `rate_limited`, credential/shared-quota guidance and upstream `Retry-After` when supplied |
|
|
218
|
-
|
|
|
219
|
-
| API unavailable (5xx) | `
|
|
232
|
+
| Attempt timeout, call budget spent, or HTTP timeout response | `Timeout` (-32004), `upstream_timeout`; a spent budget names it (`{upstream} did not deliver {request} within this call's 50-second budget for upstream requests (N attempts).`) and carries `budgetMs`/`retryAttempts`; HTTP responses retain status |
|
|
233
|
+
| API unavailable (5xx) | `ServiceUnavailable` (-32000), `upstream_error`: `Congress.gov returned an unexpected error (HTTP {status}).`; a 501 carries `retryable: false` |
|
|
220
234
|
| Entity not found (404) | `notFound('{entity} not found', { ...identifiers })` |
|
|
235
|
+
| Upstream rejects the request (400, 401, 402, 403, 422) | `invalidParams('Congress.gov rejected the request as malformed (HTTP {status}).')`, `invalid_request` |
|
|
236
|
+
| Rejected API key (401/403 whose body carries an api.data.gov `API_KEY_*` code) | `configurationError(...)` (-32008), `api_key_rejected`, `retryable: false`, gateway code in `data.upstreamCode` and the message; the hint tells the operator to replace or unset `CONGRESS_API_KEY` and never echoes the key. Classified on the body code, since a malformed path segment is also a plain 403 (#34) |
|
|
221
237
|
| Invalid params | `validationError('...', { field })` |
|
|
222
|
-
| Network error | `
|
|
238
|
+
| Network error | `ServiceUnavailable` (-32000), `upstream_error`: `Congress.gov returned an unexpected error.` |
|
|
223
239
|
|
|
224
240
|
The `content` operation adds `documentErrorContracts` (tool-helpers) on top: `document_unavailable`, `format_unavailable`, `document_fetch_failed`, `document_too_large`, and `offset_past_end` — the last of which `congressgov_bill_lookup` also raises from its `summaries` branch when `characterOffset` is past the end of every returned row's text. `CongressDocumentsService` raises each with a matching `data.reason`; the framework fills the declared `recovery` hint when the throw carries none, service throws included. Explicit recovery overrides win. A direct `definition.handler(...)` test sees the throw unchanged; retain `ctx.recoveryFor` where such a test asserts the hint, or assert the filled hint through `runToolContract`.
|
|
225
241
|
|
|
@@ -256,9 +272,18 @@ import { getServerConfig } from '@/config/server-config.js';
|
|
|
256
272
|
|
|
257
273
|
## Skills
|
|
258
274
|
|
|
259
|
-
|
|
275
|
+
Two skill trees live at the project root, written for different readers:
|
|
276
|
+
|
|
277
|
+
| Tree | For | Written by |
|
|
278
|
+
|:-----|:----|:-----------|
|
|
279
|
+
| `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |
|
|
280
|
+
| `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | `congressgov-guide/SKILL.md`, maintained with the `add-skill` skill |
|
|
281
|
+
|
|
282
|
+
Development skills are modular instructions. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Their directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand them to every agent that installs it.
|
|
283
|
+
|
|
284
|
+
`skills/congressgov-guide/SKILL.md` reaches plugin installs and the `.mcpb` bundle as-is and ships in the npm package and Docker image. `createApp({ skills: 'skills' })` serves it through the MCP Skills extension and the `skill://congressgov-guide/SKILL.md` resource. `lint:packaging` validates the tree.
|
|
260
285
|
|
|
261
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent).
|
|
286
|
+
**Agent skill directory:** Copy `framework-skills/` into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
262
287
|
|
|
263
288
|
Available skills:
|
|
264
289
|
|
|
@@ -270,6 +295,7 @@ Available skills:
|
|
|
270
295
|
| `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
|
|
271
296
|
| `add-resource` | Scaffold a new resource definition |
|
|
272
297
|
| `add-prompt` | Scaffold a new prompt definition |
|
|
298
|
+
| `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |
|
|
273
299
|
| `add-service` | Scaffold a new service integration |
|
|
274
300
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
275
301
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
@@ -317,7 +343,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
317
343
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
318
344
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
319
345
|
| `bun run lint:mcp` | Validate MCP tool/resource/prompt definitions |
|
|
320
|
-
| `bun run lint:packaging` | Validate env var alignment
|
|
346
|
+
| `bun run lint:packaging` | Validate bundle exclusions, plugin manifests, published skills, env var alignment, and version parity |
|
|
321
347
|
| `bun run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
|
|
322
348
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
323
349
|
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
@@ -334,7 +360,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
334
360
|
|
|
335
361
|
## Bundling
|
|
336
362
|
|
|
337
|
-
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop.
|
|
363
|
+
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `.mcpbignore` excludes root development paths with anchored patterns; root `skills/` ships. `scripts/clean-mcpb.ts` prunes dev dependencies and strips dependency agent docs, platform-specific native bindings, node-gyp build output that records host paths, and framework build-only files. Compiled native addons stay. Run `lint:packaging` after the completed bundle. MCPB is stdio-only; HTTP deployments are unaffected. The bundle file ships as `dist/congressgov-mcp-server.mcpb`.
|
|
338
364
|
|
|
339
365
|
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match, every `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.X}"`, and an optional string option carries `"default": ""`. The host substitutes only the `user_config` reference; `"${X}"` reaches the server literally.
|
|
340
366
|
|
|
@@ -400,6 +426,7 @@ The `release-and-publish` run handles npm, MCP Registry, bundle, GitHub Release,
|
|
|
400
426
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
401
427
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
402
428
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
429
|
+
- [ ] Root `plugin.json` metadata matches `package.json`; `mcp.json` uses the unscoped server key, `npx -y <full package name>`, and the fixed env from `.codex-plugin/mcp.json`. User variables remain in the overlays; `.codex-plugin/plugin.json` retains `mcpServers: './.codex-plugin/mcp.json'`
|
|
403
430
|
- [ ] `.codex-plugin/plugin.json` populated — `name` and `interface.displayName` = unscoped repo name; `version`, `description`, `repository`, `license` and `interface.shortDescription` from `package.json`
|
|
404
431
|
- [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; every user-supplied variable is listed in `env_vars`. Never write `"KEY": ""` into `env` — it overrides the user's exported key
|
|
405
432
|
- [ ] `.claude-plugin/plugin.json` populated — unscoped `name`, `version`, `description`, `author`, `repository`, `license`, `keywords`; inline `mcpServers` entry keyed by the unscoped repo name. User-supplied variables use `userConfig` and `"KEY": "${user_config.<option>}"` env references, matching `manifest.json`
|
package/CLAUDE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** congressgov-mcp-server
|
|
4
|
-
**Version:** 0.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 0.9.1
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/server` ^2.
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` ^2.4.0
|
|
8
8
|
**Zod:** ^4.6.7
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
@@ -74,7 +74,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
74
74
|
| `congressgov_member_lookup` | Discover members by state/district/congress, retrieve legislative portfolios |
|
|
75
75
|
| `congressgov_committee_lookup` | Browse committees and retrieve legislation, reports, nominations |
|
|
76
76
|
| `congressgov_roll_votes` | Retrieve House and Senate roll call votes and member voting positions |
|
|
77
|
-
| `congressgov_senate_nominations` | Browse presidential nominations, track Senate confirmation pipeline |
|
|
77
|
+
| `congressgov_senate_nominations` | Browse presidential nominations, track Senate confirmation pipeline; an action citing its own Senate record vote (`actions` rows, `latestAction` on `list`/`get`) carries `rollVote`, the `congressgov_roll_votes` arguments for it, from the 101st Congress on |
|
|
78
78
|
| `congressgov_bill_summaries` | Browse recent CRS bill summaries — the "what's happening" feed, bounded by the shared summary page budget and per-row text window |
|
|
79
79
|
| `congressgov_crs_reports` | Browse and retrieve nonpartisan CRS policy analysis reports |
|
|
80
80
|
| `congressgov_committee_reports` | Browse and retrieve committee reports accompanying legislation + bounded report text via `content` |
|
|
@@ -83,7 +83,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
83
83
|
|
|
84
84
|
**Summary bounding** (`src/mcp-server/tools/summary-window.ts`, constants in `tool-helpers.ts`). Both summary surfaces enforce two limits. `SUMMARY_PAGE_CHARACTERS` stops a page once its serialized rows reach the budget — the first row always returns, `pagination.nextOffset` counts the rows actually returned, and an enrichment notice discloses the stop. `DEFAULT_SUMMARY_TEXT_CHARACTERS` bounds one row's upstream `text`; a windowed row gains `textTotalCharacters` / `textTruncated` / `textNextOffset`, and its window end is retracted off any HTML tag or character reference it would otherwise split (unless the construct is wider than the whole window, where the requested limit wins). A row inside both limits is returned untouched, so an unbounded page stays byte-identical. `congressgov_bill_lookup` `summaries` overrides the row window with `characterOffset`/`characterLimit` and selects one version with `versionCode` — Congress.gov publishes no version filter, so selection reads the bill's whole summary list and matches client-side.
|
|
85
85
|
|
|
86
|
-
### Resources (
|
|
86
|
+
### Resources (6)
|
|
87
87
|
|
|
88
88
|
| URI Template | Description |
|
|
89
89
|
|:-------------|:------------|
|
|
@@ -92,6 +92,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
92
92
|
| `congress://member/{bioguideId}` | Member profile |
|
|
93
93
|
| `congress://bill/{congress}/{billType}/{billNumber}` | Bill detail |
|
|
94
94
|
| `congress://committee/{committeeCode}` | Committee detail |
|
|
95
|
+
| `skill://congressgov-guide/SKILL.md` | Published legislative research guide |
|
|
95
96
|
|
|
96
97
|
### Prompts (2)
|
|
97
98
|
|
|
@@ -110,11 +111,13 @@ src/
|
|
|
110
111
|
config/
|
|
111
112
|
server-config.ts # CONGRESS_API_KEY, base URL (Zod schema)
|
|
112
113
|
services/
|
|
114
|
+
call-budget.ts # One 50 s upstream budget per tool call / resource read, shared by the three HTTP services
|
|
113
115
|
congress-api/
|
|
114
116
|
congress-api-service.ts # API client — auth, pagination, rate limiting
|
|
115
117
|
types.ts # API response types
|
|
116
118
|
congress-documents/
|
|
117
|
-
congress-documents-service.ts # www.congress.gov document fetch — host allowlist, byte ceiling, character window
|
|
119
|
+
congress-documents-service.ts # www.congress.gov document fetch — host allowlist, byte ceiling, call budget, character window
|
|
120
|
+
document-body-cache.ts # Process-local LRU of decompressed bodies — one fetch per document per TTL
|
|
118
121
|
document-formats.ts # Format label → URL resolution over upstream format lists
|
|
119
122
|
extract-text.ts # GPO `<pre>` / XML body → deterministic plain text
|
|
120
123
|
extract-text-stream.ts # Incremental twin of extract-text.ts — one character window, constant memory
|
|
@@ -127,7 +130,7 @@ src/
|
|
|
127
130
|
types.ts # Mirror row, search filters, search page types
|
|
128
131
|
senate-lis/
|
|
129
132
|
senate-vote-service.ts # Senate LIS XML client — fetch, retry, not-found
|
|
130
|
-
parse.ts # pure XML → domain parsers (fast-xml-parser)
|
|
133
|
+
parse.ts # pure XML → domain parsers (fast-xml-parser); Senate vote coverage, session for a date
|
|
131
134
|
types.ts # Senate vote domain types
|
|
132
135
|
mcp-server/
|
|
133
136
|
tools/definitions/
|
|
@@ -153,6 +156,7 @@ src/
|
|
|
153
156
|
legislative-research.prompt.ts # congressgov_legislative_research
|
|
154
157
|
utils/
|
|
155
158
|
character-references.ts # Single-pass HTML/XML character-reference decoding
|
|
159
|
+
congress-calendar.ts # ordinal(), congressFirstYear(), currentCongressNumber() — congress numbering arithmetic
|
|
156
160
|
scripts/
|
|
157
161
|
_mirror-context.ts # Shared bootstrap for the mirror lifecycle CLI scripts
|
|
158
162
|
congress-mirror-init.ts # mirror:init — full out-of-band mirror build
|
|
@@ -166,6 +170,12 @@ scripts/
|
|
|
166
170
|
|
|
167
171
|
Four services. `CongressApiService` backs nine tools and the House branch of `congressgov_roll_votes`; `SenateVoteService` backs only the Senate branch of `congressgov_roll_votes` (the Congress.gov API exposes no Senate vote namespace); `CongressMirrorService` backs `congressgov_search_bills` only; `CongressDocumentsService` backs the `content` operation on `congressgov_bill_lookup`, `congressgov_committee_reports`, and `congressgov_daily_record`.
|
|
168
172
|
|
|
173
|
+
**Call budget** (`call-budget.ts`, `withCallBudget`) — every upstream request a tool call or resource read makes through `CongressApiService`, `SenateVoteService`, or `CongressDocumentsService` spends one 50 s budget (`CALL_BUDGET_MS`) counted from the call's start (`ctx.timestamp`), so chained and parallel requests (a roster paged four times, a `content` lookup and then its document) end together before the 60 s default MCP client timeout:
|
|
174
|
+
- Each request's retry ladder gets what is left as `withRetry` `deadlineMs`; each attempt's timeout (10 s API, 12 s Senate, 30 s document) is clamped to the remainder and its I/O carries the budget's signal
|
|
175
|
+
- A backoff the remainder cannot hold fails at once as the expiry; a `Retry-After` wait it cannot hold fails at once with the upstream's own error and `retryAfter`; a request the spent budget cannot cover is never sent
|
|
176
|
+
- Running out is `Timeout`, reason `upstream_timeout`, with `budgetMs` and `retryAttempts`. A caller abort stays `RequestCancelled`, and a 429 is still never retried
|
|
177
|
+
- Services read the budget from the handler's own `ctx` — pass it to every service call, never a context derived from it (that carries a later timestamp). A request with no `ctx` — the mirror ingester and the `mirror:*` scripts — runs the plain per-request ladder, unbudgeted
|
|
178
|
+
|
|
169
179
|
**`CongressApiService`** — wraps the Congress.gov REST API v3:
|
|
170
180
|
- API key via `X-Api-Key` header (never logged; kept out of the URL so it can't leak in upstream error messages)
|
|
171
181
|
- Pagination: `offset` + `limit` query params, max 250 per request
|
|
@@ -178,18 +188,22 @@ Four services. `CongressApiService` backs nine tools and the House branch of `co
|
|
|
178
188
|
- The whole session menu is one file; each vote's roster ships inline — pagination is client-side
|
|
179
189
|
- The host returns HTTP 200 with an HTML page for unknown congress/session/vote, so "not found" is detected from the body, not the status code
|
|
180
190
|
- Party totals are derived from the roster (the feed publishes none)
|
|
191
|
+
- senate.gov's firewall answers automated clients with HTTP 403, and a retry meets the same refusal: a 403 fails on the first attempt as `upstream_error` with `retryable: false` and a hint that House votes are unaffected, while a 5xx or timeout is retried within the call budget
|
|
181
192
|
|
|
182
193
|
**`CongressDocumentsService`** — reads the document bodies behind Congress.gov's format URLs (`www.congress.gov`):
|
|
183
194
|
- A second host, so a sibling service rather than a method on `CongressApiService` — no API key, no JSON, and enrolled omnibus bills that run to ten megabytes
|
|
184
195
|
- URLs are resolved from upstream format metadata and checked against a `www.congress.gov` allowlist; a caller never supplies one
|
|
185
|
-
- The fetch is bounded: a 25 MB ceiling (refused on `Content-Length`, else mid-stream), a
|
|
186
|
-
- The body is extracted as it streams and only the requested window is
|
|
196
|
+
- The fetch is bounded: a 25 MB ceiling (refused on `Content-Length`, else mid-stream), a content-type allowlist, and the call budget (above) — the api.congress.gov format lookup the handler makes first spends from it, and the document attempts and backoff share what is left
|
|
197
|
+
- The body is extracted as it streams and only the requested window is extracted into memory; the ceiling bounds how long a response may run, not how much of one fits in a buffer (transient allocation still scales with the body — peak RSS runs above a buffered read)
|
|
198
|
+
- A document that runs past its window keeps its decompressed body in `DocumentBodyCache` — process-local, one cache for every tenant (each body is public and fetched without credentials), 64 MB LRU, 16 MB per body (a larger one is captured until it passes that and then dropped), 10-minute TTL from insert — and later windows replay those bytes through the same extractor in 128 KiB slices with a macrotask between them: one fetch per document per TTL, and a replay never spends the budget. A window that reaches the end of its document caches nothing, a cancelled read caches nothing, and concurrent cold reads each fetch (no single-flight)
|
|
187
199
|
- The structured response is bounded by an exact character window — `structuredContent.content.text` and its offsets index the extracted plain text and are never snapped to section breaks, so feeding `nextOffset` back walks a document with no overlap and no gap; `content[]` wraps that unchanged window in a dynamically safe Markdown fence for presentation
|
|
188
200
|
- `extract-text.ts` unwraps GPO's `<pre>` print output verbatim (whitespace is the document's structure), while no-`<pre>` XML gains one space only at an otherwise-unseparated sibling close→open element boundary; both readings decode character references through `src/utils/character-references.ts` — the one decoder every HTML/XML → text path in the server shares, single-pass so an escaped reference stays escaped — and `extract-text-stream.ts` is their incremental twin, pinned byte-for-byte against the whole-string extractor
|
|
189
201
|
|
|
190
202
|
**`CongressMirrorService`** — local SQLite FTS5 mirror of bill title + CRS summary text, backing `congressgov_search_bills` only:
|
|
191
203
|
- Opt-in via `CONGRESS_MIRROR_ENABLED` (off by default); built out-of-band via the `mirror:init`/`mirror:refresh` scripts, never on server startup
|
|
192
204
|
- No live-API fallback — ingests through the existing `CongressApiService`, and a mirror that hasn't finished its initial build returns an empty result with a notice, not an error
|
|
205
|
+
- Every sync re-walks each congress's whole summaries list, requested from January 1 of the congress's first year with no end date (without `fromDateTime`, Congress.gov returns only the last day's summaries); the `updateDate` checkpoint is persisted only once the whole walk finishes; store spec `version: 2` migrates an older store by clearing its checkpoint, so the next sync backfills
|
|
206
|
+
- The `mirror:*` scripts initialize the framework logger: log lines go to stderr as JSON, stdout carries only the script's own output
|
|
193
207
|
- `createApp({ teardown })` stops the refresh schedule, cancels and awaits an active refresh, then closes the SQLite mirror. With `MCP_SESSION_MODE` unset, HTTP defaults to stateless; an explicit `MCP_SESSION_MODE` value still overrides it. No handler uses `ctx.requestInput`.
|
|
194
208
|
|
|
195
209
|
**Usage in tools:**
|
|
@@ -198,7 +212,7 @@ import { getCongressApi } from '@/services/congress-api/congress-api-service.js'
|
|
|
198
212
|
|
|
199
213
|
async handler(input, ctx) {
|
|
200
214
|
const api = getCongressApi();
|
|
201
|
-
const bills = await api.listBills({ congress: input.congress, limit: input.limit });
|
|
215
|
+
const bills = await api.listBills({ congress: input.congress, limit: input.limit }, ctx);
|
|
202
216
|
ctx.log.info('Bills fetched', { congress: input.congress, count: bills.bills.length });
|
|
203
217
|
return bills;
|
|
204
218
|
}
|
|
@@ -215,11 +229,13 @@ All tools share these patterns. The service layer handles them uniformly:
|
|
|
215
229
|
| Condition | Error |
|
|
216
230
|
|:----------|:------|
|
|
217
231
|
| Rate limit hit | `rateLimited(...)` with `rate_limited`, credential/shared-quota guidance and upstream `Retry-After` when supplied |
|
|
218
|
-
|
|
|
219
|
-
| API unavailable (5xx) | `
|
|
232
|
+
| Attempt timeout, call budget spent, or HTTP timeout response | `Timeout` (-32004), `upstream_timeout`; a spent budget names it (`{upstream} did not deliver {request} within this call's 50-second budget for upstream requests (N attempts).`) and carries `budgetMs`/`retryAttempts`; HTTP responses retain status |
|
|
233
|
+
| API unavailable (5xx) | `ServiceUnavailable` (-32000), `upstream_error`: `Congress.gov returned an unexpected error (HTTP {status}).`; a 501 carries `retryable: false` |
|
|
220
234
|
| Entity not found (404) | `notFound('{entity} not found', { ...identifiers })` |
|
|
235
|
+
| Upstream rejects the request (400, 401, 402, 403, 422) | `invalidParams('Congress.gov rejected the request as malformed (HTTP {status}).')`, `invalid_request` |
|
|
236
|
+
| Rejected API key (401/403 whose body carries an api.data.gov `API_KEY_*` code) | `configurationError(...)` (-32008), `api_key_rejected`, `retryable: false`, gateway code in `data.upstreamCode` and the message; the hint tells the operator to replace or unset `CONGRESS_API_KEY` and never echoes the key. Classified on the body code, since a malformed path segment is also a plain 403 (#34) |
|
|
221
237
|
| Invalid params | `validationError('...', { field })` |
|
|
222
|
-
| Network error | `
|
|
238
|
+
| Network error | `ServiceUnavailable` (-32000), `upstream_error`: `Congress.gov returned an unexpected error.` |
|
|
223
239
|
|
|
224
240
|
The `content` operation adds `documentErrorContracts` (tool-helpers) on top: `document_unavailable`, `format_unavailable`, `document_fetch_failed`, `document_too_large`, and `offset_past_end` — the last of which `congressgov_bill_lookup` also raises from its `summaries` branch when `characterOffset` is past the end of every returned row's text. `CongressDocumentsService` raises each with a matching `data.reason`; the framework fills the declared `recovery` hint when the throw carries none, service throws included. Explicit recovery overrides win. A direct `definition.handler(...)` test sees the throw unchanged; retain `ctx.recoveryFor` where such a test asserts the hint, or assert the filled hint through `runToolContract`.
|
|
225
241
|
|
|
@@ -256,9 +272,18 @@ import { getServerConfig } from '@/config/server-config.js';
|
|
|
256
272
|
|
|
257
273
|
## Skills
|
|
258
274
|
|
|
259
|
-
|
|
275
|
+
Two skill trees live at the project root, written for different readers:
|
|
276
|
+
|
|
277
|
+
| Tree | For | Written by |
|
|
278
|
+
|:-----|:----|:-----------|
|
|
279
|
+
| `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |
|
|
280
|
+
| `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | `congressgov-guide/SKILL.md`, maintained with the `add-skill` skill |
|
|
281
|
+
|
|
282
|
+
Development skills are modular instructions. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Their directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand them to every agent that installs it.
|
|
283
|
+
|
|
284
|
+
`skills/congressgov-guide/SKILL.md` reaches plugin installs and the `.mcpb` bundle as-is and ships in the npm package and Docker image. `createApp({ skills: 'skills' })` serves it through the MCP Skills extension and the `skill://congressgov-guide/SKILL.md` resource. `lint:packaging` validates the tree.
|
|
260
285
|
|
|
261
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent).
|
|
286
|
+
**Agent skill directory:** Copy `framework-skills/` into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
262
287
|
|
|
263
288
|
Available skills:
|
|
264
289
|
|
|
@@ -270,6 +295,7 @@ Available skills:
|
|
|
270
295
|
| `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
|
|
271
296
|
| `add-resource` | Scaffold a new resource definition |
|
|
272
297
|
| `add-prompt` | Scaffold a new prompt definition |
|
|
298
|
+
| `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |
|
|
273
299
|
| `add-service` | Scaffold a new service integration |
|
|
274
300
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
275
301
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
@@ -317,7 +343,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
317
343
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
318
344
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
319
345
|
| `bun run lint:mcp` | Validate MCP tool/resource/prompt definitions |
|
|
320
|
-
| `bun run lint:packaging` | Validate env var alignment
|
|
346
|
+
| `bun run lint:packaging` | Validate bundle exclusions, plugin manifests, published skills, env var alignment, and version parity |
|
|
321
347
|
| `bun run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
|
|
322
348
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
323
349
|
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
@@ -334,7 +360,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
334
360
|
|
|
335
361
|
## Bundling
|
|
336
362
|
|
|
337
|
-
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop.
|
|
363
|
+
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `.mcpbignore` excludes root development paths with anchored patterns; root `skills/` ships. `scripts/clean-mcpb.ts` prunes dev dependencies and strips dependency agent docs, platform-specific native bindings, node-gyp build output that records host paths, and framework build-only files. Compiled native addons stay. Run `lint:packaging` after the completed bundle. MCPB is stdio-only; HTTP deployments are unaffected. The bundle file ships as `dist/congressgov-mcp-server.mcpb`.
|
|
338
364
|
|
|
339
365
|
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match, every `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.X}"`, and an optional string option carries `"default": ""`. The host substitutes only the `user_config` reference; `"${X}"` reaches the server literally.
|
|
340
366
|
|
|
@@ -400,6 +426,7 @@ The `release-and-publish` run handles npm, MCP Registry, bundle, GitHub Release,
|
|
|
400
426
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
401
427
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
402
428
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
429
|
+
- [ ] Root `plugin.json` metadata matches `package.json`; `mcp.json` uses the unscoped server key, `npx -y <full package name>`, and the fixed env from `.codex-plugin/mcp.json`. User variables remain in the overlays; `.codex-plugin/plugin.json` retains `mcpServers: './.codex-plugin/mcp.json'`
|
|
403
430
|
- [ ] `.codex-plugin/plugin.json` populated — `name` and `interface.displayName` = unscoped repo name; `version`, `description`, `repository`, `license` and `interface.shortDescription` from `package.json`
|
|
404
431
|
- [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; every user-supplied variable is listed in `env_vars`. Never write `"KEY": ""` into `env` — it overrides the user's exported key
|
|
405
432
|
- [ ] `.claude-plugin/plugin.json` populated — unscoped `name`, `version`, `description`, `author`, `repository`, `license`, `keywords`; inline `mcpServers` entry keyed by the unscoped repo name. User-supplied variables use `userConfig` and `"KEY": "${user_config.<option>}"` env references, matching `manifest.json`
|
package/Dockerfile
CHANGED
|
@@ -28,6 +28,9 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
|
28
28
|
# Copy the rest of the source code
|
|
29
29
|
COPY . .
|
|
30
30
|
|
|
31
|
+
# Published agent skills are read from the application root at runtime.
|
|
32
|
+
RUN mkdir -p skills
|
|
33
|
+
|
|
31
34
|
# Build the application
|
|
32
35
|
RUN bun run build
|
|
33
36
|
|
|
@@ -134,6 +137,7 @@ COPY --from=deps /usr/src/app/node_modules ./node_modules
|
|
|
134
137
|
|
|
135
138
|
# Copy the compiled application code from the build stage
|
|
136
139
|
COPY --from=build /usr/src/app/dist ./dist
|
|
140
|
+
COPY --from=build /usr/src/app/skills ./skills
|
|
137
141
|
|
|
138
142
|
# Copy the mirror lifecycle scripts (mirror:init / mirror:refresh / mirror:verify)
|
|
139
143
|
# and the shared context shim they import, then emit a runtime tsconfig so Bun
|