1- """真实 MCP 端到端兼容性测试。
1+ """MCP 端到端兼容性测试。
22
33通过 stdio 启动 MCP Server 进程,进行完整的协议握手和工具调用验证。
4+ 包含开发模式(python -m)和打包模式(console script)两种测试。
45"""
56
67import asyncio
78import json
89import os
10+ import shutil
911import sys
1012import tempfile
1113
@@ -29,12 +31,10 @@ async def send_request(self, method: str, params: dict | None = None) -> dict:
2931 "params" : params or {},
3032 }
3133
32- # 发送请求
3334 message = json .dumps (request ) + "\n "
3435 self .process .stdin .write (message .encode ())
3536 await self .process .stdin .drain ()
3637
37- # 读取响应
3838 response_line = await self .process .stdout .readline ()
3939 if not response_line :
4040 raise RuntimeError ("MCP server closed connection" )
@@ -48,7 +48,6 @@ async def send_request(self, method: str, params: dict | None = None) -> dict:
4848
4949 async def initialize (self ) -> dict :
5050 """执行 MCP 初始化握手。"""
51- # 发送 initialize 请求
5251 result = await self .send_request (
5352 "initialize" ,
5453 {
@@ -58,7 +57,6 @@ async def initialize(self) -> dict:
5857 },
5958 )
6059
61- # 发送 initialized 通知
6260 notification = {"jsonrpc" : "2.0" , "method" : "notifications/initialized" }
6361 message = json .dumps (notification ) + "\n "
6462 self .process .stdin .write (message .encode ())
@@ -87,10 +85,12 @@ async def close(self) -> None:
8785 await self .process .communicate ()
8886
8987
88+ # ============== 开发模式测试(python -m) ==============
89+
90+
9091@pytest .fixture
9192async def mcp_client ():
92- """启动 MCP Server 并返回客户端实例。"""
93- # 使用 uv run 启动 autocode-mcp
93+ """启动 MCP Server 并返回客户端实例(开发模式)。"""
9494 process = await asyncio .create_subprocess_exec (
9595 sys .executable ,
9696 "-m" ,
@@ -126,10 +126,8 @@ async def test_mcp_list_tools(mcp_client: MCPClient):
126126
127127 tools = await mcp_client .list_tools ()
128128
129- # 验证有 15 个工具
130129 assert len (tools ) == 15
131130
132- # 验证关键工具存在
133131 tool_names = {t ["name" ] for t in tools }
134132 expected_tools = {
135133 "file_read" ,
@@ -158,24 +156,20 @@ async def test_mcp_call_file_read(mcp_client: MCPClient):
158156 try :
159157 result = await mcp_client .call_tool ("file_read" , {"path" : temp_path })
160158
161- # 验证返回结构
162159 assert "content" in result
163160 assert not result .get ("isError" , True )
164161
165- # 验证 content 是列表且包含 TextContent
166162 content = result ["content" ]
167163 assert isinstance (content , list )
168164 assert len (content ) == 1
169165 assert content [0 ]["type" ] == "text"
170166
171- # 验证文本内容是有效 JSON
172167 text = content [0 ]["text" ]
173168 parsed = json .loads (text )
174169 assert parsed ["success" ] is True
175170 assert "data" in parsed
176171 assert parsed ["data" ]["content" ] == "hello world"
177172
178- # 验证 structuredContent 存在
179173 assert "structuredContent" in result
180174 assert result ["structuredContent" ]["success" ] is True
181175 finally :
@@ -207,12 +201,9 @@ async def test_mcp_text_content_is_valid_json(mcp_client: MCPClient):
207201
208202 text = result ["content" ][0 ]["text" ]
209203
210- # 必须是有效 JSON
211204 parsed = json .loads (text )
212205
213- # 不能是 Python repr 格式(如 {'success': True})
214- # Python repr 使用单引号,JSON 使用双引号
215- assert "'" not in text # JSON 不使用单引号
206+ assert "'" not in text
216207 assert parsed ["success" ] is True
217208 finally :
218209 os .unlink (temp_path )
@@ -234,9 +225,86 @@ async def test_mcp_chinese_text_encoding(mcp_client: MCPClient):
234225 text = result ["content" ][0 ]["text" ]
235226 parsed = json .loads (text )
236227
237- # 验证中文正确编码(ensure_ascii=False)
238228 assert parsed ["data" ]["content" ] == chinese_content
239- # 原始文本应该包含中文字符,不是 \uXXXX 转义
240229 assert chinese_content in text
241230 finally :
242231 os .unlink (temp_path )
232+
233+
234+ # ============== 打包模式测试(console script) ==============
235+
236+
237+ @pytest .fixture
238+ async def packaged_mcp_client ():
239+ """启动打包后的 autocode-mcp console script。"""
240+ process = await asyncio .create_subprocess_exec (
241+ "autocode-mcp" ,
242+ stdin = asyncio .subprocess .PIPE ,
243+ stdout = asyncio .subprocess .PIPE ,
244+ stderr = asyncio .subprocess .PIPE ,
245+ env = {** os .environ , "PYTHONIOENCODING" : "utf-8" },
246+ )
247+
248+ client = MCPClient (process )
249+
250+ try :
251+ yield client
252+ finally :
253+ await client .close ()
254+
255+
256+ @pytest .mark .packaging
257+ @pytest .mark .asyncio
258+ async def test_packaged_console_script_handshake (packaged_mcp_client : MCPClient ):
259+ """测试打包后的 console script 能完成 MCP 握手。"""
260+ result = await packaged_mcp_client .initialize ()
261+
262+ assert "protocolVersion" in result
263+ assert "serverInfo" in result
264+ assert result ["serverInfo" ]["name" ] == "autocode-mcp"
265+
266+
267+ @pytest .mark .packaging
268+ @pytest .mark .asyncio
269+ async def test_packaged_console_script_list_tools (packaged_mcp_client : MCPClient ):
270+ """测试打包后的 console script 能列出工具。"""
271+ await packaged_mcp_client .initialize ()
272+
273+ tools = await packaged_mcp_client .list_tools ()
274+
275+ assert len (tools ) == 15
276+ tool_names = {t ["name" ] for t in tools }
277+ assert "solution_build" in tool_names
278+ assert "validator_build" in tool_names
279+
280+
281+ @pytest .mark .packaging
282+ @pytest .mark .asyncio
283+ async def test_packaged_console_script_call_tool (packaged_mcp_client : MCPClient ):
284+ """测试打包后的 console script 能调用工具。"""
285+ await packaged_mcp_client .initialize ()
286+
287+ with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".txt" , delete = False , encoding = "utf-8" ) as f :
288+ f .write ("packaged test" )
289+ temp_path = f .name
290+
291+ try :
292+ result = await packaged_mcp_client .send_request (
293+ "tools/call" , {"name" : "file_read" , "arguments" : {"path" : temp_path }}
294+ )
295+
296+ assert "content" in result
297+ assert not result .get ("isError" , True )
298+
299+ text = result ["content" ][0 ]["text" ]
300+ parsed = json .loads (text )
301+ assert parsed ["success" ] is True
302+ assert parsed ["data" ]["content" ] == "packaged test"
303+ finally :
304+ os .unlink (temp_path )
305+
306+
307+ @pytest .mark .packaging
308+ def test_console_script_exists ():
309+ """验证 autocode-mcp console script 在环境中存在。"""
310+ assert shutil .which ("autocode-mcp" ) is not None
0 commit comments