@cyanheads/congressgov-mcp-server 0.7.2 → 0.7.4

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** congressgov-mcp-server
4
- **Version:** 0.7.2
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
4
+ **Version:** 0.7.4
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` + `@modelcontextprotocol/client` ^2.0.0
7
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0
8
8
  **Zod:** ^4.6.5
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.
@@ -53,9 +53,10 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
53
53
 
54
54
  - **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
55
55
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
56
- - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
56
+ - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly. Values round-trip as JSON; a `Date` reads back as an ISO string.
57
57
  - **Need input the caller didn't supply?** `return ctx.requestInput(...)` and read `ctx.inputs` when the handler is re-entered. Never `await` for user input mid-handler.
58
58
  - **Secrets in env vars only** — never hardcoded.
59
+ - **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.
59
60
  - **All tools are read-only.** Every tool gets `annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true }`.
60
61
  - **API key stays out of logs.** The service sends it in the `X-Api-Key` header — never log credentials.
61
62
  - **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
@@ -217,10 +218,12 @@ All tools share these patterns. The service layer handles them uniformly:
217
218
  | Invalid params | `validationError('...', { field })` |
218
219
  | Network error | `serviceUnavailable('Unable to reach the Congress.gov API.')` |
219
220
 
220
- 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` and resolves the hint via `ctx.recoveryFor` — note that `ctx.fail` does **not** auto-populate `recovery`, so handler-side `ctx.fail` calls must spread `ctx.recoveryFor(reason)` into their data or the hint never reaches the wire.
221
+ 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`.
221
222
 
222
223
  Service-only contract reasons carry `thrownBy: 'service'`; handler-local reasons remain checked by `error-contract-unthrown`. `RequestCancelled` is a baseline code and needs no contract entry. Retry predicates compose `defaultIsTransient` with the existing exclusion of `RateLimited` codes and preserve upstream `retryable: false`.
223
224
 
225
+ Framework-handled tool, resource, and prompt errors carry `data.requestId`, matching that call's server log records; a tool's `content[]` closes with `request <id>`. Protocol-level rejections and resource-parameter errors containing only `{ uri }` are excluded. `runToolContract` fills declared recovery hints but adds no request ID.
226
+
224
227
  ---
225
228
 
226
229
  ## Naming
@@ -250,7 +253,7 @@ import { getServerConfig } from '@/config/server-config.js';
250
253
 
251
254
  ## Skills
252
255
 
253
- Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. Plugin hosts auto-load a root `skills/`, so development guidance belongs in `framework-skills/`.
256
+ Skills are modular instructions in `framework-skills/` at the project root. 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. The 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 these development skills to every agent that installs it. Keep `skills/` free for skills meant for those agents.
254
257
 
255
258
  **Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). This makes skills available as context without needing to reference `framework-skills/` paths manually. After framework updates, run the `maintenance` skill — it re-syncs the agent directory automatically (Phase B).
256
259
 
@@ -271,19 +274,19 @@ Available skills:
271
274
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
272
275
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
273
276
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
274
- | `git-wrapup` | Land the commit stack, version bump, and changelog on a release branch; open the release PR |
275
- | `release-pr-review` | Review the release PR and land fixes as ordinary commits; keep its body in sync |
276
- | `release-and-publish` | Fast-forward main, tag, push, and publish to npm, MCP Registry, GitHub Releases, and Docker |
277
+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
278
+ | `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
279
+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
277
280
  | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
278
- | `report-issue-framework` | File bugs/features against `@cyanheads/mcp-ts-core` |
279
- | `report-issue-local` | File bugs/features against this server's repo |
281
+ | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
282
+ | `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
280
283
  | `api-auth` | Auth modes, scopes, JWT/OAuth |
281
284
  | `api-config` | AppConfig, parseConfig, env vars |
282
285
  | `api-context` | Context interface, RequestContext, logger, state, multi-round-trip input |
283
286
  | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
284
- | `api-linter` | MCP definition linter rules reference — look up `format-parity`, `schema-*`, `name-*`, `server-json-*` diagnostics |
285
- | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper — Tier 3 opt-in |
286
- | `api-mirror` | MirrorService: persistent local SQLite mirror of bulk upstream datasets — Tier 3 opt-in |
287
+ | `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
288
+ | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
289
+ | `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
287
290
  | `api-services` | LLM, Speech, Graph services |
288
291
  | `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
