From 77a0eac2c803d207c715df2e0d4a9b9bc5059cf2 Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Tue, 11 Aug 2026 23:07:00 +0800 Subject: [PATCH] =?UTF-8?q?fix(mcp):=20=E4=BF=AE=E5=A4=8D=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E7=A9=BA=E8=BE=93=E5=85=A5=E9=87=8D=E7=BD=AE=E4=B8=BA?= =?UTF-8?q?=E4=B8=8D=E8=B6=85=E6=97=B6=EF=BC=8C=E5=A2=9E=E5=8A=A0=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E8=B7=AF=E5=BE=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P3: 清空输入框时回退到默认值 300 而非 0;新增两个 tokio 测试覆盖有超时和无超时(error pass-through)路径 --- src/mcp/tool_adapter.rs | 45 +++++++++++++++++++++ web/src/components/Settings/tabs/McpTab.tsx | 5 ++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/mcp/tool_adapter.rs b/src/mcp/tool_adapter.rs index 4dbf012..3943746 100644 --- a/src/mcp/tool_adapter.rs +++ b/src/mcp/tool_adapter.rs @@ -281,4 +281,49 @@ mod tests { // Empty stays empty assert_eq!(sanitize_tool_name(""), ""); } + + #[tokio::test] + async fn test_execute_returns_error_when_server_not_connected() { + // McpClientManager with no connected servers — call should fail immediately + // with "not connected" error, not hang or timeout. + let manager = Arc::new(McpClientManager::new()); + let schema: serde_json::Map = + serde_json::json!({"type": "object"}) + .as_object() + .unwrap() + .clone(); + let tool_info = Tool::new("echo", "Echo tool", schema); + let wrapper = McpToolWrapper::new(manager, "ghost".to_string(), tool_info, 300); + + let result = wrapper.execute(serde_json::json!({})).await; + assert!(result.is_err()); + let msg = result.unwrap_err().to_string(); + assert!( + msg.contains("not connected"), + "expected 'not connected' error, got: {msg}" + ); + } + + #[tokio::test] + async fn test_timeout_zero_does_not_wrap() { + // With timeout_secs = 0, the wrapper should pass through directly + // without tokio::time::timeout. The call still fails because no server + // is connected, but the error path is the non-timeout branch. + let manager = Arc::new(McpClientManager::new()); + let schema: serde_json::Map = + serde_json::json!({"type": "object"}) + .as_object() + .unwrap() + .clone(); + let tool_info = Tool::new("echo", "Echo tool", schema); + let wrapper = McpToolWrapper::new(manager, "ghost".to_string(), tool_info, 0); + + let result = wrapper.execute(serde_json::json!({})).await; + assert!(result.is_err()); + let msg = result.unwrap_err().to_string(); + assert!( + msg.contains("not connected"), + "expected 'not connected' error, got: {msg}" + ); + } } diff --git a/web/src/components/Settings/tabs/McpTab.tsx b/web/src/components/Settings/tabs/McpTab.tsx index b6eb512..6e206c1 100644 --- a/web/src/components/Settings/tabs/McpTab.tsx +++ b/web/src/components/Settings/tabs/McpTab.tsx @@ -63,7 +63,10 @@ export function McpTab({ config, update, setToast }: Props) { update('mcp_tool_timeout_secs', Number(e.target.value))} + onChange={(e) => { + const v = e.target.value; + update('mcp_tool_timeout_secs', v === '' ? 300 : Number(v)); + }} className={inputCls} min={0} step={30}