sorftime-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.
@@ -0,0 +1,10 @@
1
+ # Sorftime Account-SK. Do not commit a real key.
2
+ SORFTIME_API_KEY=
3
+
4
+ # Optional runtime settings.
5
+ SORFTIME_API_BASE_URL=https://standardapi.sorftime.com/api
6
+ SORFTIME_API_TIMEOUT_SECONDS=120
7
+ SORFTIME_API_MAX_RETRIES=3
8
+ SORFTIME_API_RETRY_BASE_DELAY_SECONDS=2
9
+ SORFTIME_API_RETRY_MAX_DELAY_SECONDS=15
10
+ SORFTIME_AUDIT_LOG_PATH=
@@ -0,0 +1,10 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ .pytest_cache/
5
+ *.pyc
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ logs/
10
+
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.4
2
+ Name: sorftime-mcp
3
+ Version: 0.1.0
4
+ Summary: Stdio MCP server for read-only Sorftime Enterprise API access.
5
+ Project-URL: Repository, https://github.com/ccchenhuohuo/sorftime-mcp
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: click>=8.1.8
8
+ Requires-Dist: fastmcp==3.4.2
9
+ Requires-Dist: httpx>=0.27.2
10
+ Requires-Dist: pydantic-settings>=2.8.1
11
+ Requires-Dist: pydantic>=2.10.6
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Sorftime MCP
15
+
16
+ 把 Sorftime Enterprise API 封装成 Codex、Claude Code 等 AI Agent 可直接调用的 MCP 服务。
17
+
18
+ 本项目默认使用 `stdio` 模式,本地安装后只需要配置 `SORFTIME_API_KEY`。Sorftime Account-SK 保存在使用者自己的 MCP 客户端配置或服务器环境变量中,不写入代码仓库。
19
+
20
+ ## 重点
21
+
22
+ - 默认传输:`stdio`
23
+ - 本地必需环境变量:`SORFTIME_API_KEY`
24
+ - 公开 MCP 工具数:10 个
25
+ - 支持安全取数方法:32 个
26
+ - 默认站点:`domain=1`,即美国站
27
+ - 返回统一结构:`endpoint`、`domain`、`estimatedRequestCost`、`requestConsumed`、`requestLeft`、`code`、`message`、`data`、`rawResponse`
28
+ - 默认排除账户修改、订阅管理、监控任务管理类接口
29
+
30
+ ## 安装方式
31
+
32
+ ### npm 安装
33
+
34
+ Codex 配置:
35
+
36
+ ```toml
37
+ [mcp_servers.sorftime]
38
+ command = "npx"
39
+ args = ["-y", "sorftime-mcp"]
40
+
41
+ [mcp_servers.sorftime.env]
42
+ SORFTIME_API_KEY = "你的 Sorftime Account-SK"
43
+ ```
44
+
45
+ Claude Code 安装:
46
+
47
+ ```bash
48
+ claude mcp add --scope user \
49
+ -e SORFTIME_API_KEY="你的 Sorftime Account-SK" \
50
+ sorftime -- npx -y sorftime-mcp
51
+ ```
52
+
53
+ ### PyPI 安装
54
+
55
+ Codex 配置:
56
+
57
+ ```toml
58
+ [mcp_servers.sorftime]
59
+ command = "uvx"
60
+ args = ["sorftime-mcp"]
61
+
62
+ [mcp_servers.sorftime.env]
63
+ SORFTIME_API_KEY = "你的 Sorftime Account-SK"
64
+ ```
65
+
66
+ Claude Code 安装:
67
+
68
+ ```bash
69
+ claude mcp add --scope user \
70
+ -e SORFTIME_API_KEY="你的 Sorftime Account-SK" \
71
+ sorftime -- uvx sorftime-mcp
72
+ ```
73
+
74
+ ### GitHub 源码安装
75
+
76
+ ```bash
77
+ uvx --from git+https://github.com/ccchenhuohuo/sorftime-mcp.git sorftime-mcp
78
+ ```
79
+
80
+ 对应 Codex 配置:
81
+
82
+ ```toml
83
+ [mcp_servers.sorftime]
84
+ command = "uvx"
85
+ args = [
86
+ "--from",
87
+ "git+https://github.com/ccchenhuohuo/sorftime-mcp.git",
88
+ "sorftime-mcp"
89
+ ]
90
+
91
+ [mcp_servers.sorftime.env]
92
+ SORFTIME_API_KEY = "你的 Sorftime Account-SK"
93
+ ```
94
+
95
+ ## 工具列表
96
+
97
+ | 工具 | 用途 |
98
+ | --- | --- |
99
+ | `sorftime_methods` | 查看支持的方法、分类、消耗、是否异步、是否有快捷工具 |
100
+ | `sorftime_method_schema` | 查看单个方法的参数、示例、站点映射和消耗说明 |
101
+ | `sorftime_call` | 白名单路由工具,用于调用低频安全取数方法 |
102
+ | `product_request` | 产品详情,对应 `ProductRequest` |
103
+ | `category_request` | 类目 Top 100,对应 `CategoryRequest` |
104
+ | `keyword_request` | 关键词详情,对应 `KeywordRequest` |
105
+ | `product_query` | 产品搜索,对应 `ProductQuery` |
106
+ | `category_trend` | 类目趋势,对应 `CategoryTrend` |
107
+ | `request_stream_month` | Request 使用和余额,对应 `RequestStreamMonth` |
108
+ | `coin_query` | 积分余额,对应 `CoinQuery` |
109
+
110
+ 高频场景优先使用快捷工具。低频场景使用 `sorftime_call`,先用 `sorftime_methods` 和 `sorftime_method_schema` 查询 method 和参数。
111
+
112
+ ## 调用示例
113
+
114
+ 查询产品详情:
115
+
116
+ ```json
117
+ {
118
+ "input": {
119
+ "asin": "B0CVM8TXHP",
120
+ "domain": 1,
121
+ "trend": 1
122
+ }
123
+ }
124
+ ```
125
+
126
+ 通过路由查询销量估算:
127
+
128
+ ```json
129
+ {
130
+ "input": {
131
+ "method": "AsinSalesVolume",
132
+ "domain": 1,
133
+ "params": {
134
+ "ASIN": "B0CVM8TXHP",
135
+ "Page": 1
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ 查看某个方法的参数:
142
+
143
+ ```json
144
+ {
145
+ "input": {
146
+ "method": "ProductRequest"
147
+ }
148
+ }
149
+ ```
150
+
151
+ ## 站点映射
152
+
153
+ | domain | 站点 |
154
+ | --- | --- |
155
+ | 1 | US 美国 |
156
+ | 2 | GB 英国 |
157
+ | 3 | DE 德国 |
158
+ | 4 | FR 法国 |
159
+ | 5 | IN 印度 |
160
+ | 6 | CA 加拿大 |
161
+ | 7 | JP 日本 |
162
+ | 8 | ES 西班牙 |
163
+ | 9 | IT 意大利 |
164
+ | 10 | MX 墨西哥 |
165
+ | 11 | AE 阿联酋 |
166
+ | 12 | AU 澳大利亚 |
167
+ | 13 | BR 巴西 |
168
+ | 14 | SA 沙特阿拉伯 |
169
+
170
+ IN、AE、AU、BR、SA 不支持历史数据回填。
171
+
172
+ ## 返回结构
173
+
174
+ 所有真实 Sorftime 请求都返回统一结构:
175
+
176
+ ```json
177
+ {
178
+ "endpoint": "ProductRequest",
179
+ "domain": 1,
180
+ "estimatedRequestCost": 1,
181
+ "requestConsumed": 1,
182
+ "requestLeft": 1200,
183
+ "code": 0,
184
+ "message": null,
185
+ "data": {},
186
+ "rawResponse": {}
187
+ }
188
+ ```
189
+
190
+ Agent 应优先读取 `code`、`message`、`data`、`requestConsumed` 和 `requestLeft`。
191
+
192
+ ## 安全边界
193
+
194
+ v1 只开放安全取数接口,默认不开放:
195
+
196
+ - 收藏词新增、修改、删除等账户状态变更接口
197
+ - 关键词监控、榜单监控、跟卖库存监控、ASIN 订阅等订阅管理接口
198
+ - 消耗监控点数或订阅点数、但不属于通用取数的接口
199
+
200
+ ## 审计日志
201
+
202
+ 默认不向标准输出写日志,避免污染 MCP stdio 协议。需要审计记录时,通过环境变量写入本地 JSONL 文件:
203
+
204
+ ```bash
205
+ SORFTIME_AUDIT_LOG_PATH=logs/sorftime-mcp-audit.jsonl
206
+ ```
207
+
208
+ 审计日志会记录 endpoint、domain、参数摘要、估算消耗、实际消耗、剩余 Request、耗时和 Sorftime 返回码。日志不会记录 Sorftime Account-SK。
209
+
210
+ ## 仓库
211
+
212
+ ```text
213
+ https://github.com/ccchenhuohuo/sorftime-mcp
214
+ ```
@@ -0,0 +1,201 @@
1
+ # Sorftime MCP
2
+
3
+ 把 Sorftime Enterprise API 封装成 Codex、Claude Code 等 AI Agent 可直接调用的 MCP 服务。
4
+
5
+ 本项目默认使用 `stdio` 模式,本地安装后只需要配置 `SORFTIME_API_KEY`。Sorftime Account-SK 保存在使用者自己的 MCP 客户端配置或服务器环境变量中,不写入代码仓库。
6
+
7
+ ## 重点
8
+
9
+ - 默认传输:`stdio`
10
+ - 本地必需环境变量:`SORFTIME_API_KEY`
11
+ - 公开 MCP 工具数:10 个
12
+ - 支持安全取数方法:32 个
13
+ - 默认站点:`domain=1`,即美国站
14
+ - 返回统一结构:`endpoint`、`domain`、`estimatedRequestCost`、`requestConsumed`、`requestLeft`、`code`、`message`、`data`、`rawResponse`
15
+ - 默认排除账户修改、订阅管理、监控任务管理类接口
16
+
17
+ ## 安装方式
18
+
19
+ ### npm 安装
20
+
21
+ Codex 配置:
22
+
23
+ ```toml
24
+ [mcp_servers.sorftime]
25
+ command = "npx"
26
+ args = ["-y", "sorftime-mcp"]
27
+
28
+ [mcp_servers.sorftime.env]
29
+ SORFTIME_API_KEY = "你的 Sorftime Account-SK"
30
+ ```
31
+
32
+ Claude Code 安装:
33
+
34
+ ```bash
35
+ claude mcp add --scope user \
36
+ -e SORFTIME_API_KEY="你的 Sorftime Account-SK" \
37
+ sorftime -- npx -y sorftime-mcp
38
+ ```
39
+
40
+ ### PyPI 安装
41
+
42
+ Codex 配置:
43
+
44
+ ```toml
45
+ [mcp_servers.sorftime]
46
+ command = "uvx"
47
+ args = ["sorftime-mcp"]
48
+
49
+ [mcp_servers.sorftime.env]
50
+ SORFTIME_API_KEY = "你的 Sorftime Account-SK"
51
+ ```
52
+
53
+ Claude Code 安装:
54
+
55
+ ```bash
56
+ claude mcp add --scope user \
57
+ -e SORFTIME_API_KEY="你的 Sorftime Account-SK" \
58
+ sorftime -- uvx sorftime-mcp
59
+ ```
60
+
61
+ ### GitHub 源码安装
62
+
63
+ ```bash
64
+ uvx --from git+https://github.com/ccchenhuohuo/sorftime-mcp.git sorftime-mcp
65
+ ```
66
+
67
+ 对应 Codex 配置:
68
+
69
+ ```toml
70
+ [mcp_servers.sorftime]
71
+ command = "uvx"
72
+ args = [
73
+ "--from",
74
+ "git+https://github.com/ccchenhuohuo/sorftime-mcp.git",
75
+ "sorftime-mcp"
76
+ ]
77
+
78
+ [mcp_servers.sorftime.env]
79
+ SORFTIME_API_KEY = "你的 Sorftime Account-SK"
80
+ ```
81
+
82
+ ## 工具列表
83
+
84
+ | 工具 | 用途 |
85
+ | --- | --- |
86
+ | `sorftime_methods` | 查看支持的方法、分类、消耗、是否异步、是否有快捷工具 |
87
+ | `sorftime_method_schema` | 查看单个方法的参数、示例、站点映射和消耗说明 |
88
+ | `sorftime_call` | 白名单路由工具,用于调用低频安全取数方法 |
89
+ | `product_request` | 产品详情,对应 `ProductRequest` |
90
+ | `category_request` | 类目 Top 100,对应 `CategoryRequest` |
91
+ | `keyword_request` | 关键词详情,对应 `KeywordRequest` |
92
+ | `product_query` | 产品搜索,对应 `ProductQuery` |
93
+ | `category_trend` | 类目趋势,对应 `CategoryTrend` |
94
+ | `request_stream_month` | Request 使用和余额,对应 `RequestStreamMonth` |
95
+ | `coin_query` | 积分余额,对应 `CoinQuery` |
96
+
97
+ 高频场景优先使用快捷工具。低频场景使用 `sorftime_call`,先用 `sorftime_methods` 和 `sorftime_method_schema` 查询 method 和参数。
98
+
99
+ ## 调用示例
100
+
101
+ 查询产品详情:
102
+
103
+ ```json
104
+ {
105
+ "input": {
106
+ "asin": "B0CVM8TXHP",
107
+ "domain": 1,
108
+ "trend": 1
109
+ }
110
+ }
111
+ ```
112
+
113
+ 通过路由查询销量估算:
114
+
115
+ ```json
116
+ {
117
+ "input": {
118
+ "method": "AsinSalesVolume",
119
+ "domain": 1,
120
+ "params": {
121
+ "ASIN": "B0CVM8TXHP",
122
+ "Page": 1
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ 查看某个方法的参数:
129
+
130
+ ```json
131
+ {
132
+ "input": {
133
+ "method": "ProductRequest"
134
+ }
135
+ }
136
+ ```
137
+
138
+ ## 站点映射
139
+
140
+ | domain | 站点 |
141
+ | --- | --- |
142
+ | 1 | US 美国 |
143
+ | 2 | GB 英国 |
144
+ | 3 | DE 德国 |
145
+ | 4 | FR 法国 |
146
+ | 5 | IN 印度 |
147
+ | 6 | CA 加拿大 |
148
+ | 7 | JP 日本 |
149
+ | 8 | ES 西班牙 |
150
+ | 9 | IT 意大利 |
151
+ | 10 | MX 墨西哥 |
152
+ | 11 | AE 阿联酋 |
153
+ | 12 | AU 澳大利亚 |
154
+ | 13 | BR 巴西 |
155
+ | 14 | SA 沙特阿拉伯 |
156
+
157
+ IN、AE、AU、BR、SA 不支持历史数据回填。
158
+
159
+ ## 返回结构
160
+
161
+ 所有真实 Sorftime 请求都返回统一结构:
162
+
163
+ ```json
164
+ {
165
+ "endpoint": "ProductRequest",
166
+ "domain": 1,
167
+ "estimatedRequestCost": 1,
168
+ "requestConsumed": 1,
169
+ "requestLeft": 1200,
170
+ "code": 0,
171
+ "message": null,
172
+ "data": {},
173
+ "rawResponse": {}
174
+ }
175
+ ```
176
+
177
+ Agent 应优先读取 `code`、`message`、`data`、`requestConsumed` 和 `requestLeft`。
178
+
179
+ ## 安全边界
180
+
181
+ v1 只开放安全取数接口,默认不开放:
182
+
183
+ - 收藏词新增、修改、删除等账户状态变更接口
184
+ - 关键词监控、榜单监控、跟卖库存监控、ASIN 订阅等订阅管理接口
185
+ - 消耗监控点数或订阅点数、但不属于通用取数的接口
186
+
187
+ ## 审计日志
188
+
189
+ 默认不向标准输出写日志,避免污染 MCP stdio 协议。需要审计记录时,通过环境变量写入本地 JSONL 文件:
190
+
191
+ ```bash
192
+ SORFTIME_AUDIT_LOG_PATH=logs/sorftime-mcp-audit.jsonl
193
+ ```
194
+
195
+ 审计日志会记录 endpoint、domain、参数摘要、估算消耗、实际消耗、剩余 Request、耗时和 Sorftime 返回码。日志不会记录 Sorftime Account-SK。
196
+
197
+ ## 仓库
198
+
199
+ ```text
200
+ https://github.com/ccchenhuohuo/sorftime-mcp
201
+ ```
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawn } = require("node:child_process");
5
+
6
+ const userArgs = process.argv.slice(2);
7
+ const pythonArgs = userArgs.length === 0 ? ["stdio"] : userArgs;
8
+ const child = spawn(
9
+ "uvx",
10
+ ["--from", "sorftime-mcp", "sorftime-mcp", ...pythonArgs],
11
+ {
12
+ env: process.env,
13
+ stdio: "inherit"
14
+ }
15
+ );
16
+
17
+ child.on("error", (error) => {
18
+ if (error.code === "ENOENT") {
19
+ console.error("未找到 uvx。请先安装 uv:https://docs.astral.sh/uv/getting-started/installation/");
20
+ process.exit(127);
21
+ }
22
+ console.error(`启动 sorftime-mcp 失败:${error.message}`);
23
+ process.exit(1);
24
+ });
25
+
26
+ child.on("exit", (code, signal) => {
27
+ if (signal) {
28
+ process.kill(process.pid, signal);
29
+ return;
30
+ }
31
+ process.exit(code ?? 0);
32
+ });
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "sorftime-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Sorftime API MCP stdio launcher for Codex and Claude Code.",
5
+ "bin": {
6
+ "sorftime-mcp": "bin/sorftime-mcp.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "README.md"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/ccchenhuohuo/sorftime-mcp.git"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "sorftime",
19
+ "codex",
20
+ "claude-code"
21
+ ],
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "scripts": {
29
+ "test": "node --check bin/sorftime-mcp.js"
30
+ }
31
+ }
@@ -0,0 +1,33 @@
1
+ [project]
2
+ name = "sorftime-mcp"
3
+ version = "0.1.0"
4
+ description = "Stdio MCP server for read-only Sorftime Enterprise API access."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "click>=8.1.8",
9
+ "fastmcp==3.4.2",
10
+ "httpx>=0.27.2",
11
+ "pydantic>=2.10.6",
12
+ "pydantic-settings>=2.8.1",
13
+ ]
14
+
15
+ [project.scripts]
16
+ sorftime-mcp = "sorftime_mcp.cli:cli"
17
+
18
+ [project.urls]
19
+ Repository = "https://github.com/ccchenhuohuo/sorftime-mcp"
20
+
21
+ [dependency-groups]
22
+ dev = [
23
+ "pytest>=8.3.5",
24
+ "pytest-asyncio>=0.25.3",
25
+ ]
26
+
27
+ [build-system]
28
+ requires = ["hatchling"]
29
+ build-backend = "hatchling.build"
30
+
31
+ [tool.pytest.ini_options]
32
+ asyncio_mode = "auto"
33
+ testpaths = ["tests"]
@@ -0,0 +1,6 @@
1
+ """Sorftime MCP server package."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.0"
6
+
@@ -0,0 +1,6 @@
1
+ from sorftime_mcp.cli import cli
2
+
3
+
4
+ if __name__ == "__main__":
5
+ cli()
6
+
@@ -0,0 +1,56 @@
1
+ import json
2
+ import sys
3
+ from datetime import datetime, timezone
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ SENSITIVE_KEY_PARTS = ("authorization", "token", "secret", "password", "api_key", "sk")
8
+ LONG_VALUE_KEYS = ("image",)
9
+
10
+
11
+ class AuditLogger:
12
+ def __init__(self, path: Path | None = None, *, emit_stdout: bool = True) -> None:
13
+ self._path = path
14
+ self._emit_stdout = emit_stdout
15
+ if self._path is not None:
16
+ self._path.parent.mkdir(parents=True, exist_ok=True)
17
+
18
+ def log(self, record: dict[str, Any]) -> None:
19
+ line = json.dumps(record, ensure_ascii=False, separators=(",", ":"))
20
+ if self._emit_stdout:
21
+ sys.stdout.write(f"{line}\n")
22
+ sys.stdout.flush()
23
+ if self._path is not None:
24
+ with self._path.open("a", encoding="utf-8") as file:
25
+ file.write(f"{line}\n")
26
+
27
+
28
+ def utc_now_iso() -> str:
29
+ return datetime.now(timezone.utc).isoformat()
30
+
31
+
32
+ def sanitize_for_audit(value: Any) -> Any:
33
+ if isinstance(value, dict):
34
+ sanitized: dict[str, Any] = {}
35
+ for key, item in value.items():
36
+ lower_key = str(key).lower()
37
+ if any(part in lower_key for part in SENSITIVE_KEY_PARTS):
38
+ sanitized[key] = "[REDACTED]"
39
+ elif any(part in lower_key for part in LONG_VALUE_KEYS):
40
+ sanitized[key] = summarize_long_value(item)
41
+ else:
42
+ sanitized[key] = sanitize_for_audit(item)
43
+ return sanitized
44
+ if isinstance(value, list):
45
+ return [sanitize_for_audit(item) for item in value]
46
+ if isinstance(value, str):
47
+ return summarize_long_value(value)
48
+ return value
49
+
50
+
51
+ def summarize_long_value(value: Any) -> Any:
52
+ if not isinstance(value, str):
53
+ return sanitize_for_audit(value)
54
+ if len(value) <= 180:
55
+ return value
56
+ return f"{value[:80]}...[truncated:{len(value)}]"