mcpField
E
eu-parliament-mcp-server

eu-parliament-mcp-server

Remote
github.com/cyanheadsLegal & Government1Updated 3d ago
Repository

About eu-parliament-mcp-server

eu-parliament-mcp-server provides MCP access to European Parliament open records, including procedures, MEPs, votes, documents, speeches, and questions. It is for users researching parliamentary activity, with linked source records and paged results.

Key features

  1. 01Discover procedures and their evidence
  2. 02Search documents, speeches, and questions
  3. 03Find MEPs and published membership history
  4. 04Inspect plenary decisions and voter records
  5. 05Page results with source links and identifiers

Use cases

  1. 01Follow a legislative procedure and its documents
  2. 02Look up an MEP's published memberships
  3. 03Review a plenary decision and voter positions
  4. 04Find parliamentary speeches or questions

MCP support

Exposes nine read-only European Parliament data tools to MCP clients. It runs over stdio or Streamable HTTP and retrieves records from the Parliament's Open Data API without an API key.

Configuration

Remote servers need no local install. Pick your client, then copy the config or install in one click.

Endpointhttps://eu-parliament.caseyjhand.com/mcp
Copy install command
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.

Packages
npm@cyanheads/eu-parliament-mcp-server0.1.2

Tools

9
ParameterTypeRequiredDescription
limitintegerNoWhole items requested; default 20, effective maximum 20 for organizations or 100 for other topics, with notice.
topicenumYesReference topic; organizations requires organization_ids, which other topics reject.committees | political_groups | organizations | procedure_types | document_types | question_types | activity_types | mep_filters | identifiers | coverage
offsetintegerNoZero-based collection offset; default 0.
languagestringNoSupported EU ISO-639-1 language, case-insensitive; default en; source text is never translated.
organization_idsarrayNoRequired nonempty list only for organizations; order and duplicates retained, with at most 20 looked up per page.

Details

From the project README

@cyanheads/eu-parliament-mcp-server

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

9 Tools

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


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 or a local Streamable HTTP server. No API key is required.

Tools
ToolDescription
euparl_list_referenceDiscover filter codes, identifier formats, coverage, committees, and political groups
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 tenth-term speech activities and retrieve metadata and recording-document links
euparl_search_mepsFind MEPs across a complete current 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

Capability reference

All list tools accept offset (default 0), limit (default 20, accepted 1–1000, effective maximum 100 with notice), and language (default en, one of the 24 EU language codes). Follow page.next_offset; for local collections, also pass the returned page.snapshot. A snapshot_changed error requires restarting at offset 0 without a snapshot. Local snapshots detect changes on a fresh lookup; they do not retain historical data.

Localized text includes its actual source language; fallback is disclosed and text is never translated. Missing detail normally returns found: false; empty lists succeed with guidance. Rich decision lookup uses decision_not_found instead.

euparl_list_reference tool
  • Required topic: committees, political_groups, procedure_types, document_types, question_types, activity_types, mep_filters, identifiers, or coverage
  • Committees and political groups are live corporate bodies; organization identifiers do not imply accepted filter codes. Reference pages need no snapshot

euparl_list_procedures tool
  • Optional process_type from euparl_list_reference; no keyword, year, committee, or stage filter
  • 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
  • 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"; only that family accepts text, title, sitting dates, and related_process_id. Adopted texts accept year and term; committee accepts term, plenary accepts year
  • work_type uses family-specific codes from euparl_list_reference. Committee/plenary discovery has no keyword filter
  • mode: "get" requires document_id and rejects source, search filters, and nondefault paging. IDs from procedures, speeches, votes, questions, and answers all use this same lookup
  • Detail includes metadata, procedure/parent references, and every source-file link in the selected language. Follow parent_document_ids when a subdivision lacks files; document bodies are not downloaded or extracted

euparl_search_speeches tool
  • Search by text, title, person_id, activity_type, or sitting-date range; coverage is limited to parliamentary term 10
  • 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
  • Omitted term 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_filters
  • name_contains matches every normalized token across the complete roster before paging, ignoring case, diacritics, and punctuation
  • Results use snapshot continuation. Country/group describe the selected roster context, not voting-date affiliation

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

euparl_get_plenary_votes tool
  • mode: "sittings" requires year; default decisions requires sitting_id and pages compact summaries. Sitting IDs accept a real YYYY-MM-DD date, MTG-PL- form, known event prefix, or public URI
  • mode: "decision" requires sitting and decision IDs and returns one rich record with whole labels, commentary, and document references; it rejects paging and snapshots
  • mode: "voters" also requires position: favor, against, abstention, intended_favor, intended_against, or intended_abstention. Decisions and voter positions page independently with snapshots
  • resolve_names: true is voters-only and requires roster_term (0–10); unresolved IDs remain in output. Names come from that published term roster, with no voting-date affiliations
  • Published tallies remain separate from voter-array counts. available: false means an array was absent; published empty arrays have available: true. Intended corrections never replace recorded votes

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 prefixes). Optional term resolves a citation directly; omission searches the full citation year 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

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 operation deadline covering acquisition and queue waits
  • Complete-roster name filtering and separate paging for procedure evidence, membership history, decision summaries, and voters

Agent-friendly output:

  • Source URLs, actual language labels, normalized filters, exact totals when available, and continuation guidance
  • Missing fields retain their uncertainty; published zero values, absent arrays, and historical affiliations remain distinct
  • Matching data on content[] and structuredContent, with usable source links and typed error reasons

Data source: European Parliament Open Data, licensed under CC BY 4.0. This server normalizes identifiers and record structure. Parliament records do not establish enacted-law completeness or a member's lifetime voting history.

Getting started

Self-Hosted / Local

Add the following to your MCP client configuration file.

{
  "mcpServers": {
    "eu-parliament-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/eu-parliament-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "eu-parliament-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/eu-parliament-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "eu-parliament-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/eu-parliament-mcp-server:latest"]
    }
  }
}

For Streamable HTTP, set the transport and start the server:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key or upstream account is required.
Installation
  1. Clone the repository:
git clone https://github.com/cyanheads/eu-parliament-mcp-server.git
  1. Navigate into the directory:
cd eu-parliament-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env
# edit .env to override framework settings as needed

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

No server-specific environment variables are required. See .env.example for common framework overrides.

Running the server

Local development
  • 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
    

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

bun run devcheck
bun run test

License

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

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.