DIP Bundestag Fraktion distribution
by nanditha1997
README.md
# DIP Bundestag Fraktion distribution
Look up politicians and calculate Bundestag Fraktion distributions using the DIP
API. Python computes the statistics; Groq writes the explanations. Includes a
command-line client and a bonus chat agent, both using the same MCP server.
## Setup
Requires Python 3.11+ and DIP/Groq API keys. From the project root in PowerShell:
```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
Copy-Item .env.example .env
```
Copy the example only when creating `.env` for the first time. Fill in your keys:
```dotenv
DIP_API_KEY=your-dip-key
LLM_API_KEY=your-groq-key
LLM_PROVIDER=groq
LLM_MODEL=openai/gpt-oss-120b
```
The MCP tools need only `DIP_API_KEY`. CLI distribution narration and bonus chat
also need the Groq key. Existing environment variables override `.env`.
On macOS/Linux, activate with `source .venv/bin/activate` and copy the example
with `cp .env.example .env`. Without PowerShell activation, use
`.\.venv\Scripts\python.exe` instead of `python` below.
## Run
```powershell
python -m src.cli distribution 20
python -m src.cli person "Friedrich Merz"
python -m bonus.chat_agent
```
The distribution CLI prints a table and a Groq summary.
The person CLI prints the DIP record.
Bonus chat accepts questions such as:
- "What is the party distribution in the 20th legislative session?"
- "Who is Friedrich Merz?"
Use `/quit` or `/exit` to leave chat. To ask one question and exit:
```powershell
python -m bonus.chat_agent --question "Who is Friedrich Merz?"
```
Add `--trace` to show MCP tool names and arguments on stderr:
```powershell
python -m src.cli distribution 20 --trace
python -m src.cli person "Friedrich Merz" --trace
python -m bonus.chat_agent --trace
```
## Architecture
Both clients start the same FastMCP server as a subprocess and communicate over
MCP stdio. The server fetches DIP records and runs the Python services. It returns
structured data and makes no LLM calls.
```text
CLI / bonus chat <-> MCP stdio <-> FastMCP server -> DIP API + Python services
CLI: MCP statistics -> Groq narrator -> printed summary
Bonus: Groq selects tool -> MCP data -> Groq formulates answer
```
| MCP tool | Input | Output |
| --- | --- | --- |
| `get_party_distribution` | `wahlperiode: int` | `{total, parties: [{party, count, percentage}]}` |
| `get_person_info` | `name: str` | Person fields, or `{found: false, message}` |
Clients never call the services directly. Bonus tool selection is driven by the
LLM using discovered schemas. A typical bonus lookup needs two Groq calls:
one to select the tool and one to formulate the answer.
## Assumptions and limits
- Distribution includes records with `funktion == "MdB"` or `", MdB,"` in
`titel`. This heuristic counts matching DIP records, not seats on a specific date.
- For distributions, Fraktion comes from the first nonempty matching
`person_roles` entry for the requested period, then the top-level value,
then `fraktionslos/unbekannt`. Person lookup uses the top-level snapshot.
An unknown snapshot is not proof of nonmembership.
- Fraktionen are parliamentary groups, not necessarily individual parties.
CDU/CSU is never split. Listed Wahlperioden refer to associated documents,
not continuous membership or tenure.
- DIP pagination deduplicates IDs. The client adapts observed singleton arrays
for `funktion` and `fraktion`; ambiguous arrays are rejected.
- Python rounds percentages to two decimals, so they may not total exactly 100%.
Numeric checks constrain LLM output but do not guarantee correct prose.
- API errors are reported without automatic retries. Groq 429 errors indicate
rate limits or quota restrictions. If CLI narration fails, the statistics
stay printed and the command exits with an error.
## Tests
```powershell
python -m pytest -q
python -m pip check
```
The 58 tests cover DIP requests and pagination, member filtering, aggregation,
person lookup, MCP responses and errors, narration, and the bonus tool-call flow.
They use mocked HTTP/model responses and in-memory MCP sessions, so no API keys
or internet access are needed. They do not verify live LLM quality or subprocess
startup.
## Bonus example: two tools in one question
Run `python -m bonus.chat_agent --trace` to see the selected tools. This sample
shows both tools used to answer one question; the client executes them sequentially.
The output below was captured from a run; live data and generated wording can vary.
```text
> who is alice weidel and what is the party distribution in 20th legislation
MCP tool: get_person_info {"name": "Alice Weidel"}
MCP tool: get_party_distribution {"wahlperiode": 20}
```
**Alice Weidel** is recorded by the DIP as a **member of the German Bundestag (MdB)** belonging to the **AfD Fraktion**. Her DIP identifier is **2187**.
In the 20th legislative period the DIP lists **697 matching Bundestag members**. The leading Fraktion is the **SPD** with 191 members (27.4 %), followed by **CDU/CSU** with 173 members (24.82 %) and the **BÜNDNIS 90/DIE GRÜNEN** with 114 members (16.36 %).
| Fraktion | Count | Percentage |
| --------------------- | ----- | ---------- |
| SPD | 191 | 27.4 % |
| CDU/CSU | 173 | 24.82 % |
| BÜNDNIS 90/DIE GRÜNEN | 114 | 16.36 % |
| FDP | 92 | 13.2 % |
| AfD | 86 | 12.34 % |
| DIE LINKE | 27 | 3.87 % |
| fraktionslos | 13 | 1.87 % |
| Die Linke (Gruppe) | 1 | 0.14 % |
*These figures refer to matching DIP member records under the MdB heuristic and are not a point‑in‑time seat count.*
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues