Eu Parliament Mcp Server

io.github.cyanheadsPublished by GitHub account cyanheadsv0.2.2Updated Nov 4, 2026

Track European Parliament procedures, MEPs, votes, documents, speeches, and questions.

VerifiedStreamable HTTPWeb executableKnowledge & MemoryData & Analytics

Overview

AI-generated overview

Read-only access to European Parliament open data: procedures, MEPs, plenary votes, documents, speeches and questions.

What you can do
  • List reference codes, committees, political groups and resolve organization IDs
  • List and read legislative procedures with stages, events, participants and documents
  • Search adopted texts and committee/plenary documents, and fetch any document's metadata and source-file links
  • Search speech activities and MEPs by name, country, group, gender, term or mandate date, and read membership history
  • Discover sittings, page plenary decisions and voters by position, and list questions with answer references
When to use it
Useful when an assistant needs to trace EU legislative files, check how an MEP voted or which members sat on a date, or pull citations and source links from Parliament records. Suited to research, journalism and policy work rather than live or non-EU data.
Requirements
No API key or account. Either connect to the public hosted HTTP endpoint, or run locally with npx (Node.js) or Docker; a source build needs Bun 1.4.0+ or Node.js 24+. Optional variables cover transport, HTTP host/port, auth mode, log level and limit clamping.
Before you install
All nine tools are read-only, but requests go to the European Parliament Open Data API and are paced to its quota, with a 40-second deadline per call. Data is CC BY 4.0 and should be attributed. Published labels and text are source data, not instructions. Some records are incomplete or historical, so missing data does not prove absence.

Installation

In SourceWeft

  1. Open Eu Parliament Mcp Server in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/io.github.cyanheads%2Feu-parliament-mcp-server/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "eu-parliament": {
      "type": "http",
      "url": "https://eu-parliament.caseyjhand.com/mcp"
    }
  }
}

README

@cyanheads/eu-parliament-mcp-server

Track European Parliament procedures, MEPs, votes, documents, speeches, and questions via MCP. STDIO or Streamable HTTP.

9 Tools


Overview

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.

Tools

ToolDescription
euparl_list_referenceDiscover filter codes, identifier formats, coverage, committees, and political groups; resolve organization IDs
euparl_list_proceduresList procedure references by type
euparl_get_procedureRead procedure identity, stage, events, participants, and document references
euparl_search_documentsSearch adopted texts, list committee/plenary documents, or retrieve any canonical document
euparl_search_speechesSearch ninth- or tenth-term speech activities and retrieve metadata and recording-document links
euparl_search_mepsFind MEPs across a complete current, dated, or selected-term roster
euparl_get_mepRead member identity and published membership history
euparl_get_plenary_votesDiscover sittings, inspect decisions, and page recorded or intended voters
euparl_list_questionsList questions by year/type or retrieve a question and answer references

Agent skill

skills/eu-parliament-guide is served over MCP as skill://eu-parliament-guide/SKILL.md and ships in the npm package, Docker image, and .mcpb bundle. It covers identifier chains from procedure to vote and debate, the term calendar, choosing a file's final vote, a voter's country on the vote date, and conclusions missing data cannot support.

Capability reference

euparl_list_reference tool

  • Required topic: committees, political_groups, organizations, procedure_types, document_types, question_types, activity_types, mep_filters, identifiers, or coverage
  • committees 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 available
  • organizations requires organization_ids: decimal IDs or the case-sensitive codes other tools return (IMCO, S-D, ep-9, EU_PARLIAMENT), bare, with an org/ prefix, or as exact public organization URIs. A code in the wrong case is a found: false miss; labels such as S&D are rejected. 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 snapshot

euparl_list_procedures tool

  • Optional process_type from euparl_list_reference; no keyword, year, committee, or stage filter
  • With process_type, every procedure of that type is read in one request and paged in procedure_id order; follow page.next_offset, optionally passing page.snapshot back to detect a changed set
  • Without process_type, Parliament serves pages from two orderings, so pages can repeat or omit procedures; each page says so
  • Sparse rows carry procedure_id, source URL, and available reference/type; retrieve titles, stages, and participants with euparl_get_procedure