289
292
  | `api-testing` | createMockContext, test patterns |
@@ -315,8 +318,8 @@ When you complete a skill's checklist, check the boxes and add a completion time
315
318
  | `bun run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
316
319
  | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
317
320
  | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
318
- | `bun run release:github` | Create GitHub Release from the current annotated tag |
319
- | `bun run test` | Run tests |
321
+ | `bun run release:github` | Validate the current annotated tag and create its GitHub Release; `-- --check` validates only |
322
+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
320
323
  | `bun run dev:stdio` | Dev mode (stdio) |
321
324
  | `bun run dev:http` | Dev mode (HTTP) |
322
325
  | `bun run start:stdio` | Production mode (stdio) |
@@ -392,6 +395,6 @@ Remind the user to run these after completing a release flow.
392
395
  - [ ] Registered in `createApp()` arrays (directly or via barrel exports)
393
396
  - [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
394
397
  - [ ] `.codex-plugin/plugin.json` populated — `name` and `interface.displayName` = unscoped repo name; `version`, `description`, `repository`, `license` and `interface.shortDescription` from `package.json`
395
- - [ ] `.codex-plugin/mcp.json` updated — server name key matches `package.json` name; env vars added for any required API keys
396
- - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry with server name key, env vars for any required API keys
398
+ - [ ] `.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
399
+ - [ ] `.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`
397
400
  - [ ] `bun run devcheck` passes
package/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** congressgov-mcp-server
4
- **Version:** 0.7.2
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
4
+ **Version:** 0.7.4
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` + `@modelcontextprotocol/client` ^2.0.0
7
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0
8
8
  **Zod:** ^4.6.5
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.
@@ -53,9 +53,10 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
53
53
 
54
54
  - **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
55
55
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
56
- - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
56
+ - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly. Values round-trip as JSON; a `Date` reads back as an ISO string.
57
57
  - **Need input the caller didn't supply?** `return ctx.requestInput(...)` and read `ctx.inputs` when the handler is re-entered. Never `await` for user input mid-handler.
58
58
  - **Secrets in env vars only** — never hardcoded.
59
+ - **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.
59
60
  - **All tools are read-only.** Every tool gets `annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true }`.
60
61
  - **API key stays out of logs.** The service sends it in the `X-Api-Key` header — never log credentials.
61
62
  - **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
@@ -217,10 +218,12 @@ All tools share these patterns. The service layer handles them uniformly:
217
218
  | Invalid params | `validationError('...', { field })` |
218
219
  | Network error | `serviceUnavailable('Unable to reach the Congress.gov API.')` |
219
220
 
220
- 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` and resolves the hint via `ctx.recoveryFor` — note that `ctx.fail` does **not** auto-populate `recovery`, so handler-side `ctx.fail` calls must spread `ctx.recoveryFor(reason)` into their data or the hint never reaches the wire.
221
+ 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`.
221
222
 
222
223
  Service-only contract reasons carry `thrownBy: 'service'`; handler-local reasons remain checked by `error-contract-unthrown`. `RequestCancelled` is a baseline code and needs no contract entry. Retry predicates compose `defaultIsTransient` with the existing exclusion of `RateLimited` codes and preserve upstream `retryable: false`.
223
224
 
225
+ Framework-handled tool, resource, and prompt errors carry `data.requestId`, matching that call's server log records; a tool's `content[]` closes with `request <id>`. Protocol-level rejections and resource-parameter errors containing only `{ uri }` are excluded. `runToolContract` fills declared recovery hints but adds no request ID.
226
+
224
227
  ---
225
228
 
226
229
  ## Naming
@@ -250,7 +253,7 @@ import { getServerConfig } from '@/config/server-config.js';
250
253
 
251
254
  ## Skills
252
255
 
253
- Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. Plugin hosts auto-load a root `skills/`, so development guidance belongs in `framework-skills/`.
256
+ Skills are modular instructions in `framework-skills/` at the project root. 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. The 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 these development skills to every agent that installs it. Keep `skills/` free for skills meant for those agents.
254
257
 
255
258
  **Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). This makes skills available as context without needing to reference `framework-skills/` paths manually. After framework updates, run the `maintenance` skill — it re-syncs the agent directory automatically (Phase B).
256
259
 
@@ -271,19 +274,19 @@ Available skills:
271
274
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
272
275
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
273
276
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
274
- | `git-wrapup` | Land the commit stack, version bump, and changelog on a release branch; open the release PR |
275
- | `release-pr-review` | Review the release PR and land fixes as ordinary commits; keep its body in sync |
276
- | `release-and-publish` | Fast-forward main, tag, push, and publish to npm, MCP Registry, GitHub Releases, and Docker |
277
+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
278
+ | `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
279
+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
277
280
  | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
