Skip to main content
Glama
cyrusgch

geneva-tax-mcp

by cyrusgch
README.md
# 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

A3.5/5.0

Scored across 6 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness5/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues