Skip to content

feat(mcp): [DO-NOT-MERGE-POC] add CMA plugin surface — /mcp/da (18 tools) + /mcp/eds #59

Draft
anfibiacreativa wants to merge 3 commits into
mainfrom
feat/cma-mcp-tools
Draft

feat(mcp): [DO-NOT-MERGE-POC] add CMA plugin surface — /mcp/da (18 tools) + /mcp/eds #59
anfibiacreativa wants to merge 3 commits into
mainfrom
feat/cma-mcp-tools

Conversation

@anfibiacreativa

Copy link
Copy Markdown
Member

Title

feat(mcp): add CMA plugin surface — /mcp/da (18 tools) + /mcp/eds (4 tools)

Summary

Adds the plugin surface that Claude Managed Agents (CMA) will register as remote MCP servers:
two dependency-free JSON-RPC-over-HTTP endpoints — POST /mcp/da (18 tools) and POST /mcp/eds
(4 tools). This closes the 5-of-18 tool gap so the plugin exposes exactly what the Skills Editor
advertises. It is the code deliverable for SITES-47970 (CMA Migration Phase 1). CMA-first:
branched off main with no AO code carried along.

Problem / root cause

/mcp/da previously exposed only 5 of the 18 tools the Skills Editor's da-tools bundle
advertises (content_list/read/create/update/delete). The other 13 existed only as native
Cloudflare-Worker tool bindings in the chat loop — there is no such worker under CMA, so an
authored agent that relied on them would silently lose functionality. The eds-preview bundle's
4 tools were likewise unavailable as an MCP endpoint.

What changed

  • src/mcp-server/handler.ts — dependency-free JSON-RPC MCP transport (initialize,
    tools/list, tools/call, ping) with safe error serialization (exposes message + status
    only; omits backend details).
  • src/mcp-server/da-registry.ts — full da-tools bundle (18 tools). The 13 added tools
    (content_copy/move, content_version_create/list, content_media/fragment,
    content_upload, da_get_skill/create_skill, da_list_agents/create_agent,
    da_embed_fragment, write_project_memory) reuse the existing DAAdminClient methods and the
    skills/agents/memory loaders — wiring, not new business logic.
  • src/mcp-server/eds-registry.tseds-preview bundle (4 preview/publish tools).
  • src/server.ts — wire /mcp/da and /mcp/eds routes with Authorization: Bearer
    pass-through (401 on missing token; 503 if the DAADMIN binding is absent).
  • da_embed_fragment — advertised in the catalog but never implemented; built fresh here,
    approval-gated, with path sanitization to prevent HTML-attribute injection.
  • da_get_skill — registered under the Skills-Editor-advertised name (native chat tool was
    da_read_skill).
  • Destructive tools carry a [REQUIRES_APPROVAL] description prefix; CMA's native always_ask
    gate is what enforces the pause.

How to test

  1. cd into the branch checkout, npm ci, then npm run dev (serves on http://localhost:4002).
  2. List DA tools — expect 18:
    curl -s -X POST http://localhost:4002/mcp/da
    -H "Authorization: Bearer test-token" -H "Content-Type: application/json"
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools | length'
  3. List EDS tools — expect 4: same call against /mcp/eds.
  4. Auth guard — expect HTTP 401 with no Authorization header.
  5. Unknown tool — tools/call with a bogus name returns JSON-RPC error -32602.
  6. npm test — expect the existing suite green (296 tests).

Risk / follow-ups

  • Scope boundary: this PR is the tool surface + MCP protocol, verified at the tools/list /
    dispatch level. End-to-end execution against real content (exp-workspace/frescopa) is NOT
    included — it needs the live stack running plus a valid IMS token (Phase 1 finish-out).
  • Pre-existing, not introduced here: tsc reports one error at src/server.ts:348 (sessionId
    possibly undefined in the telemetry block) that already exists on main.
  • Publishing /mcp/da at a stable public HTTPS URL + a CMA Vault static-bearer credential is a
    provisioning step (Phase 2), not part of this PR.

…tools)

- add src/mcp-server/handler.ts: dependency-free JSON-RPC MCP transport
  (initialize, tools/list, tools/call, ping) with safe error serialization
- add src/mcp-server/da-registry.ts: full da-tools bundle (18 tools) mirroring
  the native DAAdminClient ops and skills/agents/memory loaders
- add src/mcp-server/eds-registry.ts: eds-preview bundle (4 preview/publish tools)
- wire /mcp/da and /mcp/eds routes in server.ts with Bearer auth pass-through
- implement da_embed_fragment (previously advertised but never built),
  approval-gated, with path sanitization to prevent HTML-attribute injection
- register da_get_skill under the Skills Editor's advertised name

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@anfibiacreativa anfibiacreativa requested a review from mhaack July 15, 2026 09:14
@anfibiacreativa anfibiacreativa changed the title feat(mcp): add CMA plugin surface — /mcp/da (18 tools) + /mcp/eds feat(mcp): [DO-NOT-MERGE-POC] add CMA plugin surface — /mcp/da (18 tools) + /mcp/eds Jul 15, 2026
@anfibiacreativa anfibiacreativa marked this pull request as draft July 15, 2026 09:16
anfibiacreativa and others added 2 commits July 15, 2026 13:31
content_read/content_update now check whether the target page is open in a
canvas (via da-collab presence) and, if so, read/apply through the live
da-collab session — which persists to da-admin — instead of writing da-admin
directly, which would be clobbered by the live editor. Falls back to da-admin
when da-collab is unbound, unreachable, or no human has the doc open.

- collab-client: add hasOtherHumanParticipants() (awareness-based open check)
- da-registry: openCanvasCollab() helper + collab-aware content_read/update;
  createDAMCPRegistry takes optional { dacollab, daOrigin }
- server: pass env.DACOLLAB + env.DA_ORIGIN to the /mcp/da registry

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A headless MCP call can't tell whether a doc is open in a canvas, so the
open-doc detection probe (hasOtherHumanParticipants) was fragile — it raced
and failed open to da-admin, the exact clobber it aimed to prevent. Route
content_read and content_update through da-collab unconditionally (persists
to da-admin), falling back to da-admin only when da-collab is unbound or
unreachable. Removes the awareness probe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant