kronos-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,16 @@
1
+ # kronos-mcp · 远程传输形态(常驻服务 · 多客户端连)
2
+ #
3
+ # 只想在 Claude Desktop / Cursor 里用的话**不需要这个镜像** ——
4
+ # 那边走 stdio,`uvx kronos-mcp` 就够了。这个镜像是给「跑成一个服务」的场景。
5
+ FROM python:3.11-slim
6
+
7
+ WORKDIR /app
8
+ COPY requirements.txt .
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+ COPY kronos_mcp/ ./kronos_mcp/
11
+
12
+ ENV KRONOS_MCP_TRANSPORT=streamable-http KRONOS_MCP_HOST=0.0.0.0 KRONOS_MCP_PORT=8932
13
+
14
+ # KRONOS_API_KEY 必须在 run 的时候传进来 —— 不要 build 进镜像
15
+ EXPOSE 8932
16
+ CMD ["python", "-m", "kronos_mcp.server"]
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.5
2
+ Name: kronos-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for Kronos stock price forecasting — A-shares, US and HK equities
5
+ Project-URL: Homepage, https://github.com/agentpit-io/hunter-community
6
+ Project-URL: Repository, https://github.com/agentpit-io/hunter-community
7
+ Project-URL: Documentation, https://github.com/agentpit-io/hunter-community/blob/main/tools/kronos-mcp/README.md
8
+ Project-URL: Issues, https://github.com/agentpit-io/hunter-community/issues
9
+ Author-email: AgentPit <hangeaiagent@gmail.com>
10
+ License: Apache-2.0
11
+ Keywords: a-share,finance,forecast,kronos,mcp,model-context-protocol,quant,stock,timeseries
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: License :: OSI Approved :: Apache Software 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: Topic :: Office/Business :: Financial :: Investment
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: httpx>=0.27.0
23
+ Requires-Dist: mcp>=2.0.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # kronos-mcp
27
+
28
+ <!-- mcp-name: io.github.agentpit-io/kronos-mcp -->
29
+
30
+ **Stock price forecasting for your AI agent — A-shares, US and HK equities.**
31
+
32
+ An [MCP](https://modelcontextprotocol.io) server wrapping **Kronos**, a K-line
33
+ time-series model. Ask it for a symbol, get the next N daily candles predicted:
34
+ open / high / low / close / volume.
35
+
36
+ [![License](https://img.shields.io/badge/license-Apache_2.0-blue)](https://github.com/agentpit-io/hunter-community/blob/main/LICENSE)
37
+
38
+ ---
39
+
40
+ ## Tools
41
+
42
+ | Tool | What it does |
43
+ |---|---|
44
+ | `kronos_health()` | Check the service is reachable and your key works. **Call this first when something fails.** |
45
+ | `kronos_predict(symbol, pred_len)` | Predict the next `pred_len` daily candles (1–30, default 10) |
46
+
47
+ `kronos_predict` also returns `expected_return` — last predicted close over last
48
+ real close, minus one. That ratio is the number most people actually want.
49
+
50
+ ---
51
+
52
+ ## Quick start
53
+
54
+ You need an API key first — see [below](#api-key).
55
+
56
+ ### Claude Desktop / Cursor
57
+
58
+ Add to `claude_desktop_config.json`:
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "kronos": {
64
+ "command": "uvx",
65
+ "args": ["kronos-mcp"],
66
+ "env": { "KRONOS_API_KEY": "oapk_xxx" }
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### Command line
73
+
74
+ ```bash
75
+ # no install needed
76
+ KRONOS_API_KEY=oapk_xxx uvx kronos-mcp
77
+
78
+ # or install it
79
+ pip install kronos-mcp
80
+ KRONOS_API_KEY=oapk_xxx kronos-mcp
81
+ ```
82
+
83
+ ---
84
+
85
+ ## API key
86
+
87
+ **This server has no keyless mode, and cannot have one** — the upstream returns
88
+ 401 without a key.
89
+
90
+ The reason is cost, not gatekeeping: Kronos runs on our own GPUs and a single
91
+ inference takes **30–70 seconds**. Open anonymous access would let a handful of
92
+ loops saturate the service, with no way to tell who is doing it.
93
+
94
+ Get one:
95
+
96
+ 1. Open <https://hunter.agentpit.io/dev/api-keys> and sign in
97
+ 2. Click **申请 API Key** and choose API type **KRONOS**
98
+ 3. Copy the `oapk_…` key — **it is shown once**
99
+ 4. Set it as `KRONOS_API_KEY`
100
+
101
+ Approval usually lands within hours on a business day. Free, with a default
102
+ quota of 1000 calls per key.
103
+
104
+ If the key is missing, this server does **not** return sample data or a fake
105
+ success. It returns an error that tells you exactly where to get a key.
106
+
107
+ ---
108
+
109
+ ## Environment variables
110
+
111
+ | Variable | Default | Notes |
112
+ |---|---|---|
113
+ | `KRONOS_API_KEY` | — | **Required.** `oapk_` key of type KRONOS |
114
+ | `KRONOS_URL` | `https://kronos.agentpit.io` | Point this at your own deployment if you self-host |
115
+ | `KRONOS_TIMEOUT` | `180` | Seconds. GPU inference is 30–70s — don't set this tight |
116
+ | `KRONOS_MCP_TRANSPORT` | `stdio` | `stdio` \| `streamable-http` \| `sse` |
117
+ | `KRONOS_MCP_HOST` / `KRONOS_MCP_PORT` | `0.0.0.0` / `8932` | Remote transports only |
118
+
119
+ `stdio` is what Claude Desktop, Cursor and `uvx` use. Run a remote transport
120
+ only if you want one long-running server serving several clients — and note the
121
+ MCP spec marks SSE as deprecated, so prefer `streamable-http` for new setups.
122
+
123
+ ---
124
+
125
+ ## What to expect
126
+
127
+ **Every call takes 30–70 seconds.** That is the GPU, not a hang. Don't retry
128
+ because it feels slow, and don't loop it over dozens of symbols — upstream takes
129
+ one symbol per request.
130
+
131
+ **This is a statistical extrapolation, not investment advice.** The model reads
132
+ price history and nothing else: no news, no halts, no earnings dates. It will
133
+ drift badly in unusual conditions. Treat the output as one input among many.
134
+
135
+ ---
136
+
137
+ ## Errors you might hit
138
+
139
+ | Error | Meaning |
140
+ |---|---|
141
+ | `missing_api_key` | `KRONOS_API_KEY` not set — see [API key](#api-key) |
142
+ | `invalid_api_key` (401) | Key wrong, incomplete or revoked. Revocation takes up to 5 min to propagate |
143
+ | `wrong_key_type` (403) | Key is valid but isn't a **KRONOS** key — you may have applied for KPRED or FIN_R1 |
144
+ | `symbol_not_found` (404) | A-shares `600519` / `600519.SH`, US tickers `AAPL`, HK `00700` |
145
+ | `rate_limited` (429) | Per-IP cap on invalid keys, or your quota is exhausted |
146
+ | `upstream_down` (502/504) | GPU service restarting. If it lasts more than a few minutes, please open an issue |
147
+
148
+ ---
149
+
150
+ ## Related
151
+
152
+ - **[truesource-mcp](https://github.com/agentpit-io/hunter-community/tree/main/tools/truesource-mcp)** — verifiable first-hand market signals: filings, procurement wins, macro data
153
+ - **[akshare-mcp](https://github.com/agentpit-io/hunter-community/tree/main/tools/akshare-mcp)** — lets a model explore AKShare's 1000+ China market data functions on its own
154
+ - **[hunter-community](https://github.com/agentpit-io/hunter-community)** — the open-source stack these come from
155
+
156
+ Apache-2.0.
@@ -0,0 +1,131 @@
1
+ # kronos-mcp
2
+
3
+ <!-- mcp-name: io.github.agentpit-io/kronos-mcp -->
4
+
5
+ **Stock price forecasting for your AI agent — A-shares, US and HK equities.**
6
+
7
+ An [MCP](https://modelcontextprotocol.io) server wrapping **Kronos**, a K-line
8
+ time-series model. Ask it for a symbol, get the next N daily candles predicted:
9
+ open / high / low / close / volume.
10
+
11
+ [![License](https://img.shields.io/badge/license-Apache_2.0-blue)](https://github.com/agentpit-io/hunter-community/blob/main/LICENSE)
12
+
13
+ ---
14
+
15
+ ## Tools
16
+
17
+ | Tool | What it does |
18
+ |---|---|
19
+ | `kronos_health()` | Check the service is reachable and your key works. **Call this first when something fails.** |
20
+ | `kronos_predict(symbol, pred_len)` | Predict the next `pred_len` daily candles (1–30, default 10) |
21
+
22
+ `kronos_predict` also returns `expected_return` — last predicted close over last
23
+ real close, minus one. That ratio is the number most people actually want.
24
+
25
+ ---
26
+
27
+ ## Quick start
28
+
29
+ You need an API key first — see [below](#api-key).
30
+
31
+ ### Claude Desktop / Cursor
32
+
33
+ Add to `claude_desktop_config.json`:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "kronos": {
39
+ "command": "uvx",
40
+ "args": ["kronos-mcp"],
41
+ "env": { "KRONOS_API_KEY": "oapk_xxx" }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Command line
48
+
49
+ ```bash
50
+ # no install needed
51
+ KRONOS_API_KEY=oapk_xxx uvx kronos-mcp
52
+
53
+ # or install it
54
+ pip install kronos-mcp
55
+ KRONOS_API_KEY=oapk_xxx kronos-mcp
56
+ ```
57
+
58
+ ---
59
+
60
+ ## API key
61
+
62
+ **This server has no keyless mode, and cannot have one** — the upstream returns
63
+ 401 without a key.
64
+
65
+ The reason is cost, not gatekeeping: Kronos runs on our own GPUs and a single
66
+ inference takes **30–70 seconds**. Open anonymous access would let a handful of
67
+ loops saturate the service, with no way to tell who is doing it.
68
+
69
+ Get one:
70
+
71
+ 1. Open <https://hunter.agentpit.io/dev/api-keys> and sign in
72
+ 2. Click **申请 API Key** and choose API type **KRONOS**
73
+ 3. Copy the `oapk_…` key — **it is shown once**
74
+ 4. Set it as `KRONOS_API_KEY`
75
+
76
+ Approval usually lands within hours on a business day. Free, with a default
77
+ quota of 1000 calls per key.
78
+
79
+ If the key is missing, this server does **not** return sample data or a fake
80
+ success. It returns an error that tells you exactly where to get a key.
81
+
82
+ ---
83
+
84
+ ## Environment variables
85
+
86
+ | Variable | Default | Notes |
87
+ |---|---|---|
88
+ | `KRONOS_API_KEY` | — | **Required.** `oapk_` key of type KRONOS |
89
+ | `KRONOS_URL` | `https://kronos.agentpit.io` | Point this at your own deployment if you self-host |
90
+ | `KRONOS_TIMEOUT` | `180` | Seconds. GPU inference is 30–70s — don't set this tight |
91
+ | `KRONOS_MCP_TRANSPORT` | `stdio` | `stdio` \| `streamable-http` \| `sse` |
92
+ | `KRONOS_MCP_HOST` / `KRONOS_MCP_PORT` | `0.0.0.0` / `8932` | Remote transports only |
93
+
94
+ `stdio` is what Claude Desktop, Cursor and `uvx` use. Run a remote transport
95
+ only if you want one long-running server serving several clients — and note the
96
+ MCP spec marks SSE as deprecated, so prefer `streamable-http` for new setups.
97
+
98
+ ---
99
+
100
+ ## What to expect
101
+
102
+ **Every call takes 30–70 seconds.** That is the GPU, not a hang. Don't retry
103
+ because it feels slow, and don't loop it over dozens of symbols — upstream takes
104
+ one symbol per request.
105
+
106
+ **This is a statistical extrapolation, not investment advice.** The model reads
107
+ price history and nothing else: no news, no halts, no earnings dates. It will
108
+ drift badly in unusual conditions. Treat the output as one input among many.
109
+
110
+ ---
111
+
112
+ ## Errors you might hit
113
+
114
+ | Error | Meaning |
115
+ |---|---|
116
+ | `missing_api_key` | `KRONOS_API_KEY` not set — see [API key](#api-key) |
117
+ | `invalid_api_key` (401) | Key wrong, incomplete or revoked. Revocation takes up to 5 min to propagate |
118
+ | `wrong_key_type` (403) | Key is valid but isn't a **KRONOS** key — you may have applied for KPRED or FIN_R1 |
119
+ | `symbol_not_found` (404) | A-shares `600519` / `600519.SH`, US tickers `AAPL`, HK `00700` |
120
+ | `rate_limited` (429) | Per-IP cap on invalid keys, or your quota is exhausted |
121
+ | `upstream_down` (502/504) | GPU service restarting. If it lasts more than a few minutes, please open an issue |
122
+
123
+ ---
124
+
125
+ ## Related
126
+
127
+ - **[truesource-mcp](https://github.com/agentpit-io/hunter-community/tree/main/tools/truesource-mcp)** — verifiable first-hand market signals: filings, procurement wins, macro data
128
+ - **[akshare-mcp](https://github.com/agentpit-io/hunter-community/tree/main/tools/akshare-mcp)** — lets a model explore AKShare's 1000+ China market data functions on its own
129
+ - **[hunter-community](https://github.com/agentpit-io/hunter-community)** — the open-source stack these come from
130
+
131
+ Apache-2.0.
@@ -0,0 +1,5 @@
1
+ """Kronos MCP · 清华 Kronos 走势预测的 MCP 封装。"""
2
+ from .server import main
3
+
4
+ __all__ = ["main"]
5
+ __version__ = "0.1.0"
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env python3
2
+ """Kronos MCP —— 把清华 Kronos 走势预测变成 AI 能直接调的工具。
3
+
4
+ ## 它做什么
5
+
6
+ Kronos 是一个 K 线时序模型:给它一个股票代码,它预测未来 N 根 K 线的
7
+ 开高低收和成交量。A 股 / 美股 / 港股都能问。
8
+
9
+ 这个 MCP 把它包成两个工具,让 Claude / Cursor 这类客户端能直接调:
10
+
11
+ kronos_health() 先确认 key 是通的
12
+ kronos_predict("600519", 10) 预测未来 10 根 K 线
13
+
14
+ ## 为什么必须要 API key
15
+
16
+ 上游是我们自己跑的 GPU 服务,一次推理 30-70 秒。没有 key 的话:
17
+
18
+ · 任何人都能白嫖 GPU,几个爬虫就能把服务打满
19
+ · 没有任何计量,出问题查不到是谁
20
+ · 我们没法给正常用户保证可用性
21
+
22
+ 所以**这个 MCP 不做无 key 模式**,也做不了 —— 上游 nginx 直接返 401。
23
+ 没配 key 时它不会假装成功、不会返回样例数据,而是明确告诉你去哪申请。
24
+
25
+ 申请:https://hunter.agentpit.io/dev/api-keys · API 类型选 **KRONOS**
26
+ 拿到的 key 形如 `oapk_` + 32 位。工作日内通常几小时审批。
27
+
28
+ ## 跑起来
29
+
30
+ # 方式一 · uvx(推荐 · 不用装)
31
+ KRONOS_API_KEY=oapk_xxx uvx kronos-mcp
32
+
33
+ # 方式二 · pip
34
+ pip install kronos-mcp
35
+ KRONOS_API_KEY=oapk_xxx kronos-mcp
36
+
37
+ Claude Desktop / Cursor 的配置(`claude_desktop_config.json`):
38
+
39
+ {
40
+ "mcpServers": {
41
+ "kronos": {
42
+ "command": "uvx",
43
+ "args": ["kronos-mcp"],
44
+ "env": { "KRONOS_API_KEY": "oapk_xxx" }
45
+ }
46
+ }
47
+ }
48
+
49
+ ## 环境变量
50
+
51
+ KRONOS_API_KEY 必填 · oapk_ 开头的 KRONOS 类型 key
52
+ KRONOS_URL 可选 · 默认 https://kronos.agentpit.io
53
+ 自建部署或走别的网关时改这个
54
+ KRONOS_TIMEOUT 可选 · 默认 180 秒。GPU 推理 30-70s,别设太紧
55
+ KRONOS_MCP_TRANSPORT 可选 · stdio(默认)| streamable-http | sse
56
+ KRONOS_MCP_HOST/PORT 仅远程传输时用 · 默认 0.0.0.0:8932
57
+ """
58
+ from __future__ import annotations
59
+
60
+ import json
61
+ import os
62
+
63
+ import httpx
64
+ from mcp.server.mcpserver import MCPServer
65
+
66
+ mcp = MCPServer("kronos-mcp")
67
+
68
+ DEFAULT_URL = "https://kronos.agentpit.io"
69
+ APPLY_URL = "https://hunter.agentpit.io/dev/api-keys"
70
+
71
+ # GPU 推理 30-70s 是常态,不是异常。connect 短、read 长 ——
72
+ # 连不上要快速失败,连上了就得耐心等。
73
+ _TIMEOUT = httpx.Timeout(
74
+ connect=5.0,
75
+ read=float(os.getenv("KRONOS_TIMEOUT", "180")),
76
+ write=5.0,
77
+ pool=5.0,
78
+ )
79
+
80
+
81
+ def _base_url() -> str:
82
+ return (os.getenv("KRONOS_URL") or DEFAULT_URL).rstrip("/")
83
+
84
+
85
+ def _key() -> str:
86
+ return (os.getenv("KRONOS_API_KEY") or "").strip()
87
+
88
+
89
+ def _no_key_error() -> str:
90
+ """没配 key 时的回话。
91
+
92
+ **不返回假数据。**告诉模型确切的下一步 —— 它没法替用户去申请 key,
93
+ 但它可以把这段话原样转达给用户,用户照着做就行。
94
+ """
95
+ return json.dumps({
96
+ "error": "missing_api_key",
97
+ "message": "没有配置 KRONOS_API_KEY,无法调用 Kronos。",
98
+ "how_to_fix": [
99
+ f"1. 打开 {APPLY_URL} 登录",
100
+ "2. 点「申请 API Key」· API 类型选 KRONOS",
101
+ "3. 拿到 oapk_ 开头的 key(只显示一次,立即复制)",
102
+ "4. 把它设成环境变量 KRONOS_API_KEY 后重启这个 MCP",
103
+ ],
104
+ "note": "审批通常在工作日内几小时。这个服务没有免 key 模式 —— "
105
+ "上游是自建 GPU,一次推理 30-70 秒,开放匿名调用会被打满。",
106
+ }, ensure_ascii=False)
107
+
108
+
109
+ def _http_error(status: int, body: str) -> str:
110
+ """把上游的 HTTP 状态码翻译成模型能据此行动的话。
111
+
112
+ 照搬 docs/kronos-direct-access.md 里的错误表 —— 那张表是实际踩出来的,
113
+ 尤其 403(key 有效但类型不对)这条,光看 "403 Forbidden" 根本猜不到。
114
+ """
115
+ table = {
116
+ 401: ("invalid_api_key",
117
+ "key 无效、缺失或已被撤销。检查 KRONOS_API_KEY 是否复制完整"
118
+ "(oapk_ 后面还有 32 位)。刚撤销的 key 服务端有最多 5 分钟缓存。"),
119
+ 403: ("wrong_key_type",
120
+ f"key 本身有效,但它的类型不是 KRONOS(可能申请成了 KPRED / FIN_R1)。"
121
+ f"到 {APPLY_URL} 重新申请一把 KRONOS 类型的。"),
122
+ 404: ("symbol_not_found",
123
+ "找不到这个代码。A 股用 600519 或 600519.SH,美股用 ticker(AAPL),"
124
+ "港股用 5 位数字(00700)。"),
125
+ 429: ("rate_limited",
126
+ "触发速率限制。无效 key 每 IP 每分钟有硬顶(防洪水);"
127
+ "如果你的 key 是有效的,大概率是 quota 打完了(默认 1000 次)。"),
128
+ 502: ("upstream_down", "上游 GPU 服务异常,通常是重启中,稍后重试。"),
129
+ 504: ("upstream_timeout", "上游超时。GPU 排队时会这样,稍后重试。"),
130
+ }
131
+ code, msg = table.get(status, ("http_error", f"上游返回 HTTP {status}。"))
132
+ return json.dumps({
133
+ "error": code, "http_status": status, "message": msg,
134
+ "upstream_body": body[:300],
135
+ }, ensure_ascii=False)
136
+
137
+
138
+ def _request(method: str, path: str, **kw) -> str | dict:
139
+ """统一的请求出口。失败一律返回 JSON 字符串,成功返回 dict。
140
+
141
+ 调用方靠 isinstance 区分 —— 和 akshare-mcp 里 _resolve 同一套路,
142
+ 省掉一层异常包装。
143
+ """
144
+ key = _key()
145
+ if not key:
146
+ return _no_key_error()
147
+
148
+ url = f"{_base_url()}{path}"
149
+ try:
150
+ with httpx.Client(timeout=_TIMEOUT) as client:
151
+ r = client.request(
152
+ method, url,
153
+ headers={"Authorization": f"Bearer {key}",
154
+ "Content-Type": "application/json"},
155
+ **kw,
156
+ )
157
+ except httpx.TimeoutException:
158
+ return json.dumps({
159
+ "error": "timeout",
160
+ "message": f"请求超时({_TIMEOUT.read:.0f}s)。GPU 推理本来就要 30-70 秒,"
161
+ f"排队时更久 —— 调大 KRONOS_TIMEOUT 再试。",
162
+ }, ensure_ascii=False)
163
+ except httpx.HTTPError as e:
164
+ return json.dumps({
165
+ "error": "network_error",
166
+ "message": f"{type(e).__name__}: {str(e)[:200]}",
167
+ "url": url,
168
+ "hint": "连不上服务。检查网络,或用 KRONOS_URL 指到你自己的部署。",
169
+ }, ensure_ascii=False)
170
+
171
+ if r.status_code != 200:
172
+ return _http_error(r.status_code, r.text)
173
+
174
+ try:
175
+ return r.json()
176
+ except ValueError:
177
+ return json.dumps({
178
+ "error": "bad_response",
179
+ "message": "上游返回的不是 JSON。",
180
+ "body": r.text[:300],
181
+ }, ensure_ascii=False)
182
+
183
+
184
+ @mcp.tool()
185
+ def kronos_health() -> str:
186
+ """确认 Kronos 服务能连上、你的 key 是有效的。
187
+
188
+ **遇到任何问题先调这个。**它能把「key 不对」和「代码不对」区分开 ——
189
+ 直接调 predict 失败时,这两种原因看起来是一样的。
190
+ """
191
+ res = _request("GET", "/health")
192
+ if isinstance(res, str):
193
+ return res
194
+ return json.dumps({"ok": True, "upstream": res,
195
+ "base_url": _base_url()}, ensure_ascii=False)
196
+
197
+
198
+ @mcp.tool()
199
+ def kronos_predict(symbol: str, pred_len: int = 10) -> str:
200
+ """预测一只股票未来 N 根日 K 线。
201
+
202
+ symbol A 股 `600519` / `600519.SH`,美股 `AAPL`,港股 `00700`
203
+ pred_len 预测多少根,1~30。默认 10
204
+
205
+ 返回每根预测 K 线的 date / open / high / low / close / volume,
206
+ 外加 `last_close`(最后一根真实收盘价)和 `expected_return`
207
+ (末根预测收盘 / 最后真实收盘 − 1)—— 这个比例是最常用的那个数。
208
+
209
+ ⚠️ **一次调用要 30-70 秒**(GPU 推理),这是正常的,不要因为慢就重试。
210
+ 要问多只票就依次调用,不要指望批量 —— 上游一次只接受一个代码。
211
+
212
+ ⚠️ 这是模型的**统计外推,不是投资建议**。它没有读新闻、不知道停牌和财报,
213
+ 极端行情下会显著偏离。把它当成一个参考信号,不要当结论。
214
+ """
215
+ sym = (symbol or "").strip()
216
+ if not sym:
217
+ return json.dumps({"error": "bad_symbol",
218
+ "message": "symbol 不能为空"}, ensure_ascii=False)
219
+ try:
220
+ n = int(pred_len)
221
+ except (TypeError, ValueError):
222
+ return json.dumps({"error": "bad_pred_len",
223
+ "message": f"pred_len 要是整数,收到 {pred_len!r}"},
224
+ ensure_ascii=False)
225
+ if not 1 <= n <= 30:
226
+ return json.dumps({
227
+ "error": "bad_pred_len",
228
+ "message": f"pred_len 要在 1~30 之间,收到 {n}。"
229
+ f"预测越远越不可靠,超过 30 根没有参考价值。",
230
+ }, ensure_ascii=False)
231
+
232
+ res = _request("POST", "/predict", json={"symbol": sym, "pred_len": n})
233
+ if isinstance(res, str):
234
+ return res
235
+
236
+ preds = res.get("predictions") or []
237
+ last_close = res.get("last_close")
238
+
239
+ out: dict = {
240
+ "symbol": sym,
241
+ "pred_len": n,
242
+ "last_close": last_close,
243
+ "predictions": preds,
244
+ }
245
+
246
+ # expected_return 只在两个数都真实存在且为正时才给。
247
+ # 算不出就**不放这个字段**,不填 0 —— 0 会被读成「预测持平」,
248
+ # 那是个结论,而真相是「没算出来」。
249
+ try:
250
+ lc = float(last_close)
251
+ end = float(preds[-1].get("close"))
252
+ if lc > 0 and end > 0:
253
+ out["expected_return"] = round(end / lc - 1.0, 6)
254
+ out["expected_return_pct"] = round((end / lc - 1.0) * 100, 2)
255
+ except (TypeError, ValueError, IndexError, AttributeError):
256
+ out["expected_return_note"] = (
257
+ "算不出预期收益率 —— 上游没给 last_close 或末根预测收盘价")
258
+
259
+ out["disclaimer"] = ("Kronos 是纯时序统计模型,不读新闻、不知停牌与财报。"
260
+ "输出是参考信号,不是投资建议。")
261
+ return json.dumps(out, ensure_ascii=False)
262
+
263
+
264
+ def main() -> None:
265
+ """入口 —— PyPI 的 console_scripts 指向这里。
266
+
267
+ 默认 stdio:Claude Desktop / Cursor / uvx 都走这个,是 MCP 的默认形态。
268
+ streamable-http / sse 留给「跑成常驻服务、多个客户端连」的场景
269
+ (比如接进 Hunter 的「能力 → 接入一个工具」)。MCP 规范已把 SSE 标记为
270
+ 过时,新部署优先用 streamable-http。
271
+ """
272
+ transport = (os.getenv("KRONOS_MCP_TRANSPORT") or "stdio").strip().lower()
273
+ if transport == "stdio":
274
+ mcp.run(transport="stdio")
275
+ return
276
+
277
+ # 远程传输 · host/port 在 mcp 2.x 里是 run() 的关键字参数,
278
+ # 不再是 1.x 那个 mcp.settings.host/port
279
+ host = os.getenv("KRONOS_MCP_HOST", "0.0.0.0")
280
+ port = int(os.getenv("KRONOS_MCP_PORT", "8932"))
281
+ if transport in ("http", "streamable-http"):
282
+ mcp.run(transport="streamable-http", host=host, port=port)
283
+ elif transport == "sse":
284
+ mcp.run(transport="sse", host=host, port=port)
285
+ else:
286
+ raise SystemExit(
287
+ f"未知的 KRONOS_MCP_TRANSPORT={transport!r} —— "
288
+ f"只支持 stdio(默认)/ streamable-http / sse")
289
+ return
290
+
291
+ # 远程传输 · host/port 在 mcp 2.x 里是 run() 的关键字参数,
292
+ # 不再是 1.x 那个 mcp.settings.host/port
293
+ host = os.getenv("KRONOS_MCP_HOST", "0.0.0.0")
294
+ port = int(os.getenv("KRONOS_MCP_PORT", "8932"))
295
+ if transport in ("http", "streamable-http"):
296
+ mcp.run(transport="streamable-http", host=host, port=port)
297
+ elif transport == "sse":
298
+ mcp.run(transport="sse", host=host, port=port)
299
+ else:
300
+ raise SystemExit(
301
+ f"未知的 KRONOS_MCP_TRANSPORT={transport!r} —— "
302
+ f"只支持 stdio(默认)/ streamable-http / sse")
303
+
304
+
305
+ if __name__ == "__main__":
306
+ main()
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "kronos-mcp"
7
+ version = "0.1.0"
8
+ description = "MCP server for Kronos stock price forecasting — A-shares, US and HK equities"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "AgentPit", email = "hangeaiagent@gmail.com" }]
13
+ keywords = [
14
+ "mcp", "model-context-protocol", "stock", "forecast", "quant",
15
+ "finance", "kronos", "a-share", "timeseries",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Financial and Insurance Industry",
20
+ "Intended Audience :: Developers",
21
+ "License :: OSI Approved :: Apache Software License",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Office/Business :: Financial :: Investment",
27
+ ]
28
+ dependencies = [
29
+ "mcp>=2.0.0",
30
+ "httpx>=0.27.0",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/agentpit-io/hunter-community"
35
+ Repository = "https://github.com/agentpit-io/hunter-community"
36
+ Documentation = "https://github.com/agentpit-io/hunter-community/blob/main/tools/kronos-mcp/README.md"
37
+ Issues = "https://github.com/agentpit-io/hunter-community/issues"
38
+
39
+ [project.scripts]
40
+ kronos-mcp = "kronos_mcp.server:main"
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["kronos_mcp"]
@@ -0,0 +1,3 @@
1
+ # MCP Python SDK · 2.x 起 FastMCP 更名为 MCPServer,本包用的是 2.x 的 API
2
+ mcp>=2.0.0
3
+ httpx>=0.27.0
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.agentpit-io/kronos-mcp",
4
+ "title": "Kronos",
5
+ "description": "Stock price forecasting for A-shares, US and HK equities — K-line time-series model",
6
+ "repository": {
7
+ "url": "https://github.com/agentpit-io/hunter-community",
8
+ "source": "github",
9
+ "subfolder": "tools/kronos-mcp"
10
+ },
11
+ "version": "0.1.0",
12
+ "packages": [
13
+ {
14
+ "registryType": "pypi",
15
+ "registryBaseUrl": "https://pypi.org",
16
+ "identifier": "kronos-mcp",
17
+ "version": "0.1.0",
18
+ "runtimeHint": "uvx",
19
+ "transport": {
20
+ "type": "stdio"
21
+ },
22
+ "environmentVariables": [
23
+ {
24
+ "name": "KRONOS_API_KEY",
25
+ "description": "KRONOS-type API key from https://hunter.agentpit.io/dev/api-keys",
26
+ "isRequired": true,
27
+ "isSecret": true
28
+ },
29
+ {
30
+ "name": "KRONOS_URL",
31
+ "description": "Override the upstream endpoint if you self-host",
32
+ "default": "https://kronos.agentpit.io"
33
+ },
34
+ {
35
+ "name": "KRONOS_TIMEOUT",
36
+ "description": "Read timeout in seconds. GPU inference takes 30-70s",
37
+ "default": "180"
38
+ }
39
+ ]
40
+ }
41
+ ]
42
+ }