涵安智行 MCP 服务器(Python / uvx)
把后端 REST API 包成 MCP(Model Context Protocol)工具,供支持 MCP 的宿主直接调用: VS Code(Copilot agent 模式)、Claude Desktop、华为云 AgentArts 的 MCP 接入等。
- 零第三方依赖(纯标准库),stdio + 换行分隔 JSON-RPC 2.0,Python ≥ 3.9
- 16 个工具,参数已扁平化、结果已整理,模型不用自己拼 URL
- 不需要任何环境变量:后端地址默认生产域名,凭据直接写进包内
- 已按 Python 包组织:云端宿主只接受 JSON 配置 +
npx/uvx,这里用uvx拉起 - 通行判定(
assess_vehicle_access)在本地按统一口径计算,不消耗接口
云端宿主:两种接法,别选错(这一步最容易 404)
| 接法 | 平台里填什么 | 谁跑进程 | 常见失败 |
|---|---|---|---|
| A. stdio(平台拉起进程) | JSON:command: uvx + args |
平台沙箱 | 平台没装 uv / 无外网 → 启动失败 |
| B. HTTP(托管 MCP 服务) | 服务地址 + 路径 | 你在服务器/Docker 里跑 | 路径填错 / 没监听 → 404(如 .../runtimes/<id>/invocations) |
看到 404 Not Found / “MCP服务目标地址路径错误” → 你用的是 B,但服务还在 stdio 模式。
按 HOSTED-HTTP.md 起 HTTP 传输即可(--transport http,路径 /mcp)。
A 模式:stdio,直接用这份 JSON(今天就能用,已验证)
现成的配置文件在仓库根目录(复制整段贴进平台即可,env 可以空着):
| 文件 | 现在能用? | 说明 |
|---|---|---|
mcp-config.testpypi.json |
✅ 已发布、已验证 | 从 TestPyPI 装 1.4.0,需要带 --index-url |
mcp-config.vscode.json |
✅ 已验证 | VS Code 形态(servers + type: stdio),内容同上 |
mcp-config.pypi.json |
⏳ 正式 PyPI 发布后 | 最干净的形态,无 --index-url |
mcp-config.wheel.json |
⏳ 上传 wheel 后 | 完全不依赖 PyPI,走自己的静态目录 |
主推这一份(今天就能跑):
{
"mcpServers": {
"waterlog": {
"command": "uvx",
"args": [
"--index-url",
"https://test.pypi.org/simple",
"--from",
"waterlog-mcp==1.4.0",
"waterlog-mcp"
],
"env": {}
}
}
}
env留空:服务密钥已硬编码在包内,宿主没有环境变量也能用;- 换后端地址:在
waterlog-mcp后面加"--base-url=https://your-domain"(必须是工具名之后); - 访问 PyPI 慢:把
--index-url换成https://pypi.tuna.tsinghua.edu.cn/simple; - 让服务器自己生成:
uvx --from waterlog-mcp==1.4.0 waterlog-mcp --print-config。
B 模式:HTTP,服务地址 + 路径
uv tool install waterlog-mcp==1.4.0 --index-url https://test.pypi.org/simple
waterlog-mcp --transport http --host 127.0.0.1 --port 8090 --path /mcp
平台里填:服务地址 https://water.cubestudio.top,路径 /mcp(旧版 SSE 用 /mcp/sse),
健康检查 /health。Nginx 段、systemd、Dockerfile 都在 HOSTED-HTTP.md。
上传/接入前先自检(强烈建议)
# A 模式:按 JSON 里的 command+args 真拉起一次
python scripts/smoke_test.py mcp-config.testpypi.json
# B 模式:直接打服务地址,也可以拿平台的网关地址来定位问题
python scripts/smoke_test.py --url https://water.cubestudio.top/mcp
# 想看工具清单
python scripts/smoke_test.py --url https://water.cubestudio.top/mcp --list-tools
uvx 在全新机器上会自动下载一个受管 Python(CPython 3.9+),首次启动多花 10~30 秒属正常。
⚠️ 包内硬编码的服务密钥 = 公开可读:任何能
uvx --from waterlog-mcp ...的人都能解包看到HARDCODED_API_KEY。它只读(监测/预警/灾情/地名/规划路线),不会泄露用户数据、不能写、 不能读/me,所以按「公开只读凭据」对待即可;但请:
- 不要给它加任何写权限(
NewServiceKeyVerifier只挂DefaultDelegationScopes());- 发现滥用就换
AGENT_SERVICE_KEY+ 发新版(旧版的常量随之失效);- 更严格时可用自托管 wheel 放到需要鉴权的路径,而不是公开 PyPI。
发布到 PyPI
cd deploy/mcp
python -m unittest discover -s tests -v # 43 个用例(含版本/配置一致性校验)
uv build # 生成 dist/*.whl 与 *.tar.gz
$env:UV_PUBLISH_TOKEN = "pypi-..." # 见下方「token 怎么建」
.\scripts\publish.ps1 # 或直接 uv publish
token 怎么建(这里最容易踩坑)
PyPI 已关闭「用户名 + 密码」上传,只接受 API Token:
- https://pypi.org/manage/account/token/ → Add API token
- 首次发布时 Scope 只能选 All projects(包还不存在时无法选单包),发布后再换成
Project: waterlog-mcp;
- 首次发布时 Scope 只能选 All projects(包还不存在时无法选单包),发布后再换成
- 复制
pypi-...串,用环境变量传,不要在uv publish的交互提示里输入账号名:
# ✅ 正确
$env:UV_PUBLISH_TOKEN = "pypi-AgEIcHlwaS5vcmc..." # 用户名由 uv 自动填 __token__
uv publish
# ❌ 错误(会得到 403 Username/Password authentication is no longer supported)
uv publish # 然后 Enter username: <你的账号> / Enter password: <账号密码>
scripts/publish.ps1 已经处理好这些:只传 --token(不再同时传 --username,否则 uv 报
cannot be used with --token),并支持 -Repository testpypi / -DryRun。
先发 TestPyPI 演练
$env:UV_PUBLISH_TOKEN = "<TestPyPI token>"
.\scripts\publish.ps1 -Repository testpypi
# 验证(TestPyPI 上是私有/预发布,需要显式指定 index)
uvx --index-url https://test.pypi.org/simple --from waterlog-mcp==1.4.0 waterlog-mcp --version
两个源不通用:TestPyPI 和 PyPI 是两套账号、两套 token。 小技巧:
pypi-AgENdGVzdC5weXBpLm9yZw...这类 base64 里带test.pypi.org的,就是 TestPyPI 的 token, 拿去发正式 PyPI 一定 403。
当前状态
- TestPyPI:已发布 1.4.0(
https://test.pypi.org/project/waterlog-mcp/1.4.0/),uvx --index-url https://test.pypi.org/simple安装验证通过。 - 正式 PyPI:尚未发布(
https://pypi.org/pypi/waterlog-mcp/json仍 404)。需要一把 pypi.org 的 token,命令同上。
凭据从哪来
| 凭据 | 哪里拿 | 有效期 | 能做什么 |
|---|---|---|---|
服务密钥 apiKey(推荐) |
后端 .env 的 AGENT_SERVICE_KEY |
长期(改配置才失效) | 只读:监测、预警、灾情、地名、规划路线 |
用户令牌 token |
小程序/网页登录后的 accessToken |
30 分钟 | 按该用户权限 |
委派令牌 dlg.* |
POST /api/v1/ai/consultations 响应的 delegation.token |
10 分钟 | 只读,绑定某次会话 |
为什么不要硬编码用户令牌:accessToken 30 分钟就过期,dlg.* 只有 10 分钟,
写死在云端 MCP 里半天就全 401。托管 Agent 请用服务密钥。
生成服务密钥
# 1) 生成 32 字节随机串
openssl rand -hex 32
# Windows PowerShell:
# $b=New-Object byte[] 32;[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b);($b|%{$_.ToString('x2')})-join''
# 2) 写进后端 .env 并重启
AGENT_SERVICE_KEY=<上一步的串>
AGENT_SERVICE_KEY_LABEL=agentarts-triage # 可选,只用于日志区分
# 3) 把同一个串粘到 src/waterlog_mcp/server.py 顶部
# HARDCODED_API_KEY = '<上一步的串>'
服务密钥用 X-Api-Key 头(也接受 Authorization: Bearer <服务密钥>),后端会把它映射成
service:agent 身份 + service 角色:只读白名单内的接口 200,写接口一律 403
(SERVICE_KEY_FORBIDDEN),/me、管理端、AI 咨询都不可用。
启动
# 云端 / 宿主调用(包内已硬编码服务密钥):什么都不用配
uvx --from waterlog-mcp==1.4.0 waterlog-mcp
# 本地源码直接跑
python -m waterlog_mcp --base-url=http://127.0.0.1:6732
# 指定凭据
python -m waterlog_mcp --base-url=http://127.0.0.1:6732 --api-key=<服务密钥>
python -m waterlog_mcp --base-url=http://127.0.0.1:6732 --token=<用户令牌>
凭据解析优先级(靠前的优先):
- 启动参数
--api-key=/--token= - 环境变量
WATER_API_KEY/WATER_API_TOKEN - 同目录(或包目录)
water-mcp.config.json的apiKey/token - 包内
HARDCODED_API_KEY - 运行时工具
configure_water_api({apiKey})(进程内有效,重启失效)
用户令牌
token始终优先于服务密钥apiKey。只配了服务密钥时,写操作会返回一条 「需要运维/管理员令牌」的明确提示,而不是让模型去撞 403。
| 变量 / 参数 | 必填 | 说明 |
|---|---|---|
--base-url / WATER_API_BASE_URL / baseUrl |
否 | 后端地址,默认 https://water.cubestudio.top |
--api-key / WATER_API_KEY / apiKey |
二选一 | 长期只读服务密钥 |
--token / WATER_API_TOKEN / token |
二选一 | 用户令牌或 dlg.* 委派令牌 |
--admin-token / WATER_ADMIN_TOKEN / adminToken |
否 | 触发/写回研判时用(需 operator/admin) |
--timeout-ms / WATER_TIMEOUT_MS / timeoutMs |
否 | 单次请求超时,默认 20000 |
工具清单
| 工具 | 对应接口 | 说明 |
|---|---|---|
connection_status |
GET /health/live + GET /monitors |
先看后端是否活、凭据是否有效(只回显末 4 位) |
configure_water_api |
— | 运行时注册 apiKey / token / baseUrl |
list_monitors |
GET /monitors |
找监测点、拿 monitorId |
get_latest_reading |
GET /monitors/{id}/latest |
当前水位/风险/新鲜度 |
get_monitor_trend |
GET /monitors/{id}/trend |
涨落趋势 |
get_monitor_prediction |
GET /monitors/{id}/prediction |
未来水位预测 |
list_alerts |
GET /alerts |
预警(可按未读/状态/点位过滤) |
list_confirmed_floods |
GET /disaster-reports/map |
已确认积水点 |
geocode_search |
GET /maps/geocode |
地名 → 坐标 |
reverse_geocode |
GET /maps/reverse-geocode |
坐标 → 地址 |
plan_avoidance_route |
POST /routes/plan |
按进气口规划避障路线 |
assess_vehicle_access |
本地计算 | 安全余量 → 通行结论 |
list_disaster_reports |
GET /disaster-reports |
上报列表(服务密钥/运维可看全部) |
get_disaster_report |
GET /disaster-reports/{id} |
单条详情(含图片签名 URL) |
analyze_disaster_report |
POST /disaster-reports/{id}/analyze |
触发自动研判(需用户令牌 + 运维/管理员) |
update_disaster_report |
PUT /disaster-reports/{id} |
人工复核 / 修正(写操作) |
在 VS Code 里注册
.vscode/mcp.json(或用户级 mcp.json),推荐与云端同一份 uvx 配置:
{
"servers": {
"waterlog": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "waterlog-mcp==1.4.0", "waterlog-mcp"]
}
}
}
本地改代码时用源码路径:
"command": "python",
"args": ["-m", "waterlog_mcp", "--base-url=https://water.cubestudio.top"],
"env": { "PYTHONPATH": "deploy/mcp/src" }
自测
python -m unittest discover -s tests -v
覆盖:协议握手、工具清单与 JSON Schema、参数映射(含 origin/destination 组装)、
错误码透传(isError)、本地判定口径、查询参数空值过滤、凭据优先级与服务密钥通道
(X-Api-Key)、configure_water_api 两种凭据、只读写回拒绝、stdin 关闭时把应答写完,
以及 pyproject / configs/*.json 与 __version__ 的一致性(防止发版后配置忘改)。
与 OAS 的关系
| 方式 | 适合 | 差异 |
|---|---|---|
| OpenAPI(agentarts-openapi.json) | AgentArts 网关 Target / 插件 | 覆盖全部 29 个操作,参数是原始 REST 形态 |
| MCP(本目录) | 本地或远程 MCP 宿主 | 只挑 Agent 真正需要的 16 个,参数扁平、错误统一、含本地计算工具 |
两者都指向同一套后端接口;委派令牌(delegation)与服务密钥(X-Api-Key)在两边都适用。
Metadata
Release files for waterlog-mcp 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| waterlog_mcp-1.4.0.tar.gz | 37.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| waterlog_mcp-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.1 kB
Release files / waterlog_mcp-1.4.0.tar.gz
| Download URL | waterlog_mcp-1.4.0.tar.gz |
|---|---|
| Size | 37.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
efb55b0ce320b645cbb3fd38c0323380abf4c64d221ac7a0d82a6de1db2a5143
|
|
BLAKE2b-256 checksum How to use checksums |
80c79af74b7c45cef4942ac1454a6e75962f0457fc54bec3a0030e85020b6e7d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / waterlog_mcp-1.4.0-py3-none-any.whl
| Download URL | waterlog_mcp-1.4.0-py3-none-any.whl |
|---|---|
| Size | 26.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
94a5cd34680e50214e225d0153e685d738c72e41c7874d0386e1620eaaac6e47
|
|
BLAKE2b-256 checksum How to use checksums |
d00c2edb74885f4e0e9f3c12e322d520dea077479bc4825feaab00e7382373cd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for waterlog-mcp 1.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.