@cyanheads/openfec-mcp-server 0.4.0 → 0.4.2

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.
Files changed (58) hide show
  1. package/CLAUDE.md +73 -34
  2. package/README.md +2 -2
  3. package/dist/mcp-server/resources/definitions/candidate.resource.d.ts +7 -1
  4. package/dist/mcp-server/resources/definitions/candidate.resource.d.ts.map +1 -1
  5. package/dist/mcp-server/resources/definitions/candidate.resource.js +15 -2
  6. package/dist/mcp-server/resources/definitions/candidate.resource.js.map +1 -1
  7. package/dist/mcp-server/resources/definitions/committee.resource.d.ts +7 -1
  8. package/dist/mcp-server/resources/definitions/committee.resource.d.ts.map +1 -1
  9. package/dist/mcp-server/resources/definitions/committee.resource.js +17 -2
  10. package/dist/mcp-server/resources/definitions/committee.resource.js.map +1 -1
  11. package/dist/mcp-server/resources/definitions/election.resource.d.ts +3 -3
  12. package/dist/mcp-server/resources/definitions/election.resource.d.ts.map +1 -1
  13. package/dist/mcp-server/resources/definitions/index.d.ts +13 -3
  14. package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/index.d.ts +59 -9
  16. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  17. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts +1 -1
  18. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts.map +1 -1
  19. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js +3 -1
  20. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js.map +1 -1
  21. package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts +22 -1
  22. package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts.map +1 -1
  23. package/dist/mcp-server/tools/definitions/lookup-elections.tool.js +46 -6
  24. package/dist/mcp-server/tools/definitions/lookup-elections.tool.js.map +1 -1
  25. package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts +7 -1
  26. package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/search-candidates.tool.js +15 -4
  28. package/dist/mcp-server/tools/definitions/search-candidates.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts +7 -1
  30. package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/search-committees.tool.js +14 -3
  32. package/dist/mcp-server/tools/definitions/search-committees.tool.js.map +1 -1
  33. package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts +12 -1
  34. package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts.map +1 -1
  35. package/dist/mcp-server/tools/definitions/search-contributions.tool.js +20 -4
  36. package/dist/mcp-server/tools/definitions/search-contributions.tool.js.map +1 -1
  37. package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts +1 -1
  38. package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts.map +1 -1
  39. package/dist/mcp-server/tools/definitions/search-disbursements.tool.js +1 -1
  40. package/dist/mcp-server/tools/definitions/search-disbursements.tool.js.map +1 -1
  41. package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts +7 -1
  42. package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts.map +1 -1
  43. package/dist/mcp-server/tools/definitions/search-expenditures.tool.js +16 -3
  44. package/dist/mcp-server/tools/definitions/search-expenditures.tool.js.map +1 -1
  45. package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts +1 -1
  46. package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts.map +1 -1
  47. package/dist/mcp-server/tools/definitions/search-filings.tool.js +3 -1
  48. package/dist/mcp-server/tools/definitions/search-filings.tool.js.map +1 -1
  49. package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts +7 -1
  50. package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts.map +1 -1
  51. package/dist/mcp-server/tools/definitions/search-legal.tool.js +13 -3
  52. package/dist/mcp-server/tools/definitions/search-legal.tool.js.map +1 -1
  53. package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts +2 -2
  54. package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts.map +1 -1
  55. package/dist/mcp-server/tools/definitions/utils/id-validators.js +5 -5
  56. package/dist/mcp-server/tools/definitions/utils/id-validators.js.map +1 -1
  57. package/package.json +9 -10
  58. package/server.json +3 -3
