@respira/wordpress-mcp-server 1.4.0

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/dist/server.js ADDED
@@ -0,0 +1,729 @@
1
+ /**
2
+ * Respira WordPress MCP Server
3
+ *
4
+ * Provides tools for AI coding assistants to interact with WordPress sites
5
+ */
6
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
9
+ import { WordPressClient } from './wordpress-client.js';
10
+ export class RespiraWordPressServer {
11
+ server;
12
+ currentSite = null;
13
+ sites = new Map();
14
+ constructor(siteConfigs) {
15
+ this.server = new Server({
16
+ name: 'respira-wordpress',
17
+ version: '1.0.0',
18
+ }, {
19
+ capabilities: {
20
+ tools: {},
21
+ },
22
+ });
23
+ // Initialize WordPress clients for each site
24
+ siteConfigs.forEach((config) => {
25
+ const client = new WordPressClient(config);
26
+ this.sites.set(config.id, client);
27
+ // Set default site
28
+ if (config.default || this.currentSite === null) {
29
+ this.currentSite = client;
30
+ }
31
+ });
32
+ this.setupHandlers();
33
+ }
34
+ setupHandlers() {
35
+ // List available tools
36
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
37
+ return {
38
+ tools: this.getTools(),
39
+ };
40
+ });
41
+ // Handle tool calls
42
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
43
+ const { name, arguments: args } = request.params;
44
+ if (!this.currentSite) {
45
+ throw new Error('No WordPress site configured');
46
+ }
47
+ try {
48
+ const result = await this.handleToolCall(name, args || {});
49
+ return {
50
+ content: [
51
+ {
52
+ type: 'text',
53
+ text: JSON.stringify(result, null, 2),
54
+ },
55
+ ],
56
+ };
57
+ }
58
+ catch (error) {
59
+ const errorMessage = error?.message || error?.toString() || String(error) || 'Unknown error';
60
+ const errorStack = error?.stack ? `\n\nStack trace:\n${error.stack}` : '';
61
+ return {
62
+ content: [
63
+ {
64
+ type: 'text',
65
+ text: `Error: ${errorMessage}${errorStack}`,
66
+ },
67
+ ],
68
+ isError: true,
69
+ };
70
+ }
71
+ });
72
+ }
73
+ getTools() {
74
+ return [
75
+ {
76
+ name: 'wordpress_get_site_context',
77
+ description: 'Get comprehensive information about the WordPress site including version, theme, plugins, custom post types, and page builder.',
78
+ inputSchema: {
79
+ type: 'object',
80
+ properties: {},
81
+ },
82
+ },
83
+ {
84
+ name: 'wordpress_get_theme_docs',
85
+ description: 'Get theme documentation and available template files.',
86
+ inputSchema: {
87
+ type: 'object',
88
+ properties: {},
89
+ },
90
+ },
91
+ {
92
+ name: 'wordpress_get_builder_info',
93
+ description: 'Get information about the active page builder including available modules/widgets.',
94
+ inputSchema: {
95
+ type: 'object',
96
+ properties: {},
97
+ },
98
+ },
99
+ {
100
+ name: 'wordpress_list_pages',
101
+ description: 'List all pages with optional filtering.',
102
+ inputSchema: {
103
+ type: 'object',
104
+ properties: {
105
+ status: {
106
+ type: 'string',
107
+ description: 'Filter by status (publish, draft, pending)',
108
+ },
109
+ search: {
110
+ type: 'string',
111
+ description: 'Search term',
112
+ },
113
+ page: {
114
+ type: 'number',
115
+ description: 'Page number for pagination',
116
+ },
117
+ perPage: {
118
+ type: 'number',
119
+ description: 'Number of items per page',
120
+ },
121
+ },
122
+ },
123
+ },
124
+ {
125
+ name: 'wordpress_read_page',
126
+ description: 'Get full content of a specific page including meta data and builder information.',
127
+ inputSchema: {
128
+ type: 'object',
129
+ properties: {
130
+ id: {
131
+ type: 'number',
132
+ description: 'Page ID',
133
+ },
134
+ },
135
+ required: ['id'],
136
+ },
137
+ },
138
+ {
139
+ name: 'wordpress_create_page_duplicate',
140
+ description: 'Create a duplicate of an existing page for safe editing. IMPORTANT: Always use this before making changes to an original page. After creating a duplicate, edit the duplicate using wordpress_update_page. The user must then approve the duplicate in WordPress admin (Respira → Page Approvals) to replace the original page.',
141
+ inputSchema: {
142
+ type: 'object',
143
+ properties: {
144
+ originalId: {
145
+ type: 'number',
146
+ description: 'ID of the original page to duplicate',
147
+ },
148
+ suffix: {
149
+ type: 'string',
150
+ description: 'Optional suffix for the duplicated page title',
151
+ },
152
+ },
153
+ required: ['originalId'],
154
+ },
155
+ },
156
+ {
157
+ name: 'wordpress_update_page',
158
+ description: 'Update a page. ⚠️ SAFETY: Respira ALWAYS creates duplicates automatically before editing live pages. If you try to edit an original page, Respira will automatically create a duplicate and edit that instead. After editing, the user must approve the duplicate in WordPress admin (Respira → Approve Edits) to replace the original. The force parameter requires 3 separate confirmations and only works if "Allow Direct Editing" is enabled in Respira settings (disabled by default for safety).',
159
+ inputSchema: {
160
+ type: 'object',
161
+ properties: {
162
+ id: {
163
+ type: 'number',
164
+ description: 'Page ID (Respira automatically creates a duplicate if editing an original page)',
165
+ },
166
+ title: {
167
+ type: 'string',
168
+ description: 'New page title',
169
+ },
170
+ content: {
171
+ type: 'string',
172
+ description: 'New page content (HTML)',
173
+ },
174
+ status: {
175
+ type: 'string',
176
+ description: 'Page status (publish, draft, pending)',
177
+ },
178
+ meta: {
179
+ type: 'object',
180
+ description: 'Post meta data',
181
+ },
182
+ force: {
183
+ type: 'boolean',
184
+ description: '⚠️ DANGEROUS: Force direct edit of live page (requires 3 confirmations and "Allow Direct Editing" enabled). NOT RECOMMENDED - duplicates are created automatically for safety.',
185
+ },
186
+ },
187
+ required: ['id'],
188
+ },
189
+ },
190
+ {
191
+ name: 'wordpress_delete_page',
192
+ description: 'Delete a page. IMPORTANT: By default, this only works on Respira-created duplicates. The force parameter only works if "Allow Direct Editing" is enabled in Respira settings (disabled by default for safety).',
193
+ inputSchema: {
194
+ type: 'object',
195
+ properties: {
196
+ id: {
197
+ type: 'number',
198
+ description: 'Page ID',
199
+ },
200
+ force: {
201
+ type: 'boolean',
202
+ description: 'Force delete even if not a duplicate',
203
+ },
204
+ },
205
+ required: ['id'],
206
+ },
207
+ },
208
+ {
209
+ name: 'wordpress_list_posts',
210
+ description: 'List all blog posts with optional filtering.',
211
+ inputSchema: {
212
+ type: 'object',
213
+ properties: {
214
+ status: {
215
+ type: 'string',
216
+ description: 'Filter by status',
217
+ },
218
+ search: {
219
+ type: 'string',
220
+ description: 'Search term',
221
+ },
222
+ page: {
223
+ type: 'number',
224
+ description: 'Page number',
225
+ },
226
+ perPage: {
227
+ type: 'number',
228
+ description: 'Items per page',
229
+ },
230
+ },
231
+ },
232
+ },
233
+ {
234
+ name: 'wordpress_read_post',
235
+ description: 'Get full content of a specific post.',
236
+ inputSchema: {
237
+ type: 'object',
238
+ properties: {
239
+ id: {
240
+ type: 'number',
241
+ description: 'Post ID',
242
+ },
243
+ },
244
+ required: ['id'],
245
+ },
246
+ },
247
+ {
248
+ name: 'wordpress_create_post_duplicate',
249
+ description: 'Create a duplicate of an existing post for safe editing. IMPORTANT: Always use this before making changes to an original post. After creating a duplicate, edit the duplicate using wordpress_update_post. The user must then approve the duplicate in WordPress admin (Respira → Page Approvals) to replace the original post.',
250
+ inputSchema: {
251
+ type: 'object',
252
+ properties: {
253
+ originalId: {
254
+ type: 'number',
255
+ description: 'Original post ID',
256
+ },
257
+ suffix: {
258
+ type: 'string',
259
+ description: 'Optional suffix',
260
+ },
261
+ },
262
+ required: ['originalId'],
263
+ },
264
+ },
265
+ {
266
+ name: 'wordpress_update_post',
267
+ description: 'Update a post. ⚠️ SAFETY: Respira ALWAYS creates duplicates automatically before editing live posts. If you try to edit an original post, Respira will automatically create a duplicate and edit that instead. After editing, the user must approve the duplicate in WordPress admin (Respira → Approve Edits) to replace the original. The force parameter requires 3 separate confirmations and only works if "Allow Direct Editing" is enabled in Respira settings (disabled by default for safety).',
268
+ inputSchema: {
269
+ type: 'object',
270
+ properties: {
271
+ id: {
272
+ type: 'number',
273
+ description: 'Post ID (Respira automatically creates a duplicate if editing an original post)',
274
+ },
275
+ title: {
276
+ type: 'string',
277
+ description: 'New post title',
278
+ },
279
+ content: {
280
+ type: 'string',
281
+ description: 'New post content (HTML)',
282
+ },
283
+ status: {
284
+ type: 'string',
285
+ description: 'Post status (publish, draft, pending)',
286
+ },
287
+ meta: {
288
+ type: 'object',
289
+ description: 'Post meta data',
290
+ },
291
+ force: {
292
+ type: 'boolean',
293
+ description: '⚠️ DANGEROUS: Force direct edit of live post (requires 3 confirmations and "Allow Direct Editing" enabled). NOT RECOMMENDED - duplicates are created automatically for safety.',
294
+ },
295
+ },
296
+ required: ['id'],
297
+ },
298
+ },
299
+ {
300
+ name: 'wordpress_delete_post',
301
+ description: 'Delete a post. IMPORTANT: By default, this only works on Respira-created duplicates. The force parameter only works if "Allow Direct Editing" is enabled in Respira settings (disabled by default for safety).',
302
+ inputSchema: {
303
+ type: 'object',
304
+ properties: {
305
+ id: {
306
+ type: 'number',
307
+ description: 'Post ID',
308
+ },
309
+ force: {
310
+ type: 'boolean',
311
+ description: 'Force delete even if not a duplicate',
312
+ },
313
+ },
314
+ required: ['id'],
315
+ },
316
+ },
317
+ {
318
+ name: 'wordpress_list_media',
319
+ description: 'List all media files (images, videos, etc.).',
320
+ inputSchema: {
321
+ type: 'object',
322
+ properties: {
323
+ search: {
324
+ type: 'string',
325
+ description: 'Search term',
326
+ },
327
+ page: {
328
+ type: 'number',
329
+ description: 'Page number',
330
+ },
331
+ perPage: {
332
+ type: 'number',
333
+ description: 'Items per page',
334
+ },
335
+ },
336
+ },
337
+ },
338
+ {
339
+ name: 'wordpress_extract_builder_content',
340
+ description: 'Extract structured content from a page builder (Divi, Elementor, etc.).',
341
+ inputSchema: {
342
+ type: 'object',
343
+ properties: {
344
+ builder: {
345
+ type: 'string',
346
+ description: 'Builder name (gutenberg, divi, elementor, etc.)',
347
+ },
348
+ pageId: {
349
+ type: 'number',
350
+ description: 'Page ID',
351
+ },
352
+ },
353
+ required: ['builder', 'pageId'],
354
+ },
355
+ },
356
+ {
357
+ name: 'wordpress_inject_builder_content',
358
+ description: 'Inject structured content into a page builder.',
359
+ inputSchema: {
360
+ type: 'object',
361
+ properties: {
362
+ builder: {
363
+ type: 'string',
364
+ description: 'Builder name',
365
+ },
366
+ pageId: {
367
+ type: 'number',
368
+ description: 'Page ID',
369
+ },
370
+ content: {
371
+ type: 'object',
372
+ description: 'Structured content to inject',
373
+ },
374
+ },
375
+ required: ['builder', 'pageId', 'content'],
376
+ },
377
+ },
378
+ {
379
+ name: 'wordpress_validate_security',
380
+ description: 'Validate content for security issues before saving.',
381
+ inputSchema: {
382
+ type: 'object',
383
+ properties: {
384
+ content: {
385
+ type: 'string',
386
+ description: 'Content to validate',
387
+ },
388
+ },
389
+ required: ['content'],
390
+ },
391
+ },
392
+ {
393
+ name: 'wordpress_switch_site',
394
+ description: 'Switch to a different WordPress site (for multi-site setups).',
395
+ inputSchema: {
396
+ type: 'object',
397
+ properties: {
398
+ siteId: {
399
+ type: 'string',
400
+ description: 'Site ID from configuration',
401
+ },
402
+ },
403
+ required: ['siteId'],
404
+ },
405
+ },
406
+ // Page Speed Analysis tools
407
+ {
408
+ name: 'wordpress_analyze_performance',
409
+ description: 'Analyze page performance metrics including load time, image optimization, CSS/JS optimization, caching, and plugin performance impact.',
410
+ inputSchema: {
411
+ type: 'object',
412
+ properties: {
413
+ pageId: {
414
+ type: 'number',
415
+ description: 'Page ID to analyze',
416
+ },
417
+ },
418
+ required: ['pageId'],
419
+ },
420
+ },
421
+ {
422
+ name: 'wordpress_get_core_web_vitals',
423
+ description: 'Get Core Web Vitals metrics (LCP, FID, CLS) for a page.',
424
+ inputSchema: {
425
+ type: 'object',
426
+ properties: {
427
+ pageId: {
428
+ type: 'number',
429
+ description: 'Page ID to analyze',
430
+ },
431
+ },
432
+ required: ['pageId'],
433
+ },
434
+ },
435
+ {
436
+ name: 'wordpress_analyze_images',
437
+ description: 'Analyze image optimization opportunities including missing alt text, large files, and unoptimized formats.',
438
+ inputSchema: {
439
+ type: 'object',
440
+ properties: {
441
+ pageId: {
442
+ type: 'number',
443
+ description: 'Page ID to analyze',
444
+ },
445
+ },
446
+ required: ['pageId'],
447
+ },
448
+ },
449
+ // SEO Analysis tools
450
+ {
451
+ name: 'wordpress_analyze_seo',
452
+ description: 'Comprehensive SEO analysis including meta tags, heading structure, image alt text, internal linking, content quality, and schema markup.',
453
+ inputSchema: {
454
+ type: 'object',
455
+ properties: {
456
+ pageId: {
457
+ type: 'number',
458
+ description: 'Page ID to analyze',
459
+ },
460
+ },
461
+ required: ['pageId'],
462
+ },
463
+ },
464
+ {
465
+ name: 'wordpress_check_seo_issues',
466
+ description: 'Check for common SEO issues and get quick recommendations.',
467
+ inputSchema: {
468
+ type: 'object',
469
+ properties: {
470
+ pageId: {
471
+ type: 'number',
472
+ description: 'Page ID to analyze',
473
+ },
474
+ },
475
+ required: ['pageId'],
476
+ },
477
+ },
478
+ {
479
+ name: 'wordpress_analyze_readability',
480
+ description: 'Analyze content readability including Flesch Reading Ease score, sentence length, and paragraph structure.',
481
+ inputSchema: {
482
+ type: 'object',
483
+ properties: {
484
+ pageId: {
485
+ type: 'number',
486
+ description: 'Page ID to analyze',
487
+ },
488
+ },
489
+ required: ['pageId'],
490
+ },
491
+ },
492
+ // AEO (AI Engine Optimization) tools
493
+ {
494
+ name: 'wordpress_analyze_aeo',
495
+ description: 'Analyze content for AI Engine Optimization (optimizing for AI search engines like Perplexity, ChatGPT). Checks structured data, content clarity, semantic HTML, entities, content depth, and FAQ opportunities.',
496
+ inputSchema: {
497
+ type: 'object',
498
+ properties: {
499
+ pageId: {
500
+ type: 'number',
501
+ description: 'Page ID to analyze',
502
+ },
503
+ },
504
+ required: ['pageId'],
505
+ },
506
+ },
507
+ {
508
+ name: 'wordpress_check_structured_data',
509
+ description: 'Check schema markup and structured data (JSON-LD, microdata) for AI parsing.',
510
+ inputSchema: {
511
+ type: 'object',
512
+ properties: {
513
+ pageId: {
514
+ type: 'number',
515
+ description: 'Page ID to analyze',
516
+ },
517
+ },
518
+ required: ['pageId'],
519
+ },
520
+ },
521
+ // Plugin Management tools (EXPERIMENTAL)
522
+ {
523
+ name: 'wordpress_list_plugins',
524
+ description: 'EXPERIMENTAL: List all installed plugins with their status, version, and update availability. Requires plugin management to be enabled in Respira settings.',
525
+ inputSchema: {
526
+ type: 'object',
527
+ properties: {},
528
+ },
529
+ },
530
+ {
531
+ name: 'wordpress_install_plugin',
532
+ description: 'EXPERIMENTAL: Install a plugin from WordPress.org or a ZIP URL. Requires plugin management to be enabled in Respira settings. Use with caution.',
533
+ inputSchema: {
534
+ type: 'object',
535
+ properties: {
536
+ slugOrUrl: {
537
+ type: 'string',
538
+ description: 'Plugin slug (from WordPress.org) or ZIP file URL',
539
+ },
540
+ source: {
541
+ type: 'string',
542
+ description: 'Source type: "wordpress.org" or "url"',
543
+ enum: ['wordpress.org', 'url'],
544
+ },
545
+ },
546
+ required: ['slugOrUrl'],
547
+ },
548
+ },
549
+ {
550
+ name: 'wordpress_activate_plugin',
551
+ description: 'EXPERIMENTAL: Activate a plugin. Requires plugin management to be enabled in Respira settings. Monitor the site after activation.',
552
+ inputSchema: {
553
+ type: 'object',
554
+ properties: {
555
+ slug: {
556
+ type: 'string',
557
+ description: 'Plugin slug',
558
+ },
559
+ },
560
+ required: ['slug'],
561
+ },
562
+ },
563
+ {
564
+ name: 'wordpress_deactivate_plugin',
565
+ description: 'EXPERIMENTAL: Deactivate a plugin. Requires plugin management to be enabled in Respira settings.',
566
+ inputSchema: {
567
+ type: 'object',
568
+ properties: {
569
+ slug: {
570
+ type: 'string',
571
+ description: 'Plugin slug',
572
+ },
573
+ },
574
+ required: ['slug'],
575
+ },
576
+ },
577
+ {
578
+ name: 'wordpress_update_plugin',
579
+ description: 'EXPERIMENTAL: Update a plugin to the latest version. Requires plugin management to be enabled in Respira settings. Make sure you have backups before updating.',
580
+ inputSchema: {
581
+ type: 'object',
582
+ properties: {
583
+ slug: {
584
+ type: 'string',
585
+ description: 'Plugin slug',
586
+ },
587
+ },
588
+ required: ['slug'],
589
+ },
590
+ },
591
+ {
592
+ name: 'wordpress_delete_plugin',
593
+ description: 'EXPERIMENTAL: Permanently delete a plugin. Requires plugin management to be enabled in Respira settings. The plugin must be deactivated first.',
594
+ inputSchema: {
595
+ type: 'object',
596
+ properties: {
597
+ slug: {
598
+ type: 'string',
599
+ description: 'Plugin slug',
600
+ },
601
+ },
602
+ required: ['slug'],
603
+ },
604
+ },
605
+ ];
606
+ }
607
+ async handleToolCall(name, args) {
608
+ if (!this.currentSite) {
609
+ throw new Error('No WordPress site configured');
610
+ }
611
+ switch (name) {
612
+ case 'wordpress_get_site_context':
613
+ return this.currentSite.getSiteContext();
614
+ case 'wordpress_get_theme_docs':
615
+ return this.currentSite.getThemeDocs();
616
+ case 'wordpress_get_builder_info':
617
+ return this.currentSite.getBuilderInfo();
618
+ case 'wordpress_list_pages':
619
+ return this.currentSite.listPages(args);
620
+ case 'wordpress_read_page':
621
+ return this.currentSite.getPage(args.id);
622
+ case 'wordpress_create_page_duplicate':
623
+ return this.currentSite.duplicatePage(args.originalId, args.suffix);
624
+ case 'wordpress_update_page': {
625
+ const page = await this.currentSite.updatePage(args.id, args);
626
+ // Check if Respira created a duplicate
627
+ if (page.__respira_duplicate_info) {
628
+ const info = page.__respira_duplicate_info;
629
+ return {
630
+ ...page,
631
+ respira_message: info.message,
632
+ respira_instructions: info.instructions,
633
+ duplicate_created: true,
634
+ duplicate_id: info.duplicate_id,
635
+ original_id: info.original_id,
636
+ approval_url: info.approval_url,
637
+ };
638
+ }
639
+ return page;
640
+ }
641
+ case 'wordpress_delete_page':
642
+ return this.currentSite.deletePage(args.id, args.force);
643
+ case 'wordpress_list_posts':
644
+ return this.currentSite.listPosts(args);
645
+ case 'wordpress_read_post':
646
+ return this.currentSite.getPost(args.id);
647
+ case 'wordpress_create_post_duplicate':
648
+ return this.currentSite.duplicatePost(args.originalId, args.suffix);
649
+ case 'wordpress_update_post': {
650
+ const post = await this.currentSite.updatePost(args.id, args);
651
+ // Check if Respira created a duplicate
652
+ if (post.__respira_duplicate_info) {
653
+ const info = post.__respira_duplicate_info;
654
+ return {
655
+ ...post,
656
+ respira_message: info.message,
657
+ respira_instructions: info.instructions,
658
+ duplicate_created: true,
659
+ duplicate_id: info.duplicate_id,
660
+ original_id: info.original_id,
661
+ approval_url: info.approval_url,
662
+ };
663
+ }
664
+ return post;
665
+ }
666
+ case 'wordpress_delete_post':
667
+ return this.currentSite.deletePost(args.id, args.force);
668
+ case 'wordpress_list_media':
669
+ return this.currentSite.listMedia(args);
670
+ case 'wordpress_extract_builder_content':
671
+ return this.currentSite.extractBuilderContent(args.builder, args.pageId);
672
+ case 'wordpress_inject_builder_content':
673
+ return this.currentSite.injectBuilderContent(args.builder, args.pageId, args.content);
674
+ case 'wordpress_validate_security':
675
+ return this.currentSite.validateSecurity(args.content);
676
+ case 'wordpress_switch_site':
677
+ const newSite = this.sites.get(args.siteId);
678
+ if (!newSite) {
679
+ throw new Error(`Site with ID "${args.siteId}" not found in configuration`);
680
+ }
681
+ this.currentSite = newSite;
682
+ return {
683
+ success: true,
684
+ message: `Switched to site: ${newSite.getSiteName()}`,
685
+ url: newSite.getSiteUrl(),
686
+ };
687
+ // Page Speed Analysis
688
+ case 'wordpress_analyze_performance':
689
+ return this.currentSite.analyzePerformance(args.pageId);
690
+ case 'wordpress_get_core_web_vitals':
691
+ return this.currentSite.getCoreWebVitals(args.pageId);
692
+ case 'wordpress_analyze_images':
693
+ return this.currentSite.analyzeImages(args.pageId);
694
+ // SEO Analysis
695
+ case 'wordpress_analyze_seo':
696
+ return this.currentSite.analyzeSEO(args.pageId);
697
+ case 'wordpress_check_seo_issues':
698
+ return this.currentSite.checkSEOIssues(args.pageId);
699
+ case 'wordpress_analyze_readability':
700
+ return this.currentSite.analyzeReadability(args.pageId);
701
+ // AEO Analysis
702
+ case 'wordpress_analyze_aeo':
703
+ return this.currentSite.analyzeAEO(args.pageId);
704
+ case 'wordpress_check_structured_data':
705
+ return this.currentSite.checkStructuredData(args.pageId);
706
+ // Plugin Management (EXPERIMENTAL)
707
+ case 'wordpress_list_plugins':
708
+ return this.currentSite.listPlugins();
709
+ case 'wordpress_install_plugin':
710
+ return this.currentSite.installPlugin(args.slugOrUrl, args.source || 'wordpress.org');
711
+ case 'wordpress_activate_plugin':
712
+ return this.currentSite.activatePlugin(args.slug);
713
+ case 'wordpress_deactivate_plugin':
714
+ return this.currentSite.deactivatePlugin(args.slug);
715
+ case 'wordpress_update_plugin':
716
+ return this.currentSite.updatePlugin(args.slug);
717
+ case 'wordpress_delete_plugin':
718
+ return this.currentSite.deletePlugin(args.slug);
719
+ default:
720
+ throw new Error(`Unknown tool: ${name}`);
721
+ }
722
+ }
723
+ async run() {
724
+ const transport = new StdioServerTransport();
725
+ await this.server.connect(transport);
726
+ console.error('Respira WordPress MCP Server running on stdio');
727
+ }
728
+ }
729
+ //# sourceMappingURL=server.js.map