ht32-code-mcp 0.2.1__tar.gz → 0.2.2__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.
Files changed (23) hide show
  1. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/DEPLOY.md +62 -4
  2. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/PKG-INFO +1 -1
  3. ht32_code_mcp-0.2.2/README.md +6 -0
  4. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/pyproject.toml +1 -1
  5. ht32_code_mcp-0.2.2/smoke_http.py +193 -0
  6. ht32_code_mcp-0.2.2/src/ht32_code_mcp/__init__.py +4 -0
  7. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/src/ht32_code_mcp/server.py +19 -5
  8. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/uv.lock +1 -1
  9. ht32_code_mcp-0.2.1/README.md +0 -5
  10. ht32_code_mcp-0.2.1/src/ht32_code_mcp/__init__.py +0 -3
  11. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/.gitignore +0 -0
  12. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/.gitkeep +0 -0
  13. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/e2e_scenario_a.py +0 -0
  14. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/e2e_scenario_b.py +0 -0
  15. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/ht32-code-mcp-update.md +0 -0
  16. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/smoke_stdio.py +0 -0
  17. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/src/ht32_code_mcp/__main__.py +0 -0
  18. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/src/ht32_code_mcp/exporter.py +0 -0
  19. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/src/ht32_code_mcp/index.py +0 -0
  20. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/src/ht32_code_mcp/knowledge.py +0 -0
  21. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/src/ht32_code_mcp/usage.py +0 -0
  22. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/tests/conftest.py +0 -0
  23. {ht32_code_mcp-0.2.1 → ht32_code_mcp-0.2.2}/tests/test_tools.py +0 -0
