Skip to content

Commit 8f76f85

Browse files
cdeustclaude
andcommitted
chore: remove every uvx invocation — marketplace is the only path
Anthropic's official install path is the plugin marketplace (``/plugin install cortex@cortex-plugins``). uvx was never an Anthropic sanctioned distribution channel — it was an optional convenience that fragmented our deploy story (PyPI version drift, parallel cache lines, opaque failure modes when uvx wasn't on PATH). Removing it leaves exactly one supported path: the marketplace clone runs through scripts/launcher.py. Changes ------- - pyproject.toml: drop ``neuro-cortex-memory`` and ``cortex-hook`` console scripts (only kept ``cortex-doctor`` for local CLI use of the doctor from a checkout). - mcp_server/hook_runner.py + tests: deleted (obsolete — hooks now run via launcher.py per .claude-plugin/plugin.json). - mcp_server/doctor.py: removed _uvx_available() check and its entry in CHECKS. Doctor now runs 8 checks instead of 9. - mcp_server/infrastructure/ap_bridge.py: removed the uvx fallback in _resolve_command(); removed "uvx" from _extra_allowed_commands. - mcp_server/infrastructure/mcp_client.py: removed "uvx" and "uv" from _ALLOWED_COMMANDS. - README.md: replaced uvx-based doctor invocation with ``python3 -m mcp_server.doctor``. Defense-in-depth: PEP 706 tar filter ------------------------------------ mcp_server/infrastructure/pipeline_install_release.py: the tarfile.extract call now passes ``filter="data"`` (PEP 706) so the extraction is safe by default — no symlinks outside dest, no special files, no setuid bits — preventing CVE-2007-4559-class issues when consuming the upstream prebuilt tarball. Tests ----- 269 tests pass. Two install tests required a try_install_prebuilt mock now that automatised-pipeline v0.0.2 is live and the GitHub Releases fast-path actually returns assets (the tests were written before the release shipped). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c261aba commit 8f76f85

9 files changed

Lines changed: 47 additions & 278 deletions

File tree

README.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,14 @@ This handles everything: PostgreSQL + pgvector installation, database creation,
5353
After install, verify everything is wired correctly:
5454

5555
```bash
56-
uvx --python 3.13 --from 'neuro-cortex-memory[postgresql]' cortex-doctor
56+
python3 -m mcp_server.doctor
5757
```
5858

59-
Eight checks in two seconds: Python, uvx, PG driver, DATABASE_URL, PG connection, extensions, writable methodology dir, I10 pool-capacity invariant. Exit 0 means ready.
59+
(or, from inside the marketplace clone: `python3 ~/.claude/plugins/cache/cortex-plugins/cortex/*/mcp_server/doctor.py`)
6060

