|
|
@@ -474,3 +474,49 @@ class TestResumeStallGiveup(unittest.TestCase):
|
|
|
p._call_resume([{"role": "user", "content": "x"}])
|
|
|
self.assertIsNone(p._session_id, "连卡2次后应清会话换新")
|
|
|
self.assertEqual(p._resume_stall_streak, 0, "熔断后计数归零")
|
|
|
+
|
|
|
+
|
|
|
+class TestMcpIsolation(unittest.TestCase):
|
|
|
+ """claude-code 子进程必须隔离用户 MCP(--strict-mcp-config),否则模型只看到
|
|
|
+ Gmail/Calendar 等 MCP 工具、判定'没有工具写不了文件'而拒绝(run_f2a1eca9c6ca)。"""
|
|
|
+
|
|
|
+ def _capture_cmd(self, session_id):
|
|
|
+ from lambdagent.providers import claude_code_provider as ccp
|
|
|
+ from lambdagent.providers.base import ProviderConfig
|
|
|
+
|
|
|
+ class FakeResult:
|
|
|
+ returncode = 0
|
|
|
+ stdout = '{"result":"ok"}' if session_id is None else "ok"
|
|
|
+ stderr = ""
|
|
|
+
|
|
|
+ captured = {}
|
|
|
+
|
|
|
+ def fake_run(cmd, **kw):
|
|
|
+ captured["cmd"] = cmd
|
|
|
+ return FakeResult()
|
|
|
+
|
|
|
+ p = ccp.ClaudeCodeProvider.__new__(ccp.ClaudeCodeProvider)
|
|
|
+ p.config = ProviderConfig(model="sonnet", timeout=600, extra={})
|
|
|
+ p.claude_bin = "claude"
|
|
|
+ p._session_id = session_id
|
|
|
+ p._resume_count = 0
|
|
|
+ p._resume_stall_streak = 0
|
|
|
+ msgs = [{"role": "user", "content": "hi"}]
|
|
|
+ with patch.object(ccp, "_run_with_idle_timeout", side_effect=fake_run):
|
|
|
+ if session_id is None:
|
|
|
+ p._call_new(msgs)
|
|
|
+ else:
|
|
|
+ p._call_resume(msgs)
|
|
|
+ return captured["cmd"]
|
|
|
+
|
|
|
+ def test_first_turn_isolates_mcp_and_native_tools(self):
|
|
|
+ cmd = self._capture_cmd(session_id=None)
|
|
|
+ self.assertIn("--strict-mcp-config", cmd)
|
|
|
+ self.assertIn("--tools", cmd)
|
|
|
+ # --tools 后面跟空串(禁原生工具)
|
|
|
+ self.assertEqual(cmd[cmd.index("--tools") + 1], "")
|
|
|
+
|
|
|
+ def test_resume_isolates_mcp_and_native_tools(self):
|
|
|
+ cmd = self._capture_cmd(session_id="sess-1")
|
|
|
+ self.assertIn("--strict-mcp-config", cmd)
|
|
|
+ self.assertIn("--tools", cmd)
|