内容
> 🇨🇭 **瑞士公共数据MCP投资组合的一部分**
# 📰 公告门户

[](https://opensource.org/licenses/MIT)
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io/)
[](https://github.com/malkreide/amtsblatt-mcp)

> **amtsblattportal.ch** 的MCP服务器 — 瑞士官方公报门户(SHAB + 27个州公报)。公共采购和官方通知,**不包括个人数据栏目**。
[🇩🇪 德语版本](README.de.md)
## 概述
公告门户发布约 **279万** 条官方通知:公共采购、州和市政公告、法规、空间规划 — 以及破产、债务催收、遗产继承和民事身份记录中涉及自然人的栏目。
该服务器仅公开第一组栏目。涉及系统性自然人数据的栏目 **不可查询** ,且没有任何工具接受人员的姓名、出生日期或地址。这是出于数据保护的刻意决定,在[数据保护与范围](#数据保护与范围)中有解释。
**示例查询锚点:** *"哪些公共招标是提契诺州本月发布的? "*
### 演示

→ `gazette_search_procurement(canton="TI", only_language=True, language="it")` → `gazette_get_publication(id=…)`
对于其他州的采购,包括苏黎世、伯尔尼和巴塞尔-施塔特,请使用 [`swiss-procurement-mcp`](https://github.com/malkreide/swiss-procurement-mcp);参见[与 `swiss-procurement-mcp` 的边界](#与-swiss-procurement-mcp的边界)。
## 功能
- **关闭绿色允许列表** — 152个栏目中已发布49个;默认情况下,其他栏目均被阻止,包括上游添加的栏目。
- **解释性拒绝** — 被阻止的栏目会返回原因,从不返回空结果或变通提示。
- **采购感知** — 知道只有AR和TI仍然在此发布招标,BS在2024年减少,BL和VS是历史档案,`OB-ZG`在simap切换后从未填写,ZH通过simap.ch路由所有内容 — 因此它会解释而不是返回空结果。活动[被衡量,而不是从栏目标签中读取](docs/procurement-coverage.md)。
- **欧洲/ Zurich时区** 的截止日期算法
- **诚实的 多语言计数** — 门户发布每个语言的记录,具有不同的发布编号;相同的版本会被折叠,翻译版本会通过`language_mix`报告,而不是猜测,并且`only_language=True`提供单一语言视图。
- **防御性XML解析** — 模式按子栏目划分;没有硬编码的栏目特定路径,并且实体转义的HTML正文会被取消转义和剥离。
- **出口允许列表** 、重试和带回退的JSON日志记录
- **Markdown或JSON输出** ,每个响应都有出处 + `provenance`
## 先决条件
- Python 3.11+
- **无需API密钥** 。amtsblattportal.ch的读取API可免费访问。
## 安装
```bash
pip install amtsblatt-mcp
# 或,不安装:
uvx amtsblatt-mcp
```
从源码:
```bash
git clone https://github.com/malkreide/amtsblatt-mcp
cd amtsblatt-mcp
pip install -e ".[dev]"
```
## 配置
### Claude Desktop
```json
{
"mcpServers": {
"amtsblatt": {
"command": "uvx",
"args": ["amtsblatt-mcp"]
}
}
}
```
### 云部署(SSE)
```bash
export MCP_TRANSPORT=sse
export MCP_API_KEY="$(openssl rand -hex 32)" # 必需 — 如果未设置,则会报错
export PORT=8000
amtsblatt-mcp
```
| 变量 | 默认值 | 目的 |
|---|---|---|
| `MCP_TRANSPORT` | `stdio` | `stdio` 或 `sse` |
| `MCP_HOST` | `127.0.0.1` | SSE绑定地址。默认为环回地址;设置为`0.0.0.0`以在所有接口上公开(Docker镜像会这样做)。 |
| `MCP_API_KEY` | — | Bearer令牌;**必需** 用于SSE |
| `MCP_RATE_LIMIT` / `MCP_RATE_WINDOW` | `60` / `60` | 滑动窗口速率限制 |
| `MCP_ALLOWED_HOSTS` | `amtsblattportal.ch,www.amtsblattportal.ch` | 出口允许列表。覆盖项完全替换默认值。 |
| `RUBRICS_TTL` | `86400` | 分类缓存TTL(秒) |
| `LOG_LEVEL` | `INFO` | 结构化JSON日志输出到stderr |
## 可用工具
| 工具 | 签名 | 备注 |
|---|---|---|
| `gazette_search_publications` | `(keyword?, rubric?, sub_rubric?, canton?, date_start?, date_end?, limit=20, page=0, language='de', only_language=False)` | 强制绿色栏目。无`rubric`,所有绿色栏目都会被注入 — 仅关键词查询永远无法到达被阻止的栏目。 |
| `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`。具有无`OB-*`栏目的州会收到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`](https://github.com/malkreide/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`](https://github.com/malkreide/register-mcp) |
## 数据保护与范围
公告门户系统地发布 **自然** 人的个人数据。这些发布是公开的 — 但通过AI代理**按姓名系统查询** 是对发布的再利用,而这是修订后的瑞士联邦数据保护法(FADP)下的配置文件工具。
遵循四条规则,并在代码中强制执行,而不是在文档中:
1. **允许列表,从不阻止列表**。明确为绿色 ⇒ 可查询。新上游栏目默认关闭。
2. **任何工具中没有基于人员的搜索入口** 。
3. **没有持久性**。出版物有法定删除期限;超出期限的缓存会主动破坏它们。只有**分类**被缓存。
4. **阻止 ⇒ 解释**。从不返回空结果,从不提供变通提示。
### 排除的内容
🔴 破产(`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/rubric-classification.md) 中。
### 与 `register-mcp` 的边界
对于关于特定 **公司** 的发布,请使用 [`register-mcp`](https://github.com/malkreide/register-mcp)。它保持完整的栏目访问权限 — 包括公司自身的破产 — 但仅限于公司 **UID** 。公司的破产是公司数据,而不是自然人资料,UID范围使得基于名称的枚举成为不可能。
`amtsblatt-mcp`具有相反的形状:广泛搜索、狭窄的栏目。它根本不公开上游的`uids`参数。
### 与 `swiss-procurement-mcp` 的边界
**simap.ch 是瑞士公共采购的主要来源** — 所有26个州加上联邦,都有CPV和BKP代码、奖励和发布历史。使用 [`swiss-procurement-mcp`](https://github.com/malkreide/swiss-procurement-mcp) 进行采购问题。
**amtsblattportal.ch 是官方通知的主要来源** — 商业登记、空间规划、法规、州和市政公告。这是该服务器的作用;采购是其49个已发布栏目中的6个。
此处的采购在很大程度上是相同的招标的 **二次发布** ,现在已测量而不是假设。发布的XML包含 `<simapPublicationNumber>` ,当它来自simap.ch时,它会将两个公司库连接起来。整个2026年`OB-TI`语料库中, **546个记录中有503个(92.1%)带有** ;三个栏目在其标签中明确说明了这一点(`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`无法触及,而`gazette_search_procurement`为VS、OW和SH州提供服务,尽管它们没有活跃的`OB-*`栏目。 数字和方法见 [`docs/simap-overlap.md`](docs/simap-overlap.md)。
两个服务器故意保持分离:不同的来源,不同的重复使用条款,以及一个仅在服务器覆盖 **每个** 工具时才有意义的关闭栏目门。 参见 [`docs/procurement-coverage.md`](docs/procurement-coverage.md) 以获取数字。
## 架构
```
Claude / MCP 客户端
│
amtsblatt-mcp
│
┌────────┴────────┐
│ 绿色门 │ ← rubrics.py: 关闭允许列表
└────────┬────────┘ (在工具和查询构建器处检查)
│
┌────────┴────────┐
│ 参数允许- │
│ 列表 + 怪癖 │ ← Silent Ignore guard
│ │ ← Silent Empty guard(分类验证)
└────────┬────────┘ ← plausibility guard(语料库大小检查)
│
┌────────┴────────┐
│ 出口允许- │
│ 列表(httpx) │
└────────┬────────┘
│
amtsblattportal.ch/api/v1
/publications · /publications/{id}/xml · /rubrics · /tenants
```
**架构 A(仅限实时API)**。 端点稳定地回答,没有身份验证,因此不维护批量转储。
### 验证的上游怪癖(2026-07-20实时检查)
| 怪癖 | 行为 | 防御 |
|---|---|---|
| **静默忽略** | 未知参数 *名称* 返回HTTP 200 和 **完整语料库** 。 `canton=ZH`(单个输入错误)会静默删除过滤器。 | 查询参数仅使用`ALLOWED_GAZETTE_PARAMS`构建;plausibility guard 拒绝 > 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中删除 — 因此仅限直通。
- **采购边界** 。大多数州,包括 **苏黎世** ,通过simap.ch路由招标,不在此门户范围内。没有`OB-ZH`,也没有CPV分类。这里的内容与simap的不同列在 [`docs/simap-overlap.md`](docs/simap-overlap.md);`gazette_get_publication`报告`simap_publication_number`,因此镜像可与原件区分开来。
- **采购边界** 。 最多州,包括 **苏黎世** ,通过simap.ch路由招标,不在此门户范围内。没有`OB-ZH`,也没有分类。什么是此门户的内容,simap没有的内容列在 [`docs/simap-overlap.md`](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-TI` | 517 | 491 | 625 | 607 | 546 | 2026-07-27 | 活跃 |
| `OB-AR` | 95 | 85 | 79 | 56 | 40 | 2026-05-22 | 活跃 |
| `OB-BS` | 1 149 | 1 058 | 319 | 15 | **2** | 2026-05-20 | 2024年减少 |
| `OB-VS` | 0 | 1 052 | 1 | 0 | 0 | 2024-01-05 | 存档 — simap导入直到2023年末 |
| `OB-BL` | 0 | 74 | 0 | 0 | 0 | 2023-03-30 | 存档 — 标记为 «I N A K T I V» |
| `OB-ZG` | 0 | 0 | 0 | 0 | 0 | — | 从未填写 |
**仅TI和AR仍然活跃发布。** `OB-BS` 是说明性的例子:它的标签是一个普通的“Öffentliches Beschaffungswesen”,没有非活动标记,所以只有容量揭示了迁移 — 这就是为什么`active`被衡量,而不是被读取。使用`include_inactive=True`来访问BS、BL和VS存档。详细信息见 [`docs/procurement-coverage.md`](docs/procurement-coverage.md)。
- **无推送** 。仅限轮询;无订阅或Webhook机制。
- **法律约束文本** 是签署的PDF,而不是此API。
## 测试
```bash
pip install -e ".[dev]"
PYTHONPATH=src pytest tests/ -m "not live" # 75 测试,无网络
PYTHONPATH=src pytest tests/ -m live # 访问真实API
ruff check src/ tests/
```
测试覆盖了强制性投资组合集:绿色栏目搜索,带有源URL,**被阻止栏目 → 解释,无HTTP调用**,州过滤,欧洲/ Zurich截止日期算法针对固定“今天”,分页跨边界,语言重复,布尔归一化,API不可达处理。 Fixtures是缩短的真实响应,一致匿名 — 无实际个人数据。
## 项目结构
```
amtsblatt-mcp/
├── src/amtsblatt_mcp/
│ ├── rubrics.py # 关闭绿色允许列表 — 作用域决策
│ ├── server.py # FastMCP 服务器,5种工具,怪癖防护,XML解析
│ ├── _log.py # 结构化JSON日志记录 + 每工具调用事件
│ ├── _middleware.py # 持有者身份验证 + 滑动窗口速率限制(仅限SSE)
│ └── _otel.py # 可选的OpenTelemetry线路
├── tests/
│ ├── test_allowlist.py # 数据保护不变量(自己的CI作业)
│ ├── test_search.py # 搜索,采购,分页,重复,错误
│ ├── test_publication.py # XML解析,截止日期,出口允许列表
│ └── fixtures.py # 匿名真实响应
├── docs/
│ ├── rubric-classification.md # 每个152个栏目为何开放/关闭
│ ├── procurement-coverage.md # 测量的OB-*容量;为何`active`被衡量
│ └── simap-overlap.md # 镜像与原件,以simapPublicationNumber连接
├── scripts/
│ └── measure_procurement_coverage.py
├── Dockerfile · compose.yaml # 强化,非root,read-only 容器
└── server.json # MCP注册清单
```
## 更新日志
查看 [CHANGELOG.md](CHANGELOG.md)。
## 贡献
查看 [CONTRIBUTING.md](CONTRIBUTING.md)。对
[`src/amtsblatt_mcp/rubrics.py`](src/amtsblatt_mcp/rubrics.py)的更改需要PR描述中的明确理由:发布栏目是数据保护决策,而不是功能。
## 安全
查看 [SECURITY.md](SECURITY.md) 以获取报告和操作员强化说明。
## 许可证
MIT — 参见 [LICENSE](LICENSE)。
数据源:**amtsblattportal.ch**,由瑞士联邦经济事务秘书处SECO代表瑞士联邦政府运营。免费可用,但不保证完整性或准确性。只有已签署的出版物PDF才是具有法律约束力的。
## 作者
Hayal Oezkan · [malkreide](https://github.com/malkreide)
## 致谢和相关项目
瑞士公共数据MCP投资组合的一部分:
- [`register-mcp`](https://github.com/malkreide/register-mcp) — 带有公司UID连接的商业登记册与公报
<!-- mcp-name: io.github.malkreide/amtsblatt-mcp -->
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器