@cyanheads/congressgov-mcp-server 0.3.32 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +24 -3
- package/CLAUDE.md +24 -3
- package/Dockerfile +13 -0
- package/README.md +15 -4
- package/dist/config/server-config.d.ts +15 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +65 -0
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +30 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-bills.tool.d.ts +47 -0
- package/dist/mcp-server/tools/definitions/search-bills.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-bills.tool.js +99 -0
- package/dist/mcp-server/tools/definitions/search-bills.tool.js.map +1 -0
- package/dist/mcp-server/tools/format-helpers.d.ts +2 -0
- package/dist/mcp-server/tools/format-helpers.d.ts.map +1 -1
- package/dist/mcp-server/tools/format-helpers.js +31 -0
- package/dist/mcp-server/tools/format-helpers.js.map +1 -1
- package/dist/services/congress-mirror/congress-mirror-service.d.ts +45 -0
- package/dist/services/congress-mirror/congress-mirror-service.d.ts.map +1 -0
- package/dist/services/congress-mirror/congress-mirror-service.js +116 -0
- package/dist/services/congress-mirror/congress-mirror-service.js.map +1 -0
- package/dist/services/congress-mirror/ingest.d.ts +33 -0
- package/dist/services/congress-mirror/ingest.d.ts.map +1 -0
- package/dist/services/congress-mirror/ingest.js +179 -0
- package/dist/services/congress-mirror/ingest.js.map +1 -0
- package/dist/services/congress-mirror/normalize.d.ts +25 -0
- package/dist/services/congress-mirror/normalize.d.ts.map +1 -0
- package/dist/services/congress-mirror/normalize.js +47 -0
- package/dist/services/congress-mirror/normalize.js.map +1 -0
- package/dist/services/congress-mirror/schema.d.ts +19 -0
- package/dist/services/congress-mirror/schema.d.ts.map +1 -0
- package/dist/services/congress-mirror/schema.js +44 -0
- package/dist/services/congress-mirror/schema.js.map +1 -0
- package/dist/services/congress-mirror/types.d.ts +55 -0
- package/dist/services/congress-mirror/types.d.ts.map +1 -0
- package/dist/services/congress-mirror/types.js +9 -0
- package/dist/services/congress-mirror/types.js.map +1 -0
- package/package.json +30 -4
- package/scripts/_mirror-context.ts +30 -0
- package/scripts/congress-mirror-init.ts +23 -0
- package/scripts/congress-mirror-refresh.ts +24 -0
- package/scripts/congress-mirror-verify.ts +36 -0
- package/server.json +3 -3
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The Congress bill ingester — the one irreducibly per-source part
|
|
3
|
+
* of the mirror. Merges two upstream Congress.gov sources into one row per bill:
|
|
4
|
+
* the bill list (`/bill/{congress}`, sorted by updateDate) supplies the skeleton
|
|
5
|
+
* (title, chamber, latest action, update date); the CRS summaries list
|
|
6
|
+
* (`/summaries/{congress}`) supplies the summary text. Both reuse the existing
|
|
7
|
+
* `CongressApiService` — no new HTTP-calling code, only merge/orchestration.
|
|
8
|
+
*
|
|
9
|
+
* Merge order matters: the framework upsert writes every declared column for each
|
|
10
|
+
* record, so a partial (summary-only) upsert would null the title. The summaries
|
|
11
|
+
* are therefore loaded into a per-congress map first, then attached as each full
|
|
12
|
+
* bill row is emitted. Summaries are fully re-walked every sync (not date-filtered)
|
|
13
|
+
* so a re-emitted bill never loses a previously-stored summary. The durable
|
|
14
|
+
* checkpoint is the bills' `updateDate` high-water mark; refresh re-scans bills
|
|
15
|
+
* from it inclusively (`sort=updateDate asc`), and the upsert makes the overlap
|
|
16
|
+
* harmless.
|
|
17
|
+
* @module services/congress-mirror/ingest
|
|
18
|
+
*/
|
|
19
|
+
import { logger } from '@cyanheads/mcp-ts-core/utils';
|
|
20
|
+
import { htmlToPlainText } from './normalize.js';
|
|
21
|
+
/** Congress.gov list endpoints cap the page size at 250 (confirmed live). */
|
|
22
|
+
const PAGE_SIZE = 250;
|
|
23
|
+
function isRecord(value) {
|
|
24
|
+
return typeof value === 'object' && value !== null;
|
|
25
|
+
}
|
|
26
|
+
/** Read a non-empty string (or a stringified number) from a raw field. */
|
|
27
|
+
function str(value) {
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
return trimmed === '' ? undefined : trimmed;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
33
|
+
return String(value);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
/** Read an integer from a raw field (number or numeric string). */
|
|
37
|
+
function int(value) {
|
|
38
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
39
|
+
return Math.trunc(value);
|
|
40
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
41
|
+
const parsed = Number.parseInt(value.trim(), 10);
|
|
42
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Derive the composite primary key. Matches the existing
|
|
48
|
+
* `congress://bill/{congress}/{billType}/{billNumber}` resource addressing; the
|
|
49
|
+
* bill type is lowercased so both upstream sources key identically.
|
|
50
|
+
*/
|
|
51
|
+
function deriveBillId(congress, billType, billNumber) {
|
|
52
|
+
return `${congress}/${billType.toLowerCase()}/${billNumber}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The bill list emits date-only `updateDate` ("2026-06-30"); the API's
|
|
56
|
+
* `fromDateTime` filter wants full ISO 8601. Widen a date-only checkpoint to the
|
|
57
|
+
* start of that day so the inclusive re-scan catches same-day updates.
|
|
58
|
+
*/
|
|
59
|
+
function toFromDateTime(checkpoint) {
|
|
60
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(checkpoint) ? `${checkpoint}T00:00:00Z` : checkpoint;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Page the CRS summaries list for one congress into a `billId → plain-text
|
|
64
|
+
* summary` map, keeping the latest version per bill (by summary update/action
|
|
65
|
+
* stamp). Summaries ship as HTML and are stripped to plain text before storage.
|
|
66
|
+
*/
|
|
67
|
+
async function buildSummaryMap(api, congress, signal) {
|
|
68
|
+
const latest = new Map();
|
|
69
|
+
let offset = 0;
|
|
70
|
+
while (!signal.aborted) {
|
|
71
|
+
const result = await api.listSummaries({ congress, limit: PAGE_SIZE, offset });
|
|
72
|
+
const rows = result.data;
|
|
73
|
+
if (rows.length === 0)
|
|
74
|
+
break;
|
|
75
|
+
for (const raw of rows) {
|
|
76
|
+
if (!isRecord(raw) || !isRecord(raw.bill))
|
|
77
|
+
continue;
|
|
78
|
+
const bill = raw.bill;
|
|
79
|
+
const c = int(bill.congress);
|
|
80
|
+
const billType = str(bill.type)?.toLowerCase();
|
|
81
|
+
const billNumber = int(bill.number);
|
|
82
|
+
if (c === undefined || billType === undefined || billNumber === undefined)
|
|
83
|
+
continue;
|
|
84
|
+
const text = typeof raw.text === 'string' ? htmlToPlainText(raw.text) : '';
|
|
85
|
+
if (!text)
|
|
86
|
+
continue;
|
|
87
|
+
const id = deriveBillId(c, billType, billNumber);
|
|
88
|
+
const stamp = str(raw.updateDate) ?? str(raw.actionDate) ?? '';
|
|
89
|
+
const existing = latest.get(id);
|
|
90
|
+
if (!existing || stamp >= existing.stamp)
|
|
91
|
+
latest.set(id, { text, stamp });
|
|
92
|
+
}
|
|
93
|
+
if (rows.length < PAGE_SIZE || result.pagination.nextOffset == null)
|
|
94
|
+
break;
|
|
95
|
+
offset += PAGE_SIZE;
|
|
96
|
+
}
|
|
97
|
+
const map = new Map();
|
|
98
|
+
for (const [id, value] of latest)
|
|
99
|
+
map.set(id, value.text);
|
|
100
|
+
return map;
|
|
101
|
+
}
|
|
102
|
+
/** Map one raw bill-list record to a full mirror row, joining the summary map. */
|
|
103
|
+
function toBillRow(raw, fallbackCongress, summaries) {
|
|
104
|
+
const billType = str(raw.type)?.toLowerCase();
|
|
105
|
+
const billNumber = int(raw.number);
|
|
106
|
+
if (billType === undefined || billNumber === undefined)
|
|
107
|
+
return null;
|
|
108
|
+
const congress = int(raw.congress) ?? fallbackCongress;
|
|
109
|
+
const id = deriveBillId(congress, billType, billNumber);
|
|
110
|
+
const latestAction = isRecord(raw.latestAction) ? raw.latestAction : undefined;
|
|
111
|
+
return {
|
|
112
|
+
billId: id,
|
|
113
|
+
congress,
|
|
114
|
+
billType,
|
|
115
|
+
billNumber,
|
|
116
|
+
title: str(raw.title) ?? null,
|
|
117
|
+
summary: summaries.get(id) ?? null,
|
|
118
|
+
originChamber: str(raw.originChamber) ?? null,
|
|
119
|
+
latestActionDate: latestAction ? (str(latestAction.actionDate) ?? null) : null,
|
|
120
|
+
latestActionText: latestAction ? (str(latestAction.text) ?? null) : null,
|
|
121
|
+
updateDate: str(raw.updateDate) ?? null,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Build the Congress bill ingester. The returned generator walks the configured
|
|
126
|
+
* congresses; for each, it loads the summary map, then streams bill pages
|
|
127
|
+
* (`sort=updateDate asc`) as full merged rows, advancing the durable checkpoint
|
|
128
|
+
* to the max `updateDate` seen.
|
|
129
|
+
*
|
|
130
|
+
* @param getApi - Accessor for the Congress API service (injectable for tests).
|
|
131
|
+
* @param congresses - Congress numbers to mirror.
|
|
132
|
+
*/
|
|
133
|
+
export function createCongressBillIngester(getApi, congresses) {
|
|
134
|
+
return async function* sync({ mode, checkpoint, signal }) {
|
|
135
|
+
const api = getApi();
|
|
136
|
+
const fromDateTime = mode === 'refresh' && checkpoint ? toFromDateTime(checkpoint) : undefined;
|
|
137
|
+
let maxUpdate = checkpoint;
|
|
138
|
+
logger.info(`Congress bill mirror sync starting (mode=${mode}, congresses=${congresses.join(',')}${fromDateTime ? `, since=${fromDateTime}` : ''})`);
|
|
139
|
+
for (const congress of congresses) {
|
|
140
|
+
if (signal.aborted)
|
|
141
|
+
return;
|
|
142
|
+
const summaries = await buildSummaryMap(api, congress, signal);
|
|
143
|
+
logger.info(`Congress ${congress}: ${summaries.size} summaries loaded`);
|
|
144
|
+
let offset = 0;
|
|
145
|
+
let billsMirrored = 0;
|
|
146
|
+
while (!signal.aborted) {
|
|
147
|
+
const result = await api.listBills({
|
|
148
|
+
congress,
|
|
149
|
+
sort: 'updateDate asc',
|
|
150
|
+
limit: PAGE_SIZE,
|
|
151
|
+
offset,
|
|
152
|
+
...(fromDateTime ? { fromDateTime } : {}),
|
|
153
|
+
});
|
|
154
|
+
const rows = result.data;
|
|
155
|
+
if (rows.length === 0)
|
|
156
|
+
break;
|
|
157
|
+
const records = [];
|
|
158
|
+
for (const raw of rows) {
|
|
159
|
+
if (!isRecord(raw))
|
|
160
|
+
continue;
|
|
161
|
+
const record = toBillRow(raw, congress, summaries);
|
|
162
|
+
if (!record)
|
|
163
|
+
continue;
|
|
164
|
+
records.push(record);
|
|
165
|
+
const updated = typeof record.updateDate === 'string' ? record.updateDate : undefined;
|
|
166
|
+
if (updated && (maxUpdate === undefined || updated > maxUpdate))
|
|
167
|
+
maxUpdate = updated;
|
|
168
|
+
}
|
|
169
|
+
billsMirrored += records.length;
|
|
170
|
+
yield { records, ...(maxUpdate ? { checkpoint: maxUpdate } : {}) };
|
|
171
|
+
if (rows.length < PAGE_SIZE || result.pagination.nextOffset == null)
|
|
172
|
+
break;
|
|
173
|
+
offset += PAGE_SIZE;
|
|
174
|
+
}
|
|
175
|
+
logger.info(`Congress ${congress}: ${billsMirrored} bills mirrored`);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure normalization helpers for the bill mirror — HTML→plain-text
|
|
3
|
+
* stripping (summaries ship as HTML; the FTS index and snippet surface want plain
|
|
4
|
+
* text) and FTS5 `MATCH` escaping (tokenize + individually quote so a stray `-`,
|
|
5
|
+
* an unbalanced quote, or an FTS5 reserved word can't break the query). No I/O.
|
|
6
|
+
* @module services/congress-mirror/normalize
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Strip HTML tags to plain text and decode the handful of entities Congress.gov
|
|
10
|
+
* emits in summary bodies. Block-ish tags collapse to spaces so adjacent words
|
|
11
|
+
* don't fuse (`</p><p>` → space), and whitespace is collapsed to single spaces —
|
|
12
|
+
* a discovery/snippet surface doesn't need paragraph structure, and flat text
|
|
13
|
+
* tokenizes more cleanly for FTS5.
|
|
14
|
+
*/
|
|
15
|
+
export declare function htmlToPlainText(html: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build an FTS5 `MATCH` expression from free-text input. Each token is stripped
|
|
18
|
+
* of embedded quotes and wrapped in double quotes, which neutralizes every FTS5
|
|
19
|
+
* operator (`-`, `*`, `:`, `NEAR`, `AND`, `OR`, `NOT`) so punctuation or a
|
|
20
|
+
* reserved word can't raise a SQLite syntax error. Tokens are AND-combined
|
|
21
|
+
* (implicit-AND across terms). Returns an empty string when the input has no
|
|
22
|
+
* searchable tokens — callers treat that as "no match", not a thrown query.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toFtsMatch(input: string): string;
|
|
25
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcpD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure normalization helpers for the bill mirror — HTML→plain-text
|
|
3
|
+
* stripping (summaries ship as HTML; the FTS index and snippet surface want plain
|
|
4
|
+
* text) and FTS5 `MATCH` escaping (tokenize + individually quote so a stray `-`,
|
|
5
|
+
* an unbalanced quote, or an FTS5 reserved word can't break the query). No I/O.
|
|
6
|
+
* @module services/congress-mirror/normalize
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Strip HTML tags to plain text and decode the handful of entities Congress.gov
|
|
10
|
+
* emits in summary bodies. Block-ish tags collapse to spaces so adjacent words
|
|
11
|
+
* don't fuse (`</p><p>` → space), and whitespace is collapsed to single spaces —
|
|
12
|
+
* a discovery/snippet surface doesn't need paragraph structure, and flat text
|
|
13
|
+
* tokenizes more cleanly for FTS5.
|
|
14
|
+
*/
|
|
15
|
+
export function htmlToPlainText(html) {
|
|
16
|
+
return html
|
|
17
|
+
.replace(/<\s*br\s*\/?\s*>/gi, ' ')
|
|
18
|
+
.replace(/<\s*\/?(p|div|li|ul|ol|h[1-6]|tr|table)\b[^>]*>/gi, ' ')
|
|
19
|
+
.replace(/<[^>]*>/g, '')
|
|
20
|
+
.replace(/ /g, ' ')
|
|
21
|
+
.replace(/'/g, "'")
|
|
22
|
+
.replace(/'/g, "'")
|
|
23
|
+
.replace(/&/g, '&')
|
|
24
|
+
.replace(/</g, '<')
|
|
25
|
+
.replace(/>/g, '>')
|
|
26
|
+
.replace(/"/g, '"')
|
|
27
|
+
.replace(/\s+/g, ' ')
|
|
28
|
+
.trim();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build an FTS5 `MATCH` expression from free-text input. Each token is stripped
|
|
32
|
+
* of embedded quotes and wrapped in double quotes, which neutralizes every FTS5
|
|
33
|
+
* operator (`-`, `*`, `:`, `NEAR`, `AND`, `OR`, `NOT`) so punctuation or a
|
|
34
|
+
* reserved word can't raise a SQLite syntax error. Tokens are AND-combined
|
|
35
|
+
* (implicit-AND across terms). Returns an empty string when the input has no
|
|
36
|
+
* searchable tokens — callers treat that as "no match", not a thrown query.
|
|
37
|
+
*/
|
|
38
|
+
export function toFtsMatch(input) {
|
|
39
|
+
return input
|
|
40
|
+
.trim()
|
|
41
|
+
.split(/\s+/)
|
|
42
|
+
.map((t) => t.replace(/"/g, '').trim())
|
|
43
|
+
.filter((t) => t.length > 0)
|
|
44
|
+
.map((t) => `"${t}"`)
|
|
45
|
+
.join(' AND ');
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI;SACR,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC;SAClC,OAAO,CAAC,mDAAmD,EAAE,GAAG,CAAC;SACjE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK;SACT,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;SACtC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;SACpB,IAAI,CAAC,OAAO,CAAC,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Mirror store schema for the Congress bill FTS index. A single
|
|
3
|
+
* MirrorService-owned table (`bills`) with an FTS5 index over `title` + `summary`
|
|
4
|
+
* and secondary indexes on the filterable columns. The primary key is the derived
|
|
5
|
+
* `billId` (`{congress}/{billType}/{billNumber}`) — a single column, matching the
|
|
6
|
+
* `congress://bill/{congress}/{billType}/{billNumber}` resource addressing.
|
|
7
|
+
* @module services/congress-mirror/schema
|
|
8
|
+
*/
|
|
9
|
+
import type { SqliteMirrorStoreSpec } from '@cyanheads/mcp-ts-core/mirror';
|
|
10
|
+
/** Primary table name (the MirrorService-owned table). */
|
|
11
|
+
export declare const BILLS_TABLE = "bills";
|
|
12
|
+
/**
|
|
13
|
+
* The MirrorService primary-table spec. One row per bill, merged from the bill
|
|
14
|
+
* list (title, chamber, latest action, update date) and the CRS summaries list
|
|
15
|
+
* (summary text). `title` + `summary` are FTS-indexed; `congress`, `billType`,
|
|
16
|
+
* and `originChamber` back the exact-match search filters.
|
|
17
|
+
*/
|
|
18
|
+
export declare function billStoreSpec(path: string): SqliteMirrorStoreSpec;
|
|
19
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAE3E,0DAA0D;AAC1D,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CA0BjE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Mirror store schema for the Congress bill FTS index. A single
|
|
3
|
+
* MirrorService-owned table (`bills`) with an FTS5 index over `title` + `summary`
|
|
4
|
+
* and secondary indexes on the filterable columns. The primary key is the derived
|
|
5
|
+
* `billId` (`{congress}/{billType}/{billNumber}`) — a single column, matching the
|
|
6
|
+
* `congress://bill/{congress}/{billType}/{billNumber}` resource addressing.
|
|
7
|
+
* @module services/congress-mirror/schema
|
|
8
|
+
*/
|
|
9
|
+
/** Primary table name (the MirrorService-owned table). */
|
|
10
|
+
export const BILLS_TABLE = 'bills';
|
|
11
|
+
/**
|
|
12
|
+
* The MirrorService primary-table spec. One row per bill, merged from the bill
|
|
13
|
+
* list (title, chamber, latest action, update date) and the CRS summaries list
|
|
14
|
+
* (summary text). `title` + `summary` are FTS-indexed; `congress`, `billType`,
|
|
15
|
+
* and `originChamber` back the exact-match search filters.
|
|
16
|
+
*/
|
|
17
|
+
export function billStoreSpec(path) {
|
|
18
|
+
return {
|
|
19
|
+
path,
|
|
20
|
+
table: BILLS_TABLE,
|
|
21
|
+
primaryKey: 'billId',
|
|
22
|
+
version: 1,
|
|
23
|
+
columns: {
|
|
24
|
+
billId: 'TEXT',
|
|
25
|
+
congress: 'INTEGER',
|
|
26
|
+
billType: 'TEXT',
|
|
27
|
+
billNumber: 'INTEGER',
|
|
28
|
+
title: 'TEXT',
|
|
29
|
+
summary: 'TEXT',
|
|
30
|
+
originChamber: 'TEXT',
|
|
31
|
+
latestActionDate: 'TEXT',
|
|
32
|
+
latestActionText: 'TEXT',
|
|
33
|
+
updateDate: 'TEXT',
|
|
34
|
+
},
|
|
35
|
+
fts: ['title', 'summary'],
|
|
36
|
+
indexes: [
|
|
37
|
+
{ columns: ['congress'] },
|
|
38
|
+
{ columns: ['billType'] },
|
|
39
|
+
{ columns: ['originChamber'] },
|
|
40
|
+
{ columns: ['updateDate'] },
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,WAAW;QAClB,UAAU,EAAE,QAAQ;QACpB,OAAO,EAAE,CAAC;QACV,OAAO,EAAE;YACP,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,SAAS;YACrB,KAAK,EAAE,MAAM;YACb,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,MAAM;YACrB,gBAAgB,EAAE,MAAM;YACxB,gBAAgB,EAAE,MAAM;YACxB,UAAU,EAAE,MAAM;SACnB;QACD,GAAG,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;QACzB,OAAO,EAAE;YACP,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE;YACzB,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE;YACzB,EAAE,OAAO,EAAE,CAAC,eAAe,CAAC,EAAE;YAC9B,EAAE,OAAO,EAAE,CAAC,YAAY,CAAC,EAAE;SAC5B;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Domain types for the Congress bill FTS mirror — the merged row
|
|
3
|
+
* shape, the search filters, and the search page envelope. The mirror row is the
|
|
4
|
+
* single-table projection of two upstream Congress.gov sources (the bill list and
|
|
5
|
+
* the CRS summaries list) keyed by a derived `billId`.
|
|
6
|
+
* @module services/congress-mirror/types
|
|
7
|
+
*/
|
|
8
|
+
import type { BillType } from '../../services/congress-api/types.js';
|
|
9
|
+
/**
|
|
10
|
+
* One mirrored bill — a flat row keyed by the derived `billId`
|
|
11
|
+
* (`{congress}/{billType}/{billNumber}`). `title` comes from the bill list;
|
|
12
|
+
* `summary` from the CRS summaries list (HTML-stripped, latest version), nullable
|
|
13
|
+
* because CRS summarizes only a fraction of bills.
|
|
14
|
+
*/
|
|
15
|
+
export interface BillMirrorRow {
|
|
16
|
+
billId: string;
|
|
17
|
+
billNumber: number;
|
|
18
|
+
billType: string;
|
|
19
|
+
congress: number;
|
|
20
|
+
latestActionDate: string | null;
|
|
21
|
+
latestActionText: string | null;
|
|
22
|
+
originChamber: string | null;
|
|
23
|
+
summary: string | null;
|
|
24
|
+
title: string | null;
|
|
25
|
+
updateDate: string | null;
|
|
26
|
+
}
|
|
27
|
+
/** Filters accepted by the keyword search. */
|
|
28
|
+
export interface SearchBillsFilters {
|
|
29
|
+
billType?: BillType | undefined;
|
|
30
|
+
congress?: number | undefined;
|
|
31
|
+
limit: number;
|
|
32
|
+
offset: number;
|
|
33
|
+
originChamber?: string | undefined;
|
|
34
|
+
query: string;
|
|
35
|
+
}
|
|
36
|
+
/** A single search result — a merged mirror row projected for display. */
|
|
37
|
+
export interface SearchBillResult {
|
|
38
|
+
billId: string;
|
|
39
|
+
billNumber: number;
|
|
40
|
+
billType: string;
|
|
41
|
+
congress: number;
|
|
42
|
+
latestActionDate?: string;
|
|
43
|
+
latestActionText?: string;
|
|
44
|
+
originChamber?: string;
|
|
45
|
+
summaryPreview?: string;
|
|
46
|
+
title: string;
|
|
47
|
+
}
|
|
48
|
+
/** Search page returned by the mirror service. */
|
|
49
|
+
export interface SearchBillsPage {
|
|
50
|
+
items: SearchBillResult[];
|
|
51
|
+
limit: number;
|
|
52
|
+
offset: number;
|
|
53
|
+
total: number;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,8CAA8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Domain types for the Congress bill FTS mirror — the merged row
|
|
3
|
+
* shape, the search filters, and the search page envelope. The mirror row is the
|
|
4
|
+
* single-table projection of two upstream Congress.gov sources (the bill list and
|
|
5
|
+
* the CRS summaries list) keyed by a derived `billId`.
|
|
6
|
+
* @module services/congress-mirror/types
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/congressgov-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Access U.S. congressional data - bills, votes, members, committees - through MCP. STDIO & Streamable HTTP.",
|
|
5
5
|
"mcpName": "io.github.cyanheads/congressgov-mcp-server",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,19 @@
|
|
|
9
9
|
"bin": {
|
|
10
10
|
"congressgov-mcp-server": "dist/index.js"
|
|
11
11
|
},
|
|
12
|
-
"files": [
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"scripts/_mirror-context.ts",
|
|
15
|
+
"scripts/congress-mirror-init.ts",
|
|
16
|
+
"scripts/congress-mirror-refresh.ts",
|
|
17
|
+
"scripts/congress-mirror-verify.ts",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"CLAUDE.md",
|
|
21
|
+
"AGENTS.md",
|
|
22
|
+
"Dockerfile",
|
|
23
|
+
"server.json"
|
|
24
|
+
],
|
|
13
25
|
"scripts": {
|
|
14
26
|
"build": "bun scripts/build.ts",
|
|
15
27
|
"rebuild": "bun scripts/clean.ts && bun scripts/build.ts",
|
|
@@ -31,7 +43,10 @@
|
|
|
31
43
|
"dev:stdio": "MCP_TRANSPORT_TYPE=stdio bun --watch src/index.ts",
|
|
32
44
|
"dev:http": "MCP_TRANSPORT_TYPE=http bun --watch src/index.ts",
|
|
33
45
|
"start:stdio": "MCP_TRANSPORT_TYPE=stdio node dist/index.js",
|
|
34
|
-
"start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js"
|
|
46
|
+
"start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js",
|
|
47
|
+
"mirror:init": "bun run scripts/congress-mirror-init.ts",
|
|
48
|
+
"mirror:refresh": "bun run scripts/congress-mirror-refresh.ts",
|
|
49
|
+
"mirror:verify": "bun run scripts/congress-mirror-verify.ts"
|
|
35
50
|
},
|
|
36
51
|
"keywords": [
|
|
37
52
|
"mcp",
|
|
@@ -79,16 +94,27 @@
|
|
|
79
94
|
"dependencies": {
|
|
80
95
|
"@cyanheads/mcp-ts-core": "^0.10.10",
|
|
81
96
|
"fast-xml-parser": "^5.9.3",
|
|
97
|
+
"node-cron": "^4.4.1",
|
|
82
98
|
"pino-pretty": "^13.1.3",
|
|
83
99
|
"zod": "^4.4.3"
|
|
84
100
|
},
|
|
101
|
+
"peerDependencies": {
|
|
102
|
+
"better-sqlite3": "^12.0.0"
|
|
103
|
+
},
|
|
104
|
+
"peerDependenciesMeta": {
|
|
105
|
+
"better-sqlite3": {
|
|
106
|
+
"optional": true
|
|
107
|
+
}
|
|
108
|
+
},
|
|
85
109
|
"devDependencies": {
|
|
86
110
|
"@biomejs/biome": "^2.5.1",
|
|
87
111
|
"@types/node": "^26.0.1",
|
|
112
|
+
"better-sqlite3": "^12.11.1",
|
|
88
113
|
"depcheck": "^1.4.7",
|
|
89
114
|
"ignore": "^7.0.5",
|
|
90
115
|
"tsc-alias": "^1.8.17",
|
|
91
116
|
"typescript": "^6.0.3",
|
|
92
117
|
"vitest": "^4.1.9"
|
|
93
|
-
}
|
|
118
|
+
},
|
|
119
|
+
"trustedDependencies": ["better-sqlite3"]
|
|
94
120
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared bootstrap for the mirror lifecycle CLI scripts
|
|
3
|
+
* (congress-mirror-init / congress-mirror-refresh / congress-mirror-verify).
|
|
4
|
+
* Builds the Congress API client and the mirror service from the environment and
|
|
5
|
+
* hands back the underlying mirror — no MCP transport, no tool registration.
|
|
6
|
+
* Imported by the three named scripts, so it must travel with them in the npm
|
|
7
|
+
* tarball and the Docker image.
|
|
8
|
+
* @module scripts/_mirror-context
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Mirror } from '@cyanheads/mcp-ts-core/mirror';
|
|
12
|
+
import { getServerConfig } from '@/config/server-config.js';
|
|
13
|
+
import { initCongressApi } from '@/services/congress-api/congress-api-service.js';
|
|
14
|
+
import { initCongressMirror } from '@/services/congress-mirror/congress-mirror-service.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Build the mirror from env config and return its instance. The CLI builds the
|
|
18
|
+
* mirror regardless of `CONGRESS_MIRROR_ENABLED` (that flag gates the tool and the
|
|
19
|
+
* in-process scheduler, not out-of-band index construction); the ingester pages
|
|
20
|
+
* the live Congress.gov API via `CongressApiService`, so init it here.
|
|
21
|
+
*/
|
|
22
|
+
export function getMirror(): Mirror {
|
|
23
|
+
const config = getServerConfig();
|
|
24
|
+
initCongressApi();
|
|
25
|
+
const service = initCongressMirror({
|
|
26
|
+
mirrorPath: config.mirrorPath,
|
|
27
|
+
congresses: config.congresses,
|
|
28
|
+
});
|
|
29
|
+
return service.mirrorInstance;
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview mirror:init — full out-of-band build of the Congress bill FTS
|
|
4
|
+
* mirror. Pages the bill list and CRS summaries for the configured congress
|
|
5
|
+
* window, merges them into one row per bill, and writes the SQLite index.
|
|
6
|
+
* Idempotent (the framework upserts by primary key) — safe to re-run after an
|
|
7
|
+
* interrupt. Run as a one-shot job or at Docker build, never on server startup.
|
|
8
|
+
* @module scripts/congress-mirror-init
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { logger } from '@cyanheads/mcp-ts-core/utils';
|
|
12
|
+
import { getMirror } from './_mirror-context.js';
|
|
13
|
+
|
|
14
|
+
const mirror = getMirror();
|
|
15
|
+
logger.info('Starting Congress bill mirror init (full build)');
|
|
16
|
+
|
|
17
|
+
const result = await mirror.runSync({ mode: 'init', signal: AbortSignal.timeout(3_600_000) });
|
|
18
|
+
|
|
19
|
+
logger.info(
|
|
20
|
+
`Congress bill mirror init complete: ${result.recordsApplied} records applied across ${result.pagesFetched} pages (total ${result.total}).`,
|
|
21
|
+
);
|
|
22
|
+
await mirror.close();
|
|
23
|
+
process.exit(0);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview mirror:refresh — incremental refresh of the Congress bill FTS
|
|
4
|
+
* mirror. Re-scans bills updated since the durable checkpoint and re-walks the
|
|
5
|
+
* CRS summaries, merging them into the existing index; the index stays
|
|
6
|
+
* transactionally queryable throughout. Run out-of-band for stdio deployments
|
|
7
|
+
* (the HTTP server schedules this on a cron when CONGRESS_MIRROR_REFRESH_CRON is
|
|
8
|
+
* set).
|
|
9
|
+
* @module scripts/congress-mirror-refresh
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { logger } from '@cyanheads/mcp-ts-core/utils';
|
|
13
|
+
import { getMirror } from './_mirror-context.js';
|
|
14
|
+
|
|
15
|
+
const mirror = getMirror();
|
|
16
|
+
logger.info('Starting Congress bill mirror refresh');
|
|
17
|
+
|
|
18
|
+
const result = await mirror.runSync({ mode: 'refresh', signal: AbortSignal.timeout(3_600_000) });
|
|
19
|
+
|
|
20
|
+
logger.info(
|
|
21
|
+
`Congress bill mirror refresh complete: ${result.recordsApplied} records applied (total ${result.total}).`,
|
|
22
|
+
);
|
|
23
|
+
await mirror.close();
|
|
24
|
+
process.exit(0);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview mirror:verify — report the Congress bill mirror health: readiness,
|
|
4
|
+
* sync status, last completion, record count, and a SQLite integrity check. Exits
|
|
5
|
+
* non-zero when the mirror is not ready or integrity fails, so it can gate a
|
|
6
|
+
* deployment or a post-build smoke check.
|
|
7
|
+
* @module scripts/congress-mirror-verify
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { logger } from '@cyanheads/mcp-ts-core/utils';
|
|
11
|
+
import { getMirror } from './_mirror-context.js';
|
|
12
|
+
|
|
13
|
+
const mirror = getMirror();
|
|
14
|
+
const status = await mirror.status();
|
|
15
|
+
|
|
16
|
+
logger.info(
|
|
17
|
+
`Congress bill mirror status: ready=${status.ready}, status=${status.status}, total=${status.total ?? 0}, completedAt=${status.completedAt ?? 'never'}`,
|
|
18
|
+
);
|
|
19
|
+
if (status.error) logger.warning(`Last sync error: ${status.error}`);
|
|
20
|
+
|
|
21
|
+
if (!status.ready) {
|
|
22
|
+
logger.error('Mirror is NOT ready — run mirror:init to build the index.');
|
|
23
|
+
await mirror.close();
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const integrity = await mirror.store.integrityCheck();
|
|
28
|
+
if (!integrity.ok) {
|
|
29
|
+
logger.error(`SQLite integrity check FAILED: ${integrity.results.join('; ')}`);
|
|
30
|
+
await mirror.close();
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
logger.info('Congress bill mirror integrity check passed.');
|
|
35
|
+
await mirror.close();
|
|
36
|
+
process.exit(0);
|
package/server.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/cyanheads/congressgov-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.4.0",
|
|
10
10
|
"remotes": [
|
|
11
11
|
{
|
|
12
12
|
"type": "streamable-http",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
20
20
|
"identifier": "@cyanheads/congressgov-mcp-server",
|
|
21
21
|
"runtimeHint": "bun",
|
|
22
|
-
"version": "0.
|
|
22
|
+
"version": "0.4.0",
|
|
23
23
|
"packageArguments": [
|
|
24
24
|
{
|
|
25
25
|
"type": "positional",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
55
55
|
"identifier": "@cyanheads/congressgov-mcp-server",
|
|
56
56
|
"runtimeHint": "bun",
|
|
57
|
-
"version": "0.
|
|
57
|
+
"version": "0.4.0",
|
|
58
58
|
"packageArguments": [
|
|
59
59
|
{
|
|
60
60
|
"type": "positional",
|