amtsblatt-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@amtsblatt-mcpSearch for public IT tenders in Basel-Stadt in the last 3 months"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
π¨π Part of the Swiss Public Data MCP Portfolio
π° amtsblatt-mcp
MCP server for amtsblattportal.ch β the Swiss official gazette portal (SHAB + 27 cantonal gazettes). Public procurement and official notices, person-data rubrics excluded by design.
Overview
The Amtsblattportal publishes roughly 2.79 million official notices: public procurement, cantonal and communal announcements, enactments, spatial planning β and also bankruptcies, debt collection, inheritance calls and civil-status records naming natural persons.
This server exposes only the first group. Rubrics carrying systematic natural-person data are not queryable, and no tool accepts a person's name, birth date or address. That is a deliberate data-protection decision, explained in Data Protection & Scope.
Anchor demo query: "Which public IT tenders were published in canton
Basel-Stadt in the last three months?"
β search_procurement(canton="BS", keyword="Informatik") β get_publication(id=β¦)
Related MCP server: swisstopo-mcp
Features
Fail-closed green allow-list β 49 released rubrics out of 152; everything else is blocked by default, including rubrics the upstream adds later
Explanatory refusals β a blocked rubric returns why, never a silent empty result and never a workaround hint
Procurement-aware β knows that only AR, BS, TI, ZG publish tenders here and that ZH routes them through simap.ch, so it explains instead of returning nothing
Deadline arithmetic in Europe/Zurich, the legally relevant timezone
Language deduplication β a notice published in de/fr/it counts once
Defensive XML parsing β the schema is per-sub-rubric; no rubric-specific path is hard-coded, and entity-escaped HTML bodies are unescaped and stripped
Egress allow-list, retry with backoff, structured JSON logging
Markdown or JSON output with per-response attribution +
provenance
Prerequisites
Python 3.11+
No API key. The read API of amtsblattportal.ch is freely accessible.
Installation
pip install amtsblatt-mcp
# or, without installing:
uvx amtsblatt-mcpFrom source:
git clone https://github.com/malkreide/amtsblatt-mcp
cd amtsblatt-mcp
pip install -e ".[dev]"Configuration
Claude Desktop
{
"mcpServers": {
"amtsblatt": {
"command": "uvx",
"args": ["amtsblatt-mcp"]
}
}
}Cloud deployment (SSE)
export MCP_TRANSPORT=sse
export MCP_API_KEY="$(openssl rand -hex 32)" # mandatory β fails loud if unset
export PORT=8000
amtsblatt-mcpVariable | Default | Purpose |
|
|
|
|
| SSE bind address. Defaults to loopback; set |
| β | Bearer token; required for SSE |
|
| Sliding-window rate limit |
|
| Egress allow-list. An override replaces the default entirely. |
|
| Taxonomy cache TTL (seconds) |
|
| Structured JSON logs to stderr |
Available Tools
Tool | Signature | Notes |
|
| Green rubrics enforced. Without |
|
|
|
|
| Full official text from XML. Re-checks the rubric after fetching; content from a blocked rubric is discarded. |
|
|
|
|
| Reachability, latency, cache age, scope metrics. |
All tools are readOnlyHint=True.
Example use cases
Question | Tool chain |
IT tenders in Basel-Stadt this quarter |
|
What is even queryable here? |
|
Why can't I search bankruptcies? |
|
Zoning changes in Zurich |
|
Full text of a notice |
|
Everything published about one company | β use |
Data Protection & Scope
The Amtsblattportal systematically publishes personal data of natural persons. Those publications are public β but making them systematically queryable by name through an AI agent is a repurposing the publication never intended, and a profiling instrument under the revised Swiss FADP (revDSG).
Four rules follow, and they are enforced in code, not in documentation:
Allow-list, never block-list. Not explicitly green β not queryable. New upstream rubrics are closed by default.
No person-based search entry in any tool signature.
No persistence. Publications have statutory deletion periods; a cache outliving them would actively undermine them. Only the taxonomy is cached.
Blocked β explained. Never a silent empty result, never a hint at circumvention.
What is excluded
π΄ Konkurse (KK), Schuldbetreibungen (SB), Schuldenrufe (LS, SR),
Nachlass (NA), Erbschaft/Testament/Ableben (ES, TE-*, VA-*),
Familie & Zivilstand (FZ-*, BV-*, BU-*), gerichtliche Vorladungen
(UV, GB-*, GE-*, SJ-BE), Baugesuche (BP-*), Grundbuch (GR-*),
Meldungskatalog GR (AA-GR).
π‘ Deferred: Steuerwesen, Anzeigen, Bewilligungen, Bildungs- und Kirchenwesen and the general catch-all rubrics.
The full audit trail β including three documented extensions to the source
specification β is in docs/rubric-classification.md.
The boundary with register-mcp
For publications about a specific company, use
register-mcp. It keeps full
rubric access β including a firm's own bankruptcy β but only ever keyed on a
company UID. A firm's insolvency is corporate data, not natural-person
profiling, and UID scoping makes name-based enumeration impossible.
amtsblatt-mcp has the opposite shape: broad search, narrow rubrics. It does
not expose the upstream uids parameter at all.
Architecture
Claude / MCP client
β
amtsblatt-mcp
β
ββββββββββ΄βββββββββ
β green gate β β rubrics.py: fail-closed allow-list
ββββββββββ¬βββββββββ (checked at the tool AND at the query builder)
β
ββββββββββ΄βββββββββ
β param allow- β β Silent Ignore guard
β list + quirks β β Silent Empty guard (taxonomy validation)
ββββββββββ¬βββββββββ β plausibility guard (corpus-size check)
β
ββββββββββ΄βββββββββ
β egress allow- β
β list (httpx) β
ββββββββββ¬βββββββββ
β
amtsblattportal.ch/api/v1
/publications Β· /publications/{id}/xml Β· /rubrics Β· /tenantsArchitecture A (live-API-only). The endpoints answer stably without authentication, so no bulk dump is maintained.
Verified upstream quirks (live-checked 2026-07-20)
Quirk | Behaviour | Defence |
Silent Ignore | An unknown parameter name returns HTTP 200 and the full corpus. | Query params built exclusively from |
Silent Empty | An unknown rubric value returns HTTP 200 with | Every code validated against the taxonomy before the call. |
Metadata only | The list endpoint and | Full text only via |
Sorting ignored |
| Sorted client-side. |
Missing | Returns 401, not 400 β it does not mean credentials are required. | Always injected; the 401 message says so. |
No page-size cap |
| Client-side cap of 100. |
Inconsistent plurals |
| Exact spellings encoded, not a pluralisation rule. |
Known Limitations
Uneven cantonal coverage. Only 16 of 29 mandates expose their own rubric taxonomy; AG, FR, GE, GL, JU, LU, NE, UR are still incomplete.
Deletion periods. Publications drop out of the API over time β hence pass-through only.
Procurement boundary. Most cantons, including ZΓΌrich, route tenders through simap.ch, outside this portal. There is no
OB-ZH, and no CPV classification exists here.No push. Polling only; no subscription or webhook mechanism.
Legally binding text is the signed PDF, not this API.
Testing
pip install -e ".[dev]"
PYTHONPATH=src pytest tests/ -m "not live" # 75 tests, no network
PYTHONPATH=src pytest tests/ -m live # hits the real API
ruff check src/ tests/The suite covers the mandatory portfolio set: green-rubric search with source URL, blocked rubric β explanation with zero HTTP calls, canton filtering, Europe/Zurich deadline arithmetic against a fixed "today", pagination across a page boundary, language deduplication, boolean normalisation, and API-unreachable handling. Fixtures are shortened real responses, consistently anonymised β no real personal data.
Project Structure
amtsblatt-mcp/
βββ src/amtsblatt_mcp/
β βββ rubrics.py # Fail-closed green allow-list β the scope decision
β βββ server.py # FastMCP server, 5 tools, quirk guards, XML parsing
β βββ _log.py # Structured JSON logging + per-tool call events
β βββ _middleware.py # Bearer auth + sliding-window rate limit (SSE only)
β βββ _otel.py # Optional OpenTelemetry wiring
βββ tests/
β βββ test_allowlist.py # Data-protection invariants (own CI job)
β βββ test_search.py # Search, procurement, pagination, dedup, errors
β βββ test_publication.py # XML parsing, deadlines, egress allow-list
β βββ fixtures.py # Anonymised real responses
βββ docs/
β βββ rubric-classification.md # Why each of the 152 rubrics is open/closed
βββ Dockerfile Β· compose.yaml # Hardened, non-root, read-only container
βββ server.json # MCP registry manifestChangelog
See CHANGELOG.md.
Contributing
See CONTRIBUTING.md. Changes to
src/amtsblatt_mcp/rubrics.py require an
explicit rationale in the PR description: releasing a rubric is a
data-protection decision, not a feature.
Security
See SECURITY.md for reporting and operator hardening notes.
License
MIT β see LICENSE.
Data source: amtsblattportal.ch, operated by SECO / State Secretariat for Economic Affairs on behalf of the Swiss Confederation. Freely usable, but without warranty of completeness or accuracy. Only the signed PDF of a publication is legally binding.
Author
Hayal Oezkan Β· malkreide
Credits & Related Projects
Part of the Swiss Public Data MCP Portfolio:
register-mcpβ Zefix commercial register with a company-UID join to the gazettes
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/malkreide/amtsblatt-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server