Codex Plugin MCP UX Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make Codex Qiongli installs report plugin-bundled MCP accurately, avoid false strategy_only literature-review downgrades, and keep standalone Codex MCP config as an explicit recovery path.
Architecture: Keep Codex plugin installs self-contained: .codex-plugin/plugin.json points at plugin-local .mcp.json, while ~/.codex/config.toml remains a user-requested standalone fallback. Extend install discovery to distinguish effective MCP, plugin-bundled MCP, and standalone MCP. Update Qiongli workflow guidance so Codex checks qiongli_literature_status before declaring that provider-connected literature MCP is unavailable.
Tech Stack: Python 3.12, unittest, Codex local plugin manifests, Qiongli universal installer, Markdown skill contracts.
Task 1: Report Codex Plugin-Bundled MCP In qiongli check
Files:
Modify:
packages/python-qiongli/src/qiongli/install_discovery.pyModify:
tests/test_cli.py[ ] Step 1: Write the failing Codex plugin MCP JSON test
Extend tests/test_cli.py::CLITests.test_check_json_reports_codex_plugin_surface so the fixture writes a plugin-local .mcp.json next to the Codex plugin manifest:
(plugin_root / ".mcp.json").write_text(
json.dumps(
{
"mcpServers": {
"qiongli": {
"command": "qiongli",
"args": ["mcp", "serve", "--transport", "stdio"],
}
}
}
),
encoding="utf-8",
)Add these assertions after the existing plugin assertions:
self.assertTrue(codex["mcp"]["installed"])
self.assertEqual(codex["mcp"]["source"], "plugin")
self.assertEqual(codex["mcp"]["path"], str(plugin_root / ".mcp.json"))
self.assertEqual(codex["mcp"]["server"], "qiongli")
self.assertFalse(codex["standalone_mcp"]["installed"])
self.assertEqual(codex["standalone_mcp"]["source"], "standalone")
self.assertEqual(codex["standalone_mcp"]["path"], str(root / "codex-home" / "config.toml"))
self.assertTrue(codex["plugin_mcp"]["installed"])
self.assertEqual(codex["plugin_mcp"]["source"], "plugin")Update the mocked environment in this test to set CODEX_HOME so the standalone path is deterministic:
_isolated_qiongli_env(
root,
CODEX_HOME=str(root / "codex-home"),
QIONGLI_CODEX_MARKETPLACE_PATH=str(marketplace),
QIONGLI_CLAUDE_PLUGIN_PARENT=str(root / "claude-plugins"),
CLAUDE_CODE_CONFIG_PATH=str(root / "claude.json"),
ANTIGRAVITY_CONFIG_PATH=str(root / "antigravity-settings.json"),
HERMES_CONFIG_PATH=str(root / "hermes-settings.json"),
)- [ ] Step 2: Run the targeted test and verify it fails
Run:
python3 -m unittest tests.test_cli.CLITests.test_check_json_reports_codex_plugin_surface -vExpected: FAIL because codex["mcp"]["installed"] is still False and plugin_mcp / standalone_mcp are not present.
- [ ] Step 3: Add plugin and standalone MCP status helpers
In packages/python-qiongli/src/qiongli/install_discovery.py, add these helpers near _codex_mcp_status:
def _empty_mcp_status(path: Path, *, source: str) -> dict[str, object]:
return {
"installed": False,
"managed": False,
"path": str(path),
"server": "",
"source": source,
}
def _codex_plugin_mcp_status(plugin: dict[str, object]) -> dict[str, object]:
if not plugin.get("installed"):
path_text = str(plugin.get("path") or "")
plugin_root = Path(path_text) if path_text else resolve_codex_plugin_paths().plugin_root
return _empty_mcp_status(plugin_root / ".mcp.json", source="plugin")
plugin_root = Path(str(plugin["path"]))
manifest = _read_json_object(plugin_root / ".codex-plugin" / "plugin.json")
mcp_ref = manifest.get("mcpServers")
if not isinstance(mcp_ref, str) or not mcp_ref.strip():
return _empty_mcp_status(plugin_root / ".mcp.json", source="plugin")
mcp_path = (plugin_root / mcp_ref).resolve(strict=False)
status = _json_plugin_mcp_status(mcp_path)
return {
**status,
"source": "plugin",
}
def _json_plugin_mcp_status(path: Path) -> dict[str, object]:
config = _read_json_object(path)
mcp_servers = config.get("mcpServers")
server_name = ""
managed = False
if isinstance(mcp_servers, dict):
for candidate in (PLUGIN_ID, "qiongli-next"):
if isinstance(mcp_servers.get(candidate), dict):
server_name = candidate
managed = True
break
return {
"installed": bool(server_name),
"managed": managed,
"path": str(path),
"server": server_name,
}This helper intentionally accepts plugin-bundled Node MCP servers and CLI-backed MCP servers. Plugin MCP validity is determined by the plugin manifest reference plus a Qiongli server entry, not by requiring command == "qiongli".
- [ ] Step 4: Rename the existing Codex config check to standalone
Replace _codex_mcp_status with _codex_standalone_mcp_status:
def _codex_standalone_mcp_status() -> dict[str, object]:
path = default_codex_config_path()
text = _read_text(path)
installed = BEGIN_MARKER in text or (
"[mcp_servers.qiongli]" in text and 'command = "qiongli"' in text and "mcp" in text
)
return {
"installed": installed,
"managed": BEGIN_MARKER in text,
"path": str(path),
"server": "qiongli" if installed else "",
"source": "standalone",
}- [ ] Step 5: Combine effective Codex MCP status without changing install ownership
In discover_install_surfaces, compute Codex plugin and MCP state before building the Codex entry:
codex_plugin = _codex_plugin_status(check_activation=check_activation)
codex_plugin_mcp = _codex_plugin_mcp_status(codex_plugin)
codex_standalone_mcp = _codex_standalone_mcp_status()
codex_effective_mcp = codex_plugin_mcp if codex_plugin_mcp["installed"] else codex_standalone_mcpThen pass both extra diagnostics into _combine_surface:
"codex": _combine_surface(
client="codex",
plugin=codex_plugin,
skill=_skill_status(skill_dirs["codex"]),
mcp=codex_effective_mcp,
extra={
"plugin_mcp": codex_plugin_mcp,
"standalone_mcp": codex_standalone_mcp,
},
),Change _combine_surface to accept and merge extra diagnostics:
def _combine_surface(
*,
client: str,
plugin: dict[str, object],
skill: dict[str, object],
mcp: dict[str, object],
extra: dict[str, object] | None = None,
) -> dict[str, object]:
...
result = {
"client": client,
"surface": surface,
"installed": surface != "none",
"path": selected_path,
"version": version,
"subject": subject,
"coverage": coverage,
"plugin": plugin,
"skill": skill,
"mcp": mcp,
}
if extra:
result.update(extra)
return resultKeep Claude, Antigravity, and Hermes calls using the default extra=None.
- [ ] Step 6: Run the targeted test and verify it passes
Run:
python3 -m unittest tests.test_cli.CLITests.test_check_json_reports_codex_plugin_surface -vExpected: PASS.
Task 2: Preserve Standalone MCP As Explicit Codex Fallback
Files:
Modify:
tests/test_cli.pyModify:
packages/python-qiongli/src/qiongli/install_discovery.pyConfirm unchanged:
tests/test_universal_installer.py[ ] Step 1: Write a standalone Codex MCP check test
Add this test to tests/test_cli.py:
def test_check_json_reports_codex_standalone_mcp_when_no_plugin_mcp(self) -> None:
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
codex_home = root / "codex-home"
config_path = codex_home / "config.toml"
config_path.parent.mkdir(parents=True)
config_path.write_text(
"\n".join(
[
"# >>> qiongli managed mcp >>>",
"[mcp_servers.qiongli]",
'command = "qiongli"',
'args = ["mcp", "serve", "--transport", "stdio"]',
"# <<< qiongli managed mcp <<<",
"",
]
),
encoding="utf-8",
)
args = argparse.Namespace(repo="", json=True, strict_network=False, beta=False, offline=True)
stdout = io.StringIO()
with mock.patch.object(cli_module, "_find_repo_root", return_value=None), mock.patch.object(
cli_module, "_check_system_env", return_value={}
), mock.patch.object(
cli_module.os,
"environ",
_isolated_qiongli_env(root, CODEX_HOME=str(codex_home)),
), contextlib.redirect_stdout(stdout):
exit_code = cli_module.cmd_check(args)
self.assertEqual(exit_code, 0)
payload = json.loads(stdout.getvalue())
codex = payload["installed"]["codex"]
self.assertEqual(codex["surface"], "mcp")
self.assertTrue(codex["mcp"]["installed"])
self.assertEqual(codex["mcp"]["source"], "standalone")
self.assertFalse(codex["plugin_mcp"]["installed"])
self.assertTrue(codex["standalone_mcp"]["installed"])- [ ] Step 2: Run the standalone test and verify it fails before implementation
Run:
python3 -m unittest tests.test_cli.CLITests.test_check_json_reports_codex_standalone_mcp_when_no_plugin_mcp -vExpected: FAIL until _codex_standalone_mcp_status and plugin_mcp / standalone_mcp diagnostics exist.
- [ ] Step 3: Run installer ownership tests without changing installer behavior
Run:
python3 -m unittest \
tests.test_universal_installer.UniversalInstallerTests.test_plugin_surface_installs_codex_plugin_without_global_skill_or_codex_mcp_config \
tests.test_universal_installer.UniversalInstallerTests.test_mcp_part_only_registers_codex_mcp_without_global_skill \
-vExpected: PASS. The plugin surface still does not create CODEX_HOME/config.toml, while --parts mcp still creates standalone config.
Task 3: Make CLI Text Output Explain Effective MCP Source
Files:
Modify:
packages/python-qiongli/src/qiongli/cli.pyModify:
tests/test_cli.py[ ] Step 1: Write a text-summary test for Codex plugin MCP
Add this focused test to tests/test_cli.py:
def test_doctor_summary_reports_codex_plugin_mcp_source(self) -> None:
args = argparse.Namespace(cwd=".")
completed = mock.Mock(returncode=0, stdout="doctor ok\n")
installed = {
client: {
"installed": False,
"surface": "none",
"version": None,
"path": f"/tmp/{client}/qiongli-workflow",
"mcp": {"installed": False, "source": "standalone", "path": "", "server": ""},
}
for client in ("codex", "claude", "antigravity", "hermes")
}
installed["codex"] = {
"installed": True,
"surface": "plugin",
"version": "v9.9.9",
"path": "/tmp/plugins/qiongli",
"mcp": {
"installed": True,
"source": "plugin",
"path": "/tmp/plugins/qiongli/.mcp.json",
"server": "qiongli",
},
}
stdout = io.StringIO()
with mock.patch.object(cli_module.subprocess, "run", return_value=completed), mock.patch.object(
cli_module, "discover_install_surfaces", return_value=installed
), contextlib.redirect_stdout(stdout):
exit_code = cli_module.cmd_doctor(args)
self.assertEqual(exit_code, 0)
output = stdout.getvalue()
self.assertIn("- codex: installed, surface=plugin", output)
self.assertIn("mcp=plugin:qiongli", output)- [ ] Step 2: Run the text-summary test and verify it fails
Run:
python3 -m unittest tests.test_cli.CLITests.test_doctor_summary_reports_codex_plugin_mcp_source -vExpected: FAIL because _print_client_integration_summary does not include MCP source.
- [ ] Step 3: Add compact MCP summary formatting
In packages/python-qiongli/src/qiongli/cli.py, add:
def _client_mcp_summary(item: dict[str, object]) -> str:
mcp = item.get("mcp")
if not isinstance(mcp, dict) or not mcp.get("installed"):
return "mcp=none"
source = str(mcp.get("source") or "standalone")
server = str(mcp.get("server") or "qiongli")
return f"mcp={source}:{server}"Update _print_client_integration_summary:
print(
f"- {client}: {status}, surface={item['surface']}, "
f"version={version}, {_client_mcp_summary(item)}, path={item['path']}"
)- [ ] Step 4: Run CLI tests
Run:
python3 -m unittest tests.test_cli -vExpected: PASS.
Task 4: Require Literature MCP Capability Preflight Before strategy_only
Files:
Modify:
content/workflow/SKILL.mdModify:
tests/test_mcp_provider_docs.pyModify if generated sync requires it:
packages/python-qiongli/src/qiongli/subject_materializer.py[ ] Step 1: Add a contract test for Codex provider preflight guidance
Add this test to tests/test_mcp_provider_docs.py:
def test_qiongli_workflow_requires_codex_literature_status_preflight(self) -> None:
content = (REPO_ROOT / "content" / "workflow" / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("qiongli_literature_status", content)
self.assertIn("Codex", content)
self.assertIn("before declaring `strategy_only`", content)
self.assertIn("provider_connected", content)- [ ] Step 2: Run the contract test and verify it fails
Run:
python3 -m unittest tests.test_mcp_provider_docs.MCPProviderDocsTests.test_qiongli_workflow_requires_codex_literature_status_preflight -vExpected: FAIL because content/workflow/SKILL.md does not yet require Codex to call qiongli_literature_status before downgrading.
- [ ] Step 3: Update the Qiongli workflow contract
In content/workflow/SKILL.md, under ## Literature Provider Configuration, add this bullet immediately after the provider configuration bullets:
- In Codex plugin sessions, before declaring `strategy_only` for literature search, literature review, paper screening, citation snowballing, or evidence synthesis, attempt the visible `qiongli_literature_status` MCP tool. If it returns `capability_mode: provider_connected`, proceed with provider-backed literature workflow. If the tool is not visible in the session, state that Qiongli MCP tools are not visible and recommend restarting Codex or installing the explicit standalone MCP fallback with `qiongli install --target codex --parts mcp`. Only use `strategy_only` after this preflight is unavailable or returns a non-provider-connected mode.- [ ] Step 4: Run the contract test and generated skill tests
Run:
python3 -m unittest tests.test_mcp_provider_docs tests.test_subject_materializer -vExpected: PASS. If a subject materializer assertion checks exact skill text, update that assertion to include the new Codex preflight sentence.
Task 5: Document The Plugin-First MCP Model
Files:
Modify:
docs/reference/cli.mdModify:
docs/zh/reference/cli.mdModify:
docs/guide/install.mdModify:
docs/zh/guide/install.mdModify:
docs/guide/troubleshooting.mdModify:
docs/zh/guide/troubleshooting.mdModify:
tests/test_cli_setup_docs.pyModify:
tests/test_mcp_provider_docs.py[ ] Step 1: Add English docs assertions
Add assertions to tests/test_cli_setup_docs.py:
def test_codex_plugin_install_documents_plugin_bundled_mcp(self) -> None:
install = (REPO_ROOT / "docs" / "guide" / "install.md").read_text(encoding="utf-8")
cli = (REPO_ROOT / "docs" / "reference" / "cli.md").read_text(encoding="utf-8")
troubleshooting = (REPO_ROOT / "docs" / "guide" / "troubleshooting.md").read_text(encoding="utf-8")
self.assertIn("plugin-bundled MCP", install)
self.assertIn("does not write `~/.codex/config.toml`", install)
self.assertIn("standalone MCP fallback", cli)
self.assertIn("plugin_mcp", cli)
self.assertIn("standalone_mcp", cli)
self.assertIn("qiongli_literature_status", troubleshooting)- [ ] Step 2: Add Chinese docs assertions
Add assertions to tests/test_cli_setup_docs.py:
def test_zh_codex_plugin_install_documents_plugin_bundled_mcp(self) -> None:
install = (REPO_ROOT / "docs" / "zh" / "guide" / "install.md").read_text(encoding="utf-8")
cli = (REPO_ROOT / "docs" / "zh" / "reference" / "cli.md").read_text(encoding="utf-8")
troubleshooting = (REPO_ROOT / "docs" / "zh" / "guide" / "troubleshooting.md").read_text(encoding="utf-8")
self.assertIn("插件内置 MCP", install)
self.assertIn("不会写入 `~/.codex/config.toml`", install)
self.assertIn("standalone MCP fallback", cli)
self.assertIn("plugin_mcp", cli)
self.assertIn("standalone_mcp", cli)
self.assertIn("qiongli_literature_status", troubleshooting)- [ ] Step 3: Run docs tests and verify they fail
Run:
python3 -m unittest tests.test_cli_setup_docs tests.test_mcp_provider_docs -vExpected: FAIL until the docs include the plugin-first MCP explanation and preflight guidance.
- [ ] Step 4: Update English docs
In docs/guide/install.md, add a Codex note near plugin install instructions:
Codex plugin installs use plugin-bundled MCP by default. The installer writes `.mcp.json` inside the Qiongli plugin and the plugin manifest points to it; it does not write `~/.codex/config.toml` on the plugin path. Use `qiongli install --target codex --parts mcp` only when you need the standalone MCP fallback.In docs/reference/cli.md, update the qiongli check section:
For Codex plugin installs, `mcp` reports the effective MCP source. `plugin_mcp` reports the plugin-local `.mcp.json`, and `standalone_mcp` reports `~/.codex/config.toml`. A false `standalone_mcp.installed` value is expected when `plugin_mcp.installed` is true.In docs/guide/troubleshooting.md, add a recovery entry:
If a Codex literature task says Qiongli is in `strategy_only` even though the plugin is installed, run `qiongli check --json` and confirm `installed.codex.plugin_mcp.installed` is true. In a Codex session, ask Qiongli to call `qiongli_literature_status`; if the tool is not visible, restart Codex. If plugin MCP remains invisible, install the explicit standalone fallback with `qiongli install --target codex --parts mcp`.- [ ] Step 5: Update Chinese docs
In docs/zh/guide/install.md, add:
Codex 插件安装默认使用插件内置 MCP。安装器会把 `.mcp.json` 写在 Qiongli 插件目录内,并由插件 manifest 指向它;插件路径不会写入 `~/.codex/config.toml`。只有在需要 standalone MCP fallback 时,才运行 `qiongli install --target codex --parts mcp`。In docs/zh/reference/cli.md, add:
对于 Codex 插件安装,`mcp` 表示当前有效 MCP 来源。`plugin_mcp` 表示插件本地 `.mcp.json`,`standalone_mcp` 表示 `~/.codex/config.toml`。当 `plugin_mcp.installed` 为 true 时,`standalone_mcp.installed` 为 false 是预期状态。In docs/zh/guide/troubleshooting.md, add:
如果 Codex 文献任务在插件已安装时仍提示 Qiongli 处于 `strategy_only`,先运行 `qiongli check --json`,确认 `installed.codex.plugin_mcp.installed` 为 true。在 Codex 会话里要求 Qiongli 调用 `qiongli_literature_status`;如果该工具不可见,重启 Codex。如果插件 MCP 仍不可见,再使用显式 standalone fallback:`qiongli install --target codex --parts mcp`。- [ ] Step 6: Run docs tests and verify they pass
Run:
python3 -m unittest tests.test_cli_setup_docs tests.test_mcp_provider_docs -vExpected: PASS.
Task 6: Run Boundary And Release Verification
Files:
Read: all modified files in this plan
[ ] Step 1: Run focused Python verification
Run:
python3 -m unittest \
tests.test_cli \
tests.test_universal_installer \
tests.test_mcp_client_config \
tests.test_local_plugin_installer \
tests.test_plugin_artifacts \
tests.test_plugin_distribution_contract \
tests.test_mcp_provider_docs \
tests.test_cli_setup_docs \
tests.test_subject_materializer \
-vExpected: PASS.
- [ ] Step 2: Run syntax and whitespace checks
Run:
python3 -m py_compile \
packages/python-qiongli/src/qiongli/install_discovery.py \
packages/python-qiongli/src/qiongli/cli.py
git diff --checkExpected: both commands exit 0.
- [ ] Step 3: Review repository boundaries
Inspect changed files:
git status --short --untracked-files=allExpected findings:
No provider secrets in
.mcp.json, docs, tests, or generated artifacts.No machine-specific absolute user paths in committed docs or test fixtures.
No marketplace catalog files copied into source docs.
No generated plugin payloads committed unless they are already source-owned fixtures.
~/.codex/config.tomlremains an explicit standalone MCP path and is not written by plugin-surface tests.[ ] Step 4: Commit in two logical commits
First commit the existing wrapper-skill work if it is still uncommitted:
git add \
packages/python-qiongli/src/qiongli/workflow_wrapper_skills.py \
packages/python-qiongli/src/qiongli/local_plugin_installer.py \
tooling/scripts/build_plugin_artifacts.py \
tests/test_local_plugin_installer.py \
tests/test_plugin_artifacts.py \
docs/guide/install.md \
docs/guide/troubleshooting.md \
docs/guide/using-agent-skills.md \
docs/reference/cli.md \
docs/zh/guide/install.md \
docs/zh/guide/troubleshooting.md \
docs/zh/guide/using-agent-skills.md \
docs/zh/reference/cli.md
git commit -m "feat(codex): add qiongli workflow wrapper skills"Then commit the MCP UX fix from this plan:
git add \
packages/python-qiongli/src/qiongli/install_discovery.py \
packages/python-qiongli/src/qiongli/cli.py \
content/workflow/SKILL.md \
docs/guide/install.md \
docs/guide/troubleshooting.md \
docs/reference/cli.md \
docs/zh/guide/install.md \
docs/zh/guide/troubleshooting.md \
docs/zh/reference/cli.md \
tests/test_cli.py \
tests/test_cli_setup_docs.py \
tests/test_mcp_provider_docs.py \
tests/test_subject_materializer.py \
docs/superpowers/plans/2026-06-28-codex-plugin-mcp-ux.md
git commit -m "fix(codex): report plugin-bundled qiongli mcp"If packages/python-qiongli/src/qiongli/subject_materializer.py already contains unrelated local edits, inspect its diff before staging and stage only hunks that belong to the workflow preflight text propagation.
Self-Review
Spec coverage: The plan covers the requested design: Codex does not write standalone MCP by default, qiongli check reports plugin-bundled MCP clearly, standalone MCP remains an explicit fallback, and literature workflows call capability status before downgrading.
Placeholder scan: The plan contains concrete file paths, commands, expected outcomes, and code snippets for every implementation task.
Type consistency: The new JSON keys are consistently named plugin_mcp and standalone_mcp; the existing mcp key remains the effective MCP source for backward compatibility.