wop-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wop_mcp-0.1.0/.github/workflows/ci.yml +23 -0
- wop_mcp-0.1.0/.github/workflows/release.yml +59 -0
- wop_mcp-0.1.0/.gitignore +14 -0
- wop_mcp-0.1.0/.python-version +1 -0
- wop_mcp-0.1.0/LICENSE +21 -0
- wop_mcp-0.1.0/PKG-INFO +153 -0
- wop_mcp-0.1.0/README.md +125 -0
- wop_mcp-0.1.0/commitlint.config.js +23 -0
- wop_mcp-0.1.0/lefthook.yml +11 -0
- wop_mcp-0.1.0/pyproject.toml +59 -0
- wop_mcp-0.1.0/src/wop_mcp/__init__.py +4 -0
- wop_mcp-0.1.0/src/wop_mcp/config.py +48 -0
- wop_mcp-0.1.0/src/wop_mcp/doc_center.py +208 -0
- wop_mcp-0.1.0/src/wop_mcp/http_utils.py +42 -0
- wop_mcp-0.1.0/src/wop_mcp/keypair.py +134 -0
- wop_mcp-0.1.0/src/wop_mcp/main.py +148 -0
- wop_mcp-0.1.0/tests/test_doc_center.py +171 -0
- wop_mcp-0.1.0/tests/test_http_utils.py +91 -0
- wop_mcp-0.1.0/tests/test_keypair.py +108 -0
- wop_mcp-0.1.0/tests/test_main.py +181 -0
- wop_mcp-0.1.0/uv.lock +1247 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: astral-sh/setup-uv@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: uv sync
|
|
22
|
+
- name: Test with coverage gate (>=95%)
|
|
23
|
+
run: uv run pytest --cov=wop_mcp --cov-report=term-missing --cov-fail-under=95
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
# PYPI_TOKEN 配置于 org 级 Actions secrets(visibility 覆盖本仓);
|
|
11
|
+
# 不引用 environment,避免依赖环境自动创建策略
|
|
12
|
+
permissions:
|
|
13
|
+
# write 仅用于创建 GitHub Release 条目;PyPI 发布走 PYPI_TOKEN,不占 token 权限
|
|
14
|
+
contents: write
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.12'
|
|
20
|
+
- name: Verify tag matches pyproject version
|
|
21
|
+
# 版本单一源纪律:tag vX.Y.Z 必须与 pyproject [project].version 严格一致,
|
|
22
|
+
# 不一致即刻 fail(防止 tag 与发布产物版本漂移)
|
|
23
|
+
run: |
|
|
24
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
25
|
+
PROJ_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
26
|
+
if [ "$TAG_VERSION" != "$PROJ_VERSION" ]; then
|
|
27
|
+
echo "::error::tag ${GITHUB_REF_NAME}(${TAG_VERSION})与 pyproject.toml version(${PROJ_VERSION})不一致"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
- name: Install
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
# pytest/pytest-cov 属测试工具链而非运行时依赖,CI 显式安装
|
|
34
|
+
pip install -e . pytest pytest-cov
|
|
35
|
+
- name: Test with coverage gate (>= 95%, must pass before publish)
|
|
36
|
+
run: python -m pytest --cov=wop_mcp --cov-fail-under=95
|
|
37
|
+
- name: Build sdist and wheel
|
|
38
|
+
run: |
|
|
39
|
+
pip install build
|
|
40
|
+
python -m build
|
|
41
|
+
- name: Publish to PyPI
|
|
42
|
+
# 发布步骤位于测试全绿之后,失败不留半发布状态
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
with:
|
|
45
|
+
password: ${{ secrets.PYPI_TOKEN }}
|
|
46
|
+
- name: Create GitHub Release
|
|
47
|
+
# 仅在 PyPI 发布成功后执行(前置步骤失败即跳过)——Release 条目 = 已发布信号;
|
|
48
|
+
# 资产:创建即挂 dist/*(sdist+wheel,二进制归档语义);已存在(workflow 重跑)时
|
|
49
|
+
# 补传 --clobber 幂等,不重复建条目;说明用 --generate-notes 基线,发布后可随时人工编辑
|
|
50
|
+
env:
|
|
51
|
+
GH_TOKEN: ${{ github.token }}
|
|
52
|
+
run: |
|
|
53
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
54
|
+
if gh release view "$GITHUB_REF_NAME" -R "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
|
55
|
+
gh release upload "$GITHUB_REF_NAME" -R "$GITHUB_REPOSITORY" dist/* --clobber
|
|
56
|
+
else
|
|
57
|
+
gh release create "$GITHUB_REF_NAME" -R "$GITHUB_REPOSITORY" \
|
|
58
|
+
--title "wop-mcp ${VERSION}" --generate-notes dist/*
|
|
59
|
+
fi
|
wop_mcp-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
wop_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wop-platform
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
wop_mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: wop-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 万联易达开放平台(WOP)MCP Server —— 文档获取与密钥对生成
|
|
5
|
+
Project-URL: Homepage, https://github.com/wop-platform/wop-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/wop-platform/wop-mcp
|
|
7
|
+
Project-URL: Changelog, https://github.com/wop-platform/wop-mcp/releases
|
|
8
|
+
Project-URL: Issues, https://github.com/wop-platform/wop-mcp/issues
|
|
9
|
+
Author: wop-platform
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: mcp,open-platform,rsa,sm2,wanlianyida,wop
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: cryptography>=41
|
|
24
|
+
Requires-Dist: gmssl>=3.2.2
|
|
25
|
+
Requires-Dist: mcp>=2.0
|
|
26
|
+
Requires-Dist: wop-python-sdk>=0.1.7
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# wop-mcp
|
|
30
|
+
|
|
31
|
+
**万联易达开放平台(WOP)MCP Server —— 让 AI 助手安全、正确地完成 WOP 对接**
|
|
32
|
+
|
|
33
|
+
[](https://www.python.org/downloads/)
|
|
34
|
+
[](LICENSE)
|
|
35
|
+
[](https://modelcontextprotocol.io/)
|
|
36
|
+
[]()
|
|
37
|
+
|
|
38
|
+
## 简介
|
|
39
|
+
|
|
40
|
+
`wop-mcp` 是万联易达开放平台(WOP)的 [Model Context Protocol](https://modelcontextprotocol.io/) Server,
|
|
41
|
+
帮助开发者通过 AI 助手(Claude Desktop、Cursor、Claude Code 等)完成 WOP 对接:
|
|
42
|
+
|
|
43
|
+
- 📚 **文档获取**:接入指南、API 索引、接口详情(自动穿透文档 URL 的时间戳后缀)
|
|
44
|
+
- 🔐 **密钥对生成**:RSA2048 / SM2,公钥按分发契约编码返回,可直接上报平台
|
|
45
|
+
- 🛡️ **安全纪律**:私钥只写入本地 0600 文件,**永不回显进 AI 对话**(对齐 [wop-skills](https://github.com/wop-platform/wop-skills) SECURITY 纪律)
|
|
46
|
+
- 🔌 **即插即用**:stdio transport,`uvx wop-mcp`(PyPI 发布后)一行接入
|
|
47
|
+
|
|
48
|
+
## 工具总览
|
|
49
|
+
|
|
50
|
+
| 工具 | 说明 |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `wop_overview()` | 接入指南(三步流程、securityReq 组合、公钥编码、SDK 列表)+ 实时 `llms.txt` API 索引 |
|
|
53
|
+
| `wop_api_detail(api)` | API 详情;入参支持 API 路径(如 `/staffing/open/v1/invoice/apply`,可带 POST 前缀、支持省略业务域前缀的后缀匹配)、接口中文名(如 `转账提交`)、完整 `.md` URL |
|
|
54
|
+
| `wop_link_detail(url)` | 文档中心任意子页 / 外链内容(相对路径自动拼接基址,如 `llms-full.txt`) |
|
|
55
|
+
| `wop_gen_key_pair(algorithm)` | `RSA2048`(别名 `RSA`)或 `SM2`;返回 `publicKey`(契约编码)+ `privateKeyFile`(0600 路径) |
|
|
56
|
+
|
|
57
|
+
## 快速开始
|
|
58
|
+
|
|
59
|
+
### 方式一:uvx 直接运行(PyPI 发布后)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uvx wop-mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 方式二:源码运行
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/wop-platform/wop-mcp.git
|
|
69
|
+
cd wop-mcp
|
|
70
|
+
uv sync
|
|
71
|
+
uv run wop-mcp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 在 AI 工具中配置
|
|
75
|
+
|
|
76
|
+
### Cursor
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"wop-mcp": {
|
|
82
|
+
"command": "uvx",
|
|
83
|
+
"args": ["wop-mcp"],
|
|
84
|
+
"timeout": 600,
|
|
85
|
+
"autoApprove": ["wop_overview", "wop_api_detail", "wop_link_detail"]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Claude Desktop
|
|
92
|
+
|
|
93
|
+
macOS:`~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
94
|
+
Windows:`%APPDATA%\Claude\claude_desktop_config.json`
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"wop-mcp": {
|
|
100
|
+
"command": "uvx",
|
|
101
|
+
"args": ["wop-mcp"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
源码方式将 `command`/`args` 替换为:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"command": "uv",
|
|
112
|
+
"args": ["--directory", "/path/to/wop-mcp", "run", "wop-mcp"]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
> `wop_gen_key_pair` 写本地私钥文件,建议保留人工确认(未列入 autoApprove)。
|
|
117
|
+
|
|
118
|
+
## 环境变量
|
|
119
|
+
|
|
120
|
+
| 变量 | 默认值 | 说明 |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `WOP_DOC_BASE_URL` | `https://assets.wanlianyida.com/gtsp/platformResource/wop/doc-center` | 文档中心基址 |
|
|
123
|
+
| `WOP_KEYS_DIR` | `./keys` | 私钥落盘目录(0600 文件) |
|
|
124
|
+
| `WOP_INDEX_CACHE_TTL` | `300` | `llms.txt` 索引进程内缓存秒数(`0` 关闭) |
|
|
125
|
+
| `WOP_HTTP_TIMEOUT` | `15` | HTTP 超时秒数 |
|
|
126
|
+
|
|
127
|
+
## 安全纪律
|
|
128
|
+
|
|
129
|
+
- **私钥只落盘不回显**:`wop_gen_key_pair` 返回值不含任何私钥材料;私钥以 0600 权限独占创建于 `WOP_KEYS_DIR`。
|
|
130
|
+
- **公钥即上报格式**:RSA = X.509 SPKI DER 的 Base64;SM2 = 未压缩点 `04‖X‖Y`(65 字节)的 Base64([crypto-strategy-spec](https://github.com/wop-platform/wop-specs) §3.4,D10/D12)。
|
|
131
|
+
- **契约自检**:每次生成后经 [wop-python-sdk](https://github.com/wop-platform/wop-python-sdk) 官方加载器回读(含 SM2 曲线校验),编码不合约即报错。
|
|
132
|
+
- `keys/` 目录已入 `.gitignore`,**严禁**将私钥文件提交入库或粘贴进对话。
|
|
133
|
+
|
|
134
|
+
## 算法说明(与 spec 的差异)
|
|
135
|
+
|
|
136
|
+
本工具密钥生成支持 `RSA2048` / `SM2`。`crypto-strategy-spec` v0.4 草案的 securityReq
|
|
137
|
+
国际族档位为 RSA3072/RSA4096;平台实际接受的 RSA 档位以商户中心审核为准。
|
|
138
|
+
国密族与国际族禁止跨族组合。
|
|
139
|
+
|
|
140
|
+
## 开发
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
uv sync # 安装依赖(含 dev 组)
|
|
144
|
+
uv run pytest --cov=wop_mcp --cov-fail-under=95 # 测试 + 覆盖率门禁
|
|
145
|
+
npm i -g lefthook && lefthook install # 激活 git 钩子(commitlint + 覆盖率门禁)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- 提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/zh-CN/)(commitlint 校验)
|
|
149
|
+
- CI:GitHub Actions,Python 3.10–3.13 矩阵,覆盖率红线 95%
|
|
150
|
+
|
|
151
|
+
## 许可证
|
|
152
|
+
|
|
153
|
+
[MIT](LICENSE) © 2026 wop-platform
|
wop_mcp-0.1.0/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# wop-mcp
|
|
2
|
+
|
|
3
|
+
**万联易达开放平台(WOP)MCP Server —— 让 AI 助手安全、正确地完成 WOP 对接**
|
|
4
|
+
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://modelcontextprotocol.io/)
|
|
8
|
+
[]()
|
|
9
|
+
|
|
10
|
+
## 简介
|
|
11
|
+
|
|
12
|
+
`wop-mcp` 是万联易达开放平台(WOP)的 [Model Context Protocol](https://modelcontextprotocol.io/) Server,
|
|
13
|
+
帮助开发者通过 AI 助手(Claude Desktop、Cursor、Claude Code 等)完成 WOP 对接:
|
|
14
|
+
|
|
15
|
+
- 📚 **文档获取**:接入指南、API 索引、接口详情(自动穿透文档 URL 的时间戳后缀)
|
|
16
|
+
- 🔐 **密钥对生成**:RSA2048 / SM2,公钥按分发契约编码返回,可直接上报平台
|
|
17
|
+
- 🛡️ **安全纪律**:私钥只写入本地 0600 文件,**永不回显进 AI 对话**(对齐 [wop-skills](https://github.com/wop-platform/wop-skills) SECURITY 纪律)
|
|
18
|
+
- 🔌 **即插即用**:stdio transport,`uvx wop-mcp`(PyPI 发布后)一行接入
|
|
19
|
+
|
|
20
|
+
## 工具总览
|
|
21
|
+
|
|
22
|
+
| 工具 | 说明 |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `wop_overview()` | 接入指南(三步流程、securityReq 组合、公钥编码、SDK 列表)+ 实时 `llms.txt` API 索引 |
|
|
25
|
+
| `wop_api_detail(api)` | API 详情;入参支持 API 路径(如 `/staffing/open/v1/invoice/apply`,可带 POST 前缀、支持省略业务域前缀的后缀匹配)、接口中文名(如 `转账提交`)、完整 `.md` URL |
|
|
26
|
+
| `wop_link_detail(url)` | 文档中心任意子页 / 外链内容(相对路径自动拼接基址,如 `llms-full.txt`) |
|
|
27
|
+
| `wop_gen_key_pair(algorithm)` | `RSA2048`(别名 `RSA`)或 `SM2`;返回 `publicKey`(契约编码)+ `privateKeyFile`(0600 路径) |
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
### 方式一:uvx 直接运行(PyPI 发布后)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uvx wop-mcp
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 方式二:源码运行
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/wop-platform/wop-mcp.git
|
|
41
|
+
cd wop-mcp
|
|
42
|
+
uv sync
|
|
43
|
+
uv run wop-mcp
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 在 AI 工具中配置
|
|
47
|
+
|
|
48
|
+
### Cursor
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"wop-mcp": {
|
|
54
|
+
"command": "uvx",
|
|
55
|
+
"args": ["wop-mcp"],
|
|
56
|
+
"timeout": 600,
|
|
57
|
+
"autoApprove": ["wop_overview", "wop_api_detail", "wop_link_detail"]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Claude Desktop
|
|
64
|
+
|
|
65
|
+
macOS:`~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
66
|
+
Windows:`%APPDATA%\Claude\claude_desktop_config.json`
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"wop-mcp": {
|
|
72
|
+
"command": "uvx",
|
|
73
|
+
"args": ["wop-mcp"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
源码方式将 `command`/`args` 替换为:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"command": "uv",
|
|
84
|
+
"args": ["--directory", "/path/to/wop-mcp", "run", "wop-mcp"]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
> `wop_gen_key_pair` 写本地私钥文件,建议保留人工确认(未列入 autoApprove)。
|
|
89
|
+
|
|
90
|
+
## 环境变量
|
|
91
|
+
|
|
92
|
+
| 变量 | 默认值 | 说明 |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `WOP_DOC_BASE_URL` | `https://assets.wanlianyida.com/gtsp/platformResource/wop/doc-center` | 文档中心基址 |
|
|
95
|
+
| `WOP_KEYS_DIR` | `./keys` | 私钥落盘目录(0600 文件) |
|
|
96
|
+
| `WOP_INDEX_CACHE_TTL` | `300` | `llms.txt` 索引进程内缓存秒数(`0` 关闭) |
|
|
97
|
+
| `WOP_HTTP_TIMEOUT` | `15` | HTTP 超时秒数 |
|
|
98
|
+
|
|
99
|
+
## 安全纪律
|
|
100
|
+
|
|
101
|
+
- **私钥只落盘不回显**:`wop_gen_key_pair` 返回值不含任何私钥材料;私钥以 0600 权限独占创建于 `WOP_KEYS_DIR`。
|
|
102
|
+
- **公钥即上报格式**:RSA = X.509 SPKI DER 的 Base64;SM2 = 未压缩点 `04‖X‖Y`(65 字节)的 Base64([crypto-strategy-spec](https://github.com/wop-platform/wop-specs) §3.4,D10/D12)。
|
|
103
|
+
- **契约自检**:每次生成后经 [wop-python-sdk](https://github.com/wop-platform/wop-python-sdk) 官方加载器回读(含 SM2 曲线校验),编码不合约即报错。
|
|
104
|
+
- `keys/` 目录已入 `.gitignore`,**严禁**将私钥文件提交入库或粘贴进对话。
|
|
105
|
+
|
|
106
|
+
## 算法说明(与 spec 的差异)
|
|
107
|
+
|
|
108
|
+
本工具密钥生成支持 `RSA2048` / `SM2`。`crypto-strategy-spec` v0.4 草案的 securityReq
|
|
109
|
+
国际族档位为 RSA3072/RSA4096;平台实际接受的 RSA 档位以商户中心审核为准。
|
|
110
|
+
国密族与国际族禁止跨族组合。
|
|
111
|
+
|
|
112
|
+
## 开发
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uv sync # 安装依赖(含 dev 组)
|
|
116
|
+
uv run pytest --cov=wop_mcp --cov-fail-under=95 # 测试 + 覆盖率门禁
|
|
117
|
+
npm i -g lefthook && lefthook install # 激活 git 钩子(commitlint + 覆盖率门禁)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- 提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/zh-CN/)(commitlint 校验)
|
|
121
|
+
- CI:GitHub Actions,Python 3.10–3.13 矩阵,覆盖率红线 95%
|
|
122
|
+
|
|
123
|
+
## 许可证
|
|
124
|
+
|
|
125
|
+
[MIT](LICENSE) © 2026 wop-platform
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commitlint 配置 —— 对齐 wop-skills 治理标准与 Conventional Commits
|
|
3
|
+
* 规则等级:2 = error(阻断)| 1 = warn(警告不阻断)| 0 = 关闭
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
extends: ['@commitlint/config-conventional'],
|
|
7
|
+
rules: {
|
|
8
|
+
'type-enum': [
|
|
9
|
+
2,
|
|
10
|
+
'always',
|
|
11
|
+
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert'],
|
|
12
|
+
],
|
|
13
|
+
'scope-enum': [
|
|
14
|
+
1,
|
|
15
|
+
'always',
|
|
16
|
+
['api', 'docs', 'keypair', 'mcp', 'http', 'crypto', 'ci', 'build', 'test', 'security', 'deps'],
|
|
17
|
+
],
|
|
18
|
+
'subject-max-length': [2, 'always', 50],
|
|
19
|
+
'header-max-length': [2, 'always', 72],
|
|
20
|
+
// 中文 subject 不适用英文大小写规则
|
|
21
|
+
'subject-case': [0],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# 治理对齐 wop-skills:commit-msg 走 commitlint;pre-push 跑覆盖率门禁(≥95% 红线)
|
|
2
|
+
# 激活:npm i -g lefthook && lefthook install
|
|
3
|
+
commit-msg:
|
|
4
|
+
commands:
|
|
5
|
+
commitlint:
|
|
6
|
+
run: npx --yes @commitlint/cli@19 --edit {1}
|
|
7
|
+
|
|
8
|
+
pre-push:
|
|
9
|
+
commands:
|
|
10
|
+
coverage-gate:
|
|
11
|
+
run: uv run pytest --cov=wop_mcp --cov-fail-under=95 -q
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "wop-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "万联易达开放平台(WOP)MCP Server —— 文档获取与密钥对生成"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "wop-platform" }]
|
|
9
|
+
keywords = ["wop", "mcp", "wanlianyida", "open-platform", "sm2", "rsa"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Topic :: Security :: Cryptography",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"mcp>=2.0",
|
|
23
|
+
"cryptography>=41",
|
|
24
|
+
"gmssl>=3.2.2",
|
|
25
|
+
"wop-python-sdk>=0.1.7",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/wop-platform/wop-mcp"
|
|
30
|
+
Repository = "https://github.com/wop-platform/wop-mcp"
|
|
31
|
+
Changelog = "https://github.com/wop-platform/wop-mcp/releases"
|
|
32
|
+
Issues = "https://github.com/wop-platform/wop-mcp/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
wop-mcp = "wop_mcp.main:main"
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=8.0",
|
|
40
|
+
"pytest-cov>=5.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["hatchling"]
|
|
45
|
+
build-backend = "hatchling.build"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/wop_mcp"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
addopts = "-q"
|
|
53
|
+
|
|
54
|
+
[tool.coverage.run]
|
|
55
|
+
source = ["wop_mcp"]
|
|
56
|
+
|
|
57
|
+
[tool.coverage.report]
|
|
58
|
+
fail_under = 95
|
|
59
|
+
show_missing = true
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""运行配置:环境变量可覆盖,默认值指向生产文档中心 CDN。
|
|
3
|
+
|
|
4
|
+
环境变量清单:
|
|
5
|
+
- WOP_DOC_BASE_URL 文档中心基址(默认 assets.wanlianyida.com …/doc-center)
|
|
6
|
+
- WOP_KEYS_DIR 密钥对私钥落盘目录(默认 ./keys)
|
|
7
|
+
- WOP_INDEX_CACHE_TTL llms.txt 索引进程内缓存秒数(默认 300;<=0 表示不缓存)
|
|
8
|
+
- WOP_HTTP_TIMEOUT HTTP 超时秒数(默认 15)
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
DEFAULT_DOC_BASE_URL = (
|
|
16
|
+
"https://assets.wanlianyida.com/gtsp/platformResource/wop/doc-center"
|
|
17
|
+
)
|
|
18
|
+
DEFAULT_KEYS_DIR = "./keys"
|
|
19
|
+
DEFAULT_INDEX_CACHE_TTL_SECONDS = 300.0
|
|
20
|
+
DEFAULT_HTTP_TIMEOUT_SECONDS = 15.0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def doc_base_url() -> str:
|
|
24
|
+
"""文档中心基址(去尾部斜杠)。"""
|
|
25
|
+
return os.environ.get("WOP_DOC_BASE_URL", DEFAULT_DOC_BASE_URL).rstrip("/")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def keys_dir() -> Path:
|
|
29
|
+
"""私钥落盘目录。"""
|
|
30
|
+
return Path(os.environ.get("WOP_KEYS_DIR", DEFAULT_KEYS_DIR))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def index_cache_ttl_seconds() -> float:
|
|
34
|
+
"""llms.txt 索引缓存 TTL(秒)。"""
|
|
35
|
+
raw = os.environ.get("WOP_INDEX_CACHE_TTL", "")
|
|
36
|
+
try:
|
|
37
|
+
return float(raw)
|
|
38
|
+
except ValueError:
|
|
39
|
+
return DEFAULT_INDEX_CACHE_TTL_SECONDS
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def http_timeout_seconds() -> float:
|
|
43
|
+
"""HTTP 下载超时(秒)。"""
|
|
44
|
+
raw = os.environ.get("WOP_HTTP_TIMEOUT", "")
|
|
45
|
+
try:
|
|
46
|
+
return float(raw)
|
|
47
|
+
except ValueError:
|
|
48
|
+
return DEFAULT_HTTP_TIMEOUT_SECONDS
|