@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.
- package/CLAUDE.md +59 -24
- package/README.md +3 -3
- package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.d.ts.map +1 -1
- package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.js +17 -9
- package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.js.map +1 -1
- package/dist/mcp-server/prompts/definitions/money-trail.prompt.d.ts.map +1 -1
- package/dist/mcp-server/prompts/definitions/money-trail.prompt.js +10 -6
- package/dist/mcp-server/prompts/definitions/money-trail.prompt.js.map +1 -1
- package/dist/mcp-server/resources/definitions/candidate.resource.d.ts +7 -1
- package/dist/mcp-server/resources/definitions/candidate.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/candidate.resource.js +22 -5
- package/dist/mcp-server/resources/definitions/candidate.resource.js.map +1 -1
- package/dist/mcp-server/resources/definitions/committee.resource.d.ts +7 -1
- package/dist/mcp-server/resources/definitions/committee.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/committee.resource.js +27 -5
- package/dist/mcp-server/resources/definitions/committee.resource.js.map +1 -1
- package/dist/mcp-server/resources/definitions/election.resource.d.ts +16 -9
- package/dist/mcp-server/resources/definitions/election.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/election.resource.js +10 -9
- package/dist/mcp-server/resources/definitions/election.resource.js.map +1 -1
- package/dist/mcp-server/resources/definitions/index.d.ts +30 -4
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +93 -24
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts +22 -3
- package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js +26 -14
- package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts +25 -4
- package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-elections.tool.js +52 -22
- package/dist/mcp-server/tools/definitions/lookup-elections.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts +10 -4
- package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-candidates.tool.js +17 -8
- package/dist/mcp-server/tools/definitions/search-candidates.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts +9 -3
- package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-committees.tool.js +16 -7
- package/dist/mcp-server/tools/definitions/search-committees.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts +12 -1
- package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-contributions.tool.js +26 -16
- package/dist/mcp-server/tools/definitions/search-contributions.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-disbursements.tool.js +8 -14
- package/dist/mcp-server/tools/definitions/search-disbursements.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts +8 -2
- package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-expenditures.tool.js +18 -15
- package/dist/mcp-server/tools/definitions/search-expenditures.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts +5 -5
- package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-filings.tool.js +18 -13
- package/dist/mcp-server/tools/definitions/search-filings.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts +7 -1
- package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-legal.tool.js +12 -4
- package/dist/mcp-server/tools/definitions/search-legal.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/utils/format-helpers.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/utils/format-helpers.js +4 -2
- package/dist/mcp-server/tools/definitions/utils/format-helpers.js.map +1 -1
- package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/utils/id-validators.js +5 -5
- package/dist/mcp-server/tools/definitions/utils/id-validators.js.map +1 -1
- package/dist/services/openfec/openfec-service.d.ts +2 -0
- package/dist/services/openfec/openfec-service.d.ts.map +1 -1
- package/dist/services/openfec/openfec-service.js +6 -0
- package/dist/services/openfec/openfec-service.js.map +1 -1
- package/package.json +10 -10
- 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.
|
|
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`.
|
|
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 {
|
|
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
|
|
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.
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
[](https://www.npmjs.com/package/@cyanheads/openfec-mcp-server) [](https://www.npmjs.com/package/@cyanheads/openfec-mcp-server) [](./CHANGELOG.md) [](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [](https://modelcontextprotocol.io/)
|
|
10
10
|
|
|
11
|
-
[](./LICENSE) [](https://www.typescriptlang.org/) [](./LICENSE) [](https://www.typescriptlang.org/) [](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:
|
|
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;;;;
|
|
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
|
|
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.
|
|
44
|
-
Use
|
|
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
|
-
##
|
|
50
|
-
Use openfec_search_disbursements with the principal
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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,
|
|
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;;;;
|
|
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: `
|
|
10
|
-
args: z
|
|
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
|
-
|
|
49
|
-
-
|
|
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
|
|
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,
|
|
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;
|
|
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
|
|
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
|
|
23
|
-
|
|
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
|
|
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;
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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,
|
|
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
|
|
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.
|
|
11
|
-
|
|
12
|
-
|
|
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.
|
|
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/
|
|
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.
|
|
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;
|
|
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
|
|
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
|
|
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.
|
|
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/
|
|
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
|
|
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.
|
|
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/
|
|
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.
|
|
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":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG1E,mEAAmE;AACnE,KAAK,UAAU,aAAa,CAC1B,
|
|
1
|
+
{"version":3,"file":"election.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/election.resource.ts"],"names":[],"mappings":"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"}
|