diff --git a/CHANGELOG.md b/CHANGELOG.md index 619214d..cbd4db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ Versioning and uses Git tags as its release channel. ## [Unreleased] +## [0.1.1] - 2026-09-14 + +- Fixed `doctor` incorrectly treating a normal Codex project trust entry as a + recursive MCP server registration. + ## [0.1.0] - 2026-09-14 - Added the `codex` and `codex-reply` MCP tools over stdio. @@ -14,5 +19,6 @@ Versioning and uses Git tags as its release channel. - Added structured results with a text fallback for MCP clients including OpenCode. - Validated the pinned SDK against fake App Server contract tests and live Codex runtime tests. -[Unreleased]: https://github.com/PyYoshi/codex-app-mcp/compare/v0.1.0...HEAD +[Unreleased]: https://github.com/PyYoshi/codex-app-mcp/compare/v0.1.1...HEAD +[0.1.1]: https://github.com/PyYoshi/codex-app-mcp/compare/v0.1.0...v0.1.1 [0.1.0]: https://github.com/PyYoshi/codex-app-mcp/releases/tag/v0.1.0 diff --git a/README.ja.md b/README.ja.md index b30f388..0708785 100644 --- a/README.ja.md +++ b/README.ja.md @@ -38,9 +38,9 @@ Codex 連携が提供されていないツールから、Codex を協働エー ```sh cd /absolute/path/to/target-project -uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.0 \ +uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.1 \ codex-app-mcp init -uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.0 \ +uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.1 \ codex-app-mcp doctor ``` @@ -150,7 +150,7 @@ claude mcp add codex -- \ 注意: -- PyPIには公開しない。`uvx --from git+https://...@v0.1.0`でGitHubのtagを固定する。 +- PyPIには公開しない。`uvx --from git+https://...@v0.1.1`でGitHubのtagを固定する。 - 自動探索はbridge起動directoryから親へ向かう。固定したい場合は従来どおり `serve --config /absolute/path/to/bridge.toml`を指定できる。 - Codex 側(`~/.codex/config.toml` の `mcp_servers`)にこの bridge を登録すると再帰的自己接続になり得る。bridge は子 runtime 環境の `CODEX_APP_MCP_CHILD=1` で起動拒否するが、その構成自体を推奨しない。 diff --git a/README.md b/README.md index d81cc83..3b3c762 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ Create a safe project-local configuration from the target repository: ```sh cd /absolute/path/to/target-project -uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.0 \ +uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.1 \ codex-app-mcp init -uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.0 \ +uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.1 \ codex-app-mcp doctor ``` @@ -72,13 +72,13 @@ the client should launch the bridge in its workspace and leave it unset. See ## MCP client configuration Use an executable plus an argument array, not a shell command string. Before the -`v0.1.0` tag exists, replace it with a commit SHA. +`v0.1.1` tag exists, replace it with a commit SHA. ### Claude Code ```sh claude mcp add codex -- \ - uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.0 \ + uvx --from git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.1 \ codex-app-mcp serve ``` @@ -91,7 +91,7 @@ claude mcp add codex -- \ "type": "local", "command": [ "uvx", "--from", - "git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.0", + "git+https://github.com/PyYoshi/codex-app-mcp.git@v0.1.1", "codex-app-mcp", "serve" ], "enabled": true diff --git a/pyproject.toml b/pyproject.toml index 47d5ef0..9ea44e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "codex-app-mcp" -version = "0.1.0" +version = "0.1.1" description = "Local MCP bridge exposing Codex App Server threads/turns as the codex / codex-reply MCP tools (noninteractive compatibility subset)" readme = "README.md" requires-python = ">=3.14,<3.15" diff --git a/src/codex_app_mcp/__init__.py b/src/codex_app_mcp/__init__.py index e2e34be..bea18df 100644 --- a/src/codex_app_mcp/__init__.py +++ b/src/codex_app_mcp/__init__.py @@ -5,7 +5,7 @@ compatibility subset; see ``docs/`` for the specification. """ -__version__ = "0.1.0" +__version__ = "0.1.1" CHILD_ENV_FLAG = "CODEX_APP_MCP_CHILD" """Set in the child runtime environment to break recursive self-connections.""" diff --git a/src/codex_app_mcp/doctor.py b/src/codex_app_mcp/doctor.py index b25c0cf..dcfd25a 100644 --- a/src/codex_app_mcp/doctor.py +++ b/src/codex_app_mcp/doctor.py @@ -12,6 +12,7 @@ import os import re import sys +import tomllib from pathlib import Path from typing import Any @@ -36,6 +37,23 @@ def _check(ok: bool, detail: str, *, level_on_fail: str = FAIL) -> dict[str, str return {"status": _OK if ok else level_on_fail, "detail": detail} +def _contains_bridge_reference(value: object) -> bool: + if isinstance(value, str): + return re.search(r"codex[-_]app[-_]mcp", value) is not None + if isinstance(value, dict): + return any(_contains_bridge_reference(item) for item in value.values()) + if isinstance(value, list): + return any(_contains_bridge_reference(item) for item in value) + return False + + +def _codex_mcp_references_bridge(config_toml: Path) -> bool: + """Inspect MCP server definitions without matching unrelated project paths.""" + with config_toml.open("rb") as stream: + config = tomllib.load(stream) + return _contains_bridge_reference(config.get("mcp_servers", {})) + + async def run_doctor( *, config_path: str | None = None, skip_runtime: bool = False ) -> dict[str, Any]: @@ -124,8 +142,7 @@ async def run_doctor( config_toml = codex_home / "config.toml" if config_toml.is_file(): try: - text = config_toml.read_text(encoding="utf-8", errors="replace") - if re.search(r"codex[-_]app[-_]mcp", text): + if _codex_mcp_references_bridge(config_toml): checks.append( _check( False, @@ -136,7 +153,7 @@ async def run_doctor( ) else: checks.append({"status": _OK, "detail": "no self-reference in Codex config"}) - except OSError as exc: + except (OSError, tomllib.TOMLDecodeError) as exc: checks.append(_check(False, f"cannot read {config_toml}: {exc}", level_on_fail=WARN)) # --- Authentication presence (contents never displayed) -------------------- diff --git a/tests/unit/test_config_policy.py b/tests/unit/test_config_policy.py index faa7dba..f0bc8bb 100644 --- a/tests/unit/test_config_policy.py +++ b/tests/unit/test_config_policy.py @@ -15,7 +15,7 @@ load_bridge_config, user_config_path, ) -from codex_app_mcp.doctor import _is_supported_python +from codex_app_mcp.doctor import _codex_mcp_references_bridge, _is_supported_python from codex_app_mcp.errors import ( CONFIG_KEY_DENIED, MODEL_NOT_ALLOWED, @@ -138,6 +138,23 @@ def test_config_example_file_loads(): assert config.limits.max_active_turns == 1 +def test_doctor_self_reference_check_only_inspects_mcp_servers(tmp_path): + config = tmp_path / "config.toml" + config.write_text( + '[projects."/src/codex-app-mcp"]\ntrust_level = "trusted"\n', + encoding="utf-8", + ) + assert not _codex_mcp_references_bridge(config) + + config.write_text( + '[mcp_servers.codex]\ncommand = "uvx"\n' + 'args = ["--from", "git+https://example.test/codex-app-mcp", ' + '"codex-app-mcp", "serve"]\n', + encoding="utf-8", + ) + assert _codex_mcp_references_bridge(config) + + def test_config_discovery_prefers_explicit_then_nearest_then_user(tmp_path): home = tmp_path / "home" xdg = home / "xdg" diff --git a/uv.lock b/uv.lock index 883b14c..6af567a 100644 --- a/uv.lock +++ b/uv.lock @@ -79,7 +79,7 @@ wheels = [ [[package]] name = "codex-app-mcp" -version = "0.1.0" +version = "0.1.1" source = { editable = "." } dependencies = [ { name = "mcp" },