@cyanheads/openfec-mcp-server 0.4.1 → 0.4.3

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 (73) hide show
  1. package/CLAUDE.md +59 -24
  2. package/README.md +3 -3
  3. package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.d.ts.map +1 -1
  4. package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.js +17 -9
  5. package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.js.map +1 -1
  6. package/dist/mcp-server/prompts/definitions/money-trail.prompt.d.ts.map +1 -1
  7. package/dist/mcp-server/prompts/definitions/money-trail.prompt.js +10 -6
  8. package/dist/mcp-server/prompts/definitions/money-trail.prompt.js.map +1 -1
  9. package/dist/mcp-server/resources/definitions/candidate.resource.d.ts +7 -1
  10. package/dist/mcp-server/resources/definitions/candidate.resource.d.ts.map +1 -1
  11. package/dist/mcp-server/resources/definitions/candidate.resource.js +22 -5
  12. package/dist/mcp-server/resources/definitions/candidate.resource.js.map +1 -1
  13. package/dist/mcp-server/resources/definitions/committee.resource.d.ts +7 -1
  14. package/dist/mcp-server/resources/definitions/committee.resource.d.ts.map +1 -1
  15. package/dist/mcp-server/resources/definitions/committee.resource.js +27 -5
  16. package/dist/mcp-server/resources/definitions/committee.resource.js.map +1 -1
  17. package/dist/mcp-server/resources/definitions/election.resource.d.ts +16 -9
  18. package/dist/mcp-server/resources/definitions/election.resource.d.ts.map +1 -1
  19. package/dist/mcp-server/resources/definitions/election.resource.js +10 -9
  20. package/dist/mcp-server/resources/definitions/election.resource.js.map +1 -1
  21. package/dist/mcp-server/resources/definitions/index.d.ts +30 -4
  22. package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -1
  23. package/dist/mcp-server/tools/definitions/index.d.ts +93 -24
  24. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  25. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts +22 -3
  26. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js +26 -14
  28. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts +25 -4
  30. package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/lookup-elections.tool.js +52 -22
  32. package/dist/mcp-server/tools/definitions/lookup-elections.tool.js.map +1 -1
  33. package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts +10 -4
  34. package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts.map +1 -1
  35. package/dist/mcp-server/tools/definitions/search-candidates.tool.js +17 -8
  36. package/dist/mcp-server/tools/definitions/search-candidates.tool.js.map +1 -1
  37. package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts +9 -3
  38. package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts.map +1 -1
  39. package/dist/mcp-server/tools/definitions/search-committees.tool.js +16 -7
  40. package/dist/mcp-server/tools/definitions/search-committees.tool.js.map +1 -1
  41. package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts +12 -1
  42. package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts.map +1 -1
  43. package/dist/mcp-server/tools/definitions/search-contributions.tool.js +26 -16
  44. package/dist/mcp-server/tools/definitions/search-contributions.tool.js.map +1 -1
  45. package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts +1 -1
  46. package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts.map +1 -1
  47. package/dist/mcp-server/tools/definitions/search-disbursements.tool.js +8 -14
  48. package/dist/mcp-server/tools/definitions/search-disbursements.tool.js.map +1 -1
  49. package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts +8 -2
  50. package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts.map +1 -1
  51. package/dist/mcp-server/tools/definitions/search-expenditures.tool.js +18 -15
  52. package/dist/mcp-server/tools/definitions/search-expenditures.tool.js.map +1 -1
  53. package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts +5 -5
  54. package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts.map +1 -1
  55. package/dist/mcp-server/tools/definitions/search-filings.tool.js +18 -13
  56. package/dist/mcp-server/tools/definitions/search-filings.tool.js.map +1 -1
  57. package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts +7 -1
  58. package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts.map +1 -1
  59. package/dist/mcp-server/tools/definitions/search-legal.tool.js +12 -4
  60. package/dist/mcp-server/tools/definitions/search-legal.tool.js.map +1 -1
  61. package/dist/mcp-server/tools/definitions/utils/format-helpers.d.ts.map +1 -1
  62. package/dist/mcp-server/tools/definitions/utils/format-helpers.js +4 -2
  63. package/dist/mcp-server/tools/definitions/utils/format-helpers.js.map +1 -1
  64. package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts +2 -2
  65. package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts.map +1 -1
  66. package/dist/mcp-server/tools/definitions/utils/id-validators.js +5 -5
  67. package/dist/mcp-server/tools/definitions/utils/id-validators.js.map +1 -1
  68. package/dist/services/openfec/openfec-service.d.ts +2 -0
  69. package/dist/services/openfec/openfec-service.d.ts.map +1 -1
  70. package/dist/services/openfec/openfec-service.js +6 -0
  71. package/dist/services/openfec/openfec-service.js.map +1 -1
  72. package/package.json +10 -10
  73. 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.4.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
 
