@wp-playground/mcp 3.1.51 → 3.1.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -82,7 +82,7 @@ The MCP server communicates with AI clients via stdio and with the browser via W
82
82
 
83
83
  ## WebMCP
84
84
 
85
- The [Playground Website](https://playground.wordpress.net/) also supports [WebMCP](https://github.com/webmachinelearning/webmcp) — a browser-native MCP proposal that exposes tools via `navigator.modelContext`. When a Playground site loads, its tools are registered automatically with no CLI or WebSocket bridge needed.
85
+ The [Playground Website](https://playground.wordpress.net/) also supports [WebMCP](https://github.com/webmachinelearning/webmcp) — a browser-native MCP proposal that exposes tools via `document.modelContext`. When a Playground site loads, its tools are registered automatically with no CLI or WebSocket bridge needed.
86
86
 
87
87
  > **Note:** WebMCP is still a draft proposal and not widely supported.
88
88
 
package/client.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./tool-executors-DX_lR1Jc.cjs"),b=5e3;function h(r,o){const c=crypto.randomUUID();let e=null,n="",t=null,l=!1;function s(u){const d=r.list(),p=JSON.stringify(d);p!==n&&(n=p,u.send(JSON.stringify({type:"register",tabId:c,sites:d})))}async function a(){try{const u=await fetch(`http://127.0.0.1:${o}/bridge-token`);if(!u.ok){f();return}const{token:d}=await u.json();e=new WebSocket(`ws://127.0.0.1:${o}?token=${d}`)}catch{f();return}e.addEventListener("open",()=>{var u;n="",s(e),(u=r.onConnect)==null||u.call(r)}),e.addEventListener("message",async u=>{let d;try{d=JSON.parse(u.data)}catch{return}if(d.type!=="command")return;const{id:p,method:w,args:_,siteSlug:S}=d;try{const y=await E(r,w,_||[],S,o);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",value:y}))}catch(y){const v=y instanceof Error?y.message:String(y);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",error:v}))}}),e.addEventListener("close",()=>{e=null,f()}),e.addEventListener("error",()=>{})}function f(){l||(t=setTimeout(a,b))}return a(),{notifySitesChanged:()=>{(e==null?void 0:e.readyState)===WebSocket.OPEN&&s(e)},stop:()=>{l=!0,t!==null&&(clearTimeout(t),t=null),e&&(e.close(),e=null)}}}async function E(r,o,c,e,n){if(o==="__open_site_in_new_tab"){const a=new URL(window.location.href);if(a.searchParams.set("mcp-port",String(n)),a.searchParams.set("site-slug",e),!window.open(a.toString(),"_blank"))throw new Error("Pop-up blocked by browser. The user must allow pop-ups for this site.");return!0}if(o==="__rename_site"){const[a]=c;return await r.rename(a),!0}if(o==="__save_site")return await r.saveInBrowser();const t=r.getClient();if(!t)throw new Error(`No active client for site: ${e}`);const s=i.createToolClient(t)[o];if(typeof s!="function")throw new Error(`Unknown method: ${o}`);return await s(...c)}const m=i.getSiteToolDefinitions();let g=null;function N(r){const c=r.list().find(e=>e.isActive);if(!c)throw new Error("No active Playground site");return c}function T(r){if(typeof navigator>"u"||!navigator.modelContext)return;g==null||g.abort(),g=new AbortController;const o=g.signal;function c(){const n=r.getClient();if(!n)throw new Error("No client for active site");return n}const e=Object.entries(i.toolDefinitions).map(([n,t])=>({name:n,description:t.description,inputSchema:i.paramsToJsonSchema(t.params),annotations:t.annotations,execute:async l=>{try{const s=i.toolExecutors[n];if(!s)return{error:`No executor for "${n}"`};const a=i.createToolClient(c());return await s(a,l)}catch(s){return{error:`${t.errorPrefix}: ${i.stringifyError(s)}`}}}}));e.push(...x(r));for(const n of e)navigator.modelContext.registerTool(n,{signal:o})}function x(r){const o=m.playground_list_sites,c=m.playground_save_in_browser,e=m.playground_rename_site,n=m.playground_get_website_url;return[{name:"playground_list_sites",description:o.description,annotations:o.annotations,execute:async()=>{try{return{connectedTabs:1,sites:r.list().map(t=>({siteId:t.slug,name:t.name,storage:i.formatStorageLabel(t.storage),isActive:t.isActive}))}}catch(t){return{error:`${o.errorPrefix}: ${i.stringifyError(t)}`}}}},{name:"playground_save_in_browser",description:c.description,annotations:c.annotations,execute:async()=>{try{const t=N(r),l=i.formatStorageLabel(t.storage);if(l!=="temporary")return{success:!0,alreadySaved:!0,siteId:t.slug,name:t.name,storage:l};const s=await r.saveInBrowser();return{success:!0,alreadySaved:!1,siteId:s.slug,name:t.name??s.slug,storage:i.formatStorageLabel(s.storage)}}catch(t){return{error:`${c.errorPrefix}: ${i.stringifyError(t)}`}}}},{name:"playground_rename_site",description:e.description,inputSchema:i.paramsToJsonSchema(e.params),annotations:e.annotations,execute:async t=>{try{const l=t.newName,a=r.list().find(f=>f.isActive);return await r.rename(l),{success:!0,siteId:a==null?void 0:a.slug,newName:l}}catch(l){return{error:`${e.errorPrefix}: ${i.stringifyError(l)}`}}}},{name:"playground_get_website_url",description:n.description,annotations:n.annotations,execute:async()=>{try{return{url:window.location.href}}catch(t){return{error:`${n.errorPrefix}: ${i.stringifyError(t)}`}}}}]}exports.registerWebMCPTools=T;exports.startMcpBridge=h;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./tool-executors-DX_lR1Jc.cjs"),b=5e3;function v(r,o){const a=crypto.randomUUID();let e=null,d="",t=null,l=!1;function c(f){const u=r.list(),p=JSON.stringify(u);p!==d&&(d=p,f.send(JSON.stringify({type:"register",tabId:a,sites:u})))}async function n(){try{const f=await fetch(`http://127.0.0.1:${o}/bridge-token`);if(!f.ok){s();return}const{token:u}=await f.json();e=new WebSocket(`ws://127.0.0.1:${o}?token=${u}`)}catch{s();return}e.addEventListener("open",()=>{var f;d="",c(e),(f=r.onConnect)==null||f.call(r)}),e.addEventListener("message",async f=>{let u;try{u=JSON.parse(f.data)}catch{return}if(u.type!=="command")return;const{id:p,method:w,args:_,siteSlug:S}=u;try{const g=await E(r,w,_||[],S,o);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",value:g}))}catch(g){const h=g instanceof Error?g.message:String(g);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",error:h}))}}),e.addEventListener("close",()=>{e=null,s()}),e.addEventListener("error",()=>{})}function s(){l||(t=setTimeout(n,b))}return n(),{notifySitesChanged:()=>{(e==null?void 0:e.readyState)===WebSocket.OPEN&&c(e)},stop:()=>{l=!0,t!==null&&(clearTimeout(t),t=null),e&&(e.close(),e=null)}}}async function E(r,o,a,e,d){if(o==="__open_site_in_new_tab"){const n=new URL(window.location.href);if(n.searchParams.set("mcp-port",String(d)),n.searchParams.set("site-slug",e),!window.open(n.toString(),"_blank"))throw new Error("Pop-up blocked by browser. The user must allow pop-ups for this site.");return!0}if(o==="__rename_site"){const[n]=a;return await r.rename(n),!0}if(o==="__save_site")return await r.saveInBrowser();const t=r.getClient();if(!t)throw new Error(`No active client for site: ${e}`);const c=i.createToolClient(t)[o];if(typeof c!="function")throw new Error(`Unknown method: ${o}`);return await c(...a)}const m=i.getSiteToolDefinitions();let y=null;function T(r){const a=r.list().find(e=>e.isActive);if(!a)throw new Error("No active Playground site");return a}async function N(r){const o=$();if(!o)return;y==null||y.abort(),y=new AbortController;const a=y,e=a.signal;function d(){const n=r.getClient();if(!n)throw new Error("No client for active site");return n}const t=Object.entries(i.toolDefinitions).map(([n,s])=>({name:n,description:s.description,inputSchema:i.paramsToJsonSchema(s.params),annotations:s.annotations,execute:async f=>{try{const u=i.toolExecutors[n];if(!u)return{error:`No executor for "${n}"`};const p=i.createToolClient(d());return await u(p,f)}catch(u){return{error:`${s.errorPrefix}: ${i.stringifyError(u)}`}}}}));t.push(...x(r));const l=await Promise.allSettled(t.map(async n=>{await o.registerTool(n,{signal:e})})),c=[];for(const[n,s]of l.entries())s.status==="rejected"&&c.push({name:t[n].name,reason:s.reason});if(c.length>0)throw a.abort(),y===a&&(y=null),new Error("Failed to register WebMCP tools: "+c.map(({name:n,reason:s})=>`${n} (${i.stringifyError(s)})`).join(", "))}function $(){if(typeof document>"u")return;const r=document.modelContext;return typeof(r==null?void 0:r.registerTool)=="function"?r:void 0}function x(r){const o=m.playground_list_sites,a=m.playground_save_in_browser,e=m.playground_rename_site,d=m.playground_get_website_url;return[{name:"playground_list_sites",description:o.description,annotations:o.annotations,execute:async()=>{try{return{connectedTabs:1,sites:r.list().map(t=>({siteId:t.slug,name:t.name,storage:i.formatStorageLabel(t.storage),isActive:t.isActive}))}}catch(t){return{error:`${o.errorPrefix}: ${i.stringifyError(t)}`}}}},{name:"playground_save_in_browser",description:a.description,annotations:a.annotations,execute:async()=>{try{const t=T(r),l=i.formatStorageLabel(t.storage);if(l!=="temporary")return{success:!0,alreadySaved:!0,siteId:t.slug,name:t.name,storage:l};const c=await r.saveInBrowser();return{success:!0,alreadySaved:!1,siteId:c.slug,name:t.name??c.slug,storage:i.formatStorageLabel(c.storage)}}catch(t){return{error:`${a.errorPrefix}: ${i.stringifyError(t)}`}}}},{name:"playground_rename_site",description:e.description,inputSchema:i.paramsToJsonSchema(e.params),annotations:e.annotations,execute:async t=>{try{const l=t.newName,n=r.list().find(s=>s.isActive);return await r.rename(l),{success:!0,siteId:n==null?void 0:n.slug,newName:l}}catch(l){return{error:`${e.errorPrefix}: ${i.stringifyError(l)}`}}}},{name:"playground_get_website_url",description:d.description,annotations:d.annotations,execute:async()=>{try{return{url:window.location.href}}catch(t){return{error:`${d.errorPrefix}: ${i.stringifyError(t)}`}}}}]}exports.registerWebMCPTools=N;exports.startMcpBridge=v;
2
2
  //# sourceMappingURL=client.cjs.map