package/CLAUDE.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** openfec-mcp-server
4
- **Version:** 0.3.1
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
4
+ **Version:** 0.4.2
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.8.19`
6
+ **Engines:** Bun ≥1.3.0, Node ≥24.0.0
6
7
 
7
8
  > **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.
8
9
 
@@ -14,13 +15,14 @@ When the user asks what to do next, what's left, or needs direction, suggest rel
14
15
 
15
16
  1. **Re-run the `setup` skill** — ensures CLAUDE.md, skills, structure, and metadata are populated and up to date with the current codebase
16
17
  2. **Run the `design-mcp-server` skill** — if the tool/resource surface hasn't been mapped yet, work through domain design
17
- 3. **Add tools/resources/prompts** — scaffold new definitions using the `add-tool`, `add-resource`, `add-prompt` skills
18
+ 3. **Add tools/resources/prompts** — scaffold new definitions using the `add-tool`, `add-app-tool`, `add-resource`, `add-prompt` skills
18
19
  4. **Add services** — scaffold domain service integrations using the `add-service` skill
19
20
  5. **Add tests** — scaffold tests for existing definitions using the `add-test` skill
20
21
  6. **Field-test definitions** — exercise tools/resources/prompts with real inputs using the `field-test` skill, get a report of issues and pain points
21
22
  7. **Run `devcheck`** — lint, format, typecheck, and security audit
22
- 8. **Run the `polish-docs-meta` skill** — finalize README, CHANGELOG, metadata, and agent protocol for shipping
23
- 9. **Run the `maintenance` skill** — sync skills and dependencies after framework updates
23
+ 8. **Run the `security-pass` skill** — audit handlers for MCP-specific security gaps: output injection, scope blast radius, input sinks, tenant isolation
24
+ 9. **Run the `polish-docs-meta` skill** — finalize README, CHANGELOG, metadata, and agent protocol for shipping
25
+ 10. **Run the `maintenance` skill** — investigate changelogs, adopt upstream changes, and sync skills after `bun update --latest`
24
26
 
25
27
  Tailor suggestions to what's actually missing or stale — don't recite the full list every time.
26
28
 
@@ -34,9 +36,10 @@ The OpenFEC OpenAPI spec (Swagger 2.0) is at `docs/openapi-spec.json` — 100 pa
34
36
 
35
37
  ## Core Rules
36
38
 
37
- - **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.
39
+ - **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Prefer typed contracts (`errors[]` + `ctx.fail(reason)`) when the failure is part of the public surface; fall back to error factories (`notFound()`, `validationError()`) or plain `Error` otherwise. The framework catches, classifies, and formats.
38
40
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
39
41
  - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
42
+ - **Check `ctx.elicit` / `ctx.sample`** for presence before calling.
40
43
  - **Secrets in env vars only** — never hardcoded.
41
44
 
42
45
  ---
@@ -47,12 +50,24 @@ The OpenFEC OpenAPI spec (Swagger 2.0) is at `docs/openapi-spec.json` — 100 pa
47
50
 
48
51
  ```ts
49
52
  import { tool, z } from '@cyanheads/mcp-ts-core';
50
- import { invalidParams } from '@cyanheads/mcp-ts-core/errors';
53
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
51
54
  import { getOpenFecService } from '@/services/openfec/openfec-service.js';
55
+ import { validateCandidateId } from './utils/id-validators.js';
52
56
 
