From e725f1c5f2f61995bc81e4d05c54fb0ff59b7d08 Mon Sep 17 00:00:00 2001 From: kigland Date: Wed, 8 Jul 2026 10:35:57 +0800 Subject: [PATCH 1/2] fix installer mcp package pin --- README.md | 2 +- docs/installation.md | 36 ++++++++++++++++---------------- src/semble/agents/antigravity.md | 2 +- src/semble/agents/claude.md | 2 +- src/semble/agents/codex.toml | 2 +- src/semble/agents/commandcode.md | 2 +- src/semble/agents/copilot.md | 2 +- src/semble/agents/cursor.md | 2 +- src/semble/agents/gemini.md | 2 +- src/semble/agents/kiro.md | 2 +- src/semble/agents/opencode.md | 2 +- src/semble/agents/pi.md | 2 +- src/semble/agents/reasonix.md | 2 +- src/semble/agents/zcode.md | 2 +- src/semble/installer/agents.py | 13 +++++++----- src/semble/installer/config.py | 4 ++-- tests/test_installer.py | 14 +++++++++++++ 17 files changed, 55 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 9286a8d8a..6186260b7 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ semble search "deployment guide" ./my-project --content docs # or: config, all semble find-related src/auth.py 42 ./my-project ``` -`--content` accepts `code` (default), `docs`, `config`, or `all`. `path` defaults to the current directory when omitted; git URLs are accepted. If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +`--content` accepts `code` (default), `docs`, `config`, or `all`. `path` defaults to the current directory when omitted; git URLs are accepted. If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place.
Controlling which files are indexed diff --git a/docs/installation.md b/docs/installation.md index ce6f79cd5..af072add3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -51,7 +51,7 @@ semble install --agent claude pi --type mcp subagent --yes Claude Code ```bash -claude mcp add semble -s user -- uvx --from "semble[mcp]" semble +claude mcp add semble -s user -- uvx --from "semble[mcp]@0.4.2" semble ```
@@ -66,7 +66,7 @@ Add to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in your project): "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -82,7 +82,7 @@ Add to `~/.codex/config.toml`: ```toml [mcp_servers.semble] command = "uvx" -args = ["--from", "semble[mcp]", "semble"] +args = ["--from", "semble[mcp]@0.4.2", "semble"] ``` @@ -97,7 +97,7 @@ Add to `~/.config/opencode/opencode.jsonc`: "mcp": { "semble": { "type": "local", - "command": ["uvx", "--from", "semble[mcp]", "semble"] + "command": ["uvx", "--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -115,7 +115,7 @@ Add to `.vscode/mcp.json` in your project (or your user profile's `mcp.json`): "servers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -133,7 +133,7 @@ Add to `~/.copilot/mcp-config.json`: "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -151,7 +151,7 @@ Add to `~/.codeium/windsurf/mcp_config.json`: "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -169,7 +169,7 @@ Add to `~/.gemini/settings.json`: "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -187,7 +187,7 @@ Add to `~/.kiro/settings/mcp.json` (or `.kiro/settings/mcp.json` in your project "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -206,7 +206,7 @@ Add to `~/.config/zed/settings.json` (or `.zed/settings.json` in your project): "semble": { "source": "custom", "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -224,7 +224,7 @@ Add to `~/.reasonix/config.json` (the backwards-compatible MCP config path read "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -248,7 +248,7 @@ Then add to `~/.pi/agent/mcp.json`: "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -266,7 +266,7 @@ Add to `~/.gemini/config/mcp_config.json`: "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -284,7 +284,7 @@ Add to `~/.commandcode/mcp.json`: "mcpServers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"] + "args": ["--from", "semble[mcp]@0.4.2", "semble"] } } } @@ -293,7 +293,7 @@ Add to `~/.commandcode/mcp.json`: Or use the CLI: ```bash -cmd mcp add --scope user semble -- uvx --from "semble[mcp]" semble +cmd mcp add --scope user semble -- uvx --from "semble[mcp]@0.4.2" semble ``` @@ -309,7 +309,7 @@ Add to `~/.zcode/cli/config.json` under the nested `mcp.servers` key (or use Set "servers": { "semble": { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"], + "args": ["--from", "semble[mcp]@0.4.2", "semble"], "type": "stdio" } } @@ -322,7 +322,7 @@ Add to `~/.zcode/cli/config.json` under the nested `mcp.servers` key (or use Set By default the MCP server indexes only code files. To also index documentation, config, or everything, append `--content docs`, `--content config`, or `--content all` to the server command. For example, in Claude Code: ```bash -claude mcp add semble -s user -- uvx --from "semble[mcp]" semble --content all +claude mcp add semble -s user -- uvx --from "semble[mcp]@0.4.2" semble --content all ``` ### Instructions (AGENTS.md / CLAUDE.md) @@ -358,7 +358,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/antigravity.md b/src/semble/agents/antigravity.md index ad08b4ccc..b813af3e2 100644 --- a/src/semble/agents/antigravity.md +++ b/src/semble/agents/antigravity.md @@ -32,7 +32,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/claude.md b/src/semble/agents/claude.md index f790f4bfd..01dbeaaf5 100644 --- a/src/semble/agents/claude.md +++ b/src/semble/agents/claude.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/codex.toml b/src/semble/agents/codex.toml index 2a7866520..d822e78e0 100644 --- a/src/semble/agents/codex.toml +++ b/src/semble/agents/codex.toml @@ -27,7 +27,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/commandcode.md b/src/semble/agents/commandcode.md index cd764e8f9..327165dfb 100644 --- a/src/semble/agents/commandcode.md +++ b/src/semble/agents/commandcode.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/copilot.md b/src/semble/agents/copilot.md index f790f4bfd..01dbeaaf5 100644 --- a/src/semble/agents/copilot.md +++ b/src/semble/agents/copilot.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/cursor.md b/src/semble/agents/cursor.md index 0fc7a4658..b92e59dd5 100644 --- a/src/semble/agents/cursor.md +++ b/src/semble/agents/cursor.md @@ -29,7 +29,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/gemini.md b/src/semble/agents/gemini.md index ad08b4ccc..b813af3e2 100644 --- a/src/semble/agents/gemini.md +++ b/src/semble/agents/gemini.md @@ -32,7 +32,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/kiro.md b/src/semble/agents/kiro.md index 14d5ef13a..0b354b5e1 100644 --- a/src/semble/agents/kiro.md +++ b/src/semble/agents/kiro.md @@ -32,7 +32,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/opencode.md b/src/semble/agents/opencode.md index e2f394fc4..1c41399f6 100644 --- a/src/semble/agents/opencode.md +++ b/src/semble/agents/opencode.md @@ -33,7 +33,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/pi.md b/src/semble/agents/pi.md index 0fc7a4658..b92e59dd5 100644 --- a/src/semble/agents/pi.md +++ b/src/semble/agents/pi.md @@ -29,7 +29,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/reasonix.md b/src/semble/agents/reasonix.md index 1c8b91d4e..2e12f596b 100644 --- a/src/semble/agents/reasonix.md +++ b/src/semble/agents/reasonix.md @@ -31,7 +31,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/agents/zcode.md b/src/semble/agents/zcode.md index f790f4bfd..01dbeaaf5 100644 --- a/src/semble/agents/zcode.md +++ b/src/semble/agents/zcode.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. ### Workflow diff --git a/src/semble/installer/agents.py b/src/semble/installer/agents.py index a3670f018..b22af5ad2 100644 --- a/src/semble/installer/agents.py +++ b/src/semble/installer/agents.py @@ -8,6 +8,8 @@ from pathlib import Path from typing import Literal +from semble.version import __version__ + _HOME = Path.home() Action = Literal["created", "updated", "unchanged", "not-found", "removed", "error", "skipped"] @@ -24,28 +26,29 @@ class IntegrationType(str, Enum): SEMBLE_START = "" SEMBLE_END = "" +_MCP_PACKAGE_SPEC = f"semble[mcp]@{__version__}" _STDIO_SERVER_CONFIG: dict[str, object] = { "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"], + "args": ["--from", _MCP_PACKAGE_SPEC, "semble"], "type": "stdio", } _OPENCODE_SERVER_CONFIG: dict[str, object] = { - "command": ["uvx", "--from", "semble[mcp]", "semble"], + "command": ["uvx", "--from", _MCP_PACKAGE_SPEC, "semble"], "type": "local", # opencode uses "local"/"remote", not "stdio" "enabled": True, } _BARE_STDIO_SERVER_CONFIG: dict[str, object] = { # Windsurf: command/args only, no "type" "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"], + "args": ["--from", _MCP_PACKAGE_SPEC, "semble"], } _ZED_SERVER_CONFIG: dict[str, object] = { # Zed requires "source": "custom" for manual servers "source": "custom", "command": "uvx", - "args": ["--from", "semble[mcp]", "semble"], + "args": ["--from", _MCP_PACKAGE_SPEC, "semble"], } INSTRUCTIONS = f"""\ @@ -70,7 +73,7 @@ class IntegrationType(str, Enum): semble search "save model to disk" ./my-project --top-k 10 ``` -The index is built on first run and cached automatically. If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]" semble`. +The index is built on first run and cached automatically. If `semble` is not on `$PATH`, use `uvx --from "{_MCP_PACKAGE_SPEC}" semble`. ### Workflow diff --git a/src/semble/installer/config.py b/src/semble/installer/config.py index 3cb1cc8b8..c376b8c89 100644 --- a/src/semble/installer/config.py +++ b/src/semble/installer/config.py @@ -7,13 +7,13 @@ from tree_sitter import Node, Parser from tree_sitter_language_pack import SupportedLanguage, download, get_parser -from semble.installer.agents import SEMBLE_END, SEMBLE_START, Action +from semble.installer.agents import _MCP_PACKAGE_SPEC, SEMBLE_END, SEMBLE_START, Action JsonObjectResult = tuple[Node, bytes] | Literal["skipped", "error"] _T = TypeVar("_T") _CODEX_MCP_HEADER = "[mcp_servers.semble]" -_CODEX_MCP_BLOCK = '[mcp_servers.semble]\ncommand = "uvx"\nargs = ["--from", "semble[mcp]", "semble"]\n' +_CODEX_MCP_BLOCK = f'[mcp_servers.semble]\ncommand = "uvx"\nargs = ["--from", "{_MCP_PACKAGE_SPEC}", "semble"]\n' _json5_parser_cache: Parser | None | bool = False # False = not yet attempted diff --git a/tests/test_installer.py b/tests/test_installer.py index 1c89052f1..6e03d774a 100644 --- a/tests/test_installer.py +++ b/tests/test_installer.py @@ -6,6 +6,7 @@ from semble.installer import run from semble.installer.agents import ( + _MCP_PACKAGE_SPEC, _STDIO_SERVER_CONFIG, AGENTS, SEMBLE_END, @@ -73,6 +74,19 @@ def test_merge_mcp_creates_fresh_file(claude_agent): assert data["mcpServers"]["semble"] == _STDIO_SERVER_CONFIG +def test_mcp_configs_pin_current_package_version(): + """Generated uvx MCP configs pin semble to the installer package version.""" + assert "@" in _MCP_PACKAGE_SPEC + for agent in AGENTS: + if agent.mcp is None: + continue + command = agent.mcp.entry["command"] + if isinstance(command, list): + assert _MCP_PACKAGE_SPEC in command + else: + assert agent.mcp.entry["args"][1] == _MCP_PACKAGE_SPEC + + def test_merge_mcp_preserves_comments_and_other_entries(claude_agent): """merge_mcp adds semble while leaving existing comments and entries byte-intact.""" claude_agent.mcp.path.write_text('{\n // my servers\n "mcpServers": {\n "other": {"command": "x"}\n }\n}\n') From 99917e149ff05e41fd8768fad631a228696393f1 Mon Sep 17 00:00:00 2001 From: kigland Date: Wed, 8 Jul 2026 12:03:29 +0800 Subject: [PATCH 2/2] render pinned mcp package in templates --- src/semble/agents/antigravity.md | 2 +- src/semble/agents/claude.md | 2 +- src/semble/agents/codex.toml | 2 +- src/semble/agents/commandcode.md | 2 +- src/semble/agents/copilot.md | 2 +- src/semble/agents/cursor.md | 2 +- src/semble/agents/gemini.md | 2 +- src/semble/agents/kiro.md | 2 +- src/semble/agents/opencode.md | 2 +- src/semble/agents/pi.md | 2 +- src/semble/agents/reasonix.md | 2 +- src/semble/agents/zcode.md | 2 +- src/semble/installer/installer.py | 4 +++- tests/test_installer.py | 29 +++++++++++++++++++++++++++++ 14 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/semble/agents/antigravity.md b/src/semble/agents/antigravity.md index b813af3e2..e0d76c1fd 100644 --- a/src/semble/agents/antigravity.md +++ b/src/semble/agents/antigravity.md @@ -32,7 +32,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/claude.md b/src/semble/agents/claude.md index 01dbeaaf5..bbe6d645d 100644 --- a/src/semble/agents/claude.md +++ b/src/semble/agents/claude.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/codex.toml b/src/semble/agents/codex.toml index d822e78e0..5dcafd83f 100644 --- a/src/semble/agents/codex.toml +++ b/src/semble/agents/codex.toml @@ -27,7 +27,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/commandcode.md b/src/semble/agents/commandcode.md index 327165dfb..9843608c4 100644 --- a/src/semble/agents/commandcode.md +++ b/src/semble/agents/commandcode.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/copilot.md b/src/semble/agents/copilot.md index 01dbeaaf5..bbe6d645d 100644 --- a/src/semble/agents/copilot.md +++ b/src/semble/agents/copilot.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/cursor.md b/src/semble/agents/cursor.md index b92e59dd5..ef15a9380 100644 --- a/src/semble/agents/cursor.md +++ b/src/semble/agents/cursor.md @@ -29,7 +29,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/gemini.md b/src/semble/agents/gemini.md index b813af3e2..e0d76c1fd 100644 --- a/src/semble/agents/gemini.md +++ b/src/semble/agents/gemini.md @@ -32,7 +32,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/kiro.md b/src/semble/agents/kiro.md index 0b354b5e1..da3026e46 100644 --- a/src/semble/agents/kiro.md +++ b/src/semble/agents/kiro.md @@ -32,7 +32,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/opencode.md b/src/semble/agents/opencode.md index 1c41399f6..373ae5a1d 100644 --- a/src/semble/agents/opencode.md +++ b/src/semble/agents/opencode.md @@ -33,7 +33,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/pi.md b/src/semble/agents/pi.md index b92e59dd5..ef15a9380 100644 --- a/src/semble/agents/pi.md +++ b/src/semble/agents/pi.md @@ -29,7 +29,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/reasonix.md b/src/semble/agents/reasonix.md index 2e12f596b..6d960993c 100644 --- a/src/semble/agents/reasonix.md +++ b/src/semble/agents/reasonix.md @@ -31,7 +31,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/agents/zcode.md b/src/semble/agents/zcode.md index 01dbeaaf5..bbe6d645d 100644 --- a/src/semble/agents/zcode.md +++ b/src/semble/agents/zcode.md @@ -30,7 +30,7 @@ semble find-related src/auth.py 42 ./my-project `path` defaults to the current directory when omitted; git URLs are accepted. -If `semble` is not on `$PATH`, use `uvx --from "semble[mcp]@0.4.2" semble` in its place. +If `semble` is not on `$PATH`, use `uvx --from "{{MCP_PACKAGE_SPEC}}" semble` in its place. ### Workflow diff --git a/src/semble/installer/installer.py b/src/semble/installer/installer.py index 7d6cdecad..f98d03777 100644 --- a/src/semble/installer/installer.py +++ b/src/semble/installer/installer.py @@ -9,6 +9,7 @@ import questionary from semble.installer.agents import ( + _MCP_PACKAGE_SPEC, AGENTS, INSTRUCTIONS, AgentTarget, @@ -27,6 +28,7 @@ ) _T = TypeVar("_T") +_MCP_PACKAGE_SPEC_PLACEHOLDER = "{{MCP_PACKAGE_SPEC}}" _GREEN = "\033[32m" _DIM = "\033[2m" @@ -96,7 +98,7 @@ def _apply_subagent(agent: AgentTarget, mode: Mode) -> WriteResult | None: dest.parent.mkdir(parents=True, exist_ok=True) try: src = files("semble").joinpath(f"agents/{agent.id}{dest.suffix}").read_text(encoding="utf-8") - dest.write_text(src, encoding="utf-8") + dest.write_text(src.replace(_MCP_PACKAGE_SPEC_PLACEHOLDER, _MCP_PACKAGE_SPEC), encoding="utf-8") except Exception: return WriteResult(dest, "error") return WriteResult(dest, "updated" if existed else "created") diff --git a/tests/test_installer.py b/tests/test_installer.py index 6e03d774a..2b6ddd4bd 100644 --- a/tests/test_installer.py +++ b/tests/test_installer.py @@ -1,6 +1,8 @@ import json import sys from dataclasses import replace +from importlib.resources import files +from pathlib import Path import pytest @@ -27,6 +29,7 @@ ) from semble.installer.installer import ( _INTEGRATIONS, + _MCP_PACKAGE_SPEC_PLACEHOLDER, _apply_instructions, _apply_mcp, _apply_subagent, @@ -87,6 +90,32 @@ def test_mcp_configs_pin_current_package_version(): assert agent.mcp.entry["args"][1] == _MCP_PACKAGE_SPEC +def test_packaged_agent_templates_render_current_mcp_package_spec(tmp_path): + """Packaged sub-agent templates are rendered with the current MCP package spec at install time.""" + for agent in AGENTS: + if agent.subagent_path is None: + continue + suffix = agent.subagent_path.suffix + template = files("semble").joinpath(f"agents/{agent.id}{suffix}").read_text(encoding="utf-8") + assert _MCP_PACKAGE_SPEC_PLACEHOLDER in template + assert _MCP_PACKAGE_SPEC not in template + + rendered = replace(agent, subagent_path=tmp_path / agent.id / f"semble-search{suffix}") + assert _apply_subagent(rendered, "install").action == "created" + text = rendered.subagent_path.read_text(encoding="utf-8") + assert _MCP_PACKAGE_SPEC in text + assert _MCP_PACKAGE_SPEC_PLACEHOLDER not in text + + +def test_docs_examples_pin_current_mcp_package_spec(): + """Manual MCP examples stay pinned to the current package version.""" + root = Path(__file__).resolve().parents[1] + docs_text = (root / "docs" / "installation.md").read_text(encoding="utf-8") + readme_text = (root / "README.md").read_text(encoding="utf-8") + assert _MCP_PACKAGE_SPEC in docs_text + assert _MCP_PACKAGE_SPEC in readme_text + + def test_merge_mcp_preserves_comments_and_other_entries(claude_agent): """merge_mcp adds semble while leaving existing comments and entries byte-intact.""" claude_agent.mcp.path.write_text('{\n // my servers\n "mcpServers": {\n "other": {"command": "x"}\n }\n}\n')