package/client.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `navigator.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Navigator {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport function registerWebMCPTools(config: PlaygroundBridgeConfig): void {\n\tif (typeof navigator === 'undefined' || !navigator.modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst signal = registrationController.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tfor (const tool of tools) {\n\t\tnavigator.modelContext.registerTool(tool, { signal });\n\t}\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","tool","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":"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"}
1
+ {"version":3,"file":"client.cjs","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `document.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): Promise<void> | void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Document {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport async function registerWebMCPTools(\n\tconfig: PlaygroundBridgeConfig\n): Promise<void> {\n\tconst modelContext = getModelContext();\n\tif (!modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst controller = registrationController;\n\tconst signal = controller.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tconst results = await Promise.allSettled(\n\t\ttools.map(async (tool) => {\n\t\t\tawait modelContext.registerTool(tool, { signal });\n\t\t})\n\t);\n\tconst failedRegistrations: Array<{ name: string; reason: unknown }> = [];\n\tfor (const [index, result] of results.entries()) {\n\t\tif (result.status === 'rejected') {\n\t\t\tfailedRegistrations.push({\n\t\t\t\tname: tools[index].name,\n\t\t\t\treason: result.reason,\n\t\t\t});\n\t\t}\n\t}\n\tif (failedRegistrations.length > 0) {\n\t\tcontroller.abort();\n\t\tif (registrationController === controller) {\n\t\t\tregistrationController = null;\n\t\t}\n\t\tthrow new Error(\n\t\t\t'Failed to register WebMCP tools: ' +\n\t\t\t\tfailedRegistrations\n\t\t\t\t\t.map(\n\t\t\t\t\t\t({ name, reason }) =>\n\t\t\t\t\t\t\t`${name} (${stringifyError(reason)})`\n\t\t\t\t\t)\n\t\t\t\t\t.join(', ')\n\t\t);\n\t}\n}\n\nfunction getModelContext(): ModelContext | undefined {\n\tif (typeof document === 'undefined') {\n\t\treturn undefined;\n\t}\n\tconst modelContext = document.modelContext;\n\treturn typeof modelContext?.registerTool === 'function'\n\t\t? modelContext\n\t\t: undefined;\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","modelContext","getModelContext","controller","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","results","tool","failedRegistrations","index","result","reason","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":"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"}
package/client.js CHANGED
@@ -1,147 +1,168 @@
1
- import { c as _, t as N, a as x, s as m, b as S, f as w, g as $ } from "./tool-executors-DeiRNg9-.js";
2
- const T = 5e3;
3
- function I(r, o) {
4
- const a = crypto.randomUUID();
5
- let e = null, n = "", t = null, c = !1;
6
- function s(u) {
7
- const l = r.list(), p = JSON.stringify(l);
8
- p !== n && (n = p, u.send(JSON.stringify({ type: "register", tabId: a, sites: l })));
1
+ import { c as _, t as N, a as $, s as m, b as h, f as g, g as x } from "./tool-executors-DeiRNg9-.js";
2
+ const P = 5e3;
3
+ function A(r, o) {
4
+ const i = crypto.randomUUID();
5
+ let e = null, l = "", t = null, c = !1;
6
+ function a(d) {
7
+ const u = r.list(), f = JSON.stringify(u);
8
+ f !== l && (l = f, d.send(JSON.stringify({ type: "register", tabId: i, sites: u })));
9
9
  }
10
- async function i() {
10
+ async function n() {
11
11
  try {
12
- const u = await fetch(
12
+ const d = await fetch(
13
13
  `http://127.0.0.1:${o}/bridge-token`
14
14
  );
15
- if (!u.ok) {
16
- d();
15
+ if (!d.ok) {
16
+ s();
17
17
  return;
18
18
  }
19
- const { token: l } = await u.json();
20
- e = new WebSocket(`ws://127.0.0.1:${o}?token=${l}`);
19
+ const { token: u } = await d.json();
20
+ e = new WebSocket(`ws://127.0.0.1:${o}?token=${u}`);
21
21
  } catch {
22
- d();
22
+ s();
23
23
  return;
24
24
  }
25
25
  e.addEventListener("open", () => {
26
- var u;
27
- n = "", s(e), (u = r.onConnect) == null || u.call(r);
28
- }), e.addEventListener("message", async (u) => {
29
- let l;
26
+ var d;
27
+ l = "", a(e), (d = r.onConnect) == null || d.call(r);
28
+ }), e.addEventListener("message", async (d) => {
29
+ let u;
30
30
  try {
31
- l = JSON.parse(u.data);
31
+ u = JSON.parse(d.data);
32
32
  } catch {
33
33
  return;
34
34
  }
35
- if (l.type !== "command")
35
+ if (u.type !== "command")
36
36
  return;
37
- const { id: p, method: v, args: h, siteSlug: b } = l;
37
+ const { id: f, method: S, args: v, siteSlug: b } = u;
38
38
  try {
39
- const f = await C(
39
+ const y = await T(
40
40
  r,
41
- v,
42
- h || [],
41
+ S,
42
+ v || [],
43
43
  b,
44
44
  o
45
45
  );
46
- (e == null ? void 0 : e.readyState) === WebSocket.OPEN && e.send(JSON.stringify({ id: p, type: "response", value: f }));
47
- } catch (f) {
48
- const E = f instanceof Error ? f.message : String(f);
46
+ (e == null ? void 0 : e.readyState) === WebSocket.OPEN && e.send(JSON.stringify({ id: f, type: "response", value: y }));
47
+ } catch (y) {
48
+ const E = y instanceof Error ? y.message : String(y);
49
49
  (e == null ? void 0 : e.readyState) === WebSocket.OPEN && e.send(
50
50
  JSON.stringify({
51
- id: p,
51
+ id: f,
52
52
  type: "response",
53
53
  error: E
54
54
  })
55
55
  );
56
56
  }
57
57
  }), e.addEventListener("close", () => {
58
- e = null, d();
58
+ e = null, s();
59
59
  }), e.addEventListener("error", () => {
60
60
  });
61
61
  }
62
- function d() {
63
- c || (t = setTimeout(i, T));
62
+ function s() {
63
+ c || (t = setTimeout(n, P));
64
64
  }
65
- return i(), {
65
+ return n(), {
66
66
  notifySitesChanged: () => {
67
- (e == null ? void 0 : e.readyState) === WebSocket.OPEN && s(e);
67
+ (e == null ? void 0 : e.readyState) === WebSocket.OPEN && a(e);
68
68
  },
69
69
  stop: () => {
70
70
  c = !0, t !== null && (clearTimeout(t), t = null), e && (e.close(), e = null);
71
71
  }
72
72
  };
73
73
  }
74
- async function C(r, o, a, e, n) {
74
+ async function T(r, o, i, e, l) {
75
75
  if (o === "__open_site_in_new_tab") {
76
- const i = new URL(window.location.href);
77
- if (i.searchParams.set("mcp-port", String(n)), i.searchParams.set("site-slug", e), !window.open(i.toString(), "_blank"))
76
+ const n = new URL(window.location.href);
77
+ if (n.searchParams.set("mcp-port", String(l)), n.searchParams.set("site-slug", e), !window.open(n.toString(), "_blank"))
78
78
  throw new Error(
79
79
  "Pop-up blocked by browser. The user must allow pop-ups for this site."
80
80
  );
81
81
  return !0;
82
82
  }
83
83
  if (o === "__rename_site") {
84
- const [i] = a;
85
- return await r.rename(i), !0;
84
+ const [n] = i;
85
+ return await r.rename(n), !0;
86
86
  }
87
87
  if (o === "__save_site")
88
88
  return await r.saveInBrowser();
89
89
  const t = r.getClient();
90
90
  if (!t)
91
91
  throw new Error(`No active client for site: ${e}`);
92
- const s = _(t)[o];
93
- if (typeof s != "function")
92
+ const a = _(t)[o];
93
+ if (typeof a != "function")
94
94
  throw new Error(`Unknown method: ${o}`);
95
- return await s(...a);
95
+ return await a(...i);
96
96
  }
97
- const g = $();
98
- let y = null;
99
- function P(r) {
100
- const a = r.list().find((e) => e.isActive);
101
- if (!a)
97
+ const w = x();
98
+ let p = null;
99
+ function C(r) {
100
+ const i = r.list().find((e) => e.isActive);
101
+ if (!i)
102
102
  throw new Error("No active Playground site");
103
- return a;
103
+ return i;
104
104
  }
105
- function A(r) {
106
- if (typeof navigator > "u" || !navigator.modelContext)
105
+ async function D(r) {
106
+ const o = k();
107
+ if (!o)
107
108
  return;
108
- y == null || y.abort(), y = new AbortController();
109
- const o = y.signal;
110
- function a() {
109
+ p == null || p.abort(), p = new AbortController();
110
+ const i = p, e = i.signal;
111
+ function l() {
111
112
  const n = r.getClient();
112
113
  if (!n)
113
114
  throw new Error("No client for active site");
114
115
  return n;
115
116
  }
116
- const e = Object.entries(N).map(
117
- ([n, t]) => ({
117
+ const t = Object.entries(N).map(
118
+ ([n, s]) => ({
118
119
  name: n,
119
- description: t.description,
120
- inputSchema: S(t.params),
121
- annotations: t.annotations,
122
- execute: async (c) => {
120
+ description: s.description,
121
+ inputSchema: h(s.params),
122
+ annotations: s.annotations,
123
+ execute: async (d) => {
123
124
  try {
124
- const s = x[n];
125
- if (!s)
125
+ const u = $[n];
126
+ if (!u)
126
127
  return {
127
128
  error: `No executor for "${n}"`
128
129
  };
129
- const i = _(a());
130
- return await s(i, c);
131
- } catch (s) {
130
+ const f = _(l());
131
+ return await u(f, d);
132
+ } catch (u) {
132
133
  return {
133
- error: `${t.errorPrefix}: ${m(s)}`
134
+ error: `${s.errorPrefix}: ${m(u)}`
134
135
  };
135
136
  }
136
137
  }
137
138
  })
138
139
  );
139
- e.push(...k(r));
140
- for (const n of e)
141
- navigator.modelContext.registerTool(n, { signal: o });
140
+ t.push(...O(r));
141
+ const c = await Promise.allSettled(
142
+ t.map(async (n) => {
143
+ await o.registerTool(n, { signal: e });
144
+ })
145
+ ), a = [];
146
+ for (const [n, s] of c.entries())
147
+ s.status === "rejected" && a.push({
148
+ name: t[n].name,
149
+ reason: s.reason
150
+ });
151
+ if (a.length > 0)
152
+ throw i.abort(), p === i && (p = null), new Error(
153
+ "Failed to register WebMCP tools: " + a.map(
154
+ ({ name: n, reason: s }) => `${n} (${m(s)})`
155
+ ).join(", ")
156
+ );
157
+ }
158
+ function k() {
159
+ if (typeof document > "u")
160
+ return;
161
+ const r = document.modelContext;
162
+ return typeof (r == null ? void 0 : r.registerTool) == "function" ? r : void 0;
142
163
  }
143
- function k(r) {
144
- const o = g.playground_list_sites, a = g.playground_save_in_browser, e = g.playground_rename_site, n = g.playground_get_website_url;
164
+ function O(r) {
165
+ const o = w.playground_list_sites, i = w.playground_save_in_browser, e = w.playground_rename_site, l = w.playground_get_website_url;
145
166
  return [
146
167
  {
147
168
  name: "playground_list_sites",
@@ -154,7 +175,7 @@ function k(r) {
154
175
  sites: r.list().map((t) => ({
155
176
  siteId: t.slug,
156
177
  name: t.name,
157
- storage: w(t.storage),
178
+ storage: g(t.storage),
158
179
  isActive: t.isActive
159
180
  }))
160
181
  };
@@ -167,11 +188,11 @@ function k(r) {
167
188
  },
168
189
  {
169
190
  name: "playground_save_in_browser",
170
- description: a.description,
171
- annotations: a.annotations,
191
+ description: i.description,
192
+ annotations: i.annotations,
172
193
  execute: async () => {
173
194
  try {
174
- const t = P(r), c = w(t.storage);
195
+ const t = C(r), c = g(t.storage);
175
196
  if (c !== "temporary")
176
197
  return {
177
198
  success: !0,
@@ -180,17 +201,17 @@ function k(r) {
180
201
  name: t.name,
181
202
  storage: c
182
203
  };
183
- const s = await r.saveInBrowser();
204
+ const a = await r.saveInBrowser();
184
205
  return {
185
206
  success: !0,
186
207
  alreadySaved: !1,
187
- siteId: s.slug,
188
- name: t.name ?? s.slug,
189
- storage: w(s.storage)
208
+ siteId: a.slug,
209
+ name: t.name ?? a.slug,
210
+ storage: g(a.storage)
190
211
  };
191
212
  } catch (t) {
192
213
  return {
193
- error: `${a.errorPrefix}: ${m(t)}`
214
+ error: `${i.errorPrefix}: ${m(t)}`
194
215
  };
195
216
  }
196
217
  }
@@ -198,14 +219,14 @@ function k(r) {
198
219
  {
199
220
  name: "playground_rename_site",
200
221
  description: e.description,
201
- inputSchema: S(e.params),
222
+ inputSchema: h(e.params),
202
223
  annotations: e.annotations,
203
224
  execute: async (t) => {
204
225
  try {
205
- const c = t.newName, i = r.list().find((d) => d.isActive);
226
+ const c = t.newName, n = r.list().find((s) => s.isActive);
206
227
  return await r.rename(c), {
207
228
  success: !0,
208
- siteId: i == null ? void 0 : i.slug,
229
+ siteId: n == null ? void 0 : n.slug,
209
230
  newName: c
210
231
  };
211
232
  } catch (c) {
@@ -217,8 +238,8 @@ function k(r) {
217
238
  },
218
239
  {
219
240
  name: "playground_get_website_url",
220
- description: n.description,
221
- annotations: n.annotations,
241
+ description: l.description,
242
+ annotations: l.annotations,
222
243
  execute: async () => {
223
244
  try {
224
245
  return {
@@ -226,7 +247,7 @@ function k(r) {
226
247
  };
227
248
  } catch (t) {
228
249
  return {
229
- error: `${n.errorPrefix}: ${m(t)}`
250
+ error: `${l.errorPrefix}: ${m(t)}`
230
251
  };
231
252
  }
232
253
  }
@@ -234,7 +255,7 @@ function k(r) {
234
255
  ];
235
256
  }
236
257
  export {
237
- A as registerWebMCPTools,
238
- I as startMcpBridge
258
+ D as registerWebMCPTools,
259
+ A as startMcpBridge
239
260
  };
240
261
  //# sourceMappingURL=client.js.map
package/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `navigator.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Navigator {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport function registerWebMCPTools(config: PlaygroundBridgeConfig): void {\n\tif (typeof navigator === 'undefined' || !navigator.modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst signal = registrationController.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tfor (const tool of tools) {\n\t\tnavigator.modelContext.registerTool(tool, { signal });\n\t}\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","tool","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":";AA4BA,MAAMA,IAAwB;AAEvB,SAASC,EACfC,GACAC,GACkB;AAClB,QAAMC,IAAQ,OAAO,WAAA;AACrB,MAAIC,IAAuB,MACvBC,IAA0B,IAC1BC,IAAuD,MACvDC,IAAU;AAEd,WAASC,EAAsBC,GAAmB;AACjD,UAAMC,IAAQT,EAAO,KAAA,GACfU,IAAa,KAAK,UAAUD,CAAK;AACvC,IAAIC,MAAeN,MAGnBA,IAA0BM,GAC1BF,EAAO,KAAK,KAAK,UAAU,EAAE,MAAM,YAAY,OAAAN,GAAO,OAAAO,EAAA,CAAO,CAAC;AAAA,EAC/D;AAEA,iBAAeE,IAAU;AACxB,QAAI;AACH,YAAMC,IAAW,MAAM;AAAA,QACtB,oBAAoBX,CAAI;AAAA,MAAA;AAEzB,UAAI,CAACW,EAAS,IAAI;AACjB,QAAAC,EAAA;AACA;AAAA,MACD;AACA,YAAM,EAAE,OAAAC,EAAA,IAAU,MAAMF,EAAS,KAAA;AACjC,MAAAT,IAAK,IAAI,UAAU,kBAAkBF,CAAI,UAAUa,CAAK,EAAE;AAAA,IAC3D,QAAQ;AACP,MAAAD,EAAA;AACA;AAAA,IACD;AAEA,IAAAV,EAAG,iBAAiB,QAAQ,MAAM;;AACjC,MAAAC,IAA0B,IAC1BG,EAAsBJ,CAAG,IACzBY,IAAAf,EAAO,cAAP,QAAAe,EAAA,KAAAf;AAAA,IACD,CAAC,GAEDG,EAAG,iBAAiB,WAAW,OAAOa,MAAU;AAC/C,UAAIC;AACJ,UAAI;AACH,QAAAA,IAAU,KAAK,MAAMD,EAAM,IAAc;AAAA,MAC1C,QAAQ;AACP;AAAA,MACD;AACA,UAAIC,EAAQ,SAAS;AACpB;AAGD,YAAM,EAAE,IAAAC,GAAI,QAAAC,GAAQ,MAAAC,GAAM,UAAAC,MAAaJ;AACvC,UAAI;AACH,cAAMK,IAAQ,MAAMC;AAAA,UACnBvB;AAAA,UACAmB;AAAA,UACAC,KAAQ,CAAA;AAAA,UACRC;AAAA,UACApB;AAAA,QAAA;AAED,SAAIE,KAAA,gBAAAA,EAAI,gBAAe,UAAU,QAChCA,EAAG,KAAK,KAAK,UAAU,EAAE,IAAAe,GAAI,MAAM,YAAY,OAAAI,EAAA,CAAO,CAAC;AAAA,MAEzD,SAASE,GAAO;AACf,cAAMC,IACLD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AACtD,SAAIrB,KAAA,gBAAAA,EAAI,gBAAe,UAAU,QAChCA,EAAG;AAAA,UACF,KAAK,UAAU;AAAA,YACd,IAAAe;AAAA,YACA,MAAM;AAAA,YACN,OAAOO;AAAA,UAAA,CACP;AAAA,QAAA;AAAA,MAGJ;AAAA,IACD,CAAC,GAEDtB,EAAG,iBAAiB,SAAS,MAAM;AAClC,MAAAA,IAAK,MACLU,EAAA;AAAA,IACD,CAAC,GAEDV,EAAG,iBAAiB,SAAS,MAAM;AAAA,IAGnC,CAAC;AAAA,EACF;AAEA,WAASU,IAAoB;AAC5B,IAAIP,MAGJD,IAAiB,WAAWM,GAASb,CAAqB;AAAA,EAC3D;AAEA,SAAAa,EAAA,GAEO;AAAA,IACN,oBAAoB,MAAM;AACzB,OAAIR,KAAA,gBAAAA,EAAI,gBAAe,UAAU,QAChCI,EAAsBJ,CAAE;AAAA,IAE1B;AAAA,IACA,MAAM,MAAM;AACX,MAAAG,IAAU,IACND,MAAmB,SACtB,aAAaA,CAAc,GAC3BA,IAAiB,OAEdF,MACHA,EAAG,MAAA,GACHA,IAAK;AAAA,IAEP;AAAA,EAAA;AAEF;AAEA,eAAeoB,EACdvB,GACAmB,GACAC,GACAC,GACApB,GACmB;AACnB,MAAIkB,MAAW,0BAA0B;AACxC,UAAMO,IAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AAIxC,QAHAA,EAAI,aAAa,IAAI,YAAY,OAAOzB,CAAI,CAAC,GAC7CyB,EAAI,aAAa,IAAI,aAAaL,CAAQ,GAEtC,CADc,OAAO,KAAKK,EAAI,SAAA,GAAY,QAAQ;AAErD,YAAM,IAAI;AAAA,QACT;AAAA,MAAA;AAIF,WAAO;AAAA,EACR;AAEA,MAAIP,MAAW,iBAAiB;AAC/B,UAAM,CAACQ,CAAO,IAAIP;AAClB,iBAAMpB,EAAO,OAAO2B,CAAO,GACpB;AAAA,EACR;AAEA,MAAIR,MAAW;AACd,WAAO,MAAMnB,EAAO,cAAA;AAGrB,QAAM4B,IAAmB5B,EAAO,UAAA;AAChC,MAAI,CAAC4B;AACJ,UAAM,IAAI,MAAM,8BAA8BP,CAAQ,EAAE;AAIzD,QAAMQ,IADSC,EAAiBF,CAAgB,EAC9BT,CAA0B;AAC5C,MAAI,OAAOU,KAAO;AACjB,UAAM,IAAI,MAAM,mBAAmBV,CAAM,EAAE;AAE5C,SAAO,MAAOU,EAA6C,GAAGT,CAAI;AACnE;AC7KA,MAAMW,IAAsBC,EAAA;AAoC5B,IAAIC,IAAiD;AAErD,SAASC,EAAclC,GAAgC;AAEtD,QAAMmC,IADQnC,EAAO,KAAA,EACA,KAAK,CAACoC,MAAMA,EAAE,QAAQ;AAC3C,MAAI,CAACD;AACJ,UAAM,IAAI,MAAM,2BAA2B;AAE5C,SAAOA;AACR;AAEO,SAASE,EAAoBrC,GAAsC;AACzE,MAAI,OAAO,YAAc,OAAe,CAAC,UAAU;AAClD;AAID,EAAAiC,KAAA,QAAAA,EAAwB,SACxBA,IAAyB,IAAI,gBAAA;AAC7B,QAAMK,IAASL,EAAuB;AAEtC,WAASM,IAAoC;AAC5C,UAAMC,IAASxC,EAAO,UAAA;AACtB,QAAI,CAACwC;AACJ,YAAM,IAAI,MAAM,2BAA2B;AAE5C,WAAOA;AAAA,EACR;AAGA,QAAMC,IAA4B,OAAO,QAAQC,CAAe,EAAE;AAAA,IACjE,CAAC,CAACC,GAAMC,CAAG,OAAO;AAAA,MACjB,MAAAD;AAAA,MACA,aAAaC,EAAI;AAAA,MACjB,aAAaC,EAAmBD,EAAI,MAAM;AAAA,MAC1C,aAAaA,EAAI;AAAA,MACjB,SAAS,OAAOE,MAAU;AACzB,YAAI;AACH,gBAAMC,IAAWC,EAAcL,CAAI;AACnC,cAAI,CAACI;AACJ,mBAAO;AAAA,cACN,OAAO,oBAAoBJ,CAAI;AAAA,YAAA;AAGjC,gBAAMM,IAAUnB,EAAiBS,GAAiB;AAClD,iBAAO,MAAMQ,EAASE,GAASH,CAAK;AAAA,QACrC,SAAStB,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAGoB,EAAI,WAAW,KAAKM,EAAe1B,CAAK,CAAC;AAAA,UAAA;AAAA,QAErD;AAAA,MACD;AAAA,IAAA;AAAA,EACD;AAID,EAAAiB,EAAM,KAAK,GAAGU,EAA0BnD,CAAM,CAAC;AAE/C,aAAWoD,KAAQX;AAClB,cAAU,aAAa,aAAaW,GAAM,EAAE,QAAAd,GAAQ;AAEtD;AAEA,SAASa,EACRnD,GACqB;AACrB,QAAMqD,IAAUtB,EAAoB,uBAC9BuB,IAAUvB,EAAoB,4BAC9BwB,IAAYxB,EAAoB,wBAChCyB,IAAgBzB,EAAoB;AAE1C,SAAO;AAAA,IACN;AAAA,MACC,MAAM;AAAA,MACN,aAAasB,EAAQ;AAAA,MACrB,aAAaA,EAAQ;AAAA,MACrB,SAAS,YAAY;AACpB,YAAI;AACH,iBAAO;AAAA,YACN,eAAe;AAAA,YACf,OAAOrD,EAAO,KAAA,EAAO,IAAI,CAACoC,OAAO;AAAA,cAChC,QAAQA,EAAE;AAAA,cACV,MAAMA,EAAE;AAAA,cACR,SAASqB,EAAmBrB,EAAE,OAAO;AAAA,cACrC,UAAUA,EAAE;AAAA,YAAA,EACX;AAAA,UAAA;AAAA,QAEJ,SAASZ,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAG6B,EAAQ,WAAW,KAAKH,EAAe1B,CAAK,CAAC;AAAA,UAAA;AAAA,QAEzD;AAAA,MACD;AAAA,IAAA;AAAA,IAED;AAAA,MACC,MAAM;AAAA,MACN,aAAa8B,EAAQ;AAAA,MACrB,aAAaA,EAAQ;AAAA,MACrB,SAAS,YAAY;AACpB,YAAI;AACH,gBAAMI,IAAOxB,EAAclC,CAAM,GAC3B2D,IAAUF,EAAmBC,EAAK,OAAO;AAC/C,cAAIC,MAAY;AACf,mBAAO;AAAA,cACN,SAAS;AAAA,cACT,cAAc;AAAA,cACd,QAAQD,EAAK;AAAA,cACb,MAAMA,EAAK;AAAA,cACX,SAAAC;AAAA,YAAA;AAGF,gBAAMC,IAAQ,MAAM5D,EAAO,cAAA;AAC3B,iBAAO;AAAA,YACN,SAAS;AAAA,YACT,cAAc;AAAA,YACd,QAAQ4D,EAAM;AAAA,YACd,MAAMF,EAAK,QAAQE,EAAM;AAAA,YACzB,SAASH,EAAmBG,EAAM,OAAO;AAAA,UAAA;AAAA,QAE3C,SAASpC,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAG8B,EAAQ,WAAW,KAAKJ,EAAe1B,CAAK,CAAC;AAAA,UAAA;AAAA,QAEzD;AAAA,MACD;AAAA,IAAA;AAAA,IAED;AAAA,MACC,MAAM;AAAA,MACN,aAAa+B,EAAU;AAAA,MACvB,aAAaV,EAAmBU,EAAU,MAAM;AAAA,MAChD,aAAaA,EAAU;AAAA,MACvB,SAAS,OAAOT,MAAU;AACzB,YAAI;AACH,gBAAMnB,IAAUmB,EAAM,SAEhBe,IADQ7D,EAAO,KAAA,EACI,KAAK,CAACoC,MAAMA,EAAE,QAAQ;AAC/C,uBAAMpC,EAAO,OAAO2B,CAAO,GACpB;AAAA,YACN,SAAS;AAAA,YACT,QAAQkC,KAAA,gBAAAA,EAAY;AAAA,YACpB,SAAAlC;AAAA,UAAA;AAAA,QAEF,SAASH,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAG+B,EAAU,WAAW,KAAKL,EAAe1B,CAAK,CAAC;AAAA,UAAA;AAAA,QAE3D;AAAA,MACD;AAAA,IAAA;AAAA,IAED;AAAA,MACC,MAAM;AAAA,MACN,aAAagC,EAAc;AAAA,MAC3B,aAAaA,EAAc;AAAA,MAC3B,SAAS,YAAY;AACpB,YAAI;AACH,iBAAO;AAAA,YACN,KAAK,OAAO,SAAS;AAAA,UAAA;AAAA,QAEvB,SAAShC,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAGgC,EAAc,WAAW,KAAKN,EAAe1B,CAAK,CAAC;AAAA,UAAA;AAAA,QAE/D;AAAA,MACD;AAAA,IAAA;AAAA,EACD;AAEF;"}
1
+ {"version":3,"file":"client.js","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `document.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): Promise<void> | void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Document {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport async function registerWebMCPTools(\n\tconfig: PlaygroundBridgeConfig\n): Promise<void> {\n\tconst modelContext = getModelContext();\n\tif (!modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst controller = registrationController;\n\tconst signal = controller.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tconst results = await Promise.allSettled(\n\t\ttools.map(async (tool) => {\n\t\t\tawait modelContext.registerTool(tool, { signal });\n\t\t})\n\t);\n\tconst failedRegistrations: Array<{ name: string; reason: unknown }> = [];\n\tfor (const [index, result] of results.entries()) {\n\t\tif (result.status === 'rejected') {\n\t\t\tfailedRegistrations.push({\n\t\t\t\tname: tools[index].name,\n\t\t\t\treason: result.reason,\n\t\t\t});\n\t\t}\n\t}\n\tif (failedRegistrations.length > 0) {\n\t\tcontroller.abort();\n\t\tif (registrationController === controller) {\n\t\t\tregistrationController = null;\n\t\t}\n\t\tthrow new Error(\n\t\t\t'Failed to register WebMCP tools: ' +\n\t\t\t\tfailedRegistrations\n\t\t\t\t\t.map(\n\t\t\t\t\t\t({ name, reason }) =>\n\t\t\t\t\t\t\t`${name} (${stringifyError(reason)})`\n\t\t\t\t\t)\n\t\t\t\t\t.join(', ')\n\t\t);\n\t}\n}\n\nfunction getModelContext(): ModelContext | undefined {\n\tif (typeof document === 'undefined') {\n\t\treturn undefined;\n\t}\n\tconst modelContext = document.modelContext;\n\treturn typeof modelContext?.registerTool === 'function'\n\t\t? modelContext\n\t\t: undefined;\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","modelContext","getModelContext","controller","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","results","tool","failedRegistrations","index","result","reason","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":";AA4BA,MAAMA,IAAwB;AAEvB,SAASC,EACfC,GACAC,GACkB;AAClB,QAAMC,IAAQ,OAAO,WAAA;AACrB,MAAIC,IAAuB,MACvBC,IAA0B,IAC1BC,IAAuD,MACvDC,IAAU;AAEd,WAASC,EAAsBC,GAAmB;AACjD,UAAMC,IAAQT,EAAO,KAAA,GACfU,IAAa,KAAK,UAAUD,CAAK;AACvC,IAAIC,MAAeN,MAGnBA,IAA0BM,GAC1BF,EAAO,KAAK,KAAK,UAAU,EAAE,MAAM,YAAY,OAAAN,GAAO,OAAAO,EAAA,CAAO,CAAC;AAAA,EAC/D;AAEA,iBAAeE,IAAU;AACxB,QAAI;AACH,YAAMC,IAAW,MAAM;AAAA,QACtB,oBAAoBX,CAAI;AAAA,MAAA;AAEzB,UAAI,CAACW,EAAS,IAAI;AACjB,QAAAC,EAAA;AACA;AAAA,MACD;AACA,YAAM,EAAE,OAAAC,EAAA,IAAU,MAAMF,EAAS,KAAA;AACjC,MAAAT,IAAK,IAAI,UAAU,kBAAkBF,CAAI,UAAUa,CAAK,EAAE;AAAA,IAC3D,QAAQ;AACP,MAAAD,EAAA;AACA;AAAA,IACD;AAEA,IAAAV,EAAG,iBAAiB,QAAQ,MAAM;;AACjC,MAAAC,IAA0B,IAC1BG,EAAsBJ,CAAG,IACzBY,IAAAf,EAAO,cAAP,QAAAe,EAAA,KAAAf;AAAA,IACD,CAAC,GAEDG,EAAG,iBAAiB,WAAW,OAAOa,MAAU;AAC/C,UAAIC;AACJ,UAAI;AACH,QAAAA,IAAU,KAAK,MAAMD,EAAM,IAAc;AAAA,MAC1C,QAAQ;AACP;AAAA,MACD;AACA,UAAIC,EAAQ,SAAS;AACpB;AAGD,YAAM,EAAE,IAAAC,GAAI,QAAAC,GAAQ,MAAAC,GAAM,UAAAC,MAAaJ;AACvC,UAAI;AACH,cAAMK,IAAQ,MAAMC;AAAA,UACnBvB;AAAA,UACAmB;AAAA,UACAC,KAAQ,CAAA;AAAA,UACRC;AAAA,UACApB;AAAA,QAAA;AAED,SAAIE,KAAA,gBAAAA,EAAI,gBAAe,UAAU,QAChCA,EAAG,KAAK,KAAK,UAAU,EAAE,IAAAe,GAAI,MAAM,YAAY,OAAAI,EAAA,CAAO,CAAC;AAAA,MAEzD,SAASE,GAAO;AACf,cAAMC,IACLD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AACtD,SAAIrB,KAAA,gBAAAA,EAAI,gBAAe,UAAU,QAChCA,EAAG;AAAA,UACF,KAAK,UAAU;AAAA,YACd,IAAAe;AAAA,YACA,MAAM;AAAA,YACN,OAAOO;AAAA,UAAA,CACP;AAAA,QAAA;AAAA,MAGJ;AAAA,IACD,CAAC,GAEDtB,EAAG,iBAAiB,SAAS,MAAM;AAClC,MAAAA,IAAK,MACLU,EAAA;AAAA,IACD,CAAC,GAEDV,EAAG,iBAAiB,SAAS,MAAM;AAAA,IAGnC,CAAC;AAAA,EACF;AAEA,WAASU,IAAoB;AAC5B,IAAIP,MAGJD,IAAiB,WAAWM,GAASb,CAAqB;AAAA,EAC3D;AAEA,SAAAa,EAAA,GAEO;AAAA,IACN,oBAAoB,MAAM;AACzB,OAAIR,KAAA,gBAAAA,EAAI,gBAAe,UAAU,QAChCI,EAAsBJ,CAAE;AAAA,IAE1B;AAAA,IACA,MAAM,MAAM;AACX,MAAAG,IAAU,IACND,MAAmB,SACtB,aAAaA,CAAc,GAC3BA,IAAiB,OAEdF,MACHA,EAAG,MAAA,GACHA,IAAK;AAAA,IAEP;AAAA,EAAA;AAEF;AAEA,eAAeoB,EACdvB,GACAmB,GACAC,GACAC,GACApB,GACmB;AACnB,MAAIkB,MAAW,0BAA0B;AACxC,UAAMO,IAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AAIxC,QAHAA,EAAI,aAAa,IAAI,YAAY,OAAOzB,CAAI,CAAC,GAC7CyB,EAAI,aAAa,IAAI,aAAaL,CAAQ,GAEtC,CADc,OAAO,KAAKK,EAAI,SAAA,GAAY,QAAQ;AAErD,YAAM,IAAI;AAAA,QACT;AAAA,MAAA;AAIF,WAAO;AAAA,EACR;AAEA,MAAIP,MAAW,iBAAiB;AAC/B,UAAM,CAACQ,CAAO,IAAIP;AAClB,iBAAMpB,EAAO,OAAO2B,CAAO,GACpB;AAAA,EACR;AAEA,MAAIR,MAAW;AACd,WAAO,MAAMnB,EAAO,cAAA;AAGrB,QAAM4B,IAAmB5B,EAAO,UAAA;AAChC,MAAI,CAAC4B;AACJ,UAAM,IAAI,MAAM,8BAA8BP,CAAQ,EAAE;AAIzD,QAAMQ,IADSC,EAAiBF,CAAgB,EAC9BT,CAA0B;AAC5C,MAAI,OAAOU,KAAO;AACjB,UAAM,IAAI,MAAM,mBAAmBV,CAAM,EAAE;AAE5C,SAAO,MAAOU,EAA6C,GAAGT,CAAI;AACnE;AC7KA,MAAMW,IAAsBC,EAAA;AAoC5B,IAAIC,IAAiD;AAErD,SAASC,EAAclC,GAAgC;AAEtD,QAAMmC,IADQnC,EAAO,KAAA,EACA,KAAK,CAACoC,MAAMA,EAAE,QAAQ;AAC3C,MAAI,CAACD;AACJ,UAAM,IAAI,MAAM,2BAA2B;AAE5C,SAAOA;AACR;AAEA,eAAsBE,EACrBrC,GACgB;AAChB,QAAMsC,IAAeC,EAAA;AACrB,MAAI,CAACD;AACJ;AAID,EAAAL,KAAA,QAAAA,EAAwB,SACxBA,IAAyB,IAAI,gBAAA;AAC7B,QAAMO,IAAaP,GACbQ,IAASD,EAAW;AAE1B,WAASE,IAAoC;AAC5C,UAAMC,IAAS3C,EAAO,UAAA;AACtB,QAAI,CAAC2C;AACJ,YAAM,IAAI,MAAM,2BAA2B;AAE5C,WAAOA;AAAA,EACR;AAGA,QAAMC,IAA4B,OAAO,QAAQC,CAAe,EAAE;AAAA,IACjE,CAAC,CAACC,GAAMC,CAAG,OAAO;AAAA,MACjB,MAAAD;AAAA,MACA,aAAaC,EAAI;AAAA,MACjB,aAAaC,EAAmBD,EAAI,MAAM;AAAA,MAC1C,aAAaA,EAAI;AAAA,MACjB,SAAS,OAAOE,MAAU;AACzB,YAAI;AACH,gBAAMC,IAAWC,EAAcL,CAAI;AACnC,cAAI,CAACI;AACJ,mBAAO;AAAA,cACN,OAAO,oBAAoBJ,CAAI;AAAA,YAAA;AAGjC,gBAAMM,IAAUtB,EAAiBY,GAAiB;AAClD,iBAAO,MAAMQ,EAASE,GAASH,CAAK;AAAA,QACrC,SAASzB,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAGuB,EAAI,WAAW,KAAKM,EAAe7B,CAAK,CAAC;AAAA,UAAA;AAAA,QAErD;AAAA,MACD;AAAA,IAAA;AAAA,EACD;AAID,EAAAoB,EAAM,KAAK,GAAGU,EAA0BtD,CAAM,CAAC;AAE/C,QAAMuD,IAAU,MAAM,QAAQ;AAAA,IAC7BX,EAAM,IAAI,OAAOY,MAAS;AACzB,YAAMlB,EAAa,aAAakB,GAAM,EAAE,QAAAf,GAAQ;AAAA,IACjD,CAAC;AAAA,EAAA,GAEIgB,IAAgE,CAAA;AACtE,aAAW,CAACC,GAAOC,CAAM,KAAKJ,EAAQ;AACrC,IAAII,EAAO,WAAW,cACrBF,EAAoB,KAAK;AAAA,MACxB,MAAMb,EAAMc,CAAK,EAAE;AAAA,MACnB,QAAQC,EAAO;AAAA,IAAA,CACf;AAGH,MAAIF,EAAoB,SAAS;AAChC,UAAAjB,EAAW,MAAA,GACPP,MAA2BO,MAC9BP,IAAyB,OAEpB,IAAI;AAAA,MACT,sCACCwB,EACE;AAAA,QACA,CAAC,EAAE,MAAAX,GAAM,QAAAc,EAAA,MACR,GAAGd,CAAI,KAAKO,EAAeO,CAAM,CAAC;AAAA,MAAA,EAEnC,KAAK,IAAI;AAAA,IAAA;AAGf;AAEA,SAASrB,IAA4C;AACpD,MAAI,OAAO,WAAa;AACvB;AAED,QAAMD,IAAe,SAAS;AAC9B,SAAO,QAAOA,KAAA,gBAAAA,EAAc,iBAAiB,aAC1CA,IACA;AACJ;AAEA,SAASgB,EACRtD,GACqB;AACrB,QAAM6D,IAAU9B,EAAoB,uBAC9B+B,IAAU/B,EAAoB,4BAC9BgC,IAAYhC,EAAoB,wBAChCiC,IAAgBjC,EAAoB;AAE1C,SAAO;AAAA,IACN;AAAA,MACC,MAAM;AAAA,MACN,aAAa8B,EAAQ;AAAA,MACrB,aAAaA,EAAQ;AAAA,MACrB,SAAS,YAAY;AACpB,YAAI;AACH,iBAAO;AAAA,YACN,eAAe;AAAA,YACf,OAAO7D,EAAO,KAAA,EAAO,IAAI,CAACoC,OAAO;AAAA,cAChC,QAAQA,EAAE;AAAA,cACV,MAAMA,EAAE;AAAA,cACR,SAAS6B,EAAmB7B,EAAE,OAAO;AAAA,cACrC,UAAUA,EAAE;AAAA,YAAA,EACX;AAAA,UAAA;AAAA,QAEJ,SAASZ,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAGqC,EAAQ,WAAW,KAAKR,EAAe7B,CAAK,CAAC;AAAA,UAAA;AAAA,QAEzD;AAAA,MACD;AAAA,IAAA;AAAA,IAED;AAAA,MACC,MAAM;AAAA,MACN,aAAasC,EAAQ;AAAA,MACrB,aAAaA,EAAQ;AAAA,MACrB,SAAS,YAAY;AACpB,YAAI;AACH,gBAAMI,IAAOhC,EAAclC,CAAM,GAC3BmE,IAAUF,EAAmBC,EAAK,OAAO;AAC/C,cAAIC,MAAY;AACf,mBAAO;AAAA,cACN,SAAS;AAAA,cACT,cAAc;AAAA,cACd,QAAQD,EAAK;AAAA,cACb,MAAMA,EAAK;AAAA,cACX,SAAAC;AAAA,YAAA;AAGF,gBAAMC,IAAQ,MAAMpE,EAAO,cAAA;AAC3B,iBAAO;AAAA,YACN,SAAS;AAAA,YACT,cAAc;AAAA,YACd,QAAQoE,EAAM;AAAA,YACd,MAAMF,EAAK,QAAQE,EAAM;AAAA,YACzB,SAASH,EAAmBG,EAAM,OAAO;AAAA,UAAA;AAAA,QAE3C,SAAS5C,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAGsC,EAAQ,WAAW,KAAKT,EAAe7B,CAAK,CAAC;AAAA,UAAA;AAAA,QAEzD;AAAA,MACD;AAAA,IAAA;AAAA,IAED;AAAA,MACC,MAAM;AAAA,MACN,aAAauC,EAAU;AAAA,MACvB,aAAaf,EAAmBe,EAAU,MAAM;AAAA,MAChD,aAAaA,EAAU;AAAA,MACvB,SAAS,OAAOd,MAAU;AACzB,YAAI;AACH,gBAAMtB,IAAUsB,EAAM,SAEhBoB,IADQrE,EAAO,KAAA,EACI,KAAK,CAAC,MAAM,EAAE,QAAQ;AAC/C,uBAAMA,EAAO,OAAO2B,CAAO,GACpB;AAAA,YACN,SAAS;AAAA,YACT,QAAQ0C,KAAA,gBAAAA,EAAY;AAAA,YACpB,SAAA1C;AAAA,UAAA;AAAA,QAEF,SAASH,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAGuC,EAAU,WAAW,KAAKV,EAAe7B,CAAK,CAAC;AAAA,UAAA;AAAA,QAE3D;AAAA,MACD;AAAA,IAAA;AAAA,IAED;AAAA,MACC,MAAM;AAAA,MACN,aAAawC,EAAc;AAAA,MAC3B,aAAaA,EAAc;AAAA,MAC3B,SAAS,YAAY;AACpB,YAAI;AACH,iBAAO;AAAA,YACN,KAAK,OAAO,SAAS;AAAA,UAAA;AAAA,QAEvB,SAASxC,GAAO;AACf,iBAAO;AAAA,YACN,OAAO,GAAGwC,EAAc,WAAW,KAAKX,EAAe7B,CAAK,CAAC;AAAA,UAAA;AAAA,QAE/D;AAAA,MACD;AAAA,IAAA;AAAA,EACD;AAEF;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-playground/mcp",
3
- "version": "3.1.51",
3
+ "version": "3.1.52",
4
4
  "description": "MCP server for WordPress Playground - enables AI agents to interact with the WordPress Playground website.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,10 +39,10 @@
39
39
  "@modelcontextprotocol/sdk": "^1.27.0",
40
40
  "ws": "8.21.0",
41
41
  "zod": "^4.3",
42
- "@wp-playground/remote": "3.1.51",
43
- "@php-wasm/universal": "3.1.51"
42
+ "@wp-playground/remote": "3.1.52",
43
+ "@php-wasm/universal": "3.1.52"
44
44
  },
45
- "gitHead": "a18fe6b003d7c8ab12441297eca0f525cce7e3e6",
45
+ "gitHead": "a6ed3872674399baa47c2f55fe1e660633fc8051",
46
46
  "packageManager": "npm@10.9.2",
47
47
  "overrides": {
48
48
  "rollup": "^4.34.6",
package/webmcp.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WebMCP registration for WordPress Playground.
3
3
  *
4
- * Registers playground tools with `navigator.modelContext` (the
4
+ * Registers playground tools with `document.modelContext` (the
5
5
  * Chrome WebMCP API) so that browser-side AI agents can interact
6
6
  * with the running Playground site.
7
7
  */
@@ -25,13 +25,13 @@ interface ModelContext {
25
25
  }): void;
26
26
  registerTool(tool: ModelContextTool, options?: {
27
27
  signal?: AbortSignal;
28
- }): void;
28
+ }): Promise<void> | void;
29
29
  readonly tools: ModelContextTool[];
30
30
  }
31
31
  declare global {
32
- interface Navigator {
32
+ interface Document {
33
33
  modelContext?: ModelContext;
34
34
  }
35
35
  }
36
- export declare function registerWebMCPTools(config: PlaygroundBridgeConfig): void;
36
+ export declare function registerWebMCPTools(config: PlaygroundBridgeConfig): Promise<void>;
37
37
  export {};