estv-mcp
MCP server for the Swiss federal tax calculator, providing income, wealth, inheritance, and corporate tax figures for all Swiss municipalities and tax years 2010-2026.
README
estv-mcp
MCP server for the official Swiss federal tax administration calculator at swisstaxcalculator.estv.admin.ch.
It wraps the undocumented JSON API behind that SPA (no key, no auth) and exposes income, wealth, lump-sum, inheritance and corporate tax figures for every Swiss municipality and tax years 2010-2026.
Tools
Household income and wealth:
| Tool | What it does |
|---|---|
find_location |
Postal code or place name to tax_location_id |
calculate_tax |
Federal, cantonal, municipal and church tax from gross income, wealth and family situation |
list_deductions |
The ESTV budget sheet with its default values, and which lines you may override |
calculate_tax_from_taxable_amounts |
Applies the tax scales to taxable income read off an assessment |
explain_tax_brackets |
The statutory rate ladder and where an income sits in it |
Comparing places:
| Tool | What it does |
|---|---|
compare_locations |
Ranks municipalities by total burden for the same household |
find_cheapest_nearby |
Same ranking, restricted to a radius around a point |
Planning:
| Tool | What it does |
|---|---|
plan_capital_withdrawals |
Cheapest way to split a pension lump sum across tax years |
deduction_value |
What a deduction is worth, franc by franc, at this income |
Other taxes:
| Tool | What it does |
|---|---|
calculate_capital_payment_tax |
Tax on a pillar 2 / pillar 3a lump-sum withdrawal, single place or ranked |
calculate_inheritance_tax |
Inheritance and gift tax by beneficiary relationship |
calculate_company_tax |
Profit and capital tax for a GmbH or AG |
get_tax_years |
Covered year range per calculator and the ESTV data version |
Install
uv sync
Register with Claude Code
From inside the repository:
claude mcp add estv-tax -- uv run --directory "$PWD" estv-mcp
Add -s user to make it available in every project rather than just this one.
Or in any MCP client config, with /path/to/estv-mcp replaced by wherever you
cloned it:
{
"mcpServers": {
"estv-tax": {
"command": "uv",
"args": ["run", "--directory", "/path/to/estv-mcp", "estv-mcp"]
}
}
}
Development
uv sync # install, including dev group
uv run pytest -q # tool-level tests plus golden values
uv run python scripts/stdio_smoke.py # real MCP stdio handshake
uv run ruff check . && uv run ruff format --check .
Tests hit the live ESTV API, so they need network access but no credentials. CI runs lint, the suite on Python 3.11/3.12/3.13, and the stdio smoke check on every push and pull request, plus weekly on a schedule.
Golden values
tests/golden.json pins recorded figures for a closed tax year, where the
scales should never move again. tests/test_golden.py re-checks them against
the live API with caching disabled, so the weekly run reports upstream drift as
a concrete diff rather than just confirming the endpoint still answers.
When a golden test fails, read the diff first. It usually means ESTV changed something, not that this repo is broken. Once the change is understood:
uv run python scripts/update_golden.py # prints every value that moved
Caching
Responses are cached under ~/.cache/estv-mcp for a week, which matters because
the planning tools fan out into dozens of sub-queries. ESTV_MCP_NO_CACHE=1
disables it, ESTV_MCP_CACHE_DIR and ESTV_MCP_CACHE_TTL override the
location and lifetime.
Notes
- Amounts are CHF per year.
income_type='employed'means gross salary; the model derives AHV/IV/EO, ALV, NBU and BVG contributions itself. - Deduction overrides go through
deductions={id: value}oncalculate_tax, using the ids fromlist_deductions(PRAEMIEN3Apillar 3a,SCHULDZINSENmortgage interest,KKPRAEMIENhealth insurance premiums,MIETAUSGABENrent,KINDBETREUUNGSKOSTENchildcare, and so on). compare_locations(scope='switzerland')covers ~2100 municipalities; only the extremes plus summary statistics come back so results stay readable.- Church tax is only charged for
confessionvalues other thannone. plan_capital_withdrawalsmodels the rule that payouts received in the same calendar year are aggregated before the rate applies, so splitting within a year correctly shows no saving. It does not model the federal aggregation of a spouse's payouts, and years beyond ESTV's published range are priced with the latest available scales. Both are stated in the result'scaveats.explain_tax_bracketsalways takes the amount from ESTV. The ladder is rebuilt from the published scale and reproduces ESTV exactly for the ZUERICH and BUND table layouts; splitting cantons apply extra rounding (flagged vialadder_note), and BL, FR, VD and VS publish formulas rather than tables, where the ladder is omitted rather than guessed at.- Figures are the official ESTV model, not a binding assessment.
API notes
Base URL: https://swisstaxcalculator.estv.admin.ch/delegate/ost-integration/v1/lg-proxy/operation/c3b67379_ESTV.
Every operation is a POST with a JSON body answering {"response": ...}. Bad
input returns an HTML 500 page rather than a structured error, which
api.EstvClient translates into an EstvError.
Enum values (from the SPA bundle):
- Relationship: single 1, married 2, concubinage 3, registered partnership 4
- Confession: reformed 1, roman catholic 2, christ catholic 3, none 4, other 5
- Income type: employed 1, self-employed 2, pensioner 3, other 4
- Language: de 1, fr 2, it 3, en 4
TaxGroupID: cantons alphabetically 1-26 (AG=1 … ZH=26), capitals 88, all of Switzerland 99
Two gotchas worth recording:
API_exportManyTaxScalesreuses the same field names for two different layouts. InZUERICHtablesAmountis the width of a band; inBUNDtables it is the lower threshold andTaxescarries the tax already due at it. Reading aBUNDtable with theZUERICHrule gives a plausible but wrong number, soTableTypehas to be honoured.- The geo search returns at most 200 results, silently clipping a large
radius.
find_cheapest_nearbydetects the cap and says so.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.