内容
# SEAQ 采购智能 MCP 服务器
只读 [模型上下文协议](https://modelcontextprotocol.io) 服务器,允许 LLM 分析师用纯语言查询 **SEAQ** 的公共采购开放数据(安全服务子集)。小型管道下载原始 OCDS 源数据,过滤到安全招标,并规范为可查询的 SQLite 数据库(`DATA/seao.db`);MCP 服务器然后将该数据库变成分析师的助手,而不是原始 SQL 端点。

## 本仓库内容
| 文件 | 角色 |
| --- | --- |
| `fetch_seao_opendata.py` | 从 open.canada.ca Atom 源下载每周 OCDS JSON 转储到 `DATA/`。 |
| `build_raw_json_db.py` | 将这些转储(标题正则表达式 + UNSPSC 代码)过滤到 `DATA/filtered_records.json`。 |
| `build_work_sqlite_db.py` | 将过滤的记录规范化为 `DATA/seao.db`(关系模式 + FTS5 + 视图 + `schema_doc` 数据字典)。 |
| `seao_mcp_server.py` | 混合 MCP 服务器(FastMCP,只读),在 `DATA/seao.db` 上公开 9 种工具。 |
| `test_mcp_tools.py` | 对构建的 DB 进行每种工具的烟雾测试。 |
| `.mcp.json` | 用于 Claude Code 的项目范围 MCP 注册。 |
> **数据未提交。** 原始每周转储为多个 GB,构建的 `DATA/seao.db` 是生成的工件 - 两者都 `.gitignore`。使用以下管道自行构建数据库(正常连接几分钟,主要由下载步骤决定)。
## 为什么不直接指向通用 SQLite MCP?
数据中有三个部分会破坏简单的 SQL,因此精选工具为您处理它们:
| 陷阱 | 现实 | 工具如何处理 |
| --- | --- | --- |
| **供应商身份** | 同一家公司有多个名称拼写(Garda = 9 NEQ / 6 拼写)。按 `supplier_name` 分组会严重低估。 | 每个供应商指标解析名称 → `party.neq` 并聚合在 NEQ 上。 |
| **胜 / 败** | 未存储。投标者赢得过程当且仅当其 `(ocid, party_id)` 在 `award_supplier` 中。 | 胜率、正面交锋和利润率通过它得出,计算 **不同过程**(投标跨多个批次)。 |
| **gré à gré** | `direct` 奖励是单一来源的,**没有投标**。 | 胜率 / 利润率工具仅限于 `open` + `limited` 招标。 |
## 设置
需要 **Python 3.10+**。
```bash
git clone <您的仓库网址> SEAO_MCP
cd SEAO_MCP
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
## 构建数据库
运行管道一次以填充 `DATA/`:
```bash
# 1. 下载每周 OCDS 转储(写入 DATA/week_*.json — 几 GB)。
python fetch_seao_opendata.py
# 2. 过滤到安全服务招标(写入 DATA/filtered_records.json)。
python build_raw_json_db.py
# 3. 规范化为可查询的 SQLite DB(写入 DATA/seao.db)。
python build_work_sqlite_db.py
```
过滤规则(标题关键词 + UNSPSC 代码)位于 `build_raw_json_db.py` 底部 - 编辑它们以定位不同行业。`build_work_sqlite_db.py` 接受可选输入/输出路径:`python build_work_sqlite_db.py [input.json] [output.db]`。
验证构建:
```bash
python test_mcp_tools.py # 对 DATA/seao.db 进行每种工具的烟雾测试
```
## 用 MCP 客户端注册
服务器使用 stdio。 其 DB 路径默认为脚本旁边的 `DATA/seao.db`;使用环境变量 `SEAO_DB_PATH` 覆盖。
### Claude Code(此仓库)
已提交项目范围内的 [`.mcp.json`](.mcp.json) - 在 Claude Code 中打开项目,它将提示启用 **seao** 服务器。它通过项目的 `.venv` 启动服务器,因此请确保您创建了虚拟环境并构建了数据库。或者从 CLI 添加:
```bash
claude mcp add seao -- "$PWD/.venv/bin/python" "$PWD/seao_mcp_server.py"
```
### Claude Desktop
添加到 `claude_desktop_config.json`(macOS:`~/Library/Application Support/Claude/`,Windows:`%APPDATA%\Claude\`),将 `/ABS/PATH` 替换为此仓库的绝对路径:
```json
{
"mcpServers": {
"seao": {
"command": "/ABS/PATH/SEAO_MCP/.venv/bin/python",
"args": ["/ABS/PATH/SEAO_MCP/seao_mcp_server.py"],
"env": { "SEAO_DB_PATH": "/ABS/PATH/SEAO_MCP/DATA/seao.db" }
}
}
}
```
重启客户端。
### ChatGPT
ChatGPT 仅连接到 **远程** MCP 服务器(HTTP/SSE)作为 *自定义连接器* - 它无法像 Claude 那样启动本地 stdio 进程。因此,您可以用 HTTP 桥包装此 stdio 服务器,并使用隧道公开它。您需要支持自定义连接器/开发者模式的 ChatGPT 计划(Plus、Pro 或具有启用这些功能的商业/企业工作区)。
1. **桥接 stdio → HTTP/SSE** 使用 [`mcp-proxy`](https://github.com/sparfenyuk/mcp-proxy):
```bash
source .venv/bin/activate
pip install mcp-proxy
SEAO_DB_PATH="$PWD/DATA/seao.db" \
mcp-proxy --sse-port 8000 --sse-host 127.0.0.1 -- \
"$PWD/.venv/bin/python" "$PWD/seao_mcp_server.py"
```
这在 `http://127.0.0.1:8000/sse` 处提供服务器。
2. **通过 HTTPS 公开** 以便 ChatGPT(云)可以访问您的计算机:
```bash
ngrok http 8000 # 或 cloudflared / 任意隧道;复制 https URL
```
3. **在 ChatGPT 中添加连接器** → *设置 → 连接器 → 高级 → 开发者模式*,然后 *创建*。命名它,将 MCP 服务器 URL 设置为 `https://<您的隧道>/sse`,并且(由于没有身份验证)选择“无身份验证”。保存并启用它用于对话。
4. 开始聊天,选择 **seao** 连接器,并用纯语言提问。
> ⚠️ 隧道使您的本地数据库在隧道运行时可从互联网访问。此服务器是只读的(`run_sql` 拒绝写入/DDL 并以 `mode=ro` 打开数据库),但数据仍然暴露 - 仅在您使用时保持隧道打开,并优先使用身份验证隧道如果您的工具支持。
## 工具目录
映射到四个分析师目标:
### 灵活的核心
- **`describe_schema(table?)`** — 数据字典 + 代码查找(交货区名称,`value_unit` 含义,标签,采购方法)。首先调用它。
- **`run_sql(sql, limit?)`** — 单个只读 `SELECT` / `WITH`;写入/DDL/PRAGMA 被拒绝,连接以 `mode=ro` 打开,自动 `LIMIT`。长尾出口。
- **`search_processes(text?, unspsc?, buyer?, delivery_area?, status?, procurement_method?)`**
— 查找相关招标(FTS + 过滤器)。通常的起点。
### 1 — 战术情报(评估新招标)
- **`incumbent(buyer, service?, unspsc?)`** — 谁拥有 / 曾拥有买方的合同,具有授予和合同价值,最近优先。
- **`price_benchmark(unspsc?, text?, delivery_area?, value_unit?)`** — 授予价值和中标分布,以及中标者与下一个最低竞争者的利润率。
### 2 — 竞争对手分析
- **`supplier_profile(supplier)`** — 胜率,总获胜,方法混合,顶级买家,目标领域和最频繁的竞争对手(由 NEQ 解析)。
- **`head_to_head(supplier_a, supplier_b)`** — 两个公司竞标同一招标的频率和谁赢得(包括两个公司分别赢得不同批次)。
### 3 — 买方行为
- **`buyer_profile(buyer)`** — 开放 vs. gré à gré 混合,总支出,供应商忠诚度/轮换(顶级供应商份额),按年计算的金额。
### 4 — 宏观市场趋势
- **`market_overview(year?, unspsc?, text?, top_n?)`** — 按年(TAM)计算的总价值,顶级-N 集中度(整合),方法趋势,地理分布。
任何未涵盖的内容 → `run_sql`,由 `describe_schema` 指导。
## 示例(分析师 → 工具)
| 问题 | 调用 |
| --- | --- |
| “谁拥有 CSS Marguerite-Bourgeoys 的安全保障,价值多少?” | `incumbent("Marguerite-Bourgeoys", "securite")` |
| “蒙特利尔地区(90152100)守卫的平均中标价格?” | `price_benchmark(unspsc="90152100", delivery_area="6")` |
| “Garda 在开放招标中的胜率?” | `supplier_profile("Garda")` |
| “Commissionnaires vs. Neptune — 谁赢得更多?” | `head_to_head("Commissionnaires", "Neptune")` |
| “Québec 省警察局是否轮换供应商?” | `buyer_profile("Surete du Quebec")` |
| “市场中前 3 家公司的份额?” | `market_overview(top_n=3)` |
## 数据源和许可
源数据:[SEAQ 开放数据](https://www.donneesquebec.ca/) 通过 [open.canada.ca](https://open.canada.ca/) 以 OCDS 格式发布。本仓库的代码在 [MIT License](LICENSE) 下发布;底层采购数据仍受其原始发布者的条款约束。