Server data from the Official MCP Registry
Swiss official gazettes (SHAB + cantonal) β procurement & notices, person-data rubrics excluded
Swiss official gazettes (SHAB + cantonal) β procurement & notices, person-data rubrics excluded
Valid MCP server (1 strong, 2 medium validity signals). No known CVEs in dependencies. Package registry verified. Imported from the Official MCP Registry. Trust signals: trusted author (33/34 approved).
3 files analyzed Β· 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
This plugin requests these system permissions. Most are normal for its category.
Set these up before or after installing:
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-malkreide-amtsblatt-mcp": {
"args": [
"amtsblatt-mcp"
],
"command": "uvx"
}
}
}From the project's GitHub README.
π¨π Part of the Swiss Public Data MCP Portfolio
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.
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=β¦)
provenancepip install amtsblatt-mcp
# or, without installing:
uvx amtsblatt-mcp
From source:
git clone https://github.com/malkreide/amtsblatt-mcp
cd amtsblatt-mcp
pip install -e ".[dev]"
{
"mcpServers": {
"amtsblatt": {
"command": "uvx",
"args": ["amtsblatt-mcp"]
}
}
}
export MCP_TRANSPORT=sse
export MCP_API_KEY="$(openssl rand -hex 32)" # mandatory β fails loud if unset
export PORT=8000
amtsblatt-mcp
| Variable | Default | Purpose |
|---|---|---|
MCP_TRANSPORT | stdio | stdio or sse |
MCP_HOST | 127.0.0.1 | SSE bind address. Defaults to loopback; set 0.0.0.0 to expose on all interfaces (the Docker image does this deliberately). |
MCP_API_KEY | β | Bearer token; required for SSE |
MCP_RATE_LIMIT / MCP_RATE_WINDOW | 60 / 60 | Sliding-window rate limit |
MCP_ALLOWED_HOSTS | amtsblattportal.ch,www.amtsblattportal.ch | Egress allow-list. An override replaces the default entirely. |
RUBRICS_TTL | 86400 | Taxonomy cache TTL (seconds) |
LOG_LEVEL | INFO | Structured JSON logs to stderr |
| Tool | Signature | Notes |
|---|---|---|
search_publications | (keyword?, rubric?, sub_rubric?, canton?, date_start?, date_end?, limit=20, page=0, language='de') | Green rubrics enforced. Without rubric, all green rubrics are injected β a keyword-only query can never reach a blocked one. |
search_procurement | (keyword?, canton?, date_start?, date_end?, include_inactive=False, limit=20, page=0) | OB-* only. A canton without an OB-* rubric gets a simap.ch explainer and no HTTP call. No CPV β the source has none. |
get_publication | (id, response_format='markdown') | Full official text from XML. Re-checks the rubric after fetching; content from a blocked rubric is discarded. |
list_rubrics | (language='de', rubric_class='green', response_format='markdown') | rubric_class='all' shows the full taxonomy with traffic-light classes and reasons β listed β queryable. |
source_status | (response_format='markdown') | Reachability, latency, cache age, scope metrics. |
All tools are readOnlyHint=True.
| Question | Tool chain |
|---|---|
| IT tenders in Basel-Stadt this quarter | search_procurement(canton="BS", keyword="Informatik") |
| What is even queryable here? | list_rubrics() |
| Why can't I search bankruptcies? | list_rubrics(rubric_class="all") |
| Zoning changes in Zurich | search_publications(rubric="RP-ZH") |
| Full text of a notice | get_publication(id="fbf0ff9e-β¦") |
| Everything published about one company | β use register-mcp |
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:
π΄ 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.
register-mcpFor 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.
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 Β· /tenants
Architecture A (live-API-only). The endpoints answer stably without authentication, so no bulk dump is maintained.
| Quirk | Behaviour | Defence |
|---|---|---|
| Silent Ignore | An unknown parameter name returns HTTP 200 and the full corpus. canton=ZH (singular typo) silently drops the filter. | Query params built exclusively from ALLOWED_GAZETTE_PARAMS; plausibility guard rejects results > 2 000 000. |
| Silent Empty | An unknown rubric value returns HTTP 200 with total: 0 β indistinguishable from a real no-hit. | Every code validated against the taxonomy before the call. |
| Metadata only | The list endpoint and GET /publications/{id} both return content: null. | Full text only via /publications/{id}/xml. |
| Sorting ignored | pageRequest.sortOrders is accepted with 200 but has no effect; sortOrders comes back []. | Sorted client-side. |
Missing publicationStates | Returns 401, not 400 β it does not mean credentials are required. | Always injected; the 401 message says so. |
| No page-size cap | pageRequest.size=2000 returns 2000 items. | Client-side cap of 100. |
| Inconsistent plurals | rubrics/cantons/subRubrics are plural, keyword/tenant singular. | Exact spellings encoded, not a pluralisation rule. |
OB-ZH, and no CPV
classification exists here.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.
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 manifest
See CHANGELOG.md.
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.
See SECURITY.md for reporting and operator hardening notes.
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.
Hayal Oezkan Β· malkreide
Part of the Swiss Public Data MCP Portfolio:
register-mcp β Zefix commercial
register with a company-UID join to the gazettesBe the first to review this server!
by Modelcontextprotocol Β· Developer Tools
Web content fetching and conversion for efficient LLM usage
by Toleno Β· Developer Tools
Toleno Network MCP Server β Manage your Toleno mining account with Claude AI using natural language.
by mcp-marketplace Β· Developer Tools
Create, build, and publish Python MCP servers to PyPI β conversationally.