Skip to main content
Glama
matanmill304

knesset-mcp

by matanmill304
README.md
# knesset-mcp

An MCP server for the Israeli Knesset's open data: bills, laws, regulations, MKs, parties,
committees, plenum sessions, votes and parliamentary questions. It covers every table in
the Knesset's [OData v4 API](https://knesset.gov.il/OdataV4/ParliamentInfo/), including
per-MK plenum votes. No API key is needed.

## Tools

**Shortcuts for common questions**, each one call that does the joins for you:

| Tool | Answers |
|---|---|
| `find_person` | Who is this MK? Returns their Id, latest faction and Knessets served. |
| `get_person` | Every role someone held: MK, faction, minister, committee roles, plus bill counts. |
| `search_bills` | Bills by Hebrew name, Knesset, status, type (government/private/committee) or sponsor. |
| `get_bill` | One bill: status, sponsors, name history, merges, committee and plenum sessions, votes, documents. |
| `get_vote` | One plenum vote: totals, breakdown by faction, and who voted how. |
| `get_person_votes` | How one MK voted, newest first, with what each vote was about and what "for" meant on it. |
| `get_session` | One plenum sitting or committee meeting: agenda, documents and protocols, votes. |

**Generic access to every table:**

| Tool | Does |
|---|---|
| `list_tables` | All tables grouped by topic, with what each holds. |
| `describe_table` | Fields, types, links usable in `$expand`, and a sample row. |
| `query` | Any OData query (`filter`, `select`, `orderby`, `expand`, `top`, `skip`, `count`). |
| `get_record` | One record by Id. |

All data is in Hebrew, so search with Hebrew terms. Tools that take names reject Latin
text and ask for the Hebrew form.

## Setup

```bash
python3 -m venv .venv
.venv/bin/pip install -e '.[dev]'
```

Add to Claude Code (stdio):

```bash
claude mcp add knesset -- /absolute/path/to/knesset-mcp/.venv/bin/knesset-mcp
```

Run as an HTTP service (for remote clients, or a claude.ai custom connector once hosted):

```bash
.venv/bin/knesset-mcp --transport streamable-http --host 0.0.0.0 --port 8000
```

The endpoint is `http://<host>:8000/mcp`.

## Tests

```bash
.venv/bin/pytest            # offline unit tests
.venv/bin/pytest -m live    # end-to-end against knesset.gov.il
```

## API quirks this server handles

Checked against the live API on 2026-09-23:

- **Paging:** the API returns 100 rows per page plus a `@odata.nextLink`. The server follows
  it, up to 200 rows per tool call, and returns `next_skip` when more exist.
- **Firewall:** knesset.gov.il's firewall rejects any query containing `;` and any `$apply`
  with `aggregate()`, answering with an HTML "access denied" page. Both are refused locally
  with an explanation. Vote tallies are counted in the server instead.
- **`$expand` on single records** (`KNS_Bill(123)?$expand=...`) returns HTTP 500. Records
  are always fetched as `KNS_Bill?$filter=Id eq 123`, where expand works.
- **Table and type names differ** in places (the set `KNS_DocumentQuerie` has the type
  `KNS_DocumentQuery`). The schema is read live from `$metadata`, so this is handled.
- **Broken tables:** `V_Lobbyists`, `V_LobbyistsClients` and `KNS_DocumentQuerie` are listed but return 404, and `KNS_DocumentIsraelLaw` is empty.
- **A bare "for" or "against" is meaningless:** voting against "to accept the reservation" is
  not voting against the law. Every ballot `get_person_votes` returns carries `ForMeans` and
  `AgainstMeans` from the vote itself.
- **Joins never stop at one page:** internally, `fetch_all` pages through every matching row
  (up to 2,000) and logs a warning if it hits that ceiling, instead of silently returning 200.
- **Join keys that aren't obvious:** `KNS_PlenumVoteResult.MkId` = `KNS_Person.Id`, and
  `ItemID` on votes and agenda items is the `KNS_Bill.Id` when the item is a bill.
- Responses are cached in memory for an hour, and the schema for a day.

## Not yet covered

- Reading document contents. Tools return links to .pdf/.doc files, including protocols,
  but don't extract their text.
- Analytics across many records, such as how often an MK voted against their party. Each
  call reads at most 200 rows. That needs a local mirror of the data.

TDQS

A4.1/5.0

Scored across 11 tools

Disambiguation5/5

Each tool targets a distinct entity or operation: table discovery, generic querying, people, bills, votes, and sessions are clearly separated. Even generic get_record is distinguishable from get_person/get_bill by its purpose as a raw record fetch.

Naming Consistency4/5

Most tools follow a clear verb_noun pattern: list_tables, describe_table, find_person, get_person, search_bills, get_bill, get_vote, get_session. The bare 'query' tool is a minor outlier, but the overall convention is still predictable.

Tool Count5/5

With 11 tools, the server is well-scoped for a Knesset data domain. Each tool covers a meaningful operation without redundancy or bloat.

Completeness5/5

The tool set covers the full read-only lifecycle: table discovery and schema inspection, generic querying, person profiles and voting records, bill search and detail, plenum votes, and sessions. The generic query and get_record tools also prevent dead ends for entities without dedicated endpoints.

Maintenance

ActivityMaintained
ResponsivenessNo issues