Files
codex-app-server/cloud/tests/test_websearch_mcp_env.py
T
host 71e31b22a0 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 项全过。
2026-10-30 01:16:49 +08:00

72 lines
2.6 KiB
Python

"""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()