euparl_get_procedure tool

  • Required procedure_id accepts YYYY-NNNN, YYYY/NNNN(TYPE), an optional letter suffix, the known path prefix, or the exact public procedure URI
  • section: "overview" returns scalar metadata and collection counts; events, participants, and documents page one collection using snapshots. Events and participants come in date order (undated last), documents in ID order
  • Historical participant groups stay historical. Follow document IDs with euparl_search_documents in get mode

euparl_search_documents tool

  • mode: "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-specific
  • mode: "get" takes a document_id from any tool (procedures, speeches, votes, questions, answers) and rejects search filters and paging
  • Detail returns metadata, procedure and parent_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 upstream
  • Adopted texts, plenary documents (reports, motions) and committee documents publish their procedure only on their family record, so their get makes one extra request for procedure_ids. If that read fails, the rest of the record is returned with a notice that procedure_ids may be incomplete

euparl_search_speeches tool

  • Search by text, title, person_id, activity_type, or sitting-date range; optional term selects 9 or 10, defaulting to 10. Term-9 records start in July 2021
  • mode: "get" requires speech_id and rejects search filters and nondefault paging
  • Returns activity metadata and participants, not transcripts. Follow document_ids through document get and any parent references to source files

euparl_search_meps tool

  • Omitting term and mandate_date selects members in office today; term selects a published roster from terms 0–10; mandate_date (YYYY-MM-DD, not after today, exclusive with term) selects members seated on that date. Optional country, political-group, and gender codes come from mep_filters
  • On a mandate_date roster, country and group are matched and returned as published on that date, so political_group matches only the code in force then; for earlier groups omit it and read each member's published group. A term's first sitting day can return an incomplete roster or an upstream error; use the following day
  • Equivalent query spellings include EPP, Greens/EFA, GUE/NGL, Patriots, and country UK → GB, the code Parliament publishes for United Kingdom members on historical rosters; published historical group identities remain intact
  • name_contains matches every normalized token across the complete roster before paging, ignoring case, diacritics, and punctuation; a value with no letter or digit is rejected
  • Results use snapshot continuation. Country/group describe the selected roster context, never inferred for another date

euparl_get_mep tool

  • mep_id accepts a decimal person ID, person/ prefix, or exact public person URI
  • Default section: "overview" returns identity and membership_count; section: "memberships" pages published history with snapshots
  • Missing membership end dates do not establish current membership
  • Resolve membership organization IDs, decimal or term codes such as org/ep-9, through euparl_list_reference with topic: "organizations"

