legismcp 0.3.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/README.md +69 -0
- package/bin/legismcp.js +2 -0
- package/dist/services/CongressApiService.d.ts +128 -0
- package/dist/services/CongressApiService.d.ts.map +1 -0
- package/dist/services/CongressApiService.js +355 -0
- package/dist/services/CongressApiService.js.map +1 -0
- package/dist/services/RateLimitService.d.ts +38 -0
- package/dist/services/RateLimitService.d.ts.map +1 -0
- package/dist/services/RateLimitService.js +57 -0
- package/dist/services/RateLimitService.js.map +1 -0
- package/dist/stdio.d.ts +3 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +39 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tools/analysis/billAnalysisParams.d.ts +11 -0
- package/dist/tools/analysis/billAnalysisParams.d.ts.map +1 -0
- package/dist/tools/analysis/billAnalysisParams.js +21 -0
- package/dist/tools/analysis/billAnalysisParams.js.map +1 -0
- package/dist/tools/analysis/billAnalysisTool.d.ts +9 -0
- package/dist/tools/analysis/billAnalysisTool.d.ts.map +1 -0
- package/dist/tools/analysis/billAnalysisTool.js +9 -0
- package/dist/tools/analysis/billAnalysisTool.js.map +1 -0
- package/dist/tools/analysis/enhancedBillAnalysisTool.d.ts +17 -0
- package/dist/tools/analysis/enhancedBillAnalysisTool.d.ts.map +1 -0
- package/dist/tools/analysis/enhancedBillAnalysisTool.js +202 -0
- package/dist/tools/analysis/enhancedBillAnalysisTool.js.map +1 -0
- package/dist/tools/bills/getBill.d.ts +13 -0
- package/dist/tools/bills/getBill.d.ts.map +1 -0
- package/dist/tools/bills/getBill.js +87 -0
- package/dist/tools/bills/getBill.js.map +1 -0
- package/dist/tools/bills/listRecentBills.d.ts +14 -0
- package/dist/tools/bills/listRecentBills.d.ts.map +1 -0
- package/dist/tools/bills/listRecentBills.js +83 -0
- package/dist/tools/bills/listRecentBills.js.map +1 -0
- package/dist/tools/errors/index.d.ts +25 -0
- package/dist/tools/errors/index.d.ts.map +1 -0
- package/dist/tools/errors/index.js +43 -0
- package/dist/tools/errors/index.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/members/memberDetails.d.ts +47 -0
- package/dist/tools/members/memberDetails.d.ts.map +1 -0
- package/dist/tools/members/memberDetails.js +173 -0
- package/dist/tools/members/memberDetails.js.map +1 -0
- package/dist/tools/members/memberSearch.d.ts +78 -0
- package/dist/tools/members/memberSearch.d.ts.map +1 -0
- package/dist/tools/members/memberSearch.js +244 -0
- package/dist/tools/members/memberSearch.js.map +1 -0
- package/dist/tools/subresource/subresourceTool.d.ts +56 -0
- package/dist/tools/subresource/subresourceTool.d.ts.map +1 -0
- package/dist/tools/subresource/subresourceTool.js +209 -0
- package/dist/tools/subresource/subresourceTool.js.map +1 -0
- package/dist/tools/trending/trendingBillsTool.d.ts +44 -0
- package/dist/tools/trending/trendingBillsTool.d.ts.map +1 -0
- package/dist/tools/trending/trendingBillsTool.js +140 -0
- package/dist/tools/trending/trendingBillsTool.js.map +1 -0
- package/dist/utils/congress.d.ts +6 -0
- package/dist/utils/congress.d.ts.map +1 -0
- package/dist/utils/congress.js +14 -0
- package/dist/utils/congress.js.map +1 -0
- package/dist/utils/errors.d.ts +66 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +105 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/member.d.ts +12 -0
- package/dist/utils/member.d.ts.map +1 -0
- package/dist/utils/member.js +35 -0
- package/dist/utils/member.js.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# LegisMCP - Open-Source Legislative MCP Server
|
|
2
|
+
|
|
3
|
+
An open-source [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that provides AI agents with real-time access to U.S. legislative data from [Congress.gov](https://congress.gov).
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
A **Congress.gov API key is required**. Get a free one at [api.congress.gov/sign-up](https://api.congress.gov/sign-up/).
|
|
8
|
+
|
|
9
|
+
### Claude Desktop / Cursor / Windsurf
|
|
10
|
+
|
|
11
|
+
Add to your MCP configuration:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"legismcp": {
|
|
17
|
+
"command": "npx",
|
|
18
|
+
"args": ["-y", "legismcp"],
|
|
19
|
+
"env": {
|
|
20
|
+
"CONGRESS_API_KEY": "your-api-key"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That's it — no cloning or installing required.
|
|
28
|
+
|
|
29
|
+
## Available Tools
|
|
30
|
+
|
|
31
|
+
| Tool | Description |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| `get-bill` | Get detailed information about a specific bill |
|
|
34
|
+
| `list-recent-bills` | List recent bills with filtering by congress, sort, and pagination |
|
|
35
|
+
| `analyze-bill` | Comprehensive analysis of a bill's content, impact, and implications |
|
|
36
|
+
| `trending-bills` | Get trending legislative activity by timeframe and category |
|
|
37
|
+
| `member-details` | Get details about a specific member of Congress by bioguide ID |
|
|
38
|
+
| `member-search` | Search for members by name, state, or party |
|
|
39
|
+
| `subresource` | Access bill sub-resources (actions, amendments, cosponsors, etc.) |
|
|
40
|
+
|
|
41
|
+
## Example Queries
|
|
42
|
+
|
|
43
|
+
Once connected, ask your AI agent:
|
|
44
|
+
|
|
45
|
+
- "What bills were introduced this week about healthcare?"
|
|
46
|
+
- "Show me the details of HR 1234 from the 118th Congress"
|
|
47
|
+
- "Who are the senators from California?"
|
|
48
|
+
- "What's trending in Congress right now?"
|
|
49
|
+
- "Analyze the CHIPS Act and its economic implications"
|
|
50
|
+
|
|
51
|
+
## Environment Variables
|
|
52
|
+
|
|
53
|
+
| Variable | Required | Description |
|
|
54
|
+
|----------|----------|-------------|
|
|
55
|
+
| `CONGRESS_API_KEY` | **Yes** | Congress.gov API key ([get one free](https://api.congress.gov/sign-up/)) |
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
AI Agent <-> MCP Server <-> Congress.gov API
|
|
61
|
+
(stdio)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- Runs as a local Node.js process, communicates via stdin/stdout
|
|
65
|
+
- Data source: [Congress.gov API](https://api.congress.gov/)
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
MIT - See [LICENSE](../LICENSE)
|
package/bin/legismcp.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { RateLimitService } from './RateLimitService.js';
|
|
2
|
+
export interface BillResourceParams {
|
|
3
|
+
congress: string;
|
|
4
|
+
billType: string;
|
|
5
|
+
billNumber: string;
|
|
6
|
+
}
|
|
7
|
+
export interface MemberResourceParams {
|
|
8
|
+
memberId: string;
|
|
9
|
+
congress?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CongressResourceParams {
|
|
12
|
+
congress: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CommitteeResourceParams {
|
|
15
|
+
chamber: string;
|
|
16
|
+
committeeCode: string;
|
|
17
|
+
congress?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AmendmentResourceParams {
|
|
20
|
+
congress: string;
|
|
21
|
+
amendmentType: string;
|
|
22
|
+
amendmentNumber: string;
|
|
23
|
+
}
|
|
24
|
+
export interface SearchParams {
|
|
25
|
+
query?: string;
|
|
26
|
+
limit?: number;
|
|
27
|
+
offset?: number;
|
|
28
|
+
sort?: string;
|
|
29
|
+
filters?: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
export interface ApiResponse<T> {
|
|
32
|
+
data: T;
|
|
33
|
+
pagination?: {
|
|
34
|
+
count: number;
|
|
35
|
+
next?: string;
|
|
36
|
+
previous?: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Comprehensive Congress.gov API Service for CloudFlare Workers
|
|
41
|
+
* Provides a unified interface for accessing legislative data
|
|
42
|
+
*/
|
|
43
|
+
export declare class CongressApiService {
|
|
44
|
+
private baseUrl;
|
|
45
|
+
private apiKeys;
|
|
46
|
+
private keyIndex;
|
|
47
|
+
private keyCooldowns;
|
|
48
|
+
private static readonly COOLDOWN_MS;
|
|
49
|
+
private rateLimiter;
|
|
50
|
+
private timeout;
|
|
51
|
+
constructor(baseUrl?: string, apiKey?: string, rateLimiter?: RateLimitService, timeout?: number);
|
|
52
|
+
/**
|
|
53
|
+
* Get the next available API key via round-robin, skipping cooled-down keys
|
|
54
|
+
*/
|
|
55
|
+
private getNextKey;
|
|
56
|
+
/**
|
|
57
|
+
* Mark the most recently used key as rate-limited
|
|
58
|
+
*/
|
|
59
|
+
private cooldownCurrentKey;
|
|
60
|
+
/**
|
|
61
|
+
* Make authenticated API request with rate limiting
|
|
62
|
+
*/
|
|
63
|
+
private makeRequest;
|
|
64
|
+
/**
|
|
65
|
+
* Get specific bill details
|
|
66
|
+
*/
|
|
67
|
+
getBillDetails(params: BillResourceParams): Promise<any>;
|
|
68
|
+
/**
|
|
69
|
+
* Get specific member details
|
|
70
|
+
*/
|
|
71
|
+
getMemberDetails(params: MemberResourceParams): Promise<any>;
|
|
72
|
+
/**
|
|
73
|
+
* Get congress details
|
|
74
|
+
*/
|
|
75
|
+
getCongressDetails(params: CongressResourceParams): Promise<any>;
|
|
76
|
+
/**
|
|
77
|
+
* Get committee details
|
|
78
|
+
*/
|
|
79
|
+
getCommitteeDetails(params: CommitteeResourceParams): Promise<any>;
|
|
80
|
+
/**
|
|
81
|
+
* Get amendment details
|
|
82
|
+
*/
|
|
83
|
+
getAmendmentDetails(params: AmendmentResourceParams): Promise<any>;
|
|
84
|
+
/**
|
|
85
|
+
* Search across collections with comprehensive parameter support
|
|
86
|
+
*/
|
|
87
|
+
searchCollection(collection: string, params?: SearchParams): Promise<any>;
|
|
88
|
+
/**
|
|
89
|
+
* Get sub-resource data (e.g., bill actions, cosponsors, etc.)
|
|
90
|
+
*/
|
|
91
|
+
getSubResource(parentUri: string, subResource: string, params?: {
|
|
92
|
+
limit?: number;
|
|
93
|
+
offset?: number;
|
|
94
|
+
}): Promise<any>;
|
|
95
|
+
/**
|
|
96
|
+
* Parse congress-gov:// URI format
|
|
97
|
+
*/
|
|
98
|
+
private parseCongressUri;
|
|
99
|
+
/**
|
|
100
|
+
* Get supported collections
|
|
101
|
+
*/
|
|
102
|
+
getSupportedCollections(): string[];
|
|
103
|
+
/**
|
|
104
|
+
* Get supported parameters for a collection
|
|
105
|
+
*/
|
|
106
|
+
getSupportedParameters(collection: string): string[];
|
|
107
|
+
/**
|
|
108
|
+
* Check if collection supports query search
|
|
109
|
+
*/
|
|
110
|
+
supportsQuerySearch(collection: string): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Check if collection supports sorting
|
|
113
|
+
*/
|
|
114
|
+
supportsSorting(collection: string): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Check if collection supports filtering
|
|
117
|
+
*/
|
|
118
|
+
supportsFiltering(collection: string): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Get current rate limit status
|
|
121
|
+
*/
|
|
122
|
+
getRateLimitStatus(): {
|
|
123
|
+
canMakeRequest: boolean;
|
|
124
|
+
resetTime?: Date;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export declare function createCongressApiService(baseUrl?: string, apiKey?: string, rateLimiter?: RateLimitService, timeout?: number): CongressApiService;
|
|
128
|
+
//# sourceMappingURL=CongressApiService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CongressApiService.d.ts","sourceRoot":"","sources":["../../src/services/CongressApiService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { RateLimitService } from './RateLimitService.js';
|
|
2
|
+
import { ApiError, NotFoundError, RateLimitError, ValidationError, InvalidParameterError } from '../utils/errors.js';
|
|
3
|
+
// Configuration for supported query parameters per collection
|
|
4
|
+
const SUPPORTED_QUERY_PARAMS = {
|
|
5
|
+
'bill': ['fromDateTime', 'toDateTime', 'sort', 'congress'],
|
|
6
|
+
'amendment': ['fromDateTime', 'toDateTime', 'sort', 'congress'],
|
|
7
|
+
'member': ['fromDateTime', 'toDateTime', 'currentMember', 'congress'],
|
|
8
|
+
'committee': ['fromDateTime', 'toDateTime', 'chamber', 'congress'],
|
|
9
|
+
'house-communication': ['fromDateTime', 'toDateTime', 'congress'],
|
|
10
|
+
'senate-communication': ['fromDateTime', 'toDateTime', 'congress'],
|
|
11
|
+
'nomination': ['fromDateTime', 'toDateTime', 'congress'],
|
|
12
|
+
'treaty': ['fromDateTime', 'toDateTime', 'congress'],
|
|
13
|
+
'house-requirement': ['fromDateTime', 'toDateTime', 'congress'],
|
|
14
|
+
'senate-requirement': ['fromDateTime', 'toDateTime', 'congress']
|
|
15
|
+
};
|
|
16
|
+
// Collections that support filtering
|
|
17
|
+
const FILTER_SUPPORTED_COLLECTIONS = [
|
|
18
|
+
'bill', 'amendment', 'member', 'committee', 'nomination', 'treaty'
|
|
19
|
+
];
|
|
20
|
+
// Collections that support sorting
|
|
21
|
+
const SORT_SUPPORTED_COLLECTIONS = [
|
|
22
|
+
'bill', 'amendment', 'member', 'committee'
|
|
23
|
+
];
|
|
24
|
+
// Collections that support query search
|
|
25
|
+
const QUERY_SUPPORTED_COLLECTIONS = [
|
|
26
|
+
'bill', 'amendment', 'member', 'committee', 'nomination'
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Comprehensive Congress.gov API Service for CloudFlare Workers
|
|
30
|
+
* Provides a unified interface for accessing legislative data
|
|
31
|
+
*/
|
|
32
|
+
export class CongressApiService {
|
|
33
|
+
constructor(baseUrl = 'https://api.congress.gov/v3', apiKey, rateLimiter, timeout = 30000) {
|
|
34
|
+
this.keyIndex = 0;
|
|
35
|
+
this.keyCooldowns = new Map(); // index -> cooldown expiry timestamp
|
|
36
|
+
this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
37
|
+
// Support comma-separated keys for rotation
|
|
38
|
+
this.apiKeys = apiKey
|
|
39
|
+
? apiKey.split(',').map(k => k.trim()).filter(Boolean)
|
|
40
|
+
: [];
|
|
41
|
+
this.rateLimiter = rateLimiter || new RateLimitService();
|
|
42
|
+
this.timeout = timeout;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the next available API key via round-robin, skipping cooled-down keys
|
|
46
|
+
*/
|
|
47
|
+
getNextKey() {
|
|
48
|
+
if (this.apiKeys.length === 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
const totalKeys = this.apiKeys.length;
|
|
52
|
+
// Try each key starting from current index
|
|
53
|
+
for (let i = 0; i < totalKeys; i++) {
|
|
54
|
+
const idx = (this.keyIndex + i) % totalKeys;
|
|
55
|
+
const cooldownExpiry = this.keyCooldowns.get(idx);
|
|
56
|
+
if (!cooldownExpiry || now >= cooldownExpiry) {
|
|
57
|
+
this.keyCooldowns.delete(idx);
|
|
58
|
+
this.keyIndex = (idx + 1) % totalKeys;
|
|
59
|
+
return this.apiKeys[idx];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// All keys on cooldown — use the one that expires soonest
|
|
63
|
+
let soonestIdx = 0;
|
|
64
|
+
let soonestTime = Infinity;
|
|
65
|
+
for (const [idx, expiry] of this.keyCooldowns) {
|
|
66
|
+
if (expiry < soonestTime) {
|
|
67
|
+
soonestTime = expiry;
|
|
68
|
+
soonestIdx = idx;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.keyIndex = (soonestIdx + 1) % totalKeys;
|
|
72
|
+
return this.apiKeys[soonestIdx];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Mark the most recently used key as rate-limited
|
|
76
|
+
*/
|
|
77
|
+
cooldownCurrentKey() {
|
|
78
|
+
if (this.apiKeys.length === 0)
|
|
79
|
+
return;
|
|
80
|
+
const usedIdx = (this.keyIndex - 1 + this.apiKeys.length) % this.apiKeys.length;
|
|
81
|
+
this.keyCooldowns.set(usedIdx, Date.now() + CongressApiService.COOLDOWN_MS);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Make authenticated API request with rate limiting
|
|
85
|
+
*/
|
|
86
|
+
async makeRequest(endpoint, params = {}) {
|
|
87
|
+
// Check rate limit before making request
|
|
88
|
+
if (!this.rateLimiter.canMakeRequest()) {
|
|
89
|
+
const resetTime = this.rateLimiter.getResetTime();
|
|
90
|
+
throw new RateLimitError(`Rate limit exceeded. Resets at ${resetTime?.toISOString()}`);
|
|
91
|
+
}
|
|
92
|
+
// Build query parameters
|
|
93
|
+
const queryParams = new URLSearchParams();
|
|
94
|
+
// Pick next API key via round-robin rotation
|
|
95
|
+
const currentKey = this.getNextKey();
|
|
96
|
+
if (currentKey) {
|
|
97
|
+
queryParams.append('api_key', currentKey);
|
|
98
|
+
}
|
|
99
|
+
// Add format parameter
|
|
100
|
+
queryParams.append('format', 'json');
|
|
101
|
+
// Add other parameters
|
|
102
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
103
|
+
if (value !== undefined && value !== null) {
|
|
104
|
+
queryParams.append(key, String(value));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
const url = `${this.baseUrl}${endpoint}?${queryParams.toString()}`;
|
|
108
|
+
try {
|
|
109
|
+
console.log(`Making request to: ${url.replace(/api_key=[^&]*/, 'api_key=***')}`);
|
|
110
|
+
const response = await fetch(url, {
|
|
111
|
+
method: 'GET',
|
|
112
|
+
headers: {
|
|
113
|
+
'Accept': 'application/json',
|
|
114
|
+
'User-Agent': 'LegislativeMCP/1.0'
|
|
115
|
+
},
|
|
116
|
+
// CloudFlare Workers doesn't support timeout in fetch options
|
|
117
|
+
// We rely on the Workers runtime timeout
|
|
118
|
+
});
|
|
119
|
+
// Record successful request for rate limiting
|
|
120
|
+
this.rateLimiter.recordRequest();
|
|
121
|
+
// Handle HTTP errors
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
const errorText = await response.text();
|
|
124
|
+
if (response.status === 429) {
|
|
125
|
+
this.cooldownCurrentKey();
|
|
126
|
+
throw new RateLimitError('API rate limit exceeded');
|
|
127
|
+
}
|
|
128
|
+
if (response.status === 404) {
|
|
129
|
+
throw new NotFoundError(`Resource not found: ${endpoint}`);
|
|
130
|
+
}
|
|
131
|
+
if (response.status === 400) {
|
|
132
|
+
throw new ValidationError(`Invalid request: ${errorText}`);
|
|
133
|
+
}
|
|
134
|
+
throw new ApiError(`HTTP ${response.status}: ${response.statusText}`, response.status, errorText);
|
|
135
|
+
}
|
|
136
|
+
const data = await response.json();
|
|
137
|
+
// Handle API-level errors (some APIs return 200 with error messages)
|
|
138
|
+
if (data.error) {
|
|
139
|
+
if (data.error.toLowerCase().includes('not found')) {
|
|
140
|
+
throw new NotFoundError(data.error);
|
|
141
|
+
}
|
|
142
|
+
throw new ApiError(data.error, response.status);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
data: data,
|
|
146
|
+
pagination: data.pagination
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.error('API request failed:', {
|
|
151
|
+
url: url.replace(/api_key=[^&]*/, 'api_key=***'),
|
|
152
|
+
error: error instanceof Error ? error.message : String(error)
|
|
153
|
+
});
|
|
154
|
+
// Re-throw known errors
|
|
155
|
+
if (error instanceof ApiError ||
|
|
156
|
+
error instanceof NotFoundError ||
|
|
157
|
+
error instanceof RateLimitError ||
|
|
158
|
+
error instanceof ValidationError) {
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
// Handle fetch errors
|
|
162
|
+
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
163
|
+
throw new ApiError('Network error occurred', 0, error.message);
|
|
164
|
+
}
|
|
165
|
+
// Generic error fallback
|
|
166
|
+
throw new ApiError(error instanceof Error ? error.message : 'Unknown error occurred', 0, error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get specific bill details
|
|
171
|
+
*/
|
|
172
|
+
async getBillDetails(params) {
|
|
173
|
+
const { congress, billType, billNumber } = params;
|
|
174
|
+
const endpoint = `/bill/${congress}/${billType}/${billNumber}`;
|
|
175
|
+
const response = await this.makeRequest(endpoint);
|
|
176
|
+
return response.data;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get specific member details
|
|
180
|
+
*/
|
|
181
|
+
async getMemberDetails(params) {
|
|
182
|
+
const { memberId, congress } = params;
|
|
183
|
+
const endpoint = congress ?
|
|
184
|
+
`/member/${memberId}/${congress}` :
|
|
185
|
+
`/member/${memberId}`;
|
|
186
|
+
const response = await this.makeRequest(endpoint);
|
|
187
|
+
return response.data;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get congress details
|
|
191
|
+
*/
|
|
192
|
+
async getCongressDetails(params) {
|
|
193
|
+
const { congress } = params;
|
|
194
|
+
const endpoint = `/congress/${congress}`;
|
|
195
|
+
const response = await this.makeRequest(endpoint);
|
|
196
|
+
return response.data;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get committee details
|
|
200
|
+
*/
|
|
201
|
+
async getCommitteeDetails(params) {
|
|
202
|
+
const { chamber, committeeCode, congress } = params;
|
|
203
|
+
const endpoint = congress ?
|
|
204
|
+
`/committee/${chamber}/${committeeCode}/${congress}` :
|
|
205
|
+
`/committee/${chamber}/${committeeCode}`;
|
|
206
|
+
const response = await this.makeRequest(endpoint);
|
|
207
|
+
return response.data;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get amendment details
|
|
211
|
+
*/
|
|
212
|
+
async getAmendmentDetails(params) {
|
|
213
|
+
const { congress, amendmentType, amendmentNumber } = params;
|
|
214
|
+
const endpoint = `/amendment/${congress}/${amendmentType}/${amendmentNumber}`;
|
|
215
|
+
const response = await this.makeRequest(endpoint);
|
|
216
|
+
return response.data;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Search across collections with comprehensive parameter support
|
|
220
|
+
*/
|
|
221
|
+
async searchCollection(collection, params = {}) {
|
|
222
|
+
const { query, limit = 20, offset = 0, sort, filters = {} } = params;
|
|
223
|
+
// Validate collection
|
|
224
|
+
if (!SUPPORTED_QUERY_PARAMS[collection]) {
|
|
225
|
+
throw new InvalidParameterError(`Collection '${collection}' is not supported. Supported collections: ${Object.keys(SUPPORTED_QUERY_PARAMS).join(', ')}`);
|
|
226
|
+
}
|
|
227
|
+
// Build request parameters
|
|
228
|
+
const requestParams = {
|
|
229
|
+
limit,
|
|
230
|
+
offset
|
|
231
|
+
};
|
|
232
|
+
// Add query if supported
|
|
233
|
+
if (query && QUERY_SUPPORTED_COLLECTIONS.includes(collection)) {
|
|
234
|
+
requestParams.query = query;
|
|
235
|
+
}
|
|
236
|
+
else if (query) {
|
|
237
|
+
console.warn(`Query search not supported for collection: ${collection}`);
|
|
238
|
+
}
|
|
239
|
+
// Add sort if supported
|
|
240
|
+
if (sort && SORT_SUPPORTED_COLLECTIONS.includes(collection)) {
|
|
241
|
+
requestParams.sort = sort;
|
|
242
|
+
}
|
|
243
|
+
else if (sort) {
|
|
244
|
+
console.warn(`Sorting not supported for collection: ${collection}`);
|
|
245
|
+
}
|
|
246
|
+
// Add filters if supported
|
|
247
|
+
if (FILTER_SUPPORTED_COLLECTIONS.includes(collection)) {
|
|
248
|
+
const supportedParams = SUPPORTED_QUERY_PARAMS[collection];
|
|
249
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
250
|
+
if (supportedParams.includes(key)) {
|
|
251
|
+
requestParams[key] = value;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.warn(`Filter parameter '${key}' not supported for collection: ${collection}`);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
// Congress.gov API uses path segments for congress and billType filtering, not query params
|
|
259
|
+
let endpoint = `/${collection}`;
|
|
260
|
+
if (filters.congress) {
|
|
261
|
+
endpoint += `/${filters.congress}`;
|
|
262
|
+
delete requestParams.congress;
|
|
263
|
+
}
|
|
264
|
+
if (collection === 'bill' && filters.billType) {
|
|
265
|
+
// billType requires congress in the path: /bill/{congress}/{type}
|
|
266
|
+
endpoint += `/${filters.billType}`;
|
|
267
|
+
delete requestParams.billType;
|
|
268
|
+
}
|
|
269
|
+
const response = await this.makeRequest(endpoint, requestParams);
|
|
270
|
+
return response.data;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get sub-resource data (e.g., bill actions, cosponsors, etc.)
|
|
274
|
+
*/
|
|
275
|
+
async getSubResource(parentUri, subResource, params = {}) {
|
|
276
|
+
// Parse the parent URI to extract collection and identifiers
|
|
277
|
+
const parsedUri = this.parseCongressUri(parentUri);
|
|
278
|
+
if (!parsedUri) {
|
|
279
|
+
throw new InvalidParameterError(`Invalid parent URI format: ${parentUri}`);
|
|
280
|
+
}
|
|
281
|
+
// Build the sub-resource endpoint
|
|
282
|
+
const endpoint = `${parsedUri.path}/${subResource}`;
|
|
283
|
+
const requestParams = {
|
|
284
|
+
limit: params.limit || 20,
|
|
285
|
+
offset: params.offset || 0
|
|
286
|
+
};
|
|
287
|
+
const response = await this.makeRequest(endpoint, requestParams);
|
|
288
|
+
return response.data;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Parse congress-gov:// URI format
|
|
292
|
+
*/
|
|
293
|
+
parseCongressUri(uri) {
|
|
294
|
+
try {
|
|
295
|
+
// Handle both congress-gov:// and congress-gov:/ formats
|
|
296
|
+
const cleanUri = uri.replace(/^congress-gov:\/\/?/, '');
|
|
297
|
+
const parts = cleanUri.split('/');
|
|
298
|
+
if (parts.length < 1) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
const collection = parts[0];
|
|
302
|
+
const path = `/${cleanUri}`;
|
|
303
|
+
return { collection, path };
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
console.error('Error parsing congress URI:', error);
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get supported collections
|
|
312
|
+
*/
|
|
313
|
+
getSupportedCollections() {
|
|
314
|
+
return Object.keys(SUPPORTED_QUERY_PARAMS);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get supported parameters for a collection
|
|
318
|
+
*/
|
|
319
|
+
getSupportedParameters(collection) {
|
|
320
|
+
return SUPPORTED_QUERY_PARAMS[collection] || [];
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Check if collection supports query search
|
|
324
|
+
*/
|
|
325
|
+
supportsQuerySearch(collection) {
|
|
326
|
+
return QUERY_SUPPORTED_COLLECTIONS.includes(collection);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Check if collection supports sorting
|
|
330
|
+
*/
|
|
331
|
+
supportsSorting(collection) {
|
|
332
|
+
return SORT_SUPPORTED_COLLECTIONS.includes(collection);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Check if collection supports filtering
|
|
336
|
+
*/
|
|
337
|
+
supportsFiltering(collection) {
|
|
338
|
+
return FILTER_SUPPORTED_COLLECTIONS.includes(collection);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get current rate limit status
|
|
342
|
+
*/
|
|
343
|
+
getRateLimitStatus() {
|
|
344
|
+
return {
|
|
345
|
+
canMakeRequest: this.rateLimiter.canMakeRequest(),
|
|
346
|
+
resetTime: this.rateLimiter.getResetTime() || undefined
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
CongressApiService.COOLDOWN_MS = 60_000; // 60s cooldown on 429
|
|
351
|
+
// Export default instance creator
|
|
352
|
+
export function createCongressApiService(baseUrl, apiKey, rateLimiter, timeout) {
|
|
353
|
+
return new CongressApiService(baseUrl, apiKey, rateLimiter, timeout);
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=CongressApiService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CongressApiService.js","sourceRoot":"","sources":["../../src/services/CongressApiService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate limiting service for API calls
|
|
3
|
+
* Implements sliding window rate limiting
|
|
4
|
+
*/
|
|
5
|
+
export interface RateLimitConfig {
|
|
6
|
+
maxRequests: number;
|
|
7
|
+
windowMs: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class RateLimitService {
|
|
10
|
+
private requestTimestamps;
|
|
11
|
+
private config;
|
|
12
|
+
constructor(config?: RateLimitConfig);
|
|
13
|
+
/**
|
|
14
|
+
* Check if a request can be made without exceeding rate limits
|
|
15
|
+
*/
|
|
16
|
+
canMakeRequest(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Record a request
|
|
19
|
+
*/
|
|
20
|
+
recordRequest(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Get the number of remaining requests
|
|
23
|
+
*/
|
|
24
|
+
getRemainingRequests(): number;
|
|
25
|
+
/**
|
|
26
|
+
* Get the reset time for the rate limit window
|
|
27
|
+
*/
|
|
28
|
+
getResetTime(): Date | null;
|
|
29
|
+
/**
|
|
30
|
+
* Clean up timestamps outside the current window
|
|
31
|
+
*/
|
|
32
|
+
private cleanupOldTimestamps;
|
|
33
|
+
/**
|
|
34
|
+
* Reset the rate limiter (useful for testing)
|
|
35
|
+
*/
|
|
36
|
+
reset(): void;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=RateLimitService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RateLimitService.d.ts","sourceRoot":"","sources":["../../src/services/RateLimitService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,CAAC,EAAE,eAAe;IAOpC;;OAEG;IACH,cAAc,IAAI,OAAO;IAKzB;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,oBAAoB,IAAI,MAAM;IAK9B;;OAEG;IACH,YAAY,IAAI,IAAI,GAAG,IAAI;IAS3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|