Set up Studio as a Claude and Codex marketplace for data-liberation#4011
Conversation
Promotes the Studio repo root to a plugin marketplace for both Claude (.claude-plugin/marketplace.json) and Codex (.agents/) and rehomes the data-liberation plugin metadata under the Studio identity, so the bundled data-liberation-agent workspace installs as a first-class plugin in both tools. Both marketplaces point their plugin source directly at ./packages/data-liberation-agent, resolved from the repo root, so no plugin-locating symlink is needed. Corrects the plugin configs that would otherwise have broken installation: the Claude plugin now registers its MCP server via the valid mcpServers key (pointing at the canonical ./.mcp.json) instead of an unrecognized mcp field, the Codex plugin's skills/mcpServers paths are plugin-root-relative rather than double-prefixed, and both declare GPL-2.0-or-later to match the host repo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SqSer8WNQM5AVDqyrTUFcC
d64c097 to
082b2f7
Compare
📊 Performance Test ResultsComparing 237dcf7 vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
nightnei
left a comment
There was a problem hiding this comment.
@borkweb I tested and everything is good except one thing - Studio is an npm workspace now, so dependencies are hoisted to the Studio repo root node_modules — they never make it into the copy from packages folder to ~/.claude/plugins/cache/studio/data-liberation.
AI recommendation is - bundle the MCP server into a self-contained file (esbuild) and point .mcp.json at node dist/..., or ship a package-lock.json inside packages/data-liberation-agent so the plugin installer restores deps in the cache. I think that bundling is the right direction.
|
@nightnei re:
Yeah, I agree with you! That's the approach I took with the blocks-engine for WP dependencies and it seems to work well. I'll make that tweak |
…tained ## Summary Replace the plugin's `npx tsx src/mcp-server.ts` entry with a committed, self-contained esbuild bundle (`dist/mcp-server.bundle.mjs`, 3.6 MB) that `.mcp.json` and `gemini-extension.json` run with a bare `node`. ## Why Studio is an npm workspace, so the package's dependencies hoist to the repo root node_modules and never make it into the plugin installer's verbatim copy under ~/.claude/plugins/cache/studio/data-liberation — the MCP server had nothing to import once installed. Bundling follows the same approach blocks-engine took for its published dist. ## How A new `scripts/build-mcp-bundle.mjs` (wired into `npm run build`, also exposed as `build:mcp-bundle`) inlines every dependency except `playwright` and `single-file-cli`, which stay external because they are only ever loaded through guarded dynamic imports that degrade with install guidance. Modules that resolve runtime assets (vendored PHP helpers, core-block-attrs.json, the block-fixer sidecar) via per-module `import.meta.url` are rewritten at build time to point back into the shipped src/ tree relative to the bundle. Root and package .gitignore rules re-include only the bundle from the otherwise-ignored dist/, and AGENTS.md documents the regenerate-and-commit requirement. ## Testing - [ ] Copy only git-tracked package files plus the bundle to a node_modules-free directory and run `node dist/mcp-server.bundle.mjs`: initialize handshake, tools/list (38 tools), `liberate_paths`, and a live `liberate_detect` fingerprint all succeed - [ ] Reinstall the plugin from the marketplace and confirm the data-liberation MCP server starts from the cache Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnrB7DbBXeTRFR5dfauBxs
## Summary The Buildkite Lint job now rebuilds the data-liberation MCP server bundle and fails if the committed `dist/mcp-server.bundle.mjs` doesn't match `src/`. ## Why The plugin installer copies the package from git verbatim, so the bundle is a committed artifact — the one thing that could silently drift is someone editing `src/` without regenerating it. This gate makes stale bundles impossible to merge. Longer term the bundle should move to a CI-published release channel (STU-1999); this check protects the interim. ## How Appended to `run-lint.sh` (the validation job, which already has deps installed): run `npm -w data-liberation run build:mcp-bundle`, then `git diff --exit-code` on the bundle with a fix-it hint on failure. The build is byte-deterministic for a given lockfile (verified by back-to-back rebuilds hashing identically), so a clean diff is a reliable freshness signal. ## Testing - [ ] The Lint job on this PR's next build runs the new step and passes against the committed bundle Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnrB7DbBXeTRFR5dfauBxs
## Summary `.mcp.json` and `gemini-extension.json` now start the data-liberation MCP server via `scripts/mcp-launcher.mjs`, which runs `src/mcp-server.ts` through tsx whenever the package's dependencies resolve and falls back to the committed self-contained bundle otherwise. ## Why Development environments should never run a stale build artifact — previously the manifests pointed straight at the bundle, so a dev checkout would serve old code until someone remembered to rebuild. The committed bundle itself stays: we verified empirically that plugin installs are plain git copies (Claude's installer restores npm deps only when a per-package package-lock.json exists, which this workspace package doesn't have, and Codex's installer never restores deps at all), so supporting plain `claude/codex plugin marketplace add Automattic/studio` requires a prebuilt artifact in the tree. ## How The launcher requires both tsx and a real dependency to resolve from the package root before choosing dev mode (two checks so a stray ~/node_modules can't fake it), spawns tsx with inherited stdio to preserve the MCP stdio transport, dynamically imports the bundle when deps don't resolve, and exits with setup guidance when neither is available. AGENTS.md documents the install-mechanics findings so the committed-bundle requirement doesn't get relitigated without them. ## Testing - [ ] Simulated plugin cache (git-tracked files only, no node_modules): launcher serves the bundle — initialize, 38 tools, tool calls succeed - [ ] Dev checkout with the bundle removed: launcher serves identically via tsx from src/ - [ ] Neither available: launcher exits 1 with actionable guidance - [ ] Rebuilt bundle from committed src is byte-identical to the committed one (CI freshness gate stays green) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnrB7DbBXeTRFR5dfauBxs
## Summary `startStudioPreview` now passes `--runtime sandbox` to `studio site create`; the MCP server bundle is rebuilt to match. ## Why Studio 1.12 defaults new sites to the native PHP runtime, where the `/wordpress` VFS mount does not exist. Every path the pipeline passes to `studio wp eval-file` (preview, media-install, post-install, site-finalize, data-install) is a VFS path, so imports failed with "does not exist" and `liberate_preview` rolled the site back. Pinning the Playground sandbox runtime restores the mount until those callers are made runtime-aware. ## Testing - [ ] `liberate_preview` on a fresh extraction creates the site and completes the WXR import instead of rolling back Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnrB7DbBXeTRFR5dfauBxs
## Summary Removes DISCOVERIES.md from the repo, gitignores it, and updates CONTRIBUTING.md to describe it as a local, untracked log. ## Why The discovery log is working-session scratch that grows without bound (77 KB and climbing) and doesn't belong in the published package or plugin installs. The CONTRIBUTING workflow keeps the discipline — contributors still record discoveries — but the log stays on their machine instead of in git history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnrB7DbBXeTRFR5dfauBxs
|
@nightnei - Alrighty, the MCP dist bundle is implemented. The unfortunate state of Claude Code and Codex plugins is that we cannot keep the dist bundle as part of a CI process for building the dist. Technically, there is a way, but the experience for people adding the plugins to their own harness would be poor. Committing the dist allows someone to: claude plugin marketplace add Automattic/studioThere's a couple of notes on the dev experience for us:
|
| // Why: the Claude/Codex plugin installer copies this package's files into | ||
| // ~/.claude/plugins/cache/... WITHOUT node_modules — and inside the Studio npm | ||
| // workspace the dependencies are hoisted to the repo root anyway — so a | ||
| // `npx tsx src/mcp-server.ts` entry has nothing to import once installed as a |
There was a problem hiding this comment.
Hm, the same problem we shoudl expect in prod for npx tsx scripts/... in skills/replicate-theme/SKILL.md. It seems it's necessary to migarte runnign npx to MCP or some another way
There was a problem hiding this comment.
@nightnei - Alrighty, the MCP dist bundle is implemented. The unfortunate state of Claude Code and Codex plugins is that we cannot keep the dist bundle as part of a CI process for building the dist. Technically, there is a way, but the experience for people adding the plugins to their own harness would be poor. Committing the dist allows someone to:
claude plugin marketplace add Automattic/studioThere's a couple of notes on the dev experience for us:
- Buildkite has a check that ensures that the data-liberation-agent dist is not stale
- We have a
scripts/mcp-launcher.mjsthat ensures that a dev checkout of the repository will use thesrc/directory viatsxrather than deferring to the dist.
I see your points, let's start with commiting it, it isn't huge and isolated inside packages, so I don't see problems 👍
- ✅ Claude Code - everythign went great, also I tried Theme replication and teh site looks great.
- ⛔️ Codex - the mcp isn't starting. Here is the explanation from AI, so we shoudl fix it in this PR:
Root cause: Codex doesn't expand ${CLAUDE_PLUGIN_ROOT}. The shared .mcp.json uses it, and Codex passes it through literally — codex mcp get data-liberation shows:
args: ${CLAUDE_PLUGIN_ROOT}/scripts/mcp-launcher.mjs
cwd: ~/.codex/plugins/cache/studio/data-liberation/1.0.0/${CLAUDE_PLUGIN_ROOT} ← doesn't exist
Codex's own bundled plugins (codex-security, openai-developers) use plugin-root-relative paths instead: "args": ["./mcp/server.mjs"], "cwd": ".". The launcher itself is fine — invoked relatively from the Codex plugin cache, it completes the MCP initialize handshake immediately.
Suggested fix: Claude needs ${CLAUDE_PLUGIN_ROOT} (no cwd guarantee), Codex needs relative paths — and the plugin manifests already diverge per harness, so point .codex-plugin/plugin.json's "mcpServers" at a Codex-specific file (e.g. ./.mcp.codex.json):
{
"mcpServers": {
"data-liberation": {
"command": "node",
"args": ["./scripts/mcp-launcher.mjs"],
"cwd": "."
}
}
}
…leaving the existing .mcp.json for Claude untouched.
- I think this PR can be merged, as soon as we address Codex. But as a follow up thing necessary to take a look at this and most likelly address in the follow-up PR. Could you please take a look at it?
|
@borkweb I went ahead and pushed the fix to your PR to move things forward. Hope that’s okay :) The last issue that should be investigated/fixed outside this PR is the third point from here. |
|
Looks great! Merging |
Related issues
How AI was used in this PR
Authored with Claude Code. The marketplace/plugin configs were written and then verified against the actual Claude Code plugin spec and the on-disk
bork/vercelreference plugins to confirm path-resolution behavior (Claude vs. Codex both resolve a pluginsourcerelative to the marketplace repo root). The MCP-registration, relative-path, and license corrections came out of that verification pass.Proposed Changes
Promotes the Studio repo root to a plugin marketplace for both Claude (
.claude-plugin/marketplace.json) and Codex (.agents/), and rehomes thedata-liberationplugin metadata under the Studio identity. With this, the bundledpackages/data-liberation-agentworkspace installs as a first-class plugin in both Claude Code and Codex — surfacing its skills, commands, and the data-liberation MCP server — without consumers needing to add a separate marketplace.Both marketplaces point their plugin
sourcedirectly at./packages/data-liberation-agent, resolved from the repo root, so no plugin-locating symlink is required. This also corrects configuration that would otherwise have broken installation:mcpServerskey (pointing at the canonical./.mcp.json) instead of an unrecognizedmcpfield.skills/mcpServerspaths are plugin-root-relative rather than double-prefixed with the package path.GPL-2.0-or-laterto match the host repo.No application code changes; this is marketplace/plugin configuration only.
Testing Instructions
/plugin marketplace add .) and installdata-liberation. Confirm the plugin's skills/commands appear and thedata-liberationMCP server registers and starts..agents/and installdata-liberation; confirm the plugin resolves topackages/data-liberation-agentand its skills + MCP server load.