-
Notifications
You must be signed in to change notification settings - Fork 1
Rada Legislation Service
The Rada MCP Server (mcp_rada) provides AI-powered access to Ukrainian parliamentary data: legislation texts, deputies, bills, voting records, factions, and committees. It fetches data from the official Verkhovna Rada open data portals (data.rada.gov.ua and zakon.rada.gov.ua), caches it in PostgreSQL with configurable TTLs, and exposes it via MCP tools.
The service operates within the Model Context Protocol (MCP) framework, supporting a triple transport system:
| Transport | Use case | Entry point |
|---|---|---|
| MCP stdio | Claude Desktop integration | src/index.ts |
| HTTP REST API | Web applications |
src/http-server.ts (port 3001) |
| SSE streaming | Distributed real-time clients | POST /api/tools/:toolName/stream |
| Component | Default | Purpose |
|---|---|---|
| PostgreSQL | localhost:5433 |
Structured storage (schema rada) |
| Redis | localhost:6379 |
Rate limiting, health checks |
| HTTP port | 3001 |
REST API |
Redis is used for rate limiting and health check probes. Qdrant is not used by this service (no vector embeddings).
All services are composed via src/factories/rada-services.ts (createRadaCoreServices()):
RadaCoreServices
+-- Database (PostgreSQL)
+-- RadaAPIAdapter (data.rada.gov.ua)
+-- ZakonRadaAdapter (zakon.rada.gov.ua)
+-- CostTracker
+-- DeputyService (7-day cache)
+-- FactionService
+-- CommitteeService
+-- BillService (1-day cache)
+-- LegislationService (30-day cache)
+-- VotingService
+-- CrossReferenceService (links to SecondLayer court cases)
+-- MCPRadaAPI (tool routing)
Fetches structured JSON data from data.rada.gov.ua:
| Endpoint | Data | Notes |
|---|---|---|
/ogd/mps/skl9/mps-data.json |
Deputies, factions (is_fr=1), committees (type=2), assistants |
Cached in-memory for 5 min |
/ogd/zpr/skl9/billinfo_list-skl9.json |
Bills (~8 MB, light list) | Updated daily by RADA |
/ogd/zal/ppz/skl9/json/DDMMYYYY.json |
Voting records per session date | |
/ogd/zal/ppz/skl9/dict/dates.txt |
Available session dates | |
/ogd/list.json |
Dataset catalogue |
Rate limited to 10 requests/second (100 ms minimum interval).
Fetches legislation HTML from zakon.rada.gov.ua:
-
GET /laws/show/{lawNumber}- full law text -
GET /laws/search?q={keyword}- search laws by keyword
Parses HTML with Cheerio. Extracts title, metadata, adoption date, and individual articles. Rate limited to 10 rps.
| Alias | Law Number |
|---|---|
constitution / конституція
|
254к/96-вр |
цивільний кодекс |
435-15 |
кримінальний кодекс |
2341-14 |
сімейний кодекс |
2947-14 |
господарський кодекс |
436-15 |
кпк |
4651-17 |
цпк |
1618-15 |
касу |
2755-17 |
кзпп |
1023-12 |
Four tools are exposed via MCPRadaAPI:
Search bills (zakonoproekty) by query, status, initiator, committee, date range.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | Search term |
status |
enum | No |
registered, first_reading, second_reading, adopted, rejected, all
|
initiator |
string | No | Deputy name or faction |
committee |
string | No | Committee name |
date_from / date_to
|
string | No | Date range (YYYY-MM-DD) |
limit |
number | No | Max results (default 20) |
Get detailed information about a deputy. Supports search by name, rada_id, or faction.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | No | Full or partial name |
rada_id |
string | No | RADA system ID |
faction |
string | No | Faction name (returns member list) |
include_voting_record |
boolean | No | Include voting statistics |
include_assistants |
boolean | No | Include assistant list |
Full-text search within Ukrainian laws. Supports aliases and article retrieval.
| Parameter | Type | Required | Description |
|---|---|---|---|
law_identifier |
string | Yes | Law number or alias |
article |
string | No | Specific article number |
search_text |
string | No | Text to search for |
include_court_citations |
boolean | No | Cross-reference with SecondLayer court cases |
AI-powered analysis of a deputy's voting patterns.
| Parameter | Type | Required | Description |
|---|---|---|---|
deputy_name |
string | Yes | Deputy full name |
date_from / date_to
|
string | No | Analysis period |
bill_number |
string | No | Specific bill to check |
analyze_patterns |
boolean | No | Use AI (OpenAI) for pattern detection |
All caching uses PostgreSQL cache_expires_at columns. No Redis-based data cache.
| Entity | TTL | Env Override |
|---|---|---|
| Deputies | 7 days | CACHE_TTL_DEPUTIES |
| Bills | 1 day | CACHE_TTL_BILLS |
| Legislation | 30 days | CACHE_TTL_LAWS |
| Voting | 3 days | (hardcoded) |
Cache-first retrieval logic:
- Check PostgreSQL for non-expired record
- On miss: fetch from RADA/Zakon API
- Upsert to database with new TTL expiry
PostgreSQL database rada_db (user: rada_mcp, port: 5433). Tables:
| Table | Purpose | Key Fields |
|---|---|---|
deputies |
Deputy profiles |
rada_id, full_name, faction_name, committee_name, active
|
deputy_assistants |
Assistant records |
deputy_id (FK), full_name, assistant_type
|
bills |
Bills / zakonoproekty |
bill_number, title, status, stage, registration_date
|
legislation |
Full law texts |
law_number, law_alias, articles (JSONB), full_text_plain
|
voting_records |
Session voting results |
session_date, question_text, bill_number, votes (JSONB) |
factions |
Parliamentary factions |
faction_id, name, convocation
|
committees |
Parliamentary committees |
committee_id, name, chair_deputy_id
|
cost_tracking |
Per-request cost data |
request_id, tool_name, rada_api_calls
|
monthly_api_usage |
Aggregated usage |
year_month, rada_total_calls, rada_total_bytes
|
law_court_citations |
Cross-references to court cases |
law_number, court_case_number, citation_count
|
Migrations are in src/migrations/ (001 through 005). Run with npm run migrate.
Full-text search indexes exist on bills.title, legislation.title, legislation.full_text_plain, and voting_records.question_text.
| Script | Command | Purpose |
|---|---|---|
sync-laws.ts |
npm run sync:laws |
Download 40+ key laws from zakon.rada.gov.ua (Constitution, all major codes, key statutes). Configurable concurrency via CONCURRENCY env (default 3, max 5). |
sync-reference-data.ts |
npm run sync:reference |
Sync factions, committees, and deputy assistants from data.rada.gov.ua. |
sync-week-data.ts |
npm run build && node dist/scripts/sync-week-data.js |
Parallel sync of deputies, bills, and voting records for the last week. |
import-json-data.ts |
npm run build && node dist/scripts/import-json-data.js |
Import JSON data dumps. |
cleanup-cache.js |
npm run cleanup:cache |
Clean expired cache entries. |
The script syncs 40+ documents including:
- Constitution of Ukraine
- Civil, Criminal, Family, Commercial, Tax, Land, Budget, Customs codes
- Criminal Procedure, Civil Procedure, Administrative Procedure, Commercial Procedure codes
- Water, Forest, Air, Maritime codes
- Key laws: state registration, public info access, personal data, electronic documents, anti-corruption, judiciary, advocacy, notaries, banking, financial services, accounting, companies, real estate rights, land lease, mortgage, pledge, bankruptcy, enforcement proceedings
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/health |
No | Full health check (PostgreSQL + Redis) |
GET |
/health/live |
No | Liveness probe |
GET |
/health/ready |
No | Readiness probe (DB query) |
GET |
/metrics |
No | Prometheus metrics |
GET |
/api/stats |
No | Data statistics (row counts, last update) |
GET |
/api/tools |
Bearer | List available tools |
POST |
/api/tools/:toolName |
Bearer | Execute tool (JSON response) |
POST |
/api/tools/:toolName/stream |
Bearer | Execute tool (SSE streaming) |
Authentication: Bearer token via RADA_API_KEYS env var (comma-separated). SSE streaming sends connected, progress, complete/error, end events.
Rate limiting: 300 requests/minute globally (express-rate-limit), 120 req/min on /api/ routes.
- Logging: Winston structured JSON logs
-
Metrics: Prometheus (
prom-client) - HTTP request duration/count, PG pool connections, Node.js defaults -
Cost tracking: Per-request tracking of OpenAI tokens, Anthropic tokens, RADA API calls (bytes, cached vs fresh), execution time. Aggregated monthly in
monthly_api_usage.
The CrossReferenceService connects RADA data with SecondLayer court decisions:
- Calls
mcp_backendvia HTTP (POST /api/tools/find_relevant_law_articles) to find court cases citing specific laws - Stores citations in
law_court_citationstable - Exposes via
include_court_citationsflag insearch_legislation_texttool
cd mcp_rada
# Development
npm run dev:http # HTTP server with hot-reload (port 3001)
npm run dev # MCP stdio mode
# Production
npm run build && npm run start:http
# Database
npm run db:setup # Create DB + run migrations
npm run migrate # Run migrations only
# Data sync
npm run sync:laws # Sync legislation texts
npm run sync:reference # Sync factions, committees, assistants
npm run cleanup:cache # Clean expired entries
# Testing
npm test # Jest tests
npm run lint # ESLintKey runtime dependencies:
-
@modelcontextprotocol/sdk- MCP protocol -
@secondlayer/shared- Shared types, BaseDatabase, BaseCostTracker, LLMManager -
express5.x - HTTP server -
cheerio- HTML parsing for zakon.rada.gov.ua -
axios- HTTP client for RADA APIs -
pg- PostgreSQL driver -
redis- Redis client (rate limiting) -
openai- AI analysis (voting patterns) -
@anthropic-ai/sdk- Alternative AI provider -
prom-client- Prometheus metrics -
winston- Structured logging -
p-limit- Concurrency control
In production with ENABLE_UNIFIED_GATEWAY=true, RADA tools are prefixed with rada_ and accessible through the main backend gateway at mcp_backend:
rada_search_parliament_billsrada_get_deputy_inforada_search_legislation_textrada_analyze_voting_record