Skip to content

Commit 9f4ca1c

Browse files
SummerOneTwoclaude
andcommitted
fix: 修复 P0 发布阻断问题 (Phase 1)
修复 4 个 P0 问题,确保 wheel 包可用且 MCP 协议合规: 1. 打包配置修复 - 将 templates/ 移入 src/autocode_mcp/templates/ - pyproject.toml 添加 artifacts 配置 - 修复 TEMPLATES_DIR 路径计算 2. MCP call_tool 协议修复 - 返回类型从 list[TextContent] 改为 CallToolResult - 正确设置 isError 标记 - 添加 structuredContent 字段 3. MCP get_prompt 协议修复 - 返回类型从 str 改为 GetPromptResult - 添加 messages 结构 4. MCP read_resource 协议修复 - 返回类型从 str 改为 ReadResourceResult - 使用 TextResourceContents 新增 tests/test_packaging.py 验收测试 (7 个测试用例) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fea4807 commit 9f4ca1c

10 files changed

Lines changed: 6578 additions & 36 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ build-backend = "hatchling.build"
3030

3131
[tool.hatch.build.targets.wheel]
3232
packages = ["src/autocode_mcp"]
33+
artifacts = ["src/autocode_mcp/templates"]
3334

3435
[tool.pytest.ini_options]
3536
asyncio_mode = "auto"

src/autocode_mcp/__init__.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88

99
__version__ = "0.1.0"
1010

11-
# 获取 templates 目录路径
12-
# __file__ = src/autocode_mcp/__init__.py
13-
# templates = autocode_mcp/templates (需要往上两层)
11+
# 获取 templates 目录路径(包内目录)
1412
_PACKAGE_DIR = os.path.dirname(__file__)
15-
_SRC_DIR = os.path.dirname(_PACKAGE_DIR)
16-
PROJECT_ROOT = os.path.dirname(_SRC_DIR)
17-
TEMPLATES_DIR = os.path.join(PROJECT_ROOT, "templates")
13+
TEMPLATES_DIR = os.path.join(_PACKAGE_DIR, "templates")

src/autocode_mcp/server.py

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@
1111

1212
from mcp.server import Server
1313
from mcp.server.stdio import stdio_server
14-
from mcp.types import Prompt, Resource, TextContent, Tool
14+
from mcp.types import (
15+
CallToolResult,
16+
GetPromptResult,
17+
Prompt,
18+
PromptMessage,
19+
ReadResourceResult,
20+
Resource,
21+
TextContent,
22+
TextResourceContents,
23+
Tool,
24+
)
1525

1626
from . import prompts, resources
1727
from .tools.base import Tool as BaseTool
@@ -86,33 +96,31 @@ async def list_tools() -> list[Tool]:
8696

8797

8898
@app.call_tool()
89-
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
99+
async def call_tool(name: str, arguments: dict[str, Any]) -> CallToolResult:
90100
"""执行工具调用。"""
91101
if name not in TOOLS:
92-
return [
93-
TextContent(
94-
type="text",
95-
text=f"Unknown tool: {name}",
96-
)
97-
]
102+
return CallToolResult(
103+
content=[TextContent(type="text", text=f"Unknown tool: {name}")],
104+
isError=True,
105+
)
98106

99107
tool = TOOLS[name]
100108
try:
101109
result = await tool.execute(**arguments)
102-
return [
103-
TextContent(
104-
type="text",
105-
text=str(result.to_dict()),
106-
)
107-
]
110+
result_dict = result.to_dict()
111+
return CallToolResult(
112+
content=[TextContent(type="text", text=str(result_dict))],
113+
structuredContent=result_dict,
114+
isError=not result.success,
115+
)
108116
except Exception as e:
109117
error_result = ToolResult.fail(str(e))
110-
return [
111-
TextContent(
112-
type="text",
113-
text=str(error_result.to_dict()),
114-
)
115-
]
118+
error_dict = error_result.to_dict()
119+
return CallToolResult(
120+
content=[TextContent(type="text", text=str(error_dict))],
121+
structuredContent=error_dict,
122+
isError=True,
123+
)
116124

117125

118126
def main() -> None:
@@ -148,16 +156,41 @@ async def list_resources() -> list[Resource]:
148156

149157

