Bundestag MCP Server
A production-ready MCP server that provides access to the German Bundestag's parliamentary documentation system (DIP API).
README
Bundestag MCP Server
A production-ready Model Context Protocol (MCP) server that provides access to the German Bundestag's parliamentary documentation system (DIP API).
Public Instance
A public instance is available at:
https://bundestagapi.moritz-waechter.de/mcp
You can use this directly in your MCP client configuration without running your own server.
Table of Contents
- Public Instance
- Features
- Quick Start
- MCP Tools
- MCP Prompts
- MCP Resources
- Client Configuration
- API Endpoints
- Docker
- Development
- Architecture
- Semantic Search
- NLP Analysis Service
- License
Features
Core Capabilities
- Search Drucksachen - Find bills, motions, inquiries, and other printed documents
- Search Plenarprotokolle - Find plenary session transcripts
- Search Vorgaenge - Track legislative proceedings through parliament
- Search Personen - Find MPs and their information
- Search Aktivitaeten - Find parliamentary activities (speeches, questions)
- Full Text Retrieval - Get complete document texts
- Semantic Search - AI-powered search using Qdrant + Mistral embeddings
- Speech Search - Find specific statements in debates with hybrid vector/keyword search
- Document Section Search - Search within document sections (articles, questions, etc.)
- NLP Analysis - Extract speeches, analyze tone, classify topics, and compare parties
Production-Ready
- Graceful Shutdown - Clean session termination on SIGTERM/SIGINT
- Retry with Backoff - Automatic retries with exponential backoff and jitter
- Rate Limiting - Token bucket rate limiter to protect upstream API
- Circuit Breaker - Fast-fail pattern to prevent cascading failures
- Response Caching - Three-layer caching (API, entity, metadata)
Observability
- Prometheus Metrics -
/metrics/prometheusendpoint for monitoring - Deep Health Checks -
/health/deepverifies DIP API connectivity - Structured Logging - JSON logs with categories and levels
MCP Protocol
- 33 Tools - Search, retrieval, semantic search, speech search, and NLP analysis
- 8 Prompts - Guided workflows for common research tasks
- 12 Resources - Static and dynamic resource templates
- Dual Mode - Supports stateful (Claude, Cursor) and stateless (ChatGPT) clients
Quick Start
Prerequisites
- Node.js 18+
- DIP API Key (a public test key is provided)
Installation
git clone https://github.com/Movm/bundestag-mcp.git
cd bundestag-mcp
npm install
Configuration
Create a .env file (or copy .env.example):
# Public test key valid until 05/2026
DIP_API_KEY=OSOegLs.PR2lwJ1dwCeje9vTj7FPOt3hvpYKtwKkhw
# Optional
PORT=3000
PUBLIC_URL=
LOG_LEVEL=INFO
Note: For production, request your own API key from:
parlamentsdokumentation@bundestag.de
Run
# Production
npm start
# Development (auto-reload)
npm run dev
The server will start at http://localhost:3000.
MCP Tools
Document Tools
| Tool | Description |
|---|---|
bundestag_search_drucksachen |
Search printed documents (Gesetzentwürfe, Anträge, Anfragen) |
bundestag_get_drucksache |
Get specific document by ID (optionally with full text) |
bundestag_search_drucksachen_text |
Full-text search within document content |
Protocol Tools
| Tool | Description |
|---|---|
bundestag_search_plenarprotokolle |
Search plenary session transcripts |
bundestag_get_plenarprotokoll |
Get specific protocol by ID (optionally with full text) |
bundestag_search_plenarprotokolle_text |
Full-text search within transcript content |
Proceeding Tools
| Tool | Description |
|---|---|
bundestag_search_vorgaenge |
Search parliamentary proceedings |
bundestag_get_vorgang |
Get specific proceeding by ID |
bundestag_search_vorgangspositionen |
Search proceeding positions/steps (track bill progress) |
Person & Activity Tools
| Tool | Description |
|---|---|
bundestag_search_personen |
Search MPs and other persons |
bundestag_get_person |
Get person details by ID |
bundestag_search_aktivitaeten |
Search parliamentary activities |
bundestag_get_aktivitaet |
Get specific activity by ID |
Semantic Search Tools
| Tool | Description |
|---|---|
bundestag_semantic_search |
AI-powered semantic search across all documents |
bundestag_semantic_search_status |
Show semantic search system status |
bundestag_trigger_indexing |
Manually trigger document indexing |
Speech Search Tools
| Tool | Description |
|---|---|
bundestag_search_speeches |
Semantic/hybrid search through parliamentary speeches |
bundestag_trigger_protocol_indexing |
Trigger protocol chunking and indexing |
bundestag_reindex_protocols |
Force full re-indexing of all protocols |
bundestag_protocol_search_status |
Show protocol search system status |
Document Section Search Tools
| Tool | Description |
|---|---|
bundestag_search_document_sections |
Semantic search through document sections |
bundestag_trigger_document_indexing |
Trigger document chunk indexing |
bundestag_document_search_status |
Show document chunk search status |
NLP Analysis Tools
These tools use a Python FastAPI service with spaCy's German language model for natural language processing.
| Tool | Description |
|---|---|
bundestag_extract_speeches |
Parse protocol text into individual speeches with speaker, party, and type |
bundestag_analyze_text |
Analyze German text for word frequencies (nouns, adjectives, verbs) |
bundestag_analyze_tone |
Communication style analysis (aggression, collaboration, solution-focus) |
bundestag_classify_topics |
Classify text by political topics (migration, climate, economy, etc.) |
bundestag_analysis_health |
Check if the NLP analysis service is available |
bundestag_speaker_profile |
Generate comprehensive profile for a speaker based on their speeches |
bundestag_compare_parties |
Compare political parties based on their parliamentary speeches |
Utility Tools
| Tool | Description |
|---|---|
bundestag_estimate_size |
Check document/protocol size BEFORE fetching to manage context window |
bundestag_cache_stats |
Show cache statistics |
get_client_config |
Generate MCP client configurations |
Common Parameters
| Parameter | Description |
|---|---|
query |
Full-text search in title |
wahlperiode |
Electoral period (e.g., 20 for current 2021-2025) |
datum_start / datum_end |
Date range filter (YYYY-MM-DD) |
limit |
Results per page (1-100, default 10) |
cursor |
Pagination cursor from previous response |
includeFullText |
Fetch full document text |
useCache |
Whether to use cached results (default: true) |
MCP Prompts
Guided workflows for common parliamentary research tasks:
| Prompt | Description |
|---|---|
search-legislation |
Guided search for bills and legislation with semantic search and fallback strategies |
track-proceeding |
Track a parliamentary proceeding with legislative glossary and timeline |
mp-activity-report |
Comprehensive MP report with rhetorical analysis via bundestag_speaker_profile |
analyze-debate |
Analyze plenary debate with party comparison via bundestag_compare_parties |
compare-factions |
Compare party rhetoric on a topic using cross-party analysis |
find-statements |
Find specific quotes/statements from politicians with fallback strategies |
topic-trends |
Analyze how topic discussion evolved over time (across Wahlperioden/years) |
speaker-deep-dive |
Comprehensive rhetorical and policy analysis of a politician |
MCP Resources
Static Resources
| URI | Description |
|---|---|
bundestag://system-prompt |
AI usage instructions with tool selection guide and context management |
bundestag://info |
Server capabilities and tool listing |
bundestag://wahlperioden |
Electoral periods (Wahlperioden) |
bundestag://drucksachetypen |
Document types (Gesetzentwurf, Antrag, etc.) |
bundestag://factions |
Parliamentary factions with official names, aliases, colors, and positions |
Resource Templates
| URI Pattern | Description |
|---|---|
bundestag://drucksache/{id} |
Fetch document by ID |
bundestag://drucksache/{id}/text |
Fetch document full text |
bundestag://plenarprotokoll/{id} |
Fetch protocol by ID |
bundestag://plenarprotokoll/{id}/text |
Fetch protocol full text |
bundestag://vorgang/{id} |
Fetch proceeding by ID |
bundestag://person/{id} |
Fetch person by ID |
bundestag://aktivitaet/{id} |
Fetch activity by ID |
Client Configuration
Tip: Use the public instance
https://bundestagapi.moritz-waechter.de/mcpor run your own withhttp://localhost:3000/mcp
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"bundestag": {
"url": "https://bundestagapi.moritz-waechter.de/mcp"
}
}
}
Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"bundestag": {
"url": "https://bundestagapi.moritz-waechter.de/mcp"
}
}
}
VS Code (GitHub Copilot)
Add to settings:
{
"mcp.servers": {
"bundestag": {
"type": "http",
"url": "https://bundestagapi.moritz-waechter.de/mcp"
}
}
}
ChatGPT
The server is fully compatible with ChatGPT as a connector. Use the public instance or deploy your own.
-
Enable developer mode in ChatGPT:
- Go to Settings → Apps & Connectors → Advanced settings
- Enable developer mode
-
Add the connector:
- Go to Settings → Connectors → Create
- Use URL:
https://bundestagapi.moritz-waechter.de/mcp - Name it "Bundestag" and provide a description
-
Use in chat:
- Click the + button in a new chat
- Select your connector from the More menu
- Ask about German parliamentary documents!
API Endpoints
| Endpoint | Description |
|---|---|
POST /mcp |
MCP protocol endpoint |
GET /mcp |
SSE stream for sessions |
DELETE /mcp |
Close session |
GET /health |
Basic health check |
GET /health/deep |
Deep health check (verifies DIP API) |
GET /metrics |
JSON statistics |
GET /metrics/prometheus |
Prometheus format metrics |
GET /.well-known/mcp.json |
Auto-discovery |
GET /config/:client |
Client-specific config |
GET /info |
Server information |
Docker
# Build
docker build -t bundestag-mcp .
# Run
docker run -p 3000:3000 \
-e DIP_API_KEY=your-api-key \
bundestag-mcp
Docker Compose
version: '3.8'
services:
bundestag-mcp:
build: .
ports:
- "3000:3000"
environment:
- DIP_API_KEY=${DIP_API_KEY}
- LOG_LEVEL=INFO
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
Development
Running Tests
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
Project Structure
src/
├── index.js # Express server, MCP setup, endpoints
├── config.js # Environment configuration
├── api/
│ └── bundestag.js # DIP API client with retry logic
├── jobs/
│ └── indexer.js # Background document/protocol indexer
├── services/
│ ├── analysisService.js # HTTP client for Python NLP service
│ ├── embeddingService.js # Mistral embeddings
│ ├── qdrantService.js # Vector database operations
│ ├── protokollParser.js # Protocol speech extraction
│ └── drucksacheParser.js # Document section parsing
├── tools/
│ ├── search.js # 15 search/entity tools (incl. estimate_size)
│ ├── analysis.js # 7 NLP analysis tools
│ ├── semanticSearch.js # 10 semantic search tools
│ └── clientConfig.js # Client configuration tool
├── prompts/
│ └── index.js # MCP Prompts
├── resources/
│ ├── info.js # Static resources
│ └── templates.js # Resource templates
└── utils/
├── cache.js # Three-layer caching
├── logger.js # Structured logging
├── retry.js # Exponential backoff
├── rateLimiter.js # Token bucket rate limiter
├── circuitBreaker.js # Circuit breaker pattern
├── metrics.js # Prometheus metrics
└── textNormalization.js # German text handling
Architecture
Resilience Patterns
Request → Rate Limiter → Circuit Breaker → Retry Logic → DIP API
↓ ↓ ↓
(throttle) (fast-fail) (backoff)
- Rate Limiter: Token bucket algorithm prevents overwhelming the DIP API
- Circuit Breaker: Opens after consecutive failures, fast-fails during outages
- Retry Logic: Exponential backoff with jitter for transient failures
Caching Strategy
| Layer | TTL | Max Entries | Use Case |
|---|---|---|---|
| API Response | 5 min | 500 | Search results |
| Entity | 15 min | 200 | Individual documents |
| Metadata | 24 hr | 50 | Wahlperioden, document types |
Semantic Search
The server supports AI-powered semantic search using Qdrant vector database and Mistral embeddings. This enables finding conceptually related documents even when exact keywords don't match.
How It Works
- Document Indexing: Background indexer fetches documents from DIP API and generates embeddings using Mistral AI
- Vector Storage: Embeddings are stored in Qdrant with rich metadata for filtering
- Semantic Query: User queries are embedded and matched against document vectors using cosine similarity
Three Search Collections
| Collection | Description |
|---|---|
bundestag-docs |
Document-level search (Drucksachen, Vorgaenge, Aktivitaeten, Personen) |
bundestag-protocol-chunks |
Speech-level search from Plenarprotokolle |
bundestag-document-chunks |
Section-level search from Drucksachen (articles, questions, etc.) |
Features
- Multilingual: Search in English, finds German documents (e.g., "renewable energy" → "Erneuerbare Energien")
- Hybrid Search: Combines vector similarity with keyword boosting for improved precision
- Rich Filtering: Filter by document type, entity type, Wahlperiode, Sachgebiet, initiative, faction, date range
- Speech Search: Find specific statements by speaker, party, speech type, or government officials
- Document Section Search: Find specific paragraphs, questions, or articles within documents
- Incremental Indexing: Uses
f.aktualisiert.startfor efficient updates after initial indexing
Configuration
Add to your .env file:
# Semantic Search (Qdrant + Mistral)
QDRANT_ENABLED=true
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=your-qdrant-api-key # Optional: API key for Qdrant authentication
MISTRAL_API_KEY=your-mistral-api-key
# Background Indexer
INDEXER_ENABLED=true
INDEXER_INTERVAL_MINUTES=15
INDEXER_WAHLPERIODEN=19,20
Tip: Generate a secure API key with
openssl rand -hex 32
Docker Compose with Qdrant
services:
bundestag-mcp:
build: .
environment:
- QDRANT_ENABLED=true
- QDRANT_URL=http://qdrant:6333
- QDRANT_API_KEY=${QDRANT_API_KEY}
- MISTRAL_API_KEY=${MISTRAL_API_KEY}
- INDEXER_ENABLED=true
depends_on:
- qdrant
qdrant:
image: qdrant/qdrant:latest
ports:
- "6333:6333"
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY}
volumes:
qdrant_data:
Semantic Search Parameters
| Parameter | Description |
|---|---|
query |
Natural language search query |
limit |
Max results (1-50) |
docTypes |
Filter: drucksache, vorgang, aktivitaet, person |
entityTypes |
Filter: Gesetzentwurf, Kleine Anfrage, Rede, etc. |
wahlperiode |
Electoral period (19, 20) |
sachgebiet |
Subject area |
initiative |
Initiating faction (CDU/CSU, SPD, etc.) |
fraktion |
Parliamentary group |
dateFrom / dateTo |
Date range (YYYY-MM-DD) |
scoreThreshold |
Minimum similarity (0-1, default 0.3) |
Speech Search Parameters
| Parameter | Description |
|---|---|
query |
Natural language search query |
limit |
Max results (1-50) |
speaker |
Filter by speaker name (e.g., "Friedrich Merz") |
speakerParty |
Filter by party (e.g., "CDU/CSU", "SPD") |
speakerState |
Filter by state for Bundesrat (e.g., "Bayern") |
top |
Filter by agenda item (e.g., "TOP 1") |
wahlperiode |
Electoral period |
herausgeber |
Publisher: BT (Bundestag) or BR (Bundesrat) |
speechType |
Type: rede, befragung, fragestunde_antwort, kurzbeitrag |
isGovernment |
Filter for government officials (ministers, state secretaries) |
category |
Category: rede (formal speeches) or wortbeitrag (contributions) |
searchMode |
semantic (pure vector) or hybrid (vector + keyword boosting) |
keywordBoost |
Boost factor for keyword matches in hybrid mode (0-2, default 0.5) |
requiredKeywords |
Keywords that must appear in the text |
excludeKeywords |
Keywords that must not appear in the text |
Document Section Search Parameters
| Parameter | Description |
|---|---|
query |
Natural language search query |
limit |
Max results (1-50) |
drucksachetyp |
Filter: Gesetzentwurf, Kleine Anfrage, Antrag, etc. |
chunkType |
Section type: problem, loesung, artikel, question, etc. |
wahlperiode |
Electoral period |
urheber |
Author/initiator (e.g., "Bundesregierung", "CDU/CSU") |
dateFrom / dateTo |
Date range (YYYY-MM-DD) |
scoreThreshold |
Minimum similarity (0-1, default 0.3) |
NLP Analysis Service
The server includes optional NLP analysis capabilities powered by a Python FastAPI service using spaCy's German language model.
Features
- Speech Extraction: Parse Plenarprotokolle into individual speeches with speaker, party, and type
- Word Analysis: Extract and lemmatize nouns, adjectives, and verbs with frequency counts
- Tone Analysis: 12 communication style metrics including aggression, collaboration, and solution-focus
- Topic Classification: Detect focus on 13 policy areas (migration, climate, economy, etc.)
- Speaker Profiles: Generate comprehensive profiles with statistics, vocabulary, and communication style
- Party Comparison: Compare parties based on their speeches with tone rankings and topic focus
Tone Metrics
| Metric | Description |
|---|---|
affirmative |
Positive vs critical adjective ratio (0-100) |
aggression |
Aggressive language intensity (0-100) |
labeling |
"Othering" language usage (0-100) |
solution_focus |
Solution vs problem-oriented verbs (0-100) |
collaboration |
Collaborative vs confrontational (0-100) |
demand_intensity |
Demanding language (fordern, müssen) (0-100) |
authority |
Obligation vs possibility modals (0-100) |
future_orientation |
Forward vs backward-looking (0-100) |
Topic Categories
Migration, Climate (Klima), Economy (Wirtschaft), Social Policy (Soziales), Security (Sicherheit), Health (Gesundheit), Europe (Europa), Digital, Education (Bildung), Finance (Finanzen), Justice (Justiz), Labor (Arbeit), Mobility (Mobilität)
Configuration
The analysis service runs as a separate Python container. Add to your .env:
ANALYSIS_SERVICE_URL=http://analysis:8000
Docker Compose with Analysis
services:
bundestag-mcp:
build: .
environment:
- ANALYSIS_SERVICE_URL=http://analysis:8000
depends_on:
- analysis
analysis:
image: ghcr.io/movm/bundestag-analysis:latest
expose:
- "8000"
Note: The analysis service requires ~1GB RAM for the spaCy model and takes ~30s to start.
DIP API
This server uses the official Bundestag DIP API:
- Documentation: https://dip.bundestag.de/
- API Docs: https://dip.bundestag.api.bund.dev/
- GitHub: https://github.com/bundesAPI/dip-bundestag-api
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run tests (
npm test) - Commit your changes (
git commit -m 'feat: Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。