278
- | `report-issue-framework` | File bugs/features against `@cyanheads/mcp-ts-core` |
279
- | `report-issue-local` | File bugs/features against this server's repo |
281
+ | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
282
+ | `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
280
283
  | `api-auth` | Auth modes, scopes, JWT/OAuth |
281
284
  | `api-config` | AppConfig, parseConfig, env vars |
282
285
  | `api-context` | Context interface, RequestContext, logger, state, multi-round-trip input |
283
286
  | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
284
- | `api-linter` | MCP definition linter rules reference — look up `format-parity`, `schema-*`, `name-*`, `server-json-*` diagnostics |
285
- | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper — Tier 3 opt-in |
286
- | `api-mirror` | MirrorService: persistent local SQLite mirror of bulk upstream datasets — Tier 3 opt-in |
287
+ | `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
288
+ | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
289
+ | `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
287
290
  | `api-services` | LLM, Speech, Graph services |
288
291
  | `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
289
292
  | `api-testing` | createMockContext, test patterns |
@@ -315,8 +318,8 @@ When you complete a skill's checklist, check the boxes and add a completion time
315
318
  | `bun run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
316
319
  | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
317
320
  | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
318
- | `bun run release:github` | Create GitHub Release from the current annotated tag |
319
- | `bun run test` | Run tests |
321
+ | `bun run release:github` | Validate the current annotated tag and create its GitHub Release; `-- --check` validates only |
322
+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
320
323
  | `bun run dev:stdio` | Dev mode (stdio) |
321
324
  | `bun run dev:http` | Dev mode (HTTP) |
322
325
  | `bun run start:stdio` | Production mode (stdio) |
@@ -392,6 +395,6 @@ Remind the user to run these after completing a release flow.
392
395
  - [ ] Registered in `createApp()` arrays (directly or via barrel exports)
393
396
  - [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
394
397
  - [ ] `.codex-plugin/plugin.json` populated — `name` and `interface.displayName` = unscoped repo name; `version`, `description`, `repository`, `license` and `interface.shortDescription` from `package.json`
395
- - [ ] `.codex-plugin/mcp.json` updated — server name key matches `package.json` name; env vars added for any required API keys
396
- - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry with server name key, env vars for any required API keys
398
+ - [ ] `.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
399
+ - [ ] `.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`
397
400
  - [ ] `bun run devcheck` passes
package/Dockerfile CHANGED
@@ -7,9 +7,9 @@
7
7
  # Pinned to $BUILDPLATFORM: bun >= 1.4 aborts under QEMU user-mode emulation, so
8
8
  # leaving the build stage on the target platform kills the linux/amd64 leg of a
9
9
  # multi-arch buildx run on an arm64 host. This stage only runs tsc and emits
10
- # architecture-independent JavaScript, and the production stage installs its own
11
- # dependencies natively, so building it on the host architecture is safe.
12
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.0 AS build
10
+ # architecture-independent JavaScript; the native deps stage cross-installs the
11
+ # production tree, so both build-time Bun stages run on the host architecture.
12
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS build
13
13
 
