Skip to content

Commit 450824b

Browse files
committed
docs(adr): ADR-0050 — marketplace is the only path, no uvx ever
Records the decision and the failure modes of the uvx approach so a future AI session searching the web for "MCP server distribution" doesn't propose modelcontextprotocol/servers' uvx pattern again (that repo's contract is `claude mcp add`, NOT the plugin marketplace — different channel, different conventions). Companion to commits c261aba (v3.14.7 marketplace-native release) and 8f76f85 (final uvx removal).
1 parent 8f76f85 commit 450824b

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# ADR-0050: Marketplace Is the Only Supported Install Path — uvx Removed
2+
3+
**Status:** Accepted
4+
**Date:** 2026-04-25
5+
**Supersedes:** the rationale in commit `9076fe5` ("adopt canonical uvx pattern")
6+
7+
## Context
8+
9+
Cortex has oscillated between two install paths over the past month:
10+
11+
| Version | MCP launch command | Hook command |
12+
|---|---|---|
13+
| v3.13.0 → v3.14.2 | `uvx` (PyPI) | `uvx` cortex-hook |
14+
| v3.14.3 → v3.14.4 | `python3 scripts/launcher.py` | `python3 scripts/launcher.py` |
15+
| v3.14.5 → v3.14.6 | `uvx` (PyPI) | `uvx` cortex-hook |
16+
| v3.14.7+ | `bash $PY ${CLAUDE_PLUGIN_ROOT}/scripts/launcher.py` | same |
17+
18+
Commit `9076fe5` (2026-04-25) reintroduced uvx with the rationale:
19+
20+
> Researching Anthropic's reference servers shows the canonical pattern
21+
> explicitly: every Python stdio MCP server in
22+
> `modelcontextprotocol/servers` is wired up with `command: "uvx"`.
23+
24+
This rationale is **wrong for our distribution channel.** The
25+
`modelcontextprotocol/servers` reference repository targets standalone
26+
MCP servers added via `claude mcp add`, where the user separately
27+
installs the package and Claude Code spawns it. That is a different
28+
mechanism than the **plugin marketplace** (`/plugin install ...`),
29+
which clones the plugin's git repository into
30+
`~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/` and
31+
expects the plugin to launch from that directory.
32+
33+
uvx in a marketplace plugin is a category error: it bypasses the
34+
material the marketplace already cloned and re-fetches code from PyPI
35+
with separate version drift, separate caches, and separate failure
36+
modes that don't exist in the marketplace path.
37+
38+
## Concrete failures observed with the uvx path
39+
40+
1. **Code-of-record divergence.** Edits to the marketplace clone (the
41+
`${CLAUDE_PLUGIN_ROOT}` Claude Code expands at launch) were never
42+
loaded. The running server fetched a stale wheel from PyPI. Every
43+
plugin update → reconnect cycle was a no-op for code changes that
44+
weren't yet on PyPI. Hours of debugging blamed Python module caching
45+
when the actual issue was that the marketplace clone wasn't on the
46+
import path at all.
47+
48+
2. **PEP 668 friction.** uvx triggered EXTERNALLY-MANAGED errors on
49+
homebrew Python and recent Debian/Ubuntu distributions. The launcher
50+
detour in v3.14.3/v3.14.4 was an attempt to fix that, then reverted
51+
in v3.14.5 with a regression of its own.
52+
53+
3. **Two-channel publish coupling.** Every release required PyPI
54+
publish to be coordinated with the marketplace clone. When PyPI
55+
credentials were missing or twine failed, the marketplace clone
56+
advertised a new version that uvx couldn't fetch — silent failure
57+
for end-users.
58+
59+
4. **Adversarial misuse.** AI sessions assisting Cortex maintenance
60+
would search the web, find the modelcontextprotocol/servers README,
61+
and propose "follow the canonical pattern" — the **wrong** pattern
62+
for a marketplace plugin — every time the maintainer asked about
63+
distribution. Without a written-down rule, this loop is infinite.
64+
65+
## Decision
66+
67+
**The plugin marketplace is the only supported Cortex install path.**
68+
uvx is **not** removed because it is bad — it is removed because it is
69+
**not Anthropic's official mechanism for plugin distribution**. The
70+
marketplace was rolled out specifically so plugins could:
71+
72+
- Ship code, hooks, skills, and slash commands as a single git tree.
73+
- Run from the cloned tree, not from a separate package channel.
74+
- Stay in lockstep without two-channel publish coordination.
75+
- Avoid PEP 668 entirely (no `pip install` step for users).
76+
77+
Cortex's release path now follows that contract:
78+
79+
```
80+
.mcp.json::command = "bash"
81+
.mcp.json::args = "-c", "$PY ${CLAUDE_PLUGIN_ROOT}/scripts/launcher.py mcp_server"
82+
plugin.json::hooks[*].command = same shape, with the hook module name as the trailing arg
83+
```
84+
85+
`scripts/launcher.py` lives in the plugin tree. It prepends
86+
`${CLAUDE_PLUGIN_ROOT}` to `PYTHONPATH` and runs the module directly.
87+
There is no PyPI fetch, no uvx step, no version drift.
88+
89+
## Consequences
90+
91+
- `pyproject.toml::[project.scripts]` no longer declares the
92+
`neuro-cortex-memory` or `cortex-hook` console scripts. The package
93+
is **not** published to PyPI.
94+
- `mcp_server/hook_runner.py` is deleted (it existed solely to be the
95+
uvx target for `cortex-hook`).
96+
- `mcp_server/doctor.py::_uvx_available` check is deleted. Doctor no
97+
longer reports uvx state because uvx state is no longer relevant.
98+
- `mcp_server/infrastructure/mcp_client.py::_ALLOWED_COMMANDS` no
99+
longer accepts `"uvx"` or `"uv"` for sub-MCP-client spawns. CWE-78
100+
attack surface narrowed.
101+
- `mcp_server/infrastructure/ap_bridge.py::_resolve_command` no longer
102+
has a uvx fallback. AP discovery returns `None` (graceful
103+
degradation) when no install can be found.
104+
105+
## Note to future AI assistants and contributors
106+
107+
If you propose reintroducing uvx because some Anthropic reference repo
108+
("modelcontextprotocol/servers", "claude-code-plugins-cookbook", a blog
109+
post, etc.) uses it — that reference is for a **different distribution
110+
channel** (`claude mcp add` standalone servers). Cortex is a
111+
**marketplace plugin** and the marketplace is the path. Read this ADR
112+
first. Do not ship a PR that adds `uvx` to `.mcp.json`,
113+
`.claude-plugin/plugin.json`, or any hook command.
114+
115+
## References
116+
117+
- `9076fe5` (commit that reintroduced uvx, now reverted)
118+
- `8f76f85` (commit that removed every uvx invocation)
119+
- Anthropic plugin marketplace documentation:
120+
https://docs.claude.com/en/docs/claude-code/plugins

0 commit comments

Comments
 (0)