fix(install): Codex cleanup drops hand-wired twins; pip3 fallback explains an out-of-range Python

Two more things the same incident showed. The user's ~/.codex/config.toml
held a second table, `treg-to_mcp`, pointing at our MCP url with the token
in the env-var-NAME field; the installer now removes any `mcp_servers.*`
table whose url is ours before writing the one correct table, so Codex is
not left showing a dead server beside the working one.

install.sh's pip3 fallback ran on a machine whose pip3 belonged to a Python
outside the supported range and died on pip's "Ignored the following
versions" wall. pip cannot pick another interpreter, so the script now says
which Python pip3 belongs to and prints the uv one-liner instead.

Fragment: docs/context/interface/landing-sandbox.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jason Zhou
2026-09-19 15:58:13 +10:00
co-authored by Claude Fable 5.1
parent 0727d1d32b
commit e6b4e219be
4 changed files with 28 additions and 4 deletions
+4 -1
View File
@@ -144,7 +144,10 @@ files a skill folder is — `SKILL.md` (agent recipe: call the treg proxy, key i
supported interpreter range (`PYREQ`, kept in sync with `requires-python` in `pyproject.toml`): without
it uv resolves against its *default* interpreter — its own managed Python first — and a machine whose
default falls outside the range fails resolution instead of picking (or auto-downloading) a compatible
one. It also installs the official
one. The pip3 path cannot pick an interpreter at all, so it checks `pip3 --version` against the range
first and, outside it, prints which Python pip3 belongs to plus the uv one-liner instead of letting
pip fail with its "Ignored the following versions" wall (a real user hit exactly that). It also
installs the official
**tools-registry skill** into every detected agent via `treg skill bootstrap` (Claude Code, Cursor, Codex,
Gemini, Copilot, OpenCode, Windsurf …), falling back on older CLIs to a Claude-only drop that curls
`{BASE}/skill.md` into `~/.claude/skills/treg`. Because the package is public on PyPI,
+13 -3
View File
@@ -155,12 +155,22 @@ def _write_toml_agent(meta: dict, name: str, url: str, token: str) -> tuple[str,
old = path.read_text() if path.exists() else ""
tomllib.loads(old) # a config Codex can't read is not ours to edit
header = f"[mcp_servers.{name}]"
kept = re.sub(rf"(?ms)^\[mcp_servers\.{re.escape(name)}\]\n.*?(?=^\[|\Z)", "", old).rstrip()
# Cut our table, and any other table pointing at this same MCP url under another name — an
# agent wiring Codex by hand tends to leave a `treg-to_mcp` twin with the token in the wrong
# field, which Codex then shows as a dead server beside the working one.
stale = {name} | {k for k, v in tomllib.loads(old).get("mcp_servers", {}).items()
if isinstance(v, dict) and v.get("url") == url}
kept = old
for k in stale:
kept = re.sub(rf"(?ms)^\[mcp_servers\.{re.escape(k)}\]\n.*?(?=^\[|\Z)", "", kept)
kept = kept.rstrip()
block = (f"{header}\nurl = {json.dumps(url)}\n"
f"http_headers = {{ \"Authorization\" = {json.dumps('Bearer ' + token)} }}\n")
new = (kept + "\n\n" if kept else "") + block
entry = tomllib.loads(new)["mcp_servers"][name]
if entry != {"url": url, "http_headers": {"Authorization": f"Bearer {token}"}}:
servers = tomllib.loads(new)["mcp_servers"]
if (servers[name] != {"url": url, "http_headers": {"Authorization": f"Bearer {token}"}}
or any(k != name and isinstance(v, dict) and v.get("url") == url
for k, v in servers.items())):
return "error", f"{path}: a sub-table of {header} survived; edit it by hand"
_write_private(path, new)
return "ok", str(path)
+8
View File
@@ -52,6 +52,14 @@ elif command -v pipx >/dev/null 2>&1; then
pipx install --force "$SRC"
fi
elif command -v pip3 >/dev/null 2>&1; then
# pip can only install into the interpreter it belongs to. Outside PYREQ it prints a wall of
# "Ignored the following versions" and dies — say what is wrong and how to fix it instead.
if ! pip3 --version 2>/dev/null | grep -qE '\(python 3\.1[23]\)'; then
echo "pip3 belongs to $(pip3 --version 2>/dev/null | sed -n 's/.*(python \([0-9.]*\)).*/Python \1/p'), but treg needs Python 3.12 or 3.13." >&2
echo "Easiest fix — install uv (it fetches a matching Python by itself), then rerun this command:" >&2
echo " curl -LsSf https://astral.sh/uv/install.sh | sh" >&2
exit 1
fi
pip3 install --user --upgrade "$SRC"
else
echo "Need Python 3.12 or 3.13 and one of: uv (recommended), pipx, or pip3." >&2
+3
View File
@@ -403,6 +403,8 @@ def test_codex_replaces_a_stale_env_var_table_and_keeps_the_rest(tmp_path, monke
import tomllib
before = ('model = "gpt-5"\n\n[mcp_servers.figma]\nurl = "https://mcp.figma.com/mcp"\n\n'
'[mcp_servers.treg]\nurl = "https://treg.to/mcp/"\nbearer_token_env_var = "TREG_TOKEN"\n\n'
# a hand-wired twin under another name, token in the env-var-NAME field (seen in the wild)
'[mcp_servers.treg-to_mcp]\nurl = "https://treg.to/mcp/"\nbearer_token_env_var = "eyJ.x"\n\n'
'[mcp_servers.node_repl]\ncommand = "node_repl"\n\n[mcp_servers.node_repl.env]\nX = "1"\n')
out, cfg = _codex_install(tmp_path, monkeypatch, existing=before, token="NEWKEY")
assert out["results"][0][1] == "ok", out
@@ -413,6 +415,7 @@ def test_codex_replaces_a_stale_env_var_table_and_keeps_the_rest(tmp_path, monke
assert data["mcp_servers"]["treg"] == {
"url": "https://treg.to/mcp/", "http_headers": {"Authorization": "Bearer NEWKEY"}}
assert cfg.read_text().count("[mcp_servers.treg]") == 1
assert "treg-to_mcp" not in cfg.read_text() and "eyJ.x" not in cfg.read_text()
# idempotent
mcp_install.install_mcp(base_url="https://treg.to", token="NEWKEY", only=["codex"])
assert cfg.read_text().count("[mcp_servers.treg]") == 1