@wp-playground/mcp 3.1.38 → 3.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,11 +42,40 @@ Add to `~/.gemini/settings.json` (or `.gemini/settings.json` in your project):
42
42
  ### 2. Open the Playground website
43
43
 
44
44
  Your AI assistant will ask you to open the Playground website and provide the exact URL. You can also ask it: _"What's the Playground website URL?"_
45
+ The MCP server chooses the local bridge connection automatically, so you do not need to configure it in your MCP client.
46
+
47
+ To connect to Personal Playground, pass its URL to the MCP server:
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "wordpress-playground": {
53
+ "type": "stdio",
54
+ "command": "npx",
55
+ "args": ["-y", "@wp-playground/mcp", "--url=https://my.wordpress.net/"]
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ For a staging deployment, use that origin instead:
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "wordpress-playground": {
67
+ "type": "stdio",
68
+ "command": "npx",
69
+ "args": ["-y", "@wp-playground/mcp", "--url=https://mywp.kirk.at/"]
70
+ }
71
+ }
72
+ }
73
+ ```
45
74
 
46
75
  ## How it works
47
76
 
48
77
  ```
49
- AI Client (stdio) → MCP Server (Node.js) → WebSocket (port 7999) → Browser (Playground website)
78
+ AI Client (stdio) → MCP Server (Node.js) → WebSocket → Browser (Playground website)
50
79
  ```
51
80
 
52
81
  The MCP server communicates with AI clients via stdio and with the browser via WebSocket. A bridge client (`bridge-client.ts`) integrated into the Playground website via Redux middleware auto-connects to the WebSocket server and proxies commands to the PlaygroundClient API.
@@ -105,4 +134,4 @@ Replace `ABS_PATH_TO_PLAYGROUND` with the absolute path to your local checkout o
105
134
 
106
135
  ### 3. Open the Playground website
107
136
 
108
- Navigate to http://127.0.0.1:5400/website-server/?mcp=yes in your browser. The MCP bridge connects automatically.
137
+ Ask your AI assistant for the Playground website URL and open it in your browser. The MCP bridge connects automatically.
@@ -19,6 +19,7 @@ export interface SiteEntry {
19
19
  activeInTabs: string[];
20
20
  }
21
21
  export declare class PlaygroundBridge {
22
+ private additionalAllowedOrigins;
22
23
  private connections;
23
24
  private sites;
24
25
  private pendingRequests;
@@ -27,9 +28,11 @@ export declare class PlaygroundBridge {
27
28
  private httpServer;
28
29
  private sessionToken;
29
30
  private siteActivatedListeners;
31
+ constructor(additionalAllowedUrls?: string[]);
30
32
  getPort(): number;
31
33
  startWebSocketServer(port?: number): Promise<WebSocketServer>;
32
34
  private handleHttpRequest;
35
+ private isAllowedOrigin;
33
36
  private handleConnection;
34
37
  private updateSitesForTab;
35
38
  sendCommand(siteId: string, method: string, args?: unknown[]): Promise<unknown>;
package/client.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./tool-executors-CnK8qvl5.cjs"),v=5e3;function b(r,o){const c=crypto.randomUUID();let e=null,n="",t=null,l=!1;function s(u){const d=r.list(),p=JSON.stringify(d);p!==n&&(n=p,u.send(JSON.stringify({type:"register",tabId:c,sites:d})))}async function i(){try{const u=await fetch(`http://127.0.0.1:${o}/bridge-token`);if(!u.ok){f();return}const{token:d}=await u.json();e=new WebSocket(`ws://127.0.0.1:${o}?token=${d}`)}catch{f();return}e.addEventListener("open",()=>{var u;n="",s(e),(u=r.onConnect)==null||u.call(r)}),e.addEventListener("message",async u=>{let d;try{d=JSON.parse(u.data)}catch{return}if(d.type!=="command")return;const{id:p,method:w,args:_,siteSlug:S}=d;try{const y=await E(r,w,_||[],S,o);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",value:y}))}catch(y){const h=y instanceof Error?y.message:String(y);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",error:h}))}}),e.addEventListener("close",()=>{e=null,f()}),e.addEventListener("error",()=>{})}function f(){l||(t=setTimeout(i,v))}return i(),{notifySitesChanged:()=>{(e==null?void 0:e.readyState)===WebSocket.OPEN&&s(e)},stop:()=>{l=!0,t!==null&&(clearTimeout(t),t=null),e&&(e.close(),e=null)}}}async function E(r,o,c,e,n){if(o==="__open_site_in_new_tab"){const i=new URL(window.location.href);if(i.searchParams.set("mcp","yes"),i.searchParams.set("mcp-port",String(n)),i.searchParams.set("site-slug",e),!window.open(i.toString(),"_blank"))throw new Error("Pop-up blocked by browser. The user must allow pop-ups for this site.");return!0}if(o==="__rename_site"){const[i]=c;return await r.rename(i),!0}if(o==="__save_site")return await r.saveInBrowser();const t=r.getClient();if(!t)throw new Error(`No active client for site: ${e}`);const s=a.createToolClient(t)[o];if(typeof s!="function")throw new Error(`Unknown method: ${o}`);return await s(...c)}const m=a.getSiteToolDefinitions();let g=null;function N(r){const c=r.list().find(e=>e.isActive);if(!c)throw new Error("No active Playground site");return c}function T(r){if(typeof navigator>"u"||!navigator.modelContext)return;g==null||g.abort(),g=new AbortController;const o=g.signal;function c(){const n=r.getClient();if(!n)throw new Error("No client for active site");return n}const e=Object.entries(a.toolDefinitions).map(([n,t])=>({name:n,description:t.description,inputSchema:a.paramsToJsonSchema(t.params),annotations:t.annotations,execute:async l=>{try{const s=a.toolExecutors[n];if(!s)return{error:`No executor for "${n}"`};const i=a.createToolClient(c());return await s(i,l)}catch(s){return{error:`${t.errorPrefix}: ${a.stringifyError(s)}`}}}}));e.push(...x(r));for(const n of e)navigator.modelContext.registerTool(n,{signal:o})}function x(r){const o=m.playground_list_sites,c=m.playground_save_in_browser,e=m.playground_rename_site,n=m.playground_get_website_url;return[{name:"playground_list_sites",description:o.description,annotations:o.annotations,execute:async()=>{try{return{connectedTabs:1,sites:r.list().map(t=>({siteId:t.slug,name:t.name,storage:a.formatStorageLabel(t.storage),isActive:t.isActive}))}}catch(t){return{error:`${o.errorPrefix}: ${a.stringifyError(t)}`}}}},{name:"playground_save_in_browser",description:c.description,annotations:c.annotations,execute:async()=>{try{const t=N(r),l=a.formatStorageLabel(t.storage);if(l!=="temporary")return{success:!0,alreadySaved:!0,siteId:t.slug,name:t.name,storage:l};const s=await r.saveInBrowser();return{success:!0,alreadySaved:!1,siteId:s.slug,name:t.name??s.slug,storage:a.formatStorageLabel(s.storage)}}catch(t){return{error:`${c.errorPrefix}: ${a.stringifyError(t)}`}}}},{name:"playground_rename_site",description:e.description,inputSchema:a.paramsToJsonSchema(e.params),annotations:e.annotations,execute:async t=>{try{const l=t.newName,i=r.list().find(f=>f.isActive);return await r.rename(l),{success:!0,siteId:i==null?void 0:i.slug,newName:l}}catch(l){return{error:`${e.errorPrefix}: ${a.stringifyError(l)}`}}}},{name:"playground_get_website_url",description:n.description,annotations:n.annotations,execute:async()=>{try{return{url:window.location.href}}catch(t){return{error:`${n.errorPrefix}: ${a.stringifyError(t)}`}}}}]}exports.registerWebMCPTools=T;exports.startMcpBridge=b;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./tool-executors-DtS6CH_z.cjs"),b=5e3;function h(r,o){const c=crypto.randomUUID();let e=null,n="",t=null,l=!1;function s(u){const d=r.list(),p=JSON.stringify(d);p!==n&&(n=p,u.send(JSON.stringify({type:"register",tabId:c,sites:d})))}async function a(){try{const u=await fetch(`http://127.0.0.1:${o}/bridge-token`);if(!u.ok){f();return}const{token:d}=await u.json();e=new WebSocket(`ws://127.0.0.1:${o}?token=${d}`)}catch{f();return}e.addEventListener("open",()=>{var u;n="",s(e),(u=r.onConnect)==null||u.call(r)}),e.addEventListener("message",async u=>{let d;try{d=JSON.parse(u.data)}catch{return}if(d.type!=="command")return;const{id:p,method:w,args:_,siteSlug:S}=d;try{const y=await E(r,w,_||[],S,o);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",value:y}))}catch(y){const v=y instanceof Error?y.message:String(y);(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify({id:p,type:"response",error:v}))}}),e.addEventListener("close",()=>{e=null,f()}),e.addEventListener("error",()=>{})}function f(){l||(t=setTimeout(a,b))}return a(),{notifySitesChanged:()=>{(e==null?void 0:e.readyState)===WebSocket.OPEN&&s(e)},stop:()=>{l=!0,t!==null&&(clearTimeout(t),t=null),e&&(e.close(),e=null)}}}async function E(r,o,c,e,n){if(o==="__open_site_in_new_tab"){const a=new URL(window.location.href);if(a.searchParams.set("mcp-port",String(n)),a.searchParams.set("site-slug",e),!window.open(a.toString(),"_blank"))throw new Error("Pop-up blocked by browser. The user must allow pop-ups for this site.");return!0}if(o==="__rename_site"){const[a]=c;return await r.rename(a),!0}if(o==="__save_site")return await r.saveInBrowser();const t=r.getClient();if(!t)throw new Error(`No active client for site: ${e}`);const s=i.createToolClient(t)[o];if(typeof s!="function")throw new Error(`Unknown method: ${o}`);return await s(...c)}const m=i.getSiteToolDefinitions();let g=null;function N(r){const c=r.list().find(e=>e.isActive);if(!c)throw new Error("No active Playground site");return c}function T(r){if(typeof navigator>"u"||!navigator.modelContext)return;g==null||g.abort(),g=new AbortController;const o=g.signal;function c(){const n=r.getClient();if(!n)throw new Error("No client for active site");return n}const e=Object.entries(i.toolDefinitions).map(([n,t])=>({name:n,description:t.description,inputSchema:i.paramsToJsonSchema(t.params),annotations:t.annotations,execute:async l=>{try{const s=i.toolExecutors[n];if(!s)return{error:`No executor for "${n}"`};const a=i.createToolClient(c());return await s(a,l)}catch(s){return{error:`${t.errorPrefix}: ${i.stringifyError(s)}`}}}}));e.push(...x(r));for(const n of e)navigator.modelContext.registerTool(n,{signal:o})}function x(r){const o=m.playground_list_sites,c=m.playground_save_in_browser,e=m.playground_rename_site,n=m.playground_get_website_url;return[{name:"playground_list_sites",description:o.description,annotations:o.annotations,execute:async()=>{try{return{connectedTabs:1,sites:r.list().map(t=>({siteId:t.slug,name:t.name,storage:i.formatStorageLabel(t.storage),isActive:t.isActive}))}}catch(t){return{error:`${o.errorPrefix}: ${i.stringifyError(t)}`}}}},{name:"playground_save_in_browser",description:c.description,annotations:c.annotations,execute:async()=>{try{const t=N(r),l=i.formatStorageLabel(t.storage);if(l!=="temporary")return{success:!0,alreadySaved:!0,siteId:t.slug,name:t.name,storage:l};const s=await r.saveInBrowser();return{success:!0,alreadySaved:!1,siteId:s.slug,name:t.name??s.slug,storage:i.formatStorageLabel(s.storage)}}catch(t){return{error:`${c.errorPrefix}: ${i.stringifyError(t)}`}}}},{name:"playground_rename_site",description:e.description,inputSchema:i.paramsToJsonSchema(e.params),annotations:e.annotations,execute:async t=>{try{const l=t.newName,a=r.list().find(f=>f.isActive);return await r.rename(l),{success:!0,siteId:a==null?void 0:a.slug,newName:l}}catch(l){return{error:`${e.errorPrefix}: ${i.stringifyError(l)}`}}}},{name:"playground_get_website_url",description:n.description,annotations:n.annotations,execute:async()=>{try{return{url:window.location.href}}catch(t){return{error:`${n.errorPrefix}: ${i.stringifyError(t)}`}}}}]}exports.registerWebMCPTools=T;exports.startMcpBridge=h;
2
2
  //# sourceMappingURL=client.cjs.map