@@ -25,6 +25,44 @@ PyPI 发布包 → 1Panel 原生 MCP 管理(类型 uvx,启动命令 uvx ht32
25
25
  代码默认走 `stdio`(`MCP_TRANSPORT` 未设置时),supergateway 负责 stdio→Streamable HTTP 转换。
26
26
  与 ht32-wiki 同构:SSE 无心跳会被 OpenResty 空闲切断,故走 streamableHttp stateful。
27
27
 
28
+ ## 传输模式(0.2.2+ 支持原生 HTTP)
29
+
30
+ `server.py` 按 `MCP_TRANSPORT` 分派三种传输,**与 ht32-wiki 同构**:
31
+
32
+ | `MCP_TRANSPORT` | 行为 | 典型用途 |
33
+ |---|---|---|
34
+ | `stdio`(默认) | 标准输入输出 | 本地开发;或供 supergateway 包装为 Streamable HTTP |
35
+ | `streamable-http` | 单端点 HTTP,**默认有状态会话**(FastMCP `stateless_http=False`) | 直接对外暴露,无需 supergateway 这一层 |
36
+ | `sse` | 旧式 SSE(无心跳,易被反代空闲切断) | 兼容旧客户端,不推荐 |
37
+
38
+ HTTP 模式监听地址由 `MCP_HOST`(默认 `0.0.0.0`)/ `MCP_PORT`(默认 `8101`,
39
+ **避开 wiki 的 8100**)控制;端点路径为 FastMCP 默认的 `/mcp`。
40
+
41
+ ```bash
42
+ # 原生 Streamable HTTP(stateful),供反代直连
43
+ MCP_TRANSPORT=streamable-http MCP_HOST=0.0.0.0 MCP_PORT=8101 uvx ht32-code-mcp
44
+ ```
45
+
46
+ > **stateful 是默认值而非开关**:FastMCP 4.0.5 的 `stateless_http` 默认 `False`,
47
+ > 即同一 `mcp-session-id` 复用同一会话,子进程/进程内状态不每次 POST 重建——
48
+ > 与 supergateway 的 `--stateful --sessionTimeout 3600000` 语义一致。
49
+ > 若将来需要 stateless,须显式改 FastMCP 设置,届时 Wiki/Code 两端要一起对照。
50
+
51
+ ### 生产接线:一律用 supergateway 包装(2026-09-23 定)
52
+
53
+ 两个 MCP(wiki 与 code)**统一由 supergateway 包装**,不接受「服务自身原生 HTTP」形态:
54
+
55
+ - 容器里 `uvx ht32-code-mcp` 保持 stdio,由网关转 Streamable HTTP + stateful
56
+ - 好处:**网关这一层统一管协议版本与会话**(`--protocolVersion 2025-06-18`
57
+ `--stateful --sessionTimeout 3600000`),云端反代只认固定的容器端口/路径,
58
+ 换服务或改内部实现时反代无需跟着动
59
+
60
+ 原生 `streamable-http` / `sse` 保留为**能力**(本地直跑、调试、将来去网关时可用),
61
+ 但**当前生产不启用**;本地 compose 部署方案见本仓库 `.work/docker/docker-compose.yml`(按此约定编写)。
62
+
63
+ 无论走哪种,**监听端口必须绑 `0.0.0.0`**(默认值),否则网关/反代从容器外连不上。
64
+ 验证:`ss -ltnp | grep -E '1808[0-2]'` 应显示 `0.0.0.0`,而非 `127.0.0.1`。
65
+
28
66
  ## 关键配置
29
67
 
30
68
  1Panel MCP 表单:
@@ -191,6 +229,20 @@ docker exec 1Panel-openresty-ih7J nginx -t
191
229
  反馈通道补 middleware 判断不了的语义问题。每条记录都带 `service` 字段
192
230
  (`code`),便于两服务日志合并分析。
193
231
 
232
+ ### `ok` 字段的口径(0.2.1+)
233
+
234
+ 工具用**字符串**返回错误而不抛异常,middleware 拿不到协议级 `is_error`,
235
+ 故靠返回值文本判断。口径是**工具是否完成了它的工作**:
236
+
237
+ | 返回值 | `ok` | 说明 |
238
+ |---|---|---|
239
+ | `错误:…` / `导出失败…` | `false` | 参数校验、路径越界、IO 失败、无法产出工程 |
240
+ | `未找到…` / `不在支持列表` / `不在覆盖范围` / `暂无模板` | `true` | **"无数据"≠失败** |
241
+
242
+ "无数据"刻意记为成功:`用户搜 BLDC 但知识库没有` 正是指导 P2-1 补内容的
243
+ 最强信号,若标成失败会被分析工具当作噪声剔除。标记表在 `usage.py` 的
244
+ `SOFT_FAILURE_MARKERS`,**改动工具返回值文案时需同步维护**。
245
+
194
246
  ### 目录布局
195
247
 
196
248
  ```
@@ -205,9 +257,14 @@ docker exec 1Panel-openresty-ih7J nginx -t
205
257
 
206
258
  | 变量 | 默认 | 说明 |
207
259
  |------|------|------|
208
- | `FEEDBACK_ROOT` | 空(= **关闭**,本地开发默认不落盘) | 日志根目录,服务名子目录自动拼接 |
260
+ | `FEEDBACK_ROOT` | 空(= **关闭**,本地开发默认不落盘) | 日志根目录,服务名子目录自动拼接;**必须填绝对路径**(如 `/app/feedback`,不要写 `app/feedback`) |
209
261
  | `CODE_LOG_DIR` | — | 已废弃,改用 `FEEDBACK_ROOT` |
210
262
 
263
+ > ⚠️ **必须用绝对路径**:`FEEDBACK_ROOT=app/feedback` 会相对容器 WORKDIR
264
+ > 解析。实测云端曾因漏写前导 `/`,`get_code_version` 显示为
265
+ > `反馈与用量日志:启用(app/feedback/code)`——靠 WORKDIR 恰为 `/` 才侥幸
266
+ > 落对位置,改镜像 WORKDIR 后会静默写到别处。验证时看输出路径是否以 `/` 开头。
267
+
211
268
  > **隐私**:参数按原文记录,`query` / `question` 可能含用户项目信息。
212
269
  > 该目录只应挂在受控服务器上,**不要提交进仓库**(定时脚本是
213
270
  > `git reset --hard` 镜像模式,写进仓库也会被覆盖丢失)。
@@ -305,10 +362,11 @@ uv lock && uv build && uv publish
305
362
  | # | 任务 | 说明 | 状态 |
306
363
  |---|------|------|------|
307
364
  | 3.1 | MCP 工具实现(7 检索/导出 + 1 反馈) | `.work/mcp-server/src/ht32_code_mcp/`(FastMCP) | ✅ |
308
- | 3.2 | 工具级测试 | `pytest` 44 用例全绿 | ✅ |
309
- | 3.3 | PyPI 发布(hatchling) | `ht32-code-mcp` 0.2.0(2026-09-21,含 bundle 通道 + 统一反馈/用量日志) | ✅ |
365
+ | 3.2 | 工具级测试 | `pytest` 46 用例全绿 | ✅ |
366
+ | 3.3 | PyPI 发布(hatchling) | `ht32-code-mcp` 0.2.2(2026-09-23,含 bundle 通道 + 统一反馈/用量日志 + 软失败识别 + 原生 Streamable HTTP) | ✅ |
310
367
  | 3.4 | 云端 git clone + 挂载 | `/opt/ht32-code:/app/code` | ✅ |
311
368
  | 3.5 | 1Panel 部署(uvx + supergateway → streamableHttp stateful,镜像 next-uvx) | `https://mcp.s621.work/ht32-code` | ✅ |
312
369
  | 3.6 | OpenResty 超时修复(`proxy_read_timeout 3600s`) | 修复 60s 空闲断连;2026-09-21 复核两端空闲 100s 后调用仍 200,未被 1Panel 回退 | ✅ |
313
370
  | 3.7 | 云端 zip 下载通道(P1-3) | 服务器侧已上线:下载 1608157 字节 / SHA256 一致 / 解压 455 文件 / UV4 0E0W | ✅ |
314
- | 3.8 | 定时同步脚本含 bundle 清理(P1-2) | `ht32-code-mcp-update.md` 第 6 步 `find … -mmin +60 -delete` | ✅ |
371
+ | 3.8 | 定时同步脚本含 bundle 清理(P1-2) | `ht32-code-mcp-update.md` 第 6 步 `find … -mmin +60 -delete` | ✅ |
372
+ | 3.9 | 反馈与用量日志(P1-5,与 wiki 统一) | 0.2.1;云端已启用,但 `FEEDBACK_ROOT` 漏写前导 `/`(显示 `app/feedback/code`),需改绝对路径 | ⚠️ |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ht32-code-mcp
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: HT32 Code MCP Server - 为 AI Agent 提供 HT32 函数库/例程/模板检索与工程导出
5
5
  License-Expression: MIT
6
6
  Keywords: firmware,holtek,ht32,mcp,mcu
@@ -0,0 +1,6 @@
1
+ # .work/mcp-server/
2
+
3
+ MCP server 代码目录(Python + FastMCP),实现 7 个检索/导出工具 + 1 个反馈工具。
4
+
5
+ 传输由 `MCP_TRANSPORT` 决定:`stdio`(默认,供 supergateway 包装)/ `streamable-http` / `sse`。
6
+ 部署与排错见 [DEPLOY.md](DEPLOY.md)。
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ht32-code-mcp"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "HT32 Code MCP Server - 为 AI Agent 提供 HT32 函数库/例程/模板检索与工程导出"
9
9
  requires-python = ">=3.11"
10
10
  license = "MIT"
@@ -0,0 +1,193 @@
1
+ """Streamable HTTP 冒烟测试:以子进程起 server(MCP_TRANSPORT=streamable-http),
2
+ 用真 HTTP 客户端走 initialize → notifications/initialized → tools/list → tools/call,
3
+ 并验证**会话复用**(stateful:同一 mcp-session-id 持续可用)。
4
+
5
+ 为什么必须真起进程 + 真 HTTP:与 smoke_stdio.py 同理——直接 import 调库测不到
6
+ 握手时序、传输层会话管理、stdout 洁净度;而这两者正是线上故障的高发面
7
+ (wiki 侧曾因 smoke 缺失漏掉协议层两个缺陷到线上)。
8
+
9
+ 用法(从 .work/mcp-server/ 目录,需已安装依赖):
10
+ python smoke_http.py
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import os
16
+ import socket
17
+ import subprocess
18
+ import sys
19
+ import time
20
+ import urllib.request
21
+ from pathlib import Path
22
+
23
+ SCRIPT_DIR = Path(__file__).resolve().parent
24
+ SRC = SCRIPT_DIR / "src"
25
+ sys.path.insert(0, str(SRC))
26
+
27
+ from fastmcp import Client # noqa: E402
28
+
29
+ # server.py 里 FastMCP.run("streamable-http") 的默认端点路径
30
+ MCP_PATH = "/mcp"
31
+ EXPECTED_TOOLS = {
32
+ "get_code_version", "list_libraries", "find_example", "get_file",
33
+ "search_code_knowledge", "get_project_manifest", "export_project_bundle",
34
+ }
35
+ START_TIMEOUT = 60.0
36
+ REQUEST_TIMEOUT = 60.0
37
+
38
+
39
+ def _free_port() -> int:
40
+ with socket.socket() as s:
41
+ s.bind(("127.0.0.1", 0))
42
+ return int(s.getsockname()[1])
43
+
44
+
45
+ def _wait_port(port: int, proc: subprocess.Popen, deadline: float) -> None:
46
+ while time.monotonic() < deadline:
47
+ if proc.poll() is not None:
48
+ raise RuntimeError(f"服务进程提前退出,退出码 {proc.returncode}")
49
+ with socket.socket() as s:
50
+ s.settimeout(0.5)
51
+ if s.connect_ex(("127.0.0.1", port)) == 0:
52
+ return
53
+ time.sleep(0.2)
54
+
55
+ raise TimeoutError(f"端口 {port} 在 {START_TIMEOUT}s 内未监听")
56
+
57
+
58
+ def _parse_sse(text: str) -> dict:
59
+ """从 SSE 文本中取第一条 data: 的 JSON(FastMCP 默认 json_response=False)。"""
60
+ for line in text.splitlines():
61
+ if line.startswith("data:"):
62
+ return json.loads(line[len("data:"):].strip())
63
+
64
+ raise AssertionError(f"响应中没有 SSE data 行:{text[:300]!r}")
65
+
66
+
67
+ def _rpc(url: str, payload: dict, session_id: str | None = None):
68
+ """POST 一次 JSON-RPC(标准库实现,避免为测试引入 httpx 依赖)。"""
69
+ headers = {
70
+ "Content-Type": "application/json",
71
+ "Accept": "application/json, text/event-stream",
72
+ }
73
+ if session_id:
74
+ headers["mcp-session-id"] = session_id
75
+
76
+ req = urllib.request.Request(
77
+ url, data=json.dumps(payload).encode("utf-8"), headers=headers, method="POST")
78
+ with urllib.request.urlopen(req, timeout=REQUEST_TIMEOUT) as resp:
79
+ return resp.status, dict(resp.headers), resp.read().decode("utf-8", "replace")
80
+
81
+
82
+ def check_transport_layer(url: str) -> str:
83
+ """协议层:initialize 拿 session → initialized 通知 → 复用同一 session 调 tools/list。"""
84
+ status, headers, body = _rpc(url, {
85
+ "jsonrpc": "2.0", "id": 1, "method": "initialize",
86
+ "params": {
87
+ "protocolVersion": "2025-06-18",
88
+ "capabilities": {},
89
+ "clientInfo": {"name": "smoke-http", "version": "0"},
90
+ },
91
+ })
92
+ assert status == 200, f"initialize 非 200:{status} {body[:300]}"
93
+ session_id = headers.get("mcp-session-id")
94
+ assert session_id, f"initialize 未返回 mcp-session-id(头:{headers})"
95
+ print(f"✅ initialize 成功,会话 id = {session_id[:16]}…")
96
+
97
+ proto = _parse_sse(body).get("result", {}).get("protocolVersion")
98
+ assert proto, f"initialize 响应缺少 protocolVersion:{body[:300]}"
99
+ print(f"✅ 协议版本协商 = {proto}")
100
+
101
+ status, _, _ = _rpc(url, {"jsonrpc": "2.0", "method": "notifications/initialized"},
102
+ session_id=session_id)
103
+ assert status in (200, 202), f"initialized 通知非 2xx:{status}"
104
+
105
+ status, headers, body = _rpc(
106
+ url, {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}, session_id=session_id)
107
+ assert status == 200, f"tools/list 非 200:{status}"
108
+ assert headers.get("mcp-session-id") == session_id, "tools/list 换用了新会话(非 stateful)"
109
+
110
+ tools = {t["name"] for t in _parse_sse(body)["result"]["tools"]}
111
+ missing = EXPECTED_TOOLS - tools
112
+ assert not missing, f"缺失工具:{missing}"
113
+ print(f"✅ 同一会话复用成功(stateful),工具 {len(tools)} 个:{sorted(tools)}")
114
+
115
+ return session_id
116
+
117
+
118
+ async def check_client_layer(url: str) -> None:
119
+ """客户端层:用 FastMCP 官方 Client 走完整 tools/call(含结果反序列化)。"""
120
+ async with Client(url) as client:
121
+ tools = {t.name for t in await client.list_tools()}
122
+ missing = EXPECTED_TOOLS - tools
123
+ assert not missing, f"Client 视角缺失工具:{missing}"
124
+
125
+ r = await client.call_tool("get_code_version", {})
126
+ print("✅ get_code_version:", (r.content[0].text or "").splitlines()[0])
127
+
128
+ r = await client.call_tool("find_example", {"peripheral": "GPIO", "limit": 3})
129
+ text = r.content[0].text or ""
130
+ assert "example/GPIO" in text, text
131
+ print(f"✅ find_example(GPIO): {text.splitlines()[0]}")
132
+
133
+
134
+ def main() -> int:
135
+ port = _free_port()
136
+ url = f"http://127.0.0.1:{port}{MCP_PATH}"
137
+
138
+ env = dict(os.environ)
139
+ env["PYTHONPATH"] = str(SRC) + os.pathsep + env.get("PYTHONPATH", "")
140
+ env["MCP_TRANSPORT"] = "streamable-http"
141
+ env["MCP_HOST"] = "127.0.0.1"
142
+ env["MCP_PORT"] = str(port)
143
+ env.pop("FEEDBACK_ROOT", None) # 不带日志落盘,保持测试无副作用
144
+
145
+ # stdout 丢弃(HTTP 模式下 uvicorn 访问日志走 stdout,与协议无关);
146
+ # 启动与异常信息走 stderr,失败时打印出来供排查
147
+ proc = subprocess.Popen(
148
+ [sys.executable, "-m", "ht32_code_mcp"],
149
+ cwd=str(SCRIPT_DIR),
150
+ env=env,
151
+ stdout=subprocess.DEVNULL,
152
+ stderr=subprocess.PIPE,
153
+ text=True,
154
+ encoding="utf-8",
155
+ errors="replace",
156
+ )
157
+
158
+ def stop() -> str:
159
+ """服务是长驻进程,须先 terminate 再取输出,不能直接 communicate。"""
160
+ if proc.poll() is None:
161
+ proc.terminate()
162
+ try:
163
+ _, err = proc.communicate(timeout=10)
164
+ except subprocess.TimeoutExpired:
165
+ proc.kill()
166
+ _, err = proc.communicate()
167
+ return err or ""
168
+
169
+ try:
170
+ _wait_port(port, proc, time.monotonic() + START_TIMEOUT)
171
+ print(f"✅ 服务已监听 http://127.0.0.1:{port}{MCP_PATH}")
172
+
173
+ check_transport_layer(url)
174
+
175
+ import anyio
176
+ anyio.run(check_client_layer, url)
177
+
178
+ stop()
179
+
180
+ print("✅ Streamable HTTP 冒烟全部通过(stateful 会话复用已证实)")
181
+ return 0
182
+
183
+ except Exception:
184
+ print("❌ 冒烟失败,服务 stderr:", file=sys.stderr)
185
+ print(stop().strip()[:3000], file=sys.stderr)
186
+ raise
187
+
188
+ finally:
189
+ stop()
190
+
191
+
192
+ if __name__ == "__main__":
193
+ raise SystemExit(main())
@@ -0,0 +1,4 @@
1
+ """ht32-code-mcp:HT32 代码知识库 MCP Server(7 检索/导出工具 + 1 反馈工具)。
2
+
3
+ 传输:stdio(默认)/ streamable-http / sse,由 MCP_TRANSPORT 决定。
4
+ """
@@ -1,7 +1,7 @@
1
1
  """HT32 Code MCP Server。
2
2
 
3
3
  提供函数库/例程/模板检索与工程导出,供 AI Agent 调用。
4
- 传输协议:stdio(P0 本地阶段;云端阶段见总纲 5.3)。
4
+ 传输协议:stdio(供 supergateway 包装为 Streamable HTTP)或 Streamable HTTP / SSE。
5
5
  """
6
6
  from __future__ import annotations
7
7
 
@@ -49,6 +49,11 @@ CLOUD_BUNDLE = bool(BUNDLE_DIR and BUNDLE_BASE_URL)
49
49
  FEEDBACK_ROOT = os.environ.get("FEEDBACK_ROOT", "").strip()
50
50
  LOG_DIR = resolve_log_dir(FEEDBACK_ROOT, SERVICE_CODE)
51
51
 
52
+ # HTTP 传输模式监听地址(MCP_TRANSPORT 非 stdio 时生效)
53
+ # 默认 8101 而非 8100:wiki 占用 8100,两服务常同机部署,避开默认端口冲突
54
+ HOST = os.environ.get("MCP_HOST", "0.0.0.0")
55
+ PORT = _env_int("MCP_PORT", 8101)
56
+
52
57
  index = CodeIndex(CODE_ROOT).load()
53
58
  knowledge = KnowledgeSearch(CODE_ROOT)
54
59
  exporter = ProjectExporter(CODE_ROOT)
@@ -427,7 +432,12 @@ def submit_feedback(
427
432
 
428
433
 
429
434
  def main() -> None:
430
- """入口:按 MCP_TRANSPORT 环境变量启动(默认 stdio,P0 本地阶段)。"""
435
+ """入口:按 MCP_TRANSPORT 环境变量启动对应传输模式的服务器。
436
+
437
+ - stdio: 供 supergateway 包装为 Streamable HTTP(默认)
438
+ - streamable-http: 直接对外暴露的单端点 HTTP(有状态会话)
439
+ - sse: FastMCP 内置 SSE 服务器(旧式,无心跳易被反代空闲切断)
440
+ """
431
441
  transport = os.environ.get("MCP_TRANSPORT", "stdio").lower()
432
442
  stats = index.data.get("stats", {})
433
443
  print(f"[ht32-code-mcp] 程序版本: ht32-code-mcp {_MCP_VERSION}", file=sys.stderr)
@@ -437,11 +447,15 @@ def main() -> None:
437
447
  print(f"[ht32-code-mcp] 索引就绪: {stats.get('library_modules', 0)} 模块 / "
438
448
  f"{stats.get('examples', 0)} 例程 / {stats.get('raw_files', 0)} raw 文件", file=sys.stderr)
439
449
  if transport == "stdio":
440
- print("[ht32-code-mcp] 启动服务: stdio", file=sys.stderr)
450
+ print("[ht32-code-mcp] 启动服务: stdio(供 supergateway 包装为 Streamable HTTP)",
451
+ file=sys.stderr)
441
452
  mcp.run(transport="stdio")
453
+ elif transport == "sse":
454
+ print(f"[ht32-code-mcp] 启动服务: SSE {HOST}:{PORT}", file=sys.stderr)
455
+ mcp.run(transport="sse", host=HOST, port=PORT)
442
456
  else:
443
- print(f"[ht32-code-mcp] 传输模式 '{transport}' 暂未启用(P0 仅 stdio)", file=sys.stderr)
444
- sys.exit(2)
457
+ print(f"[ht32-code-mcp] 启动服务: Streamable HTTP {HOST}:{PORT}", file=sys.stderr)
458
+ mcp.run(transport="streamable-http", host=HOST, port=PORT)
445
459
 
446
460
 
447
461
  if __name__ == "__main__":
@@ -440,7 +440,7 @@ wheels = [
440
440
 
441
441
  [[package]]
442
442
  name = "ht32-code-mcp"
443
- version = "0.2.1"
443
+ version = "0.2.2"
444
444
  source = { editable = "." }
445
445
  dependencies = [
446
446
  { name = "fastmcp" },
@@ -1,5 +0,0 @@
1
- # .work/mcp-server/
2
-
3
- MCP server 代码目录(Python/FastMCP),P0-2 在此实现 7 工具(本地 stdio)。
4
-
5
- 当前为占位:仓库初始化(P0-1)时建立骨架,保证目录结构随 git 入库。
@@ -1,3 +0,0 @@
1
- """ht32-code-mcp:HT32 代码知识库 MCP Server(7 工具,本地 stdio)。"""
2
-
3
- __version__ = "0.1.0"
File without changes
File without changes