geneva-tax-mcp
# Geneva Tax MCP
A **strictly read-only local MCP server** for Geneva (Switzerland) personal tax declarations (GeTax / Dr. Tax PDFs) and **tax simulations** via the official ESTV calculator.
> No write, submit or send tool. Read-only **by construction**.
## Quick examples
> Show my 2024 salary and tax totals, including the source page and extraction
> confidence.
> Compare my 2023 and 2024 declarations and show only recognized differences.
> Estimate the tax for a hypothetical single household in Geneva, using only
> the income and deductions I provide explicitly.
## Features
| MCP tool | Purpose |
|---|---|
| `list_tax_returns()` | Years and documents available in the local folder |
| `get_tax_return(year, sections, include_raw)` | Normalized declaration (ICC/IFD totals, key fields, provenance, confidence) |
| `compare_tax_returns(year1, year2)` | Differences between two years on recognized fields |
| `simulate(...)` | Tax simulation via the official ESTV calculator (Geneva included) |
| `find_location(query, year)` | Resolve a municipality/postal code to an ESTV tax location id |
| `list_deductions_estv(...)` | ESTV deduction sheet for a household |
Every extracted value carries its **provenance** (document, original label, and page for text-layer PDFs) and a **confidence** level (`exact` / `approx`), and distinguishes `zero` / `absent` / `unrecognized`. Responses only expose paths relative to the tax folder.
## Installation
Prerequisites: Python 3.11+, [Tesseract](https://github.com/tesseract-ocr/tesseract) (for OCR of scanned PDFs, optional).
```bash
git clone https://github.com/cyrusgch/geneva-tax-mcp.git mcp-taxes
cd mcp-taxes
py -m venv .venv # or python3 -m venv .venv
.venv/Scripts/python.exe -m pip install -e . # Windows
# .venv/bin/pip install -e . # macOS / Linux
# French language pack for OCR (optional, ~1.1 MB)
curl -L -o tessdata/fra.traineddata https://github.com/tesseract-ocr/tessdata_fast/raw/main/fra.traineddata
```
## MCP configuration
The tax folder is **auto-detected** (Windows: `My Drive\Impots`, `Impots`, `Documents\Impots`; macOS: Google Drive, `Documents/Impots`; Linux: `~/Impots`...). Override:
```bash
export GENEVA_TAX_DATA_ROOT="/path/to/my/tax/folder" # optional
```
### Claude Code
```bash
claude mcp add geneva-tax -s project \
-e "GENEVA_TAX_DATA_ROOT=C:\path\to\tax\folder" \
-- "/path/to/mcp-taxes/.venv/Scripts/python.exe" -m geneva_tax_mcp.server
```
### Codex (OpenAI)
```bash
codex mcp add geneva-tax \
--env "GENEVA_TAX_DATA_ROOT=C:\path\to\tax\folder" \
-- "C:\path\to\mcp-taxes\.venv\Scripts\python.exe" -m geneva_tax_mcp.server
```
Or directly in `~/.codex/config.toml`:
```toml
[mcp_servers.geneva-tax]
command = "C:/path/to/mcp-taxes/.venv/Scripts/python.exe"
args = ["-m", "geneva_tax_mcp.server"]
env = { GENEVA_TAX_DATA_ROOT = "C:/path/to/tax/folder" }
```
Verify with `codex mcp list`.
### Generic format (any stdio MCP client)
```json
{
"mcpServers": {
"geneva-tax": {
"command": "/path/to/.venv/bin/python",
"args": ["-m", "geneva_tax_mcp.server"],
"env": { "GENEVA_TAX_DATA_ROOT": "/path/to/tax/folder" }
}
}
}
```
## Tests
```bash
.venv/Scripts/python.exe -m pip install -e ".[dev]" # installs pytest
.venv/Scripts/python.exe -m pytest tests/ -q
```
**Anonymized** fixtures (Jean Dupont), no real data. Includes store, simulator (mocked HTTP), OCR, symlink and corrupted-PDF tests.
```bash
.venv/Scripts/ruff.exe check src tests scripts # lint
.venv/Scripts/ruff.exe format --check src tests scripts
.venv/Scripts/python.exe -m pytest tests/ -q --cov=geneva_tax_mcp --cov-fail-under=70
```
A GitHub Actions workflow (`.github/workflows/ci.yml`) runs lint, format and the coverage-gated suite on every push and pull request (Ubuntu + Windows, Python 3.11/3.12/3.13).
## Security and privacy
- No writes into the data folder (PDFs opened read-only). The only writes are the technical ESTV cache, which is **forced to the project `cache/` folder** (an external `ESTV_MCP_CACHE_DIR` cannot redirect it), gitignored, purged after 7 days, and contains **only the explicit simulation parameters you sent and the returned estimates** (no PDF content). Note: because the project lives in a cloud-synced folder (e.g. Google Drive), this cache is synchronized too — it is not encrypted.
- Simulations only send **explicit parameters** to the official ESTV calculator (`swisstaxcalculator.estv.admin.ch`, whitelisted), and return the sent payload for audit.
- Simulation results = **estimates produced by the official ESTV calculator**, non-binding.
- MCP responses expose paths **relative to the tax folder** (no username/machine layout).
## Known limitations
- 2025 "Dr. Tax" PDF: partially badly encoded fonts (partial extraction).
- Scanned forms: automatic OCR via Tesseract (`approx` confidence).
- `.tax` GeTax files: binary `dvbern-tax` format, **encrypted**, not usable without GeTax (see `scripts/spike_tax.py`).
- Undocumented ESTV API: may change without notice; retry on rate limit.
## Structure
```
src/geneva_tax_mcp/ MCP server, PDF parser, store, simulator, ESTV client, OCR
scripts/ tools (.tax spike)
tests/ anonymized fixtures + regression tests
ARCHITECTURE.md design decisions, trust boundaries and roadmap
AGENTS.md guide for AI agents
```
## License
MIT — see [LICENSE](LICENSE). The ESTV client (`src/geneva_tax_mcp/estv_client.py`) is vendored code from [noaahh/estv-mcp](https://github.com/noaahh/estv-mcp) (MIT), attribution kept.
TDQS
Scored across 6 tools
Each tool targets a distinct operation: listing available returns, retrieving a normalized declaration, comparing two declarations, listing deduction options, simulating tax, and resolving location IDs. Even the two list_* tools are clearly separated by their object.
Most tools follow a clear snake_case verb_noun pattern such as get_tax_return, list_tax_returns, and find_location. The single tool named simulate is the only mild deviation because it lacks an explicit object, but the pattern remains predictable overall.
Six tools is a well-scoped size for this domain. Each tool supports a distinct part of the tax workflow without redundancy or unnecessary surface area.
The toolset covers the full local workflow: discover available returns, retrieve normalized data, compare years, load deduction options, resolve locations, and run simulations. There are no apparent dead ends for an agent trying to analyze or estimate a household's Swiss tax situation.