@@ -35,7 +36,7 @@ The OpenFEC OpenAPI spec (Swagger 2.0) is at `docs/openapi-spec.json` — 100 pa
35
36
 
36
37
  ## Core Rules
37
38
 
38
- - **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.
39
40
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
40
41
  - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
41
42
  - **Check `ctx.elicit` / `ctx.sample`** for presence before calling.
@@ -49,12 +50,24 @@ The OpenFEC OpenAPI spec (Swagger 2.0) is at `docs/openapi-spec.json` — 100 pa
49
50
 
50
51
  ```ts
51
52
  import { tool, z } from '@cyanheads/mcp-ts-core';
52
- import { invalidParams } from '@cyanheads/mcp-ts-core/errors';
53
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
53
54
  import { getOpenFecService } from '@/services/openfec/openfec-service.js';
55
+ import { validateCandidateId } from './utils/id-validators.js';
54
56
 
55
57
  export const searchCandidates = tool('openfec_search_candidates', {
56
58
  description: 'Find federal candidates by name, state, office, party, or cycle.',
57
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
+
58
71
  input: z.object({
59
72
  query: z.string().optional().describe('Full-text candidate name search.'),
60
73
  candidate_id: z.string().optional().describe('FEC candidate ID (e.g., P00003392).'),
@@ -68,10 +81,14 @@ export const searchCandidates = tool('openfec_search_candidates', {
68
81
  }),
69
82
  async handler(input, ctx) {
70
83
  const fec = getOpenFecService();
71
- if (input.candidate_id && !/^[HSP]\d+$/i.test(input.candidate_id)) {
72
- throw invalidParams('Invalid candidate ID format', { candidate_id: input.candidate_id });
73
- }
84
+ if (input.candidate_id) validateCandidateId(input.candidate_id);
74
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
+ }
75
92
  ctx.log.info('Candidate search completed', { count: result.pagination.count });
76
93
  return result;
77
94
  },
@@ -169,24 +186,38 @@ Handlers receive a unified `ctx` object. Key properties:
169
186
 
170
187
  ## Errors
171
188
 
172
- 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.
173
192
 
174
193
  ```ts
175
- // 1. Plain Error — framework auto-classifies from message patterns
176
- throw new Error('Item not found'); // NotFound
177
- 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
+ ```
178
208
 
179
- // 2. Error factoriesexplicit code, concise
180
- import { notFound, validationError, forbidden, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
181
- throw notFound('Item not found', { itemId });
182
- 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.
183
210
 
184
- // 3. McpError full control over code and data
185
- import { McpError, JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
186
- 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
187
218
  ```
188
219
 
189
- 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.
190
221
 
191
222
  ---
192
223
 
@@ -253,6 +284,7 @@ Available skills:
253
284
  | `add-service` | Scaffold a new service integration |
254
285
  | `add-test` | Scaffold test file for a tool, resource, or service |
255
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, …) |
256
288
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
257
289
  | `devcheck` | Lint, format, typecheck, audit |
258
290
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
@@ -262,13 +294,15 @@ Available skills:
262
294
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
263
295
  | `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
264
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 |
265
298
  | `api-config` | AppConfig, parseConfig, env vars |
266
- | `api-context` | Context interface, logger, state, progress |
267
- | `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 |
268
301
  | `api-linter` | MCP definition lint rules reference — look here when devcheck flags `format-parity`, `schema-*`, `name-*`, `server-json-*`, etc. |
269
302
  | `api-services` | LLM, Speech, Graph services |
303
+ | `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
270
304
  | `api-testing` | createMockContext, test patterns |
271
- | `api-utils` | Formatting, parsing, security, pagination, scheduling |
305
+ | `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
272
306
  | `api-workers` | Cloudflare Workers runtime |
