@wp-playground/mcp 3.1.5 → 3.1.9

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.
@@ -1,527 +0,0 @@
1
- /**
2
- * Tool metadata and schema helpers for WordPress Playground.
3
- *
4
- * Pure data — no execution logic. Both the MCP server and
5
- * WebMCP import these for consistent descriptions, annotations,
6
- * and schema conversion.
7
- */
8
-
9
- export interface ToolAnnotations {
10
- readOnlyHint?: boolean;
11
- destructiveHint?: boolean;
12
- idempotentHint?: boolean;
13
- openWorldHint?: boolean;
14
- }
15
-
16
- export type ToolParamType = 'string' | 'boolean' | 'object';
17
-
18
- export interface ToolParam {
19
- name: string;
20
- type: ToolParamType;
21
- description: string;
22
- required: boolean;
23
- additionalProperties?: boolean;
24
- default?: unknown;
25
- }
26
-
27
- export interface ToolDefinition {
28
- title: string;
29
- description: string;
30
- errorPrefix: string;
31
- annotations: ToolAnnotations;
32
- params: ToolParam[];
33
- }
34
-
35
- const PLAYGROUND_BASE_URL = 'https://playground.wordpress.net/';
36
-
37
- export function playgroundUrl(port: number): string {
38
- return `${PLAYGROUND_BASE_URL}?mcp=yes&mcp-port=${port}`;
39
- }
40
-
41
- // -- Per-site tool definitions --
42
-
43
- export const toolDefinitions: Record<string, ToolDefinition> = {
44
- playground_execute_php: {
45
- title: 'Execute PHP Code',
46
- errorPrefix: 'Error executing PHP',
47
- description: `Run arbitrary PHP code in WordPress Playground
48
- and return the output.
49
-
50
- WordPress is NOT bootstrapped automatically. To use
51
- WordPress functions, start your code with:
52
- require("/wordpress/wp-load.php");
53
- Always include the opening <?php tag.
54
-
55
- The response JSON contains three fields:
56
- - "text": stdout output
57
- - "errors": PHP warnings, notices, and fatal error
58
- messages from stderr
59
- - "exitCode": 0 on success, non-zero on fatal error
60
- Check both "errors" and "exitCode" to determine
61
- whether the call succeeded.
62
-
63
- WARNING: output is returned in full with no
64
- truncation — avoid queries that produce unbounded
65
- output (e.g. SELECT * without LIMIT). Keep output
66
- under 50 KB to avoid filling the context window.`,
67
- annotations: {
68
- readOnlyHint: false,
69
- destructiveHint: true,
70
- idempotentHint: false,
71
- openWorldHint: true,
72
- },
73
- params: [
74
- {
75
- name: 'code',
76
- type: 'string',
77
- description: `PHP code to execute. Example:
78
- "<?php echo get_bloginfo('name');"`,
79
- required: true,
80
- },
81
- ],
82
- },
83
- playground_request: {
84
- title: 'HTTP Request',
85
- errorPrefix: 'Error making request',
86
- description: `Make an HTTP request to the WordPress site
87
- running in Playground. Requests are authenticated
88
- automatically via the browser session's cookie
89
- store.
90
-
91
- Prefer playground_execute_php for reading WordPress
92
- data (posts, options, plugin state) — it is faster
93
- and returns only what you echo. Use this tool only
94
- when the HTTP layer itself is what you are testing,
95
- for example: verifying that a URL returns a 301
96
- redirect, that a form submission sets a cookie, or
97
- that a REST endpoint returns the correct status
98
- code.
99
-
100
- Note: full HTML responses can be very large and may
101
- fill the context window. To change the URL the user
102
- sees in their tab, use playground_navigate instead.`,
103
- annotations: {
104
- readOnlyHint: false,
105
- destructiveHint: false,
106
- idempotentHint: false,
107
- openWorldHint: true,
108
- },
109
- params: [
110
- {
111
- name: 'url',
112
- type: 'string',
113
- description: `Request URL path, e.g.
114
- "/wp-json/wp/v2/posts" or
115
- "/wp-admin/plugins.php"`,
116
- required: true,
117
- },
118
- {
119
- name: 'method',
120
- type: 'string',
121
- description: `HTTP method (GET, POST, PUT,
122
- DELETE, etc.). Defaults to GET.`,
123
- required: false,
124
- default: 'GET',
125
- },
126
- {
127
- name: 'headers',
128
- type: 'object',
129
- description: 'Request headers as key-value pairs',
130
- required: false,
131
- additionalProperties: true,
132
- },
133
- {
134
- name: 'body',
135
- type: 'string',
136
- description: 'Request body (for POST/PUT requests)',
137
- required: false,
138
- },
139
- ],
140
- },
141
- playground_navigate: {
142
- title: 'Navigate to URL',
143
- errorPrefix: 'Error navigating',
144
- description: `Navigate to a URL path in WordPress
145
- Playground and return the final URL after any
146
- redirects. Examples: "/wp-admin/",
147
- "/wp-login.php", "/".
148
-
149
- On 404 or error pages, navigation still succeeds
150
- from the tool's perspective — check the returned
151
- URL or use playground_request to verify the HTTP
152
- status code if needed.`,
153
- annotations: {
154
- readOnlyHint: false,
155
- destructiveHint: false,
156
- idempotentHint: false,
157
- openWorldHint: true,
158
- },
159
- params: [
160
- {
161
- name: 'path',
162
- type: 'string',
163
- description: `The URL path to navigate to,
164
- e.g. "/wp-admin/" or
165
- "/wp-login.php"`,
166
- required: true,
167
- },
168
- ],
169
- },
170
- playground_get_current_url: {
171
- title: 'Get Current URL',
172
- errorPrefix: 'Error getting current URL',
173
- description: `Get the current URL path of the WordPress
174
- site displayed in Playground. For additional
175
- metadata (WordPress version, PHP version, document
176
- root), use playground_get_site_info instead.`,
177
- annotations: {
178
- readOnlyHint: true,
179
- destructiveHint: false,
180
- openWorldHint: true,
181
- },
182
- params: [],
183
- },
184
- playground_get_site_info: {
185
- title: 'Get Site Info',
186
- errorPrefix: 'Error getting site info',
187
- description: `Get metadata about the running WordPress
188
- instance: current URL, document root, site URL,
189
- WordPress version, and PHP version. Use this when
190
- you need version information or the document root
191
- path. For just the current URL, prefer
192
- playground_get_current_url.`,
193
- annotations: {
194
- readOnlyHint: true,
195
- destructiveHint: false,
196
- openWorldHint: true,
197
- },
198
- params: [],
199
- },
200
- playground_read_file: {
201
- title: 'Read File',
202
- errorPrefix: 'Error reading file',
203
- description: `Read a file from the WordPress virtual
204
- filesystem. Returns the file contents as text.`,
205
- annotations: {
206
- readOnlyHint: true,
207
- destructiveHint: false,
208
- openWorldHint: true,
209
- },
210
- params: [
211
- {
212
- name: 'path',
213
- type: 'string',
214
- description: `Absolute path to the file, e.g.
215
- "/wordpress/wp-config.php"`,
216
- required: true,
217
- },
218
- ],
219
- },
220
- playground_write_file: {
221
- title: 'Write File',
222
- errorPrefix: 'Error writing file',
223
- description: `Write content to a file in the WordPress
224
- virtual filesystem.
225
-
226
- WARNING: Overwrites the entire file — existing
227
- content is permanently lost. Read the file first
228
- with playground_read_file if you need to preserve
229
- any content.
230
-
231
- Creates the file if it does not exist. Parent
232
- directories are NOT created automatically — call
233
- playground_mkdir first if needed, otherwise the
234
- write will fail with a "no such file or directory"
235
- error.`,
236
- annotations: {
237
- readOnlyHint: false,
238
- destructiveHint: true,
239
- idempotentHint: false,
240
- openWorldHint: true,
241
- },
242
- params: [
243
- {
244
- name: 'path',
245
- type: 'string',
246
- description: `Absolute path to write to, e.g.
247
- "/wordpress/wp-content/test.txt"`,
248
- required: true,
249
- },
250
- {
251
- name: 'contents',
252
- type: 'string',
253
- description: 'File contents to write',
254
- required: true,
255
- },
256
- ],
257
- },
258
- playground_list_files: {
259
- title: 'List Files',
260
- errorPrefix: 'Error listing files',
261
- description: `List files and directories at a given path
262
- in the WordPress virtual filesystem. Returns a
263
- flat, non-recursive listing of the immediate
264
- contents. To explore subdirectories, call this tool
265
- again with the subdirectory path.`,
266
- annotations: {
267
- readOnlyHint: true,
268
- destructiveHint: false,
269
- openWorldHint: true,
270
- },
271
- params: [
272
- {
273
- name: 'path',
274
- type: 'string',
275
- description: `Absolute path to list, e.g.
276
- "/wordpress/wp-content/plugins"`,
277
- required: true,
278
- },
279
- ],
280
- },
281
- playground_mkdir: {
282
- title: 'Create Directory',
283
- errorPrefix: 'Error creating directory',
284
- description: `Create a directory (and all required parent
285
- directories) in the WordPress virtual filesystem.
286
- Call this before playground_write_file when writing
287
- to a path whose parent directories do not yet
288
- exist.`,
289
- annotations: {
290
- readOnlyHint: false,
291
- destructiveHint: false,
292
- idempotentHint: true,
293
- openWorldHint: true,
294
- },
295
- params: [
296
- {
297
- name: 'path',
298
- type: 'string',
299
- description: `Absolute path of directory to
300
- create, e.g.
301
- "/wordpress/wp-content/my-plugin"`,
302
- required: true,
303
- },
304
- ],
305
- },
306
- playground_delete_file: {
307
- title: 'Delete File',
308
- errorPrefix: 'Error deleting file',
309
- description: `Delete a file from the WordPress virtual
310
- filesystem.
311
-
312
- WARNING: Deletion is permanent and cannot be
313
- undone. Returns an error if the file does not
314
- exist — use playground_file_exists first if
315
- deletion is conditional.`,
316
- annotations: {
317
- readOnlyHint: false,
318
- destructiveHint: true,
319
- idempotentHint: false,
320
- openWorldHint: true,
321
- },
322
- params: [
323
- {
324
- name: 'path',
325
- type: 'string',
326
- description: 'Absolute path of file to delete',
327
- required: true,
328
- },
329
- ],
330
- },
331
- playground_delete_directory: {
332
- title: 'Delete Directory',
333
- errorPrefix: 'Error deleting directory',
334
- description: `Delete a directory from the WordPress
335
- virtual filesystem.
336
-
337
- WARNING: Deletion is permanent and cannot be
338
- undone. By default (recursive=false), the directory
339
- must be empty or the call will fail. Set
340
- recursive=true to delete a directory and all its
341
- contents — use with care.`,
342
- annotations: {
343
- readOnlyHint: false,
344
- destructiveHint: true,
345
- idempotentHint: false,
346
- openWorldHint: true,
347
- },
348
- params: [
349
- {
350
- name: 'path',
351
- type: 'string',
352
- description: 'Absolute path of directory to delete',
353
- required: true,
354
- },
355
- {
356
- name: 'recursive',
357
- type: 'boolean',
358
- description: `If true, delete directory and
359
- all contents. If false (default), fails
360
- on non-empty directories.`,
361
- required: false,
362
- default: false,
363
- },
364
- ],
365
- },
366
- playground_file_exists: {
367
- title: 'File Exists',
368
- errorPrefix: 'Error checking file existence',
369
- description: `Check whether a file or directory exists
370
- in the WordPress virtual filesystem.`,
371
- annotations: {
372
- readOnlyHint: true,
373
- destructiveHint: false,
374
- openWorldHint: true,
375
- },
376
- params: [
377
- {
378
- name: 'path',
379
- type: 'string',
380
- description: 'Absolute path to check',
381
- required: true,
382
- },
383
- ],
384
- },
385
- };
386
-
387
- // -- Site management tool definitions --
388
-
389
- export function getSiteToolDefinitions(
390
- port: number
391
- ): Record<string, ToolDefinition> {
392
- const url = playgroundUrl(port);
393
- return {
394
- playground_list_sites: {
395
- title: 'List Available Sites',
396
- errorPrefix: 'Error listing sites',
397
- description: `List all WordPress Playground sites
398
- available. Call this before any other playground
399
- tool — it returns the siteId required by every
400
- other operation.
401
-
402
- If this returns no sites, the user may need to
403
- open Playground at ${url} .
404
-
405
- Returns site names and storage type. "temporary"
406
- sites are lost on page reload, "opfs" sites persist
407
- across reloads. Call playground_save_site to persist
408
- a temporary site.`,
409
- annotations: {
410
- readOnlyHint: true,
411
- destructiveHint: false,
412
- },
413
- params: [],
414
- },
415
- playground_open_site: {
416
- title: 'Open Site in Browser',
417
- errorPrefix: 'Error opening site',
418
- description: `Open a WordPress Playground site in a new
419
- browser tab. The site must appear in
420
- playground_list_sites.
421
-
422
- Check playground_get_current_url first — if the
423
- site is already open in a tab, calling this tool
424
- will open a second tab rather than switching to
425
- the existing one.`,
426
- annotations: {
427
- readOnlyHint: false,
428
- destructiveHint: false,
429
- },
430
- params: [],
431
- },
432
- playground_rename_site: {
433
- title: 'Rename Site',
434
- errorPrefix: 'Error renaming site',
435
- description: `Rename a WordPress Playground site. Updates
436
- the display name shown in the browser UI.`,
437
- annotations: {
438
- readOnlyHint: false,
439
- destructiveHint: false,
440
- },
441
- params: [
442
- {
443
- name: 'newName',
444
- type: 'string',
445
- description: 'The new display name for the site',
446
- required: true,
447
- },
448
- ],
449
- },
450
- playground_save_site: {
451
- title: 'Save Site',
452
- errorPrefix: 'Error saving site',
453
- description: `Save a temporary WordPress Playground site
454
- to browser storage so it survives page reloads.
455
- Safe to call even if the site is already saved
456
- (no-op).
457
-
458
- Sites start as temporary by default and are lost
459
- when the browser tab is closed or the page is
460
- reloaded. Call this early in any multi-step
461
- workflow where losing progress would be costly.`,
462
- annotations: {
463
- readOnlyHint: false,
464
- destructiveHint: false,
465
- },
466
- params: [],
467
- },
468
- };
469
- }
470
-
471
- export function stringifyError(error: unknown): string {
472
- if (error instanceof Error) {
473
- return error.message;
474
- }
475
- if (typeof error === 'string') {
476
- return error;
477
- }
478
- try {
479
- return JSON.stringify(error);
480
- } catch {
481
- return String(error);
482
- }
483
- }
484
-
485
- /**
486
- * Translate internal Playground storage types to user-facing names.
487
- */
488
- export function presentStorage(raw: string): string {
489
- return raw === 'none' ? 'temporary' : raw;
490
- }
491
-
492
- /**
493
- * Convert ToolParam[] to a plain JSON Schema object.
494
- * Used by WebMCP which expects raw JSON Schema (not Zod).
495
- */
496
- export function paramsToJsonSchema(
497
- params: ToolParam[]
498
- ): Record<string, unknown> {
499
- const properties: Record<string, Record<string, unknown>> = {};
500
- const required: string[] = [];
501
-
502
- for (const param of params) {
503
- const prop: Record<string, unknown> = {
504
- type: param.type,
505
- description: param.description,
506
- };
507
- if (param.additionalProperties !== undefined) {
508
- prop['additionalProperties'] = param.additionalProperties;
509
- }
510
- if (param.default !== undefined) {
511
- prop['default'] = param.default;
512
- }
513
- properties[param.name] = prop;
514
- if (param.required) {
515
- required.push(param.name);
516
- }
517
- }
518
-
519
- const schema: Record<string, unknown> = {
520
- type: 'object',
521
- properties,
522
- };
523
- if (required.length > 0) {
524
- schema['required'] = required;
525
- }
526
- return schema;
527
- }