@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 +1 -1
- package/client.cjs.map +1 -1
- package/client.d.ts +1 -1
- package/client.js +163 -160
- package/client.js.map +1 -1
- package/package.json +5 -5
- package/webmcp-site-tools.d.ts +8 -2
package/client.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
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
|
|
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(
|
|
4
|
+
function L(a, c) {
|
|
5
5
|
const d = crypto.randomUUID();
|
|
6
|
-
let
|
|
7
|
-
function
|
|
8
|
-
const
|
|
9
|
-
f !==
|
|
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
|
|
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
|
-
|
|
17
|
+
r();
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
-
const { token:
|
|
21
|
-
|
|
20
|
+
const { token: i } = await l.json();
|
|
21
|
+
e = new WebSocket(`ws://127.0.0.1:${c}?token=${i}`);
|
|
22
22
|
} catch {
|
|
23
|
-
|
|
23
|
+
r();
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
e.addEventListener("open", () => {
|
|
27
27
|
var l;
|
|
28
|
-
|
|
29
|
-
}),
|
|
30
|
-
let
|
|
28
|
+
o = "", s(e), (l = a.onConnect) == null || l.call(a);
|
|
29
|
+
}), e.addEventListener("message", async (l) => {
|
|
30
|
+
let i;
|
|
31
31
|
try {
|
|
32
|
-
|
|
32
|
+
i = JSON.parse(l.data);
|
|
33
33
|
} catch {
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
if (
|
|
36
|
+
if (i.type !== "command")
|
|
37
37
|
return;
|
|
38
|
-
const { id: f, method:
|
|
38
|
+
const { id: f, method: y, args: h, siteSlug: _ } = i;
|
|
39
39
|
try {
|
|
40
40
|
const m = await N(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
a,
|
|
42
|
+
y,
|
|
43
|
+
h || [],
|
|
44
44
|
_,
|
|
45
45
|
c
|
|
46
46
|
);
|
|
47
|
-
(
|
|
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
|
-
(
|
|
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
|
-
}),
|
|
59
|
-
|
|
60
|
-
}),
|
|
58
|
+
}), e.addEventListener("close", () => {
|
|
59
|
+
e = null, r();
|
|
60
|
+
}), e.addEventListener("error", () => {
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
63
|
+
function r() {
|
|
64
|
+
n || (u = setTimeout(t, I));
|
|
65
65
|
}
|
|
66
|
-
return
|
|
66
|
+
return t(), {
|
|
67
67
|
notifySitesChanged: () => {
|
|
68
|
-
(
|
|
68
|
+
(e == null ? void 0 : e.readyState) === WebSocket.OPEN && s(e);
|
|
69
69
|
},
|
|
70
70
|
stop: () => {
|
|
71
|
-
|
|
71
|
+
n = !0, u !== null && (clearTimeout(u), u = null), e && (e.close(), e = null);
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
-
async function N(
|
|
75
|
+
async function N(a, c, d, e, o) {
|
|
76
76
|
if (c === "__open_site_in_new_tab") {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
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 [
|
|
86
|
-
return await
|
|
85
|
+
const [t] = d;
|
|
86
|
+
return await a.rename(t), !0;
|
|
87
87
|
}
|
|
88
88
|
if (c === "__save_site")
|
|
89
|
-
return await
|
|
90
|
-
const u =
|
|
89
|
+
return await a.saveInBrowser();
|
|
90
|
+
const u = a.getClient();
|
|
91
91
|
if (!u)
|
|
92
|
-
throw new Error(`No active client for site: ${
|
|
93
|
-
const
|
|
94
|
-
if (typeof
|
|
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
|
|
96
|
+
return await s(...d);
|
|
97
97
|
}
|
|
98
|
-
const
|
|
98
|
+
const b = $();
|
|
99
99
|
let w = null;
|
|
100
|
-
function W(
|
|
101
|
-
const d =
|
|
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(
|
|
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,
|
|
112
|
-
function
|
|
113
|
-
const
|
|
114
|
-
if (!
|
|
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
|
|
116
|
+
return t;
|
|
117
117
|
}
|
|
118
118
|
const u = Object.entries(P).map(
|
|
119
|
-
([
|
|
120
|
-
name:
|
|
121
|
-
description:
|
|
122
|
-
inputSchema:
|
|
123
|
-
annotations:
|
|
124
|
-
execute: async (l,
|
|
125
|
-
var f,
|
|
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 =
|
|
128
|
-
const
|
|
129
|
-
if (!
|
|
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 "${
|
|
131
|
+
error: `No executor for "${t}"`
|
|
132
132
|
};
|
|
133
|
-
const _ = C(
|
|
133
|
+
const _ = C(o()), m = await h(
|
|
134
134
|
_,
|
|
135
135
|
l,
|
|
136
|
-
|
|
136
|
+
i == null ? void 0 : i.signal
|
|
137
137
|
);
|
|
138
|
-
return (
|
|
139
|
-
} catch (
|
|
138
|
+
return (y = i == null ? void 0 : i.signal) == null || y.throwIfAborted(), m;
|
|
139
|
+
} catch (h) {
|
|
140
140
|
return {
|
|
141
|
-
error: `${
|
|
141
|
+
error: `${r.errorPrefix}: ${p(h)}`
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
})
|
|
146
146
|
);
|
|
147
|
-
u.push(...A(
|
|
148
|
-
const
|
|
149
|
-
u.map(async (
|
|
150
|
-
await c.registerTool(
|
|
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
|
-
),
|
|
153
|
-
for (const [
|
|
154
|
-
|
|
155
|
-
name: u[
|
|
156
|
-
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 (
|
|
158
|
+
if (s.length > 0)
|
|
159
159
|
throw d.abort(), w === d && (w = null), new Error(
|
|
160
|
-
"Failed to register WebMCP tools: " +
|
|
161
|
-
({ name:
|
|
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
|
|
169
|
-
return typeof (
|
|
168
|
+
const a = document.modelContext;
|
|
169
|
+
return typeof (a == null ? void 0 : a.registerTool) == "function" ? a : void 0;
|
|
170
170
|
}
|
|
171
|
-
function A(
|
|
172
|
-
const c =
|
|
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:
|
|
177
|
+
inputSchema: g(c.params),
|
|
178
178
|
annotations: c.annotations,
|
|
179
|
-
execute: async (u,
|
|
180
|
-
var
|
|
179
|
+
execute: async (u, n) => {
|
|
180
|
+
var s;
|
|
181
181
|
try {
|
|
182
|
-
return (
|
|
182
|
+
return (s = n == null ? void 0 : n.signal) == null || s.throwIfAborted(), {
|
|
183
183
|
connectedTabs: 1,
|
|
184
|
-
sites:
|
|
185
|
-
siteId:
|
|
186
|
-
name:
|
|
187
|
-
storage: v(
|
|
188
|
-
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 (
|
|
191
|
+
} catch (t) {
|
|
192
192
|
return {
|
|
193
|
-
error: `${c.errorPrefix}: ${p(
|
|
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:
|
|
201
|
+
inputSchema: g(d.params),
|
|
202
202
|
annotations: d.annotations,
|
|
203
|
-
execute: async (u,
|
|
204
|
-
var
|
|
203
|
+
execute: async (u, n) => {
|
|
204
|
+
var s, t;
|
|
205
205
|
try {
|
|
206
|
-
(
|
|
207
|
-
const
|
|
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:
|
|
213
|
-
name:
|
|
212
|
+
siteId: r.slug,
|
|
213
|
+
name: r.name,
|
|
214
214
|
storage: l
|
|
215
215
|
};
|
|
216
|
-
const
|
|
217
|
-
return (
|
|
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:
|
|
221
|
-
name:
|
|
222
|
-
storage: v(
|
|
220
|
+
siteId: i.slug,
|
|
221
|
+
name: r.name ?? i.slug,
|
|
222
|
+
storage: v(i.storage)
|
|
223
223
|
};
|
|
224
|
-
} catch (
|
|
224
|
+
} catch (r) {
|
|
225
225
|
return {
|
|
226
|
-
error: `${d.errorPrefix}: ${p(
|
|
226
|
+
error: `${d.errorPrefix}: ${p(r)}`
|
|
227
227
|
};
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
232
|
name: "playground_rename_site",
|
|
233
|
-
description:
|
|
234
|
-
inputSchema:
|
|
235
|
-
annotations:
|
|
236
|
-
execute: async (u,
|
|
237
|
-
var
|
|
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
|
-
(
|
|
240
|
-
const
|
|
241
|
-
return await
|
|
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:
|
|
244
|
-
newName:
|
|
243
|
+
siteId: i == null ? void 0 : i.slug,
|
|
244
|
+
newName: r
|
|
245
245
|
};
|
|
246
|
-
} catch (
|
|
246
|
+
} catch (r) {
|
|
247
247
|
return {
|
|
248
|
-
error: `${
|
|
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:
|
|
256
|
-
inputSchema:
|
|
257
|
-
annotations:
|
|
258
|
-
execute: async (u,
|
|
259
|
-
var
|
|
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 (
|
|
261
|
+
return (s = n == null ? void 0 : n.signal) == null || s.throwIfAborted(), {
|
|
262
262
|
url: window.location.href
|
|
263
263
|
};
|
|
264
|
-
} catch (
|
|
264
|
+
} catch (t) {
|
|
265
265
|
return {
|
|
266
|
-
error: `${
|
|
266
|
+
error: `${o.errorPrefix}: ${p(t)}`
|
|
267
267
|
};
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
];
|
|
272
272
|
}
|
|
273
|
-
function U(
|
|
273
|
+
function U(a) {
|
|
274
274
|
const c = x();
|
|
275
275
|
if (!c)
|
|
276
276
|
return { stop: () => {
|
|
277
277
|
} };
|
|
278
|
-
const d = c,
|
|
279
|
-
let
|
|
280
|
-
async function
|
|
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
|
|
284
|
-
if (
|
|
283
|
+
const l = JSON.stringify(r);
|
|
284
|
+
if (l === n)
|
|
285
285
|
return;
|
|
286
|
-
|
|
287
|
-
const { signal:
|
|
288
|
-
for (const
|
|
289
|
-
if (
|
|
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 "${
|
|
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(
|
|
297
|
-
signal:
|
|
296
|
+
await d.registerTool(k(a, f), {
|
|
297
|
+
signal: i
|
|
298
298
|
});
|
|
299
|
-
} catch (
|
|
299
|
+
} catch (y) {
|
|
300
300
|
S.warn(
|
|
301
|
-
`Failed to register the site's WebMCP tool "${
|
|
301
|
+
`Failed to register the site's WebMCP tool "${f.name}": ${p(y)}`
|
|
302
302
|
);
|
|
303
303
|
}
|
|
304
|
-
if (
|
|
304
|
+
if (i.aborted)
|
|
305
305
|
return;
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
|
-
return
|
|
309
|
-
|
|
310
|
-
}).
|
|
311
|
-
|
|
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,
|
|
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(
|
|
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,
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
S.warn("Failed to cancel the site tool:",
|
|
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
|
-
|
|
338
|
-
const
|
|
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
|
|
344
|
-
} catch (
|
|
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(
|
|
348
|
+
error: `Error running the WordPress site's "${c.name}" tool: ${p(s)}`
|
|
347
349
|
};
|
|
348
350
|
} finally {
|
|
349
|
-
|
|
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.
|
|
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.
|
|
44
|
-
"@php-wasm/universal": "3.1.
|
|
45
|
-
"@php-wasm/logger": "3.1.
|
|
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": "
|
|
47
|
+
"gitHead": "d9392e2c6a2eb8c5cd5c4d357c725572fac56c12",
|
|
48
48
|
"packageManager": "npm@10.9.2",
|
|
49
49
|
"overrides": {
|
|
50
50
|
"rollup": "^4.34.6",
|
package/webmcp-site-tools.d.ts
CHANGED
|
@@ -13,10 +13,15 @@
|
|
|
13
13
|
* → remote frame (postMessage)
|
|
14
14
|
* → WordPress document (the plugin's execute())
|
|
15
15
|
*/
|
|
16
|
-
import type {
|
|
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:
|
|
32
|
+
export declare function startWebMCPSiteToolProxy(client: WebMCPSiteToolClient): WebMCPSiteToolProxy;
|
|
33
|
+
export {};
|