61-
> **Using Claude Cowork?** Install [Cortex-cowork](https://github.com/cdeust/Cortex-cowork) instead — uses SQLite, no PostgreSQL required.
62-
63-
Or add as a standalone MCP server (no hooks, no skills — just the 33 tools):
61+
Seven checks in two seconds: Python, PG driver, DATABASE_URL, PG connection, extensions, writable methodology dir, I10 pool-capacity invariant. Exit 0 means ready.
6462

65-
```bash
66-
claude mcp add cortex -- uvx --from "neuro-cortex-memory[postgresql]" neuro-cortex-memory
67-
```
63+
> **Using Claude Cowork?** Install [Cortex-cowork](https://github.com/cdeust/Cortex-cowork) instead — uses SQLite, no PostgreSQL required.
6864
6965
<details>
7066
<summary><strong>More options</strong> (Clone, Docker, Manual setup)</summary>

mcp_server/doctor.py

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,8 @@ def _python_version() -> Check:
6060
"Python >= 3.10",
6161
False,
6262
f"Python {ver.major}.{ver.minor}.{ver.micro}",
63-
"Upgrade Python: `uvx --python 3.13 ...` (recommended) or install 3.10+.",
64-
)
65-
66-
67-
def _uvx_available() -> Check:
68-
"""The marketplace install path uses ``uvx`` for both the MCP server
69-
and the lifecycle hooks (plugin.json). If uvx is missing, Claude
70-
Code cannot start the plugin. Users who get here via pip install
71-
(server deployment) don't need uvx and can ignore this warning."""
72-
uvx = shutil.which("uvx")
73-
if uvx:
74-
return Check("uvx (marketplace install path)", True, uvx)
75-
return Check(
76-
"uvx (marketplace install path)",
77-
False,
78-
"not found on PATH",
79-
"Install uv: `curl -LsSf https://astral.sh/uv/install.sh | sh` (macOS/Linux) "
80-
'or `powershell -c "irm https://astral.sh/uv/install.ps1 | iex"` (Windows). '
81-
"Not needed if you installed via pip directly.",
63+
"Upgrade Python: install Python 3.10+ via the official installer "
64+
"(https://www.python.org/downloads/) or your platform's package manager.",
8265
)
8366

8467

@@ -277,7 +260,6 @@ def _i10_config() -> Check:
277260

278261
CHECKS: list[Callable[[], Check]] = [
279262
_python_version,
280-
_uvx_available,
281263
_pg_driver,
282264
_database_url,
283265
_pg_connection,

mcp_server/hook_runner.py

Lines changed: 0 additions & 99 deletions
This file was deleted.

mcp_server/infrastructure/ap_bridge.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,16 @@ def _resolve_command() -> dict | None:
138138
1. ``CORTEX_AP_COMMAND`` env var — full shell-free invocation
139139
spec (JSON: ``{"command": "...", "args": [...]}``).
140140
2. Methodology bin symlink — ``~/.claude/methodology/bin/mcp-server``
141-
set up by ``cortex setup-project``; basename ``mcp-server``
142-
matches the MCPClient allowlist.
143-
3. Plugin-cache probe — ``automatised-pipeline`` installed as
144-
a sibling plugin exposes its MCP entrypoint via
145-
``~/.claude/plugins/cache/<mp>/automatised-pipeline/*``.
146-
4. ``uvx`` fallback for when upstream publishes the package
147-
(currently a no-op until upstream ships a uvx-runnable
148-
entrypoint).
141+
set up by Cortex's silent installer (pipeline_installer.py).
142+
Basename ``mcp-server`` matches the MCPClient allowlist.
143+
3. Plugin-cache probe — ``automatised-pipeline`` installed as a
144+
sibling marketplace plugin exposes its MCP entrypoint via
145+
``~/.claude/plugins/cache/<marketplace>/automatised-pipeline/
146+
*/bin/*``.
147+
148+
Returns None when no AP install can be discovered; callers treat
149+
that as graceful degradation (ingest_codebase fails with the
150+
standard McpConnectionError).
149151
"""
150152
raw = os.environ.get("CORTEX_AP_COMMAND")
151153
if raw:
@@ -171,12 +173,7 @@ def _resolve_command() -> dict | None:
171173
for root in (home / ".claude/plugins/cache").glob("*/automatised-pipeline/*/bin/*"):
172174
if root.is_file() and os.access(root, os.X_OK):
173175
return {"command": "node", "args": [str(root)]}
174-
# uvx fallback (placeholder — requires upstream to publish a
175-
# uvx-runnable package; tracked upstream as packaging work).
176-
return {
177-
"command": "uvx",
178-
"args": ["--from", "automatised-pipeline", "automatised-pipeline"],
179-
}
176+
return None
180177

181178

182179
class APBridge:
@@ -227,11 +224,10 @@ async def connect(self) -> bool:
227224
self._client = MCPClient(cfg)
228225
# AP's binary is not in the default allowlist.
229226
# ``ai-architect-mcp`` is the crate name in
230-
# automatised-pipeline; the others are fallbacks for
231-
# the plugin-cache / uvx resolution paths.
227+
# automatised-pipeline; ``node`` is for the
228+
# plugin-cache resolution path.
232229
self._client._extra_allowed_commands = {
233230
"node",
234-
"uvx",
235231
"automatised-pipeline",
236232
"ai-architect-mcp",
237233
}

mcp_server/infrastructure/mcp_client.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ async def connect(self) -> None:
6464
"npx",
6565
"python",
6666
"python3",
67-
"uvx",
68-
"uv",
6967
"cortex",
7068
"mcp-server",
7169
}

mcp_server/infrastructure/pipeline_install_release.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,11 @@ def _verify_and_extract(tar_path: str, expected_sha: str, dest_dir: str) -> Opti
108108
if not str(target).startswith(str(dest) + os.sep) and target != dest:
109109
return None
110110
if member.isfile() and Path(member.name).name == "ai-architect-mcp":
111-
tar.extract(member, dest_dir)
111+
# filter="data" enforces safe extraction (no symlinks
112+
# outside dest, no special files, no setuid bits) —
113+
# required default in Python 3.14, opt-in earlier.
114+
# PEP 706 / CVE-2007-4559.
115+
tar.extract(member, dest_dir, filter="data")
112116
extracted = dest / member.name
113117
os.chmod(extracted, 0o755)
114118
return str(extracted)

pyproject.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ Homepage = "https://github.com/cdeust/Cortex"
3232
Repository = "https://github.com/cdeust/Cortex"
3333

3434
[project.scripts]
35-
neuro-cortex-memory = "mcp_server.__main__:main"
35+
# Cortex's only supported install path is Anthropic's plugin marketplace
36+
# (`/plugin install cortex@cortex-plugins`). The marketplace clones this
37+
# repo into ~/.claude/plugins/cache/cortex-plugins/cortex/<version>/ and
38+
# Claude Code spawns the MCP server + hooks via scripts/launcher.py
39+
# (see .mcp.json + .claude-plugin/plugin.json). Cortex is NOT published
40+
# to PyPI and uvx-based invocation is NOT supported.
41+
# The single console script below is for local CLI use of the doctor
42+
# from a checkout (not from a PyPI install).
3643
cortex-doctor = "mcp_server.doctor:run"
37-
cortex-hook = "mcp_server.hook_runner:run"
3844

3945
[project.optional-dependencies]
4046
postgresql = [

tests_py/infrastructure/test_pipeline_discovery.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,14 @@ def test_zero_byte_binary_not_trusted(self, tmp_path, monkeypatch):
259259
for k in pipeline_installer._CI_ENV_VARS:
260260
monkeypatch.delenv(k, raising=False)
261261
monkeypatch.setenv("CORTEX_AUTO_INSTALL_RUST", "0")
262+
# Disable the GitHub-Releases fast-path so the missing-toolchain
263+
# branch is reachable. (Without this, the test pulls the real
264+
# release binary and short-circuits at installed_prebuilt.)
265+
monkeypatch.setattr(
266+
pipeline_installer,
267+
"try_install_prebuilt",
268+
lambda *a, **kw: {"action": "prebuilt_disabled"},
269+
)
262270
# No cargo on PATH and no ~/.cargo/bin — should fall through
263271
# to missing_toolchain rather than short-circuit.
264272
monkeypatch.setattr(pipeline_installer.shutil, "which", lambda n: None)
@@ -313,6 +321,13 @@ def test_missing_toolchain_returns_structured_failure(self, tmp_path, monkeypatc
313321
tmp_path / "nonexistent" / "mcp-server",
314322
)
315323
from mcp_server.infrastructure import pipeline_install_rust
324+
# Disable the GitHub-Releases fast-path so the missing-toolchain
325+
# branch is reachable.
326+
monkeypatch.setattr(
327+
pipeline_installer,
328+
"try_install_prebuilt",
329+
lambda *a, **kw: {"action": "prebuilt_disabled"},
330+
)
316331
monkeypatch.setattr(pipeline_installer.shutil, "which", lambda n: None)
317332
monkeypatch.setattr(pipeline_install_rust.shutil, "which", lambda n: None)
318333
monkeypatch.setattr(

0 commit comments

Comments
 (0)