273
307
 
274
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`).
@@ -287,11 +321,12 @@ When you complete a skill's checklist, check the boxes and add a completion time
287
321
  | `bun run format` | Auto-fix formatting |
288
322
  | `bun run lint:mcp` | Validate MCP tool/resource/prompt definitions |
289
323
  | `bun run test` | Run tests |
290
- | `bun run dev:stdio` | Dev mode (stdio) |
291
- | `bun run dev:http` | Dev mode (HTTP) |
324
+ | `bun run start` | Production mode (transport from `MCP_TRANSPORT_TYPE`) |
292
325
  | `bun run start:stdio` | Production mode (stdio) |
293
326
  | `bun run start:http` | Production mode (HTTP) |
294
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
+
295
330
  ---
296
331
 
297
332
  ## Publishing
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.1-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.3-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
 
@@ -121,7 +121,7 @@ Look up federal election races.
121
121
 
122
122
  - **search**: Candidates in a race with financial totals
123
123
  - **summary**: Aggregate race financial summary
124
- - Office types: president, senate, house
124
+ - Office types: H (House), S (Senate), P (President)
125
125
  - ZIP code lookup to find races covering a location
126
126
  - Full election period expansion (4yr president, 6yr senate, 2yr house)
127
127
 
@@ -1 +1 @@
1
- {"version":3,"file":"campaign-analysis.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/campaign-analysis.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,sBAAsB;;;;kBAsEjC,CAAC"}
1
+ {"version":3,"file":"campaign-analysis.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/campaign-analysis.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,sBAAsB;;;;kBA8EjC,CAAC"}
@@ -7,7 +7,8 @@
7
7
  import { prompt, z } from '@cyanheads/mcp-ts-core';
8
8
  export const campaignAnalysisPrompt = prompt('openfec_campaign_analysis', {
9
9
  description: `Structured analysis of a candidate's financial position — fundraising trajectory, burn rate, cash reserves, donor composition, and opponent comparison.`,
