websearch: MCP env 写进租户 config,不只靠 docker -e
2026-10-30 实测:Codex bash 里有 SEARXNG_URL + proxy, MCP 链却只有 duckduckgo、DNS 瞬间失败——Codex 的 MCP 子进程不继承 Codex 进程 env;给租户 config 加 [mcp_servers.websearch.env] 后同一任务立刻返回真实 SearXNG 结果。修法:worker 在 _docker_task_context 算出网关后、容器启动前,用 ensure_websearch_mcp_env 把 proxy + 搜索 env 写进租户 config(网关此时才已知, 播种时写不死;写失败只记日志不阻塞)。 测试:新增 test_websearch_mcp_env 4 项,相关共 33 项全过。
This commit is contained in:
1 parent
08d56b15ce
commit
71e31b22a0
3 files changed
+164
No files matched your search
@@ -0,0 +1,71 @@
|
||||
"""ensure_websearch_mcp_env: MCP env lives in config.toml, because
|
||||
Codex's MCP subprocess does not inherit the Codex process env
|
||||
(2026-10-30: Codex bash had SEARXNG_URL + proxy, MCP chain was
|
||||
still ["duckduckgo"] only; adding the config env section made
|
||||
the same task return real SearXNG results)."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
from cloud.websearch import ensure_websearch_mcp_env # noqa: E402
|
||||
|
||||
|
||||
BASE = ("model = \"gpt-6.1-sol\"\n\n"
|
||||
"[mcp_servers.websearch]\n"
|
||||
"command = \"python3\"\n"
|
||||
"args = [\"/var/lib/codex-app-server/codex-home/"
|
||||
"websearch_mcp.py\"]\n")
|
||||
|
||||
|
||||
class TestMcpEnv(unittest.TestCase):
|
||||
def _home(self, text):
|
||||
tmp = tempfile.mkdtemp()
|
||||
with open(os.path.join(tmp, "config.toml"), "w") as fh:
|
||||
fh.write(text)
|
||||
return tmp
|
||||
|
||||
def _read(self, home):
|
||||
with open(os.path.join(home, "config.toml")) as fh:
|
||||
return fh.read()
|
||||
|
||||
def test_insert_env_after_websearch_section(self):
|
||||
home = self._home(BASE)
|
||||
self.assertTrue(ensure_websearch_mcp_env(
|
||||
home, {"SEARXNG_URL": "http://searx.test",
|
||||
"http_proxy": "http://10.0.0.1:8888"}))
|
||||
text = self._read(home)
|
||||
self.assertIn("[mcp_servers.websearch.env]", text)
|
||||
self.assertIn('SEARXNG_URL = "http://searx.test"', text)
|
||||
self.assertIn('http_proxy = "http://10.0.0.1:8888"', text)
|
||||
self.assertIn('command = "python3"', text)
|
||||
|
||||
def test_replace_existing_env_preserves_other_sections(self):
|
||||
home = self._home(BASE + "[mcp_servers.websearch.env]\n"
|
||||
"SEARXNG_URL = \"http://old.test\"\n\n"
|
||||
"[other]\nfoo = 1\n")
|
||||
self.assertTrue(ensure_websearch_mcp_env(
|
||||
home, {"SEARXNG_URL": "http://new.test"}))
|
||||
text = self._read(home)
|
||||
self.assertIn('SEARXNG_URL = "http://new.test"', text)
|
||||
self.assertNotIn("old.test", text)
|
||||
self.assertIn("[other]\nfoo = 1", text)
|
||||
|
||||
def test_empty_env_writes_nothing(self):
|
||||
home = self._home(BASE)
|
||||
self.assertFalse(ensure_websearch_mcp_env(home, {}))
|
||||
self.assertEqual(self._read(home), BASE)
|
||||
|
||||
def test_idempotent_second_write(self):
|
||||
home = self._home(BASE)
|
||||
env = {"SEARXNG_URL": "http://searx.test"}
|
||||
self.assertTrue(ensure_websearch_mcp_env(home, env))
|
||||
self.assertFalse(ensure_websearch_mcp_env(home, env))
|
||||
self.assertEqual(self._read(home).count(
|
||||
"[mcp_servers.websearch.env]"), 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user