package/client.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp', 'yes');\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `navigator.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Navigator {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport function registerWebMCPTools(config: PlaygroundBridgeConfig): void {\n\tif (typeof navigator === 'undefined' || !navigator.modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst signal = registrationController.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tfor (const tool of tools) {\n\t\tnavigator.modelContext.registerTool(tool, { signal });\n\t}\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","tool","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":"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"}
1
+ {"version":3,"file":"client.cjs","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `navigator.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Navigator {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport function registerWebMCPTools(config: PlaygroundBridgeConfig): void {\n\tif (typeof navigator === 'undefined' || !navigator.modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst signal = registrationController.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tfor (const tool of tools) {\n\t\tnavigator.modelContext.registerTool(tool, { signal });\n\t}\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","tool","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":"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"}
package/client.js CHANGED
@@ -1,30 +1,30 @@
1
- import { c as _, t as N, a as x, s as y, b as S, f as w, g as $ } from "./tool-executors-SsQekZdW.js";
2
- const P = 5e3;
3
- function I(r, s) {
1
+ import { c as _, t as N, a as x, s as m, b as S, f as w, g as $ } from "./tool-executors-CphtBrKd.js";
2
+ const T = 5e3;
3
+ function I(r, o) {
4
4
  const a = crypto.randomUUID();
5
5
  let e = null, n = "", t = null, c = !1;
6
- function o(u) {
6
+ function s(u) {
7
7
  const l = r.list(), p = JSON.stringify(l);
8
8
  p !== n && (n = p, u.send(JSON.stringify({ type: "register", tabId: a, sites: l })));
9
9
  }
10
10
  async function i() {
11
11
  try {
12
12
  const u = await fetch(
13
- `http://127.0.0.1:${s}/bridge-token`
13
+ `http://127.0.0.1:${o}/bridge-token`
14
14
  );
15
15
  if (!u.ok) {
16
16
  d();
17
17
  return;
18
18
  }
19
19
  const { token: l } = await u.json();
20
- e = new WebSocket(`ws://127.0.0.1:${s}?token=${l}`);
20
+ e = new WebSocket(`ws://127.0.0.1:${o}?token=${l}`);
21
21
  } catch {
22
22
  d();
23
23
  return;
24
24
  }
25
25
  e.addEventListener("open", () => {
26
26
  var u;
27
- n = "", o(e), (u = r.onConnect) == null || u.call(r);
27
+ n = "", s(e), (u = r.onConnect) == null || u.call(r);
28
28
  }), e.addEventListener("message", async (u) => {
29
29
  let l;
30
30
  try {
@@ -34,14 +34,14 @@ function I(r, s) {
34
34
  }
35
35
  if (l.type !== "command")
36
36
  return;
37
- const { id: p, method: h, args: v, siteSlug: b } = l;
37
+ const { id: p, method: v, args: h, siteSlug: b } = l;
38
38
  try {
39
- const f = await T(
39
+ const f = await C(
40
40
  r,
41
- h,
42
- v || [],
41
+ v,
42
+ h || [],
43
43
  b,
44
- s
44
+ o
45
45
  );
46
46
  (e == null ? void 0 : e.readyState) === WebSocket.OPEN && e.send(JSON.stringify({ id: p, type: "response", value: f }));
47
47
  } catch (f) {
@@ -60,43 +60,43 @@ function I(r, s) {
60
60
  });
61
61
  }
62
62
  function d() {
63
- c || (t = setTimeout(i, P));
63
+ c || (t = setTimeout(i, T));
64
64
  }
65
65
  return i(), {
66
66
  notifySitesChanged: () => {
67
- (e == null ? void 0 : e.readyState) === WebSocket.OPEN && o(e);
67
+ (e == null ? void 0 : e.readyState) === WebSocket.OPEN && s(e);
68
68
  },
69
69
  stop: () => {
70
70
  c = !0, t !== null && (clearTimeout(t), t = null), e && (e.close(), e = null);
71
71
  }
72
72
  };
73
73
  }
74
- async function T(r, s, a, e, n) {
75
- if (s === "__open_site_in_new_tab") {
74
+ async function C(r, o, a, e, n) {
75
+ if (o === "__open_site_in_new_tab") {
76
76
  const i = new URL(window.location.href);
77
- if (i.searchParams.set("mcp", "yes"), i.searchParams.set("mcp-port", String(n)), i.searchParams.set("site-slug", e), !window.open(i.toString(), "_blank"))
77
+ if (i.searchParams.set("mcp-port", String(n)), i.searchParams.set("site-slug", e), !window.open(i.toString(), "_blank"))
78
78
  throw new Error(
79
79
  "Pop-up blocked by browser. The user must allow pop-ups for this site."
80
80
  );
81
81
  return !0;
82
82
  }
83
- if (s === "__rename_site") {
83
+ if (o === "__rename_site") {
84
84
  const [i] = a;
85
85
  return await r.rename(i), !0;
86
86
  }
87
- if (s === "__save_site")
87
+ if (o === "__save_site")
88
88
  return await r.saveInBrowser();
89
89
  const t = r.getClient();
90
90
  if (!t)
91
91
  throw new Error(`No active client for site: ${e}`);
92
- const o = _(t)[s];
93
- if (typeof o != "function")
94
- throw new Error(`Unknown method: ${s}`);
95
- return await o(...a);
92
+ const s = _(t)[o];
93
+ if (typeof s != "function")
94
+ throw new Error(`Unknown method: ${o}`);
95
+ return await s(...a);
96
96
  }
97
97
  const g = $();
98
- let m = null;
99
- function C(r) {
98
+ let y = null;
99
+ function P(r) {
100
100
  const a = r.list().find((e) => e.isActive);
101
101
  if (!a)
102
102
  throw new Error("No active Playground site");
@@ -105,8 +105,8 @@ function C(r) {
105
105
  function A(r) {
106
106
  if (typeof navigator > "u" || !navigator.modelContext)
107
107
  return;
108
- m == null || m.abort(), m = new AbortController();
109
- const s = m.signal;
108
+ y == null || y.abort(), y = new AbortController();
109
+ const o = y.signal;
110
110
  function a() {
111
111
  const n = r.getClient();
112
112
  if (!n)
@@ -121,16 +121,16 @@ function A(r) {
121
121
  annotations: t.annotations,
122
122
  execute: async (c) => {
123
123
  try {
124
- const o = x[n];
125
- if (!o)
124
+ const s = x[n];
125
+ if (!s)
126
126
  return {
127
127
  error: `No executor for "${n}"`
128
128
  };
129
129
  const i = _(a());
130
- return await o(i, c);
131
- } catch (o) {
130
+ return await s(i, c);
131
+ } catch (s) {
132
132
  return {
133
- error: `${t.errorPrefix}: ${y(o)}`
133
+ error: `${t.errorPrefix}: ${m(s)}`
134
134
  };
135
135
  }
136
136
  }
@@ -138,15 +138,15 @@ function A(r) {
138
138
  );
139
139
  e.push(...k(r));
140
140
  for (const n of e)
141
- navigator.modelContext.registerTool(n, { signal: s });
141
+ navigator.modelContext.registerTool(n, { signal: o });
142
142
  }
143
143
  function k(r) {
144
- const s = g.playground_list_sites, a = g.playground_save_in_browser, e = g.playground_rename_site, n = g.playground_get_website_url;
144
+ const o = g.playground_list_sites, a = g.playground_save_in_browser, e = g.playground_rename_site, n = g.playground_get_website_url;
145
145
  return [
146
146
  {
147
147
  name: "playground_list_sites",
148
- description: s.description,
149
- annotations: s.annotations,
148
+ description: o.description,
149
+ annotations: o.annotations,
150
150
  execute: async () => {
151
151
  try {
152
152
  return {
@@ -160,7 +160,7 @@ function k(r) {
160
160
  };
161
161
  } catch (t) {
162
162
  return {
163
- error: `${s.errorPrefix}: ${y(t)}`
163
+ error: `${o.errorPrefix}: ${m(t)}`
164
164
  };
165
165
  }
166
166
  }
@@ -171,7 +171,7 @@ function k(r) {
171
171
  annotations: a.annotations,
172
172
  execute: async () => {
173
173
  try {
174
- const t = C(r), c = w(t.storage);
174
+ const t = P(r), c = w(t.storage);
175
175
  if (c !== "temporary")
176
176
  return {
177
177
  success: !0,
@@ -180,17 +180,17 @@ function k(r) {
180
180
  name: t.name,
181
181
  storage: c
182
182
  };
183
- const o = await r.saveInBrowser();
183
+ const s = await r.saveInBrowser();
184
184
  return {
185
185
  success: !0,
186
186
  alreadySaved: !1,
187
- siteId: o.slug,
188
- name: t.name ?? o.slug,
189
- storage: w(o.storage)
187
+ siteId: s.slug,
188
+ name: t.name ?? s.slug,
189
+ storage: w(s.storage)
190
190
  };
191
191
  } catch (t) {
192
192
  return {
193
- error: `${a.errorPrefix}: ${y(t)}`
193
+ error: `${a.errorPrefix}: ${m(t)}`
194
194
  };
195
195
  }
196
196
  }
@@ -210,7 +210,7 @@ function k(r) {
210
210
  };
211
211
  } catch (c) {
212
212
  return {
213
- error: `${e.errorPrefix}: ${y(c)}`
213
+ error: `${e.errorPrefix}: ${m(c)}`
214
214
  };
215
215
  }
216
216
  }
@@ -226,7 +226,7 @@ function k(r) {
226
226
  };
227
227
  } catch (t) {
228
228
  return {
229
- error: `${n.errorPrefix}: ${y(t)}`
229
+ error: `${n.errorPrefix}: ${m(t)}`
230
230
  };
231
231
  }
232
232
  }
package/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp', 'yes');\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `navigator.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Navigator {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport function registerWebMCPTools(config: PlaygroundBridgeConfig): void {\n\tif (typeof navigator === 'undefined' || !navigator.modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst signal = registrationController.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tfor (const tool of tools) {\n\t\tnavigator.modelContext.registerTool(tool, { signal });\n\t}\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","tool","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sources":["../../../../packages/playground/mcp/src/bridge-client.ts","../../../../packages/playground/mcp/src/webmcp.ts"],"sourcesContent":["import type { PlaygroundClient } from '@wp-playground/remote';\nimport { createToolClient } from './tools/tool-executors';\nimport type { ToolClient } from './tools/tool-executors';\n\n/**\n * Configuration accepted by `startMcpBridge`. Only includes the\n * methods the bridge actually calls — callers can pass any wider\n * object (e.g. the full site-management API) and TypeScript will\n * accept it structurally.\n */\nexport interface PlaygroundBridgeConfig {\n\tlist(): Array<{\n\t\tslug: string;\n\t\tname: string;\n\t\tstorage: string;\n\t\tisActive: boolean;\n\t}>;\n\tgetClient(): PlaygroundClient | undefined;\n\trename(newName: string): Promise<void>;\n\tsaveInBrowser(): Promise<{ slug: string; storage: string }>;\n\tonConnect?: () => void;\n}\n\nexport interface McpBridgeHandle {\n\tnotifySitesChanged: () => void;\n\tstop: () => void;\n}\n\nconst RECONNECT_INTERVAL_MS = 5000;\n\nexport function startMcpBridge(\n\tconfig: PlaygroundBridgeConfig,\n\tport: number\n): McpBridgeHandle {\n\tconst tabId = crypto.randomUUID();\n\tlet ws: WebSocket | null = null;\n\tlet previousSitesSerialized = '';\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet stopped = false;\n\n\tfunction sendSitesRegistration(socket: WebSocket) {\n\t\tconst sites = config.list();\n\t\tconst serialized = JSON.stringify(sites);\n\t\tif (serialized === previousSitesSerialized) {\n\t\t\treturn;\n\t\t}\n\t\tpreviousSitesSerialized = serialized;\n\t\tsocket.send(JSON.stringify({ type: 'register', tabId, sites }));\n\t}\n\n\tasync function connect() {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://127.0.0.1:${port}/bridge-token`\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\tscheduleReconnect();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { token } = await response.json();\n\t\t\tws = new WebSocket(`ws://127.0.0.1:${port}?token=${token}`);\n\t\t} catch {\n\t\t\tscheduleReconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tws.addEventListener('open', () => {\n\t\t\tpreviousSitesSerialized = '';\n\t\t\tsendSitesRegistration(ws!);\n\t\t\tconfig.onConnect?.();\n\t\t});\n\n\t\tws.addEventListener('message', async (event) => {\n\t\t\tlet message;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(event.data as string);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (message.type !== 'command') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { id, method, args, siteSlug } = message;\n\t\t\ttry {\n\t\t\t\tconst value = await handleCommand(\n\t\t\t\t\tconfig,\n\t\t\t\t\tmethod,\n\t\t\t\t\targs || [],\n\t\t\t\t\tsiteSlug,\n\t\t\t\t\tport\n\t\t\t\t);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(JSON.stringify({ id, type: 'response', value }));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\t\tws.send(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttype: 'response',\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tws.addEventListener('close', () => {\n\t\t\tws = null;\n\t\t\tscheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener('error', () => {\n\t\t\t// Error will be followed by close event,\n\t\t\t// which handles reconnect\n\t\t});\n\t}\n\n\tfunction scheduleReconnect() {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\treconnectTimer = setTimeout(connect, RECONNECT_INTERVAL_MS);\n\t}\n\n\tconnect();\n\n\treturn {\n\t\tnotifySitesChanged: () => {\n\t\t\tif (ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tsendSitesRegistration(ws);\n\t\t\t}\n\t\t},\n\t\tstop: () => {\n\t\t\tstopped = true;\n\t\t\tif (reconnectTimer !== null) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t\treconnectTimer = null;\n\t\t\t}\n\t\t\tif (ws) {\n\t\t\t\tws.close();\n\t\t\t\tws = null;\n\t\t\t}\n\t\t},\n\t};\n}\n\nasync function handleCommand(\n\tconfig: PlaygroundBridgeConfig,\n\tmethod: string,\n\targs: unknown[],\n\tsiteSlug: string,\n\tport: number\n): Promise<unknown> {\n\tif (method === '__open_site_in_new_tab') {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('mcp-port', String(port));\n\t\turl.searchParams.set('site-slug', siteSlug);\n\t\tconst newWindow = window.open(url.toString(), '_blank');\n\t\tif (!newWindow) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pop-up blocked by browser. The user ' +\n\t\t\t\t\t'must allow pop-ups for this site.'\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (method === '__rename_site') {\n\t\tconst [newName] = args as [string];\n\t\tawait config.rename(newName);\n\t\treturn true;\n\t}\n\n\tif (method === '__save_site') {\n\t\treturn await config.saveInBrowser();\n\t}\n\n\tconst playgroundClient = config.getClient();\n\tif (!playgroundClient) {\n\t\tthrow new Error(`No active client for site: ${siteSlug}`);\n\t}\n\n\tconst client = createToolClient(playgroundClient);\n\tconst fn = client[method as keyof ToolClient];\n\tif (typeof fn !== 'function') {\n\t\tthrow new Error(`Unknown method: ${method}`);\n\t}\n\treturn await (fn as (...a: unknown[]) => Promise<unknown>)(...args);\n}\n","/**\n * WebMCP registration for WordPress Playground.\n *\n * Registers playground tools with `navigator.modelContext` (the\n * Chrome WebMCP API) so that browser-side AI agents can interact\n * with the running Playground site.\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport {\n\ttoolDefinitions,\n\tgetSiteToolDefinitions,\n\tformatStorageLabel,\n\tparamsToJsonSchema,\n\tstringifyError,\n} from './tools/tool-definitions';\nimport { toolExecutors, createToolClient } from './tools/tool-executors';\nimport type { PlaygroundBridgeConfig } from './bridge-client';\n\nconst siteToolDefinitions = getSiteToolDefinitions();\n\n// -- WebMCP type declarations --\n\ninterface ModelContextTool {\n\tname: string;\n\tdescription: string;\n\tinputSchema?: Record<string, unknown>;\n\texecute: (\n\t\tinput: Record<string, unknown>,\n\t\tclient: ModelContextClient\n\t) => Promise<unknown>;\n\tannotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\ninterface ModelContextClient {\n\trequestUserInteraction(callback: () => Promise<unknown>): Promise<unknown>;\n}\n\ninterface ModelContext {\n\tprovideContext(options: { tools: ModelContextTool[] }): void;\n\tregisterTool(\n\t\ttool: ModelContextTool,\n\t\toptions?: { signal?: AbortSignal }\n\t): void;\n\treadonly tools: ModelContextTool[];\n}\n\ndeclare global {\n\tinterface Navigator {\n\t\tmodelContext?: ModelContext;\n\t}\n}\n\n// -- Registration --\n\nlet registrationController: AbortController | null = null;\n\nfunction getActiveSite(config: PlaygroundBridgeConfig) {\n\tconst sites = config.list();\n\tconst active = sites.find((s) => s.isActive);\n\tif (!active) {\n\t\tthrow new Error('No active Playground site');\n\t}\n\treturn active;\n}\n\nexport function registerWebMCPTools(config: PlaygroundBridgeConfig): void {\n\tif (typeof navigator === 'undefined' || !navigator.modelContext) {\n\t\treturn;\n\t}\n\n\t// Abort any previous registration before re-registering.\n\tregistrationController?.abort();\n\tregistrationController = new AbortController();\n\tconst signal = registrationController.signal;\n\n\tfunction getActiveClient(): PlaygroundClient {\n\t\tconst client = config.getClient();\n\t\tif (!client) {\n\t\t\tthrow new Error('No client for active site');\n\t\t}\n\t\treturn client;\n\t}\n\n\t// Per-site tools\n\tconst tools: ModelContextTool[] = Object.entries(toolDefinitions).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\tdescription: def.description,\n\t\t\tinputSchema: paramsToJsonSchema(def.params),\n\t\t\tannotations: def.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst executor = toolExecutors[name];\n\t\t\t\t\tif (!executor) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\terror: `No executor for \"${name}\"`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst adapter = createToolClient(getActiveClient());\n\t\t\t\t\treturn await executor(adapter, input);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${def.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t);\n\n\t// Site management tools\n\ttools.push(...createSiteManagementTools(config));\n\n\tfor (const tool of tools) {\n\t\tnavigator.modelContext.registerTool(tool, { signal });\n\t}\n}\n\nfunction createSiteManagementTools(\n\tconfig: PlaygroundBridgeConfig\n): ModelContextTool[] {\n\tconst listDef = siteToolDefinitions['playground_list_sites'];\n\tconst saveDef = siteToolDefinitions['playground_save_in_browser'];\n\tconst renameDef = siteToolDefinitions['playground_rename_site'];\n\tconst websiteUrlDef = siteToolDefinitions['playground_get_website_url'];\n\n\treturn [\n\t\t{\n\t\t\tname: 'playground_list_sites',\n\t\t\tdescription: listDef.description,\n\t\t\tannotations: listDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconnectedTabs: 1,\n\t\t\t\t\t\tsites: config.list().map((s) => ({\n\t\t\t\t\t\t\tsiteId: s.slug,\n\t\t\t\t\t\t\tname: s.name,\n\t\t\t\t\t\t\tstorage: formatStorageLabel(s.storage),\n\t\t\t\t\t\t\tisActive: s.isActive,\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${listDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_save_in_browser',\n\t\t\tdescription: saveDef.description,\n\t\t\tannotations: saveDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst site = getActiveSite(config);\n\t\t\t\t\tconst storage = formatStorageLabel(site.storage);\n\t\t\t\t\tif (storage !== 'temporary') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\talreadySaved: true,\n\t\t\t\t\t\t\tsiteId: site.slug,\n\t\t\t\t\t\t\tname: site.name,\n\t\t\t\t\t\t\tstorage,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst saved = await config.saveInBrowser();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\talreadySaved: false,\n\t\t\t\t\t\tsiteId: saved.slug,\n\t\t\t\t\t\tname: site.name ?? saved.slug,\n\t\t\t\t\t\tstorage: formatStorageLabel(saved.storage),\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${saveDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_rename_site',\n\t\t\tdescription: renameDef.description,\n\t\t\tinputSchema: paramsToJsonSchema(renameDef.params),\n\t\t\tannotations: renameDef.annotations,\n\t\t\texecute: async (input) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst newName = input['newName'] as string;\n\t\t\t\t\tconst sites = config.list();\n\t\t\t\t\tconst activeSite = sites.find((s) => s.isActive);\n\t\t\t\t\tawait config.rename(newName);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tsiteId: activeSite?.slug,\n\t\t\t\t\t\tnewName,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${renameDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'playground_get_website_url',\n\t\t\tdescription: websiteUrlDef.description,\n\t\t\tannotations: websiteUrlDef.annotations,\n\t\t\texecute: async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: window.location.href,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: `${websiteUrlDef.errorPrefix}: ${stringifyError(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t];\n}\n"],"names":["RECONNECT_INTERVAL_MS","startMcpBridge","config","port","tabId","ws","previousSitesSerialized","reconnectTimer","stopped","sendSitesRegistration","socket","sites","serialized","connect","response","scheduleReconnect","token","_a","event","message","id","method","args","siteSlug","value","handleCommand","error","errorMsg","url","newName","playgroundClient","fn","createToolClient","siteToolDefinitions","getSiteToolDefinitions","registrationController","getActiveSite","active","s","registerWebMCPTools","signal","getActiveClient","client","tools","toolDefinitions","name","def","paramsToJsonSchema","input","executor","toolExecutors","adapter","stringifyError","createSiteManagementTools","tool","listDef","saveDef","renameDef","websiteUrlDef","formatStorageLabel","site","storage","saved","activeSite"],"mappings":"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"}