Track European Parliament procedures, MEPs, votes, documents, speeches, and questions.
One address for any MCP client. No sign-in needed on this server.
9 read
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Track European Parliament procedures, MEPs, votes, documents, speeches, and questions via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://eu-parliament.caseyjhand.com/mcp
European Parliament records from the Open Data API: discover legislative procedures, follow their evidence, find MEPs, inspect plenary decisions and voters, and retrieve document, speech, and question metadata. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above. No API key is required.
| Tool | Description |
|---|---|
euparl_list_reference | Discover filter codes, identifier formats, coverage, committees, and political groups; resolve organization IDs |
euparl_list_procedures | List procedure references by type |
euparl_get_procedure | Read procedure identity, stage, events, participants, and document references |
euparl_search_documents | Search adopted texts, list committee/plenary documents, or retrieve any canonical document |
euparl_search_speeches | Search ninth- or tenth-term speech activities and retrieve metadata and recording-document links |
euparl_search_meps | Find MEPs across a complete current or selected-term roster |
euparl_get_mep | Read member identity and published membership history |
euparl_get_plenary_votes | Discover sittings, inspect decisions, and page recorded or intended voters |
euparl_list_questions | List questions by year/type or retrieve a question and answer references |
euparl_list_reference tooltopic: committees, political_groups, organizations, procedure_types, document_types, question_types, activity_types, mep_filters, identifiers, or coveragecommittees and political_groups page Parliament's corporate-body records, historical bodies included; their organization IDs are not filter codes. Published full names and dates are included when availableorganizations requires organization_ids: decimal IDs, org/ prefixes, or exact public organization URIs. Results retain input order and duplicates with per-item found; repeat the same IDs with page.next_offset for continuation. At most 20 IDs are looked up per call; reference pages need no snapshoteuparl_list_procedures toolprocess_type from euparl_list_reference; no keyword, year, committee, or stage filterprocedure_id, source URL, and available reference/type; retrieve titles, stages, and participants with euparl_get_procedureeuparl_get_procedure toolprocedure_id accepts YYYY-NNNN, YYYY/NNNN(TYPE), an optional letter suffix, the known path prefix, or the exact public procedure URIsection: "overview" returns scalar metadata and collection counts; events, participants, and documents page one collection using snapshotseuparl_search_documents in get modeeuparl_search_documents toolmode: "search" defaults to source: "adopted_texts", the only family with text, title, sitting-date, and related_process_id filters; adopted texts take year and term, committee takes term, plenary takes year. work_type codes are family-specificmode: "get" takes a document_id from any tool (procedures, speeches, votes, questions, answers) and rejects search filters and pagingparent_document_ids references, published adopted_document_ids and based_on_document_ids, optional public_register_ref, and every source-file link in the selected language; bodies are linked, not extracted. A published relation does not guarantee its target is available upstreameuparl_search_speeches tooltext, title, person_id, activity_type, or sitting-date range; optional term selects 9 or 10, defaulting to 10mode: "get" requires speech_id and rejects search filters and nondefault pagingdocument_ids through document get and any parent references to source fileseuparl_search_meps toolterm selects members in office today; explicit term selects a published roster from terms 0–10. Optional country, political-group, and gender codes come from mep_filtersEPP, Greens/EFA, GUE/NGL, Patriots, and historical country GB → UK; published historical group identities remain intactname_contains matches every normalized token across the complete roster before paging, ignoring case, diacritics, and punctuationeuparl_get_mep toolmep_id accepts a decimal person ID, person/ prefix, or exact public person URIsection: "overview" returns identity and membership_count; section: "memberships" pages published history with snapshotseuparl_list_reference with topic: "organizations"euparl_get_plenary_votes toolmode: "sittings" takes year; default decisions takes sitting_id (a YYYY-MM-DD date or MTG-PL- ID) and pages summaries with labels, tallies, and distinct decided-on/recording document IDs. Optional document_id filters either relation by exact ID or hyphen-boundary descendant before paging; a procedure event's vote date can select the sittingdecision adds decision_id and returns one rich record with commentary (an ID absent from the sitting fails decision_not_found). Optional mep_id returns separate recorded/intended positions and publication availability without a roster lookup; an empty match does not prove absence from the votemode: "voters" adds position: favor, against, abstention, or the intended_* corrections, which never replace recorded votes. available: false means the array was not published; a published empty array has available: trueresolve_names: true requires roster_term (0–10) and joins names from that term's roster only; unresolved IDs stay in the outputeuparl_list_questions toolmode: "list" accepts year and work_type; no keyword, author, or addressee searchmode: "get" requires question_id: a canonical work ID, known document prefix/public URI, or human citation such as E-000001/2025 (also P/O prefixes). Optional term resolves a citation directly; omission checks the corresponding mandate, including both candidates in election years, and requires one exact matchanswers does not prove the question is unansweredBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
European Parliament-specific:
offset, limit (default 20, effective maximum 100; larger requests up to 1000 are normalized with a notice), and language (default en, any of the 24 EU languages). Rosters, procedure sections, memberships, decisions, and voters are acquired whole, then paged locally with a page.snapshot that continuation must pass back; snapshot_changed means restart at offset 0has_more can remain true without a reachable next_offsetAgent-friendly output:
found: false, empty lists succeed with guidance, and published zeros, unpublished arrays, and historical affiliations are never conflatedcontent[] and structuredContentData source: European Parliament Open Data, licensed under CC BY 4.0. This server normalizes identifiers and record structure.
Connect to the hosted instance, no install:
claude mcp add --transport http eu-parliament-mcp-server https://eu-parliament.caseyjhand.com/mcp
Or run it locally:
claude mcp add eu-parliament-mcp-server -- npx -y @cyanheads/eu-parliament-mcp-server
For other clients, add this to the MCP config file:
{
"mcpServers": {
"eu-parliament-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/eu-parliament-mcp-server"]
}
}
}
For Docker or a source build, see Running the server.
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_HOST | HTTP listen host. | 127.0.0.1 |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOGS_DIR | Directory for log files (Node.js only). | <app-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
No server-specific environment variables are required. See .env.example for common framework overrides.
Requires Bun v1.4.0 or higher (or Node.js v24+). No API key or upstream account is required.
git clone https://github.com/cyanheads/eu-parliament-mcp-server.git
cd eu-parliament-mcp-server && bun install
cp .env.example .env
Build and run the production version:
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more
bun run test # Runs the test suite
docker run --rm -p 3010:3010 ghcr.io/cyanheads/eu-parliament-mcp-server:latest
The image defaults to HTTP transport, stateless sessions, and logs to /var/log/eu-parliament-mcp-server. For stdio clients, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/eu-parliament-mcp-server:latest"]. Build locally with docker build -t eu-parliament-mcp-server .; OpenTelemetry peers install by default, or use --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/mcp-server/tools/definitions | Nine read-only tool definitions (*.tool.ts). |
src/mcp-server/tools/utils | Shared schemas, lossless rendering, and enrichment. |
src/services/eu-parliament | JSON-LD client, edge schemas, reference codes, normalization, and domain workflows. |
tests/ | Unit and integration tests, mirroring the src/ structure. |
docs/ | Design contracts and generated project tree. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for logging, ctx.state for storagecreateApp() arrays in src/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.