10
- args: z.object({
10
+ args: z
11
+ .object({
11
12
  candidate_name: z
12
13
  .string()
13
14
  .optional()
@@ -20,6 +21,10 @@ export const campaignAnalysisPrompt = prompt('openfec_campaign_analysis', {
20
21
  .string()
21
22
  .optional()
22
23
  .describe('Election cycle year (e.g., 2024). Defaults to current cycle.'),
24
+ })
25
+ .refine((args) => Boolean(args.candidate_id || args.candidate_name), {
26
+ message: 'Provide candidate_id or candidate_name to identify the candidate to analyze.',
27
+ path: ['candidate_id'],
23
28
  }),
24
29
  generate: (args) => {
25
30
  const target = args.candidate_id
@@ -40,29 +45,32 @@ Use openfec_search_candidates with include_totals=true to get:
40
45
  - Total receipts, disbursements, cash on hand, debt
41
46
  - Coverage period dates
42
47
 
43
- ## 2. Fundraising Analysis
44
- Use openfec_search_contributions with the candidate's principal committee:
48
+ ## 2. Find the Principal Committee
49
+ Use openfec_search_committees with the candidate_id to identify the principal campaign committee. Use that committee_id for the contribution and disbursement queries in steps 3 and 4.
50
+
51
+ ## 3. Fundraising Analysis
52
+ Use openfec_search_contributions with the principal committee_id:
45
53
  - **Size breakdown** (mode: by_size): What share comes from small vs. large donors?
46
54
  - **Geographic reach** (mode: by_state): Where is financial support concentrated?
47
55
  - **Industry patterns** (mode: by_employer): Which employers/industries are top sources?
48
56
 
49
- ## 3. Burn Rate & Spending
50
- Use openfec_search_disbursements with the principal committee:
57
+ ## 4. Burn Rate & Spending
58
+ Use openfec_search_disbursements with the principal committee_id:
51
59
  - **Purpose breakdown** (mode: by_purpose): Media buys, consulting, payroll, fundraising, travel
52
60
  - **Top recipients** (mode: by_recipient): Where is the money going?
53
61
  - Calculate burn rate: disbursements / receipts
54
62
 
55
- ## 4. Competitive Position
63
+ ## 5. Competitive Position
56
64
  Use openfec_lookup_elections to find all candidates in the race:
57
65
  - Compare total raised, cash on hand, and burn rates
58
66
  - Identify financial advantages and gaps
59
67
 
60
- ## 5. Outside Money Context
61
- Use openfec_search_expenditures with candidate_id:
68
+ ## 6. Outside Money Context
69
+ Use openfec_search_expenditures with the candidate_id:
62
70
  - Total independent expenditure support vs. opposition
63
71
  - Key outside groups involved
64
72
 
65
- ## 6. Assessment
73
+ ## 7. Assessment
66
74
  Synthesize into a financial health assessment:
67
75
  - **Fundraising trajectory**: Growing, flat, or declining?
68
76
  - **Donor base health**: Broad grassroots vs. maxed-out large donors?
@@ -1 +1 @@
1
- {"version":3,"file":"campaign-analysis.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/campaign-analysis.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,2BAA2B,EAAE;IACxE,WAAW,EAAE,yJAAyJ;IACtK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,cAAc,EAAE,CAAC;aACd,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,CAAC;QACvE,YAAY,EAAE,CAAC;aACZ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,qEAAqE,CAAC;QAClF,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY;YAC9B,CAAC,CAAC,gBAAgB,IAAI,CAAC,YAAY,EAAE;YACrC,CAAC,CAAC,IAAI,CAAC,cAAc;gBACnB,CAAC,CAAC,IAAI,IAAI,CAAC,cAAc,GAAG;gBAC5B,CAAC,CAAC,yBAAyB,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAEnE,OAAO;YACL;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,qDAAqD,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2EAoCZ;iBAClE;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"campaign-analysis.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/campaign-analysis.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,2BAA2B,EAAE;IACxE,WAAW,EAAE,yJAAyJ;IACtK,IAAI,EAAE,CAAC;SACJ,MAAM,CAAC;QACN,cAAc,EAAE,CAAC;aACd,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,CAAC;QACvE,YAAY,EAAE,CAAC;aACZ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,qEAAqE,CAAC;QAClF,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC;SACD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,cAAc,CAAC,EAAE;QACnE,OAAO,EAAE,8EAA8E;QACvF,IAAI,EAAE,CAAC,cAAc,CAAC;KACvB,CAAC;IACJ,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY;YAC9B,CAAC,CAAC,gBAAgB,IAAI,CAAC,YAAY,EAAE;YACrC,CAAC,CAAC,IAAI,CAAC,cAAc;gBACnB,CAAC,CAAC,IAAI,IAAI,CAAC,cAAc,GAAG;gBAC5B,CAAC,CAAC,yBAAyB,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAEnE,OAAO;YACL;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,qDAAqD,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2EAuCZ;iBAClE;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"money-trail.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/money-trail.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,gBAAgB;;;;kBAoE3B,CAAC"}
1
+ {"version":3,"file":"money-trail.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/money-trail.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,gBAAgB;;;;kBAwE3B,CAAC"}
@@ -6,8 +6,9 @@
6
6
  */
7
7
  import { prompt, z } from '@cyanheads/mcp-ts-core';
8
8
  export const moneyTrailPrompt = prompt('openfec_money_trail', {
9
- description: `Framework for tracing the flow of money around a candidate or race — direct fundraising, PAC support, independent expenditures, and party spending. Guides the agent through a multi-tool investigation.`,
10
- args: z.object({
9
+ description: `Multi-step framework for tracing the flow of money around a candidate or race — direct fundraising, PAC support, independent expenditures, and party spending.`,
10
+ args: z
11
+ .object({
11
12
  candidate_name: z
12
13
  .string()
13
14
  .optional()
@@ -20,6 +21,10 @@ export const moneyTrailPrompt = prompt('openfec_money_trail', {
20
21
  .string()
21
22
  .optional()
22
23
  .describe('Election cycle year (e.g., 2024). Defaults to current cycle.'),
24
+ })
25
+ .refine((args) => Boolean(args.candidate_id || args.candidate_name), {
26
+ message: 'Provide candidate_id or candidate_name to identify the candidate to investigate.',
27
+ path: ['candidate_id'],
23
28
  }),
24
29
  generate: (args) => {
25
30
  const target = args.candidate_id
@@ -45,11 +50,10 @@ Use openfec_search_committees with the candidate_id to find:
45
50
  - Joint fundraising committees
46
51
 
47
52
  ## Step 3: Follow direct fundraising
48
- For the principal campaign committee, use openfec_search_contributions to examine:
49
- - Overall totals (include_totals from step 1)
50
- - Contribution breakdown by size (mode: by_size)
53
+ Carry forward the receipt totals already retrieved in step 1. Use openfec_search_contributions with the principal campaign committee_id to break down where the money came from:
54
+ - Contribution size distribution (mode: by_size)
51
55
  - Top donor states (mode: by_state)
52
- - Top employer/occupation patterns (mode: by_employer, by_occupation)
56
+ - Top employer and occupation patterns (mode: by_employer, by_occupation)
53
57
 
54
58
  ## Step 4: Track outside money
55
59
  Use openfec_search_expenditures with the candidate_id to find:
@@ -1 +1 @@
1
- {"version":3,"file":"money-trail.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/money-trail.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,qBAAqB,EAAE;IAC5D,WAAW,EAAE,0MAA0M;IACvN,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,cAAc,EAAE,CAAC;aACd,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8DAA8D,CAAC;QAC3E,YAAY,EAAE,CAAC;aACZ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,qEAAqE,CAAC;QAClF,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY;YAC9B,CAAC,CAAC,gBAAgB,IAAI,CAAC,YAAY,EAAE;YACrC,CAAC,CAAC,IAAI,CAAC,cAAc;gBACnB,CAAC,CAAC,IAAI,IAAI,CAAC,cAAc,GAAG;gBAC5B,CAAC,CAAC,yBAAyB,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAEnE,OAAO;YACL;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,kCAAkC,MAAM,GAAG,SAAS;;;EAGlE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,qBAAqB,IAAI,CAAC,YAAY,4DAA4D,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,cAAc,0GAA0G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8CA+BzN;iBACrC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"money-trail.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/money-trail.prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,qBAAqB,EAAE;IAC5D,WAAW,EAAE,gKAAgK;IAC7K,IAAI,EAAE,CAAC;SACJ,MAAM,CAAC;QACN,cAAc,EAAE,CAAC;aACd,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8DAA8D,CAAC;QAC3E,YAAY,EAAE,CAAC;aACZ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,qEAAqE,CAAC;QAClF,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC;SACD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,cAAc,CAAC,EAAE;QACnE,OAAO,EAAE,kFAAkF;QAC3F,IAAI,EAAE,CAAC,cAAc,CAAC;KACvB,CAAC;IACJ,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY;YAC9B,CAAC,CAAC,gBAAgB,IAAI,CAAC,YAAY,EAAE;YACrC,CAAC,CAAC,IAAI,CAAC,cAAc;gBACnB,CAAC,CAAC,IAAI,IAAI,CAAC,cAAc,GAAG;gBAC5B,CAAC,CAAC,yBAAyB,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAEnE,OAAO;YACL;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,kCAAkC,MAAM,GAAG,SAAS;;;EAGlE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,qBAAqB,IAAI,CAAC,YAAY,4DAA4D,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,cAAc,0GAA0G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8CA8BzN;iBACrC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -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;;;;;;;GA2C5B,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,17 +14,33 @@ 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
- const candidateResult = await fec.getCandidate(params.candidate_id, ctx);
28
+ const [candidateResult, totalsResult, committeesResult] = await Promise.all([
29
+ fec.getCandidate(params.candidate_id, ctx),
30
+ fec.getCandidateTotals({ candidate_id: params.candidate_id }, ctx),
31
+ fec.getCandidateCommittees(params.candidate_id, { designation: 'P' }, ctx),
32
+ ]);
20
33
  const candidate = candidateResult.results[0];
21
- if (!candidate)
22
- throw new Error(`Candidate ${params.candidate_id} not found`);
23
- const totalsResult = await fec.getCandidateTotals({ candidate_id: params.candidate_id }, ctx);
34
+ if (!candidate) {
35
+ throw ctx.fail('candidate_not_found', `Candidate ${params.candidate_id} not found.`, {
36
+ candidate_id: params.candidate_id,
37
+ ...ctx.recoveryFor('candidate_not_found'),
38
+ });
39
+ }
24
40
  const totals = totalsResult.results[0];
41
+ const principal_committees = committeesResult.results;
25
42
  ctx.log.info('Candidate resource fetched', { candidate_id: params.candidate_id });
26
- return { ...candidate, ...(totals ?? {}) };
43
+ return { ...candidate, ...(totals ?? {}), principal_committees };
27
44
  },
28
45
  });
29
46
  //# sourceMappingURL=candidate.resource.js.map
@@ -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,CAAC,eAAe,EAAE,YAAY,EAAE,gBAAgB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC1E,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC;YAC1C,GAAG,CAAC,kBAAkB,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,EAAE,GAAG,CAAC;YAClE,GAAG,CAAC,sBAAsB,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC;SAC3E,CAAC,CAAC;QAEH,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,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAEtD,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,oBAAoB,EAAE,CAAC;IACnE,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;;;;;;;GA4C5B,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,14 +14,34 @@ 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
- const result = await fec.getCommittee(params.committee_id, ctx);
18
- const committee = result.results[0];
19
- if (!committee)
20
- throw new Error(`Committee ${params.committee_id} not found`);
28
+ // Totals 404 for committees that don't file Form 3/3X/3P — treat any
29
+ // totals failure as "no totals" while letting the base fetch surface
30
+ // the committee_not_found contract.
31
+ const [committeeResult, totalsResult] = await Promise.all([
32
+ fec.getCommittee(params.committee_id, ctx),
33
+ fec.getCommitteeTotals(params.committee_id, { per_page: 1 }, ctx).catch(() => null),
34
+ ]);
35
+ const committee = committeeResult.results[0];
36
+ if (!committee) {
37
+ throw ctx.fail('committee_not_found', `Committee ${params.committee_id} not found.`, {
38
+ committee_id: params.committee_id,
39
+ ...ctx.recoveryFor('committee_not_found'),
40
+ });
41
+ }
42
+ const totals = totalsResult?.results[0];
21
43
  ctx.log.info('Committee resource fetched', { committee_id: params.committee_id });
22
- return committee;
44
+ return { ...committee, ...(totals ?? {}) };
23
45
  },
24
46
  });
25
47
  //# sourceMappingURL=committee.resource.js.map
@@ -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,qEAAqE;QACrE,qEAAqE;QACrE,oCAAoC;QACpC,MAAM,CAAC,eAAe,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxD,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC;YAC1C,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;SACpF,CAAC,CAAC;QAEH,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,MAAM,GAAG,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAExC,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,22 +4,29 @@
4
4
  * @module src/mcp-server/resources/definitions/election.resource
5
5
  */
6
6
  import { z } from '@cyanheads/mcp-ts-core';
7
- /** Presidential and at-large races: openfec://election/2024/president */
7
+ /** Presidential races: openfec://election/2024/P */
8
8
  export declare const electionResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
9
9
  cycle: z.ZodString;
10
- office: z.ZodString;
11
- }, z.core.$strip>, undefined>;
12
- /** Senate races: openfec://election/2024/senate/AZ */
10
+ office: z.ZodEnum<{
11
+ P: "P";
12
+ }>;
13
+ }, z.core.$strip>, undefined, undefined>;
14
+ /** Senate or at-large house races: openfec://election/2024/S/AZ */
13
15
  export declare const electionStateResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
14
16
  cycle: z.ZodString;
15
- office: z.ZodString;
17
+ office: z.ZodEnum<{
18
+ H: "H";
19
+ S: "S";
20
+ }>;
16
21
  state: z.ZodString;
17
- }, z.core.$strip>, undefined>;
18
- /** House races: openfec://election/2024/house/CA/12 */
22
+ }, z.core.$strip>, undefined, undefined>;
23
+ /** House district races: openfec://election/2024/H/CA/12 */
19
24
  export declare const electionDistrictResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
20
25
  cycle: z.ZodString;
21
- office: z.ZodString;
26
+ office: z.ZodEnum<{
27
+ H: "H";
28
+ }>;
22
29
  state: z.ZodString;
23
30
  district: z.ZodString;
24
- }, z.core.$strip>, undefined>;
31
+ }, z.core.$strip>, undefined, undefined>;
25
32
  //# 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;AAsCrD,oDAAoD;AACpD,eAAO,MAAM,gBAAgB;;;;;wCAU3B,CAAC;AAEH,mEAAmE;AACnE,eAAO,MAAM,qBAAqB;;;;;;;wCAWhC,CAAC;AAEH,4DAA4D;AAC5D,eAAO,MAAM,wBAAwB;;;;;;;wCAcpC,CAAC"}
@@ -5,12 +5,13 @@
5
5
  */
6
6
  import { resource, z } from '@cyanheads/mcp-ts-core';
7
7
  import { getOpenFecService } from '../../../services/openfec/openfec-service.js';
8
+ const OFFICE_API_FORM = { H: 'house', S: 'senate', P: 'president' };
8
9
  /** Shared handler logic for all election resource URI variants. */
9
10
  async function fetchElection(params, ctx) {
10
11
  const fec = getOpenFecService();
11
12
  const apiParams = {
12
13
  cycle: params.cycle,
13
- office: params.office,
14
+ office: OFFICE_API_FORM[params.office],
14
15
  election_full: true,
15
16
  };
16
17
  if (params.state)
@@ -32,37 +33,37 @@ async function fetchElection(params, ctx) {
32
33
  candidates: result.results,
33
34
  };
34
35
  }
35
- /** Presidential and at-large races: openfec://election/2024/president */
36
+ /** Presidential races: openfec://election/2024/P */
36
37
  export const electionResource = resource('openfec://election/{cycle}/{office}', {
37
38
  name: 'FEC Election Race',
38
- description: 'Fetch an election race with candidate financial totals. For senate races use openfec://election/{cycle}/senate/{state}. For house races use openfec://election/{cycle}/house/{state}/{district}.',
39
+ description: 'Fetch a presidential election race with candidate financial totals. For senate races use openfec://election/{cycle}/S/{state}. For house races use openfec://election/{cycle}/H/{state}/{district}.',
39
40
  mimeType: 'application/json',
40
41
  params: z.object({
41
42
  cycle: z.string().describe('Election cycle year (e.g., 2024)'),
42
- office: z.string().describe('Office: president, senate, or house'),
43
+ office: z.enum(['P']).describe('Office code: P=President.'),
43
44
  }),
44
45
  handler: (params, ctx) => fetchElection(params, ctx),
45
46
  });
46
- /** Senate races: openfec://election/2024/senate/AZ */
47
+ /** Senate or at-large house races: openfec://election/2024/S/AZ */
47
48
  export const electionStateResource = resource('openfec://election/{cycle}/{office}/{state}', {
48
49
  name: 'FEC Election Race (State)',
49
- description: 'Fetch a senate or at-large election race with candidate financial totals.',
50
+ description: 'Fetch a senate race, or a house at-large race in a single-district state, with candidate financial totals.',
50
51
  mimeType: 'application/json',
51
52
  params: z.object({
52
53
  cycle: z.string().describe('Election cycle year (e.g., 2024)'),
53
- office: z.string().describe('Office: senate (or president)'),
54
+ office: z.enum(['S', 'H']).describe('Office code: S=Senate, H=House (at-large).'),
54
55
  state: z.string().describe('Two-letter US state code (e.g., AZ)'),
55
56
  }),
56
57
  handler: (params, ctx) => fetchElection(params, ctx),
57
58
  });
58
- /** House races: openfec://election/2024/house/CA/12 */
59
+ /** House district races: openfec://election/2024/H/CA/12 */
59
60
  export const electionDistrictResource = resource('openfec://election/{cycle}/{office}/{state}/{district}', {
60
61
  name: 'FEC Election Race (District)',
61
62
  description: 'Fetch a house election race with candidate financial totals.',
62
63
  mimeType: 'application/json',
63
64
  params: z.object({
64
65
  cycle: z.string().describe('Election cycle year (e.g., 2024)'),
65
- office: z.string().describe('Office: house'),
66
+ office: z.enum(['H']).describe('Office code: H=House.'),
66
67
  state: z.string().describe('Two-letter US state code (e.g., CA)'),
67
68
  district: z.string().describe('Two-digit district number (e.g., 12)'),
68
69
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"election.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/election.resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"election.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/election.resource.ts"],"names":[],"mappings":"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"}