14
14
  WORKDIR /usr/src/app
15
15
 
@@ -32,6 +32,79 @@ COPY . .
32
32
  RUN bun run build
33
33
 
34
34
 
35
+ # ==============================================================================
36
+ # Production Dependencies Stage
37
+ #
38
+ # Installs the production dependency tree for the target platform. Every step
39
+ # here can run JavaScript — bunfig.toml's security scanner runs as a Bun
40
+ # program, and so do the OTel and musl-prune scripts — so the stage runs on
41
+ # $BUILDPLATFORM and cross-installs with `--os`/`--cpu`, which pick each
42
+ # platform-specific optional dependency (native bindings such as DuckDB's) for
43
+ # the target. Only `node_modules` leaves this stage.
44
+ #
45
+ # A clean image rather than `FROM build`: the build stage's node_modules holds
46
+ # devDependencies.
47
+ # ==============================================================================
48
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS deps
49
+
50
+ WORKDIR /usr/src/app
51
+
52
+ # Copy dependency manifests. `bunfig.toml` rides along so every install below
53
+ # passes its release-age gate and security scanner, as a local install does.
54
+ COPY package.json bun.lock bunfig.toml ./
55
+
56
+ # The scanner bunfig.toml names is a devDependency, and Bun installs a missing
57
+ # scanner through the same production-filtered install, which omits it and
58
+ # aborts. Seed it from the build stage's full install instead. Remove this line,
59
+ # and the `rm` at the end of this stage, if bunfig.toml stops naming a scanner.
60
+ COPY --from=build /usr/src/app/node_modules/@socketsecurity/bun-security-scanner ./node_modules/@socketsecurity/bun-security-scanner
61
+
62
+ # Docker names the target architecture `amd64`/`arm64`; Bun's `--cpu` takes
63
+ # `x64`/`arm64`. Mapped once here, read by both installs below. `oven/bun`
64
+ # publishes only these two architectures, so any other target fails here.
65
+ ARG TARGETOS
66
+ ARG TARGETARCH
67
+ RUN case "$TARGETARCH" in \
68
+ amd64) echo x64 ;; \
69
+ arm64) echo arm64 ;; \
70
+ *) echo "Unsupported TARGETARCH '$TARGETARCH': expected amd64 or arm64" >&2; exit 1 ;; \
71
+ esac > .bun-cpu
72
+
73
+ # Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
74
+ # that are not needed in the final production image.
75
+ # `--omit=peer` drops the framework's optional peer tiers (test runner, service
76
+ # SDKs, parsers) that Bun would otherwise auto-install. Anything this server
77
+ # actually imports belongs in its own `dependencies`, so nothing needed at
78
+ # runtime is lost.
79
+ RUN --mount=type=cache,target=/root/.bun/install/cache \
80
+ bun install --production --omit=peer --frozen-lockfile --ignore-scripts \
81
+ --os="$TARGETOS" --cpu="$(cat .bun-cpu)"
82
+
83
+ # Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
84
+ # Installed by default. Omit them for a leaner image at build time
85
+ # with: docker build --build-arg OTEL_ENABLED=false
86
+ # The script reads the list and each range from the installed framework's
87
+ # `peerDependencies` and passes the target flags on to its `bun install`.
88
+ COPY scripts/install-otel.ts ./scripts/
89
+ ARG OTEL_ENABLED=true
90
+ RUN --mount=type=cache,target=/root/.bun/install/cache \
91
+ if [ "$OTEL_ENABLED" = "true" ]; then \
92
+ bun scripts/install-otel.ts --os="$TARGETOS" --cpu="$(cat .bun-cpu)"; \
93
+ fi
94
+
95
+ # `--os`/`--cpu` have no libc counterpart, so a native dependency published in
96
+ # glibc and musl variants (DuckDB's bindings, for one) installs both. The
97
+ # runtime image is Debian (glibc) and never loads the musl copy; the script
98
+ # deletes every package whose own `libc` admits only musl. It follows every
99
+ # install, since a later `bun install` restores what it removes. A server on a
100
+ # musl (Alpine) runtime image drops these two lines.
101
+ COPY scripts/prune-musl-packages.ts ./scripts/
102
+ RUN bun scripts/prune-musl-packages.ts
103
+
104
+ # The seeded scanner served only the installs above; keep it out of the image.
105
+ RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
106
+
107
+
35
108
  # ==============================================================================
