@cyanheads/openfec-mcp-server 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CLAUDE.md +328 -0
  2. package/Dockerfile +99 -0
  3. package/LICENSE +191 -0
  4. package/README.md +297 -0
  5. package/dist/config/server-config.d.ts +17 -0
  6. package/dist/config/server-config.d.ts.map +1 -0
  7. package/dist/config/server-config.js +31 -0
  8. package/dist/config/server-config.js.map +1 -0
  9. package/dist/index.d.ts +8 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +20 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.d.ts +13 -0
  14. package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.d.ts.map +1 -0
  15. package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.js +79 -0
  16. package/dist/mcp-server/prompts/definitions/campaign-analysis.prompt.js.map +1 -0
  17. package/dist/mcp-server/prompts/definitions/index.d.ts +12 -0
  18. package/dist/mcp-server/prompts/definitions/index.d.ts.map +1 -0
  19. package/dist/mcp-server/prompts/definitions/index.js +10 -0
  20. package/dist/mcp-server/prompts/definitions/index.js.map +1 -0
  21. package/dist/mcp-server/prompts/definitions/money-trail.prompt.d.ts +13 -0
  22. package/dist/mcp-server/prompts/definitions/money-trail.prompt.d.ts.map +1 -0
  23. package/dist/mcp-server/prompts/definitions/money-trail.prompt.js +78 -0
  24. package/dist/mcp-server/prompts/definitions/money-trail.prompt.js.map +1 -0
  25. package/dist/mcp-server/resources/definitions/candidate.resource.d.ts +10 -0
  26. package/dist/mcp-server/resources/definitions/candidate.resource.d.ts.map +1 -0
  27. package/dist/mcp-server/resources/definitions/candidate.resource.js +28 -0
  28. package/dist/mcp-server/resources/definitions/candidate.resource.js.map +1 -0
  29. package/dist/mcp-server/resources/definitions/committee.resource.d.ts +10 -0
  30. package/dist/mcp-server/resources/definitions/committee.resource.d.ts.map +1 -0
  31. package/dist/mcp-server/resources/definitions/committee.resource.js +26 -0
  32. package/dist/mcp-server/resources/definitions/committee.resource.js.map +1 -0
  33. package/dist/mcp-server/resources/definitions/election.resource.d.ts +25 -0
  34. package/dist/mcp-server/resources/definitions/election.resource.d.ts.map +1 -0
  35. package/dist/mcp-server/resources/definitions/election.resource.js +73 -0
  36. package/dist/mcp-server/resources/definitions/election.resource.js.map +1 -0
  37. package/dist/mcp-server/resources/definitions/index.d.ts +16 -0
  38. package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
  39. package/dist/mcp-server/resources/definitions/index.js +18 -0
  40. package/dist/mcp-server/resources/definitions/index.js.map +1 -0
  41. package/dist/mcp-server/tools/definitions/index.d.ts +285 -0
  42. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
  43. package/dist/mcp-server/tools/definitions/index.js +34 -0
  44. package/dist/mcp-server/tools/definitions/index.js.map +1 -0
  45. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts +37 -0
  46. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.d.ts.map +1 -0
  47. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js +172 -0
  48. package/dist/mcp-server/tools/definitions/lookup-calendar.tool.js.map +1 -0
  49. package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts +31 -0
  50. package/dist/mcp-server/tools/definitions/lookup-elections.tool.d.ts.map +1 -0
  51. package/dist/mcp-server/tools/definitions/lookup-elections.tool.js +134 -0
  52. package/dist/mcp-server/tools/definitions/lookup-elections.tool.js.map +1 -0
  53. package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts +46 -0
  54. package/dist/mcp-server/tools/definitions/search-candidates.tool.d.ts.map +1 -0
  55. package/dist/mcp-server/tools/definitions/search-candidates.tool.js +170 -0
  56. package/dist/mcp-server/tools/definitions/search-candidates.tool.js.map +1 -0
  57. package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts +29 -0
  58. package/dist/mcp-server/tools/definitions/search-committees.tool.d.ts.map +1 -0
  59. package/dist/mcp-server/tools/definitions/search-committees.tool.js +120 -0
  60. package/dist/mcp-server/tools/definitions/search-committees.tool.js.map +1 -0
  61. package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts +47 -0
  62. package/dist/mcp-server/tools/definitions/search-contributions.tool.d.ts.map +1 -0
  63. package/dist/mcp-server/tools/definitions/search-contributions.tool.js +252 -0
  64. package/dist/mcp-server/tools/definitions/search-contributions.tool.js.map +1 -0
  65. package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts +43 -0
  66. package/dist/mcp-server/tools/definitions/search-disbursements.tool.d.ts.map +1 -0
  67. package/dist/mcp-server/tools/definitions/search-disbursements.tool.js +212 -0
  68. package/dist/mcp-server/tools/definitions/search-disbursements.tool.js.map +1 -0
  69. package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts +52 -0
  70. package/dist/mcp-server/tools/definitions/search-expenditures.tool.d.ts.map +1 -0
  71. package/dist/mcp-server/tools/definitions/search-expenditures.tool.js +247 -0
  72. package/dist/mcp-server/tools/definitions/search-expenditures.tool.js.map +1 -0
  73. package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts +31 -0
  74. package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts.map +1 -0
  75. package/dist/mcp-server/tools/definitions/search-filings.tool.js +123 -0
  76. package/dist/mcp-server/tools/definitions/search-filings.tool.js.map +1 -0
  77. package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts +32 -0
  78. package/dist/mcp-server/tools/definitions/search-legal.tool.d.ts.map +1 -0
  79. package/dist/mcp-server/tools/definitions/search-legal.tool.js +179 -0
  80. package/dist/mcp-server/tools/definitions/search-legal.tool.js.map +1 -0
  81. package/dist/mcp-server/tools/definitions/utils/format-helpers.d.ts +19 -0
  82. package/dist/mcp-server/tools/definitions/utils/format-helpers.d.ts.map +1 -0
  83. package/dist/mcp-server/tools/definitions/utils/format-helpers.js +19 -0
  84. package/dist/mcp-server/tools/definitions/utils/format-helpers.js.map +1 -0
  85. package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts +11 -0
  86. package/dist/mcp-server/tools/definitions/utils/id-validators.d.ts.map +1 -0
  87. package/dist/mcp-server/tools/definitions/utils/id-validators.js +22 -0
  88. package/dist/mcp-server/tools/definitions/utils/id-validators.js.map +1 -0
  89. package/dist/services/openfec/openfec-service.d.ts +54 -0
  90. package/dist/services/openfec/openfec-service.d.ts.map +1 -0
  91. package/dist/services/openfec/openfec-service.js +384 -0
  92. package/dist/services/openfec/openfec-service.js.map +1 -0
  93. package/dist/services/openfec/types.d.ts +86 -0
  94. package/dist/services/openfec/types.d.ts.map +1 -0
  95. package/dist/services/openfec/types.js +8 -0
  96. package/dist/services/openfec/types.js.map +1 -0
  97. package/package.json +91 -0
  98. package/server.json +97 -0
