@wp-playground/mcp 3.1.40 → 3.1.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-playground/mcp",
3
- "version": "3.1.40",
3
+ "version": "3.1.41",
4
4
  "description": "MCP server for WordPress Playground - enables AI agents to interact with the WordPress Playground website.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,10 +39,10 @@
39
39
  "@modelcontextprotocol/sdk": "^1.27.0",
40
40
  "ws": "8.21.0",
41
41
  "zod": "^4.3",
42
- "@wp-playground/remote": "3.1.40",
43
- "@php-wasm/universal": "3.1.40"
42
+ "@wp-playground/remote": "3.1.41",
43
+ "@php-wasm/universal": "3.1.41"
44
44
  },
45
- "gitHead": "6d9e3421ede727df67dc158e09c4af2e907ee605",
45
+ "gitHead": "d32ef94fc8b765bd0c6ed7d5ba32e2ebd34b8a75",
46
46
  "packageManager": "npm@10.9.2",
47
47
  "overrides": {
48
48
  "rollup": "^4.34.6",
@@ -1,4 +1,4 @@
1
- "use strict";const h="https://playground.wordpress.net/";function f(e,t){if(!t)return`${h}?mcp-port=${e}`;const n=new URL(t);return n.searchParams.set("mcp-port",String(e)),n.toString()}const y={playground_execute_php:{title:"Execute PHP Code",errorPrefix:"Error executing PHP",description:`Run arbitrary PHP code in WordPress Playground
1
+ "use strict";const y="https://playground.wordpress.net/";function f(e,t){if(!t)return`${y}?mcp-port=${e}`;const n=new URL(t);return n.searchParams.set("mcp-port",String(e)),n.toString()}const g={playground_execute_php:{title:"Execute PHP Code",errorPrefix:"Error executing PHP",description:`Run arbitrary PHP code in WordPress Playground
2
2
  and return the output.
3
3
 
4
4
  WordPress is NOT bootstrapped automatically. To use
@@ -88,7 +88,8 @@
88
88
  "/wp-admin/plugins.php"`,required:!0},{name:"method",type:"string",description:`HTTP method (GET, POST, PUT,
89
89
  DELETE, etc.). Defaults to GET.`,required:!1,default:"GET"},{name:"headers",type:"object",description:`Request headers as string key-value
90
90
  pairs, e.g. {"Content-Type":
91
- "application/json"}`,required:!1,additionalProperties:!0},{name:"body",type:"string",description:"Request body (for POST/PUT requests)",required:!1}]},playground_navigate:{title:"Navigate to URL",errorPrefix:"Error navigating",description:`Navigate to a URL path in WordPress
91
+ "application/json"}`,required:!1,additionalProperties:!0},{name:"body",type:"string_or_object",description:`Request body (for POST/PUT requests).
92
+ Accepts a JSON string or an object.`,required:!1}]},playground_navigate:{title:"Navigate to URL",errorPrefix:"Error navigating",description:`Navigate to a URL path in WordPress
92
93
  Playground and return the final URL after any
93
94
  redirects. Examples: "/wp-admin/",
94
95
  "/wp-login.php", "/".
@@ -148,7 +149,7 @@
148
149
  contents — use with care.`,annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!0},params:[{name:"path",type:"string",description:"Absolute path of directory to delete",required:!0},{name:"recursive",type:"boolean",description:`If true, delete directory and
149
150
  all contents. If false (default), fails
150
151
  on non-empty directories.`,required:!1,default:!1}]},playground_file_exists:{title:"File Exists",errorPrefix:"Error checking file existence",description:`Check whether a file or directory exists
151
- in the WordPress virtual filesystem.`,annotations:{readOnlyHint:!0,destructiveHint:!1,openWorldHint:!0},params:[{name:"path",type:"string",description:"Absolute path to check",required:!0}]}};function g(){return{playground_list_sites:{title:"List Available Sites",errorPrefix:"Error listing sites",description:`List all WordPress Playground sites
152
+ in the WordPress virtual filesystem.`,annotations:{readOnlyHint:!0,destructiveHint:!1,openWorldHint:!0},params:[{name:"path",type:"string",description:"Absolute path to check",required:!0}]}};function m(){return{playground_list_sites:{title:"List Available Sites",errorPrefix:"Error listing sites",description:`List all WordPress Playground sites
152
153
  available. Call this before any other playground
153
154
  tool — it returns the siteId required by every
154
155
  other operation.
@@ -182,12 +183,12 @@
182
183
  server. Use this tool whenever you need to send
183
184
  the user to Playground or open it autonomously —
184
185
  always obtain the URL from this tool rather than
185
- constructing it manually.`,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},params:[]}}}function m(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function w(e){return e==="none"?"temporary":e}function _(e){const t={},n=[];for(const r of e){const s={type:r.type,description:r.description};r.additionalProperties!==void 0&&(s.additionalProperties=r.additionalProperties),r.default!==void 0&&(s.default=r.default),t[r.name]=s,r.required&&n.push(r.name)}const i={type:"object",properties:t};return n.length>0&&(i.required=n),i}async function P(e){const[t,n]=await Promise.all([e.getCurrentURL(),e.run({code:`<?php
186
+ constructing it manually.`,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},params:[]}}}function w(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function _(e){return e==="none"?"temporary":e}function b(e){const t={},n=[];for(const r of e){const o={};r.type==="string_or_object"?o.oneOf=[{type:"string"},{type:"object"}]:o.type=r.type,o.description=r.description,r.additionalProperties!==void 0&&(o.additionalProperties=r.additionalProperties),r.default!==void 0&&(o.default=r.default),t[r.name]=o,r.required&&n.push(r.name)}const i={type:"object",properties:t};return n.length>0&&(i.required=n),i}async function P(e){const[t,n]=await Promise.all([e.getCurrentURL(),e.run({code:`<?php
186
187
  require_once "/wordpress/wp-load.php";
187
188
  echo json_encode([
188
189
  "documentRoot" => ABSPATH,
189
190
  "wpVersion" => get_bloginfo("version"),
190
191
  "siteUrl" => get_site_url(),
191
192
  "phpVersion" => phpversion(),
192
- ]);`}).then(r=>r.text)]);let i;try{i=JSON.parse(n)}catch{i={}}return{url:String(t),documentRoot:i.documentRoot??"/wordpress",siteUrl:i.siteUrl??String(t),wpVersion:i.wpVersion??"unknown",phpVersion:i.phpVersion??"unknown"}}const b={playground_execute_php:(e,t)=>e.run({code:t.code}),playground_request:async(e,t)=>{const n=t.url,i=t.method??"GET",r={...t.headers??{}},s=t.body;try{const a=new URL(n,"http://localhost"),d=n.includes("/wp-json/")||a.searchParams.has("rest_route");d&&s&&!Object.keys(r).some(o=>o.toLowerCase()==="content-type")&&(r["Content-Type"]="application/json");const p=Object.keys(r).some(o=>o.toLowerCase()==="x-wp-nonce");if(d&&!p){const o=Math.random().toString(36).slice(2,10),u=`/wordpress/wp-content/mcp-nonce-${o}.php`,c=`/wp-content/mcp-nonce-${o}.php`;await e.writeFile(u,"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');");let l="";try{l=(await e.request({url:c,method:"GET"})).text.trim()}finally{await e.unlink(u)}l&&l!=="0"&&(r["X-WP-Nonce"]=l)}}catch{}return await e.request({url:n,method:i,headers:r,body:s})},playground_navigate:async(e,t)=>(await e.goTo(t.path),{url:await e.getCurrentURL()}),playground_get_current_url:async e=>({url:await e.getCurrentURL()}),playground_get_site_info:e=>P(e),playground_read_file:async(e,t)=>({contents:await e.readFileAsText(t.path)}),playground_write_file:async(e,t)=>(await e.writeFile(t.path,t.contents),{success:!0}),playground_list_files:async(e,t)=>({files:await e.listFiles(t.path)}),playground_mkdir:async(e,t)=>(await e.mkdirTree(t.path),{success:!0}),playground_delete_file:async(e,t)=>(await e.unlink(t.path),{success:!0}),playground_delete_directory:async(e,t)=>(await e.rmdir(t.path,{recursive:t.recursive??!1}),{success:!0}),playground_file_exists:async(e,t)=>({exists:await e.fileExists(t.path)})};function v(e){const t=new TextDecoder,n={async run(i){const r=await e.run(i);return{text:t.decode(r.bytes),errors:r.errors,exitCode:r.exitCode}},async request(i){const r=await e.request({url:i.url,method:i.method,headers:i.headers,body:i.body});return{text:t.decode(r.bytes),httpStatusCode:r.httpStatusCode,headers:r.headers}}};return new Proxy(e,{get:(i,r)=>{const s=n[r];if(s!==void 0)return s;const a=i[r];return typeof a=="function"?a.bind(i):a}})}exports.createToolClient=v;exports.formatStorageLabel=w;exports.getSiteToolDefinitions=g;exports.paramsToJsonSchema=_;exports.playgroundUrl=f;exports.stringifyError=m;exports.toolDefinitions=y;exports.toolExecutors=b;
193
- //# sourceMappingURL=tool-executors-DtS6CH_z.cjs.map
193
+ ]);`}).then(r=>r.text)]);let i;try{i=JSON.parse(n)}catch{i={}}return{url:String(t),documentRoot:i.documentRoot??"/wordpress",siteUrl:i.siteUrl??String(t),wpVersion:i.wpVersion??"unknown",phpVersion:i.phpVersion??"unknown"}}const v={playground_execute_php:(e,t)=>e.run({code:t.code}),playground_request:async(e,t)=>{const n=t.url,i=t.method??"GET",r={...t.headers??{}},o=t.body,s=typeof o=="object"&&o!==null?JSON.stringify(o):o;try{const p=new URL(n,"http://localhost"),d=n.includes("/wp-json/")||p.searchParams.has("rest_route");d&&s&&!Object.keys(r).some(a=>a.toLowerCase()==="content-type")&&(r["Content-Type"]="application/json");const c=Object.keys(r).some(a=>a.toLowerCase()==="x-wp-nonce");if(d&&!c){const a=Math.random().toString(36).slice(2,10),u=`/wordpress/wp-content/mcp-nonce-${a}.php`,h=`/wp-content/mcp-nonce-${a}.php`;await e.writeFile(u,"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');");let l="";try{l=(await e.request({url:h,method:"GET"})).text.trim()}finally{await e.unlink(u)}l&&l!=="0"&&(r["X-WP-Nonce"]=l)}}catch{}return await e.request({url:n,method:i,headers:r,body:s})},playground_navigate:async(e,t)=>(await e.goTo(t.path),{url:await e.getCurrentURL()}),playground_get_current_url:async e=>({url:await e.getCurrentURL()}),playground_get_site_info:e=>P(e),playground_read_file:async(e,t)=>({contents:await e.readFileAsText(t.path)}),playground_write_file:async(e,t)=>(await e.writeFile(t.path,t.contents),{success:!0}),playground_list_files:async(e,t)=>({files:await e.listFiles(t.path)}),playground_mkdir:async(e,t)=>(await e.mkdirTree(t.path),{success:!0}),playground_delete_file:async(e,t)=>(await e.unlink(t.path),{success:!0}),playground_delete_directory:async(e,t)=>(await e.rmdir(t.path,{recursive:t.recursive??!1}),{success:!0}),playground_file_exists:async(e,t)=>({exists:await e.fileExists(t.path)})};function H(e){const t=new TextDecoder,n={async run(i){const r=await e.run(i);return{text:t.decode(r.bytes),errors:r.errors,exitCode:r.exitCode}},async request(i){const r=await e.request({url:i.url,method:i.method,headers:i.headers,body:i.body});return{text:t.decode(r.bytes),httpStatusCode:r.httpStatusCode,headers:r.headers}}};return new Proxy(e,{get:(i,r)=>{const o=n[r];if(o!==void 0)return o;const s=i[r];return typeof s=="function"?s.bind(i):s}})}exports.createToolClient=H;exports.formatStorageLabel=_;exports.getSiteToolDefinitions=m;exports.paramsToJsonSchema=b;exports.playgroundUrl=f;exports.stringifyError=w;exports.toolDefinitions=g;exports.toolExecutors=v;
194
+ //# sourceMappingURL=tool-executors-DX_lR1Jc.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-executors-DX_lR1Jc.cjs","sources":["../../../../packages/playground/mcp/src/tools/tool-definitions.ts","../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Tool metadata and schema helpers for WordPress Playground.\n *\n * Pure data — no execution logic. Both the MCP server and\n * WebMCP import these for consistent descriptions, annotations,\n * and schema conversion.\n */\n\nexport interface ToolAnnotations {\n\treadOnlyHint?: boolean;\n\tdestructiveHint?: boolean;\n\tidempotentHint?: boolean;\n\topenWorldHint?: boolean;\n}\n\nexport type ToolParamType =\n\t| 'string'\n\t| 'boolean'\n\t| 'object'\n\t| 'string_or_object';\n\nexport interface ToolParam {\n\tname: string;\n\ttype: ToolParamType;\n\tdescription: string;\n\trequired: boolean;\n\tadditionalProperties?: boolean;\n\tdefault?: unknown;\n}\n\nexport interface ToolDefinition {\n\ttitle: string;\n\tdescription: string;\n\terrorPrefix: string;\n\tannotations: ToolAnnotations;\n\tparams: ToolParam[];\n}\n\nconst PLAYGROUND_BASE_URL = 'https://playground.wordpress.net/';\n\nexport function playgroundUrl(port: number, baseUrl?: string): string {\n\tif (!baseUrl) {\n\t\treturn `${PLAYGROUND_BASE_URL}?mcp-port=${port}`;\n\t}\n\tconst url = new URL(baseUrl);\n\turl.searchParams.set('mcp-port', String(port));\n\treturn url.toString();\n}\n\n// -- Per-site tool definitions --\n\nexport const toolDefinitions: Record<string, ToolDefinition> = {\n\tplayground_execute_php: {\n\t\ttitle: 'Execute PHP Code',\n\t\terrorPrefix: 'Error executing PHP',\n\t\tdescription: `Run arbitrary PHP code in WordPress Playground\n\t\t\tand return the output.\n\n\t\t\tWordPress is NOT bootstrapped automatically. To use\n\t\t\tWordPress functions, start your code with:\n\t\t\trequire(\"/wordpress/wp-load.php\");\n\t\t\tAlways include the opening <?php tag.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": stdout output\n\t\t\t- \"errors\": PHP warnings, notices, and fatal error\n\t\t\t messages from stderr\n\t\t\t- \"exitCode\": 0 on success, non-zero on fatal error\n\t\t\tCheck both \"errors\" and \"exitCode\" to determine\n\t\t\twhether the call succeeded.\n\n\t\t\tWARNING: output is returned in full with no\n\t\t\ttruncation — avoid queries that produce unbounded\n\t\t\toutput (e.g. SELECT * without LIMIT). Keep output\n\t\t\tunder 50 KB to avoid filling the context window.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'code',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `PHP code to execute. Example:\n\t\t\t\t\t\"<?php echo get_bloginfo('name');\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_request: {\n\t\ttitle: 'HTTP Request',\n\t\terrorPrefix: 'Error making request',\n\t\tdescription: `Make an HTTP request to the WordPress site\n\t\t\trunning in Playground. REST API requests\n\t\t\t(/wp-json/* or ?rest_route=) are automatically\n\t\t\tauthenticated with a valid nonce — no manual\n\t\t\tauth needed.\n\n\t\t\tTool selection guide:\n\t\t\t1. Use this tool (playground_request) with the\n\t\t\t REST API for standard content CRUD — posts,\n\t\t\t pages, users, terms, comments, settings, etc.\n\t\t\t The REST API handles serialization, pagination,\n\t\t\t and field filtering for you. Prefer compact\n\t\t\t REST responses: use _fields to request only the\n\t\t\t fields needed and per_page/page for large\n\t\t\t collections. Before using endpoint-specific\n\t\t\t filters, inspect the route metadata from\n\t\t\t /wp-json/ and use only args documented for that\n\t\t\t route.\n\t\t\t2. Use this tool with the WordPress REST API to\n\t\t\t discover and call registered abilities when the\n\t\t\t site exposes the Abilities API:\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities\n\t\t\t lists registered abilities. Always assume this\n\t\t\t response may be large and start with _fields,\n\t\t\t e.g.\n\t\t\t /wp-json/wp-abilities/v1/abilities?_fields=name,category,label,description\n\t\t\t Do not assume filters like search or category\n\t\t\t exist unless /wp-json/ route metadata documents\n\t\t\t them.\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities/{name}\n\t\t\t gets one ability, including schemas and\n\t\t\t metadata. Ability names include \"/\" in the\n\t\t\t path; do not encode it. For example, use\n\t\t\t /wp-json/wp-abilities/v1/abilities/memex/save-note\n\t\t\t for \"memex/save-note\".\n\t\t\t - GET, POST, or DELETE\n\t\t\t /wp-json/wp-abilities/v1/abilities/{name}/run\n\t\t\t executes the ability. Inspect the ability\n\t\t\t metadata first, then call /run with the\n\t\t\t documented method. POST bodies usually wrap\n\t\t\t arguments in an \"input\" object, e.g.\n\t\t\t {\"input\":{\"title\":\"Hello\"}}.\n\t\t\t - GET /wp-json/wp-abilities/v1/categories\n\t\t\t lists ability categories. Each category includes\n\t\t\t _links.abilities; follow that URL to list\n\t\t\t abilities in that category. The abilities\n\t\t\t collection can also be filtered directly with\n\t\t\t /wp-json/wp-abilities/v1/abilities?category={slug}.\n\t\t\t3. Use playground_execute_php when the data you\n\t\t\t need is not exposed by the REST API (e.g.\n\t\t\t raw options, direct database queries, or\n\t\t\t custom table access).\n\t\t\t4. Use this tool as a plain HTTP request (non-REST)\n\t\t\t when the HTTP layer itself matters: verifying\n\t\t\t redirects, status codes, cookies, or response\n\t\t\t headers.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": the response body as a string\n\t\t\t- \"httpStatusCode\": HTTP status code (200, 404…)\n\t\t\t- \"headers\": response headers as key-value pairs\n\t\t\tCheck \"httpStatusCode\" to determine success.\n\n\t\t\tNote: full HTML responses can be very large and may\n\t\t\tfill the context window. To change the URL the user\n\t\t\tsees in their tab, use playground_navigate instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Request URL path, e.g.\n\t\t\t\t\t\"/wp-json/wp/v2/posts\" or\n\t\t\t\t\t\"/wp-admin/plugins.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'method',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `HTTP method (GET, POST, PUT,\n\t\t\t\t\tDELETE, etc.). Defaults to GET.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: 'GET',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'headers',\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: `Request headers as string key-value\n\t\t\t\t\tpairs, e.g. {\"Content-Type\":\n\t\t\t\t\t\"application/json\"}`,\n\t\t\t\trequired: false,\n\t\t\t\tadditionalProperties: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'body',\n\t\t\t\ttype: 'string_or_object',\n\t\t\t\tdescription: `Request body (for POST/PUT requests).\n\t\t\t\t\tAccepts a JSON string or an object.`,\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_navigate: {\n\t\ttitle: 'Navigate to URL',\n\t\terrorPrefix: 'Error navigating',\n\t\tdescription: `Navigate to a URL path in WordPress\n\t\t\tPlayground and return the final URL after any\n\t\t\tredirects. Examples: \"/wp-admin/\",\n\t\t\t\"/wp-login.php\", \"/\".\n\n\t\t\tOn 404 or error pages, navigation still succeeds\n\t\t\tfrom the tool's perspective — check the returned\n\t\t\tURL or use playground_request to verify the HTTP\n\t\t\tstatus code if needed.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `The URL path to navigate to,\n\t\t\t\t\te.g. \"/wp-admin/\" or\n\t\t\t\t\t\"/wp-login.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_get_current_url: {\n\t\ttitle: 'Get Current URL',\n\t\terrorPrefix: 'Error getting current URL',\n\t\tdescription: `Get the current URL path of the WordPress\n\t\t\tsite displayed in Playground. For additional\n\t\t\tmetadata (WordPress version, PHP version, document\n\t\t\troot), use playground_get_site_info instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_get_site_info: {\n\t\ttitle: 'Get Site Info',\n\t\terrorPrefix: 'Error getting site info',\n\t\tdescription: `Get metadata about the running WordPress\n\t\t\tinstance: current URL, document root, site URL,\n\t\t\tWordPress version, and PHP version. Use this when\n\t\t\tyou need version information or the document root\n\t\t\tpath. For just the current URL, prefer\n\t\t\tplayground_get_current_url.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_read_file: {\n\t\ttitle: 'Read File',\n\t\terrorPrefix: 'Error reading file',\n\t\tdescription: `Read a file from the WordPress virtual\n\t\t\tfilesystem. Returns the file contents as text.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to the file, e.g.\n\t\t\t\t\t\"/wordpress/wp-config.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_write_file: {\n\t\ttitle: 'Write File',\n\t\terrorPrefix: 'Error writing file',\n\t\tdescription: `Write content to a file in the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Overwrites the entire file — existing\n\t\t\tcontent is permanently lost. Read the file first\n\t\t\twith playground_read_file if you need to preserve\n\t\t\tany content.\n\n\t\t\tCreates the file if it does not exist. Parent\n\t\t\tdirectories are NOT created automatically — call\n\t\t\tplayground_mkdir first if needed, otherwise the\n\t\t\twrite will fail with a \"no such file or directory\"\n\t\t\terror.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to write to, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/test.txt\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'contents',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'File contents to write',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_list_files: {\n\t\ttitle: 'List Files',\n\t\terrorPrefix: 'Error listing files',\n\t\tdescription: `List files and directories at a given path\n\t\t\tin the WordPress virtual filesystem. Returns a\n\t\t\tflat, non-recursive listing of the immediate\n\t\t\tcontents. To explore subdirectories, call this tool\n\t\t\tagain with the subdirectory path.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to list, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/plugins\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_mkdir: {\n\t\ttitle: 'Create Directory',\n\t\terrorPrefix: 'Error creating directory',\n\t\tdescription: `Create a directory (and all required parent\n\t\t\tdirectories) in the WordPress virtual filesystem.\n\t\t\tCall this before playground_write_file when writing\n\t\t\tto a path whose parent directories do not yet\n\t\t\texist.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: true,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path of directory to\n\t\t\t\t\tcreate, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/my-plugin\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_file: {\n\t\ttitle: 'Delete File',\n\t\terrorPrefix: 'Error deleting file',\n\t\tdescription: `Delete a file from the WordPress virtual\n\t\t\tfilesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. Returns an error if the file does not\n\t\t\texist — use playground_file_exists first if\n\t\t\tdeletion is conditional.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of file to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_directory: {\n\t\ttitle: 'Delete Directory',\n\t\terrorPrefix: 'Error deleting directory',\n\t\tdescription: `Delete a directory from the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. By default (recursive=false), the directory\n\t\t\tmust be empty or the call will fail. Set\n\t\t\trecursive=true to delete a directory and all its\n\t\t\tcontents — use with care.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of directory to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'recursive',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: `If true, delete directory and\n\t\t\t\t\tall contents. If false (default), fails\n\t\t\t\t\ton non-empty directories.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_file_exists: {\n\t\ttitle: 'File Exists',\n\t\terrorPrefix: 'Error checking file existence',\n\t\tdescription: `Check whether a file or directory exists\n\t\t\tin the WordPress virtual filesystem.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path to check',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n};\n\n// -- Site management tool definitions --\n\nexport function getSiteToolDefinitions(): Record<string, ToolDefinition> {\n\treturn {\n\t\tplayground_list_sites: {\n\t\t\ttitle: 'List Available Sites',\n\t\t\terrorPrefix: 'Error listing sites',\n\t\t\tdescription: `List all WordPress Playground sites\n\t\t\tavailable. Call this before any other playground\n\t\t\ttool — it returns the siteId required by every\n\t\t\tother operation.\n\n\t\t\tIf this returns no sites, the user may need to\n\t\t\topen Playground in their browser. Use\n\t\t\tplayground_get_website_url to get the exact URL\n\t\t\tto send to the user.\n\n\t\t\tReturns site names and storage type. \"temporary\"\n\t\t\tsites are lost on page reload, \"opfs\" sites persist\n\t\t\tacross reloads. Call playground_save_in_browser to persist\n\t\t\ta temporary site.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_open_site_in_new_tab: {\n\t\t\ttitle: 'Open Site in New Tab',\n\t\t\terrorPrefix: 'Error opening site in new tab',\n\t\t\tdescription: `Open a WordPress Playground site in a new\n\t\t\tbrowser tab. The site must appear in\n\t\t\tplayground_list_sites.\n\n\t\t\tCheck playground_get_current_url first — if the\n\t\t\tsite is already open in a tab, calling this tool\n\t\t\twill open a second tab rather than switching to\n\t\t\tthe existing one.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_rename_site: {\n\t\t\ttitle: 'Rename Site',\n\t\t\terrorPrefix: 'Error renaming site',\n\t\t\tdescription: `Rename a WordPress Playground site. Updates\n\t\t\tthe display name shown in the browser UI.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: 'newName',\n\t\t\t\t\ttype: 'string',\n\t\t\t\t\tdescription: 'The new display name for the site',\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tplayground_save_in_browser: {\n\t\t\ttitle: 'Save in Browser',\n\t\t\terrorPrefix: 'Error saving site',\n\t\t\tdescription: `Save a temporary WordPress Playground site\n\t\t\tto browser storage so it survives page reloads.\n\t\t\tSafe to call even if the site is already saved\n\t\t\t(no-op).\n\n\t\t\tSites start as temporary by default and are lost\n\t\t\twhen the browser tab is closed or the page is\n\t\t\treloaded. Call this early in any multi-step\n\t\t\tworkflow where losing progress would be costly.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_get_website_url: {\n\t\t\ttitle: 'Get Playground Website URL',\n\t\t\terrorPrefix: 'Error getting website URL',\n\t\t\tdescription: `Return the URL the user must open in their\n\t\t\tbrowser to use WordPress Playground with this MCP\n\t\t\tserver. Use this tool whenever you need to send\n\t\t\tthe user to Playground or open it autonomously —\n\t\t\talways obtain the URL from this tool rather than\n\t\t\tconstructing it manually.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t\tidempotentHint: true,\n\t\t\t\topenWorldHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t};\n}\n\nexport function stringifyError(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\tif (typeof error === 'string') {\n\t\treturn error;\n\t}\n\ttry {\n\t\treturn JSON.stringify(error);\n\t} catch {\n\t\treturn String(error);\n\t}\n}\n\n/**\n * Translate internal Playground storage types to user-facing names.\n */\nexport function formatStorageLabel(raw: string): string {\n\treturn raw === 'none' ? 'temporary' : raw;\n}\n\n/**\n * Convert ToolParam[] to a plain JSON Schema object.\n * Used by WebMCP which expects raw JSON Schema (not Zod).\n */\nexport function paramsToJsonSchema(\n\tparams: ToolParam[]\n): Record<string, unknown> {\n\tconst properties: Record<string, Record<string, unknown>> = {};\n\tconst required: string[] = [];\n\n\tfor (const param of params) {\n\t\tconst prop: Record<string, unknown> = {};\n\t\tif (param.type === 'string_or_object') {\n\t\t\tprop['oneOf'] = [{ type: 'string' }, { type: 'object' }];\n\t\t} else {\n\t\t\tprop['type'] = param.type;\n\t\t}\n\t\tprop['description'] = param.description;\n\t\tif (param.additionalProperties !== undefined) {\n\t\t\tprop['additionalProperties'] = param.additionalProperties;\n\t\t}\n\t\tif (param.default !== undefined) {\n\t\t\tprop['default'] = param.default;\n\t\t}\n\t\tproperties[param.name] = prop;\n\t\tif (param.required) {\n\t\t\trequired.push(param.name);\n\t\t}\n\t}\n\n\tconst schema: Record<string, unknown> = {\n\t\ttype: 'object',\n\t\tproperties,\n\t};\n\tif (required.length > 0) {\n\t\tschema['required'] = required;\n\t}\n\treturn schema;\n}\n","/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst url = input['url'] as string;\n\t\tconst method = (input['method'] as string) ?? 'GET';\n\t\tconst headers = {\n\t\t\t...((input['headers'] as Record<string, string>) ?? {}),\n\t\t};\n\t\tconst rawBody = input['body'];\n\t\tconst body =\n\t\t\ttypeof rawBody === 'object' && rawBody !== null\n\t\t\t\t? JSON.stringify(rawBody)\n\t\t\t\t: (rawBody as string | undefined);\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url, 'http://localhost');\n\t\t\tconst isRestApi =\n\t\t\t\turl.includes('/wp-json/') ||\n\t\t\t\tparsedUrl.searchParams.has('rest_route');\n\n\t\t\t// Auto-set Content-Type for REST API JSON bodies.\n\t\t\tif (\n\t\t\t\tisRestApi &&\n\t\t\t\tbody &&\n\t\t\t\t!Object.keys(headers).some(\n\t\t\t\t\t(k) => k.toLowerCase() === 'content-type'\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\t}\n\n\t\t\tconst hasNonce = Object.keys(headers).some(\n\t\t\t\t(k) => k.toLowerCase() === 'x-wp-nonce'\n\t\t\t);\n\n\t\t\tif (isRestApi && !hasNonce) {\n\t\t\t\t// Generate the nonce via a temporary PHP file requested\n\t\t\t\t// through request() — not run() — so that the cookie\n\t\t\t\t// store is included and WordPress ties the nonce to\n\t\t\t\t// the logged-in user.\n\t\t\t\tconst nonceId = Math.random().toString(36).slice(2, 10);\n\t\t\t\tconst noncePath = `/wordpress/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceUrl = `/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceCode =\n\t\t\t\t\t\"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');\";\n\t\t\t\tawait client.writeFile(noncePath, nonceCode);\n\t\t\t\tlet nonce = '';\n\t\t\t\ttry {\n\t\t\t\t\tconst nonceResp = await client.request({\n\t\t\t\t\t\turl: nonceUrl,\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t});\n\t\t\t\t\tnonce = nonceResp.text.trim();\n\t\t\t\t} finally {\n\t\t\t\t\tawait client.unlink(noncePath);\n\t\t\t\t}\n\t\t\t\tif (nonce && nonce !== '0') {\n\t\t\t\t\theaders['X-WP-Nonce'] = nonce;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Nonce generation failed — proceed without it.\n\t\t}\n\n\t\treturn await client.request({ url, method, headers, body });\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["PLAYGROUND_BASE_URL","playgroundUrl","port","baseUrl","url","toolDefinitions","getSiteToolDefinitions","stringifyError","error","formatStorageLabel","raw","paramsToJsonSchema","params","properties","required","param","prop","schema","executeSiteInfo","client","infoText","resp","info","toolExecutors","input","method","headers","rawBody","body","parsedUrl","isRestApi","k","hasNonce","nonceId","noncePath","nonceUrl","nonce","createToolClient","decoder","overrides","options","target","override","val"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- const h = "https://playground.wordpress.net/";
2
- function m(e, t) {
1
+ const y = "https://playground.wordpress.net/";
2
+ function w(e, t) {
3
3
  if (!t)
4
- return `${h}?mcp-port=${e}`;
4
+ return `${y}?mcp-port=${e}`;
5
5
  const n = new URL(t);
6
6
  return n.searchParams.set("mcp-port", String(e)), n.toString();
7
7
  }
8
- const w = {
8
+ const _ = {
9
9
  playground_execute_php: {
10
10
  title: "Execute PHP Code",
11
11
  errorPrefix: "Error executing PHP",
@@ -148,8 +148,9 @@ const w = {
148
148
  },
149
149
  {
150
150
  name: "body",
151
- type: "string",
152
- description: "Request body (for POST/PUT requests)",
151
+ type: "string_or_object",
152
+ description: `Request body (for POST/PUT requests).
153
+ Accepts a JSON string or an object.`,
153
154
  required: !1
154
155
  }
155
156
  ]
@@ -399,7 +400,7 @@ const w = {
399
400
  ]
400
401
  }
401
402
  };
402
- function _() {
403
+ function b() {
403
404
  return {
404
405
  playground_list_sites: {
405
406
  title: "List Available Sites",
@@ -507,17 +508,14 @@ function P(e) {
507
508
  return String(e);
508
509
  }
509
510
  }
510
- function b(e) {
511
+ function v(e) {
511
512
  return e === "none" ? "temporary" : e;
512
513
  }
513
- function v(e) {
514
+ function H(e) {
514
515
  const t = {}, n = [];
515
516
  for (const r of e) {
516
- const s = {
517
- type: r.type,
518
- description: r.description
519
- };
520
- r.additionalProperties !== void 0 && (s.additionalProperties = r.additionalProperties), r.default !== void 0 && (s.default = r.default), t[r.name] = s, r.required && n.push(r.name);
517
+ const s = {};
518
+ r.type === "string_or_object" ? s.oneOf = [{ type: "string" }, { type: "object" }] : s.type = r.type, s.description = r.description, r.additionalProperties !== void 0 && (s.additionalProperties = r.additionalProperties), r.default !== void 0 && (s.default = r.default), t[r.name] = s, r.required && n.push(r.name);
521
519
  }
522
520
  const i = {
523
521
  type: "object",
@@ -553,27 +551,27 @@ async function f(e) {
553
551
  phpVersion: i.phpVersion ?? "unknown"
554
552
  };
555
553
  }
556
- const H = {
554
+ const x = {
557
555
  playground_execute_php: (e, t) => e.run({ code: t.code }),
558
556
  playground_request: async (e, t) => {
559
557
  const n = t.url, i = t.method ?? "GET", r = {
560
558
  ...t.headers ?? {}
561
- }, s = t.body;
559
+ }, s = t.body, o = typeof s == "object" && s !== null ? JSON.stringify(s) : s;
562
560
  try {
563
- const a = new URL(n, "http://localhost"), d = n.includes("/wp-json/") || a.searchParams.has("rest_route");
564
- d && s && !Object.keys(r).some(
565
- (o) => o.toLowerCase() === "content-type"
561
+ const p = new URL(n, "http://localhost"), d = n.includes("/wp-json/") || p.searchParams.has("rest_route");
562
+ d && o && !Object.keys(r).some(
563
+ (a) => a.toLowerCase() === "content-type"
566
564
  ) && (r["Content-Type"] = "application/json");
567
- const p = Object.keys(r).some(
568
- (o) => o.toLowerCase() === "x-wp-nonce"
565
+ const c = Object.keys(r).some(
566
+ (a) => a.toLowerCase() === "x-wp-nonce"
569
567
  );
570
- if (d && !p) {
571
- const o = Math.random().toString(36).slice(2, 10), u = `/wordpress/wp-content/mcp-nonce-${o}.php`, c = `/wp-content/mcp-nonce-${o}.php`;
568
+ if (d && !c) {
569
+ const a = Math.random().toString(36).slice(2, 10), u = `/wordpress/wp-content/mcp-nonce-${a}.php`, h = `/wp-content/mcp-nonce-${a}.php`;
572
570
  await e.writeFile(u, "<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');");
573
571
  let l = "";
574
572
  try {
575
573
  l = (await e.request({
576
- url: c,
574
+ url: h,
577
575
  method: "GET"
578
576
  })).text.trim();
579
577
  } finally {
@@ -583,7 +581,7 @@ const H = {
583
581
  }
584
582
  } catch {
585
583
  }
586
- return await e.request({ url: n, method: i, headers: r, body: s });
584
+ return await e.request({ url: n, method: i, headers: r, body: o });
587
585
  },
588
586
  playground_navigate: async (e, t) => (await e.goTo(t.path), { url: await e.getCurrentURL() }),
589
587
  playground_get_current_url: async (e) => ({
@@ -609,7 +607,7 @@ const H = {
609
607
  exists: await e.fileExists(t.path)
610
608
  })
611
609
  };
612
- function x(e) {
610
+ function T(e) {
613
611
  const t = new TextDecoder(), n = {
614
612
  async run(i) {
615
613
  const r = await e.run(i);
@@ -638,19 +636,19 @@ function x(e) {
638
636
  const s = n[r];
639
637
  if (s !== void 0)
640
638
  return s;
641
- const a = i[r];
642
- return typeof a == "function" ? a.bind(i) : a;
639
+ const o = i[r];
640
+ return typeof o == "function" ? o.bind(i) : o;
643
641
  }
644
642
  });
645
643
  }
646
644
  export {
647
- H as a,
648
- v as b,
649
- x as c,
650
- b as f,
651
- _ as g,
652
- m as p,
645
+ x as a,
646
+ H as b,
647
+ T as c,
648
+ v as f,
649
+ b as g,
650
+ w as p,
653
651
  P as s,
654
- w as t
652
+ _ as t
655
653
  };
656
- //# sourceMappingURL=tool-executors-CphtBrKd.js.map
654
+ //# sourceMappingURL=tool-executors-DeiRNg9-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-executors-DeiRNg9-.js","sources":["../../../../packages/playground/mcp/src/tools/tool-definitions.ts","../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Tool metadata and schema helpers for WordPress Playground.\n *\n * Pure data — no execution logic. Both the MCP server and\n * WebMCP import these for consistent descriptions, annotations,\n * and schema conversion.\n */\n\nexport interface ToolAnnotations {\n\treadOnlyHint?: boolean;\n\tdestructiveHint?: boolean;\n\tidempotentHint?: boolean;\n\topenWorldHint?: boolean;\n}\n\nexport type ToolParamType =\n\t| 'string'\n\t| 'boolean'\n\t| 'object'\n\t| 'string_or_object';\n\nexport interface ToolParam {\n\tname: string;\n\ttype: ToolParamType;\n\tdescription: string;\n\trequired: boolean;\n\tadditionalProperties?: boolean;\n\tdefault?: unknown;\n}\n\nexport interface ToolDefinition {\n\ttitle: string;\n\tdescription: string;\n\terrorPrefix: string;\n\tannotations: ToolAnnotations;\n\tparams: ToolParam[];\n}\n\nconst PLAYGROUND_BASE_URL = 'https://playground.wordpress.net/';\n\nexport function playgroundUrl(port: number, baseUrl?: string): string {\n\tif (!baseUrl) {\n\t\treturn `${PLAYGROUND_BASE_URL}?mcp-port=${port}`;\n\t}\n\tconst url = new URL(baseUrl);\n\turl.searchParams.set('mcp-port', String(port));\n\treturn url.toString();\n}\n\n// -- Per-site tool definitions --\n\nexport const toolDefinitions: Record<string, ToolDefinition> = {\n\tplayground_execute_php: {\n\t\ttitle: 'Execute PHP Code',\n\t\terrorPrefix: 'Error executing PHP',\n\t\tdescription: `Run arbitrary PHP code in WordPress Playground\n\t\t\tand return the output.\n\n\t\t\tWordPress is NOT bootstrapped automatically. To use\n\t\t\tWordPress functions, start your code with:\n\t\t\trequire(\"/wordpress/wp-load.php\");\n\t\t\tAlways include the opening <?php tag.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": stdout output\n\t\t\t- \"errors\": PHP warnings, notices, and fatal error\n\t\t\t messages from stderr\n\t\t\t- \"exitCode\": 0 on success, non-zero on fatal error\n\t\t\tCheck both \"errors\" and \"exitCode\" to determine\n\t\t\twhether the call succeeded.\n\n\t\t\tWARNING: output is returned in full with no\n\t\t\ttruncation — avoid queries that produce unbounded\n\t\t\toutput (e.g. SELECT * without LIMIT). Keep output\n\t\t\tunder 50 KB to avoid filling the context window.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'code',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `PHP code to execute. Example:\n\t\t\t\t\t\"<?php echo get_bloginfo('name');\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_request: {\n\t\ttitle: 'HTTP Request',\n\t\terrorPrefix: 'Error making request',\n\t\tdescription: `Make an HTTP request to the WordPress site\n\t\t\trunning in Playground. REST API requests\n\t\t\t(/wp-json/* or ?rest_route=) are automatically\n\t\t\tauthenticated with a valid nonce — no manual\n\t\t\tauth needed.\n\n\t\t\tTool selection guide:\n\t\t\t1. Use this tool (playground_request) with the\n\t\t\t REST API for standard content CRUD — posts,\n\t\t\t pages, users, terms, comments, settings, etc.\n\t\t\t The REST API handles serialization, pagination,\n\t\t\t and field filtering for you. Prefer compact\n\t\t\t REST responses: use _fields to request only the\n\t\t\t fields needed and per_page/page for large\n\t\t\t collections. Before using endpoint-specific\n\t\t\t filters, inspect the route metadata from\n\t\t\t /wp-json/ and use only args documented for that\n\t\t\t route.\n\t\t\t2. Use this tool with the WordPress REST API to\n\t\t\t discover and call registered abilities when the\n\t\t\t site exposes the Abilities API:\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities\n\t\t\t lists registered abilities. Always assume this\n\t\t\t response may be large and start with _fields,\n\t\t\t e.g.\n\t\t\t /wp-json/wp-abilities/v1/abilities?_fields=name,category,label,description\n\t\t\t Do not assume filters like search or category\n\t\t\t exist unless /wp-json/ route metadata documents\n\t\t\t them.\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities/{name}\n\t\t\t gets one ability, including schemas and\n\t\t\t metadata. Ability names include \"/\" in the\n\t\t\t path; do not encode it. For example, use\n\t\t\t /wp-json/wp-abilities/v1/abilities/memex/save-note\n\t\t\t for \"memex/save-note\".\n\t\t\t - GET, POST, or DELETE\n\t\t\t /wp-json/wp-abilities/v1/abilities/{name}/run\n\t\t\t executes the ability. Inspect the ability\n\t\t\t metadata first, then call /run with the\n\t\t\t documented method. POST bodies usually wrap\n\t\t\t arguments in an \"input\" object, e.g.\n\t\t\t {\"input\":{\"title\":\"Hello\"}}.\n\t\t\t - GET /wp-json/wp-abilities/v1/categories\n\t\t\t lists ability categories. Each category includes\n\t\t\t _links.abilities; follow that URL to list\n\t\t\t abilities in that category. The abilities\n\t\t\t collection can also be filtered directly with\n\t\t\t /wp-json/wp-abilities/v1/abilities?category={slug}.\n\t\t\t3. Use playground_execute_php when the data you\n\t\t\t need is not exposed by the REST API (e.g.\n\t\t\t raw options, direct database queries, or\n\t\t\t custom table access).\n\t\t\t4. Use this tool as a plain HTTP request (non-REST)\n\t\t\t when the HTTP layer itself matters: verifying\n\t\t\t redirects, status codes, cookies, or response\n\t\t\t headers.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": the response body as a string\n\t\t\t- \"httpStatusCode\": HTTP status code (200, 404…)\n\t\t\t- \"headers\": response headers as key-value pairs\n\t\t\tCheck \"httpStatusCode\" to determine success.\n\n\t\t\tNote: full HTML responses can be very large and may\n\t\t\tfill the context window. To change the URL the user\n\t\t\tsees in their tab, use playground_navigate instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Request URL path, e.g.\n\t\t\t\t\t\"/wp-json/wp/v2/posts\" or\n\t\t\t\t\t\"/wp-admin/plugins.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'method',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `HTTP method (GET, POST, PUT,\n\t\t\t\t\tDELETE, etc.). Defaults to GET.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: 'GET',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'headers',\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: `Request headers as string key-value\n\t\t\t\t\tpairs, e.g. {\"Content-Type\":\n\t\t\t\t\t\"application/json\"}`,\n\t\t\t\trequired: false,\n\t\t\t\tadditionalProperties: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'body',\n\t\t\t\ttype: 'string_or_object',\n\t\t\t\tdescription: `Request body (for POST/PUT requests).\n\t\t\t\t\tAccepts a JSON string or an object.`,\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_navigate: {\n\t\ttitle: 'Navigate to URL',\n\t\terrorPrefix: 'Error navigating',\n\t\tdescription: `Navigate to a URL path in WordPress\n\t\t\tPlayground and return the final URL after any\n\t\t\tredirects. Examples: \"/wp-admin/\",\n\t\t\t\"/wp-login.php\", \"/\".\n\n\t\t\tOn 404 or error pages, navigation still succeeds\n\t\t\tfrom the tool's perspective — check the returned\n\t\t\tURL or use playground_request to verify the HTTP\n\t\t\tstatus code if needed.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `The URL path to navigate to,\n\t\t\t\t\te.g. \"/wp-admin/\" or\n\t\t\t\t\t\"/wp-login.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_get_current_url: {\n\t\ttitle: 'Get Current URL',\n\t\terrorPrefix: 'Error getting current URL',\n\t\tdescription: `Get the current URL path of the WordPress\n\t\t\tsite displayed in Playground. For additional\n\t\t\tmetadata (WordPress version, PHP version, document\n\t\t\troot), use playground_get_site_info instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_get_site_info: {\n\t\ttitle: 'Get Site Info',\n\t\terrorPrefix: 'Error getting site info',\n\t\tdescription: `Get metadata about the running WordPress\n\t\t\tinstance: current URL, document root, site URL,\n\t\t\tWordPress version, and PHP version. Use this when\n\t\t\tyou need version information or the document root\n\t\t\tpath. For just the current URL, prefer\n\t\t\tplayground_get_current_url.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_read_file: {\n\t\ttitle: 'Read File',\n\t\terrorPrefix: 'Error reading file',\n\t\tdescription: `Read a file from the WordPress virtual\n\t\t\tfilesystem. Returns the file contents as text.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to the file, e.g.\n\t\t\t\t\t\"/wordpress/wp-config.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_write_file: {\n\t\ttitle: 'Write File',\n\t\terrorPrefix: 'Error writing file',\n\t\tdescription: `Write content to a file in the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Overwrites the entire file — existing\n\t\t\tcontent is permanently lost. Read the file first\n\t\t\twith playground_read_file if you need to preserve\n\t\t\tany content.\n\n\t\t\tCreates the file if it does not exist. Parent\n\t\t\tdirectories are NOT created automatically — call\n\t\t\tplayground_mkdir first if needed, otherwise the\n\t\t\twrite will fail with a \"no such file or directory\"\n\t\t\terror.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to write to, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/test.txt\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'contents',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'File contents to write',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_list_files: {\n\t\ttitle: 'List Files',\n\t\terrorPrefix: 'Error listing files',\n\t\tdescription: `List files and directories at a given path\n\t\t\tin the WordPress virtual filesystem. Returns a\n\t\t\tflat, non-recursive listing of the immediate\n\t\t\tcontents. To explore subdirectories, call this tool\n\t\t\tagain with the subdirectory path.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to list, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/plugins\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_mkdir: {\n\t\ttitle: 'Create Directory',\n\t\terrorPrefix: 'Error creating directory',\n\t\tdescription: `Create a directory (and all required parent\n\t\t\tdirectories) in the WordPress virtual filesystem.\n\t\t\tCall this before playground_write_file when writing\n\t\t\tto a path whose parent directories do not yet\n\t\t\texist.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: true,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path of directory to\n\t\t\t\t\tcreate, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/my-plugin\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_file: {\n\t\ttitle: 'Delete File',\n\t\terrorPrefix: 'Error deleting file',\n\t\tdescription: `Delete a file from the WordPress virtual\n\t\t\tfilesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. Returns an error if the file does not\n\t\t\texist — use playground_file_exists first if\n\t\t\tdeletion is conditional.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of file to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_directory: {\n\t\ttitle: 'Delete Directory',\n\t\terrorPrefix: 'Error deleting directory',\n\t\tdescription: `Delete a directory from the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. By default (recursive=false), the directory\n\t\t\tmust be empty or the call will fail. Set\n\t\t\trecursive=true to delete a directory and all its\n\t\t\tcontents — use with care.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of directory to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'recursive',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: `If true, delete directory and\n\t\t\t\t\tall contents. If false (default), fails\n\t\t\t\t\ton non-empty directories.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_file_exists: {\n\t\ttitle: 'File Exists',\n\t\terrorPrefix: 'Error checking file existence',\n\t\tdescription: `Check whether a file or directory exists\n\t\t\tin the WordPress virtual filesystem.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path to check',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n};\n\n// -- Site management tool definitions --\n\nexport function getSiteToolDefinitions(): Record<string, ToolDefinition> {\n\treturn {\n\t\tplayground_list_sites: {\n\t\t\ttitle: 'List Available Sites',\n\t\t\terrorPrefix: 'Error listing sites',\n\t\t\tdescription: `List all WordPress Playground sites\n\t\t\tavailable. Call this before any other playground\n\t\t\ttool — it returns the siteId required by every\n\t\t\tother operation.\n\n\t\t\tIf this returns no sites, the user may need to\n\t\t\topen Playground in their browser. Use\n\t\t\tplayground_get_website_url to get the exact URL\n\t\t\tto send to the user.\n\n\t\t\tReturns site names and storage type. \"temporary\"\n\t\t\tsites are lost on page reload, \"opfs\" sites persist\n\t\t\tacross reloads. Call playground_save_in_browser to persist\n\t\t\ta temporary site.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_open_site_in_new_tab: {\n\t\t\ttitle: 'Open Site in New Tab',\n\t\t\terrorPrefix: 'Error opening site in new tab',\n\t\t\tdescription: `Open a WordPress Playground site in a new\n\t\t\tbrowser tab. The site must appear in\n\t\t\tplayground_list_sites.\n\n\t\t\tCheck playground_get_current_url first — if the\n\t\t\tsite is already open in a tab, calling this tool\n\t\t\twill open a second tab rather than switching to\n\t\t\tthe existing one.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_rename_site: {\n\t\t\ttitle: 'Rename Site',\n\t\t\terrorPrefix: 'Error renaming site',\n\t\t\tdescription: `Rename a WordPress Playground site. Updates\n\t\t\tthe display name shown in the browser UI.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: 'newName',\n\t\t\t\t\ttype: 'string',\n\t\t\t\t\tdescription: 'The new display name for the site',\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tplayground_save_in_browser: {\n\t\t\ttitle: 'Save in Browser',\n\t\t\terrorPrefix: 'Error saving site',\n\t\t\tdescription: `Save a temporary WordPress Playground site\n\t\t\tto browser storage so it survives page reloads.\n\t\t\tSafe to call even if the site is already saved\n\t\t\t(no-op).\n\n\t\t\tSites start as temporary by default and are lost\n\t\t\twhen the browser tab is closed or the page is\n\t\t\treloaded. Call this early in any multi-step\n\t\t\tworkflow where losing progress would be costly.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_get_website_url: {\n\t\t\ttitle: 'Get Playground Website URL',\n\t\t\terrorPrefix: 'Error getting website URL',\n\t\t\tdescription: `Return the URL the user must open in their\n\t\t\tbrowser to use WordPress Playground with this MCP\n\t\t\tserver. Use this tool whenever you need to send\n\t\t\tthe user to Playground or open it autonomously —\n\t\t\talways obtain the URL from this tool rather than\n\t\t\tconstructing it manually.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t\tidempotentHint: true,\n\t\t\t\topenWorldHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t};\n}\n\nexport function stringifyError(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\tif (typeof error === 'string') {\n\t\treturn error;\n\t}\n\ttry {\n\t\treturn JSON.stringify(error);\n\t} catch {\n\t\treturn String(error);\n\t}\n}\n\n/**\n * Translate internal Playground storage types to user-facing names.\n */\nexport function formatStorageLabel(raw: string): string {\n\treturn raw === 'none' ? 'temporary' : raw;\n}\n\n/**\n * Convert ToolParam[] to a plain JSON Schema object.\n * Used by WebMCP which expects raw JSON Schema (not Zod).\n */\nexport function paramsToJsonSchema(\n\tparams: ToolParam[]\n): Record<string, unknown> {\n\tconst properties: Record<string, Record<string, unknown>> = {};\n\tconst required: string[] = [];\n\n\tfor (const param of params) {\n\t\tconst prop: Record<string, unknown> = {};\n\t\tif (param.type === 'string_or_object') {\n\t\t\tprop['oneOf'] = [{ type: 'string' }, { type: 'object' }];\n\t\t} else {\n\t\t\tprop['type'] = param.type;\n\t\t}\n\t\tprop['description'] = param.description;\n\t\tif (param.additionalProperties !== undefined) {\n\t\t\tprop['additionalProperties'] = param.additionalProperties;\n\t\t}\n\t\tif (param.default !== undefined) {\n\t\t\tprop['default'] = param.default;\n\t\t}\n\t\tproperties[param.name] = prop;\n\t\tif (param.required) {\n\t\t\trequired.push(param.name);\n\t\t}\n\t}\n\n\tconst schema: Record<string, unknown> = {\n\t\ttype: 'object',\n\t\tproperties,\n\t};\n\tif (required.length > 0) {\n\t\tschema['required'] = required;\n\t}\n\treturn schema;\n}\n","/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst url = input['url'] as string;\n\t\tconst method = (input['method'] as string) ?? 'GET';\n\t\tconst headers = {\n\t\t\t...((input['headers'] as Record<string, string>) ?? {}),\n\t\t};\n\t\tconst rawBody = input['body'];\n\t\tconst body =\n\t\t\ttypeof rawBody === 'object' && rawBody !== null\n\t\t\t\t? JSON.stringify(rawBody)\n\t\t\t\t: (rawBody as string | undefined);\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url, 'http://localhost');\n\t\t\tconst isRestApi =\n\t\t\t\turl.includes('/wp-json/') ||\n\t\t\t\tparsedUrl.searchParams.has('rest_route');\n\n\t\t\t// Auto-set Content-Type for REST API JSON bodies.\n\t\t\tif (\n\t\t\t\tisRestApi &&\n\t\t\t\tbody &&\n\t\t\t\t!Object.keys(headers).some(\n\t\t\t\t\t(k) => k.toLowerCase() === 'content-type'\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\t}\n\n\t\t\tconst hasNonce = Object.keys(headers).some(\n\t\t\t\t(k) => k.toLowerCase() === 'x-wp-nonce'\n\t\t\t);\n\n\t\t\tif (isRestApi && !hasNonce) {\n\t\t\t\t// Generate the nonce via a temporary PHP file requested\n\t\t\t\t// through request() — not run() — so that the cookie\n\t\t\t\t// store is included and WordPress ties the nonce to\n\t\t\t\t// the logged-in user.\n\t\t\t\tconst nonceId = Math.random().toString(36).slice(2, 10);\n\t\t\t\tconst noncePath = `/wordpress/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceUrl = `/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceCode =\n\t\t\t\t\t\"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');\";\n\t\t\t\tawait client.writeFile(noncePath, nonceCode);\n\t\t\t\tlet nonce = '';\n\t\t\t\ttry {\n\t\t\t\t\tconst nonceResp = await client.request({\n\t\t\t\t\t\turl: nonceUrl,\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t});\n\t\t\t\t\tnonce = nonceResp.text.trim();\n\t\t\t\t} finally {\n\t\t\t\t\tawait client.unlink(noncePath);\n\t\t\t\t}\n\t\t\t\tif (nonce && nonce !== '0') {\n\t\t\t\t\theaders['X-WP-Nonce'] = nonce;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Nonce generation failed — proceed without it.\n\t\t}\n\n\t\treturn await client.request({ url, method, headers, body });\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["PLAYGROUND_BASE_URL","playgroundUrl","port","baseUrl","url","toolDefinitions","getSiteToolDefinitions","stringifyError","error","formatStorageLabel","raw","paramsToJsonSchema","params","properties","required","param","prop","schema","executeSiteInfo","client","infoText","resp","info","toolExecutors","input","method","headers","rawBody","body","parsedUrl","isRestApi","k","hasNonce","nonceId","noncePath","nonceUrl","nonce","createToolClient","decoder","overrides","options","target","override","val"],"mappings":"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"}
@@ -11,7 +11,7 @@ export interface ToolAnnotations {
11
11
  idempotentHint?: boolean;
12
12
  openWorldHint?: boolean;
13
13
  }
14
- export type ToolParamType = 'string' | 'boolean' | 'object';
14
+ export type ToolParamType = 'string' | 'boolean' | 'object' | 'string_or_object';
15
15
  export interface ToolParam {
16
16
  name: string;
17
17
  type: ToolParamType;
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-executors-CphtBrKd.js","sources":["../../../../packages/playground/mcp/src/tools/tool-definitions.ts","../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Tool metadata and schema helpers for WordPress Playground.\n *\n * Pure data — no execution logic. Both the MCP server and\n * WebMCP import these for consistent descriptions, annotations,\n * and schema conversion.\n */\n\nexport interface ToolAnnotations {\n\treadOnlyHint?: boolean;\n\tdestructiveHint?: boolean;\n\tidempotentHint?: boolean;\n\topenWorldHint?: boolean;\n}\n\nexport type ToolParamType = 'string' | 'boolean' | 'object';\n\nexport interface ToolParam {\n\tname: string;\n\ttype: ToolParamType;\n\tdescription: string;\n\trequired: boolean;\n\tadditionalProperties?: boolean;\n\tdefault?: unknown;\n}\n\nexport interface ToolDefinition {\n\ttitle: string;\n\tdescription: string;\n\terrorPrefix: string;\n\tannotations: ToolAnnotations;\n\tparams: ToolParam[];\n}\n\nconst PLAYGROUND_BASE_URL = 'https://playground.wordpress.net/';\n\nexport function playgroundUrl(port: number, baseUrl?: string): string {\n\tif (!baseUrl) {\n\t\treturn `${PLAYGROUND_BASE_URL}?mcp-port=${port}`;\n\t}\n\tconst url = new URL(baseUrl);\n\turl.searchParams.set('mcp-port', String(port));\n\treturn url.toString();\n}\n\n// -- Per-site tool definitions --\n\nexport const toolDefinitions: Record<string, ToolDefinition> = {\n\tplayground_execute_php: {\n\t\ttitle: 'Execute PHP Code',\n\t\terrorPrefix: 'Error executing PHP',\n\t\tdescription: `Run arbitrary PHP code in WordPress Playground\n\t\t\tand return the output.\n\n\t\t\tWordPress is NOT bootstrapped automatically. To use\n\t\t\tWordPress functions, start your code with:\n\t\t\trequire(\"/wordpress/wp-load.php\");\n\t\t\tAlways include the opening <?php tag.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": stdout output\n\t\t\t- \"errors\": PHP warnings, notices, and fatal error\n\t\t\t messages from stderr\n\t\t\t- \"exitCode\": 0 on success, non-zero on fatal error\n\t\t\tCheck both \"errors\" and \"exitCode\" to determine\n\t\t\twhether the call succeeded.\n\n\t\t\tWARNING: output is returned in full with no\n\t\t\ttruncation — avoid queries that produce unbounded\n\t\t\toutput (e.g. SELECT * without LIMIT). Keep output\n\t\t\tunder 50 KB to avoid filling the context window.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'code',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `PHP code to execute. Example:\n\t\t\t\t\t\"<?php echo get_bloginfo('name');\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_request: {\n\t\ttitle: 'HTTP Request',\n\t\terrorPrefix: 'Error making request',\n\t\tdescription: `Make an HTTP request to the WordPress site\n\t\t\trunning in Playground. REST API requests\n\t\t\t(/wp-json/* or ?rest_route=) are automatically\n\t\t\tauthenticated with a valid nonce — no manual\n\t\t\tauth needed.\n\n\t\t\tTool selection guide:\n\t\t\t1. Use this tool (playground_request) with the\n\t\t\t REST API for standard content CRUD — posts,\n\t\t\t pages, users, terms, comments, settings, etc.\n\t\t\t The REST API handles serialization, pagination,\n\t\t\t and field filtering for you. Prefer compact\n\t\t\t REST responses: use _fields to request only the\n\t\t\t fields needed and per_page/page for large\n\t\t\t collections. Before using endpoint-specific\n\t\t\t filters, inspect the route metadata from\n\t\t\t /wp-json/ and use only args documented for that\n\t\t\t route.\n\t\t\t2. Use this tool with the WordPress REST API to\n\t\t\t discover and call registered abilities when the\n\t\t\t site exposes the Abilities API:\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities\n\t\t\t lists registered abilities. Always assume this\n\t\t\t response may be large and start with _fields,\n\t\t\t e.g.\n\t\t\t /wp-json/wp-abilities/v1/abilities?_fields=name,category,label,description\n\t\t\t Do not assume filters like search or category\n\t\t\t exist unless /wp-json/ route metadata documents\n\t\t\t them.\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities/{name}\n\t\t\t gets one ability, including schemas and\n\t\t\t metadata. Ability names include \"/\" in the\n\t\t\t path; do not encode it. For example, use\n\t\t\t /wp-json/wp-abilities/v1/abilities/memex/save-note\n\t\t\t for \"memex/save-note\".\n\t\t\t - GET, POST, or DELETE\n\t\t\t /wp-json/wp-abilities/v1/abilities/{name}/run\n\t\t\t executes the ability. Inspect the ability\n\t\t\t metadata first, then call /run with the\n\t\t\t documented method. POST bodies usually wrap\n\t\t\t arguments in an \"input\" object, e.g.\n\t\t\t {\"input\":{\"title\":\"Hello\"}}.\n\t\t\t - GET /wp-json/wp-abilities/v1/categories\n\t\t\t lists ability categories. Each category includes\n\t\t\t _links.abilities; follow that URL to list\n\t\t\t abilities in that category. The abilities\n\t\t\t collection can also be filtered directly with\n\t\t\t /wp-json/wp-abilities/v1/abilities?category={slug}.\n\t\t\t3. Use playground_execute_php when the data you\n\t\t\t need is not exposed by the REST API (e.g.\n\t\t\t raw options, direct database queries, or\n\t\t\t custom table access).\n\t\t\t4. Use this tool as a plain HTTP request (non-REST)\n\t\t\t when the HTTP layer itself matters: verifying\n\t\t\t redirects, status codes, cookies, or response\n\t\t\t headers.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": the response body as a string\n\t\t\t- \"httpStatusCode\": HTTP status code (200, 404…)\n\t\t\t- \"headers\": response headers as key-value pairs\n\t\t\tCheck \"httpStatusCode\" to determine success.\n\n\t\t\tNote: full HTML responses can be very large and may\n\t\t\tfill the context window. To change the URL the user\n\t\t\tsees in their tab, use playground_navigate instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Request URL path, e.g.\n\t\t\t\t\t\"/wp-json/wp/v2/posts\" or\n\t\t\t\t\t\"/wp-admin/plugins.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'method',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `HTTP method (GET, POST, PUT,\n\t\t\t\t\tDELETE, etc.). Defaults to GET.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: 'GET',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'headers',\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: `Request headers as string key-value\n\t\t\t\t\tpairs, e.g. {\"Content-Type\":\n\t\t\t\t\t\"application/json\"}`,\n\t\t\t\trequired: false,\n\t\t\t\tadditionalProperties: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'body',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Request body (for POST/PUT requests)',\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_navigate: {\n\t\ttitle: 'Navigate to URL',\n\t\terrorPrefix: 'Error navigating',\n\t\tdescription: `Navigate to a URL path in WordPress\n\t\t\tPlayground and return the final URL after any\n\t\t\tredirects. Examples: \"/wp-admin/\",\n\t\t\t\"/wp-login.php\", \"/\".\n\n\t\t\tOn 404 or error pages, navigation still succeeds\n\t\t\tfrom the tool's perspective — check the returned\n\t\t\tURL or use playground_request to verify the HTTP\n\t\t\tstatus code if needed.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `The URL path to navigate to,\n\t\t\t\t\te.g. \"/wp-admin/\" or\n\t\t\t\t\t\"/wp-login.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_get_current_url: {\n\t\ttitle: 'Get Current URL',\n\t\terrorPrefix: 'Error getting current URL',\n\t\tdescription: `Get the current URL path of the WordPress\n\t\t\tsite displayed in Playground. For additional\n\t\t\tmetadata (WordPress version, PHP version, document\n\t\t\troot), use playground_get_site_info instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_get_site_info: {\n\t\ttitle: 'Get Site Info',\n\t\terrorPrefix: 'Error getting site info',\n\t\tdescription: `Get metadata about the running WordPress\n\t\t\tinstance: current URL, document root, site URL,\n\t\t\tWordPress version, and PHP version. Use this when\n\t\t\tyou need version information or the document root\n\t\t\tpath. For just the current URL, prefer\n\t\t\tplayground_get_current_url.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_read_file: {\n\t\ttitle: 'Read File',\n\t\terrorPrefix: 'Error reading file',\n\t\tdescription: `Read a file from the WordPress virtual\n\t\t\tfilesystem. Returns the file contents as text.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to the file, e.g.\n\t\t\t\t\t\"/wordpress/wp-config.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_write_file: {\n\t\ttitle: 'Write File',\n\t\terrorPrefix: 'Error writing file',\n\t\tdescription: `Write content to a file in the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Overwrites the entire file — existing\n\t\t\tcontent is permanently lost. Read the file first\n\t\t\twith playground_read_file if you need to preserve\n\t\t\tany content.\n\n\t\t\tCreates the file if it does not exist. Parent\n\t\t\tdirectories are NOT created automatically — call\n\t\t\tplayground_mkdir first if needed, otherwise the\n\t\t\twrite will fail with a \"no such file or directory\"\n\t\t\terror.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to write to, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/test.txt\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'contents',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'File contents to write',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_list_files: {\n\t\ttitle: 'List Files',\n\t\terrorPrefix: 'Error listing files',\n\t\tdescription: `List files and directories at a given path\n\t\t\tin the WordPress virtual filesystem. Returns a\n\t\t\tflat, non-recursive listing of the immediate\n\t\t\tcontents. To explore subdirectories, call this tool\n\t\t\tagain with the subdirectory path.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to list, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/plugins\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_mkdir: {\n\t\ttitle: 'Create Directory',\n\t\terrorPrefix: 'Error creating directory',\n\t\tdescription: `Create a directory (and all required parent\n\t\t\tdirectories) in the WordPress virtual filesystem.\n\t\t\tCall this before playground_write_file when writing\n\t\t\tto a path whose parent directories do not yet\n\t\t\texist.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: true,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path of directory to\n\t\t\t\t\tcreate, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/my-plugin\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_file: {\n\t\ttitle: 'Delete File',\n\t\terrorPrefix: 'Error deleting file',\n\t\tdescription: `Delete a file from the WordPress virtual\n\t\t\tfilesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. Returns an error if the file does not\n\t\t\texist — use playground_file_exists first if\n\t\t\tdeletion is conditional.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of file to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_directory: {\n\t\ttitle: 'Delete Directory',\n\t\terrorPrefix: 'Error deleting directory',\n\t\tdescription: `Delete a directory from the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. By default (recursive=false), the directory\n\t\t\tmust be empty or the call will fail. Set\n\t\t\trecursive=true to delete a directory and all its\n\t\t\tcontents — use with care.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of directory to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'recursive',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: `If true, delete directory and\n\t\t\t\t\tall contents. If false (default), fails\n\t\t\t\t\ton non-empty directories.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_file_exists: {\n\t\ttitle: 'File Exists',\n\t\terrorPrefix: 'Error checking file existence',\n\t\tdescription: `Check whether a file or directory exists\n\t\t\tin the WordPress virtual filesystem.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path to check',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n};\n\n// -- Site management tool definitions --\n\nexport function getSiteToolDefinitions(): Record<string, ToolDefinition> {\n\treturn {\n\t\tplayground_list_sites: {\n\t\t\ttitle: 'List Available Sites',\n\t\t\terrorPrefix: 'Error listing sites',\n\t\t\tdescription: `List all WordPress Playground sites\n\t\t\tavailable. Call this before any other playground\n\t\t\ttool — it returns the siteId required by every\n\t\t\tother operation.\n\n\t\t\tIf this returns no sites, the user may need to\n\t\t\topen Playground in their browser. Use\n\t\t\tplayground_get_website_url to get the exact URL\n\t\t\tto send to the user.\n\n\t\t\tReturns site names and storage type. \"temporary\"\n\t\t\tsites are lost on page reload, \"opfs\" sites persist\n\t\t\tacross reloads. Call playground_save_in_browser to persist\n\t\t\ta temporary site.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_open_site_in_new_tab: {\n\t\t\ttitle: 'Open Site in New Tab',\n\t\t\terrorPrefix: 'Error opening site in new tab',\n\t\t\tdescription: `Open a WordPress Playground site in a new\n\t\t\tbrowser tab. The site must appear in\n\t\t\tplayground_list_sites.\n\n\t\t\tCheck playground_get_current_url first — if the\n\t\t\tsite is already open in a tab, calling this tool\n\t\t\twill open a second tab rather than switching to\n\t\t\tthe existing one.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_rename_site: {\n\t\t\ttitle: 'Rename Site',\n\t\t\terrorPrefix: 'Error renaming site',\n\t\t\tdescription: `Rename a WordPress Playground site. Updates\n\t\t\tthe display name shown in the browser UI.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: 'newName',\n\t\t\t\t\ttype: 'string',\n\t\t\t\t\tdescription: 'The new display name for the site',\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tplayground_save_in_browser: {\n\t\t\ttitle: 'Save in Browser',\n\t\t\terrorPrefix: 'Error saving site',\n\t\t\tdescription: `Save a temporary WordPress Playground site\n\t\t\tto browser storage so it survives page reloads.\n\t\t\tSafe to call even if the site is already saved\n\t\t\t(no-op).\n\n\t\t\tSites start as temporary by default and are lost\n\t\t\twhen the browser tab is closed or the page is\n\t\t\treloaded. Call this early in any multi-step\n\t\t\tworkflow where losing progress would be costly.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_get_website_url: {\n\t\t\ttitle: 'Get Playground Website URL',\n\t\t\terrorPrefix: 'Error getting website URL',\n\t\t\tdescription: `Return the URL the user must open in their\n\t\t\tbrowser to use WordPress Playground with this MCP\n\t\t\tserver. Use this tool whenever you need to send\n\t\t\tthe user to Playground or open it autonomously —\n\t\t\talways obtain the URL from this tool rather than\n\t\t\tconstructing it manually.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t\tidempotentHint: true,\n\t\t\t\topenWorldHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t};\n}\n\nexport function stringifyError(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\tif (typeof error === 'string') {\n\t\treturn error;\n\t}\n\ttry {\n\t\treturn JSON.stringify(error);\n\t} catch {\n\t\treturn String(error);\n\t}\n}\n\n/**\n * Translate internal Playground storage types to user-facing names.\n */\nexport function formatStorageLabel(raw: string): string {\n\treturn raw === 'none' ? 'temporary' : raw;\n}\n\n/**\n * Convert ToolParam[] to a plain JSON Schema object.\n * Used by WebMCP which expects raw JSON Schema (not Zod).\n */\nexport function paramsToJsonSchema(\n\tparams: ToolParam[]\n): Record<string, unknown> {\n\tconst properties: Record<string, Record<string, unknown>> = {};\n\tconst required: string[] = [];\n\n\tfor (const param of params) {\n\t\tconst prop: Record<string, unknown> = {\n\t\t\ttype: param.type,\n\t\t\tdescription: param.description,\n\t\t};\n\t\tif (param.additionalProperties !== undefined) {\n\t\t\tprop['additionalProperties'] = param.additionalProperties;\n\t\t}\n\t\tif (param.default !== undefined) {\n\t\t\tprop['default'] = param.default;\n\t\t}\n\t\tproperties[param.name] = prop;\n\t\tif (param.required) {\n\t\t\trequired.push(param.name);\n\t\t}\n\t}\n\n\tconst schema: Record<string, unknown> = {\n\t\ttype: 'object',\n\t\tproperties,\n\t};\n\tif (required.length > 0) {\n\t\tschema['required'] = required;\n\t}\n\treturn schema;\n}\n","/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst url = input['url'] as string;\n\t\tconst method = (input['method'] as string) ?? 'GET';\n\t\tconst headers = {\n\t\t\t...((input['headers'] as Record<string, string>) ?? {}),\n\t\t};\n\t\tconst body = input['body'] as string | undefined;\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url, 'http://localhost');\n\t\t\tconst isRestApi =\n\t\t\t\turl.includes('/wp-json/') ||\n\t\t\t\tparsedUrl.searchParams.has('rest_route');\n\n\t\t\t// Auto-set Content-Type for REST API JSON bodies.\n\t\t\tif (\n\t\t\t\tisRestApi &&\n\t\t\t\tbody &&\n\t\t\t\t!Object.keys(headers).some(\n\t\t\t\t\t(k) => k.toLowerCase() === 'content-type'\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\t}\n\n\t\t\tconst hasNonce = Object.keys(headers).some(\n\t\t\t\t(k) => k.toLowerCase() === 'x-wp-nonce'\n\t\t\t);\n\n\t\t\tif (isRestApi && !hasNonce) {\n\t\t\t\t// Generate the nonce via a temporary PHP file requested\n\t\t\t\t// through request() — not run() — so that the cookie\n\t\t\t\t// store is included and WordPress ties the nonce to\n\t\t\t\t// the logged-in user.\n\t\t\t\tconst nonceId = Math.random().toString(36).slice(2, 10);\n\t\t\t\tconst noncePath = `/wordpress/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceUrl = `/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceCode =\n\t\t\t\t\t\"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');\";\n\t\t\t\tawait client.writeFile(noncePath, nonceCode);\n\t\t\t\tlet nonce = '';\n\t\t\t\ttry {\n\t\t\t\t\tconst nonceResp = await client.request({\n\t\t\t\t\t\turl: nonceUrl,\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t});\n\t\t\t\t\tnonce = nonceResp.text.trim();\n\t\t\t\t} finally {\n\t\t\t\t\tawait client.unlink(noncePath);\n\t\t\t\t}\n\t\t\t\tif (nonce && nonce !== '0') {\n\t\t\t\t\theaders['X-WP-Nonce'] = nonce;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Nonce generation failed — proceed without it.\n\t\t}\n\n\t\treturn await client.request({ url, method, headers, body });\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["PLAYGROUND_BASE_URL","playgroundUrl","port","baseUrl","url","toolDefinitions","getSiteToolDefinitions","stringifyError","error","formatStorageLabel","raw","paramsToJsonSchema","params","properties","required","param","prop","schema","executeSiteInfo","client","infoText","resp","info","toolExecutors","input","method","headers","body","parsedUrl","isRestApi","k","hasNonce","nonceId","noncePath","nonceUrl","nonce","createToolClient","decoder","overrides","options","target","override","val"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-executors-DtS6CH_z.cjs","sources":["../../../../packages/playground/mcp/src/tools/tool-definitions.ts","../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Tool metadata and schema helpers for WordPress Playground.\n *\n * Pure data — no execution logic. Both the MCP server and\n * WebMCP import these for consistent descriptions, annotations,\n * and schema conversion.\n */\n\nexport interface ToolAnnotations {\n\treadOnlyHint?: boolean;\n\tdestructiveHint?: boolean;\n\tidempotentHint?: boolean;\n\topenWorldHint?: boolean;\n}\n\nexport type ToolParamType = 'string' | 'boolean' | 'object';\n\nexport interface ToolParam {\n\tname: string;\n\ttype: ToolParamType;\n\tdescription: string;\n\trequired: boolean;\n\tadditionalProperties?: boolean;\n\tdefault?: unknown;\n}\n\nexport interface ToolDefinition {\n\ttitle: string;\n\tdescription: string;\n\terrorPrefix: string;\n\tannotations: ToolAnnotations;\n\tparams: ToolParam[];\n}\n\nconst PLAYGROUND_BASE_URL = 'https://playground.wordpress.net/';\n\nexport function playgroundUrl(port: number, baseUrl?: string): string {\n\tif (!baseUrl) {\n\t\treturn `${PLAYGROUND_BASE_URL}?mcp-port=${port}`;\n\t}\n\tconst url = new URL(baseUrl);\n\turl.searchParams.set('mcp-port', String(port));\n\treturn url.toString();\n}\n\n// -- Per-site tool definitions --\n\nexport const toolDefinitions: Record<string, ToolDefinition> = {\n\tplayground_execute_php: {\n\t\ttitle: 'Execute PHP Code',\n\t\terrorPrefix: 'Error executing PHP',\n\t\tdescription: `Run arbitrary PHP code in WordPress Playground\n\t\t\tand return the output.\n\n\t\t\tWordPress is NOT bootstrapped automatically. To use\n\t\t\tWordPress functions, start your code with:\n\t\t\trequire(\"/wordpress/wp-load.php\");\n\t\t\tAlways include the opening <?php tag.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": stdout output\n\t\t\t- \"errors\": PHP warnings, notices, and fatal error\n\t\t\t messages from stderr\n\t\t\t- \"exitCode\": 0 on success, non-zero on fatal error\n\t\t\tCheck both \"errors\" and \"exitCode\" to determine\n\t\t\twhether the call succeeded.\n\n\t\t\tWARNING: output is returned in full with no\n\t\t\ttruncation — avoid queries that produce unbounded\n\t\t\toutput (e.g. SELECT * without LIMIT). Keep output\n\t\t\tunder 50 KB to avoid filling the context window.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'code',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `PHP code to execute. Example:\n\t\t\t\t\t\"<?php echo get_bloginfo('name');\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_request: {\n\t\ttitle: 'HTTP Request',\n\t\terrorPrefix: 'Error making request',\n\t\tdescription: `Make an HTTP request to the WordPress site\n\t\t\trunning in Playground. REST API requests\n\t\t\t(/wp-json/* or ?rest_route=) are automatically\n\t\t\tauthenticated with a valid nonce — no manual\n\t\t\tauth needed.\n\n\t\t\tTool selection guide:\n\t\t\t1. Use this tool (playground_request) with the\n\t\t\t REST API for standard content CRUD — posts,\n\t\t\t pages, users, terms, comments, settings, etc.\n\t\t\t The REST API handles serialization, pagination,\n\t\t\t and field filtering for you. Prefer compact\n\t\t\t REST responses: use _fields to request only the\n\t\t\t fields needed and per_page/page for large\n\t\t\t collections. Before using endpoint-specific\n\t\t\t filters, inspect the route metadata from\n\t\t\t /wp-json/ and use only args documented for that\n\t\t\t route.\n\t\t\t2. Use this tool with the WordPress REST API to\n\t\t\t discover and call registered abilities when the\n\t\t\t site exposes the Abilities API:\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities\n\t\t\t lists registered abilities. Always assume this\n\t\t\t response may be large and start with _fields,\n\t\t\t e.g.\n\t\t\t /wp-json/wp-abilities/v1/abilities?_fields=name,category,label,description\n\t\t\t Do not assume filters like search or category\n\t\t\t exist unless /wp-json/ route metadata documents\n\t\t\t them.\n\t\t\t - GET /wp-json/wp-abilities/v1/abilities/{name}\n\t\t\t gets one ability, including schemas and\n\t\t\t metadata. Ability names include \"/\" in the\n\t\t\t path; do not encode it. For example, use\n\t\t\t /wp-json/wp-abilities/v1/abilities/memex/save-note\n\t\t\t for \"memex/save-note\".\n\t\t\t - GET, POST, or DELETE\n\t\t\t /wp-json/wp-abilities/v1/abilities/{name}/run\n\t\t\t executes the ability. Inspect the ability\n\t\t\t metadata first, then call /run with the\n\t\t\t documented method. POST bodies usually wrap\n\t\t\t arguments in an \"input\" object, e.g.\n\t\t\t {\"input\":{\"title\":\"Hello\"}}.\n\t\t\t - GET /wp-json/wp-abilities/v1/categories\n\t\t\t lists ability categories. Each category includes\n\t\t\t _links.abilities; follow that URL to list\n\t\t\t abilities in that category. The abilities\n\t\t\t collection can also be filtered directly with\n\t\t\t /wp-json/wp-abilities/v1/abilities?category={slug}.\n\t\t\t3. Use playground_execute_php when the data you\n\t\t\t need is not exposed by the REST API (e.g.\n\t\t\t raw options, direct database queries, or\n\t\t\t custom table access).\n\t\t\t4. Use this tool as a plain HTTP request (non-REST)\n\t\t\t when the HTTP layer itself matters: verifying\n\t\t\t redirects, status codes, cookies, or response\n\t\t\t headers.\n\n\t\t\tThe response JSON contains three fields:\n\t\t\t- \"text\": the response body as a string\n\t\t\t- \"httpStatusCode\": HTTP status code (200, 404…)\n\t\t\t- \"headers\": response headers as key-value pairs\n\t\t\tCheck \"httpStatusCode\" to determine success.\n\n\t\t\tNote: full HTML responses can be very large and may\n\t\t\tfill the context window. To change the URL the user\n\t\t\tsees in their tab, use playground_navigate instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Request URL path, e.g.\n\t\t\t\t\t\"/wp-json/wp/v2/posts\" or\n\t\t\t\t\t\"/wp-admin/plugins.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'method',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `HTTP method (GET, POST, PUT,\n\t\t\t\t\tDELETE, etc.). Defaults to GET.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: 'GET',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'headers',\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: `Request headers as string key-value\n\t\t\t\t\tpairs, e.g. {\"Content-Type\":\n\t\t\t\t\t\"application/json\"}`,\n\t\t\t\trequired: false,\n\t\t\t\tadditionalProperties: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'body',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Request body (for POST/PUT requests)',\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_navigate: {\n\t\ttitle: 'Navigate to URL',\n\t\terrorPrefix: 'Error navigating',\n\t\tdescription: `Navigate to a URL path in WordPress\n\t\t\tPlayground and return the final URL after any\n\t\t\tredirects. Examples: \"/wp-admin/\",\n\t\t\t\"/wp-login.php\", \"/\".\n\n\t\t\tOn 404 or error pages, navigation still succeeds\n\t\t\tfrom the tool's perspective — check the returned\n\t\t\tURL or use playground_request to verify the HTTP\n\t\t\tstatus code if needed.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `The URL path to navigate to,\n\t\t\t\t\te.g. \"/wp-admin/\" or\n\t\t\t\t\t\"/wp-login.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_get_current_url: {\n\t\ttitle: 'Get Current URL',\n\t\terrorPrefix: 'Error getting current URL',\n\t\tdescription: `Get the current URL path of the WordPress\n\t\t\tsite displayed in Playground. For additional\n\t\t\tmetadata (WordPress version, PHP version, document\n\t\t\troot), use playground_get_site_info instead.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_get_site_info: {\n\t\ttitle: 'Get Site Info',\n\t\terrorPrefix: 'Error getting site info',\n\t\tdescription: `Get metadata about the running WordPress\n\t\t\tinstance: current URL, document root, site URL,\n\t\t\tWordPress version, and PHP version. Use this when\n\t\t\tyou need version information or the document root\n\t\t\tpath. For just the current URL, prefer\n\t\t\tplayground_get_current_url.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [],\n\t},\n\tplayground_read_file: {\n\t\ttitle: 'Read File',\n\t\terrorPrefix: 'Error reading file',\n\t\tdescription: `Read a file from the WordPress virtual\n\t\t\tfilesystem. Returns the file contents as text.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to the file, e.g.\n\t\t\t\t\t\"/wordpress/wp-config.php\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_write_file: {\n\t\ttitle: 'Write File',\n\t\terrorPrefix: 'Error writing file',\n\t\tdescription: `Write content to a file in the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Overwrites the entire file — existing\n\t\t\tcontent is permanently lost. Read the file first\n\t\t\twith playground_read_file if you need to preserve\n\t\t\tany content.\n\n\t\t\tCreates the file if it does not exist. Parent\n\t\t\tdirectories are NOT created automatically — call\n\t\t\tplayground_mkdir first if needed, otherwise the\n\t\t\twrite will fail with a \"no such file or directory\"\n\t\t\terror.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to write to, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/test.txt\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'contents',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'File contents to write',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_list_files: {\n\t\ttitle: 'List Files',\n\t\terrorPrefix: 'Error listing files',\n\t\tdescription: `List files and directories at a given path\n\t\t\tin the WordPress virtual filesystem. Returns a\n\t\t\tflat, non-recursive listing of the immediate\n\t\t\tcontents. To explore subdirectories, call this tool\n\t\t\tagain with the subdirectory path.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path to list, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/plugins\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_mkdir: {\n\t\ttitle: 'Create Directory',\n\t\terrorPrefix: 'Error creating directory',\n\t\tdescription: `Create a directory (and all required parent\n\t\t\tdirectories) in the WordPress virtual filesystem.\n\t\t\tCall this before playground_write_file when writing\n\t\t\tto a path whose parent directories do not yet\n\t\t\texist.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: false,\n\t\t\tidempotentHint: true,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: `Absolute path of directory to\n\t\t\t\t\tcreate, e.g.\n\t\t\t\t\t\"/wordpress/wp-content/my-plugin\"`,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_file: {\n\t\ttitle: 'Delete File',\n\t\terrorPrefix: 'Error deleting file',\n\t\tdescription: `Delete a file from the WordPress virtual\n\t\t\tfilesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. Returns an error if the file does not\n\t\t\texist — use playground_file_exists first if\n\t\t\tdeletion is conditional.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of file to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_delete_directory: {\n\t\ttitle: 'Delete Directory',\n\t\terrorPrefix: 'Error deleting directory',\n\t\tdescription: `Delete a directory from the WordPress\n\t\t\tvirtual filesystem.\n\n\t\t\tWARNING: Deletion is permanent and cannot be\n\t\t\tundone. By default (recursive=false), the directory\n\t\t\tmust be empty or the call will fail. Set\n\t\t\trecursive=true to delete a directory and all its\n\t\t\tcontents — use with care.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path of directory to delete',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'recursive',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: `If true, delete directory and\n\t\t\t\t\tall contents. If false (default), fails\n\t\t\t\t\ton non-empty directories.`,\n\t\t\t\trequired: false,\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t],\n\t},\n\tplayground_file_exists: {\n\t\ttitle: 'File Exists',\n\t\terrorPrefix: 'Error checking file existence',\n\t\tdescription: `Check whether a file or directory exists\n\t\t\tin the WordPress virtual filesystem.`,\n\t\tannotations: {\n\t\t\treadOnlyHint: true,\n\t\t\tdestructiveHint: false,\n\t\t\topenWorldHint: true,\n\t\t},\n\t\tparams: [\n\t\t\t{\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Absolute path to check',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t},\n};\n\n// -- Site management tool definitions --\n\nexport function getSiteToolDefinitions(): Record<string, ToolDefinition> {\n\treturn {\n\t\tplayground_list_sites: {\n\t\t\ttitle: 'List Available Sites',\n\t\t\terrorPrefix: 'Error listing sites',\n\t\t\tdescription: `List all WordPress Playground sites\n\t\t\tavailable. Call this before any other playground\n\t\t\ttool — it returns the siteId required by every\n\t\t\tother operation.\n\n\t\t\tIf this returns no sites, the user may need to\n\t\t\topen Playground in their browser. Use\n\t\t\tplayground_get_website_url to get the exact URL\n\t\t\tto send to the user.\n\n\t\t\tReturns site names and storage type. \"temporary\"\n\t\t\tsites are lost on page reload, \"opfs\" sites persist\n\t\t\tacross reloads. Call playground_save_in_browser to persist\n\t\t\ta temporary site.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_open_site_in_new_tab: {\n\t\t\ttitle: 'Open Site in New Tab',\n\t\t\terrorPrefix: 'Error opening site in new tab',\n\t\t\tdescription: `Open a WordPress Playground site in a new\n\t\t\tbrowser tab. The site must appear in\n\t\t\tplayground_list_sites.\n\n\t\t\tCheck playground_get_current_url first — if the\n\t\t\tsite is already open in a tab, calling this tool\n\t\t\twill open a second tab rather than switching to\n\t\t\tthe existing one.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_rename_site: {\n\t\t\ttitle: 'Rename Site',\n\t\t\terrorPrefix: 'Error renaming site',\n\t\t\tdescription: `Rename a WordPress Playground site. Updates\n\t\t\tthe display name shown in the browser UI.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: 'newName',\n\t\t\t\t\ttype: 'string',\n\t\t\t\t\tdescription: 'The new display name for the site',\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tplayground_save_in_browser: {\n\t\t\ttitle: 'Save in Browser',\n\t\t\terrorPrefix: 'Error saving site',\n\t\t\tdescription: `Save a temporary WordPress Playground site\n\t\t\tto browser storage so it survives page reloads.\n\t\t\tSafe to call even if the site is already saved\n\t\t\t(no-op).\n\n\t\t\tSites start as temporary by default and are lost\n\t\t\twhen the browser tab is closed or the page is\n\t\t\treloaded. Call this early in any multi-step\n\t\t\tworkflow where losing progress would be costly.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: false,\n\t\t\t\tdestructiveHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t\tplayground_get_website_url: {\n\t\t\ttitle: 'Get Playground Website URL',\n\t\t\terrorPrefix: 'Error getting website URL',\n\t\t\tdescription: `Return the URL the user must open in their\n\t\t\tbrowser to use WordPress Playground with this MCP\n\t\t\tserver. Use this tool whenever you need to send\n\t\t\tthe user to Playground or open it autonomously —\n\t\t\talways obtain the URL from this tool rather than\n\t\t\tconstructing it manually.`,\n\t\t\tannotations: {\n\t\t\t\treadOnlyHint: true,\n\t\t\t\tdestructiveHint: false,\n\t\t\t\tidempotentHint: true,\n\t\t\t\topenWorldHint: false,\n\t\t\t},\n\t\t\tparams: [],\n\t\t},\n\t};\n}\n\nexport function stringifyError(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\tif (typeof error === 'string') {\n\t\treturn error;\n\t}\n\ttry {\n\t\treturn JSON.stringify(error);\n\t} catch {\n\t\treturn String(error);\n\t}\n}\n\n/**\n * Translate internal Playground storage types to user-facing names.\n */\nexport function formatStorageLabel(raw: string): string {\n\treturn raw === 'none' ? 'temporary' : raw;\n}\n\n/**\n * Convert ToolParam[] to a plain JSON Schema object.\n * Used by WebMCP which expects raw JSON Schema (not Zod).\n */\nexport function paramsToJsonSchema(\n\tparams: ToolParam[]\n): Record<string, unknown> {\n\tconst properties: Record<string, Record<string, unknown>> = {};\n\tconst required: string[] = [];\n\n\tfor (const param of params) {\n\t\tconst prop: Record<string, unknown> = {\n\t\t\ttype: param.type,\n\t\t\tdescription: param.description,\n\t\t};\n\t\tif (param.additionalProperties !== undefined) {\n\t\t\tprop['additionalProperties'] = param.additionalProperties;\n\t\t}\n\t\tif (param.default !== undefined) {\n\t\t\tprop['default'] = param.default;\n\t\t}\n\t\tproperties[param.name] = prop;\n\t\tif (param.required) {\n\t\t\trequired.push(param.name);\n\t\t}\n\t}\n\n\tconst schema: Record<string, unknown> = {\n\t\ttype: 'object',\n\t\tproperties,\n\t};\n\tif (required.length > 0) {\n\t\tschema['required'] = required;\n\t}\n\treturn schema;\n}\n","/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst url = input['url'] as string;\n\t\tconst method = (input['method'] as string) ?? 'GET';\n\t\tconst headers = {\n\t\t\t...((input['headers'] as Record<string, string>) ?? {}),\n\t\t};\n\t\tconst body = input['body'] as string | undefined;\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url, 'http://localhost');\n\t\t\tconst isRestApi =\n\t\t\t\turl.includes('/wp-json/') ||\n\t\t\t\tparsedUrl.searchParams.has('rest_route');\n\n\t\t\t// Auto-set Content-Type for REST API JSON bodies.\n\t\t\tif (\n\t\t\t\tisRestApi &&\n\t\t\t\tbody &&\n\t\t\t\t!Object.keys(headers).some(\n\t\t\t\t\t(k) => k.toLowerCase() === 'content-type'\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\t}\n\n\t\t\tconst hasNonce = Object.keys(headers).some(\n\t\t\t\t(k) => k.toLowerCase() === 'x-wp-nonce'\n\t\t\t);\n\n\t\t\tif (isRestApi && !hasNonce) {\n\t\t\t\t// Generate the nonce via a temporary PHP file requested\n\t\t\t\t// through request() — not run() — so that the cookie\n\t\t\t\t// store is included and WordPress ties the nonce to\n\t\t\t\t// the logged-in user.\n\t\t\t\tconst nonceId = Math.random().toString(36).slice(2, 10);\n\t\t\t\tconst noncePath = `/wordpress/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceUrl = `/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceCode =\n\t\t\t\t\t\"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');\";\n\t\t\t\tawait client.writeFile(noncePath, nonceCode);\n\t\t\t\tlet nonce = '';\n\t\t\t\ttry {\n\t\t\t\t\tconst nonceResp = await client.request({\n\t\t\t\t\t\turl: nonceUrl,\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t});\n\t\t\t\t\tnonce = nonceResp.text.trim();\n\t\t\t\t} finally {\n\t\t\t\t\tawait client.unlink(noncePath);\n\t\t\t\t}\n\t\t\t\tif (nonce && nonce !== '0') {\n\t\t\t\t\theaders['X-WP-Nonce'] = nonce;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Nonce generation failed — proceed without it.\n\t\t}\n\n\t\treturn await client.request({ url, method, headers, body });\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["PLAYGROUND_BASE_URL","playgroundUrl","port","baseUrl","url","toolDefinitions","getSiteToolDefinitions","stringifyError","error","formatStorageLabel","raw","paramsToJsonSchema","params","properties","required","param","prop","schema","executeSiteInfo","client","infoText","resp","info","toolExecutors","input","method","headers","body","parsedUrl","isRestApi","k","hasNonce","nonceId","noncePath","nonceUrl","nonce","createToolClient","decoder","overrides","options","target","override","val"],"mappings":"aAkCA,MAAMA,EAAsB,oCAErB,SAASC,EAAcC,EAAcC,EAA0B,CACrE,GAAI,CAACA,EACJ,MAAO,GAAGH,CAAmB,aAAaE,CAAI,GAE/C,MAAME,EAAM,IAAI,IAAID,CAAO,EAC3B,OAAAC,EAAI,aAAa,IAAI,WAAY,OAAOF,CAAI,CAAC,EACtCE,EAAI,SAAA,CACZ,CAIO,MAAMC,EAAkD,CAC9D,uBAAwB,CACvB,MAAO,mBACP,YAAa,sBACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qDAoBb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa;AAAA,yCAEb,SAAU,EAAA,CACX,CACD,EAED,mBAAoB,CACnB,MAAO,eACP,YAAa,uBACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wDAkEb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,MACN,KAAM,SACN,YAAa;AAAA;AAAA,8BAGb,SAAU,EAAA,EAEX,CACC,KAAM,SACN,KAAM,SACN,YAAa;AAAA,sCAEb,SAAU,GACV,QAAS,KAAA,EAEV,CACC,KAAM,UACN,KAAM,SACN,YAAa;AAAA;AAAA,0BAGb,SAAU,GACV,qBAAsB,EAAA,EAEvB,CACC,KAAM,OACN,KAAM,SACN,YAAa,uCACb,SAAU,EAAA,CACX,CACD,EAED,oBAAqB,CACpB,MAAO,kBACP,YAAa,mBACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BASb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa;AAAA;AAAA,sBAGb,SAAU,EAAA,CACX,CACD,EAED,2BAA4B,CAC3B,MAAO,kBACP,YAAa,4BACb,YAAa;AAAA;AAAA;AAAA,iDAIb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,cAAe,EAAA,EAEhB,OAAQ,CAAA,CAAC,EAEV,yBAA0B,CACzB,MAAO,gBACP,YAAa,0BACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA,gCAMb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,cAAe,EAAA,EAEhB,OAAQ,CAAA,CAAC,EAEV,qBAAsB,CACrB,MAAO,YACP,YAAa,qBACb,YAAa;AAAA,mDAEb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa;AAAA,iCAEb,SAAU,EAAA,CACX,CACD,EAED,sBAAuB,CACtB,MAAO,aACP,YAAa,qBACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAab,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa;AAAA,uCAEb,SAAU,EAAA,EAEX,CACC,KAAM,WACN,KAAM,SACN,YAAa,yBACb,SAAU,EAAA,CACX,CACD,EAED,sBAAuB,CACtB,MAAO,aACP,YAAa,sBACb,YAAa;AAAA;AAAA;AAAA;AAAA,sCAKb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa;AAAA,sCAEb,SAAU,EAAA,CACX,CACD,EAED,iBAAkB,CACjB,MAAO,mBACP,YAAa,2BACb,YAAa;AAAA;AAAA;AAAA;AAAA,WAKb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa;AAAA;AAAA,wCAGb,SAAU,EAAA,CACX,CACD,EAED,uBAAwB,CACvB,MAAO,cACP,YAAa,sBACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAOb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa,kCACb,SAAU,EAAA,CACX,CACD,EAED,4BAA6B,CAC5B,MAAO,mBACP,YAAa,2BACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAQb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa,uCACb,SAAU,EAAA,EAEX,CACC,KAAM,YACN,KAAM,UACN,YAAa;AAAA;AAAA,gCAGb,SAAU,GACV,QAAS,EAAA,CACV,CACD,EAED,uBAAwB,CACvB,MAAO,cACP,YAAa,gCACb,YAAa;AAAA,yCAEb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,cAAe,EAAA,EAEhB,OAAQ,CACP,CACC,KAAM,OACN,KAAM,SACN,YAAa,yBACb,SAAU,EAAA,CACX,CACD,CAEF,EAIO,SAASC,GAAyD,CACxE,MAAO,CACN,sBAAuB,CACtB,MAAO,uBACP,YAAa,sBACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAcb,YAAa,CACZ,aAAc,GACd,gBAAiB,EAAA,EAElB,OAAQ,CAAA,CAAC,EAEV,gCAAiC,CAChC,MAAO,uBACP,YAAa,gCACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAQb,YAAa,CACZ,aAAc,GACd,gBAAiB,EAAA,EAElB,OAAQ,CAAA,CAAC,EAEV,uBAAwB,CACvB,MAAO,cACP,YAAa,sBACb,YAAa;AAAA,8CAEb,YAAa,CACZ,aAAc,GACd,gBAAiB,EAAA,EAElB,OAAQ,CACP,CACC,KAAM,UACN,KAAM,SACN,YAAa,oCACb,SAAU,EAAA,CACX,CACD,EAED,2BAA4B,CAC3B,MAAO,kBACP,YAAa,oBACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oDASb,YAAa,CACZ,aAAc,GACd,gBAAiB,EAAA,EAElB,OAAQ,CAAA,CAAC,EAEV,2BAA4B,CAC3B,MAAO,6BACP,YAAa,4BACb,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMb,YAAa,CACZ,aAAc,GACd,gBAAiB,GACjB,eAAgB,GAChB,cAAe,EAAA,EAEhB,OAAQ,CAAA,CAAC,CACV,CAEF,CAEO,SAASC,EAAeC,EAAwB,CACtD,GAAIA,aAAiB,MACpB,OAAOA,EAAM,QAEd,GAAI,OAAOA,GAAU,SACpB,OAAOA,EAER,GAAI,CACH,OAAO,KAAK,UAAUA,CAAK,CAC5B,MAAQ,CACP,OAAO,OAAOA,CAAK,CACpB,CACD,CAKO,SAASC,EAAmBC,EAAqB,CACvD,OAAOA,IAAQ,OAAS,YAAcA,CACvC,CAMO,SAASC,EACfC,EAC0B,CAC1B,MAAMC,EAAsD,CAAA,EACtDC,EAAqB,CAAA,EAE3B,UAAWC,KAASH,EAAQ,CAC3B,MAAMI,EAAgC,CACrC,KAAMD,EAAM,KACZ,YAAaA,EAAM,WAAA,EAEhBA,EAAM,uBAAyB,SAClCC,EAAK,qBAA0BD,EAAM,sBAElCA,EAAM,UAAY,SACrBC,EAAK,QAAaD,EAAM,SAEzBF,EAAWE,EAAM,IAAI,EAAIC,EACrBD,EAAM,UACTD,EAAS,KAAKC,EAAM,IAAI,CAE1B,CAEA,MAAME,EAAkC,CACvC,KAAM,SACN,WAAAJ,CAAA,EAED,OAAIC,EAAS,OAAS,IACrBG,EAAO,SAAcH,GAEfG,CACR,CCjiBA,eAAeC,EAAgBC,EAAuC,CACrE,KAAM,CAACf,EAAKgB,CAAQ,EAAI,MAAM,QAAQ,IAAI,CACzCD,EAAO,cAAA,EACPA,EACE,IAAI,CACJ,KAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAQN,EACA,KAAME,GAASA,EAAK,IAAI,CAAA,CAC1B,EAED,IAAIC,EACJ,GAAI,CACHA,EAAO,KAAK,MAAMF,CAAQ,CAC3B,MAAQ,CACPE,EAAO,CAAA,CACR,CAEA,MAAO,CACN,IAAK,OAAOlB,CAAG,EACf,aAAckB,EAAK,cAAgB,aACnC,QAASA,EAAK,SAAW,OAAOlB,CAAG,EACnC,UAAWkB,EAAK,WAAa,UAC7B,WAAYA,EAAK,YAAc,SAAA,CAEjC,CAEO,MAAMC,EAGT,CACH,uBAAwB,CAACJ,EAAQK,IAChCL,EAAO,IAAI,CAAE,KAAMK,EAAM,KAAmB,EAE7C,mBAAoB,MAAOL,EAAQK,IAAU,CAC5C,MAAMpB,EAAMoB,EAAM,IACZC,EAAUD,EAAM,QAAwB,MACxCE,EAAU,CACf,GAAKF,EAAM,SAAyC,CAAA,CAAC,EAEhDG,EAAOH,EAAM,KAEnB,GAAI,CACH,MAAMI,EAAY,IAAI,IAAIxB,EAAK,kBAAkB,EAC3CyB,EACLzB,EAAI,SAAS,WAAW,GACxBwB,EAAU,aAAa,IAAI,YAAY,EAIvCC,GACAF,GACA,CAAC,OAAO,KAAKD,CAAO,EAAE,KACpBI,GAAMA,EAAE,gBAAkB,cAAA,IAG5BJ,EAAQ,cAAc,EAAI,oBAG3B,MAAMK,EAAW,OAAO,KAAKL,CAAO,EAAE,KACpCI,GAAMA,EAAE,gBAAkB,YAAA,EAG5B,GAAID,GAAa,CAACE,EAAU,CAK3B,MAAMC,EAAU,KAAK,SAAS,SAAS,EAAE,EAAE,MAAM,EAAG,EAAE,EAChDC,EAAY,mCAAmCD,CAAO,OACtDE,EAAW,yBAAyBF,CAAO,OAGjD,MAAMb,EAAO,UAAUc,EADtB,+EAC0C,EAC3C,IAAIE,EAAQ,GACZ,GAAI,CAKHA,GAJkB,MAAMhB,EAAO,QAAQ,CACtC,IAAKe,EACL,OAAQ,KAAA,CACR,GACiB,KAAK,KAAA,CACxB,QAAA,CACC,MAAMf,EAAO,OAAOc,CAAS,CAC9B,CACIE,GAASA,IAAU,MACtBT,EAAQ,YAAY,EAAIS,EAE1B,CACD,MAAQ,CAER,CAEA,OAAO,MAAMhB,EAAO,QAAQ,CAAE,IAAAf,EAAK,OAAAqB,EAAQ,QAAAC,EAAS,KAAAC,EAAM,CAC3D,EAEA,oBAAqB,MAAOR,EAAQK,KACnC,MAAML,EAAO,KAAKK,EAAM,IAAiB,EAClC,CAAE,IAAK,MAAML,EAAO,eAAc,GAG1C,2BAA4B,MAAOA,IAAY,CAC9C,IAAK,MAAMA,EAAO,cAAA,CAAc,GAGjC,yBAA2BA,GAC1BD,EAAgBC,CAAM,EAEvB,qBAAsB,MAAOA,EAAQK,KAAW,CAC/C,SAAU,MAAML,EAAO,eAAeK,EAAM,IAAiB,CAAA,GAG9D,sBAAuB,MAAOL,EAAQK,KACrC,MAAML,EAAO,UACZK,EAAM,KACNA,EAAM,QAAU,EAEV,CAAE,QAAS,EAAA,GAGnB,sBAAuB,MAAOL,EAAQK,KAAW,CAChD,MAAO,MAAML,EAAO,UAAUK,EAAM,IAAiB,CAAA,GAGtD,iBAAkB,MAAOL,EAAQK,KAChC,MAAML,EAAO,UAAUK,EAAM,IAAiB,EACvC,CAAE,QAAS,EAAA,GAGnB,uBAAwB,MAAOL,EAAQK,KACtC,MAAML,EAAO,OAAOK,EAAM,IAAiB,EACpC,CAAE,QAAS,EAAA,GAGnB,4BAA6B,MAAOL,EAAQK,KAC3C,MAAML,EAAO,MAAMK,EAAM,KAAmB,CAC3C,UAAYA,EAAM,WAA4B,EAAA,CAC9C,EACM,CAAE,QAAS,EAAA,GAGnB,uBAAwB,MAAOL,EAAQK,KAAW,CACjD,OAAQ,MAAML,EAAO,WAAWK,EAAM,IAAiB,CAAA,EAEzD,EASO,SAASY,EAAiBjB,EAAsC,CACtE,MAAMkB,EAAU,IAAI,YACdC,EAAiC,CACtC,MAAM,IAAIC,EAAS,CAClB,MAAMlB,EAAO,MAAMF,EAAO,IAAIoB,CAAO,EACrC,MAAO,CACN,KAAMF,EAAQ,OAAOhB,EAAK,KAAK,EAC/B,OAAQA,EAAK,OACb,SAAUA,EAAK,QAAA,CAEjB,EACA,MAAM,QAAQkB,EAAS,CACtB,MAAMlB,EAAO,MAAMF,EAAO,QAAQ,CACjC,IAAKoB,EAAQ,IACb,OAAQA,EAAQ,OAChB,QAASA,EAAQ,QACjB,KAAMA,EAAQ,IAAA,CACd,EACD,MAAO,CACN,KAAMF,EAAQ,OAAOhB,EAAK,KAAK,EAC/B,eAAgBA,EAAK,eACrB,QAASA,EAAK,OAAA,CAEhB,CAAA,EAED,OAAO,IAAI,MAAMF,EAAiC,CACjD,IAAK,CAACqB,EAAQxB,IAAiB,CAC9B,MAAMyB,EAAYH,EAAsCtB,CAAI,EAC5D,GAAIyB,IAAa,OAChB,OAAOA,EAER,MAAMC,EAAOF,EAA8CxB,CAAI,EAC/D,OAAO,OAAO0B,GAAQ,WAAaA,EAAI,KAAKF,CAAM,EAAIE,CACvD,CAAA,CACA,CACF"}