关于 amtsblatt-mcp
amtsblatt-mcp 提供对瑞士官方公报中部分公告的只读访问,包括采购、规划和公共公告,适用于检索官方出版物。严格的允许列表会排除系统性公开自然人数据的类别。
主要功能
- 01搜索允许访问的公报公告
- 02获取公告全文
- 03搜索采购公告
- 04筛选多语言公告结果
- 05说明被阻止的类别
适用场景
- 01查找提契诺州发布的招标
- 02搜索苏黎世的规划变更
- 03获取官方公告全文
- 04查看哪些公告类别可查询
MCP 相关
向 MCP 客户端提供只读搜索、检索、类别列表和数据源状态工具。默认拒绝的允许列表限制可访问类别,并阻止涉及个人数据的类别。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
🇨🇭 属于 瑞士公共数据 MCP 组合
📰 amtsblatt-mcp
面向 amtsblattportal.ch 的 MCP 服务——瑞士官方公报门户 (SHAB + 27 个州级公报)。提供公共采购和官方公告, 按设计排除涉及个人数据的栏目。
概览
Amtsblattportal 发布了约 279 万 条官方公告:公共采购、州级和市级公告、法规、空间规划, 以及涉及自然人的破产、债务追讨、遗产公告和民事身份记录。
此服务仅开放前一类内容。涉及系统性自然人数据的栏目不可查询,任何工具都不接受个人姓名、 出生日期或地址。这是有意为之的数据保护决定,详见 数据保护与范围。
示例查询: “提契诺州本月发布了哪些公共招标?”
演示
→
gazette_search_procurement(canton="TI", only_language=True, language="it") → gazette_get_publication(id=…)
如需查询其他州的采购信息(包括苏黎世、伯尔尼和巴塞尔城市州),
请使用 swiss-procurement-mcp;
参见 与 swiss-procurement-mcp 的边界。
功能
- 默认拒绝的绿色允许列表——152 个栏目中开放 49 个;其余栏目默认一律阻止, 包括上游之后新增的栏目
- 说明拒绝原因——被阻止的栏目会返回原因,不会静默返回空结果,也不会提供规避提示
- 了解采购情况——知道目前只有 AR 和 TI 仍在此发布招标,BS 在 2024 年逐步停止发布,
BL/VS 是历史档案,
OB-ZG在切换至 simap 后从未填充,而 ZH 则将所有内容都转至 simap.ch, 因而会作出说明,而非返回空结果。活动情况 通过测量得出,而不是根据栏目标签判断 - 截止日期计算使用具有法律效力的时区 Europe/Zurich
- 如实统计多语言内容——门户按语言发布记录,每种语言都有不同的出版编号;相同版本会合并,
翻译版本通过
language_mix报告,而非猜测;only_language=True可提供单一语言视图 - 防御性 XML 解析——架构按子栏目定义;不对特定栏目硬编码路径,并会对经过实体转义的 HTML 正文进行反转义和清理
- 出站流量允许列表、带退避机制的重试、结构化 JSON 日志
- Markdown 或 JSON 输出,每个响应都附带来源归属和
provenance
前提条件
- Python 3.11+
- 无需 API 密钥。 amtsblattportal.ch 的读取 API 可免费访问。
安装
pip install amtsblatt-mcp
# or, without installing:
uvx amtsblatt-mcp
从源代码安装:
git clone https://github.com/malkreide/amtsblatt-mcp
cd amtsblatt-mcp
pip install -e ".[dev]"
配置
Claude Desktop
{
"mcpServers": {
"amtsblatt": {
"command": "uvx",
"args": ["amtsblatt-mcp"]
}
}
}
云端部署(streamable-http)
export MCP_TRANSPORT=streamable-http
export MCP_API_KEY="$(openssl rand -hex 32)" # mandatory — fails loud if unset
export PORT=8000
amtsblatt-mcp
端点为 /mcp。
从 SSE 迁移。 在 0.18.0 之前,此服务仅使用 SSE,路径为
/sse+/messages。MCP 规范2026-07-28将 HTTP+SSE 重新归类为 已弃用,并设定了十二个月的移除期限,同时移除了协议级会话,因此 streamable-http 现为默认方式。MCP_TRANSPORT=sse仍然有效,且仍包含完整的 bearer 身份验证、速率限制和 CORS 机制——启动时会记录一条警告并注明截止日期。切换时请更新客户端 URL:路径变更才是会导致故障却不易察觉的部分。
| 变量 | 默认值 | 用途 |
|---|---|---|
MCP_TRANSPORT | stdio | stdio、streamable-http(别名 http)或已弃用的 sse |
MCP_HOST | 127.0.0.1 | HTTP 绑定地址。默认为回环地址;设为 0.0.0.0 可在所有接口上公开访问(Docker 镜像有意这样设置)。 |
MCP_STATELESS | (开启) | streamable-http 完全不跟踪会话——这是规范 2026-07-28 的原生模式,该规范没有协议级会话。它不再试图回答会话劫持和会话亲和性问题,而是直接消除这些问题(SEC-009、SCALE-002)。设为 0 / false / no 可为握手时代的客户端恢复会话;其他任何值都会保留默认设置。在 sse 下忽略此项,因为它没有无状态模式。 |
MCP_CORS_ORIGINS | (未设置) | 允许从浏览器调用端点的来源,以逗号分隔。未设置表示完全禁止跨源浏览器访问——不影响 stdio 和非浏览器客户端。对列出的来源会公开并接受 Mcp-Session-Id,因此浏览器客户端可以保持会话。* 会被接受,但会记录警告并禁用凭据,因为浏览器不允许通配符来源与凭据同时使用。 |
MCP_API_KEY | — | Bearer 令牌;每种 HTTP 传输方式都必须设置 |
MCP_RATE_LIMIT / MCP_RATE_WINDOW | 60 / 60 | 滑动窗口速率限制 |
RUBRICS_TTL | 86400 | 分类体系缓存 TTL(秒) |
LOG_LEVEL | INFO | DEBUG | INFO | WARNING | ERROR。始终在 stderr 输出结构化 JSON,每行一个对象——stdio 传输时,stdout 用于传输 MCP 协议。 |
日志记录
基于 structlog 构建。工具调用期间发出的每条事件都带有该次调用的 correlation_id,通过 contextvars 绑定——因此,即使是在 HTTP 请求路径深处记录的重试或出站请求拒绝,也能与触发它的请求关联起来,无需在每个函数签名中传递上下文。
| 级别 | 触发时机 |
|---|---|
DEBUG | 开始执行工具调用(tool_call_started)——可据此判断卡住的调用是否曾经启动 |
INFO | 工具调用顺利完成,并记录延迟 |
WARNING | 上游重试、出站请求被拒绝、身份验证失败、速率受限 |
ERROR | 工具调用引发异常 |
记录只包含异常的类型——绝不包含异常消息,也不包含上游响应正文。
{"event":"tool_call_started","tool":"gazette_search_procurement","correlation_id":"23221af26ae640c7","level":"debug","timestamp":"2026-07-27T22:20:07.494276Z"}
{"status":"ok","latency_ms":312,"event":"tool_call","tool":"gazette_search_procurement","correlation_id":"23221af26ae640c7","level":"info","timestamp":"2026-07-27T22:20:07.806Z"}
将这些记录发送到 SIEM,并针对 auth_failed、
rate_limited、egress_denied、green_gate_violation 和 blocked_publication_requested 设置告警——
最后两项表示有请求试图访问此服务不提供的 rubric。
可用工具
| 工具 | 签名 | 说明 |
|---|---|---|
gazette_search_publications | (keyword?, rubric?, sub_rubric?, canton?, date_start?, date_end?, limit=20, page=0, language='de', only_language=False) | 强制执行绿色类别。未指定 rubric 时,会注入所有绿色类别,因此仅使用关键词的查询无法触及受限类别。 |
gazette_search_detailed | 相同筛选条件 + top_n=3 | 聚合查询。 一次调用即可搜索,并并行获取排名前 top_n 条结果的全文。每份展开的文档都会经过相同的绿色类别检查;受限内容会被隐藏并计数,不会呈现。 |
gazette_search_procurement | (keyword?, canton?, date_start?, date_end?, include_inactive=False, limit=20, page=0, language='de', only_language=False) | OB-* 类别,以及公报原生子类别 AR-VS40、AR-OW40、BA-SH40。不含上述类别的州会返回 simap.ch 说明,且不会发起 HTTP 请求。没有 CPV——来源不包含该数据。 |
gazette_get_publication | (id, response_format='markdown') | 从 XML 获取完整官方文本。获取后会再次检查类别;受限类别的内容会被丢弃。 |
gazette_list_rubrics | (language='de', rubric_class='green', response_format='markdown') | rubric_class='all' 会显示包含交通灯类别和原因的完整分类体系——列出 ≠ 可查询。 |
gazette_source_status | (response_format='markdown') | 可访问性、延迟、缓存时长、范围指标。 |
所有工具均为 readOnlyHint=True。
示例用例
| 问题 | 工具调用链 |
|---|---|
| 本季度提契诺州的招标 | gazette_search_procurement(canton="TI", only_language=True, language="it") |
| simap.ch 没有的采购信息 | gazette_search_procurement(canton="VS") — 瓦莱州有 150 项授标记录,simap 上均未收录 |
| 其他任何州的招标 | → 使用 swiss-procurement-mcp |
| 这里到底有哪些内容可查询? | gazette_list_rubrics() |
| 为什么我不能搜索破产信息? | gazette_list_rubrics(rubric_class="all") |
| 苏黎世的分区规划变更 | gazette_search_publications(rubric="RP-ZH") |
| 某则公告的全文 | gazette_get_publication(id="fbf0ff9e-…") |
| 某公司的所有发布信息 | → 使用 register-mcp |
数据保护与范围
Amtsblattportal 会系统地发布自然人的个人数据。这些发布内容是公开的——但通过 AI Agent 按姓名系统性地查询这些信息,是一种原发布目的未涵盖的二次利用,也会构成瑞士修订版《联邦数据保护法》(revDSG)所指的画像工具。
由此确立了四项规则,并且这些规则是在代码中强制执行的,而非仅写在文档里:
- 使用允许列表,绝不使用禁止列表。 未明确列为绿色 ⇒ 不可查询。 上游新增类别默认关闭。
- 任何工具签名中都不提供按个人搜索的入口。
- 不持久化。 发布内容有法定删除期限;缓存若超过该期限,将直接损害这些规定的效力。仅缓存分类体系。
- 受限 ⇒ 说明原因。 绝不静默返回空结果,也绝不暗示如何绕过限制。
不包含的内容
🔴 破产程序(KK)、债务追偿(SB)、债务催告(LS、SR)、
遗产延期(NA)、继承/遗嘱/身故(ES、TE-*、VA-*)、
家庭与民事身份(FZ-*、BV-*、BU-*)、法院传票
(UV、GB-*、GE-*、SJ-BE)、建筑申请(BP-*)、土地登记簿(GR-*)、
GR 公告目录(AA-GR)。
🟡 暂缓处理:税务、公告、许可、教育与教会事务,以及通用兜底类别。
完整的审计记录——包括对源规范所作的三项有文档记录的扩展——见
docs/rubric-classification.md。
每项决策覆盖多少内容,是经过测量的,而非估算的。
docs/coverage-matrix.md 列出了源数据自身的
类别轴,并标示本服务对其的覆盖范围:2 804 063 条公告中,84.2 %
可访问,12.6 % 因决策而受限,3.3 % 尚未分类。仅破产类别就有 321 704 条公告——
这些公告存在于源数据中,但我们有意不提供访问。若没有这个数字,在审查中,“超出范围”
和“源数据中不存在”看起来并无区别,而本仓库此前就犯过这样的错误(见
SECURITY.md 中的 ARCH-003)。
与 register-mcp 的边界
对于特定公司的公告,请使用
register-mcp。它保留完整的
类别访问权限——包括公司的自身破产记录——但查询始终以公司的 UID 为依据。
公司的破产属于企业数据,而非自然人画像;按 UID 限定范围可杜绝按名称枚举。
amtsblatt-mcp 则恰好相反:搜索范围广,类别范围窄。它完全不提供上游的 uids 参数。
与 swiss-procurement-mcp 的边界
simap.ch 是瑞士公共采购的主要来源——涵盖全部 26 个州和联邦,提供 CPV 和 BKP 代码、
授标信息及公告历史。采购相关问题请使用
swiss-procurement-mcp。
amtsblattportal.ch 是官方公告的主要来源——包括商业登记、空间规划、法规、州级和市镇公告。 本服务正是用于此类信息;采购仅占其已发布 49 个类别中的 6 个。
此处的采购信息大多是相同招标的二次发布,如今这一点已有数据衡量,而非仅凭假设。
公告的 XML 在内容源自 simap.ch 时会包含 <simapPublicationNumber>,可据此精确关联两个数据集。
在完整的 2026 年 OB-TI 数据集中,546 条记录中有 503 条(92.1%)
包含该字段;六个 OB-* 类别中有三个在自身标签中明确说明了这一点
(OB-BL——“über Simap importiert (I N A K T I V)”)。
例外范围很小且界限明确:AR-VS40(瓦莱州,150 项授标)、
AR-OW40(上瓦尔登州,7 项)、BA-SH40(沙夫豪森州,2 项)以及提契诺的子类别
OB-TI65(“Avvisi di gara non CIAP”)完全不含 simap 引用。这是本门户的采购覆盖范围中
swiss-procurement-mcp 无法访问的部分;即使 VS、OW 和 SH 没有启用的 OB-* 类别,
gazette_search_procurement 仍可提供这些内容。数据和方法见
docs/simap-overlap.md。
这两个服务有意保持独立:来源不同、再利用条款不同,而且只有在覆盖服务中的每个工具时,
失效关闭的类别闸门才有意义。数据见
docs/procurement-coverage.md。
成熟度与阶段
阶段 1 — 只读。 六个工具均为只读;没有写入路径,也没有计划添加。请参阅 ROADMAP.md,了解各阶段的待办事项、明确不计划实现的内容,以及阶段转换所需条件。
这里最重要的范围限制不是阶段,而是绿色允许列表——包含系统性自然人数据的分类不可查询,且这一限制由代码强制执行,并在每次抓取后重新检查。阶段变化不会影响这一点。请参阅数据保护与范围。
SDK 和依赖项更新会通过 Dependabot PR 提交,因此对协议或 SDK 的破坏性变更会经过审慎审查,而不会悄然引入。
架构
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
架构 A(仅使用实时 API)。 这些端点无需身份验证即可稳定响应,因此不维护批量数据转储。
已验证的上游特性(2026-07-20 实时检查)
| 特性 | 行为 | 防护措施 |
|---|---|---|
| 静默忽略 | 未知参数名称会返回 HTTP 200 和完整语料库。canton=ZH(单数拼写错误)会被静默忽略,筛选条件也随之失效。 | 查询参数仅使用 ALLOWED_GAZETTE_PARAMS 构建;合理性检查会拒绝结果超过 2 000 000 条的情况。 |
| 静默返回空结果 | 未知分类值会返回 HTTP 200 和 total: 0,无法与确实没有匹配项的情况区分。 | 每个代码都会在调用前根据分类体系进行验证。 |
| 仅返回元数据 | 列表端点和 GET /publications/{id} 均会返回 content: null。 | 只能通过 /publications/{id}/xml 获取全文。 |
| 忽略排序 | pageRequest.sortOrders 会被接受并返回 200,但不起作用;返回的 sortOrders 为 []。 | 在客户端排序。 |
缺少 publicationStates | 返回 401,而不是 400——这并不表示需要凭据。 | 始终注入该参数;401 消息会说明这一点。 |
| 没有页面大小上限 | pageRequest.size=2000 会返回 2000 条项目。 | 客户端上限为 100。 |
| 复数形式不一致 | rubrics/cantons/subRubrics 为复数,keyword/tenant 为单数。 | 编码时使用精确拼写,不采用复数化规则。 |
已知限制
-
各州覆盖不均。 29 个类别中,只有 16 个提供了自己的分类体系;AG、FR、GE、GL、JU、LU、NE、UR 仍不完整。
-
删除时限。 公告会随时间从 API 中消失,因此仅透传。
-
采购边界。 包括 Zürich 在内的大多数州都通过 simap.ch 发布招标,不在本门户范围内。这里没有
OB-ZH,也没有 CPV 分类。此门户提供而 simap 不提供的内容列于docs/simap-overlap.md;gazette_get_publication会报告simap_publication_number,以便区分镜像与原始公告。 -
实测采购覆盖率(
publicationStates=PUBLISHED,2026-07-27,按日历年统计记录数——可通过python scripts/measure_procurement_coverage.py复现):分类 2022 2023 2024 2025 2026 最新记录 状态 OB-TI517 491 625 607 546 2026-07-27 活跃 OB-AR95 85 79 56 40 2026-05-22 活跃 OB-BS1 149 1 058 319 15 2 2026-05-20 2024 年期间逐步停止发布 OB-VS0 1 052 1 0 0 2024-01-05 存档 — 导入 simap 的数据截至 2023 年底 OB-BL0 74 0 0 0 2023-03-30 存档 — 标记为 «I N A K T I V» OB-ZG0 0 0 0 0 — 从未填充 目前只有 TI 和 AR 仍在持续发布。
OB-BS是一个很有说明性的案例: 它的标签只是普通的“Öffentliches Beschaffungswesen”,没有停用标记, 因此只有数据量能反映迁移情况——这也是active需要实测而不能直接读取的原因。使用include_inactive=True可访问 BS、BL 和 VS 的存档。 详情见docs/procurement-coverage.md。 -
不支持推送。 仅支持轮询;没有订阅或 webhook 机制。
-
具有法律效力的文本是签署后的 PDF,而不是此 API。
MCP 协议版本
通过 initialize 握手提供 | 2024-11-05 … 2025-11-25 — 握手支持的上限 |
| 通过逐请求封套提供 | 2026-07-28 |
| 选择方式 | 使用 HTTP 时,每个请求的 MCP-Protocol-Version 标头决定版本:2026-07-28 会作为不需要握手且没有会话的独立请求提供;握手时期的版本值或未提供标头时,则走 initialize 路径。使用 stdio 时,由客户端的首个请求决定:server/discover 探测会建立现代连接。 |
serverInfo | 名称为 amtsblatt_mcp,并包含软件包版本;该信息会写入每个现代结果的 _meta |
| 固定在 | _app.py 中的 MCP_PROTOCOL_VERSION,并由 server.py 重新导出 |
| SDK | mcp[cli]>=2.0.0,<3 |
| 缓存提示 | tools/list 和 server/discover:ttlMs 为 300000,cacheScope 为 public |
MCP Python SDK 会在会话层协商协议版本,且不提供用于设置版本的构造函数参数,因此无法通过配置固定版本。版本是通过声明常量固定的,并通过检测强制执行:
- **运行时,**版本不匹配会以
WARNING级别记录protocol_version_drift事件。 服务会继续运行。 - 在 CI 中,
tests/test_protocol_version.py会失败。
SDK 升级应该导致我们的构建失败,而不是导致某人在自己的环境中升级 mcp 后无法运行。
tests/test_modern_era.py 会验证这两个时期的协议是否确实可用——经过 bearer 验证、速率限制和 CORS,而不只是出现在常量中:它测试带有路由标头的原始 2026-07-28 请求、在 HTTP 和 stdio 上以 auto 模式运行的 mcp.Client,以及作为负向对照的旧式握手。
更新策略
- Dependabot 每月都会创建 SDK 更新 PR(
.github/dependabot.yml)。 - 当更新改变协议版本时,CI 测试会失败。修复方法不是盲目编辑常量:应阅读规范变更日志,确认服务仍按预期运行——尤其是绿色允许列表的不变量——然后在同一个提交中更新常量、本节和
CHANGELOG.md。 - 协议版本更新会在
CHANGELOG.md中明确说明,不会并入依赖更新条目。
缓存提示
规范 2026-07-28 为每个可缓存结果提供 ttlMs 和 cacheScope。
SDK 默认将两者设为“立即过期,绝不共享”,因此,不传入 cache_hints 的服务并非中立——它会要求每个客户端在每次连接时重新列出。此服务的工具列表在导入时注册,对每个调用者都相同,因此会声明其可缓存五分钟,并可在授权上下文之间共享(_app.py 中的 CACHE_HINTS)。
public 依据的是后一项属性,而非便利性:绿色允许列表在工具内部按请求执行,绝不会通过对调用者隐藏工具来实现。工具列表一旦开始因调用者而异,就必须在同一个提交中将范围改为 private。
基元:仅工具
此服务只公开工具,不公开资源或提示词。这是一个有意作出的决定,并非遗漏(ARCH-008)。
为什么不使用资源。 资源用于客户端可以枚举和缓存的、可识别且可列出的内容。此语料库包含 279 万份出版物,且每天都在增长;更重要的是,并非所有内容都可以提供。根据设计,包含系统性个人数据的分类标准会被排除,而这一排除会在两处执行:请求前对筛选条件进行绿色检查,以及获取后对返回文档进行检查。
资源 URI 会将出版物 ID 作为可枚举地址交给客户端。由于 ID 是不透明的,只有获取文档后才能知道它对应的分类标准——这正是设置获取后检查的原因。将出版物公开为资源,就意味着要么枚举尚未通过检查的 ID,要么仍然在获取时进行检查;此时资源抽象毫无益处,反而需要维护第二条内容路径,才能确保保证继续有效。这个仓库已经从中吸取过一次教训:之所以必须将聚合工具中的检查提取到共享辅助函数中,正是因为第二条内容路径最容易让这类保证悄然失效。
我们具体评估过一个候选项:
| 候选项 | 保留为工具的原因 |
|---|---|
gazette_list_rubrics | 形式上确实很像资源——它是一个有限、变化缓慢的分类体系,并且已经使用 TTL 缓存。但它的全部用途,是传达“列出 ≠ 可查询”:它会呈现红绿灯分类,以及每个受限分类被限制的原因。若将其作为资源,这种说明就会变成模型可能读取也可能不读取的文档;作为工具,它则是对模型所提问题的回答。 |
为什么不使用提示词。 问题模板会在第二个可能与允许列表不同步的位置,重复工具文档字符串中已有的指导。既然文档字符串会告知模型哪些分类可访问,一个信息来源比两个更安全。
返回类型:渲染后的文本,而非模型
工具返回 str——默认使用 Markdown,也可以通过 response_format='json' 返回 JSON——而不是 Pydantic 模型。这是对 SDK-002 的明确记录的偏离,是有意为之,并非疏忽。
渲染后的输出不是内部对象的序列化结果,而是为读者编写的内容。它包含来源说明、范围声明(green_rubrics_only)、合并语言变体时的去重警告,以及被屏蔽的 rubric 返回的、用来代替数据的说明。这些内容能避免模型得出错误结论,而且它们是文字说明,不是字段。
返回模型要么会丢掉这些内容,要么会把它们塞回字符串字段中;本质上并无不同,只是形式更繁琐。对于需要机器可读结果的调用方,json 格式已经满足需求。
**可能改变这一做法的情况:**调用方需要对结果进行计算,而不是阅读结果。届时,正确的做法是仅在 JSON 路径上使用类型化模型,而不是彻底改变每个工具的返回方式。
测试
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/ scripts/
ruff format --check src/ tests/ scripts/
python scripts/check_version_sync.py
这些检查与 CI 对相同目录运行的检查相同。dev extra 将 ruff 固定为 CI 安装的版本,因此本地运行结果与 CI 运行结果一致。
测试套件涵盖必需的组合:带来源 URL 的绿色 rubric 搜索、被屏蔽的 rubric → 返回说明且不发起 HTTP 调用、按州筛选、根据固定的“今天”计算 Europe/Zurich 截止日期、跨页分页、语言去重、布尔值规范化,以及 API 无法访问时的处理。测试数据是缩短后的真实响应,并统一做了匿名化处理——不含任何真实个人数据。
项目结构
amtsblatt-mcp/
├── src/amtsblatt_mcp/
│ ├── rubrics.py # Fail-closed green allow-list — the scope decision
│ ├── server.py # MCPServer, 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
│ ├── procurement-coverage.md # Measured OB-* volume; why `active` is measured
│ ├── coverage-matrix.md # Measured reach: which part of the corpus the tools cover
│ └── simap-overlap.md # Mirror vs. original, joined on simapPublicationNumber
├── scripts/
│ ├── measure_procurement_coverage.py
│ └── measure_coverage_matrix.py
├── Dockerfile · compose.yaml # Hardened, non-root, read-only container
└── server.json # MCP registry manifest
更新日志
请参阅 CHANGELOG.md。
贡献
请参阅 CONTRIBUTING.md。对
src/amtsblatt_mcp/rubrics.py 的更改必须在 PR 描述中明确说明理由:发布 rubric 是一项数据保护决策,而非功能。
安全
有关报告问题和运营方加固说明,请参阅 SECURITY.md。
许可证
MIT——请参阅 LICENSE。数据源声明:NOTICE.md。
数据源:amtsblattportal.ch,由 SECO / 瑞士经济事务国务秘书处代表瑞士联邦运营。可自由使用,但不保证内容完整或准确。只有出版物的签署 PDF 具有法律约束力。
作者
Hayal Oezkan · malkreide
致谢与相关项目
属于 Swiss Public Data MCP Portfolio:
register-mcp— Zefix 商业登记册,可通过公司 UID 与官方公报关联
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。