@@ -0,0 +1,384 @@
1
+ /**
2
+ * @fileoverview OpenFEC API service. Wraps all FEC REST API interactions
3
+ * with timeout, retry, and pagination handling. Single service used by
4
+ * all tools and resources.
5
+ * @module src/services/openfec/openfec-service
6
+ */
7
+ import { fetchWithTimeout, withRetry } from '@cyanheads/mcp-ts-core/utils';
8
+ import { getServerConfig } from '../../config/server-config.js';
9
+ /* ------------------------------------------------------------------ */
10
+ /* Cursor encoding for keyset pagination */
11
+ /* ------------------------------------------------------------------ */
12
+ /** Encode `last_indexes` from SEEK pagination into an opaque cursor. */
13
+ export function encodeCursor(lastIndexes) {
14
+ return btoa(JSON.stringify(lastIndexes));
15
+ }
16
+ /** Decode an opaque cursor back to `last_indexes` query params. */
17
+ export function decodeCursor(cursor) {
18
+ return JSON.parse(atob(cursor));
19
+ }
20
+ /* ------------------------------------------------------------------ */
21
+ /* Context adapter */
22
+ /* ------------------------------------------------------------------ */
23
+ /**
24
+ * Extract a RequestContext from a handler Context.
25
+ * Needed because `exactOptionalPropertyTypes` makes Context's optional
26
+ * `T | undefined` fields incompatible with RequestContext's optional `T` fields.
27
+ */
28
+ function toRequestContext(ctx) {
29
+ const rc = { requestId: ctx.requestId, timestamp: ctx.timestamp };
30
+ if (ctx.tenantId)
31
+ rc.tenantId = ctx.tenantId;
32
+ if (ctx.traceId)
33
+ rc.traceId = ctx.traceId;
34
+ if (ctx.spanId)
35
+ rc.spanId = ctx.spanId;
36
+ if (ctx.auth)
37
+ rc.auth = ctx.auth;
38
+ return rc;
39
+ }
40
+ /* ------------------------------------------------------------------ */
41
+ /* Service class */
42
+ /* ------------------------------------------------------------------ */
43
+ export class OpenFecService {
44
+ config;
45
+ constructor() {
46
+ this.config = getServerConfig();
47
+ }
48
+ /* ---------------------------------------------------------------- */
49
+ /* Internal fetch helpers */
50
+ /* ---------------------------------------------------------------- */
51
+ /** Build a full URL with query params, injecting the API key. */
52
+ buildUrl(path, params = {}) {
53
+ const base = this.config.fecBaseUrl.replace(/\/$/, '');
54
+ const url = new URL(`${base}${path}`);
55
+ url.searchParams.set('api_key', this.config.fecApiKey);
56
+ for (const [key, value] of Object.entries(params)) {
57
+ if (value === undefined || value === '')
58
+ continue;
59
+ if (Array.isArray(value)) {
60
+ for (const v of value)
61
+ url.searchParams.append(key, v);
62
+ }
63
+ else {
64
+ url.searchParams.set(key, String(value));
65
+ }
66
+ }
67
+ return url.toString();
68
+ }
69
+ /**
70
+ * Fetch JSON from a page-based endpoint with retry.
71
+ * Wraps the full pipeline (fetch + JSON parse) in the retry boundary.
72
+ */
73
+ async fetchPage(path, params, ctx) {
74
+ const url = this.buildUrl(path, params);
75
+ const reqCtx = toRequestContext(ctx);
76
+ try {
77
+ return await withRetry(async () => {
78
+ const response = await fetchWithTimeout(url, this.config.fecRequestTimeout, reqCtx, {
79
+ signal: ctx.signal,
80
+ });
81
+ const body = (await response.json());
82
+ this.validateEnvelope(body);
83
+ return {
84
+ pagination: {
85
+ page: body.pagination?.page ?? 1,
86
+ pages: body.pagination?.pages ?? 1,
87
+ count: body.pagination?.count ?? body.results?.length ?? 0,
88
+ per_page: body.pagination?.per_page ?? 20,
89
+ },
90
+ results: body.results,
91
+ };
92
+ }, {
93
+ maxRetries: this.config.fecMaxRetries,
94
+ baseDelayMs: 1_000,
95
+ operation: `FEC ${path}`,
96
+ context: reqCtx,
97
+ signal: ctx.signal,
98
+ isTransient: isTransientFecError,
99
+ });
100
+ }
101
+ catch (err) {
102
+ rethrowSanitized(err);
103
+ }
104
+ }
105
+ /**
106
+ * Fetch JSON from a keyset (SEEK) endpoint with retry.
107
+ * Returns a `nextCursor` from `last_indexes` when more results exist.
108
+ */
109
+ async fetchSeek(path, params, ctx) {
110
+ const url = this.buildUrl(path, params);
111
+ const reqCtx = toRequestContext(ctx);
112
+ try {
113
+ return await withRetry(async () => {
114
+ const response = await fetchWithTimeout(url, this.config.fecRequestTimeout, reqCtx, {
115
+ signal: ctx.signal,
116
+ });
117
+ const body = (await response.json());
118
+ this.validateEnvelope(body);
119
+ const lastIndexes = body.pagination.last_indexes;
120
+ const hasMore = lastIndexes && Object.keys(lastIndexes).length > 0 && body.results.length > 0;
121
+ return {
122
+ pagination: {
123
+ count: body.pagination.count,
124
+ per_page: body.pagination.per_page,
125
+ },
126
+ results: body.results,
127
+ nextCursor: hasMore ? encodeCursor(lastIndexes) : null,
128
+ };
129
+ }, {
130
+ maxRetries: this.config.fecMaxRetries,
131
+ baseDelayMs: 1_000,
132
+ operation: `FEC ${path}`,
133
+ context: reqCtx,
134
+ signal: ctx.signal,
135
+ isTransient: isTransientFecError,
136
+ });
137
+ }
138
+ catch (err) {
139
+ rethrowSanitized(err);
140
+ }
141
+ }
142
+ /** Fetch legal search results with retry. Normalizes type-keyed arrays. */
143
+ async fetchLegalSearch(params, ctx) {
144
+ const url = this.buildUrl('/legal/search/', params);
145
+ const reqCtx = toRequestContext(ctx);
146
+ try {
147
+ return await withRetry(async () => {
148
+ const response = await fetchWithTimeout(url, this.config.fecRequestTimeout, reqCtx, {
149
+ signal: ctx.signal,
150
+ });
151
+ const body = (await response.json());
152
+ const results = [];
153
+ for (const ao of body.advisory_opinions ?? []) {
154
+ results.push({ ...ao, document_type: 'advisory_opinion' });
155
+ }
156
+ for (const mur of body.murs ?? []) {
157
+ results.push({ ...mur, document_type: 'mur' });
158
+ }
159
+ for (const adr of body.adrs ?? []) {
160
+ results.push({ ...adr, document_type: 'adr' });
161
+ }
162
+ for (const fine of body.admin_fines ?? []) {
163
+ results.push({ ...fine, document_type: 'admin_fine' });
164
+ }
165
+ for (const statute of body.statutes ?? []) {
166
+ results.push({ ...statute, document_type: 'statute' });
167
+ }
168
+ return { results, totalCount: body.total_all ?? results.length };
169
+ }, {
170
+ maxRetries: this.config.fecMaxRetries,
171
+ baseDelayMs: 1_000,
172
+ operation: 'FEC /legal/search/',
173
+ context: reqCtx,
174
+ signal: ctx.signal,
175
+ isTransient: isTransientFecError,
176
+ });
177
+ }
178
+ catch (err) {
179
+ rethrowSanitized(err);
180
+ }
181
+ }
182
+ /** Validate that the API returned a recognizable envelope, not an HTML error page. */
183
+ validateEnvelope(body) {
184
+ if (!body || typeof body !== 'object' || !('results' in body)) {
185
+ throw new Error('FEC API returned an unexpected response (possible HTML error page)');
186
+ }
187
+ }
188
+ /* ---------------------------------------------------------------- */
189
+ /* Candidates */
190
+ /* ---------------------------------------------------------------- */
191
+ searchCandidates(params, ctx) {
192
+ return this.fetchPage('/candidates/', params, ctx);
193
+ }
194
+ getCandidate(candidateId, ctx) {
195
+ return this.fetchPage(`/candidate/${candidateId}/`, {}, ctx);
196
+ }
197
+ getCandidateTotals(params, ctx) {
198
+ return this.fetchPage('/candidates/totals/', params, ctx);
199
+ }
200
+ /* ---------------------------------------------------------------- */
201
+ /* Committees */
202
+ /* ---------------------------------------------------------------- */
203
+ searchCommittees(params, ctx) {
204
+ return this.fetchPage('/committees/', params, ctx);
205
+ }
206
+ getCommittee(committeeId, ctx) {
207
+ return this.fetchPage(`/committee/${committeeId}/`, {}, ctx);
208
+ }
209
+ /* ---------------------------------------------------------------- */
210
+ /* Contributions (Schedule A) */
211
+ /* ---------------------------------------------------------------- */
212
+ searchContributions(params, ctx) {
213
+ return this.fetchSeek('/schedules/schedule_a/', params, ctx);
214
+ }
215
+ getContributionAggregates(mode, params, ctx) {
216
+ const paths = {
217
+ by_size: '/schedules/schedule_a/by_size/',
218
+ by_size_candidate: '/schedules/schedule_a/by_size/by_candidate/',
219
+ by_state: '/schedules/schedule_a/by_state/',
220
+ by_state_candidate: '/schedules/schedule_a/by_state/by_candidate/',
221
+ by_employer: '/schedules/schedule_a/by_employer/',
222
+ by_occupation: '/schedules/schedule_a/by_occupation/',
223
+ };
224
+ const path = paths[mode];
225
+ if (!path)
226
+ throw new Error(`Unknown contribution aggregate mode: ${mode}`);
227
+ return this.fetchPage(path, params, ctx);
228
+ }
229
+ /* ---------------------------------------------------------------- */
230
+ /* Disbursements (Schedule B) */
231
+ /* ---------------------------------------------------------------- */
232
+ searchDisbursements(params, ctx) {
233
+ return this.fetchSeek('/schedules/schedule_b/', params, ctx);
234
+ }
235
+ getDisbursementAggregates(mode, params, ctx) {
236
+ const paths = {
237
+ by_purpose: '/schedules/schedule_b/by_purpose/',
238
+ by_recipient: '/schedules/schedule_b/by_recipient/',
239
+ by_recipient_id: '/schedules/schedule_b/by_recipient_id/',
240
+ };
241
+ const path = paths[mode];
242
+ if (!path)
243
+ throw new Error(`Unknown disbursement aggregate mode: ${mode}`);
244
+ return this.fetchPage(path, params, ctx);
245
+ }
246
+ /* ---------------------------------------------------------------- */
247
+ /* Independent Expenditures (Schedule E) */
248
+ /* ---------------------------------------------------------------- */
249
+ searchExpenditures(params, ctx) {
250
+ return this.fetchSeek('/schedules/schedule_e/', params, ctx);
251
+ }
252
+ getExpendituresByCandidate(params, ctx) {
253
+ return this.fetchPage('/schedules/schedule_e/by_candidate/', params, ctx);
254
+ }
255
+ /* ---------------------------------------------------------------- */
256
+ /* Filings */
257
+ /* ---------------------------------------------------------------- */
258
+ searchFilings(params, ctx) {
259
+ return this.fetchPage('/filings/', params, ctx);
260
+ }
261
+ /* ---------------------------------------------------------------- */
262
+ /* Elections */
263
+ /* ---------------------------------------------------------------- */
264
+ searchElections(params, ctx) {
265
+ return this.fetchPage('/elections/', params, ctx);
266
+ }
267
+ /** Fetch election summary — flat response (no pagination wrapper). */
268
+ async getElectionSummary(params, ctx) {
269
+ const url = this.buildUrl('/elections/summary/', params);
270
+ const reqCtx = toRequestContext(ctx);
271
+ try {
272
+ return await withRetry(async () => {
273
+ const response = await fetchWithTimeout(url, this.config.fecRequestTimeout, reqCtx, {
274
+ signal: ctx.signal,
275
+ });
276
+ const body = (await response.json());
277
+ if (typeof body?.count !== 'number') {
278
+ throw new Error('FEC API returned an unexpected response (possible HTML error page)');
279
+ }
280
+ return body;
281
+ }, {
282
+ maxRetries: this.config.fecMaxRetries,
283
+ baseDelayMs: 1_000,
284
+ operation: 'FEC /elections/summary/',
285
+ context: reqCtx,
286
+ signal: ctx.signal,
287
+ isTransient: isTransientFecError,
288
+ });
289
+ }
290
+ catch (err) {
291
+ rethrowSanitized(err);
292
+ }
293
+ }
294
+ /* ---------------------------------------------------------------- */
295
+ /* Legal */
296
+ /* ---------------------------------------------------------------- */
297
+ searchLegal(params, ctx) {
298
+ return this.fetchLegalSearch(params, ctx);
299
+ }
300
+ /* ---------------------------------------------------------------- */
301
+ /* Calendar */
302
+ /* ---------------------------------------------------------------- */
303
+ getCalendarDates(params, ctx) {
304
+ return this.fetchPage('/calendar-dates/', params, ctx);
305
+ }
306
+ getReportingDates(params, ctx) {
307
+ return this.fetchPage('/reporting-dates/', params, ctx);
308
+ }
309
+ getElectionDates(params, ctx) {
310
+ return this.fetchPage('/election-dates/', params, ctx);
311
+ }
312
+ }
313
+ /* ------------------------------------------------------------------ */
314
+ /* Error sanitization */
315
+ /* ------------------------------------------------------------------ */
316
+ /** Strip API key values from error messages to prevent leaking secrets in tool output. */
317
+ function sanitizeErrorMessage(msg) {
318
+ return msg.replace(/api_key=[^&\s"')]+/g, 'api_key=REDACTED');
319
+ }
320
+ /** Map HTTP status codes from upstream FEC API to actionable messages. */
321
+ function enrichStatusError(msg) {
322
+ const statusMatch = msg.match(/Status:\s*(\d{3})/);
323
+ if (!statusMatch)
324
+ return msg;
325
+ const status = Number(statusMatch[1]);
326
+ const hints = {
327
+ 400: 'Bad request — check parameter names and types.',
328
+ 403: 'Forbidden — the API key may be invalid or expired.',
329
+ 404: 'Endpoint not found — verify the API path.',
330
+ 422: 'The FEC API rejected the request parameters. Check required fields and value formats for this endpoint.',
331
+ 429: 'FEC API rate limit exceeded. Wait a moment and retry.',
332
+ 500: 'FEC API internal error. Retry shortly.',
333
+ 502: 'FEC API is temporarily unreachable. Retry shortly.',
334
+ 503: 'FEC API is temporarily unavailable. Retry shortly.',
335
+ };
336
+ const hint = hints[status];
337
+ return hint ? `${sanitizeErrorMessage(msg)} — ${hint}` : sanitizeErrorMessage(msg);
338
+ }
339
+ /**
340
+ * Re-throw an error with its message sanitized (API key stripped)
341
+ * and enriched with actionable context for HTTP status errors.
342
+ * Preserves the original error as `cause` for internal debugging.
343
+ */
344
+ function rethrowSanitized(err) {
345
+ if (err instanceof Error) {
346
+ const clean = new Error(enrichStatusError(err.message), { cause: err });
347
+ clean.name = err.name;
348
+ throw clean;
349
+ }
350
+ throw err;
351
+ }
352
+ /* ------------------------------------------------------------------ */
353
+ /* Transient error classification */
354
+ /* ------------------------------------------------------------------ */
355
+ /** Classify errors as transient for retry purposes. */
356
+ function isTransientFecError(error) {
357
+ if (!error || typeof error !== 'object')
358
+ return false;
359
+ const msg = 'message' in error ? String(error.message) : '';
360
+ if (msg.includes('ServiceUnavailable') || msg.includes('503') || msg.includes('502'))
361
+ return true;
362
+ if (msg.includes('429') || msg.includes('OVER_RATE_LIMIT') || msg.includes('rate limit')) {
363
+ return true;
364
+ }
365
+ if (msg.includes('unexpected response') || msg.includes('HTML error page'))
366
+ return true;
367
+ if (msg.includes('ECONNRESET') || msg.includes('ETIMEDOUT') || msg.includes('fetch failed')) {
368
+ return true;
369
+ }
370
+ return false;
371
+ }
372
+ /* ------------------------------------------------------------------ */
373
+ /* Singleton accessor */
374
+ /* ------------------------------------------------------------------ */
375
+ let _service;
376
+ export function initOpenFecService() {
377
+ _service = new OpenFecService();
378
+ }
379
+ export function getOpenFecService() {
380
+ if (!_service)
381
+ throw new Error('OpenFecService not initialized — call initOpenFecService() in setup()');
382
+ return _service;
383
+ }
384
+ //# sourceMappingURL=openfec-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openfec-service.js","sourceRoot":"","sources":["../../../src/services/openfec/openfec-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @fileoverview Types for OpenFEC API responses and service return values.
3
+ * Covers the three pagination models (page-based, keyset/SEEK, legal search)
4
+ * and normalized result shapes returned by the service layer.
5
+ * @module src/services/openfec/types
6
+ */
7
+ /** Page-based pagination metadata (candidates, committees, filings, elections, calendar). */
8
+ export interface FecPagePagination {
9
+ count: number;
10
+ is_count_exact?: boolean;
11
+ page: number;
12
+ pages: number;
13
+ per_page: number;
14
+ }
15
+ /** Keyset (SEEK) pagination metadata (Schedule A/B/E). */
16
+ export interface FecSeekPagination {
17
+ count: number;
18
+ is_count_exact?: boolean;
19
+ last_indexes?: Record<string, string>;
20
+ per_page: number;
21
+ }
22
+ /** Standard FEC API response envelope with page-based pagination. */
23
+ export interface FecPageEnvelope<T = Record<string, unknown>> {
24
+ api_version: string;
25
+ pagination: FecPagePagination;
26
+ results: T[];
27
+ }
28
+ /** FEC API response envelope with keyset pagination. */
29
+ export interface FecSeekEnvelope<T = Record<string, unknown>> {
30
+ api_version: string;
31
+ pagination: FecSeekPagination;
32
+ results: T[];
33
+ }
34
+ /**
35
+ * Legal search response — type-keyed result arrays instead of a
36
+ * uniform `results` array. Each type has its own array and total count.
37
+ */
38
+ export interface FecLegalEnvelope {
39
+ admin_fines: Record<string, unknown>[];
40
+ adrs: Record<string, unknown>[];
41
+ advisory_opinions: Record<string, unknown>[];
42
+ murs: Record<string, unknown>[];
43
+ statutes: Record<string, unknown>[];
44
+ total_admin_fines: number;
45
+ total_adrs: number;
46
+ total_advisory_opinions: number;
47
+ total_all: number;
48
+ total_murs: number;
49
+ total_statutes: number;
50
+ }
51
+ /** Normalized result from page-based endpoints. */
52
+ export interface PageResult<T = Record<string, unknown>> {
53
+ pagination: {
54
+ page: number;
55
+ pages: number;
56
+ count: number;
57
+ per_page: number;
58
+ };
59
+ results: T[];
60
+ }
61
+ /** Normalized result from keyset (SEEK) endpoints. */
62
+ export interface SeekResult<T = Record<string, unknown>> {
63
+ nextCursor: string | null;
64
+ pagination: {
65
+ count: number;
66
+ per_page: number;
67
+ };
68
+ results: T[];
69
+ }
70
+ /** Normalized legal search result with a flat results array. */
71
+ export interface LegalResult {
72
+ results: Array<Record<string, unknown> & {
73
+ document_type: string;
74
+ }>;
75
+ totalCount: number;
76
+ }
77
+ /** Flat response from the /elections/summary/ endpoint (no pagination wrapper). */
78
+ export interface ElectionSummary {
79
+ count: number;
80
+ disbursements: number;
81
+ independent_expenditures: number;
82
+ receipts: number;
83
+ }
84
+ /** Query params passed to FEC API methods. Undefined values are stripped. Arrays serialize as repeated query params. */
85
+ export type FecParams = Record<string, string | number | boolean | string[] | undefined>;
86
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/openfec/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @fileoverview Types for OpenFEC API responses and service return values.
3
+ * Covers the three pagination models (page-based, keyset/SEEK, legal search)
4
+ * and normalized result shapes returned by the service layer.
5
+ * @module src/services/openfec/types
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/openfec/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@cyanheads/openfec-mcp-server",
3
+ "version": "0.2.2",
4
+ "mcpName": "io.github.cyanheads/openfec-mcp-server",
5
+ "description": "Access FEC campaign finance data through MCP. Query data about candidates, money trails, and election filings. STDIO & Streamable HTTP.",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "openfec-mcp-server": "dist/index.js"
11
+ },
12
+ "files": ["dist/", "README.md", "LICENSE", "CLAUDE.md", "Dockerfile", "server.json"],
13
+ "scripts": {
14
+ "build": "bun run scripts/build.ts",
15
+ "rebuild": "bun run scripts/clean.ts && bun run build",
16
+ "clean": "bun run scripts/clean.ts",
17
+ "devcheck": "bun run scripts/devcheck.ts",
18
+ "tree": "bun run scripts/tree.ts",
19
+ "format": "biome check --write --unsafe",
20
+ "lint:mcp": "bun run scripts/lint-mcp.ts",
21
+ "test": "bunx vitest run",
22
+ "dev:stdio": "MCP_TRANSPORT_TYPE=stdio bun --watch src/index.ts",
23
+ "dev:http": "MCP_TRANSPORT_TYPE=http bun --watch src/index.ts",
24
+ "start:stdio": "MCP_TRANSPORT_TYPE=stdio bun ./dist/index.js",
25
+ "start:http": "MCP_TRANSPORT_TYPE=http bun ./dist/index.js"
26
+ },
27
+ "keywords": [
28
+ "mcp",
29
+ "mcp-server",
30
+ "model-context-protocol",
31
+ "openfec",
32
+ "fec",
33
+ "campaign-finance",
34
+ "elections",
35
+ "federal-elections",
36
+ "political-contributions",
37
+ "typescript",
38
+ "ai-agent"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/cyanheads/openfec-mcp-server.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/cyanheads/openfec-mcp-server/issues"
46
+ },
47
+ "homepage": "https://github.com/cyanheads/openfec-mcp-server#readme",
48
+ "author": "cyanheads <casey@caseyjhand.com> (https://github.com/cyanheads/openfec-mcp-server#readme)",
49
+ "funding": [
50
+ {
51
+ "type": "github",
52
+ "url": "https://github.com/sponsors/cyanheads"
53
+ },
54
+ {
55
+ "type": "buy_me_a_coffee",
56
+ "url": "https://www.buymeacoffee.com/cyanheads"
57
+ }
58
+ ],
59
+ "license": "Apache-2.0",
60
+ "packageManager": "bun@1.3.11",
61
+ "engines": {
62
+ "bun": ">=1.2.0",
63
+ "node": ">=22.0.0"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "overrides": {
69
+ "brace-expansion": ">=2.0.3",
70
+ "express-rate-limit": ">=8.2.2",
71
+ "hono": ">=4.12.7",
72
+ "path-to-regexp": ">=8.4.0",
73
+ "picomatch": ">=4.0.4",
74
+ "yaml": ">=2.8.3",
75
+ "lodash": ">=4.17.24"
76
+ },
77
+ "dependencies": {
78
+ "@cyanheads/mcp-ts-core": "^0.2.12",
79
+ "@opentelemetry/api": "^1.9.1",
80
+ "pino-pretty": "^13.1.3"
81
+ },
82
+ "devDependencies": {
83
+ "@biomejs/biome": "^2.4.10",
84
+ "@types/node": "^25.5.2",
85
+ "depcheck": "^1.4.7",
86
+ "ignore": "^7.0.5",
87
+ "tsc-alias": "^1.8.16",
88
+ "typescript": "^6.0.2",
89
+ "vitest": "^4.1.2"
90
+ }
91
+ }