150158
@app.read_resource()
151-
async def read_resource(uri: str) -> str:
159+
async def read_resource(uri: str) -> ReadResourceResult:
152160
"""读取资源内容。"""
153161
if uri.startswith("template://"):
154162
template_name = uri[11:]
155163
path = resources.get_template_path(template_name)
156164
if path:
157165
with open(path, encoding="utf-8") as f:
158-
return f.read()
159-
return f"Template not found: {template_name}"
160-
return f"Unknown resource: {uri}"
166+
content = f.read()
167+
return ReadResourceResult(
168+
contents=[
169+
TextResourceContents(
170+
uri=uri,
171+
text=content,
172+
mimeType="text/plain",
173+
)
174+
]
175+
)
176+
return ReadResourceResult(
177+
contents=[
178+
TextResourceContents(
179+
uri=uri,
180+
text=f"Template not found: {template_name}",
181+
mimeType="text/plain",
182+
)
183+
]
184+
)
185+
return ReadResourceResult(
186+
contents=[
187+
TextResourceContents(
188+
uri=uri,
189+
text=f"Unknown resource: {uri}",
190+
mimeType="text/plain",
191+
)
192+
]
193+
)
161194

162195

163196
@app.list_prompts()
@@ -173,12 +206,28 @@ async def list_prompts() -> list[Prompt]:
173206

174207

175208
@app.get_prompt()
176-
async def get_prompt(name: str, arguments: dict[str, str] | None = None) -> str:
209+
async def get_prompt(name: str, arguments: dict[str, str] | None = None) -> GetPromptResult:
177210
"""获取提示词内容。"""
178211
content = prompts.get_prompt(name)
179212
if not content:
180-
return f"Prompt not found: {name}"
181-
return content
213+
return GetPromptResult(
214+
description="Error: Prompt not found",
215+
messages=[
216+
PromptMessage(
217+
role="user",
218+
content=TextContent(type="text", text=f"Prompt not found: {name}"),
219+
)
220+
],
221+
)
222+
return GetPromptResult(
223+
description=f"Prompt template: {name}",
224+
messages=[
225+
PromptMessage(
226+
role="user",
227+
content=TextContent(type="text", text=content),
228+
)
229+
],
230+
)
182231

183232

184233
if __name__ == "__main__":
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Checker 模板 - 基于 testlib.h
2+
// 用于比较选手输出和标准答案
3+
4+
#include "testlib.h"
5+
6+
int main(int argc, char* argv[]) {
7+
registerTestlibCmd(argc, argv);
8+
9+
// 读取输入
10+
int n = inf.readInt();
11+
12+
// 读取答案
13+
long long jury = ans.readLong();
14+
15+
// 读取选手输出
16+
long long contestant = ouf.readLong();
17+
18+
// 比较
19+
if (jury == contestant) {
20+
quitf(_ok, "Correct");
21+
} else {
22+
quitf(_wa, "Expected %lld, got %lld", jury, contestant);
23+
}
24+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Generator 模板 - 基于 testlib.h
2+
// 用于生成测试数据
3+
4+
#include "testlib.h"
5+
#include <iostream>
6+
7+
int main(int argc, char* argv[]) {
8+
registerGen(argc, argv, 1);
9+
10+
// 参数解析
11+
// gen.exe <seed> <type> <n_min> <n_max> <t_min> <t_max>
12+
int seed = atoi(argv[1]);
13+
int type = atoi(argv[2]);
14+
int n_min = atoi(argv[3]);
15+
int n_max = atoi(argv[4]);
16+
int t_min = atoi(argv[5]);
17+
int t_max = atoi(argv[6]);
18+
19+
rnd.setSeed(seed);
20+
21+
// 根据类型生成数据
22+
// type: 1=tiny, 2=random, 3=extreme, 4=tle
23+
int n;
24+
switch (type) {
25+
case 1: // tiny
26+
n = rnd.next(1, 10);
27+
break;
28+
case 2: // random
29+
n = rnd.next(n_min, n_max);
30+
break;
31+
case 3: // extreme
32+
n = n_max;
33+
break;
34+
case 4: // tle
35+
n = n_max;
36+
break;
37+
default:
38+
n = rnd.next(n_min, n_max);
39+
}
40+
41+
// 输出测试数据
42+
std::cout << n << std::endl;
43+
44+
for (int i = 0; i < n; i++) {
45+
if (i > 0) std::cout << " ";
46+
std::cout << rnd.next(1, 1000000000);
47+
}
48+
std::cout << std::endl;
49+
50+
return 0;
51+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Interactor 模板 - 基于 testlib.h
2+
// 用于交互题,与选手程序进行交互
3+
4+
#include "testlib.h"
5+
#include <iostream>
6+
7+
int main(int argc, char* argv[]) {
8+
registerInteraction(argc, argv);
9+
10+
// 读取输入数据
11+
// int n = inf.readInt();
12+
13+
// 与选手交互
14+
// std::cout << n << std::endl;
15+
// std::cout.flush();
16+
17+
// 读取选手输出
18+
// int answer = ouf.readInt();
19+
20+
// 验证答案
21+
// if (answer == expected) {
22+
// quitf(_ok, "Correct");
23+
// } else {
24+
// quitf(_wa, "Wrong answer");
25+
// }
26+
27+
quitf(_ok, "Interactor template");
28+
}

0 commit comments

Comments
 (0)