euparl_get_plenary_votes tool

  • mode: "sittings" takes year (sittings data starts in 2014; earlier years return no sittings); default decisions takes sitting_id (a YYYY-MM-DD date or MTG-PL- ID) and pages summaries with labels, tallies, distinct decided-on, recording and part-of document IDs, and vote item IDs. Decisions start in 2019: 2014–2018 sittings list but return no decisions, and not every later sitting has them. Optional document_id filters any document relation by exact ID or hyphen-boundary descendant before paging; optional vote_item_id (a procedure's PLENARY_VOTE_RESULTS event) selects every decision of one vote item, the route to a report's final vote. A procedure event's vote date can select the sitting. Older sittings publish no vote items or report relations on decisions; there, the report citation appears only in decision labels
  • Optional label_contains selects a literal substring of the selected-language decision label, ignoring case, repeated whitespace and dash style. Use a report citation such as A9-0179/2022 on older sittings without document relations; matching text does not establish a final vote. It combines with document and vote-item filters before paging
  • decision adds decision_id and returns one rich record with commentary (an ID absent from the sitting fails decision_not_found). Optional mep_id in decisions or decision returns separate recorded/intended positions and publication availability without a roster lookup; an empty match does not prove absence from the vote
  • mode: "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: true
  • resolve_names: true requires roster_term (0–10) and joins names from that term's roster only; unresolved IDs stay in the output

euparl_list_questions tool

  • Default mode: "list" accepts year and work_type; no keyword, author, or addressee search
  • mode: "get" requires question_id: a canonical work ID, known document prefix/public URI, or human citation such as E-000001/2025 (also P, O, and the G/K interpellation prefixes). Optional term resolves a citation directly; omission checks the corresponding mandate, including both candidates in election years, and requires one exact match
  • Get rejects list filters and nondefault paging. Detail returns metadata, source files, and published answer references; empty answers does not prove the question is unanswered
  • Question prefixes E/P/O/G/K are case-insensitive and sequences of one to six digits are padded to six: e-3/2025 and E-10-2025-3 resolve as E-10-2025-000003

Features

Built 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:

  • Keyless access to Parliament's public JSON-LD records, with canonical identifiers linking discovery, detail, and source evidence

  • Shared request pacing, bounded retries, and one 40-second deadline per call covering every upstream request and queue wait

  • Shared paging on every list tool: offset, limit (default 20), 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 0. Procedure listings with process_type are acquired whole too, and passing their snapshot back is optional

  • Whole-acquired local collections return the requested count or remaining rows without an output-size cap or character budget. Complete relation arrays are preserved. Upstream request bounds are separate from caller page sizes:

    CollectionAcquisition / caller limit
    Process-type procedures, MEP rosters, memberships, procedure sections, decisions, voters, static referencesRequested count or remaining rows
    Non-keyword adopted texts and speechesFixed aligned 100-row upstream pages; caller pages span requests within one deadline
    Unfiltered procedures, questions, committee/plenary documents, keyword adopted texts and speeches, live reference bodies, sittings1,000 per request
    Organization lookups (one paced request each)20 per call

    Larger bounded-mode limits normalize with a notice. EUPARL_CLAMP_LIMITS=false rejects limits above the largest bound only for tools whose modes are all bounded; smaller mode bounds still normalize. Very large finite limits normalize to the safe-integer range. Non-keyword adopted texts sort by document ID; speeches by sitting date and numbering. Keyword searches retain relevance order and can repeat or omit records. Past-end pages of nonempty local collections disclose the offset, exact total and restart guidance after snapshot validation.

  • Speech and adopted-text searches, and procedure listings without process_type, expose a 10,000-result window. A page reaching past it narrows to the results left, with a notice; an offset of 10,000 or more is rejected. Narrow supported filters when the window ends; a capped upstream total is disclosed as a lower bound, and has_more can remain true without a reachable next_offset

Agent-friendly output:

  • Provenance on every response: source URLs, the actual language of each text (fallback disclosed, never translated), normalized filter echo, and continuation guidance
  • Missing data stays distinguishable: detail misses return found: false, empty lists succeed with guidance, and published zeros, unpublished arrays, and historical affiliations are never conflated
  • Typed error reasons with recovery hints, and the same data on content[] and structuredContent

Data source: European Parliament Open Data, licensed under CC BY 4.0. This server normalizes identifiers and record structure.

Getting started

Connect to the hosted instance, no install:

sh
claude mcp add --transport http eu-parliament-mcp-server https://eu-parliament.caseyjhand.com/mcp

Or run it locally:

sh
claude mcp add eu-parliament-mcp-server -- npx -y @cyanheads/eu-parliament-mcp-server

For other clients, add this to the MCP config file:

json
{  "mcpServers": {    "eu-parliament-mcp-server": {      "command": "npx",      "args": ["-y", "@cyanheads/eu-parliament-mcp-server"]    }  }}

For Docker or a source build, see Running the server.

Configuration

VariableDescriptionDefault
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_HOSTHTTP listen host.127.0.0.1
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto.stateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
LOGS_DIRDirectory for log files (Node.js only).<app-root>/logs
STORAGE_PROVIDER_TYPEStorage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1.in-memory
OTEL_ENABLEDEnable OpenTelemetry.false
EUPARL_CLAMP_LIMITSNormalize bounded-mode limits with a notice; false rejects limits above a fully bounded tool's largest acquisition bound. Whole-acquired local modes have no output-size cap.true

No variables are required; EUPARL_CLAMP_LIMITS is the one optional server-specific variable. See .env.example for common framework overrides.

Running the server

Requires Bun v1.4.0 or higher (or Node.js v24+). No API key or upstream account is required.

sh
git clone https://github.com/cyanheads/eu-parliament-mcp-server.gitcd eu-parliament-mcp-server && bun installcp .env.example .env

Local development

  • Build and run the production version:

    sh
    # One-time buildbun run rebuild
    # Run the built serverbun run start:http# orbun run start:stdio
  • Run checks and tests:

    sh
    bun run devcheck  # Lints, formats, type-checks, and morebun run test      # Runs the test suite

Docker

sh
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.

Project structure

DirectoryPurpose
src/mcp-server/tools/definitionsNine read-only tool definitions (*.tool.ts).
src/mcp-server/tools/utilsShared schemas, lossless rendering, and enrichment.
src/configServer environment: the limit clamp switch.
src/services/eu-parliamentJSON-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.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for logging, ctx.state for storage
  • Register new tools and resources in the createApp() arrays in src/index.ts
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheckbun run test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Source: README.md at commit 5fad29b

Tools

9

euparl_list_reference

List parliamentary reference data

List parliamentary codes, identifier formats, historical committees and political groups, or coverage limitations. Choose organizations with organization_ids to resolve the organization IDs other tools return, decimal (org/5154) or code (org/IMCO, org/S-D, org/ep-9), into published names and dates; ordered per-item found false means no detail was published, as for a code in the wrong case. Organization lookups page the supplied IDs with an effective maximum of 20 per call; repeat the same ID list with page.next_offset. Live committees and political groups retain historical coverage and an acquisition bound of 1000 per page; static reference topics return the requested count or remaining entries without an output-size cap. These lists do not require snapshots; missing end dates do not establish current status.

Parameterslimittopicrequiredoffsetlanguageorganization_ids

euparl_list_procedures

List parliamentary procedures

List procedure references by procedure type. Titles, stages, and participants require euparl_get_procedure with a returned procedure_id. Use euparl_list_reference topic procedure_types for supported process_type codes. No keyword, year, committee, or stage filter is available. With process_type, every procedure of that type is listed in procedure_id order: continue with page.next_offset, passing the returned page.snapshot to detect a changed set. Without process_type, at most 10000 results are reachable and Parliament serves pages from two orderings, so pages can repeat or omit procedures; continue with page.next_offset when present, and set process_type for a complete walk.

Parameterslimitoffsetlanguagesnapshotprocess_type

euparl_get_procedure

Read a parliamentary procedure

Read a procedure title, stage, events, participants, or linked parliamentary documents. Default section overview returns scalar fields and collection counts; events, participants, and documents page one selected collection. Continue with page.next_offset and the returned snapshot; restart at offset 0 without snapshot if the collection changes. Historical participation groups remain historical. Missing detail succeeds with found false; discover references with euparl_list_procedures. Linked documents can be read with euparl_search_documents mode get; files are linked, not parsed.

Parameterslimitoffsetsectionlanguagesnapshotprocedure_idrequired

euparl_search_documents

Search parliamentary documents

Search adopted texts, committee documents, or plenary documents, then retrieve metadata and source-file links with mode get and document_id. Get accepts IDs from procedures, speeches, votes, questions and answers without a source family; parent_document_ids lead from subdivisions to parent files. Detail preserves adopted_document_ids, based_on_document_ids and public_register_ref; follow published document relations with mode get, while preserving ordinary missing-record and upstream-error outcomes for unavailable targets. Detail procedure_ids also read the procedure an adopted text, plenary or committee document publishes on its family record; adopted and based-on document relations never imply procedure links. Only adopted_texts supports text, title, sitting dates and related_process_id. Committee supports term but no year; plenary supports year but no term. Use euparl_list_reference topic document_types for family-specific work_type codes. Detail returns links, not extracted full text. Non-keyword adopted texts use doc-id:asc and aligned 100-row upstream pages independently of caller limit. Keyword searches retain relevance order and can repeat or omit records across pages. Adopted texts expose at most 10000 search results; continue with page.next_offset when present, otherwise narrow supported filters.

Parametersmodetermtextyearlimittitleoffsetsourcelanguagework_typedocument_idsitting_date+2 more

euparl_search_speeches

Search parliamentary speeches

Search speech activities in parliamentary term 9 or 10, default 10, or retrieve one with mode get and speech_id. Text and title search return activity metadata, not transcripts. Follow document_ids with euparl_search_documents mode get and any returned parent_document_ids to source files. Use euparl_list_reference topic activity_types for activity_type codes. Get rejects search filters and nondefault paging. Non-keyword lists use sitting-date:asc,numbering:asc and aligned 100-row upstream pages independently of caller limit. Keyword searches retain relevance order and can repeat or omit records across pages. Search exposes at most 10000 results; continue with page.next_offset when present, otherwise narrow text, title, person_id, activity_type or sitting dates.

Parametersmodetermtextlimittitleoffsetlanguageperson_idspeech_idsitting_dateactivity_typesitting_date_end

euparl_search_meps

Find members of the European Parliament

Find MEPs by name, country, political group, gender, parliamentary term, or mandate date. Omitting term and mandate_date selects members in office today; term selects that published term roster; mandate_date selects members seated on that date. Name matching requires every normalized token after case, diacritic, and punctuation normalization over the complete roster before paging. Country and group are published for the selected roster, never inferred for another date. Continue with page.next_offset and snapshot; restart at offset 0 without snapshot after snapshot_changed. Read identity and membership history with euparl_get_mep. Empty matches succeed with guidance.

Parameterstermlimitgenderoffsetcountrylanguagesnapshotmandate_datename_containspolitical_group

euparl_get_mep

Read a member and membership history

Read an MEP identity or membership history including published committee, delegation, and political-group records. Default section overview returns identity and membership_count; section memberships pages full membership records. Missing end dates do not establish current membership. Continue memberships with page.next_offset and snapshot; restart at offset 0 without snapshot after snapshot_changed. Missing detail succeeds with found false; discover IDs with euparl_search_meps.

Parameterslimitmep_idrequiredoffsetsectionlanguagesnapshot

euparl_get_plenary_votes

Explore plenary sittings, decisions, and voters

Discover sittings with mode sittings and year; default decisions pages compact summaries with labels, separate decided-on, part-of and recording document relations, and vote items for sitting_id. Decisions and voters return the requested whole-item count or remaining rows without an output-size cap. Optional decisions-only label_contains selects a literal selected-language label substring, ignoring case, repeated whitespace and dash style without altering published text or establishing a final vote. Optional document_id filters decisions before paging by exact document identity or hyphen-boundary descendant; optional vote_item_id, such as a procedure's PLENARY_VOTE_RESULTS event, selects every decision of that vote item. Mode decision requires decision_id and returns one rich record with reference text and commentary; it rejects nondefault paging and snapshot. Optional decisions or decision mep_id returns independent recorded and intended positions with publication availability, without a roster lookup; empty positions do not prove absence from the vote. Mode voters requires decision_id and one position, paging recorded and intended voters independently. Missing arrays succeed with available false; published empty arrays have available true. Optional resolve_names requires explicit roster_term and joins only published names from that complete term roster, never voting-date affiliations or current-roster substitutes. Some IDs may remain unresolved. Continue local decisions or voters with page.next_offset and snapshot; restart at offset 0 without snapshot after snapshot_changed. Missing sitting decisions succeed empty; absent selected decision fails decision_not_found.

Parametersmodeyearlimitmep_idoffsetlanguagepositionsnapshotsitting_iddecision_iddocument_idroster_term+3 more

euparl_list_questions

List parliamentary questions

List parliamentary questions by year and work_type or retrieve metadata, source-file links and published answers with mode get and question_id. Human citations such as E-000001/2025 retain their full year; omission of term checks canonical candidates for every overlapping parliamentary term and requires one verified match. Supply term to disambiguate; canonical IDs resolve directly. Use euparl_list_reference topic question_types for work_type codes. No keyword, author or addressee search is available. Empty answers means no answer relation was published in this response. Get rejects list filters and nondefault paging; continue lists with page.next_offset.

Parametersmodetermyearlimitoffsetlanguagework_typequestion_id

Version history

5
  1. v0.2.2LatestNov 4, 2026
  2. v0.2.1Nov 3, 2026
  3. v0.2.0Nov 1, 2026
  4. v0.1.2Oct 26, 2026
  5. v0.1.1Oct 25, 2026

Score

100% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts