Commit e554b0e
feat(ai): MCP connectors — expose CMS tools to external AI clients (CoreBunch#109)
* chore(ai): add MCP SDK + fetch-to-node, scope SDK isolation gate to drivers only
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): MCP connector model, token hashing, store + migration 018
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(content): extract actor-agnostic page-tree service; plugin handler delegates
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): page-tree MCP tools + capability-filtered MCP registry
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): MCP bearer auth, capability-scoped server, Web-standard HTTP transport mounted at /_instatic/mcp
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): MCP connector CRUD handlers (list/create/revoke) with privilege floor + audit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(admin): MCP connectors tab — create/list/revoke with capability picker + client snippets
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(ai): document MCP connectors; scope SDK gate note; fix Button title gate
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(ai): deterministic end-to-end MCP flow (stateless initialize/list/call) over the real handler
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(ai): type the MCP e2e RPC responses (drop explicit any)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(admin): MCP connector dialog reuses the Role dialog capability picker
The capabilities list collided with the AiPage two-column field grid. Switch the
dialog to the SiteCreateDialog form layout + the UsersPage capability-picker
styles + shared CAPABILITY_META, grouped Read/Write, for visual consistency
with the role management dialog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(admin): extract shared CapabilityPicker used by Role + MCP dialogs
Move the role dialog's capability checklist into a shared, presentational
`@admin/shared/CapabilityPicker` (+ co-located CAPABILITY_META). RoleDialog and
the MCP connector dialog both consume it, so they stay visually consistent and
the markup/CSS lives in one place. UsersPage.module.css keeps only the role
identity grid; users/utils/capabilities.ts keeps only the role grouping.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): MCP live editor bridge — expose the full tool catalog via an open editor
Browser-execution editor tools (HTML/CSS authoring, design tokens, page
lifecycle, content CRUD, code assets, live-DOM reads) have no server impl —
they run in the editor app. This relays MCP calls for those tools to the
connector owner's open editor and awaits the result, reusing the chat bridge
machinery wholesale (createBridge / resolveBridgeToolResult / /tool-result).
- server/ai/mcp/editorBridge.ts: per-user bridge registry + NDJSON stream.
- GET /admin/api/ai/editor-bridge: the stream the editor holds open.
- MCP registry now exposes the full deduped catalog; server.ts routes browser
tools to the live bridge (clear 'open the editor' error when none connected).
- src/admin/.../useEditorMcpBridge.ts: editor-side listener (mounted in SitePage)
running executeAgentTool + postToolResult, with reconnect.
- MCP picker expanded to all tool-backed editing capabilities.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): headless read_styles tool + clearer MCP results + write-flush + sharper descriptions
Addresses live-use friction:
- read_styles: returns the design system as a CSS stylesheet (tokens + classes)
read straight from the DB via the publisher emitters — headless, no editor,
no snapshot. Replaces the snapshot-dependent list_tokens (excluded from MCP
along with list_breakpoints, which silently returned nothing over MCP).
- MCP success with no payload now reads as {ok:true}, not the literal 'null',
so mutating tools (deleteNode) report unambiguous success.
- The editor bridge flushes the draft save after a write tool, so a follow-up
headless read sees the change instead of stale state (fixes the duplicatePage
'looked like it failed' race).
- Sharpened read_page_tree/mutate_page_tree descriptions to steer the agent
(headless-by-id vs editor HTML/CSS authoring; pointer to read_styles).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai): forward MCP tool images (render_snapshot PNG was dropped)
The MCP CallTool result emitted only text, discarding output.images — so
render_snapshot's screenshot never reached the client. Forward images as MCP
image content blocks. Adds an integration test relaying render_snapshot through
the editor bridge and asserting the PNG arrives as an image block.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai): delete headless read_page_tree/mutate_page_tree — one source of truth
These MCP tools (added earlier this branch) edited the DB directly, creating a
second copy of each page with identical node ids. Mixed with the open editor's
browser tools they desynced, and the editor's autosave clobbered the headless
write (data loss the agent reproduced). Superseded by the live editor bridge:
ALL page editing now goes through the editor store (browser tools), persisted by
the existing save-flush. Removed the two tools + their registry wiring; tests
repointed to the headless content reads. treeService stays — the plugin RPC
(cms.content.tree.*) still uses it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai): editor MCP bridge self-heals — never permanently stops on auth blip
The reconnect loop set stopped=true on a 401/403, so a transient auth failure
during a dev-server restart silently killed the bridge for the whole session
('editor open but MCP can't see it'). Now it always retries (longer backoff for
auth failures) and logs on connect, so the bridge survives restarts and brief
session blips.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): Tier 1 MCP tool improvements — list_breakpoints, read_styles summary, insertHtml created map
- list_breakpoints: headless (reads the site shell) — fixes 'guessed desktop';
replaces the snapshot-based version that returned nothing over MCP.
- read_styles: format:'summary' returns a compact class catalog (selector +
referenced token vars, no declarations) for cheap scanning before editing.
- insertHtml now returns 'created' — every inserted node { id, moduleId,
classes } — so the agent can target nested nodes without re-dumping the tree.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ai): Tier 3 — get_context tool (editor connected? which templates wrap pages?)
Headless orientation tool: reports whether a live editor is connected (browser
tools need it) and which everywhere/post-type templates wrap pages — the two
things that silently confused live use. Reads bridge presence + template rows
from the DB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(ai): consistent snake_case + site_/content_ prefix for all agent/MCP tools
Standardizes the whole agent toolset (shared by the built-in panel and MCP) on
snake_case with a domain prefix, so the tool list self-groups and humans+agents
can tell at a glance what's site vs content:
insertHtml→site_insert_html, applyCss→site_apply_css, deleteNode→site_delete_node,
addPage→site_add_page, render_snapshot→site_render_snapshot, read_styles→site_read_styles,
create_document→content_create_document, list_collections→content_list_collections, …
get_context stays unprefixed (cross-cutting). The old site/content list_documents
name collision is resolved into distinct prefixed names.
Scoped strictly to agent tool-name references (server defs, both executors, the
panel row formatter, prompts, MCP registry/tools, SSOT gate, tests, docs) —
never the identically-spelled TreeOperation kinds or editor-store methods.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai): Tier 2 — scoped render_snapshot via iframe + importer no double-prop
render_snapshot: the canvas renders each breakpoint inside an <iframe> (the
data-breakpoint-id is on the host wrapper, the nodes live in the iframe's
document). The capture searched the host frame, so scoped node lookups never
matched and full captures grabbed the empty wrapper. Now it resolves the
breakpoint's iframe and uses contentDocument as the node-lookup + screenshot
root, and defaults to the active breakpoint (not the first DOM frame, which is
mobile in a mobile-first layout). Scoped site_render_snapshot({nodeId}) and the
right-viewport full capture now work.
HTML importer: a node that recurses into child nodes no longer also keeps a
flattened 'text' prop — children are the source of truth. Fixes a loop <a>
wrapping spans/tokens that got BOTH a text prop and children (ambiguous,
double-render risk). Mirrors the conditional heading/label rules.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai): site_insert_html 'created' was always empty (stale pre-insert snapshot)
runInsertHtml read activeDocumentNodes(store) from the snapshot captured BEFORE
insertImportedNodes ran, so the just-created nodes (and auto-created classes)
weren't in it — created came back []. Re-read fresh state after the insert.
Now created returns the full inserted subtree { id, moduleId, classes } with
resolved class names, so an agent can target nested nodes without re-reading the
tree. Regression test added.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ai): remove unused MCP review symbols
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent d32a262 commit e554b0e
99 files changed
Lines changed: 4445 additions & 1153 deletions
File tree
- docs
- features
- reference
- server
- ai
- content
- drivers/http
- handlers
- mcp
- connectors
- handlers
- tools
- transports
- tools
- content
- site
- db
- plugins/host/handlers
- repositories
- src
- __tests__
- agent
- ai
- architecture
- htmlImport
- server
- admin
- ai
- pages
- ai
- tabs
- content/agent
- site
- agent
- hooks
- panels/AgentPanel
- users
- components
- tabs
- utils
- core
- ai
- htmlImport
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
| 50 | + | |
| 51 | + | |
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
148 | 148 | | |
149 | 149 | | |
150 | 150 | | |
| 151 | + | |
151 | 152 | | |
152 | 153 | | |
153 | 154 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
0 commit comments