@wp-playground/mcp 3.1.62 → 3.1.63

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/client.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./tool-executors-CkOyDA_-.cjs"),b=require("@php-wasm/logger"),T=5e3;function E(n,c){const l=crypto.randomUUID();let r=null,t="",d=null,o=!1;function a(f){const s=n.list(),p=JSON.stringify(s);p!==t&&(t=p,f.send(JSON.stringify({type:"register",tabId:l,sites:s})))}async function e(){try{const f=await fetch(`http://127.0.0.1:${c}/bridge-token`);if(!f.ok){i();return}const{token:s}=await f.json();r=new WebSocket(`ws://127.0.0.1:${c}?token=${s}`)}catch{i();return}r.addEventListener("open",()=>{var f;t="",a(r),(f=n.onConnect)==null||f.call(n)}),r.addEventListener("message",async f=>{let s;try{s=JSON.parse(f.data)}catch{return}if(s.type!=="command")return;const{id:p,method:w,args:y,siteSlug:S}=s;try{const m=await P(n,w,y||[],S,c);(r==null?void 0:r.readyState)===WebSocket.OPEN&&r.send(JSON.stringify({id:p,type:"response",value:m}))}catch(m){const v=m instanceof Error?m.message:String(m);(r==null?void 0:r.readyState)===WebSocket.OPEN&&r.send(JSON.stringify({id:p,type:"response",error:v}))}}),r.addEventListener("close",()=>{r=null,i()}),r.addEventListener("error",()=>{})}function i(){o||(d=setTimeout(e,T))}return e(),{notifySitesChanged:()=>{(r==null?void 0:r.readyState)===WebSocket.OPEN&&a(r)},stop:()=>{o=!0,d!==null&&(clearTimeout(d),d=null),r&&(r.close(),r=null)}}}async function P(n,c,l,r,t){if(c==="__open_site_in_new_tab"){const e=new URL(window.location.href);if(e.searchParams.set("mcp-port",String(t)),e.searchParams.set("site-slug",r),!window.open(e.toString(),"_blank"))throw new Error("Pop-up blocked by browser. The user must allow pop-ups for this site.");return!0}if(c==="__rename_site"){const[e]=l;return await n.rename(e),!0}if(c==="__save_site")return await n.saveInBrowser();const d=n.getClient();if(!d)throw new Error(`No active client for site: ${r}`);const a=u.createToolClient(d)[c];if(typeof a!="function")throw new Error(`Unknown method: ${c}`);return await a(...l)}const h=u.getSiteToolDefinitions();let g=null;function C(n){const l=n.list().find(r=>r.isActive);if(!l)throw new Error("No active Playground site");return l}async function x(n){const c=_();if(!c)return;g==null||g.abort(),g=new AbortController;const l=g,r=l.signal;function t(){const e=n.getClient();if(!e)throw new Error("No client for active site");return e}const d=Object.entries(u.toolDefinitions).map(([e,i])=>({name:e,description:i.description,inputSchema:u.paramsToJsonSchema(i.params),annotations:i.annotations,execute:async(f,s)=>{var p,w;try{(p=s==null?void 0:s.signal)==null||p.throwIfAborted();const y=u.toolExecutors[e];if(!y)return{error:`No executor for "${e}"`};const S=u.createToolClient(t()),m=await y(S,f,s==null?void 0:s.signal);return(w=s==null?void 0:s.signal)==null||w.throwIfAborted(),m}catch(y){return{error:`${i.errorPrefix}: ${u.stringifyError(y)}`}}}}));d.push(...$(n));const o=await Promise.allSettled(d.map(async e=>{await c.registerTool(e,{signal:r})})),a=[];for(const[e,i]of o.entries())i.status==="rejected"&&a.push({name:d[e].name,reason:i.reason});if(a.length>0)throw l.abort(),g===l&&(g=null),new Error("Failed to register WebMCP tools: "+a.map(({name:e,reason:i})=>`${e} (${u.stringifyError(i)})`).join(", "))}function _(){if(typeof document>"u")return;const n=document.modelContext;return typeof(n==null?void 0:n.registerTool)=="function"?n:void 0}function $(n){const c=h.playground_list_sites,l=h.playground_save_in_browser,r=h.playground_rename_site,t=h.playground_get_website_url;return[{name:"playground_list_sites",description:c.description,inputSchema:u.paramsToJsonSchema(c.params),annotations:c.annotations,execute:async(d,o)=>{var a;try{return(a=o==null?void 0:o.signal)==null||a.throwIfAborted(),{connectedTabs:1,sites:n.list().map(e=>({siteId:e.slug,name:e.name,storage:u.formatStorageLabel(e.storage),isActive:e.isActive}))}}catch(e){return{error:`${c.errorPrefix}: ${u.stringifyError(e)}`}}}},{name:"playground_save_in_browser",description:l.description,inputSchema:u.paramsToJsonSchema(l.params),annotations:l.annotations,execute:async(d,o)=>{var a,e;try{(a=o==null?void 0:o.signal)==null||a.throwIfAborted();const i=C(n),f=u.formatStorageLabel(i.storage);if(f!=="temporary")return{success:!0,alreadySaved:!0,siteId:i.slug,name:i.name,storage:f};const s=await n.saveInBrowser();return(e=o==null?void 0:o.signal)==null||e.throwIfAborted(),{success:!0,alreadySaved:!1,siteId:s.slug,name:i.name??s.slug,storage:u.formatStorageLabel(s.storage)}}catch(i){return{error:`${l.errorPrefix}: ${u.stringifyError(i)}`}}}},{name:"playground_rename_site",description:r.description,inputSchema:u.paramsToJsonSchema(r.params),annotations:r.annotations,execute:async(d,o)=>{var a,e;try{(a=o==null?void 0:o.signal)==null||a.throwIfAborted();const i=d.newName,s=n.list().find(p=>p.isActive);return await n.rename(i),(e=o==null?void 0:o.signal)==null||e.throwIfAborted(),{success:!0,siteId:s==null?void 0:s.slug,newName:i}}catch(i){return{error:`${r.errorPrefix}: ${u.stringifyError(i)}`}}}},{name:"playground_get_website_url",description:t.description,inputSchema:u.paramsToJsonSchema(t.params),annotations:t.annotations,execute:async(d,o)=>{var a;try{return(a=o==null?void 0:o.signal)==null||a.throwIfAborted(),{url:window.location.href}}catch(e){return{error:`${t.errorPrefix}: ${u.stringifyError(e)}`}}}}]}function I(n){const c=_();if(!c)return{stop:()=>{}};const l=c,r=N();let t=null,d=!1,o="";async function a(e){if(d)return;const i=JSON.stringify(e);if(i===o)return;o=i,t==null||t.abort(),t=new AbortController;const{signal:f}=t;for(const s of e){if(r.has(s.name)){b.logger.warn(`Skipping the WebMCP tool "${s.name}" registered by the site: Playground already provides a tool under that name.`);continue}try{await l.registerTool(M(n,s),{signal:f})}catch(p){b.logger.warn(`Failed to register the site's WebMCP tool "${s.name}": ${u.stringifyError(p)}`)}if(f.aborted)return}}return n.onWebMCPToolsChanged(e=>{a(e)}).catch(e=>{b.logger.warn("Failed to observe the site's WebMCP tools:",e)}),{stop(){d=!0,t==null||t.abort(),t=null,o=""}}}function N(){return new Set([...Object.keys(u.toolDefinitions),...Object.keys(u.getSiteToolDefinitions())])}function M(n,c){return{name:c.name,description:c.description,inputSchema:c.inputSchema,annotations:c.annotations,execute:async(l,r)=>{const t=r==null?void 0:r.signal,d=crypto.randomUUID(),o=()=>{n.cancelWebMCPTool(d).catch(a=>{b.logger.warn("Failed to cancel the site tool:",a)})};try{t==null||t.throwIfAborted(),t==null||t.addEventListener("abort",o,{once:!0});const a=await n.callWebMCPTool(c.name,l??{},d);return t==null||t.throwIfAborted(),a}catch(a){return{error:`Error running the WordPress site's "${c.name}" tool: ${u.stringifyError(a)}`}}finally{t==null||t.removeEventListener("abort",o)}}}}exports.registerWebMCPTools=x;exports.startMcpBridge=E;exports.startWebMCPSiteToolProxy=I;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("./tool-executors-CkOyDA_-.cjs"),b=require("@php-wasm/logger"),T=5e3;function E(a,c){const d=crypto.randomUUID();let e=null,o="",u=null,n=!1;function s(f){const i=a.list(),p=JSON.stringify(i);p!==o&&(o=p,f.send(JSON.stringify({type:"register",tabId:d,sites:i})))}async function t(){try{const f=await fetch(`http://127.0.0.1:${c}/bridge-token`);if(!f.ok){r();return}const{token:i}=await f.json();e=new WebSocket(`ws://127.0.0.1:${c}?token=${i}`)}catch{r();return}e.addEventListener("open",()=>{var f;o="",s(e),(f=a.onConnect)==null||f.call(a)}),e.addEventListener("message",async f=>{let i;try{i=JSON.parse(f.data)}catch{return}if(i.type!=="command")return;const{id:p,method:y,args:w,siteSlug:S}=i;try{const m=await C(a,y,w||[],S,c);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",value:m}))}catch(m){const v=m instanceof Error?m.message:String(m);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",error:v}))}}),e.addEventListener("close",()=>{e=null,r()}),e.addEventListener("error",()=>{})}function r(){n||(u=setTimeout(t,T))}return t(),{notifySitesChanged:()=>{(e==null?void 0:e.readyState)===WebSocket.OPEN&&s(e)},stop:()=>{n=!0,u!==null&&(clearTimeout(u),u=null),e&&(e.close(),e=null)}}}async function C(a,c,d,e,o){if(c==="__open_site_in_new_tab"){const t=new URL(window.location.href);if(t.searchParams.set("mcp-port",String(o)),t.searchParams.set("site-slug",e),!window.open(t.toString(),"_blank"))throw new Error("Pop-up blocked by browser. The user must allow pop-ups for this site.");return!0}if(c==="__rename_site"){const[t]=d;return await a.rename(t),!0}if(c==="__save_site")return await a.saveInBrowser();const u=a.getClient();if(!u)throw new Error(`No active client for site: ${e}`);const s=l.createToolClient(u)[c];if(typeof s!="function")throw new Error(`Unknown method: ${c}`);return await s(...d)}const h=l.getSiteToolDefinitions();let g=null;function P(a){const d=a.list().find(e=>e.isActive);if(!d)throw new Error("No active Playground site");return d}async function x(a){const c=_();if(!c)return;g==null||g.abort(),g=new AbortController;const d=g,e=d.signal;function o(){const t=a.getClient();if(!t)throw new Error("No client for active site");return t}const u=Object.entries(l.toolDefinitions).map(([t,r])=>({name:t,description:r.description,inputSchema:l.paramsToJsonSchema(r.params),annotations:r.annotations,execute:async(f,i)=>{var p,y;try{(p=i==null?void 0:i.signal)==null||p.throwIfAborted();const w=l.toolExecutors[t];if(!w)return{error:`No executor for "${t}"`};const S=l.createToolClient(o()),m=await w(S,f,i==null?void 0:i.signal);return(y=i==null?void 0:i.signal)==null||y.throwIfAborted(),m}catch(w){return{error:`${r.errorPrefix}: ${l.stringifyError(w)}`}}}}));u.push(...$(a));const n=await Promise.allSettled(u.map(async t=>{await c.registerTool(t,{signal:e})})),s=[];for(const[t,r]of n.entries())r.status==="rejected"&&s.push({name:u[t].name,reason:r.reason});if(s.length>0)throw d.abort(),g===d&&(g=null),new Error("Failed to register WebMCP tools: "+s.map(({name:t,reason:r})=>`${t} (${l.stringifyError(r)})`).join(", "))}function _(){if(typeof document>"u")return;const a=document.modelContext;return typeof(a==null?void 0:a.registerTool)=="function"?a:void 0}function $(a){const c=h.playground_list_sites,d=h.playground_save_in_browser,e=h.playground_rename_site,o=h.playground_get_website_url;return[{name:"playground_list_sites",description:c.description,inputSchema:l.paramsToJsonSchema(c.params),annotations:c.annotations,execute:async(u,n)=>{var s;try{return(s=n==null?void 0:n.signal)==null||s.throwIfAborted(),{connectedTabs:1,sites:a.list().map(t=>({siteId:t.slug,name:t.name,storage:l.formatStorageLabel(t.storage),isActive:t.isActive}))}}catch(t){return{error:`${c.errorPrefix}: ${l.stringifyError(t)}`}}}},{name:"playground_save_in_browser",description:d.description,inputSchema:l.paramsToJsonSchema(d.params),annotations:d.annotations,execute:async(u,n)=>{var s,t;try{(s=n==null?void 0:n.signal)==null||s.throwIfAborted();const r=P(a),f=l.formatStorageLabel(r.storage);if(f!=="temporary")return{success:!0,alreadySaved:!0,siteId:r.slug,name:r.name,storage:f};const i=await a.saveInBrowser();return(t=n==null?void 0:n.signal)==null||t.throwIfAborted(),{success:!0,alreadySaved:!1,siteId:i.slug,name:r.name??i.slug,storage:l.formatStorageLabel(i.storage)}}catch(r){return{error:`${d.errorPrefix}: ${l.stringifyError(r)}`}}}},{name:"playground_rename_site",description:e.description,inputSchema:l.paramsToJsonSchema(e.params),annotations:e.annotations,execute:async(u,n)=>{var s,t;try{(s=n==null?void 0:n.signal)==null||s.throwIfAborted();const r=u.newName,i=a.list().find(p=>p.isActive);return await a.rename(r),(t=n==null?void 0:n.signal)==null||t.throwIfAborted(),{success:!0,siteId:i==null?void 0:i.slug,newName:r}}catch(r){return{error:`${e.errorPrefix}: ${l.stringifyError(r)}`}}}},{name:"playground_get_website_url",description:o.description,inputSchema:l.paramsToJsonSchema(o.params),annotations:o.annotations,execute:async(u,n)=>{var s;try{return(s=n==null?void 0:n.signal)==null||s.throwIfAborted(),{url:window.location.href}}catch(t){return{error:`${o.errorPrefix}: ${l.stringifyError(t)}`}}}}]}function I(a){const c=_();if(!c)return{stop:()=>{}};const d=c,e=M();let o=null,u=!1,n="",s=null;async function t(r){if(u)return;const f=JSON.stringify(r);if(f===n)return;n=f,o==null||o.abort(),o=new AbortController;const{signal:i}=o;for(const p of r){if(e.has(p.name)){b.logger.warn(`Skipping the WebMCP tool "${p.name}" registered by the site: Playground already provides a tool under that name.`);continue}try{await d.registerTool(N(a,p),{signal:i})}catch(y){b.logger.warn(`Failed to register the site's WebMCP tool "${p.name}": ${l.stringifyError(y)}`)}if(i.aborted)return}}return a.onWebMCPToolsChanged(r=>{t(r)}).then(r=>{u?r():s=r}).catch(r=>{b.logger.warn("Failed to observe the site's WebMCP tools:",r)}),{stop(){u=!0,s==null||s(),s=null,o==null||o.abort(),o=null,n=""}}}function M(){return new Set([...Object.keys(l.toolDefinitions),...Object.keys(l.getSiteToolDefinitions())])}function N(a,c){return{name:c.name,description:c.description,inputSchema:c.inputSchema,annotations:c.annotations,execute:async(d,e)=>{const o=e==null?void 0:e.signal,u=crypto.randomUUID(),n=()=>{a.cancelWebMCPTool(u).catch(s=>{b.logger.warn("Failed to cancel the site tool:",s)})};try{o==null||o.throwIfAborted(),o==null||o.addEventListener("abort",n,{once:!0});const s=await a.callWebMCPTool(c.name,d??{},u);return o==null||o.throwIfAborted(),s}catch(s){return{error:`Error running the WordPress site's "${c.name}" tool: ${l.stringifyError(s)}`}}finally{o==null||o.removeEventListener("abort",n)}}}}exports.getModelContext=_;exports.registerWebMCPTools=x;exports.startMcpBridge=E;exports.startWebMCPSiteToolProxy=I;
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","../../../../packages/playground/mcp/src/webmcp-site-tools.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\nexport interface 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?: {\n\t\treadOnlyHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t\tconsequentialHint?: boolean;\n\t\tuntrustedContentHint?: boolean;\n\t\tdebugging?: boolean;\n\t};\n}\n\ninterface ModelContextClient {\n\tsignal?: AbortSignal;\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tconst result = await executor(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tinput,\n\t\t\t\t\t\tclient?.signal\n\t\t\t\t\t);\n\t\t\t\t\tclient?.signal?.throwIfAborted();\n\t\t\t\t\treturn result;\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\nexport function 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\tinputSchema: paramsToJsonSchema(listDef.params),\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(saveDef.params),\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\t// Storage operations do not currently expose an interrupt API.\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(websiteUrlDef.params),\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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","/**\n * Re-advertises the WebMCP tools registered inside the WordPress document as\n * tools of the Playground page itself.\n *\n * A plugin running in Playground can register tools with\n * `document.modelContext`, but that registry lives in a nested,\n * service-worker-served iframe an agent never sees. This proxy mirrors those\n * tools onto the top-level `document.modelContext` and forwards each call back\n * into the WordPress document:\n *\n * agent → document.modelContext (this page)\n * → PlaygroundClient.callWebMCPTool() (Comlink)\n * → remote frame (postMessage)\n * → WordPress document (the plugin's execute())\n */\n\nimport type {\n\tPlaygroundClient,\n\tWebMCPToolDescriptor,\n} from '@wp-playground/remote';\nimport { logger } from '@php-wasm/logger';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport type { ModelContextTool } from './webmcp';\nimport { getModelContext } from './webmcp';\n\nexport interface WebMCPSiteToolProxy {\n\tstop(): void;\n}\n\n/**\n * Starts mirroring `client`'s in-site WebMCP tools onto this page.\n *\n * Every announcement carries the full tool list, so the previous registration\n * is aborted and the new list registered in its place. Returns a handle that\n * unregisters everything it registered.\n */\nexport function startWebMCPSiteToolProxy(\n\tclient: PlaygroundClient\n): WebMCPSiteToolProxy {\n\tconst maybeModelContext = getModelContext();\n\tif (!maybeModelContext) {\n\t\t// Nothing can be advertised, so there is nothing to observe for.\n\t\treturn { stop: () => {} };\n\t}\n\t// `register` is hoisted, so it does not inherit the narrowing above.\n\tconst modelContext = maybeModelContext;\n\n\tconst reserved = reservedToolNames();\n\tlet registration: AbortController | null = null;\n\tlet stopped = false;\n\tlet registeredDescriptors = '';\n\n\tasync function register(tools: WebMCPToolDescriptor[]) {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\t// Registration is asynchronous, so a burst of announcements could\n\t\t// otherwise re-register an unchanged list several times over.\n\t\tconst descriptors = JSON.stringify(tools);\n\t\tif (descriptors === registeredDescriptors) {\n\t\t\treturn;\n\t\t}\n\t\tregisteredDescriptors = descriptors;\n\n\t\tregistration?.abort();\n\t\tregistration = new AbortController();\n\t\tconst { signal } = registration;\n\n\t\tfor (const tool of tools) {\n\t\t\tif (reserved.has(tool.name)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Skipping the WebMCP tool \"${tool.name}\" registered by ` +\n\t\t\t\t\t\t`the site: Playground already provides a tool under ` +\n\t\t\t\t\t\t`that name.`\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait modelContext.registerTool(proxyTool(client, tool), {\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to register the site's WebMCP tool ` +\n\t\t\t\t\t\t`\"${tool.name}\": ${stringifyError(error)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (signal.aborted) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tclient\n\t\t.onWebMCPToolsChanged((tools: WebMCPToolDescriptor[]) => {\n\t\t\tvoid register(tools);\n\t\t})\n\t\t.catch((error) => {\n\t\t\tlogger.warn(\"Failed to observe the site's WebMCP tools:\", error);\n\t\t});\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tregistration?.abort();\n\t\t\tregistration = null;\n\t\t\tregisteredDescriptors = '';\n\t\t},\n\t};\n}\n\n/**\n * Names Playground registers itself. A site tool may not take one of them\n * over, or an agent asking to execute PHP would reach the site's plugin\n * instead.\n */\nfunction reservedToolNames(): Set<string> {\n\treturn new Set([\n\t\t...Object.keys(toolDefinitions),\n\t\t...Object.keys(getSiteToolDefinitions()),\n\t]);\n}\n\nfunction proxyTool(\n\tclient: PlaygroundClient,\n\ttool: WebMCPToolDescriptor\n): ModelContextTool {\n\treturn {\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tinputSchema: tool.inputSchema,\n\t\tannotations: tool.annotations,\n\t\texecute: async (input, clientArg) => {\n\t\t\tconst signal = clientArg?.signal;\n\t\t\tconst callId = crypto.randomUUID();\n\t\t\tconst abort = () => {\n\t\t\t\tvoid client.cancelWebMCPTool(callId).catch((error) => {\n\t\t\t\t\tlogger.warn('Failed to cancel the site tool:', error);\n\t\t\t\t});\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tsignal?.addEventListener('abort', abort, { once: true });\n\t\t\t\tconst result = await client.callWebMCPTool(\n\t\t\t\t\ttool.name,\n\t\t\t\t\tinput ?? {},\n\t\t\t\t\tcallId\n\t\t\t\t);\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\terror:\n\t\t\t\t\t\t`Error running the WordPress site's \"${tool.name}\" ` +\n\t\t\t\t\t\t`tool: ${stringifyError(error)}`,\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tsignal?.removeEventListener('abort', abort);\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","result","_b","stringifyError","createSiteManagementTools","results","tool","failedRegistrations","index","reason","listDef","saveDef","renameDef","websiteUrlDef","_input","formatStorageLabel","site","storage","saved","activeSite","startWebMCPSiteToolProxy","maybeModelContext","reserved","reservedToolNames","registration","registeredDescriptors","register","descriptors","logger","proxyTool","clientArg","callId","abort"],"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","../../../../packages/playground/mcp/src/webmcp-site-tools.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\nexport interface 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?: {\n\t\treadOnlyHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t\tconsequentialHint?: boolean;\n\t\tuntrustedContentHint?: boolean;\n\t\tdebugging?: boolean;\n\t};\n}\n\ninterface ModelContextClient {\n\tsignal?: AbortSignal;\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tconst result = await executor(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tinput,\n\t\t\t\t\t\tclient?.signal\n\t\t\t\t\t);\n\t\t\t\t\tclient?.signal?.throwIfAborted();\n\t\t\t\t\treturn result;\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\nexport function 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\tinputSchema: paramsToJsonSchema(listDef.params),\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(saveDef.params),\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\t// Storage operations do not currently expose an interrupt API.\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(websiteUrlDef.params),\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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","/**\n * Re-advertises the WebMCP tools registered inside the WordPress document as\n * tools of the Playground page itself.\n *\n * A plugin running in Playground can register tools with\n * `document.modelContext`, but that registry lives in a nested,\n * service-worker-served iframe an agent never sees. This proxy mirrors those\n * tools onto the top-level `document.modelContext` and forwards each call back\n * into the WordPress document:\n *\n * agent → document.modelContext (this page)\n * → PlaygroundClient.callWebMCPTool() (Comlink)\n * → remote frame (postMessage)\n * → WordPress document (the plugin's execute())\n */\n\nimport type { WebMCPToolDescriptor } from '@wp-playground/remote';\nimport { logger } from '@php-wasm/logger';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport type { ModelContextTool } from './webmcp';\nimport { getModelContext } from './webmcp';\n\nexport interface WebMCPSiteToolProxy {\n\tstop(): void;\n}\n\ninterface WebMCPSiteToolClient {\n\tonWebMCPToolsChanged(\n\t\tlistener: (tools: WebMCPToolDescriptor[]) => void\n\t): Promise<() => void>;\n\tcallWebMCPTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t\tcallId?: string\n\t): Promise<unknown>;\n\tcancelWebMCPTool(callId: string): Promise<void>;\n}\n\n/**\n * Starts mirroring `client`'s in-site WebMCP tools onto this page.\n *\n * Every announcement carries the full tool list, so the previous registration\n * is aborted and the new list registered in its place. Returns a handle that\n * unregisters everything it registered.\n */\nexport function startWebMCPSiteToolProxy(\n\tclient: WebMCPSiteToolClient\n): WebMCPSiteToolProxy {\n\tconst maybeModelContext = getModelContext();\n\tif (!maybeModelContext) {\n\t\t// Nothing can be advertised, so there is nothing to observe for.\n\t\treturn { stop: () => {} };\n\t}\n\t// `register` is hoisted, so it does not inherit the narrowing above.\n\tconst modelContext = maybeModelContext;\n\n\tconst reserved = reservedToolNames();\n\tlet registration: AbortController | null = null;\n\tlet stopped = false;\n\tlet registeredDescriptors = '';\n\tlet unsubscribe: (() => void) | null = null;\n\n\tasync function register(tools: WebMCPToolDescriptor[]) {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\t// Registration is asynchronous, so a burst of announcements could\n\t\t// otherwise re-register an unchanged list several times over.\n\t\tconst descriptors = JSON.stringify(tools);\n\t\tif (descriptors === registeredDescriptors) {\n\t\t\treturn;\n\t\t}\n\t\tregisteredDescriptors = descriptors;\n\n\t\tregistration?.abort();\n\t\tregistration = new AbortController();\n\t\tconst { signal } = registration;\n\n\t\tfor (const tool of tools) {\n\t\t\tif (reserved.has(tool.name)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Skipping the WebMCP tool \"${tool.name}\" registered by ` +\n\t\t\t\t\t\t`the site: Playground already provides a tool under ` +\n\t\t\t\t\t\t`that name.`\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait modelContext.registerTool(proxyTool(client, tool), {\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to register the site's WebMCP tool ` +\n\t\t\t\t\t\t`\"${tool.name}\": ${stringifyError(error)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (signal.aborted) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tclient\n\t\t.onWebMCPToolsChanged((tools: WebMCPToolDescriptor[]) => {\n\t\t\tvoid register(tools);\n\t\t})\n\t\t.then((remove) => {\n\t\t\tif (stopped) {\n\t\t\t\tremove();\n\t\t\t} else {\n\t\t\t\tunsubscribe = remove;\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tlogger.warn(\"Failed to observe the site's WebMCP tools:\", error);\n\t\t});\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tunsubscribe?.();\n\t\t\tunsubscribe = null;\n\t\t\tregistration?.abort();\n\t\t\tregistration = null;\n\t\t\tregisteredDescriptors = '';\n\t\t},\n\t};\n}\n\n/**\n * Names Playground registers itself. A site tool may not take one of them\n * over, or an agent asking to execute PHP would reach the site's plugin\n * instead.\n */\nfunction reservedToolNames(): Set<string> {\n\treturn new Set([\n\t\t...Object.keys(toolDefinitions),\n\t\t...Object.keys(getSiteToolDefinitions()),\n\t]);\n}\n\nfunction proxyTool(\n\tclient: WebMCPSiteToolClient,\n\ttool: WebMCPToolDescriptor\n): ModelContextTool {\n\treturn {\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tinputSchema: tool.inputSchema,\n\t\tannotations: tool.annotations,\n\t\texecute: async (input, clientArg) => {\n\t\t\tconst signal = clientArg?.signal;\n\t\t\tconst callId = crypto.randomUUID();\n\t\t\tconst abort = () => {\n\t\t\t\tvoid client.cancelWebMCPTool(callId).catch((error) => {\n\t\t\t\t\tlogger.warn('Failed to cancel the site tool:', error);\n\t\t\t\t});\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tsignal?.addEventListener('abort', abort, { once: true });\n\t\t\t\tconst result = await client.callWebMCPTool(\n\t\t\t\t\ttool.name,\n\t\t\t\t\tinput ?? {},\n\t\t\t\t\tcallId\n\t\t\t\t);\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\terror:\n\t\t\t\t\t\t`Error running the WordPress site's \"${tool.name}\" ` +\n\t\t\t\t\t\t`tool: ${stringifyError(error)}`,\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tsignal?.removeEventListener('abort', abort);\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","result","_b","stringifyError","createSiteManagementTools","results","tool","failedRegistrations","index","reason","listDef","saveDef","renameDef","websiteUrlDef","_input","formatStorageLabel","site","storage","saved","activeSite","startWebMCPSiteToolProxy","maybeModelContext","reserved","reservedToolNames","registration","registeredDescriptors","unsubscribe","register","descriptors","logger","proxyTool","remove","clientArg","callId","abort"],"mappings":"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"}
package/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { startMcpBridge } from './bridge-client';
2
2
  export type { PlaygroundBridgeConfig, McpBridgeHandle } from './bridge-client';
3
- export { registerWebMCPTools } from './webmcp';
3
+ export { registerWebMCPTools, getModelContext } from './webmcp';
4
4
  export { startWebMCPSiteToolProxy } from './webmcp-site-tools';
5
5
  export type { WebMCPSiteToolProxy } from './webmcp-site-tools';
package/client.js CHANGED
@@ -1,53 +1,53 @@
1
- import { c as C, t as P, a as T, s as p, b as h, f as v, g as $ } from "./tool-executors-D1J1qKnB.js";
1
+ import { c as C, t as P, a as T, s as p, b as g, f as v, g as $ } from "./tool-executors-D1J1qKnB.js";
2
2
  import { logger as S } from "@php-wasm/logger";
3
3
  const I = 5e3;
4
- function L(n, c) {
4
+ function L(a, c) {
5
5
  const d = crypto.randomUUID();
6
- let t = null, r = "", u = null, o = !1;
7
- function a(l) {
8
- const s = n.list(), f = JSON.stringify(s);
9
- f !== r && (r = f, l.send(JSON.stringify({ type: "register", tabId: d, sites: s })));
6
+ let e = null, o = "", u = null, n = !1;
7
+ function s(l) {
8
+ const i = a.list(), f = JSON.stringify(i);
9
+ f !== o && (o = f, l.send(JSON.stringify({ type: "register", tabId: d, sites: i })));
10
10
  }
11
- async function e() {
11
+ async function t() {
12
12
  try {
13
13
  const l = await fetch(
14
14
  `http://127.0.0.1:${c}/bridge-token`
15
15
  );
16
16
  if (!l.ok) {
17
- i();
17
+ r();
18
18
  return;
19
19
  }
20
- const { token: s } = await l.json();
21
- t = new WebSocket(`ws://127.0.0.1:${c}?token=${s}`);
20
+ const { token: i } = await l.json();
21
+ e = new WebSocket(`ws://127.0.0.1:${c}?token=${i}`);
22
22
  } catch {
23
- i();
23
+ r();
24
24
  return;
25
25
  }
26
- t.addEventListener("open", () => {
26
+ e.addEventListener("open", () => {
27
27
  var l;
28
- r = "", a(t), (l = n.onConnect) == null || l.call(n);
29
- }), t.addEventListener("message", async (l) => {
30
- let s;
28
+ o = "", s(e), (l = a.onConnect) == null || l.call(a);
29
+ }), e.addEventListener("message", async (l) => {
30
+ let i;
31
31
  try {
32
- s = JSON.parse(l.data);
32
+ i = JSON.parse(l.data);
33
33
  } catch {
34
34
  return;
35
35
  }
36
- if (s.type !== "command")
36
+ if (i.type !== "command")
37
37
  return;
38
- const { id: f, method: b, args: y, siteSlug: _ } = s;
38
+ const { id: f, method: y, args: h, siteSlug: _ } = i;
39
39
  try {
40
40
  const m = await N(
41
- n,
42
- b,
43
- y || [],
41
+ a,
42
+ y,
43
+ h || [],
44
44
  _,
45
45
  c
46
46
  );
47
- (t == null ? void 0 : t.readyState) === WebSocket.OPEN && t.send(JSON.stringify({ id: f, type: "response", value: m }));
47
+ (e == null ? void 0 : e.readyState) === WebSocket.OPEN && e.send(JSON.stringify({ id: f, type: "response", value: m }));
48
48
  } catch (m) {
49
49
  const E = m instanceof Error ? m.message : String(m);
50
- (t == null ? void 0 : t.readyState) === WebSocket.OPEN && t.send(
50
+ (e == null ? void 0 : e.readyState) === WebSocket.OPEN && e.send(
51
51
  JSON.stringify({
52
52
  id: f,
53
53
  type: "response",
@@ -55,142 +55,142 @@ function L(n, c) {
55
55
  })
56
56
  );
57
57
  }
58
- }), t.addEventListener("close", () => {
59
- t = null, i();
60
- }), t.addEventListener("error", () => {
58
+ }), e.addEventListener("close", () => {
59
+ e = null, r();
60
+ }), e.addEventListener("error", () => {
61
61
  });
62
62
  }
63
- function i() {
64
- o || (u = setTimeout(e, I));
63
+ function r() {
64
+ n || (u = setTimeout(t, I));
65
65
  }
66
- return e(), {
66
+ return t(), {
67
67
  notifySitesChanged: () => {
68
- (t == null ? void 0 : t.readyState) === WebSocket.OPEN && a(t);
68
+ (e == null ? void 0 : e.readyState) === WebSocket.OPEN && s(e);
69
69
  },
70
70
  stop: () => {
71
- o = !0, u !== null && (clearTimeout(u), u = null), t && (t.close(), t = null);
71
+ n = !0, u !== null && (clearTimeout(u), u = null), e && (e.close(), e = null);
72
72
  }
73
73
  };
74
74
  }
75
- async function N(n, c, d, t, r) {
75
+ async function N(a, c, d, e, o) {
76
76
  if (c === "__open_site_in_new_tab") {
77
- const e = new URL(window.location.href);
78
- if (e.searchParams.set("mcp-port", String(r)), e.searchParams.set("site-slug", t), !window.open(e.toString(), "_blank"))
77
+ const t = new URL(window.location.href);
78
+ if (t.searchParams.set("mcp-port", String(o)), t.searchParams.set("site-slug", e), !window.open(t.toString(), "_blank"))
79
79
  throw new Error(
80
80
  "Pop-up blocked by browser. The user must allow pop-ups for this site."
81
81
  );
82
82
  return !0;
83
83
  }
84
84
  if (c === "__rename_site") {
85
- const [e] = d;
86
- return await n.rename(e), !0;
85
+ const [t] = d;
86
+ return await a.rename(t), !0;
87
87
  }
88
88
  if (c === "__save_site")
89
- return await n.saveInBrowser();
90
- const u = n.getClient();
89
+ return await a.saveInBrowser();
90
+ const u = a.getClient();
91
91
  if (!u)
92
- throw new Error(`No active client for site: ${t}`);
93
- const a = C(u)[c];
94
- if (typeof a != "function")
92
+ throw new Error(`No active client for site: ${e}`);
93
+ const s = C(u)[c];
94
+ if (typeof s != "function")
95
95
  throw new Error(`Unknown method: ${c}`);
96
- return await a(...d);
96
+ return await s(...d);
97
97
  }
98
- const g = $();
98
+ const b = $();
99
99
  let w = null;
100
- function W(n) {
101
- const d = n.list().find((t) => t.isActive);
100
+ function W(a) {
101
+ const d = a.list().find((e) => e.isActive);
102
102
  if (!d)
103
103
  throw new Error("No active Playground site");
104
104
  return d;
105
105
  }
106
- async function J(n) {
106
+ async function J(a) {
107
107
  const c = x();
108
108
  if (!c)
109
109
  return;
110
110
  w == null || w.abort(), w = new AbortController();
111
- const d = w, t = d.signal;
112
- function r() {
113
- const e = n.getClient();
114
- if (!e)
111
+ const d = w, e = d.signal;
112
+ function o() {
113
+ const t = a.getClient();
114
+ if (!t)
115
115
  throw new Error("No client for active site");
116
- return e;
116
+ return t;
117
117
  }
118
118
  const u = Object.entries(P).map(
119
- ([e, i]) => ({
120
- name: e,
121
- description: i.description,
122
- inputSchema: h(i.params),
123
- annotations: i.annotations,
124
- execute: async (l, s) => {
125
- var f, b;
119
+ ([t, r]) => ({
120
+ name: t,
121
+ description: r.description,
122
+ inputSchema: g(r.params),
123
+ annotations: r.annotations,
124
+ execute: async (l, i) => {
125
+ var f, y;
126
126
  try {
127
- (f = s == null ? void 0 : s.signal) == null || f.throwIfAborted();
128
- const y = T[e];
129
- if (!y)
127
+ (f = i == null ? void 0 : i.signal) == null || f.throwIfAborted();
128
+ const h = T[t];
129
+ if (!h)
130
130
  return {
131
- error: `No executor for "${e}"`
131
+ error: `No executor for "${t}"`
132
132
  };
133
- const _ = C(r()), m = await y(
133
+ const _ = C(o()), m = await h(
134
134
  _,
135
135
  l,
136
- s == null ? void 0 : s.signal
136
+ i == null ? void 0 : i.signal
137
137
  );
138
- return (b = s == null ? void 0 : s.signal) == null || b.throwIfAborted(), m;
139
- } catch (y) {
138
+ return (y = i == null ? void 0 : i.signal) == null || y.throwIfAborted(), m;
139
+ } catch (h) {
140
140
  return {
141
- error: `${i.errorPrefix}: ${p(y)}`
141
+ error: `${r.errorPrefix}: ${p(h)}`
142
142
  };
143
143
  }
144
144
  }
145
145
  })
146
146
  );
147
- u.push(...A(n));
148
- const o = await Promise.allSettled(
149
- u.map(async (e) => {
150
- await c.registerTool(e, { signal: t });
147
+ u.push(...A(a));
148
+ const n = await Promise.allSettled(
149
+ u.map(async (t) => {
150
+ await c.registerTool(t, { signal: e });
151
151
  })
152
- ), a = [];
153
- for (const [e, i] of o.entries())
154
- i.status === "rejected" && a.push({
155
- name: u[e].name,
156
- reason: i.reason
152
+ ), s = [];
153
+ for (const [t, r] of n.entries())
154
+ r.status === "rejected" && s.push({
155
+ name: u[t].name,
156
+ reason: r.reason
157
157
  });
158
- if (a.length > 0)
158
+ if (s.length > 0)
159
159
  throw d.abort(), w === d && (w = null), new Error(
160
- "Failed to register WebMCP tools: " + a.map(
161
- ({ name: e, reason: i }) => `${e} (${p(i)})`
160
+ "Failed to register WebMCP tools: " + s.map(
161
+ ({ name: t, reason: r }) => `${t} (${p(r)})`
162
162
  ).join(", ")
163
163
  );
164
164
  }
165
165
  function x() {
166
166
  if (typeof document > "u")
167
167
  return;
168
- const n = document.modelContext;
169
- return typeof (n == null ? void 0 : n.registerTool) == "function" ? n : void 0;
168
+ const a = document.modelContext;
169
+ return typeof (a == null ? void 0 : a.registerTool) == "function" ? a : void 0;
170
170
  }
171
- function A(n) {
172
- const c = g.playground_list_sites, d = g.playground_save_in_browser, t = g.playground_rename_site, r = g.playground_get_website_url;
171
+ function A(a) {
172
+ const c = b.playground_list_sites, d = b.playground_save_in_browser, e = b.playground_rename_site, o = b.playground_get_website_url;
173
173
  return [
174
174
  {
175
175
  name: "playground_list_sites",
176
176
  description: c.description,
177
- inputSchema: h(c.params),
177
+ inputSchema: g(c.params),
178
178
  annotations: c.annotations,
179
- execute: async (u, o) => {
180
- var a;
179
+ execute: async (u, n) => {
180
+ var s;
181
181
  try {
182
- return (a = o == null ? void 0 : o.signal) == null || a.throwIfAborted(), {
182
+ return (s = n == null ? void 0 : n.signal) == null || s.throwIfAborted(), {
183
183
  connectedTabs: 1,
184
- sites: n.list().map((e) => ({
185
- siteId: e.slug,
186
- name: e.name,
187
- storage: v(e.storage),
188
- isActive: e.isActive
184
+ sites: a.list().map((t) => ({
185
+ siteId: t.slug,
186
+ name: t.name,
187
+ storage: v(t.storage),
188
+ isActive: t.isActive
189
189
  }))
190
190
  };
191
- } catch (e) {
191
+ } catch (t) {
192
192
  return {
193
- error: `${c.errorPrefix}: ${p(e)}`
193
+ error: `${c.errorPrefix}: ${p(t)}`
194
194
  };
195
195
  }
196
196
  }
@@ -198,120 +198,122 @@ function A(n) {
198
198
  {
199
199
  name: "playground_save_in_browser",
200
200
  description: d.description,
201
- inputSchema: h(d.params),
201
+ inputSchema: g(d.params),
202
202
  annotations: d.annotations,
203
- execute: async (u, o) => {
204
- var a, e;
203
+ execute: async (u, n) => {
204
+ var s, t;
205
205
  try {
206
- (a = o == null ? void 0 : o.signal) == null || a.throwIfAborted();
207
- const i = W(n), l = v(i.storage);
206
+ (s = n == null ? void 0 : n.signal) == null || s.throwIfAborted();
207
+ const r = W(a), l = v(r.storage);
208
208
  if (l !== "temporary")
209
209
  return {
210
210
  success: !0,
211
211
  alreadySaved: !0,
212
- siteId: i.slug,
213
- name: i.name,
212
+ siteId: r.slug,
213
+ name: r.name,
214
214
  storage: l
215
215
  };
216
- const s = await n.saveInBrowser();
217
- return (e = o == null ? void 0 : o.signal) == null || e.throwIfAborted(), {
216
+ const i = await a.saveInBrowser();
217
+ return (t = n == null ? void 0 : n.signal) == null || t.throwIfAborted(), {
218
218
  success: !0,
219
219
  alreadySaved: !1,
220
- siteId: s.slug,
221
- name: i.name ?? s.slug,
222
- storage: v(s.storage)
220
+ siteId: i.slug,
221
+ name: r.name ?? i.slug,
222
+ storage: v(i.storage)
223
223
  };
224
- } catch (i) {
224
+ } catch (r) {
225
225
  return {
226
- error: `${d.errorPrefix}: ${p(i)}`
226
+ error: `${d.errorPrefix}: ${p(r)}`
227
227
  };
228
228
  }
229
229
  }
230
230
  },
231
231
  {
232
232
  name: "playground_rename_site",
233
- description: t.description,
234
- inputSchema: h(t.params),
235
- annotations: t.annotations,
236
- execute: async (u, o) => {
237
- var a, e;
233
+ description: e.description,
234
+ inputSchema: g(e.params),
235
+ annotations: e.annotations,
236
+ execute: async (u, n) => {
237
+ var s, t;
238
238
  try {
239
- (a = o == null ? void 0 : o.signal) == null || a.throwIfAborted();
240
- const i = u.newName, s = n.list().find((f) => f.isActive);
241
- return await n.rename(i), (e = o == null ? void 0 : o.signal) == null || e.throwIfAborted(), {
239
+ (s = n == null ? void 0 : n.signal) == null || s.throwIfAborted();
240
+ const r = u.newName, i = a.list().find((f) => f.isActive);
241
+ return await a.rename(r), (t = n == null ? void 0 : n.signal) == null || t.throwIfAborted(), {
242
242
  success: !0,
243
- siteId: s == null ? void 0 : s.slug,
244
- newName: i
243
+ siteId: i == null ? void 0 : i.slug,
244
+ newName: r
245
245
  };
246
- } catch (i) {
246
+ } catch (r) {
247
247
  return {
248
- error: `${t.errorPrefix}: ${p(i)}`
248
+ error: `${e.errorPrefix}: ${p(r)}`
249
249
  };
250
250
  }
251
251
  }
252
252
  },
253
253
  {
254
254
  name: "playground_get_website_url",
255
- description: r.description,
256
- inputSchema: h(r.params),
257
- annotations: r.annotations,
258
- execute: async (u, o) => {
259
- var a;
255
+ description: o.description,
256
+ inputSchema: g(o.params),
257
+ annotations: o.annotations,
258
+ execute: async (u, n) => {
259
+ var s;
260
260
  try {
261
- return (a = o == null ? void 0 : o.signal) == null || a.throwIfAborted(), {
261
+ return (s = n == null ? void 0 : n.signal) == null || s.throwIfAborted(), {
262
262
  url: window.location.href
263
263
  };
264
- } catch (e) {
264
+ } catch (t) {
265
265
  return {
266
- error: `${r.errorPrefix}: ${p(e)}`
266
+ error: `${o.errorPrefix}: ${p(t)}`
267
267
  };
268
268
  }
269
269
  }
270
270
  }
271
271
  ];
272
272
  }
273
- function U(n) {
273
+ function U(a) {
274
274
  const c = x();
275
275
  if (!c)
276
276
  return { stop: () => {
277
277
  } };
278
- const d = c, t = M();
279
- let r = null, u = !1, o = "";
280
- async function a(e) {
278
+ const d = c, e = M();
279
+ let o = null, u = !1, n = "", s = null;
280
+ async function t(r) {
281
281
  if (u)
282
282
  return;
283
- const i = JSON.stringify(e);
284
- if (i === o)
283
+ const l = JSON.stringify(r);
284
+ if (l === n)
285
285
  return;
286
- o = i, r == null || r.abort(), r = new AbortController();
287
- const { signal: l } = r;
288
- for (const s of e) {
289
- if (t.has(s.name)) {
286
+ n = l, o == null || o.abort(), o = new AbortController();
287
+ const { signal: i } = o;
288
+ for (const f of r) {
289
+ if (e.has(f.name)) {
290
290
  S.warn(
291
- `Skipping the WebMCP tool "${s.name}" registered by the site: Playground already provides a tool under that name.`
291
+ `Skipping the WebMCP tool "${f.name}" registered by the site: Playground already provides a tool under that name.`
292
292
  );
293
293
  continue;
294
294
  }
295
295
  try {
296
- await d.registerTool(k(n, s), {
297
- signal: l
296
+ await d.registerTool(k(a, f), {
297
+ signal: i
298
298
  });
299
- } catch (f) {
299
+ } catch (y) {
300
300
  S.warn(
301
- `Failed to register the site's WebMCP tool "${s.name}": ${p(f)}`
301
+ `Failed to register the site's WebMCP tool "${f.name}": ${p(y)}`
302
302
  );
303
303
  }
304
- if (l.aborted)
304
+ if (i.aborted)
305
305
  return;
306
306
  }
307
307
  }
308
- return n.onWebMCPToolsChanged((e) => {
309
- a(e);
310
- }).catch((e) => {
311
- S.warn("Failed to observe the site's WebMCP tools:", e);
308
+ return a.onWebMCPToolsChanged((r) => {
309
+ t(r);
310
+ }).then((r) => {
311
+ u ? r() : s = r;
312
+ }).catch((r) => {
313
+ S.warn("Failed to observe the site's WebMCP tools:", r);
312
314
  }), {
313
315
  stop() {
314
- u = !0, r == null || r.abort(), r = null, o = "";
316
+ u = !0, s == null || s(), s = null, o == null || o.abort(), o = null, n = "";
315
317
  }
316
318
  };
317
319
  }
@@ -321,37 +323,38 @@ function M() {
321
323
  ...Object.keys($())
322
324
  ]);
323
325
  }
324
- function k(n, c) {
326
+ function k(a, c) {
325
327
  return {
326
328
  name: c.name,
327
329
  description: c.description,
328
330
  inputSchema: c.inputSchema,
329
331
  annotations: c.annotations,
330
- execute: async (d, t) => {
331
- const r = t == null ? void 0 : t.signal, u = crypto.randomUUID(), o = () => {
332
- n.cancelWebMCPTool(u).catch((a) => {
333
- S.warn("Failed to cancel the site tool:", a);
332
+ execute: async (d, e) => {
333
+ const o = e == null ? void 0 : e.signal, u = crypto.randomUUID(), n = () => {
334
+ a.cancelWebMCPTool(u).catch((s) => {
335
+ S.warn("Failed to cancel the site tool:", s);
334
336
  });
335
337
  };
336
338
  try {
337
- r == null || r.throwIfAborted(), r == null || r.addEventListener("abort", o, { once: !0 });
338
- const a = await n.callWebMCPTool(
339
+ o == null || o.throwIfAborted(), o == null || o.addEventListener("abort", n, { once: !0 });
340
+ const s = await a.callWebMCPTool(
339
341
  c.name,
340
342
  d ?? {},
341
343
  u
342
344
  );
343
- return r == null || r.throwIfAborted(), a;
344
- } catch (a) {
345
+ return o == null || o.throwIfAborted(), s;
346
+ } catch (s) {
345
347
  return {
346
- error: `Error running the WordPress site's "${c.name}" tool: ${p(a)}`
348
+ error: `Error running the WordPress site's "${c.name}" tool: ${p(s)}`
347
349
  };
348
350
  } finally {
349
- r == null || r.removeEventListener("abort", o);
351
+ o == null || o.removeEventListener("abort", n);
350
352
  }
351
353
  }
352
354
  };
353
355
  }
354
356
  export {
357
+ x as getModelContext,
355
358
  J as registerWebMCPTools,
356
359
  L as startMcpBridge,
357
360
  U as startWebMCPSiteToolProxy
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","../../../../packages/playground/mcp/src/webmcp-site-tools.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\nexport interface 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?: {\n\t\treadOnlyHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t\tconsequentialHint?: boolean;\n\t\tuntrustedContentHint?: boolean;\n\t\tdebugging?: boolean;\n\t};\n}\n\ninterface ModelContextClient {\n\tsignal?: AbortSignal;\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tconst result = await executor(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tinput,\n\t\t\t\t\t\tclient?.signal\n\t\t\t\t\t);\n\t\t\t\t\tclient?.signal?.throwIfAborted();\n\t\t\t\t\treturn result;\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\nexport function 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\tinputSchema: paramsToJsonSchema(listDef.params),\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(saveDef.params),\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\t// Storage operations do not currently expose an interrupt API.\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(websiteUrlDef.params),\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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","/**\n * Re-advertises the WebMCP tools registered inside the WordPress document as\n * tools of the Playground page itself.\n *\n * A plugin running in Playground can register tools with\n * `document.modelContext`, but that registry lives in a nested,\n * service-worker-served iframe an agent never sees. This proxy mirrors those\n * tools onto the top-level `document.modelContext` and forwards each call back\n * into the WordPress document:\n *\n * agent → document.modelContext (this page)\n * → PlaygroundClient.callWebMCPTool() (Comlink)\n * → remote frame (postMessage)\n * → WordPress document (the plugin's execute())\n */\n\nimport type {\n\tPlaygroundClient,\n\tWebMCPToolDescriptor,\n} from '@wp-playground/remote';\nimport { logger } from '@php-wasm/logger';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport type { ModelContextTool } from './webmcp';\nimport { getModelContext } from './webmcp';\n\nexport interface WebMCPSiteToolProxy {\n\tstop(): void;\n}\n\n/**\n * Starts mirroring `client`'s in-site WebMCP tools onto this page.\n *\n * Every announcement carries the full tool list, so the previous registration\n * is aborted and the new list registered in its place. Returns a handle that\n * unregisters everything it registered.\n */\nexport function startWebMCPSiteToolProxy(\n\tclient: PlaygroundClient\n): WebMCPSiteToolProxy {\n\tconst maybeModelContext = getModelContext();\n\tif (!maybeModelContext) {\n\t\t// Nothing can be advertised, so there is nothing to observe for.\n\t\treturn { stop: () => {} };\n\t}\n\t// `register` is hoisted, so it does not inherit the narrowing above.\n\tconst modelContext = maybeModelContext;\n\n\tconst reserved = reservedToolNames();\n\tlet registration: AbortController | null = null;\n\tlet stopped = false;\n\tlet registeredDescriptors = '';\n\n\tasync function register(tools: WebMCPToolDescriptor[]) {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\t// Registration is asynchronous, so a burst of announcements could\n\t\t// otherwise re-register an unchanged list several times over.\n\t\tconst descriptors = JSON.stringify(tools);\n\t\tif (descriptors === registeredDescriptors) {\n\t\t\treturn;\n\t\t}\n\t\tregisteredDescriptors = descriptors;\n\n\t\tregistration?.abort();\n\t\tregistration = new AbortController();\n\t\tconst { signal } = registration;\n\n\t\tfor (const tool of tools) {\n\t\t\tif (reserved.has(tool.name)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Skipping the WebMCP tool \"${tool.name}\" registered by ` +\n\t\t\t\t\t\t`the site: Playground already provides a tool under ` +\n\t\t\t\t\t\t`that name.`\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait modelContext.registerTool(proxyTool(client, tool), {\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to register the site's WebMCP tool ` +\n\t\t\t\t\t\t`\"${tool.name}\": ${stringifyError(error)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (signal.aborted) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tclient\n\t\t.onWebMCPToolsChanged((tools: WebMCPToolDescriptor[]) => {\n\t\t\tvoid register(tools);\n\t\t})\n\t\t.catch((error) => {\n\t\t\tlogger.warn(\"Failed to observe the site's WebMCP tools:\", error);\n\t\t});\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tregistration?.abort();\n\t\t\tregistration = null;\n\t\t\tregisteredDescriptors = '';\n\t\t},\n\t};\n}\n\n/**\n * Names Playground registers itself. A site tool may not take one of them\n * over, or an agent asking to execute PHP would reach the site's plugin\n * instead.\n */\nfunction reservedToolNames(): Set<string> {\n\treturn new Set([\n\t\t...Object.keys(toolDefinitions),\n\t\t...Object.keys(getSiteToolDefinitions()),\n\t]);\n}\n\nfunction proxyTool(\n\tclient: PlaygroundClient,\n\ttool: WebMCPToolDescriptor\n): ModelContextTool {\n\treturn {\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tinputSchema: tool.inputSchema,\n\t\tannotations: tool.annotations,\n\t\texecute: async (input, clientArg) => {\n\t\t\tconst signal = clientArg?.signal;\n\t\t\tconst callId = crypto.randomUUID();\n\t\t\tconst abort = () => {\n\t\t\t\tvoid client.cancelWebMCPTool(callId).catch((error) => {\n\t\t\t\t\tlogger.warn('Failed to cancel the site tool:', error);\n\t\t\t\t});\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tsignal?.addEventListener('abort', abort, { once: true });\n\t\t\t\tconst result = await client.callWebMCPTool(\n\t\t\t\t\ttool.name,\n\t\t\t\t\tinput ?? {},\n\t\t\t\t\tcallId\n\t\t\t\t);\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\terror:\n\t\t\t\t\t\t`Error running the WordPress site's \"${tool.name}\" ` +\n\t\t\t\t\t\t`tool: ${stringifyError(error)}`,\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tsignal?.removeEventListener('abort', abort);\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","result","_b","stringifyError","createSiteManagementTools","results","tool","failedRegistrations","index","reason","listDef","saveDef","renameDef","websiteUrlDef","_input","formatStorageLabel","site","storage","saved","activeSite","startWebMCPSiteToolProxy","maybeModelContext","reserved","reservedToolNames","registration","registeredDescriptors","register","descriptors","logger","proxyTool","clientArg","callId","abort"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts","../../../../packages/playground/mcp/src/webmcp-site-tools.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\nexport interface 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?: {\n\t\treadOnlyHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t\tconsequentialHint?: boolean;\n\t\tuntrustedContentHint?: boolean;\n\t\tdebugging?: boolean;\n\t};\n}\n\ninterface ModelContextClient {\n\tsignal?: AbortSignal;\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tconst result = await executor(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tinput,\n\t\t\t\t\t\tclient?.signal\n\t\t\t\t\t);\n\t\t\t\t\tclient?.signal?.throwIfAborted();\n\t\t\t\t\treturn result;\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\nexport function 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\tinputSchema: paramsToJsonSchema(listDef.params),\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(saveDef.params),\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\t// Storage operations do not currently expose an interrupt API.\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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\tclient?.signal?.throwIfAborted();\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\tinputSchema: paramsToJsonSchema(websiteUrlDef.params),\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async (_input, client) => {\n\t\t\t\ttry {\n\t\t\t\t\tclient?.signal?.throwIfAborted();\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","/**\n * Re-advertises the WebMCP tools registered inside the WordPress document as\n * tools of the Playground page itself.\n *\n * A plugin running in Playground can register tools with\n * `document.modelContext`, but that registry lives in a nested,\n * service-worker-served iframe an agent never sees. This proxy mirrors those\n * tools onto the top-level `document.modelContext` and forwards each call back\n * into the WordPress document:\n *\n * agent → document.modelContext (this page)\n * → PlaygroundClient.callWebMCPTool() (Comlink)\n * → remote frame (postMessage)\n * → WordPress document (the plugin's execute())\n */\n\nimport type { WebMCPToolDescriptor } from '@wp-playground/remote';\nimport { logger } from '@php-wasm/logger';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport type { ModelContextTool } from './webmcp';\nimport { getModelContext } from './webmcp';\n\nexport interface WebMCPSiteToolProxy {\n\tstop(): void;\n}\n\ninterface WebMCPSiteToolClient {\n\tonWebMCPToolsChanged(\n\t\tlistener: (tools: WebMCPToolDescriptor[]) => void\n\t): Promise<() => void>;\n\tcallWebMCPTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t\tcallId?: string\n\t): Promise<unknown>;\n\tcancelWebMCPTool(callId: string): Promise<void>;\n}\n\n/**\n * Starts mirroring `client`'s in-site WebMCP tools onto this page.\n *\n * Every announcement carries the full tool list, so the previous registration\n * is aborted and the new list registered in its place. Returns a handle that\n * unregisters everything it registered.\n */\nexport function startWebMCPSiteToolProxy(\n\tclient: WebMCPSiteToolClient\n): WebMCPSiteToolProxy {\n\tconst maybeModelContext = getModelContext();\n\tif (!maybeModelContext) {\n\t\t// Nothing can be advertised, so there is nothing to observe for.\n\t\treturn { stop: () => {} };\n\t}\n\t// `register` is hoisted, so it does not inherit the narrowing above.\n\tconst modelContext = maybeModelContext;\n\n\tconst reserved = reservedToolNames();\n\tlet registration: AbortController | null = null;\n\tlet stopped = false;\n\tlet registeredDescriptors = '';\n\tlet unsubscribe: (() => void) | null = null;\n\n\tasync function register(tools: WebMCPToolDescriptor[]) {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\t// Registration is asynchronous, so a burst of announcements could\n\t\t// otherwise re-register an unchanged list several times over.\n\t\tconst descriptors = JSON.stringify(tools);\n\t\tif (descriptors === registeredDescriptors) {\n\t\t\treturn;\n\t\t}\n\t\tregisteredDescriptors = descriptors;\n\n\t\tregistration?.abort();\n\t\tregistration = new AbortController();\n\t\tconst { signal } = registration;\n\n\t\tfor (const tool of tools) {\n\t\t\tif (reserved.has(tool.name)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Skipping the WebMCP tool \"${tool.name}\" registered by ` +\n\t\t\t\t\t\t`the site: Playground already provides a tool under ` +\n\t\t\t\t\t\t`that name.`\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait modelContext.registerTool(proxyTool(client, tool), {\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to register the site's WebMCP tool ` +\n\t\t\t\t\t\t`\"${tool.name}\": ${stringifyError(error)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (signal.aborted) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tclient\n\t\t.onWebMCPToolsChanged((tools: WebMCPToolDescriptor[]) => {\n\t\t\tvoid register(tools);\n\t\t})\n\t\t.then((remove) => {\n\t\t\tif (stopped) {\n\t\t\t\tremove();\n\t\t\t} else {\n\t\t\t\tunsubscribe = remove;\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tlogger.warn(\"Failed to observe the site's WebMCP tools:\", error);\n\t\t});\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tunsubscribe?.();\n\t\t\tunsubscribe = null;\n\t\t\tregistration?.abort();\n\t\t\tregistration = null;\n\t\t\tregisteredDescriptors = '';\n\t\t},\n\t};\n}\n\n/**\n * Names Playground registers itself. A site tool may not take one of them\n * over, or an agent asking to execute PHP would reach the site's plugin\n * instead.\n */\nfunction reservedToolNames(): Set<string> {\n\treturn new Set([\n\t\t...Object.keys(toolDefinitions),\n\t\t...Object.keys(getSiteToolDefinitions()),\n\t]);\n}\n\nfunction proxyTool(\n\tclient: WebMCPSiteToolClient,\n\ttool: WebMCPToolDescriptor\n): ModelContextTool {\n\treturn {\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tinputSchema: tool.inputSchema,\n\t\tannotations: tool.annotations,\n\t\texecute: async (input, clientArg) => {\n\t\t\tconst signal = clientArg?.signal;\n\t\t\tconst callId = crypto.randomUUID();\n\t\t\tconst abort = () => {\n\t\t\t\tvoid client.cancelWebMCPTool(callId).catch((error) => {\n\t\t\t\t\tlogger.warn('Failed to cancel the site tool:', error);\n\t\t\t\t});\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tsignal?.addEventListener('abort', abort, { once: true });\n\t\t\t\tconst result = await client.callWebMCPTool(\n\t\t\t\t\ttool.name,\n\t\t\t\t\tinput ?? {},\n\t\t\t\t\tcallId\n\t\t\t\t);\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\terror:\n\t\t\t\t\t\t`Error running the WordPress site's \"${tool.name}\" ` +\n\t\t\t\t\t\t`tool: ${stringifyError(error)}`,\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tsignal?.removeEventListener('abort', abort);\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","result","_b","stringifyError","createSiteManagementTools","results","tool","failedRegistrations","index","reason","listDef","saveDef","renameDef","websiteUrlDef","_input","formatStorageLabel","site","storage","saved","activeSite","startWebMCPSiteToolProxy","maybeModelContext","reserved","reservedToolNames","registration","registeredDescriptors","unsubscribe","register","descriptors","logger","proxyTool","remove","clientArg","callId","abort"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-playground/mcp",
3
- "version": "3.1.62",
3
+ "version": "3.1.63",
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",
@@ -40,11 +40,11 @@
40
40
  "postal-mime": "2.7.5",
41
41
  "ws": "8.21.0",
42
42
  "zod": "^4.3",
43
- "@wp-playground/remote": "3.1.62",
44
- "@php-wasm/universal": "3.1.62",
45
- "@php-wasm/logger": "3.1.62"
43
+ "@wp-playground/remote": "3.1.63",
44
+ "@php-wasm/universal": "3.1.63",
45
+ "@php-wasm/logger": "3.1.63"
46
46
  },
47
- "gitHead": "8c52f103f3ce30668994c0e75c2c530b82097a5e",
47
+ "gitHead": "d9392e2c6a2eb8c5cd5c4d357c725572fac56c12",
48
48
  "packageManager": "npm@10.9.2",
49
49
  "overrides": {
50
50
  "rollup": "^4.34.6",
@@ -13,10 +13,15 @@
13
13
  * → remote frame (postMessage)
14
14
  * → WordPress document (the plugin's execute())
15
15
  */
16
- import type { PlaygroundClient } from '@wp-playground/remote';
16
+ import type { WebMCPToolDescriptor } from '@wp-playground/remote';
17
17
  export interface WebMCPSiteToolProxy {
18
18
  stop(): void;
19
19
  }
20
+ interface WebMCPSiteToolClient {
21
+ onWebMCPToolsChanged(listener: (tools: WebMCPToolDescriptor[]) => void): Promise<() => void>;
22
+ callWebMCPTool(name: string, args: Record<string, unknown>, callId?: string): Promise<unknown>;
23
+ cancelWebMCPTool(callId: string): Promise<void>;
24
+ }
20
25
  /**
21
26
  * Starts mirroring `client`'s in-site WebMCP tools onto this page.
22
27
  *
@@ -24,4 +29,5 @@ export interface WebMCPSiteToolProxy {
24
29
  * is aborted and the new list registered in its place. Returns a handle that
25
30
  * unregisters everything it registered.
26
31
  */
27
- export declare function startWebMCPSiteToolProxy(client: PlaygroundClient): WebMCPSiteToolProxy;
32
+ export declare function startWebMCPSiteToolProxy(client: WebMCPSiteToolClient): WebMCPSiteToolProxy;
33
+ export {};