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
@@ -647,3 +647,79 @@ def ensure_codex_home_websearch(codex_home_dir, command_args=None,
|
||||
"config_changed": config_changed,
|
||||
"script_changed": script_changed,
|
||||
"script_differs": script_differs}
|
||||
|
||||
|
||||
def ensure_websearch_mcp_env(codex_home_dir, env):
|
||||
"""Write [mcp_servers.websearch.env] into a tenant config.toml.
|
||||
|
||||
Why this exists (measured 2026-10-30, not guessed): Codex's
|
||||
MCP server subprocess does NOT inherit the Codex process env.
|
||||
A Docker task whose Codex bash env contained SEARXNG_URL and
|
||||
http_proxy still ran web_search with chain ["duckduckgo"]
|
||||
only, failing instantly with "Temporary failure in name
|
||||
resolution" -- SEARXNG_URL never reached the MCP process, and
|
||||
without a proxy the container has no direct DNS. Adding an
|
||||
explicit ``env`` table to the MCP config made the same task
|
||||
return real SearXNG results. Container env (docker -e) is
|
||||
therefore NOT sufficient for MCP tools; the env must live in
|
||||
config.toml, where Codex actually reads it for MCP servers.
|
||||
|
||||
Idempotent: an existing env subsection is replaced in place,
|
||||
other sections are left untouched, and a missing websearch
|
||||
section is created first (command/args only, no env values
|
||||
invented). Values are written as TOML basic strings via
|
||||
json.dumps (safe for URLs and proxy URLs). Returns True when
|
||||
the file was written, False when env is empty or the file
|
||||
could not be written (caller logs, never crashes the task).
|
||||
"""
|
||||
if not env:
|
||||
return False
|
||||
home = os.path.abspath(codex_home_dir)
|
||||
config_path = os.path.join(home, "config.toml")
|
||||
try:
|
||||
existing = ""
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, encoding="utf-8") as fh:
|
||||
existing = fh.read()
|
||||
if "[mcp_servers.websearch]" not in existing:
|
||||
existing += ("\n[mcp_servers.websearch]\n"
|
||||
"command = \"python3\"\n"
|
||||
"args = [\"/var/lib/codex-app-server/"
|
||||
"codex-home/websearch_mcp.py\"]\n")
|
||||
env_lines = ["[mcp_servers.websearch.env]"]
|
||||
for key in sorted(env):
|
||||
env_lines.append("%s = %s" % (key, json.dumps(env[key])))
|
||||
env_block = "\n".join(env_lines) + "\n"
|
||||
lines = existing.splitlines(keepends=True)
|
||||
start = end = None
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip() == "[mcp_servers.websearch.env]":
|
||||
start = i
|
||||
elif start is not None and line.startswith("["):
|
||||
end = i
|
||||
break
|
||||
if start is not None:
|
||||
if end is None:
|
||||
end = len(lines)
|
||||
lines[start:end] = [env_block]
|
||||
new_text = "".join(lines)
|
||||
else:
|
||||
insert_at = None
|
||||
in_section = False
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip() == "[mcp_servers.websearch]":
|
||||
in_section = True
|
||||
elif in_section and line.startswith("["):
|
||||
insert_at = i
|
||||
break
|
||||
if insert_at is None:
|
||||
insert_at = len(lines)
|
||||
lines.insert(insert_at, env_block)
|
||||
new_text = "".join(lines)
|
||||
if new_text == existing:
|
||||
return False
|
||||
with open(config_path, "w", encoding="utf-8") as fh:
|
||||
fh.write(new_text)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
Reference in new issue
Block a user