53
57
  export const searchCandidates = tool('openfec_search_candidates', {
54
58
  description: 'Find federal candidates by name, state, office, party, or cycle.',
55
59
  annotations: { readOnlyHint: true, idempotentHint: true },
60
+
61
+ errors: [
62
+ {
63
+ reason: 'candidate_not_found',
64
+ code: JsonRpcErrorCode.NotFound,
65
+ when: 'Single-candidate lookup by candidate_id returned no record',
66
+ recovery:
67
+ 'Verify the candidate_id format (H/S/P + digits) or drop it and search by name, state, or cycle.',
68
+ },
69
+ ],
70
+
56
71
  input: z.object({
57
72
  query: z.string().optional().describe('Full-text candidate name search.'),
58
73
  candidate_id: z.string().optional().describe('FEC candidate ID (e.g., P00003392).'),
@@ -66,10 +81,14 @@ export const searchCandidates = tool('openfec_search_candidates', {
66
81
  }),
67
82
  async handler(input, ctx) {
68
83
  const fec = getOpenFecService();
69
- if (input.candidate_id && !/^[HSP]\d+$/i.test(input.candidate_id)) {
70
- throw invalidParams('Invalid candidate ID format', { candidate_id: input.candidate_id });
71
- }
84
+ if (input.candidate_id) validateCandidateId(input.candidate_id);
72
85
  const result = await fec.searchCandidates(input, ctx);
86
+ if (input.candidate_id && result.candidates.length === 0) {
87
+ throw ctx.fail('candidate_not_found', `Candidate ${input.candidate_id} not found.`, {
88
+ candidate_id: input.candidate_id,
89
+ ...ctx.recoveryFor('candidate_not_found'),
90
+ });
91
+ }
73
92
  ctx.log.info('Candidate search completed', { count: result.pagination.count });
74
93
  return result;
75
94
  },
@@ -167,24 +186,38 @@ Handlers receive a unified `ctx` object. Key properties:
167
186
 
168
187
  ## Errors
169
188
 
170
- Handlers throw — the framework catches, classifies, and formats. Three escalation levels:
189
+ Handlers throw — the framework catches, classifies, and formats.
190
+
191
+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()`. The handler then receives `ctx.fail(reason, msg?, data?)` keyed against the contract reason union — `ctx.fail('typo')` is a TypeScript error. The framework auto-populates `data.reason`, the linter enforces conformance, and the `recovery` string (≥5 words) is the source of truth for the recovery hint that flows to the wire. Spread `ctx.recoveryFor('reason')` into `data` to opt the contract recovery onto the wire payload.
171
192
 
172
193
  ```ts
173
- // 1. Plain Error — framework auto-classifies from message patterns
174
- throw new Error('Item not found'); // NotFound
175
- throw new Error('Invalid query format'); // ValidationError
194
+ errors: [
195
+ { reason: 'candidate_not_found', code: JsonRpcErrorCode.NotFound,
196
+ when: 'Single-candidate lookup returned no record',
197
+ recovery: 'Verify the candidate_id (H/S/P + digits) or drop it and search by name.' },
198
+ ],
199
+ async handler(input, ctx) {
200
+ if (!found) {
201
+ throw ctx.fail('candidate_not_found', `Candidate ${id} not found.`, {
202
+ candidate_id: id,
203
+ ...ctx.recoveryFor('candidate_not_found'),
204
+ });
205
+ }
206
+ }
207
+ ```
176
208
 
177
- // 2. Error factoriesexplicit code, concise
178
- import { notFound, validationError, forbidden, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
179
- throw notFound('Item not found', { itemId });
180
- throw serviceUnavailable('API unavailable', { url }, { cause: err });
209
+ **Declare contracts inline on each tool**, even when reasons look similar across tools per-tool repetition is the intended cost of locality.
181
210
 
182
- // 3. McpError full control over code and data
183
- import { McpError, JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
184
- throw new McpError(JsonRpcErrorCode.DatabaseError, 'Connection failed', { pool: 'primary' });
211
+ **Fallback (no contract entry fits):** error factories or plain `Error`. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely without contract entries.
212
+
213
+ ```ts
214
+ import { notFound, validationError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
215
+ throw validationError('Invalid query format', { field: 'query' }); // baseline — fine
216
+ throw serviceUnavailable('API unavailable', { url }, { cause: err });
217
+ throw new Error('Item not found'); // auto-classifies → NotFound
185
218
  ```
186
219
 
187
- Plain `Error` is fine for most cases. Use factories when the error code matters. See framework CLAUDE.md for the full auto-classification table and all available factories.
220
+ Note: `invalidParams` is for malformed JSON-RPC params (rare post-Zod). For semantic post-shape validation, use `validationError`. See the framework `api-errors` skill for the full reference.
188
221
 
189
222
  ---
190
223
 
@@ -236,7 +269,7 @@ src/
236
269
 
237
270
  Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool.
238
271
 
239
- **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 `skills/` paths manually. After framework updates, re-copy to pick up changes.
272
+ **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 `skills/` paths manually. After framework updates, run the `maintenance` skill — it re-syncs the agent directory automatically (Phase B).
240
273
 
241
274
  Available skills:
242
275
 
@@ -251,18 +284,25 @@ Available skills:
251
284
  | `add-service` | Scaffold a new service integration |
252
285
  | `add-test` | Scaffold test file for a tool, resource, or service |
253
286
  | `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
287
+ | `tool-defs-analysis` | Read-only audit of definition language across the surface (10 categories: voice, leaks, defaults, recovery, structure, …) |
288
+ | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
254
289
  | `devcheck` | Lint, format, typecheck, audit |
255
290
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
256
- | `maintenance` | Sync skills and dependencies after updates |
291
+ | `release-and-publish` | Post-wrapup ship workflow: verification gate, push commits/tags, publish to npm + MCP Registry + GHCR |
292
+ | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills and framework scripts |
293
+ | `migrate-mcp-ts-template` | Migrate a legacy template fork to use `@cyanheads/mcp-ts-core` as a package dependency |
257
294
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
258
295
  | `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
259
296
  | `api-auth` | Auth modes, scopes, JWT/OAuth |
297
+ | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
260
298
  | `api-config` | AppConfig, parseConfig, env vars |
261
- | `api-context` | Context interface, logger, state, progress |
262
- | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
299
+ | `api-context` | Context interface, logger, state, progress, sessionId, recoveryFor |
300
+ | `api-errors` | McpError, JsonRpcErrorCode, typed error contracts, factories |
301
+ | `api-linter` | MCP definition lint rules reference — look here when devcheck flags `format-parity`, `schema-*`, `name-*`, `server-json-*`, etc. |
263
302
  | `api-services` | LLM, Speech, Graph services |
303
+ | `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
264
304
  | `api-testing` | createMockContext, test patterns |
265
- | `api-utils` | Formatting, parsing, security, pagination, scheduling |
305
+ | `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
266
306
  | `api-workers` | Cloudflare Workers runtime |
267
307
 
268
308
  When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
@@ -281,16 +321,17 @@ When you complete a skill's checklist, check the boxes and add a completion time
281
321
  | `bun run format` | Auto-fix formatting |
282
322
  | `bun run lint:mcp` | Validate MCP tool/resource/prompt definitions |
283
323
  | `bun run test` | Run tests |
284
- | `bun run dev:stdio` | Dev mode (stdio) |
285
- | `bun run dev:http` | Dev mode (HTTP) |
324
+ | `bun run start` | Production mode (transport from `MCP_TRANSPORT_TYPE`) |
286
325
  | `bun run start:stdio` | Production mode (stdio) |
287
326
  | `bun run start:http` | Production mode (HTTP) |
288
327
 
328
+ For dev / smoke-testing, use `bun run rebuild && bun run start:stdio` (or `start:http`) — production-shape execution against the built `dist/`.
329
+
289
330
  ---
290
331
 
291
332
  ## Publishing
292
333
 
293
- After a version bump and final commit, publish to both npm and GHCR:
334
+ Run the `release-and-publish` skill for the full flow (verification gate, push, npm + MCP Registry + GHCR). Reference commands:
294
335
 
295
336
  ```bash
296
337
  bun publish --access public
@@ -301,8 +342,6 @@ docker buildx build --platform linux/amd64,linux/arm64 \
301
342
  --push .
302
343
  ```
303
344
 
304
- Remind the user to run these after completing a release flow.
305
-
306
345
  ---
307
346
 
308
347
  ## Imports
@@ -320,12 +359,12 @@ import { getOpenFecService } from '@/services/openfec/openfec-service.js';
320
359
 
321
360
  ## Checklist
322
361
 
323
- - [ ] Zod schemas: all fields have `.describe()`, only JSON-Schema-serializable types (no `z.custom()`, `z.date()`, `z.transform()`, etc.)
362
+ - [ ] Zod schemas: all fields have `.describe()` (including nested object fields and array element types), only JSON-Schema-serializable types (no `z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`)
324
363
  - [ ] Optional nested objects: handler guards for empty inner values from form-based clients (`if (input.obj?.field && ...)`, not just `if (input.obj)`)
325
364
  - [ ] JSDoc `@fileoverview` + `@module` on every file
326
365
  - [ ] `ctx.log` for logging, `ctx.state` for storage
327
366
  - [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
328
- - [ ] `format()` renders all data the LLM needs — `content[]` is the only field most clients forward to the model
367
+ - [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
329
368
  - [ ] Registered in `createApp()` arrays (directly or via barrel exports)
330
369
  - [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
331
370
  - [ ] `bun run devcheck` passes
package/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  <div align="center">
8
8
 
9
- [![npm](https://img.shields.io/npm/v/@cyanheads/openfec-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/openfec-mcp-server) [![Version](https://img.shields.io/badge/Version-0.4.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-259?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/)
9
+ [![npm](https://img.shields.io/npm/v/@cyanheads/openfec-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/openfec-mcp-server) [![Version](https://img.shields.io/badge/Version-0.4.2-blue.svg?style=flat-square)](./CHANGELOG.md) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-259?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/)
10
10
 
11
- [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-^1.2.0-f9f1e1.svg?style=flat-square)](https://bun.sh/)
11
+ [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-^1.3.0-f9f1e1.svg?style=flat-square)](https://bun.sh/)
12
12
 
13
13
  </div>
14
14
 
@@ -4,7 +4,13 @@
4
4
  * @module src/mcp-server/resources/definitions/candidate.resource
5
5
  */
6
6
  import { z } from '@cyanheads/mcp-ts-core';
7
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
8
  export declare const candidateResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
8
9
  candidate_id: z.ZodString;
9
- }, z.core.$strip>, undefined>;
10
+ }, z.core.$strip>, undefined, readonly [{
11
+ readonly reason: "candidate_not_found";
12
+ readonly code: JsonRpcErrorCode.NotFound;
13
+ readonly when: "No candidate exists for the supplied candidate_id";
14
+ readonly recovery: "Verify the candidate_id format (H/S/P + digits) or look up the candidate by name via openfec_search_candidates.";
15
+ }]>;
10
16
  //# sourceMappingURL=candidate.resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"candidate.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/candidate.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIrD,eAAO,MAAM,iBAAiB;;6BAuB5B,CAAC"}
1
+ {"version":3,"file":"candidate.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/candidate.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAIjE,eAAO,MAAM,iBAAiB;;;;;;;GAsC5B,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * @module src/mcp-server/resources/definitions/candidate.resource
5
5
  */
6
6
  import { resource, z } from '@cyanheads/mcp-ts-core';
7
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
8
  import { validateCandidateId } from '../../../mcp-server/tools/definitions/utils/id-validators.js';
8
9
  import { getOpenFecService } from '../../../services/openfec/openfec-service.js';
9
10
  export const candidateResource = resource('openfec://candidate/{candidate_id}', {
@@ -13,13 +14,25 @@ export const candidateResource = resource('openfec://candidate/{candidate_id}',
13
14
  params: z.object({
14
15
  candidate_id: z.string().describe('FEC candidate ID (e.g., P00003392, H2CO07170, S4AZ00345)'),
15
16
  }),
17
+ errors: [
18
+ {
19
+ reason: 'candidate_not_found',
20
+ code: JsonRpcErrorCode.NotFound,
21
+ when: 'No candidate exists for the supplied candidate_id',
22
+ recovery: 'Verify the candidate_id format (H/S/P + digits) or look up the candidate by name via openfec_search_candidates.',
23
+ },
24
+ ],
16
25
  async handler(params, ctx) {
17
26
  validateCandidateId(params.candidate_id);
18
27
  const fec = getOpenFecService();
19
28
  const candidateResult = await fec.getCandidate(params.candidate_id, ctx);
20
29
  const candidate = candidateResult.results[0];
21
- if (!candidate)
22
- throw new Error(`Candidate ${params.candidate_id} not found`);
30
+ if (!candidate) {
31
+ throw ctx.fail('candidate_not_found', `Candidate ${params.candidate_id} not found.`, {
32
+ candidate_id: params.candidate_id,
33
+ ...ctx.recoveryFor('candidate_not_found'),
34
+ });
35
+ }
23
36
  const totalsResult = await fec.getCandidateTotals({ candidate_id: params.candidate_id }, ctx);
24
37
  const totals = totalsResult.results[0];
25
38
  ctx.log.info('Candidate resource fetched', { candidate_id: params.candidate_id });
@@ -1 +1 @@
1
- {"version":3,"file":"candidate.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/candidate.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAE1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,oCAAoC,EAAE;IAC9E,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,uJAAuJ;IACzJ,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0DAA0D,CAAC;KAC9F,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,mBAAmB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAEzC,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;QAChC,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,MAAM,CAAC,YAAY,YAAY,CAAC,CAAC;QAE9E,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,EAAE,GAAG,CAAC,CAAC;QAC9F,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEvC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAClF,OAAO,EAAE,GAAG,SAAS,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;IAC7C,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"candidate.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/candidate.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAE1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,oCAAoC,EAAE;IAC9E,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,uJAAuJ;IACzJ,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0DAA0D,CAAC;KAC9F,CAAC;IAEF,MAAM,EAAE;QACN;YACE,MAAM,EAAE,qBAAqB;YAC7B,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,mDAAmD;YACzD,QAAQ,EACN,iHAAiH;SACpH;KACF;IAED,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,mBAAmB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAEzC,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;QAChC,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,aAAa,MAAM,CAAC,YAAY,aAAa,EAAE;gBACnF,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,GAAG,GAAG,CAAC,WAAW,CAAC,qBAAqB,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,EAAE,GAAG,CAAC,CAAC;QAC9F,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEvC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAClF,OAAO,EAAE,GAAG,SAAS,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;IAC7C,CAAC;CACF,CAAC,CAAC"}
@@ -4,7 +4,13 @@
4
4
  * @module src/mcp-server/resources/definitions/committee.resource
5
5
  */
6
6
  import { z } from '@cyanheads/mcp-ts-core';
7
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
8
  export declare const committeeResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
8
9
  committee_id: z.ZodString;
9
- }, z.core.$strip>, undefined>;
10
+ }, z.core.$strip>, undefined, readonly [{
11
+ readonly reason: "committee_not_found";
12
+ readonly code: JsonRpcErrorCode.NotFound;
13
+ readonly when: "No committee exists for the supplied committee_id";
14
+ readonly recovery: "Verify the committee_id format (C + digits) or look up the committee by name via openfec_search_committees.";
15
+ }]>;
10
16
  //# sourceMappingURL=committee.resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"committee.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/committee.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGrD,eAAO,MAAM,iBAAiB;;6BAkB5B,CAAC"}
1
+ {"version":3,"file":"committee.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/committee.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAIjE,eAAO,MAAM,iBAAiB;;;;;;;GAmC5B,CAAC"}
@@ -4,6 +4,8 @@
4
4
  * @module src/mcp-server/resources/definitions/committee.resource
5
5
  */
6
6
  import { resource, z } from '@cyanheads/mcp-ts-core';
7
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
8
+ import { validateCommitteeId } from '../../../mcp-server/tools/definitions/utils/id-validators.js';
7
9
  import { getOpenFecService } from '../../../services/openfec/openfec-service.js';
8
10
  export const committeeResource = resource('openfec://committee/{committee_id}', {
9
11
  name: 'FEC Committee Profile',
@@ -12,12 +14,25 @@ export const committeeResource = resource('openfec://committee/{committee_id}',
12
14
  params: z.object({
13
15
  committee_id: z.string().describe('FEC committee ID (e.g., C00358796)'),
14
16
  }),
17
+ errors: [
18
+ {
19
+ reason: 'committee_not_found',
20
+ code: JsonRpcErrorCode.NotFound,
21
+ when: 'No committee exists for the supplied committee_id',
22
+ recovery: 'Verify the committee_id format (C + digits) or look up the committee by name via openfec_search_committees.',
23
+ },
24
+ ],
15
25
  async handler(params, ctx) {
26
+ validateCommitteeId(params.committee_id);
16
27
  const fec = getOpenFecService();
17
28
  const result = await fec.getCommittee(params.committee_id, ctx);
18
29
  const committee = result.results[0];
19
- if (!committee)
20
- throw new Error(`Committee ${params.committee_id} not found`);
30
+ if (!committee) {
31
+ throw ctx.fail('committee_not_found', `Committee ${params.committee_id} not found.`, {
32
+ committee_id: params.committee_id,
33
+ ...ctx.recoveryFor('committee_not_found'),
34
+ });
35
+ }
21
36
  ctx.log.info('Committee resource fetched', { committee_id: params.committee_id });
22
37
  return committee;
23
38
  },
@@ -1 +1 @@
1
- {"version":3,"file":"committee.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/committee.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAE1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,oCAAoC,EAAE;IAC9E,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,qJAAqJ;IACvJ,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;KACxE,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,MAAM,CAAC,YAAY,YAAY,CAAC,CAAC;QAE9E,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAClF,OAAO,SAAS,CAAC;IACnB,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"committee.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/committee.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAE1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,oCAAoC,EAAE;IAC9E,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,qJAAqJ;IACvJ,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;KACxE,CAAC;IAEF,MAAM,EAAE;QACN;YACE,MAAM,EAAE,qBAAqB;YAC7B,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,mDAAmD;YACzD,QAAQ,EACN,6GAA6G;SAChH;KACF;IAED,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,mBAAmB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAEzC,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,aAAa,MAAM,CAAC,YAAY,aAAa,EAAE;gBACnF,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,GAAG,GAAG,CAAC,WAAW,CAAC,qBAAqB,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAClF,OAAO,SAAS,CAAC;IACnB,CAAC;CACF,CAAC,CAAC"}
@@ -8,18 +8,18 @@ import { z } from '@cyanheads/mcp-ts-core';
8
8
  export declare const electionResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
9
9
  cycle: z.ZodString;
10
10
  office: z.ZodString;
11
- }, z.core.$strip>, undefined>;
11
+ }, z.core.$strip>, undefined, undefined>;
12
12
  /** Senate races: openfec://election/2024/senate/AZ */
13
13
  export declare const electionStateResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
14
14
  cycle: z.ZodString;
15
15
  office: z.ZodString;
16
16
  state: z.ZodString;
17
- }, z.core.$strip>, undefined>;
17
+ }, z.core.$strip>, undefined, undefined>;
18
18
  /** House races: openfec://election/2024/house/CA/12 */
19
19
  export declare const electionDistrictResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
20
20
  cycle: z.ZodString;
21
21
  office: z.ZodString;
22
22
  state: z.ZodString;
23
23
  district: z.ZodString;
24
- }, z.core.$strip>, undefined>;
24
+ }, z.core.$strip>, undefined, undefined>;
25
25
  //# sourceMappingURL=election.resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"election.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/election.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAmCrD,yEAAyE;AACzE,eAAO,MAAM,gBAAgB;;;6BAU3B,CAAC;AAEH,sDAAsD;AACtD,eAAO,MAAM,qBAAqB;;;;6BAUhC,CAAC;AAEH,uDAAuD;AACvD,eAAO,MAAM,wBAAwB;;;;;6BAcpC,CAAC"}
1
+ {"version":3,"file":"election.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/election.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAmCrD,yEAAyE;AACzE,eAAO,MAAM,gBAAgB;;;wCAU3B,CAAC;AAEH,sDAAsD;AACtD,eAAO,MAAM,qBAAqB;;;;wCAUhC,CAAC;AAEH,uDAAuD;AACvD,eAAO,MAAM,wBAAwB;;;;;wCAcpC,CAAC"}
@@ -7,10 +7,20 @@ export { committeeResource } from './committee.resource.js';
7
7
  export { electionDistrictResource, electionResource, electionStateResource, } from './election.resource.js';
8
8
  export declare const allResourceDefinitions: (import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
9
9
  candidate_id: import("zod").ZodString;
10
- }, import("zod/v4/core").$strip>, undefined> | import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
10
+ }, import("zod/v4/core").$strip>, undefined, readonly [{
11
+ readonly reason: "candidate_not_found";
12
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
13
+ readonly when: "No candidate exists for the supplied candidate_id";
14
+ readonly recovery: "Verify the candidate_id format (H/S/P + digits) or look up the candidate by name via openfec_search_candidates.";
15
+ }]> | import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
11
16
  committee_id: import("zod").ZodString;
12
- }, import("zod/v4/core").$strip>, undefined> | import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
17
+ }, import("zod/v4/core").$strip>, undefined, readonly [{
18
+ readonly reason: "committee_not_found";
19
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
20
+ readonly when: "No committee exists for the supplied committee_id";
21
+ readonly recovery: "Verify the committee_id format (C + digits) or look up the committee by name via openfec_search_committees.";
22
+ }]> | import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
13
23
  cycle: import("zod").ZodString;
14
24
  office: import("zod").ZodString;
15
- }, import("zod/v4/core").$strip>, undefined>)[];
25
+ }, import("zod/v4/core").$strip>, undefined, undefined>)[];
16
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAUhC,eAAO,MAAM,sBAAsB;;;;;;;+CAMlC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAUhC,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;0DAMlC,CAAC"}
@@ -41,7 +41,7 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
41
41
  per_page: import("zod").ZodNumber;
42
42
  }, import("zod/v4/core").$strip>;
43
43
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
44
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
44
+ }, import("zod/v4/core").$strip>, undefined> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
45
45
  mode: import("zod").ZodDefault<import("zod").ZodEnum<{
46
46
  summary: "summary";
47
47
  search: "search";
@@ -65,7 +65,27 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
65
65
  per_page: import("zod").ZodNumber;
66
66
  }, import("zod/v4/core").$strip>;
67
67
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
68
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
68
+ }, import("zod/v4/core").$strip>, readonly [{
69
+ readonly reason: "cycle_must_be_even";
70
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
71
+ readonly when: "Cycle is an odd year";
72
+ readonly recovery: "Federal election cycles are two-year periods ending in even years (e.g., 2024, 2026).";
73
+ }, {
74
+ readonly reason: "missing_state_for_office";
75
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
76
+ readonly when: "Senate or House office without a state and without a zip";
77
+ readonly recovery: "Provide a two-letter state code (e.g., AZ) or a zip code to scope the senate or house race.";
78
+ }, {
79
+ readonly reason: "missing_district_for_house";
80
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
81
+ readonly when: "House office without a district number and without a zip";
82
+ readonly recovery: "Provide a two-digit district number (e.g., \"07\") or a zip code to identify the House race.";
83
+ }, {
84
+ readonly reason: "summary_does_not_support_zip";
85
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
86
+ readonly when: "Summary mode invoked with a zip parameter";
87
+ readonly recovery: "Use mode \"search\" for ZIP-based lookups, or remove zip and use state and district for summary mode.";
88
+ }]> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
69
89
  query: import("zod").ZodOptional<import("zod").ZodString>;
70
90
  candidate_id: import("zod").ZodOptional<import("zod").ZodString>;
71
91
  state: import("zod").ZodOptional<import("zod").ZodString>;
@@ -103,7 +123,12 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
103
123
  per_page: import("zod").ZodNumber;
104
124
  }, import("zod/v4/core").$strip>;
105
125
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
106
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
126
+ }, import("zod/v4/core").$strip>, readonly [{
127
+ readonly reason: "candidate_not_found";
128
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
129
+ readonly when: "Single-candidate lookup by candidate_id returned no record";
130
+ readonly recovery: "Verify the candidate_id format (H/S/P + digits) or drop it and search by name, state, or cycle.";
131
+ }]> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
107
132
  query: import("zod").ZodOptional<import("zod").ZodString>;
108
133
  committee_id: import("zod").ZodOptional<import("zod").ZodString>;
109
134
  candidate_id: import("zod").ZodOptional<import("zod").ZodString>;
@@ -124,7 +149,12 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
124
149
  per_page: import("zod").ZodNumber;
125
150
  }, import("zod/v4/core").$strip>;
126
151
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
127
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
152
+ }, import("zod/v4/core").$strip>, readonly [{
153
+ readonly reason: "committee_not_found";
154
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
155
+ readonly when: "Single-committee lookup by committee_id returned no record";
156
+ readonly recovery: "Verify the committee_id format (C + digits) or drop it and search by name, candidate_id, or type.";
157
+ }]> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
128
158
  mode: import("zod").ZodDefault<import("zod").ZodEnum<{
129
159
  by_size: "by_size";
130
160
  by_state: "by_state";
@@ -163,7 +193,17 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
163
193
  per_page: import("zod").ZodNumber;
164
194
  }, import("zod/v4/core").$strip>>;
165
195
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
166
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
196
+ }, import("zod/v4/core").$strip>, readonly [{
197
+ readonly reason: "itemized_requires_committee_id";
198
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
199
+ readonly when: "Itemized mode invoked without a committee_id";
200
+ readonly recovery: "Provide a committee_id, switch to a by_size or by_state aggregate mode with candidate_id, or look up the candidate's committee with openfec_search_committees.";
201
+ }, {
202
+ readonly reason: "aggregate_requires_committee_id";
203
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
204
+ readonly when: "by_employer or by_occupation aggregate without a committee_id";
205
+ readonly recovery: "These aggregates roll up to a single committee — provide a committee_id for the spending committee.";
206
+ }]> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
167
207
  mode: import("zod").ZodDefault<import("zod").ZodEnum<{
168
208
  by_purpose: "by_purpose";
169
209
  by_recipient: "by_recipient";
@@ -199,7 +239,7 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
199
239
  per_page: import("zod").ZodNumber;
200
240
  }, import("zod/v4/core").$strip>>;
201
241
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
202
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
242
+ }, import("zod/v4/core").$strip>, undefined> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
203
243
  mode: import("zod").ZodDefault<import("zod").ZodEnum<{
204
244
  itemized: "itemized";
205
245
  by_candidate: "by_candidate";
@@ -243,7 +283,12 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
243
283
  per_page: import("zod").ZodNumber;
244
284
  }, import("zod/v4/core").$strip>>;
245
285
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
246
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
286
+ }, import("zod/v4/core").$strip>, readonly [{
287
+ readonly reason: "by_candidate_requires_candidate_id";
288
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
289
+ readonly when: "by_candidate mode invoked without a candidate_id";
290
+ readonly recovery: "Find the candidate ID via openfec_search_candidates, then pass it here to see independent expenditures supporting or opposing that candidate.";
291
+ }]> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
247
292
  committee_id: import("zod").ZodOptional<import("zod").ZodString>;
248
293
  candidate_id: import("zod").ZodOptional<import("zod").ZodString>;
249
294
  filer_name: import("zod").ZodOptional<import("zod").ZodString>;
@@ -266,7 +311,7 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
266
311
  per_page: import("zod").ZodNumber;
267
312
  }, import("zod/v4/core").$strip>;
268
313
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
269
- }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
314
+ }, import("zod/v4/core").$strip>, undefined> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
270
315
  query: import("zod").ZodOptional<import("zod").ZodString>;
271
316
  type: import("zod").ZodOptional<import("zod").ZodEnum<{
272
317
  advisory_opinions: "advisory_opinions";
@@ -290,5 +335,10 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
290
335
  results: import("zod").ZodArray<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
291
336
  total_count: import("zod").ZodNumber;
292
337
  search_criteria: import("zod").ZodOptional<import("zod").ZodObject<{}, import("zod/v4/core").$loose>>;
293
- }, import("zod/v4/core").$strip>>)[];
338
+ }, import("zod/v4/core").$strip>, readonly [{
339
+ readonly reason: "missing_filter";
340
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
341
+ readonly when: "Called without query, type, or a specific identifier (ao_number / case_number)";
342
+ readonly recovery: "Provide at least a search query, document type, or specific identifier (ao_number, case_number) to scope the legal search.";
343
+ }]>)[];
294
344
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAYrD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAU9B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAYrD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAU9B,CAAC"}
@@ -34,5 +34,5 @@ export declare const lookupCalendar: import("@cyanheads/mcp-ts-core").ToolDefini
34
34
  per_page: z.ZodNumber;
35
35
  }, z.core.$strip>;
36
36
  search_criteria: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
37
- }, z.core.$strip>>;
37
+ }, z.core.$strip>, undefined>;
38
38
  //# sourceMappingURL=lookup-calendar.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lookup-calendar.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/lookup-calendar.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAUjD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiJzB,CAAC"}
1
+ {"version":3,"file":"lookup-calendar.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/lookup-calendar.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAUjD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAuJzB,CAAC"}
@@ -46,7 +46,9 @@ export const lookupCalendar = tool('openfec_lookup_calendar', {
46
46
  }),
47
47
  output: z.object({
48
48
  results: z
49
- .array(z.looseObject({}))
49
+ .array(z
50
+ .looseObject({})
51
+ .describe('A calendar record (event, filing deadline, or election date depending on mode).'))
50
52
  .describe('Calendar records — events, filing deadlines, or election dates depending on mode.'),
51
53
  pagination: z
52
54
  .object({