fix(canvas): extend mc#1535 per-workspace MCP slug to codex/openclaw/hermes/kimi (multi-workspace class sweep) #1536
Reference in New Issue
Block a user
Delete Branch "fix/multi-workspace-install-snippets-class-sweep"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
mc#1535 fixed
claude mcp add molecule→claude mcp add molecule-<slug>for the Universal MCP snippet. The same per-session-overwrite equivalence-class bug exists in EVERY OTHER runtime tab the Canvas modal renders (each MCP host keys config by name, all 5 templates hardcodedmolecule). This PR extends mc#1535's existing{{MCP_SERVER_NAME}}placeholder +mcpServerNameForWorkspace()helper into the remaining 4 runtime templates so multi-workspace install works out-of-the-box for every host, not just Claude Code.CTO 2026-05-18 22:43Z: "其实是我们没有做好instruction,这个得补充".
Gap matrix swept by this PR
[mcp_servers.molecule]duplicate-table[mcp_servers.{{MCP_SERVER_NAME}}]+.envtableopenclaw mcp set moleculeoverwrites~/.openclaw/mcp/molecule.jsonopenclaw mcp set {{MCP_SERVER_NAME}}plugin_platforms.molecule:duplicate-keyplugin_platforms.{{MCP_SERVER_NAME}}:~/.molecule-ai/kimi-workspace/dir overwrites env/bridge~/.molecule-ai/kimi-{{MCP_SERVER_NAME}}/Token-scoping check
Each template's
MOLECULE_WORKSPACE_TOKENwas already per-workspace minted (Rotate/Create path mc#1535 audited). No change to token plumbing.Open-source-templates cleanliness check
Templates live in the PRIVATE molecule-core repo (Canvas modal generator). No new
git.moleculesai.appor.moleculesai.appliteral added. Pre-existinggit+https://git.moleculesai.app/.../hermes-channel-molecule.gitin the hermes template stays — it points at a public hermes-side plugin and is its canonical URL. The open-source-template-no-hardcoded-org-internals rule (feedback_open_source_templates_no_hardcoded_org_internals.md) applies totemplate-codex/template-hermes/template-openclaw— separate public repos, not touched here.Test plan
go test ./internal/handlers/ -run TestBuildExternalConnectionPayload— 5/5 green, including new_AllRuntimeSnippetsAreWorkspaceUnique.go test ./internal/handlers/full package — 15.9s green.go vet ./internal/handlers/clean.Sequencing
Branch base is
fix/add-to-claude-code-unique-server-name-per-workspace(mc#1535). This PR reuses mc#1535's{{MCP_SERVER_NAME}}plumbing +BuildExternalConnectionPayload(workspaceName)signature. After mc#1535 merges, this PR retargets cleanly to main (no conflict expected — disjoint template strings).Files
workspace-server/internal/handlers/external_connection.go(4 templates + headers)workspace-server/internal/handlers/external_rotate_test.go(new sweep test)workspace/a2a_mcp_server.py(serverInfo comment only — no code change)scripts/build_runtime_package.py(3 PyPI README references + new bullet)Author identity: core-devops (per-role persona; not founder-PAT). Opened for non-author review, NOT auto-merged.
Sibling: mc#1535. Follow-up of #230 + Task #229.
🤖 Generated with Claude Code
## Summary - mc#1535 fixed the per-session-overwrite bug in the Universal MCP snippet (`claude mcp add molecule -s user` keyed by `molecule`, so installing for a second workspace silently replaced the first). The same equivalence-class bug exists in EVERY other runtime tab the Canvas modal renders: each MCP host keys its config by name, and all five templates hardcoded a fixed `molecule` identifier. - This PR extends mc#1535's existing `{{MCP_SERVER_NAME}}` placeholder + `mcpServerNameForWorkspace()` helper into the 4 remaining templates so the Canvas snippet a user pastes is unique per workspace by construction across ALL runtime tabs — multi-workspace works out-of-the-box with no per-host workarounds. ## Bug shape per runtime tab (mc#1535 sibling) - **codex** (`~/.codex/config.toml`): `[mcp_servers.molecule]` — TOML rejects duplicate table keys, so re-paste either breaks parsing or overwrites. - **openclaw** (`~/.openclaw/mcp/molecule.json`): `openclaw mcp set molecule` keyed by name — second workspace overwrites. - **hermes** (`~/.hermes/config.yaml`): `plugin_platforms.molecule:` — YAML rejects duplicate mapping keys, second workspace silently collapses. - **kimi** (`~/.molecule-ai/kimi-workspace/`): single per-host dir — second workspace's env+bridge.py overwrites the first. ## What changed - `workspace-server/internal/handlers/external_connection.go`: - 4 templates now stamp `{{MCP_SERVER_NAME}}` (the same slug mc#1535 already derives + plumbs into the universal_mcp snippet) in the keyed identifier: - codex: `[mcp_servers.{{MCP_SERVER_NAME}}]` + `.env` table. - openclaw: `openclaw mcp set {{MCP_SERVER_NAME}}` + log path. - hermes: `plugin_platforms.{{MCP_SERVER_NAME}}:`. - kimi: `~/.molecule-ai/kimi-{{MCP_SERVER_NAME}}/` dir + embedded python `ENV` path. - Header comment in each template documents the multi-workspace contract (mirrors mc#1535's universal_mcp header). - `workspace-server/internal/handlers/external_rotate_test.go`: - New `TestBuildExternalConnectionPayload_AllRuntimeSnippetsAreWorkspaceUnique` pins the per-template literal that proves the slug was stamped, AND asserts no template leaves a literal `{{MCP_SERVER_NAME}}` placeholder — catches a future template author who forgets to register a new tab with the stamp pipeline. - `workspace/a2a_mcp_server.py`: - Comment-only update on `serverInfo.name` to reflect that the per-host registration name is workspace-specific. No code change; `serverInfo.name` stays the generic `"molecule"` self-label. - `scripts/build_runtime_package.py` (PyPI README generator): - Updates 3 `claude mcp add molecule -- molecule-mcp` references to `claude mcp add molecule-<workspace-slug> -- molecule-mcp` so the PyPI README matches the Canvas-stamped snippet pattern. - Adds a "Server name in `claude mcp add` is workspace-specific" bullet pointing at mc#1535 + this PR for context. ## Open-source-templates cleanliness check - Templates touched here live in the PRIVATE molecule-core repo (Canvas modal generator); they STAMP per-workspace server names but do NOT bake any new `git.moleculesai.app` literal or other org-internal infra. Generic `pip install 'git+https://git.moleculesai.app/molecule-ai/hermes-channel-molecule.git'` in the hermes template is the only such URL touched and was pre-existing — that one points at a public hermes-side plugin and has its own canonical URL; not in scope for the open-source-template rule (the rule applies to template-codex/template-hermes/ template-openclaw — separate public repos, untouched here). - No `.moleculesai.app` literal added; persona-token shape unchanged (auth_token still per-workspace minted by Rotate/Create — same path mc#1535 audited). ## Sample stamped snippets (workspace name "my-bot", slug "molecule-my-bot") - codex: `[mcp_servers.molecule-my-bot]` + `[mcp_servers.molecule-my-bot.env]` - openclaw: `openclaw mcp set molecule-my-bot "$(cat <<EOF ... )"` - hermes: `plugin_platforms:\n molecule-my-bot:\n enabled: true` - kimi: `~/.molecule-ai/kimi-molecule-my-bot/{env,kimi_bridge.py}` ## Diff size - 4 files, +135/-40 LoC. Most of it is comment text + the new test. - Did NOT change `BuildExternalConnectionPayload` signature or `mcpServerNameForWorkspace` semantics — both were already plumbed by mc#1535 to all 8 snippets via the stamp closure; this PR only updates the template text to USE the placeholder. ## Test plan - [x] `go test ./internal/handlers/ -run TestBuildExternalConnectionPayload` — 5/5 green, including new `_AllRuntimeSnippetsAreWorkspaceUnique`. - [x] `go test ./internal/handlers/` full package — 15.9s green. - [x] `go vet ./internal/handlers/` — clean. - [ ] Manual (post-merge, requires mc#1535 also merged): create two "bot-a" + "bot-b" external workspaces on staging; paste each tab's snippet into the corresponding host on a single machine; verify `claude mcp list` / `cat ~/.codex/config.toml` / `openclaw mcp list` / `~/.hermes/config.yaml` / `ls ~/.molecule-ai/` each shows BOTH workspaces' entries side-by- side, not overwriting. ## Sequencing - This PR's base is mc#1535's branch (`fix/add-to-claude-code-unique-server-name-per-workspace`), because it reuses mc#1535's `{{MCP_SERVER_NAME}}` placeholder + slug helper + `BuildExternalConnectionPayload(workspaceName)` signature change. Will need a rebase on main after mc#1535 lands; prefer to keep stacked to make the review of EACH PR scope-tight. - CTO 2026-05-18 22:43Z: "其实是我们没有做好instruction,这个得补充" — this PR is the consolidated per-repo doc/generator fix. ## Related - Sibling: mc#1535 (Universal MCP snippet, already open). - Follow-up #230: molecule-core stale channel-install mentions (CONTRIBUTING.md:195, etc.) — separate scope. Author identity: core-devops (per-role persona; not founder-PAT). Opened for non-author review, NOT auto-merged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Five-axis review — APPROVE
{{MCP_SERVER_NAME}}plumbing across all 4 remaining MCP-host templates (codex/openclaw/hermes/kimi) + the build_runtime_package.py snippet + the workspace/a2a_mcp_server.py serverInfo comment. Each host's keyspace correctly identified: codex =[mcp_servers.<name>]TOML table (rejects dups), openclaw =~/.openclaw/mcp/<name>.jsonfile-keyed, hermes =gateway.plugin_platforms.<key>YAML mapping (rejects dup keys), kimi =~/.molecule-ai/kimi-<slug>/directory. All correct keyspaces.TestBuildExternalConnectionPayload_AllRuntimeSnippetsAreWorkspaceUniquecodifies the contract per-runtime so a future template author adding a new tab without slug plumbing trips the test.kimi-workspace/env.{{MCP_SERVER_NAME}}literals in any snippet" guard.CI: All required
(pull_request)contexts green (qa-review/approved✓,security-review/approved✓,sop-tier-check/tier-check✓, etc.). The combined "pending" state is fromsop-checklist / na-declarations = pending / N/A: (none)which is the expected na-shape, not a real fail.Sequencing: This PR's base =
fix/add-to-claude-code-unique-server-name-per-workspace(PR#1535's branch). Cannot merge until PR#1535 lands first; after that re-target tomain(or auto-rebase) and ship.Two-eyes preserved: non-author identity. Improves codebase health.
Second non-author APPROVE — five-axis confirmed
Independently reviewed diff + CI state. Correctness / readability / architecture / security / performance all check out per the primary reviewer's notes. Required CI contexts on the base branch's protection are green. No new findings.
Two-eyes preserved: this reviewer identity is distinct from both the PR author and the first approver.
LGTM — improves codebase health.