36
109
  # Production Stage
37
110
  #
@@ -39,7 +112,7 @@ RUN bun run build
39
112
  # application. It uses a slim base image and only includes production
40
113
  # dependencies and build artifacts.
41
114
  # ==============================================================================
42
- FROM oven/bun:1.4.0-slim AS production
115
+ FROM oven/bun:1.4.2-slim AS production
43
116
 
44
117
  WORKDIR /usr/src/app
45
118
 
@@ -50,41 +123,14 @@ ENV NODE_ENV=production
50
123
  # OCI image metadata (https://github.com/opencontainers/image-spec/blob/main/annotations.md)
51
124
  ARG APP_VERSION
52
125
  LABEL org.opencontainers.image.title="congressgov-mcp-server"
53
- LABEL org.opencontainers.image.description="Access U.S. congressional data - bills, votes, members, committees - through MCP. STDIO & Streamable HTTP."
126
+ LABEL org.opencontainers.image.description="Access U.S. congressional data (bills, votes, members, committees) via MCP. STDIO or Streamable HTTP."
54
127
  LABEL org.opencontainers.image.source="https://github.com/cyanheads/congressgov-mcp-server"
55
128
  LABEL org.opencontainers.image.licenses="Apache-2.0"
56
129
  LABEL org.opencontainers.image.version="${APP_VERSION}"
57
130
 
58
- # Copy dependency manifests
59
- COPY package.json bun.lock ./
60
-
61
- # Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
62
- # that are not needed in the final production image.
63
- # `--omit=peer` drops the framework's optional peer tiers (test runner, service
64
- # SDKs, parsers) that Bun would otherwise auto-install. Anything this server
65
- # actually imports belongs in its own `dependencies`, so nothing needed at
66
- # runtime is lost. The OTEL step below carries the same flag — without it, that
67
- # install re-resolves the graph and pulls every optional peer back in.
68
- RUN --mount=type=cache,target=/root/.bun/install/cache \
69
- bun install --production --omit=peer --frozen-lockfile --ignore-scripts
70
-
71
- # Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
72
- # These are installed by default. Omit them for a smaller image with:
73
- # docker build --build-arg OTEL_ENABLED=false
74
- ARG OTEL_ENABLED=true
75
- RUN --mount=type=cache,target=/root/.bun/install/cache \
76
- if [ "$OTEL_ENABLED" = "true" ]; then \
77
- bun add --omit=dev --omit=peer --ignore-scripts @hono/otel \
78
- @opentelemetry/instrumentation-http \
79
- @opentelemetry/exporter-metrics-otlp-http \
80
- @opentelemetry/exporter-trace-otlp-http \
81
- @opentelemetry/instrumentation-pino \
82
- @opentelemetry/resources \
83
- @opentelemetry/sdk-metrics \
84
- @opentelemetry/sdk-node \
85
- @opentelemetry/sdk-trace-node \
86
- @opentelemetry/semantic-conventions; \
87
- fi
131
+ # The deps manifest is rewritten by the OTel install; retain the server manifest.
132
+ COPY package.json ./
133
+ COPY --from=deps /usr/src/app/node_modules ./node_modules
88
134
 
89
135
  # Copy the compiled application code from the build stage
90
136
  COPY --from=build /usr/src/app/dist ./dist
@@ -128,7 +174,6 @@ ENV MCP_TRANSPORT_TYPE="http"
128
174
  ENV MCP_SESSION_MODE="stateless"
129
175
  ENV MCP_LOG_LEVEL="info"
130
176
  ENV LOGS_DIR="/var/log/congressgov-mcp-server"
131
- ENV MCP_FORCE_CONSOLE_LOGGING="true"
132
177
 
133
178
  # Expose the port the server listens on
134
179
  EXPOSE ${MCP_HTTP_PORT}