@adsim/wordpress-mcp-server 4.5.0 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -3
- package/dxt/manifest.json +1 -1
- package/index.js +263 -161
- package/package.json +1 -1
- package/tests/unit/tools/dynamicFiltering.test.js +136 -0
- package/tests/unit/tools/outputCompression.test.js +342 -0
- package/tests/unit/tools/site.test.js +3 -1
package/index.js
CHANGED
|
@@ -447,213 +447,225 @@ function strip(html) { return (html || '').replace(/<[^>]*>/g, '').trim(); }
|
|
|
447
447
|
|
|
448
448
|
const TOOLS_DEFINITIONS = [
|
|
449
449
|
// ── POSTS (6) ──
|
|
450
|
-
{ name: 'wp_list_posts', description: '
|
|
451
|
-
{ name: 'wp_get_post', description: '
|
|
452
|
-
{ name: 'wp_create_post', description: '
|
|
453
|
-
{ name: 'wp_update_post', description: '
|
|
454
|
-
{ name: 'wp_delete_post', description: '
|
|
455
|
-
{ name: 'wp_search', description: '
|
|
450
|
+
{ name: 'wp_list_posts', description: 'Use to browse/filter posts by status, category, tag, author, or search. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, status: { type: 'string', default: 'publish' }, orderby: { type: 'string', default: 'date' }, order: { type: 'string', default: 'desc' }, categories: { type: 'string' }, tags: { type: 'string' }, search: { type: 'string' }, author: { type: 'number' }, mode: { type: 'string', default: 'full', description: 'full=all fields, summary=id/title/slug/date/status/link only, ids_only=flat ID array' } }}},
|
|
451
|
+
{ name: 'wp_get_post', description: 'Use to read a single post with full content and meta. Read-only. Hint: use content_format=\'links_only\' for link audits (~800 chars vs 187k), \'text\' for rewrites, fields=[\'id\',\'title\',\'meta\'] for SEO-only.', inputSchema: { type: 'object', properties: { id: { type: 'number' }, fields: { type: 'array', items: { type: 'string' }, description: 'Return only specified fields (id,title,content,excerpt,slug,status,date,modified,categories,tags,author,featured_media,meta,link). Omit for all.' }, content_format: { type: 'string', default: 'html', description: 'html=raw HTML (truncated at WP_MAX_CONTENT_CHARS), text=plain text, links_only=extract internal links only' } }, required: ['id'] }},
|
|
452
|
+
{ name: 'wp_create_post', description: 'Use to create a post (defaults to draft). Accepts title, HTML content, categories, tags, featured_media, meta. Write — blocked by WP_READ_ONLY.', inputSchema: { type: 'object', properties: { title: { type: 'string' }, content: { type: 'string' }, status: { type: 'string', default: 'draft' }, excerpt: { type: 'string' }, categories: { type: 'array', items: { type: 'number' } }, tags: { type: 'array', items: { type: 'number' } }, slug: { type: 'string' }, featured_media: { type: 'number' }, meta: { type: 'object' }, author: { type: 'number' } }, required: ['title', 'content'] }},
|
|
453
|
+
{ name: 'wp_update_post', description: 'Use to modify any post field — only provided fields change. Write — blocked by WP_READ_ONLY.', inputSchema: { type: 'object', properties: { id: { type: 'number' }, title: { type: 'string' }, content: { type: 'string' }, status: { type: 'string' }, excerpt: { type: 'string' }, categories: { type: 'array', items: { type: 'number' } }, tags: { type: 'array', items: { type: 'number' } }, slug: { type: 'string' }, featured_media: { type: 'number' }, meta: { type: 'object' }, author: { type: 'number' } }, required: ['id'] }},
|
|
454
|
+
{ name: 'wp_delete_post', description: 'Use to trash or permanently delete a post. Write — blocked by WP_READ_ONLY, WP_DISABLE_DELETE.', inputSchema: { type: 'object', properties: { id: { type: 'number' }, force: { type: 'boolean', default: false }, confirmation_token: { type: 'string', description: 'Confirmation token returned by the first call when WP_CONFIRM_DESTRUCTIVE=true' } }, required: ['id'] }},
|
|
455
|
+
{ name: 'wp_search', description: 'Use for full-text search across all content types. Returns id, title, url, type. Read-only.', inputSchema: { type: 'object', properties: { search: { type: 'string' }, per_page: { type: 'number', default: 10 }, type: { type: 'string', default: '' } }, required: ['search'] }},
|
|
456
456
|
|
|
457
457
|
// ── APPROVAL WORKFLOW (3) ──
|
|
458
|
-
{ name: 'wp_submit_for_review', description: '
|
|
459
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
460
|
-
{ name: 'wp_approve_post', description: '
|
|
461
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
462
|
-
{ name: 'wp_reject_post', description: '
|
|
463
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
458
|
+
{ name: 'wp_submit_for_review', description: 'Use to transition a draft post to pending status for editorial review. Write — blocked by WP_READ_ONLY.',
|
|
459
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, note: { type: 'string', description: 'Optional review note (stored as post meta _mcp_review_note)' } }, required: ['id'] }},
|
|
460
|
+
{ name: 'wp_approve_post', description: 'Use to transition a pending post to published (editor/admin action). Write — blocked by WP_READ_ONLY, WP_DRAFT_ONLY.',
|
|
461
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' } }, required: ['id'] }},
|
|
462
|
+
{ name: 'wp_reject_post', description: 'Use to return a pending post to draft with a mandatory rejection reason. Write — blocked by WP_READ_ONLY.',
|
|
463
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, reason: { type: 'string', description: 'Reason for rejection (stored as post meta _mcp_rejection_reason)' } }, required: ['id', 'reason'] }},
|
|
464
464
|
|
|
465
465
|
// ── PAGES (4) ──
|
|
466
|
-
{ name: 'wp_list_pages', description: '
|
|
467
|
-
{ name: 'wp_get_page', description: '
|
|
468
|
-
{ name: 'wp_create_page', description: '
|
|
469
|
-
{ name: 'wp_update_page', description: '
|
|
466
|
+
{ name: 'wp_list_pages', description: 'Use to browse pages with hierarchy and templates. Supports parent filter, menu_order sort. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, status: { type: 'string', default: 'publish' }, parent: { type: 'number' }, orderby: { type: 'string', default: 'menu_order' }, order: { type: 'string', default: 'asc' }, search: { type: 'string' }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
467
|
+
{ name: 'wp_get_page', description: 'Use to read a single page with content and template. Read-only. Warning: Elementor pages can exceed 100k chars.', inputSchema: { type: 'object', properties: { id: { type: 'number' } }, required: ['id'] }},
|
|
468
|
+
{ name: 'wp_create_page', description: 'Use to create a page (defaults to draft). Supports parent, template, menu_order. Write — blocked by WP_READ_ONLY.', inputSchema: { type: 'object', properties: { title: { type: 'string' }, content: { type: 'string' }, status: { type: 'string', default: 'draft' }, parent: { type: 'number', default: 0 }, template: { type: 'string' }, menu_order: { type: 'number', default: 0 }, excerpt: { type: 'string' }, slug: { type: 'string' }, featured_media: { type: 'number' }, meta: { type: 'object' } }, required: ['title', 'content'] }},
|
|
469
|
+
{ name: 'wp_update_page', description: 'Use to modify any page field. Write — blocked by WP_READ_ONLY.', inputSchema: { type: 'object', properties: { id: { type: 'number' }, title: { type: 'string' }, content: { type: 'string' }, status: { type: 'string' }, parent: { type: 'number' }, template: { type: 'string' }, menu_order: { type: 'number' }, excerpt: { type: 'string' }, slug: { type: 'string' }, featured_media: { type: 'number' }, meta: { type: 'object' } }, required: ['id'] }},
|
|
470
470
|
|
|
471
471
|
// ── MEDIA (3) ──
|
|
472
|
-
{ name: 'wp_list_media', description: '
|
|
473
|
-
{ name: 'wp_get_media', description: '
|
|
474
|
-
{ name: 'wp_upload_media', description: '
|
|
472
|
+
{ name: 'wp_list_media', description: 'Use to browse media library by type (image/video/audio). Returns id, URL, alt_text, dimensions. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, media_type: { type: 'string' }, search: { type: 'string' }, orderby: { type: 'string', default: 'date' }, order: { type: 'string', default: 'desc' }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
473
|
+
{ name: 'wp_get_media', description: 'Use to get full media details with all available sizes. Read-only.', inputSchema: { type: 'object', properties: { id: { type: 'number' } }, required: ['id'] }},
|
|
474
|
+
{ name: 'wp_upload_media', description: 'Use to upload a file from a public URL to the media library. Set alt_text for image SEO. Write — blocked by WP_READ_ONLY.', inputSchema: { type: 'object', properties: { url: { type: 'string' }, filename: { type: 'string' }, title: { type: 'string' }, alt_text: { type: 'string' }, caption: { type: 'string' }, description: { type: 'string' }, post_id: { type: 'number' } }, required: ['url'] }},
|
|
475
475
|
|
|
476
476
|
// ── TAXONOMIES (3) ──
|
|
477
|
-
{ name: 'wp_list_categories', description: '
|
|
478
|
-
{ name: 'wp_list_tags', description: '
|
|
479
|
-
{ name: 'wp_create_taxonomy_term', description: '
|
|
477
|
+
{ name: 'wp_list_categories', description: 'Use to list categories with hierarchy, post count, and descriptions. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 100 }, page: { type: 'number', default: 1 }, parent: { type: 'number' }, search: { type: 'string' }, orderby: { type: 'string', default: 'name' }, hide_empty: { type: 'boolean', default: false }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
478
|
+
{ name: 'wp_list_tags', description: 'Use to list tags with post count. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 100 }, page: { type: 'number', default: 1 }, search: { type: 'string' }, orderby: { type: 'string', default: 'name' }, hide_empty: { type: 'boolean', default: false }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
479
|
+
{ name: 'wp_create_taxonomy_term', description: 'Use to create a new category or tag. Write — blocked by WP_READ_ONLY.', inputSchema: { type: 'object', properties: { taxonomy: { type: 'string' }, name: { type: 'string' }, slug: { type: 'string' }, description: { type: 'string' }, parent: { type: 'number' } }, required: ['taxonomy', 'name'] }},
|
|
480
480
|
|
|
481
481
|
// ── COMMENTS (2) ──
|
|
482
|
-
{ name: 'wp_list_comments', description: '
|
|
483
|
-
{ name: 'wp_create_comment', description: '
|
|
482
|
+
{ name: 'wp_list_comments', description: 'Use to list comments filtered by post or status (approved/hold/spam). Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, post: { type: 'number' }, status: { type: 'string' }, orderby: { type: 'string', default: 'date_gmt' }, order: { type: 'string', default: 'desc' }, search: { type: 'string' }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
483
|
+
{ name: 'wp_create_comment', description: 'Use to post a comment or reply on any post. Write — blocked by WP_READ_ONLY.', inputSchema: { type: 'object', properties: { post: { type: 'number' }, content: { type: 'string' }, parent: { type: 'number', default: 0 }, author_name: { type: 'string' }, author_email: { type: 'string' }, status: { type: 'string', default: 'approved' } }, required: ['post', 'content'] }},
|
|
484
484
|
|
|
485
485
|
// ── CUSTOM POST TYPES (2) ──
|
|
486
|
-
{ name: 'wp_list_post_types', description: '
|
|
487
|
-
{ name: 'wp_list_custom_posts', description: '
|
|
486
|
+
{ name: 'wp_list_post_types', description: 'Use to discover all registered post types including custom ones (products, portfolio, events). Read-only.', inputSchema: { type: 'object', properties: {} }},
|
|
487
|
+
{ name: 'wp_list_custom_posts', description: 'Use to list any custom post type (products, portfolio, events). Requires post_type slug. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { post_type: { type: 'string' }, per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, status: { type: 'string', default: 'publish' }, orderby: { type: 'string', default: 'date' }, order: { type: 'string', default: 'desc' }, search: { type: 'string' }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }, required: ['post_type'] }},
|
|
488
488
|
|
|
489
489
|
// ── USERS (1) ──
|
|
490
|
-
{ name: 'wp_list_users', description: '
|
|
490
|
+
{ name: 'wp_list_users', description: 'Use to list site users with roles. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.', inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, roles: { type: 'string' }, search: { type: 'string' }, orderby: { type: 'string', default: 'name' }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
491
491
|
|
|
492
492
|
// ── MULTI-TARGET (1) ──
|
|
493
|
-
{ name: 'wp_set_target', description: '
|
|
493
|
+
{ name: 'wp_set_target', description: 'Use to switch active WordPress site in multi-target mode. Write.',
|
|
494
494
|
inputSchema: { type: 'object', properties: { site: { type: 'string', description: 'Site key from targets config' } }, required: ['site'] }},
|
|
495
495
|
|
|
496
496
|
// ── SITE INFO (1) ──
|
|
497
|
-
{ name: 'wp_site_info', description: '
|
|
497
|
+
{ name: 'wp_site_info', description: 'Use first to discover site config, user, post types, governance flags, and available targets. Read-only.',
|
|
498
498
|
inputSchema: { type: 'object', properties: {} }},
|
|
499
499
|
|
|
500
500
|
// ── SEO METADATA (3) ──
|
|
501
|
-
{ name: 'wp_get_seo_meta', description: '
|
|
502
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
503
|
-
{ name: 'wp_update_seo_meta', description: '
|
|
504
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
505
|
-
{ name: 'wp_audit_seo', description: '
|
|
501
|
+
{ name: 'wp_get_seo_meta', description: 'Use to read SEO title, description, focus keyword, canonical, robots, OG for one post. Auto-detects Yoast/RankMath/SEOPress/AIOSEO. Read-only. Hint: prefer this over wp_get_post for SEO-only workflows.',
|
|
502
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, post_type: { type: 'string', default: 'post', description: 'post or page' } }, required: ['id'] }},
|
|
503
|
+
{ name: 'wp_update_seo_meta', description: 'Use to update SEO title, description, focus keyword, canonical, or robots. Auto-detects SEO plugin. Write — blocked by WP_READ_ONLY.',
|
|
504
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, post_type: { type: 'string', default: 'post', description: 'post or page' }, title: { type: 'string', description: 'SEO title' }, description: { type: 'string', description: 'Meta description' }, focus_keyword: { type: 'string', description: 'Focus keyword' }, canonical_url: { type: 'string' }, robots_noindex: { type: 'boolean' }, robots_nofollow: { type: 'boolean' } }, required: ['id'] }},
|
|
505
|
+
{ name: 'wp_audit_seo', description: 'Use for quick bulk SEO scoring (0-100) across posts or pages. Checks missing titles, descriptions, keywords, and length issues. Read-only. Hint: use wp_audit_rendered_seo for rendered-vs-stored comparison.',
|
|
506
506
|
inputSchema: { type: 'object', properties: { post_type: { type: 'string', default: 'post', description: 'post or page' }, per_page: { type: 'number', default: 20, description: 'Number of posts to audit (max 100)' }, status: { type: 'string', default: 'publish' }, orderby: { type: 'string', default: 'date' }, order: { type: 'string', default: 'desc' } }}},
|
|
507
507
|
|
|
508
508
|
// ── PLUGINS (3) ──
|
|
509
|
-
{ name: 'wp_list_plugins', description: '
|
|
510
|
-
inputSchema: { type: 'object', properties: { search: { type: 'string'
|
|
511
|
-
{ name: 'wp_activate_plugin', description: '
|
|
509
|
+
{ name: 'wp_list_plugins', description: 'Use to list installed plugins with status and version. Requires Administrator role. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.',
|
|
510
|
+
inputSchema: { type: 'object', properties: { search: { type: 'string' }, status: { type: 'string', enum: ['active', 'inactive', 'all'], default: 'all' }, per_page: { type: 'number', default: 20 }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
511
|
+
{ name: 'wp_activate_plugin', description: 'Use to activate a plugin. Write — blocked by WP_READ_ONLY, WP_DISABLE_PLUGIN_MANAGEMENT.',
|
|
512
512
|
inputSchema: { type: 'object', properties: { plugin: { type: 'string', description: 'Plugin slug/file (e.g. "akismet/akismet.php"). Use wp_list_plugins to find the correct value.' } }, required: ['plugin'] }},
|
|
513
|
-
{ name: 'wp_deactivate_plugin', description: '
|
|
513
|
+
{ name: 'wp_deactivate_plugin', description: 'Use to deactivate a plugin. Write — blocked by WP_READ_ONLY, WP_DISABLE_PLUGIN_MANAGEMENT.',
|
|
514
514
|
inputSchema: { type: 'object', properties: { plugin: { type: 'string', description: 'Plugin slug/file (e.g. "akismet/akismet.php"). Use wp_list_plugins to find the correct value.' } }, required: ['plugin'] }},
|
|
515
515
|
|
|
516
516
|
// ── THEMES (2) ──
|
|
517
|
-
{ name: 'wp_list_themes', description: '
|
|
518
|
-
inputSchema: { type: 'object', properties: { status: { type: 'string', enum: ['active', 'inactive', 'all'], default: 'all',
|
|
519
|
-
{ name: 'wp_get_theme', description: '
|
|
517
|
+
{ name: 'wp_list_themes', description: 'Use to list installed themes with active theme detection. Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.',
|
|
518
|
+
inputSchema: { type: 'object', properties: { status: { type: 'string', enum: ['active', 'inactive', 'all'], default: 'all' }, per_page: { type: 'number', default: 20 }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }}},
|
|
519
|
+
{ name: 'wp_get_theme', description: 'Use to get theme details by stylesheet slug. Read-only.',
|
|
520
520
|
inputSchema: { type: 'object', properties: { stylesheet: { type: 'string', description: 'Theme stylesheet slug (e.g. "twentytwentyfour"). Use wp_list_themes to find the correct value.' } }, required: ['stylesheet'] }},
|
|
521
521
|
|
|
522
522
|
// ── REVISIONS (4) ──
|
|
523
|
-
{ name: 'wp_list_revisions', description: '
|
|
524
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
525
|
-
{ name: 'wp_get_revision', description: '
|
|
526
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
527
|
-
{ name: 'wp_restore_revision', description: '
|
|
528
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
529
|
-
{ name: 'wp_delete_revision', description: '
|
|
530
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
523
|
+
{ name: 'wp_list_revisions', description: 'Use to list revisions of a post or page (metadata only). Read-only. Hint: use mode=\'summary\' for listings, \'ids_only\' for batch ops.',
|
|
524
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'], default: 'post' }, per_page: { type: 'number', default: 10 }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'], default: 'full', description: 'full=all fields, summary=key fields only, ids_only=flat ID array' } }, required: ['post_id'] }},
|
|
525
|
+
{ name: 'wp_get_revision', description: 'Use to read a specific revision with full content. Read-only.',
|
|
526
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, revision_id: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'], default: 'post' } }, required: ['post_id', 'revision_id'] }},
|
|
527
|
+
{ name: 'wp_restore_revision', description: 'Use to restore a post to a previous revision. Write — blocked by WP_READ_ONLY.',
|
|
528
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, revision_id: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'], default: 'post' } }, required: ['post_id', 'revision_id'] }},
|
|
529
|
+
{ name: 'wp_delete_revision', description: 'Use to permanently delete a revision. Write — blocked by WP_READ_ONLY, WP_DISABLE_DELETE, WP_CONFIRM_DESTRUCTIVE.',
|
|
530
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, revision_id: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'], default: 'post' }, confirmation_token: { type: 'string', description: 'Confirmation token returned by the first call when WP_CONFIRM_DESTRUCTIVE=true' } }, required: ['post_id', 'revision_id'] }},
|
|
531
531
|
|
|
532
532
|
// ── LINK ANALYSIS (2) ──
|
|
533
|
-
{ name: 'wp_analyze_links', description: '
|
|
534
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
535
|
-
{ name: 'wp_suggest_internal_links', description: '
|
|
536
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
533
|
+
{ name: 'wp_analyze_links', description: 'Use to audit all internal and external links in a single post via HEAD requests. Returns broken/warning/ok status per link. Read-only.',
|
|
534
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, check_broken: { type: 'boolean', default: true, description: 'Check broken internal links via HEAD request' }, timeout_ms: { type: 'number', default: 5000, description: 'Timeout per HEAD request in ms' } }, required: ['post_id'] }},
|
|
535
|
+
{ name: 'wp_suggest_internal_links', description: 'Use to get scored internal link suggestions for a post based on keyword relevance, categories, and freshness. Read-only. Hint: call wp_get_post with content_format=\'links_only\' first to map existing links.',
|
|
536
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, max_suggestions: { type: 'number', default: 5, description: 'Number of suggestions (1-10)' }, focus_keywords: { type: 'array', items: { type: 'string' }, description: 'Additional keywords to match against' }, exclude_already_linked: { type: 'boolean', default: true, description: 'Exclude posts already linked from the current post' } }, required: ['post_id'] }},
|
|
537
537
|
|
|
538
538
|
// ── WOOCOMMERCE (6) ──
|
|
539
|
-
{ name: 'wc_list_products', description: '
|
|
540
|
-
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, status: { type: 'string', default: 'any', description: 'any, draft, pending, private, publish' }, search: { type: 'string' }, category: { type: 'number'
|
|
541
|
-
{ name: 'wc_get_product', description: '
|
|
542
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
543
|
-
{ name: 'wc_list_orders', description: '
|
|
544
|
-
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, status: { type: 'string', default: 'any', description: 'any, pending, processing, on-hold, completed, cancelled, refunded, failed' }, customer: { type: 'number'
|
|
545
|
-
{ name: 'wc_get_order', description: '
|
|
546
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
547
|
-
{ name: 'wc_list_customers', description: '
|
|
539
|
+
{ name: 'wc_list_products', description: 'Use to browse WooCommerce products. Filter by status, category, or search. Read-only.',
|
|
540
|
+
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, status: { type: 'string', default: 'any', description: 'any, draft, pending, private, publish' }, search: { type: 'string' }, category: { type: 'number' }, orderby: { type: 'string', default: 'date', description: 'date, id, title, price, popularity' }, order: { type: 'string', default: 'desc' } }}},
|
|
541
|
+
{ name: 'wc_get_product', description: 'Use to get full product details including variations summary. Read-only.',
|
|
542
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' } }, required: ['id'] }},
|
|
543
|
+
{ name: 'wc_list_orders', description: 'Use to browse WooCommerce orders. Filter by status or customer. Read-only.',
|
|
544
|
+
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, status: { type: 'string', default: 'any', description: 'any, pending, processing, on-hold, completed, cancelled, refunded, failed' }, customer: { type: 'number' }, orderby: { type: 'string', default: 'date' }, order: { type: 'string', default: 'desc' } }}},
|
|
545
|
+
{ name: 'wc_get_order', description: 'Use to get order details with line items, shipping, billing, and payment info. Read-only.',
|
|
546
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' } }, required: ['id'] }},
|
|
547
|
+
{ name: 'wc_list_customers', description: 'Use to list WooCommerce customers with search and role filtering. Read-only.',
|
|
548
548
|
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 10 }, page: { type: 'number', default: 1 }, search: { type: 'string' }, orderby: { type: 'string', default: 'date_created' }, order: { type: 'string', default: 'desc' }, role: { type: 'string', default: 'customer' } }}},
|
|
549
|
-
{ name: 'wc_price_guardrail', description: '
|
|
550
|
-
inputSchema: { type: 'object', properties: { product_id: { type: 'number'
|
|
549
|
+
{ name: 'wc_price_guardrail', description: 'Use BEFORE changing a product price. Analyzes safety of proposed price change. Read-only, always allowed.',
|
|
550
|
+
inputSchema: { type: 'object', properties: { product_id: { type: 'number' }, new_price: { type: 'number', description: 'Proposed new price' }, threshold_percent: { type: 'number', default: 20, description: 'Maximum allowed change percentage (default 20)' } }, required: ['product_id', 'new_price'] }},
|
|
551
551
|
|
|
552
552
|
// ── WOOCOMMERCE INTELLIGENCE (4) ──
|
|
553
|
-
{ name: 'wc_inventory_alert', description: '
|
|
554
|
-
inputSchema: { type: 'object', properties: { threshold: { type: 'number', default: 5, description: 'Stock quantity threshold (default 5)' }, per_page: { type: 'number', default: 50
|
|
555
|
-
{ name: 'wc_order_intelligence', description: '
|
|
556
|
-
inputSchema: { type: 'object', properties: { customer_id: { type: 'number'
|
|
557
|
-
{ name: 'wc_seo_product_audit', description: '
|
|
558
|
-
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 20
|
|
559
|
-
{ name: 'wc_suggest_product_links', description: '
|
|
560
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
553
|
+
{ name: 'wc_inventory_alert', description: 'Use to identify low-stock and out-of-stock products below a threshold, sorted by urgency. Read-only.',
|
|
554
|
+
inputSchema: { type: 'object', properties: { threshold: { type: 'number', default: 5, description: 'Stock quantity threshold (default 5)' }, per_page: { type: 'number', default: 50 }, include_out_of_stock: { type: 'boolean', default: true, description: 'Include out-of-stock products' } }}},
|
|
555
|
+
{ name: 'wc_order_intelligence', description: 'Use to analyze a customer\'s purchase history: lifetime value, average order, favorite products, frequency. Read-only.',
|
|
556
|
+
inputSchema: { type: 'object', properties: { customer_id: { type: 'number' } }, required: ['customer_id'] }},
|
|
557
|
+
{ name: 'wc_seo_product_audit', description: 'Use to audit WooCommerce product listings for SEO issues (missing descriptions, images, alt text, generic slugs). Read-only.',
|
|
558
|
+
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 20 }, page: { type: 'number', default: 1 } }}},
|
|
559
|
+
{ name: 'wc_suggest_product_links', description: 'Use to suggest WooCommerce products to link from a blog post based on keyword relevance. Read-only.',
|
|
560
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, max_suggestions: { type: 'number', default: 3, description: 'Maximum suggestions (1-5)' } }, required: ['post_id'] }},
|
|
561
561
|
|
|
562
562
|
// ── WOOCOMMERCE WRITE (3) ──
|
|
563
|
-
{ name: 'wc_update_product', description: '
|
|
564
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
565
|
-
{ name: 'wc_update_stock', description: '
|
|
566
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
567
|
-
{ name: 'wc_update_order_status', description: '
|
|
568
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
563
|
+
{ name: 'wc_update_product', description: 'Use to update product fields (title, description, price, stock, status). Write — blocked by WP_READ_ONLY. Hint: call wc_price_guardrail first for price changes.',
|
|
564
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, name: { type: 'string' }, description: { type: 'string' }, short_description: { type: 'string' }, regular_price: { type: 'string', description: 'Format "19.99"' }, sale_price: { type: 'string' }, status: { type: 'string', description: 'publish, draft, or private' }, price_guardrail_confirmed: { type: 'boolean', default: false, description: 'Set true to bypass price guardrail after calling wc_price_guardrail' } }, required: ['id'] }},
|
|
565
|
+
{ name: 'wc_update_stock', description: 'Use to update stock quantity of a product or variation. Write — blocked by WP_READ_ONLY.',
|
|
566
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, stock_quantity: { type: 'number' }, variation_id: { type: 'number', description: 'Variation ID (for variable products)' } }, required: ['id', 'stock_quantity'] }},
|
|
567
|
+
{ name: 'wc_update_order_status', description: 'Use to transition order status (e.g. processing → completed). Write — blocked by WP_READ_ONLY.',
|
|
568
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, status: { type: 'string', description: 'processing, completed, cancelled, refunded, on-hold, failed' }, note: { type: 'string' } }, required: ['id', 'status'] }},
|
|
569
569
|
|
|
570
570
|
// ── SEO ADVANCED (3) ──
|
|
571
|
-
{ name: 'wp_audit_media_seo', description: '
|
|
572
|
-
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 50
|
|
573
|
-
{ name: 'wp_find_orphan_pages', description: '
|
|
574
|
-
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 100
|
|
575
|
-
{ name: 'wp_audit_heading_structure', description: '
|
|
576
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
571
|
+
{ name: 'wp_audit_media_seo', description: 'Use when checking image SEO. Scans media library for missing/short alt text and bad filenames. Returns per-image scores + fix list. Read-only.',
|
|
572
|
+
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 50 }, page: { type: 'number', default: 1 }, post_id: { type: 'number', description: 'Also scan inline images from this post' } }}},
|
|
573
|
+
{ name: 'wp_find_orphan_pages', description: 'Use to find posts with zero inbound internal links, sorted by word count. Read-only. Hint: combine with wp_suggest_internal_links to fix orphans.',
|
|
574
|
+
inputSchema: { type: 'object', properties: { per_page: { type: 'number', default: 100 }, exclude_ids: { type: 'array', items: { type: 'number' }, description: 'Page IDs to exclude from orphan check' }, min_words: { type: 'number', default: 0, description: 'Minimum word count to include in results' } }}},
|
|
575
|
+
{ name: 'wp_audit_heading_structure', description: 'Use to check H1-H6 hierarchy in a single post. Detects H1 in body, level skips, empty headings. Read-only.',
|
|
576
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, post_type: { type: 'string', default: 'post', description: 'post or page' }, focus_keyword: { type: 'string', description: 'Keyword to check in H2 headings' } }, required: ['id'] }},
|
|
577
577
|
|
|
578
578
|
// ── SEO ADVANCED v4.1 (3) ──
|
|
579
|
-
{ name: 'wp_find_thin_content', description: '
|
|
580
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 100
|
|
581
|
-
{ name: 'wp_audit_canonicals', description: '
|
|
582
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 50
|
|
583
|
-
{ name: 'wp_analyze_eeat_signals', description: '
|
|
584
|
-
inputSchema: { type: 'object', properties: { post_ids: { type: 'array', items: { type: 'number' }, description: 'Specific post IDs
|
|
579
|
+
{ name: 'wp_find_thin_content', description: 'Use to surface short/low-quality posts below a word count threshold. Classifies severity. Read-only.',
|
|
580
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 100 }, min_words: { type: 'number', default: 300, description: 'Threshold for "too short"' }, critical_words: { type: 'number', default: 150, description: 'Threshold for "very short"' }, max_age_days: { type: 'number', default: 730, description: 'Days since update to flag as outdated' }, include_uncategorized: { type: 'boolean', default: true, description: 'Flag uncategorized posts' }, post_type: { type: 'string', default: 'post', description: 'post or page' } }}},
|
|
581
|
+
{ name: 'wp_audit_canonicals', description: 'Use to validate canonical URLs across posts/pages. Detects missing, mismatched, or staging URLs. Auto-detects SEO plugin. Read-only.',
|
|
582
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 50 }, post_type: { type: 'string', default: 'post', description: 'post, page, or both' }, check_staging_patterns: { type: 'boolean', default: true, description: 'Detect staging/dev URLs' } }}},
|
|
583
|
+
{ name: 'wp_analyze_eeat_signals', description: 'Use to score E-E-A-T per post (0-100): author bio, dates, citations, word count, structured data. Read-only.',
|
|
584
|
+
inputSchema: { type: 'object', properties: { post_ids: { type: 'array', items: { type: 'number' }, description: 'Specific post IDs (if empty, audits latest N)' }, limit: { type: 'number', default: 10 }, post_type: { type: 'string', default: 'post', description: 'post or page' }, authoritative_domains: { type: 'array', items: { type: 'string' }, default: ['wikipedia.org', 'gov', 'edu', 'who.int', 'pubmed'], description: 'Domains considered authoritative' } }}},
|
|
585
585
|
|
|
586
586
|
// ── SEO ADVANCED v4.2 (4) ──
|
|
587
|
-
{ name: 'wp_find_broken_internal_links', description: '
|
|
588
|
-
inputSchema: { type: 'object', properties: { limit_posts: { type: 'number', default: 20
|
|
589
|
-
{ name: 'wp_find_keyword_cannibalization', description: '
|
|
590
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 200
|
|
591
|
-
{ name: 'wp_audit_taxonomies', description: '
|
|
592
|
-
inputSchema: { type: 'object', properties: { check_tags: { type: 'boolean', default: true
|
|
593
|
-
{ name: 'wp_audit_outbound_links', description: '
|
|
594
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 30
|
|
587
|
+
{ name: 'wp_find_broken_internal_links', description: 'Use to check internal links via HEAD requests. Returns broken (4xx), redirected (3xx), and slow links. Read-only.',
|
|
588
|
+
inputSchema: { type: 'object', properties: { limit_posts: { type: 'number', default: 20 }, batch_size: { type: 'number', default: 5, description: 'Links per batch (1-10)' }, timeout_ms: { type: 'number', default: 5000, description: 'Timeout per HEAD request (1000-30000)' }, delay_ms: { type: 'number', default: 200, description: 'Delay between batches (0-2000)' }, post_type: { type: 'string', default: 'post', description: 'post, page, or both' }, include_redirects: { type: 'boolean', default: true, description: 'Include 301/302 redirects in results' } }}},
|
|
589
|
+
{ name: 'wp_find_keyword_cannibalization', description: 'Use to detect posts competing on the same focus keyword. Groups conflicts, flags weakest post. Read-only.',
|
|
590
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 200 }, post_type: { type: 'string', default: 'post', description: 'post, page, or both' }, similarity_mode: { type: 'string', default: 'normalized', description: 'exact or normalized keyword matching' }, min_group_size: { type: 'number', default: 2, description: 'Minimum articles per group (min 2)' } }}},
|
|
591
|
+
{ name: 'wp_audit_taxonomies', description: 'Use to detect taxonomy bloat: empty/single-post terms, near-duplicates, missing descriptions. Read-only.',
|
|
592
|
+
inputSchema: { type: 'object', properties: { check_tags: { type: 'boolean', default: true }, check_categories: { type: 'boolean', default: true }, min_posts_threshold: { type: 'number', default: 2, description: 'Minimum posts per term' }, detect_duplicates: { type: 'boolean', default: true, description: 'Detect near-duplicate terms via Levenshtein' } }}},
|
|
593
|
+
{ name: 'wp_audit_outbound_links', description: 'Use to analyze external link profile per post. Detects over-linking, missing nofollow, broken external URLs. Read-only.',
|
|
594
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 30 }, post_type: { type: 'string', default: 'post', description: 'post or page' }, min_outbound: { type: 'number', default: 1, description: 'Minimum outbound links threshold' }, max_outbound: { type: 'number', default: 15, description: 'Maximum outbound links before dilution warning' }, authoritative_domains: { type: 'array', items: { type: 'string' }, default: ['wikipedia.org', 'gov', 'edu', 'who.int', 'pubmed.ncbi'], description: 'Domains considered authoritative' } }}},
|
|
595
595
|
|
|
596
596
|
// ── CONTENT INTELLIGENCE v4.4 (2) ──
|
|
597
|
-
{ name: 'wp_get_content_brief', description: '
|
|
598
|
-
inputSchema: { type: 'object', properties: { id: { type: 'number'
|
|
599
|
-
{ name: 'wp_extract_post_outline', description: '
|
|
600
|
-
inputSchema: { type: 'object', properties: { category_id: { type: 'number'
|
|
597
|
+
{ name: 'wp_get_content_brief', description: 'Use to get a compact content brief in 1 call: title, SEO meta, headings, word count, links, categories. Read-only. Hint: start here before writing or rewriting content.',
|
|
598
|
+
inputSchema: { type: 'object', properties: { id: { type: 'number' }, post_type: { type: 'string', default: 'post', description: 'post or page' } }, required: ['id'] }},
|
|
599
|
+
{ name: 'wp_extract_post_outline', description: 'Use to extract H1-H4 outline from N posts in a category as a reference template for new content. Read-only.',
|
|
600
|
+
inputSchema: { type: 'object', properties: { category_id: { type: 'number' }, post_type: { type: 'string', default: 'post', description: 'post or page' }, limit: { type: 'number', default: 10 } }, required: ['category_id'] }},
|
|
601
601
|
|
|
602
602
|
// ── CONTENT INTELLIGENCE v4.4 Week 2 (3) ──
|
|
603
|
-
{ name: 'wp_audit_readability', description: '
|
|
604
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
605
|
-
{ name: 'wp_audit_update_frequency', description: '
|
|
606
|
-
inputSchema: { type: 'object', properties: { days_threshold: { type: 'number', description: 'Flag posts not modified in X days (default 180)' }, limit: { type: 'number'
|
|
607
|
-
{ name: 'wp_build_link_map', description: '
|
|
608
|
-
inputSchema: { type: 'object', properties: { post_type: { type: 'string', enum: ['post', 'page', 'both']
|
|
603
|
+
{ name: 'wp_audit_readability', description: 'Use to score text readability (Flesch-Kincaid adapted). Returns transition density and passive ratio. Read-only.',
|
|
604
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'] }, category_id: { type: 'number' }, min_words: { type: 'number', description: 'Minimum word count to include (default 100)' } }}},
|
|
605
|
+
{ name: 'wp_audit_update_frequency', description: 'Use to find stale posts not updated since N days, cross-referenced with SEO score. Read-only.',
|
|
606
|
+
inputSchema: { type: 'object', properties: { days_threshold: { type: 'number', description: 'Flag posts not modified in X days (default 180)' }, limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'] }, include_seo_score: { type: 'boolean', description: 'Cross-reference with SEO metadata quality (default true)' } }}},
|
|
607
|
+
{ name: 'wp_build_link_map', description: 'Use to generate full internal link matrix with simplified PageRank scores per post. Read-only.',
|
|
608
|
+
inputSchema: { type: 'object', properties: { post_type: { type: 'string', enum: ['post', 'page', 'both'] }, limit: { type: 'number' }, category_id: { type: 'number' } }}},
|
|
609
609
|
|
|
610
610
|
// ── CONTENT INTELLIGENCE v4.4 Week 3 (3) ──
|
|
611
|
-
{ name: 'wp_audit_anchor_texts', description: '
|
|
612
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
613
|
-
{ name: 'wp_audit_schema_markup', description: '
|
|
614
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
615
|
-
{ name: 'wp_audit_content_structure', description: '
|
|
616
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
611
|
+
{ name: 'wp_audit_anchor_texts', description: 'Use to check internal link anchor diversity. Detects generic (\'click here\') and over-optimized anchors. Read-only.',
|
|
612
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page', 'both'] } }}},
|
|
613
|
+
{ name: 'wp_audit_schema_markup', description: 'Use to detect and validate JSON-LD in post HTML content (Article, FAQ, HowTo, LocalBusiness). Read-only. Hint: use wp_audit_schema_plugins for plugin-native schema instead.',
|
|
614
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page', 'both'] } }}},
|
|
615
|
+
{ name: 'wp_audit_content_structure', description: 'Use to analyze post structure: intro/body/conclusion ratio, FAQ presence, TOC, lists, tables. Read-only.',
|
|
616
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page', 'both'] }, category_id: { type: 'number' } }}},
|
|
617
617
|
|
|
618
618
|
// ── CONTENT INTELLIGENCE v4.4 Batch 4A (4) ──
|
|
619
|
-
{ name: 'wp_find_duplicate_content', description: '
|
|
620
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
621
|
-
{ name: 'wp_find_content_gaps', description: '
|
|
622
|
-
inputSchema: { type: 'object', properties: { min_posts: { type: 'number', description: 'Minimum posts per term to NOT be flagged (default 3)' }, taxonomy: { type: 'string', enum: ['category', 'post_tag', 'both']
|
|
623
|
-
{ name: 'wp_extract_faq_blocks', description: '
|
|
624
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
625
|
-
{ name: 'wp_audit_cta_presence', description: '
|
|
626
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
619
|
+
{ name: 'wp_find_duplicate_content', description: 'Use to detect near-duplicate posts via TF-IDF cosine similarity. Read-only.',
|
|
620
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'] }, category_id: { type: 'number' }, similarity_threshold: { type: 'number', description: 'Minimum similarity to flag (0.0-1.0, default 0.7)' } }}},
|
|
621
|
+
{ name: 'wp_find_content_gaps', description: 'Use to find under-represented taxonomy terms (< N posts) as content creation opportunities. Read-only.',
|
|
622
|
+
inputSchema: { type: 'object', properties: { min_posts: { type: 'number', description: 'Minimum posts per term to NOT be flagged (default 3)' }, taxonomy: { type: 'string', enum: ['category', 'post_tag', 'both'] }, exclude_empty: { type: 'boolean', description: 'Exclude terms with 0 posts (default false)' } }}},
|
|
623
|
+
{ name: 'wp_extract_faq_blocks', description: 'Use to inventory all FAQ blocks (Gutenberg + schema JSON-LD) across the corpus. Read-only.',
|
|
624
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page', 'both'] } }}},
|
|
625
|
+
{ name: 'wp_audit_cta_presence', description: 'Use to detect presence/absence of CTAs (contact links, forms, buttons) per post. Read-only.',
|
|
626
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page', 'both'] }, category_id: { type: 'number' } }}},
|
|
627
627
|
|
|
628
628
|
// ── CONTENT INTELLIGENCE v4.4 Batch 4B (4) ──
|
|
629
|
-
{ name: 'wp_extract_entities', description: '
|
|
630
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
631
|
-
{ name: 'wp_get_publishing_velocity', description: '
|
|
632
|
-
inputSchema: { type: 'object', properties: { periods: { type: 'string', description: "Comma-separated day periods (default '30,90,180')" }, post_type: { type: 'string', enum: ['post', 'page']
|
|
633
|
-
{ name: 'wp_compare_revisions_diff', description: '
|
|
634
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
635
|
-
{ name: 'wp_list_posts_by_word_count', description: '
|
|
636
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
629
|
+
{ name: 'wp_extract_entities', description: 'Use to extract named entities (brands, places, people, organizations) from post content. Read-only.',
|
|
630
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'] }, min_occurrences: { type: 'number', description: 'Minimum total occurrences across corpus (default 2)' } }}},
|
|
631
|
+
{ name: 'wp_get_publishing_velocity', description: 'Use to measure publication cadence per author and category over 30/90/180 days. Read-only.',
|
|
632
|
+
inputSchema: { type: 'object', properties: { periods: { type: 'string', description: "Comma-separated day periods (default '30,90,180')" }, post_type: { type: 'string', enum: ['post', 'page'] }, limit: { type: 'number' } }}},
|
|
633
|
+
{ name: 'wp_compare_revisions_diff', description: 'Use to diff two revisions and measure update amplitude. Read-only.',
|
|
634
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, revision_id_from: { type: 'number', description: 'Older revision ID (baseline)' }, revision_id_to: { type: 'number', description: 'Newer revision ID (omit for current post)' }, post_type: { type: 'string', enum: ['post', 'page'] } }, required: ['post_id', 'revision_id_from'] }},
|
|
635
|
+
{ name: 'wp_list_posts_by_word_count', description: 'Use to rank posts by length with auto-segmentation (<500, 500-1k, 1k-2k, 2k+). Read-only.',
|
|
636
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page', 'both'] }, order: { type: 'string', enum: ['asc', 'desc'] }, category_id: { type: 'number' } }}},
|
|
637
637
|
|
|
638
638
|
// ── PLUGIN INTELLIGENCE v4.5 (3) ──
|
|
639
|
-
{ name: 'wp_get_rendered_head', description: '
|
|
640
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
641
|
-
{ name: 'wp_audit_rendered_seo', description: '
|
|
642
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
643
|
-
{ name: 'wp_get_pillar_content', description: '
|
|
644
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
639
|
+
{ name: 'wp_get_rendered_head', description: 'Use to fetch the real <head> HTML Google sees via RankMath/Yoast headless endpoint. Compares rendered vs stored meta. Read-only. Requires WP_ENABLE_PLUGIN_INTELLIGENCE=true.',
|
|
640
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'] } }, required: ['post_id'] }},
|
|
641
|
+
{ name: 'wp_audit_rendered_seo', description: 'Use for bulk rendered-vs-stored SEO divergence detection with per-post scoring. Read-only. Requires WP_ENABLE_PLUGIN_INTELLIGENCE=true.',
|
|
642
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'] } }}},
|
|
643
|
+
{ name: 'wp_get_pillar_content', description: 'Use to read or set RankMath cornerstone/pillar flag. Read always allowed. Write — blocked by WP_READ_ONLY. Requires WP_ENABLE_PLUGIN_INTELLIGENCE=true.',
|
|
644
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, set_pillar: { type: 'boolean', description: 'Set pillar flag (true=pillar, false=not). Requires write access.' }, list_pillars: { type: 'boolean', description: 'List all pillar content posts (ignores post_id)' }, post_type: { type: 'string', enum: ['post', 'page'] }, limit: { type: 'number' } }}},
|
|
645
645
|
|
|
646
646
|
// ── PLUGIN INTELLIGENCE v4.5 batch 2 (3) ──
|
|
647
|
-
{ name: 'wp_audit_schema_plugins', description: '
|
|
648
|
-
inputSchema: { type: 'object', properties: { limit: { type: 'number'
|
|
649
|
-
{ name: 'wp_get_seo_score', description: '
|
|
650
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
651
|
-
{ name: 'wp_get_twitter_meta', description: '
|
|
652
|
-
inputSchema: { type: 'object', properties: { post_id: { type: 'number'
|
|
647
|
+
{ name: 'wp_audit_schema_plugins', description: 'Use to validate JSON-LD from SEO plugin native fields (rank_math_schema or yoast_head_json). Read-only. Requires WP_ENABLE_PLUGIN_INTELLIGENCE=true.',
|
|
648
|
+
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page', 'both'] } }}},
|
|
649
|
+
{ name: 'wp_get_seo_score', description: 'Use to read RankMath native SEO score (0-100). Bulk mode shows distribution stats. Read-only. Requires WP_ENABLE_PLUGIN_INTELLIGENCE=true.',
|
|
650
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, limit: { type: 'number', description: 'Bulk mode: max posts (1-100, default 20)' }, post_type: { type: 'string', enum: ['post', 'page'] }, order: { type: 'string', enum: ['asc', 'desc'] } }}},
|
|
651
|
+
{ name: 'wp_get_twitter_meta', description: 'Use to read or update Twitter Card meta (title, description, image) for RankMath/Yoast/SEOPress. Write — blocked by WP_READ_ONLY. Requires WP_ENABLE_PLUGIN_INTELLIGENCE=true.',
|
|
652
|
+
inputSchema: { type: 'object', properties: { post_id: { type: 'number' }, post_type: { type: 'string', enum: ['post', 'page'] }, twitter_title: { type: 'string', description: 'Set Twitter title (write mode)' }, twitter_description: { type: 'string', description: 'Set Twitter description (write mode)' }, twitter_image: { type: 'string', description: 'Set Twitter image URL (write mode)' } }, required: ['post_id'] }}
|
|
653
653
|
];
|
|
654
654
|
|
|
655
|
+
function getFilteredTools(allTools = TOOLS_DEFINITIONS) {
|
|
656
|
+
const pluginIntelTools = ['wp_get_rendered_head', 'wp_audit_rendered_seo', 'wp_get_pillar_content', 'wp_audit_schema_plugins', 'wp_get_seo_score', 'wp_get_twitter_meta'];
|
|
657
|
+
const editorialTools = ['wp_submit_for_review', 'wp_approve_post', 'wp_reject_post'];
|
|
658
|
+
return allTools.filter(tool => {
|
|
659
|
+
const n = tool.name;
|
|
660
|
+
if (n.startsWith('wc_') && !process.env.WC_CONSUMER_KEY) return false;
|
|
661
|
+
if (editorialTools.includes(n) && process.env.WP_REQUIRE_APPROVAL !== 'true') return false;
|
|
662
|
+
if (pluginIntelTools.includes(n) && process.env.WP_ENABLE_PLUGIN_INTELLIGENCE !== 'true') return false;
|
|
663
|
+
return true;
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
|
|
655
667
|
function registerHandlers(s) {
|
|
656
|
-
s.setRequestHandler(ListToolsRequestSchema, async () => ({ tools:
|
|
668
|
+
s.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: getFilteredTools() }));
|
|
657
669
|
s.setRequestHandler(CallToolRequestSchema, handleToolCall);
|
|
658
670
|
}
|
|
659
671
|
|
|
@@ -795,11 +807,19 @@ export async function handleToolCall(request) {
|
|
|
795
807
|
// ── PAGES ──
|
|
796
808
|
|
|
797
809
|
case 'wp_list_pages': {
|
|
798
|
-
validateInput(args, { per_page: { type: 'number', min: 1, max: 100 }, page: { type: 'number', min: 1 }, status: { type: 'string', enum: STATUSES }, order: { type: 'string', enum: ORDERS } });
|
|
799
|
-
const { per_page = 10, page = 1, status = 'publish', parent, orderby = 'menu_order', order = 'asc', search } = args;
|
|
810
|
+
validateInput(args, { per_page: { type: 'number', min: 1, max: 100 }, page: { type: 'number', min: 1 }, status: { type: 'string', enum: STATUSES }, order: { type: 'string', enum: ORDERS }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'] } });
|
|
811
|
+
const { per_page = 10, page = 1, status = 'publish', parent, orderby = 'menu_order', order = 'asc', search, mode = 'full' } = args;
|
|
800
812
|
let ep = `/pages?per_page=${per_page}&page=${page}&status=${status}&orderby=${orderby}&order=${order}`;
|
|
801
813
|
if (parent !== undefined) ep += `&parent=${parent}`; if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
802
814
|
const pgs = await wpApiCall(ep);
|
|
815
|
+
if (mode === 'ids_only') {
|
|
816
|
+
result = json({ total: pgs.length, page, mode: 'ids_only', ids: pgs.map(p => p.id) });
|
|
817
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
818
|
+
}
|
|
819
|
+
if (mode === 'summary') {
|
|
820
|
+
result = json({ total: pgs.length, page, mode: 'summary', pages: pgs.map(p => ({ id: p.id, title: p.title.rendered, slug: p.slug, status: p.status, link: p.link, parent: p.parent })) });
|
|
821
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
822
|
+
}
|
|
803
823
|
result = json({ total: pgs.length, page, pages: pgs.map(p => ({ id: p.id, title: p.title.rendered, status: p.status, date: p.date, link: p.link, parent: p.parent, menu_order: p.menu_order, template: p.template, excerpt: strip(p.excerpt.rendered).substring(0, 200) })) });
|
|
804
824
|
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 });
|
|
805
825
|
break;
|
|
@@ -839,11 +859,19 @@ export async function handleToolCall(request) {
|
|
|
839
859
|
// ── MEDIA ──
|
|
840
860
|
|
|
841
861
|
case 'wp_list_media': {
|
|
842
|
-
validateInput(args, { per_page: { type: 'number', min: 1, max: 100 }, page: { type: 'number', min: 1 }, media_type: { type: 'string', enum: MEDIA_TYPES }, order: { type: 'string', enum: ORDERS } });
|
|
843
|
-
const { per_page = 10, page = 1, media_type, search, orderby = 'date', order = 'desc' } = args;
|
|
862
|
+
validateInput(args, { per_page: { type: 'number', min: 1, max: 100 }, page: { type: 'number', min: 1 }, media_type: { type: 'string', enum: MEDIA_TYPES }, order: { type: 'string', enum: ORDERS }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'] } });
|
|
863
|
+
const { per_page = 10, page = 1, media_type, search, orderby = 'date', order = 'desc', mode = 'full' } = args;
|
|
844
864
|
let ep = `/media?per_page=${per_page}&page=${page}&orderby=${orderby}&order=${order}`;
|
|
845
865
|
if (media_type) ep += `&media_type=${media_type}`; if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
846
866
|
const media = await wpApiCall(ep);
|
|
867
|
+
if (mode === 'ids_only') {
|
|
868
|
+
result = json({ total: media.length, page, mode: 'ids_only', ids: media.map(m => m.id) });
|
|
869
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
870
|
+
}
|
|
871
|
+
if (mode === 'summary') {
|
|
872
|
+
result = json({ total: media.length, page, mode: 'summary', media: media.map(m => ({ id: m.id, title: m.title.rendered, mime_type: m.mime_type, source_url: m.source_url, alt_text: m.alt_text })) });
|
|
873
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
874
|
+
}
|
|
847
875
|
result = json({ total: media.length, page, media: media.map(m => ({ id: m.id, title: m.title.rendered, date: m.date, mime_type: m.mime_type, source_url: m.source_url, alt_text: m.alt_text, width: m.media_details?.width, height: m.media_details?.height })) });
|
|
848
876
|
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 });
|
|
849
877
|
break;
|
|
@@ -880,20 +908,36 @@ export async function handleToolCall(request) {
|
|
|
880
908
|
// ── TAXONOMIES ──
|
|
881
909
|
|
|
882
910
|
case 'wp_list_categories': {
|
|
883
|
-
const { per_page = 100, page = 1, parent, search, orderby = 'name', hide_empty = false } = args;
|
|
911
|
+
const { per_page = 100, page = 1, parent, search, orderby = 'name', hide_empty = false, mode = 'full' } = args;
|
|
884
912
|
let ep = `/categories?per_page=${per_page}&page=${page}&orderby=${orderby}&hide_empty=${hide_empty}`;
|
|
885
913
|
if (parent !== undefined) ep += `&parent=${parent}`; if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
886
914
|
const cats = await wpApiCall(ep);
|
|
915
|
+
if (mode === 'ids_only') {
|
|
916
|
+
result = json({ total: cats.length, mode: 'ids_only', ids: cats.map(c => c.id) });
|
|
917
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
918
|
+
}
|
|
919
|
+
if (mode === 'summary') {
|
|
920
|
+
result = json({ total: cats.length, mode: 'summary', categories: cats.map(c => ({ id: c.id, name: c.name, slug: c.slug, count: c.count, parent: c.parent })) });
|
|
921
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
922
|
+
}
|
|
887
923
|
result = json({ total: cats.length, categories: cats.map(c => ({ id: c.id, name: c.name, slug: c.slug, description: c.description, parent: c.parent, count: c.count })) });
|
|
888
924
|
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 });
|
|
889
925
|
break;
|
|
890
926
|
}
|
|
891
927
|
|
|
892
928
|
case 'wp_list_tags': {
|
|
893
|
-
const { per_page = 100, page = 1, search, orderby = 'name', hide_empty = false } = args;
|
|
929
|
+
const { per_page = 100, page = 1, search, orderby = 'name', hide_empty = false, mode = 'full' } = args;
|
|
894
930
|
let ep = `/tags?per_page=${per_page}&page=${page}&orderby=${orderby}&hide_empty=${hide_empty}`;
|
|
895
931
|
if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
896
932
|
const tags = await wpApiCall(ep);
|
|
933
|
+
if (mode === 'ids_only') {
|
|
934
|
+
result = json({ total: tags.length, mode: 'ids_only', ids: tags.map(t => t.id) });
|
|
935
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
936
|
+
}
|
|
937
|
+
if (mode === 'summary') {
|
|
938
|
+
result = json({ total: tags.length, mode: 'summary', tags: tags.map(t => ({ id: t.id, name: t.name, slug: t.slug, count: t.count })) });
|
|
939
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
940
|
+
}
|
|
897
941
|
result = json({ total: tags.length, tags: tags.map(t => ({ id: t.id, name: t.name, slug: t.slug, count: t.count })) });
|
|
898
942
|
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 });
|
|
899
943
|
break;
|
|
@@ -914,10 +958,18 @@ export async function handleToolCall(request) {
|
|
|
914
958
|
// ── COMMENTS ──
|
|
915
959
|
|
|
916
960
|
case 'wp_list_comments': {
|
|
917
|
-
const { per_page = 10, page = 1, post, status, orderby = 'date_gmt', order = 'desc', search } = args;
|
|
961
|
+
const { per_page = 10, page = 1, post, status, orderby = 'date_gmt', order = 'desc', search, mode = 'full' } = args;
|
|
918
962
|
let ep = `/comments?per_page=${per_page}&page=${page}&orderby=${orderby}&order=${order}`;
|
|
919
963
|
if (post) ep += `&post=${post}`; if (status) ep += `&status=${status}`; if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
920
964
|
const comments = await wpApiCall(ep);
|
|
965
|
+
if (mode === 'ids_only') {
|
|
966
|
+
result = json({ total: comments.length, page, mode: 'ids_only', ids: comments.map(c => c.id) });
|
|
967
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
968
|
+
}
|
|
969
|
+
if (mode === 'summary') {
|
|
970
|
+
result = json({ total: comments.length, page, mode: 'summary', comments: comments.map(c => ({ id: c.id, post: c.post, author_name: c.author_name, date: c.date, status: c.status })) });
|
|
971
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
972
|
+
}
|
|
921
973
|
result = json({ total: comments.length, page, comments: comments.map(c => ({ id: c.id, post: c.post, parent: c.parent, author_name: c.author_name, date: c.date, status: c.status, content: strip(c.content.rendered).substring(0, 300), link: c.link })) });
|
|
922
974
|
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 });
|
|
923
975
|
break;
|
|
@@ -944,9 +996,9 @@ export async function handleToolCall(request) {
|
|
|
944
996
|
}
|
|
945
997
|
|
|
946
998
|
case 'wp_list_custom_posts': {
|
|
947
|
-
validateInput(args, { post_type: { type: 'string', required: true }, per_page: { type: 'number', min: 1, max: 100 }, page: { type: 'number', min: 1 }, order: { type: 'string', enum: ORDERS } });
|
|
999
|
+
validateInput(args, { post_type: { type: 'string', required: true }, per_page: { type: 'number', min: 1, max: 100 }, page: { type: 'number', min: 1 }, order: { type: 'string', enum: ORDERS }, mode: { type: 'string', enum: ['full', 'summary', 'ids_only'] } });
|
|
948
1000
|
enforceAllowedTypes(args.post_type);
|
|
949
|
-
const { post_type, per_page = 10, page = 1, status = 'publish', orderby = 'date', order = 'desc', search } = args;
|
|
1001
|
+
const { post_type, per_page = 10, page = 1, status = 'publish', orderby = 'date', order = 'desc', search, mode = 'full' } = args;
|
|
950
1002
|
const types = await wpApiCall('/types');
|
|
951
1003
|
const typeInfo = Object.values(types).find(t => t.slug === post_type || t.rest_base === post_type);
|
|
952
1004
|
if (!typeInfo) throw new Error(`Post type "${post_type}" not found.`);
|
|
@@ -954,6 +1006,14 @@ export async function handleToolCall(request) {
|
|
|
954
1006
|
let ep = `/${restBase}?per_page=${per_page}&page=${page}&status=${status}&orderby=${orderby}&order=${order}`;
|
|
955
1007
|
if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
956
1008
|
const posts = await wpApiCall(ep);
|
|
1009
|
+
if (mode === 'ids_only') {
|
|
1010
|
+
result = json({ post_type, total: posts.length, page, mode: 'ids_only', ids: posts.map(p => p.id) });
|
|
1011
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0, params: { post_type } }); break;
|
|
1012
|
+
}
|
|
1013
|
+
if (mode === 'summary') {
|
|
1014
|
+
result = json({ post_type, total: posts.length, page, mode: 'summary', posts: posts.map(p => ({ id: p.id, title: p.title?.rendered || p.title, slug: p.slug, date: p.date, status: p.status, link: p.link })) });
|
|
1015
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0, params: { post_type } }); break;
|
|
1016
|
+
}
|
|
957
1017
|
result = json({ post_type, total: posts.length, page, posts: posts.map(p => ({ id: p.id, title: p.title?.rendered || p.title, status: p.status, date: p.date, link: p.link, slug: p.slug, type: p.type, meta: p.meta || {} })) });
|
|
958
1018
|
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0, params: { post_type } });
|
|
959
1019
|
break;
|
|
@@ -962,10 +1022,18 @@ export async function handleToolCall(request) {
|
|
|
962
1022
|
// ── USERS ──
|
|
963
1023
|
|
|
964
1024
|
case 'wp_list_users': {
|
|
965
|
-
const { per_page = 10, page = 1, roles, search, orderby = 'name' } = args;
|
|
1025
|
+
const { per_page = 10, page = 1, roles, search, orderby = 'name', mode = 'full' } = args;
|
|
966
1026
|
let ep = `/users?per_page=${per_page}&page=${page}&orderby=${orderby}`;
|
|
967
1027
|
if (roles) ep += `&roles=${roles}`; if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
968
1028
|
const users = await wpApiCall(ep);
|
|
1029
|
+
if (mode === 'ids_only') {
|
|
1030
|
+
result = json({ total: users.length, mode: 'ids_only', ids: users.map(u => u.id) });
|
|
1031
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
1032
|
+
}
|
|
1033
|
+
if (mode === 'summary') {
|
|
1034
|
+
result = json({ total: users.length, mode: 'summary', users: users.map(u => ({ id: u.id, name: u.name, slug: u.slug, roles: u.roles || [] })) });
|
|
1035
|
+
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 }); break;
|
|
1036
|
+
}
|
|
969
1037
|
result = json({ total: users.length, users: users.map(u => ({ id: u.id, name: u.name, slug: u.slug, link: u.link, roles: u.roles, avatar: u.avatar_urls?.['96'] })) });
|
|
970
1038
|
auditLog({ tool: name, action: 'list', status: 'success', latency_ms: Date.now() - t0 });
|
|
971
1039
|
break;
|
|
@@ -1022,7 +1090,14 @@ export async function handleToolCall(request) {
|
|
|
1022
1090
|
enabled: isMultiTarget, active_site: currentTarget?.name || 'default',
|
|
1023
1091
|
available_sites: Object.keys(targets)
|
|
1024
1092
|
},
|
|
1025
|
-
server:
|
|
1093
|
+
server: (() => {
|
|
1094
|
+
const exposed = getFilteredTools().length;
|
|
1095
|
+
const groups = [];
|
|
1096
|
+
if (!process.env.WC_CONSUMER_KEY) groups.push('woocommerce');
|
|
1097
|
+
if (process.env.WP_REQUIRE_APPROVAL !== 'true') groups.push('editorial');
|
|
1098
|
+
if (process.env.WP_ENABLE_PLUGIN_INTELLIGENCE !== 'true') groups.push('plugin_intelligence');
|
|
1099
|
+
return { mcp_version: VERSION, tools_total: TOOLS_COUNT, tools_exposed: exposed, filtered_out: groups };
|
|
1100
|
+
})()
|
|
1026
1101
|
});
|
|
1027
1102
|
auditLog({ tool: name, action: 'info', status: 'success', latency_ms: Date.now() - t0 });
|
|
1028
1103
|
break;
|
|
@@ -1264,15 +1339,20 @@ export async function handleToolCall(request) {
|
|
|
1264
1339
|
validateInput(args, {
|
|
1265
1340
|
search: { type: 'string' },
|
|
1266
1341
|
status: { type: 'string', enum: ['active', 'inactive', 'all'] },
|
|
1267
|
-
per_page: { type: 'number', min: 1, max: 100 }
|
|
1342
|
+
per_page: { type: 'number', min: 1, max: 100 },
|
|
1343
|
+
mode: { type: 'string', enum: ['full', 'summary', 'ids_only'] }
|
|
1268
1344
|
});
|
|
1269
|
-
const { search, status = 'all', per_page = 20 } = args;
|
|
1345
|
+
const { search, status = 'all', per_page = 20, mode = 'full' } = args;
|
|
1270
1346
|
let ep = `/plugins?per_page=${per_page}&context=edit`;
|
|
1271
1347
|
if (search) ep += `&search=${encodeURIComponent(search)}`;
|
|
1272
1348
|
if (status && status !== 'all') ep += `&status=${status}`;
|
|
1273
1349
|
|
|
1274
1350
|
try {
|
|
1275
1351
|
const plugins = await wpApiCall(ep);
|
|
1352
|
+
if (mode === 'ids_only') {
|
|
1353
|
+
result = json({ total: plugins.length, mode: 'ids_only', ids: plugins.map(p => p.plugin) });
|
|
1354
|
+
auditLog({ tool: name, action: 'list', target_type: 'plugin', status: 'success', latency_ms: Date.now() - t0, params: { search, status, per_page } }); break;
|
|
1355
|
+
}
|
|
1276
1356
|
const mapped = plugins.map(p => ({
|
|
1277
1357
|
plugin: p.plugin,
|
|
1278
1358
|
name: p.name,
|
|
@@ -1286,6 +1366,10 @@ export async function handleToolCall(request) {
|
|
|
1286
1366
|
network_only: p.network_only ?? false,
|
|
1287
1367
|
textdomain: p.textdomain ?? ''
|
|
1288
1368
|
}));
|
|
1369
|
+
if (mode === 'summary') {
|
|
1370
|
+
result = json({ total: mapped.length, mode: 'summary', plugins: mapped.map(p => ({ plugin: p.plugin, name: p.name, status: p.status, version: p.version })) });
|
|
1371
|
+
auditLog({ tool: name, action: 'list', target_type: 'plugin', status: 'success', latency_ms: Date.now() - t0, params: { search, status, per_page } }); break;
|
|
1372
|
+
}
|
|
1289
1373
|
const activeCount = mapped.filter(p => p.status === 'active').length;
|
|
1290
1374
|
const inactiveCount = mapped.filter(p => p.status === 'inactive').length;
|
|
1291
1375
|
result = json({ total: mapped.length, active: activeCount, inactive: inactiveCount, plugins: mapped });
|
|
@@ -1346,13 +1430,18 @@ export async function handleToolCall(request) {
|
|
|
1346
1430
|
case 'wp_list_themes': {
|
|
1347
1431
|
validateInput(args, {
|
|
1348
1432
|
status: { type: 'string', enum: ['active', 'inactive', 'all'] },
|
|
1349
|
-
per_page: { type: 'number', min: 1, max: 100 }
|
|
1433
|
+
per_page: { type: 'number', min: 1, max: 100 },
|
|
1434
|
+
mode: { type: 'string', enum: ['full', 'summary', 'ids_only'] }
|
|
1350
1435
|
});
|
|
1351
|
-
const { status = 'all', per_page = 20 } = args;
|
|
1436
|
+
const { status = 'all', per_page = 20, mode = 'full' } = args;
|
|
1352
1437
|
let ep = `/themes?per_page=${per_page}&context=edit`;
|
|
1353
1438
|
if (status && status !== 'all') ep += `&status=${status}`;
|
|
1354
1439
|
try {
|
|
1355
1440
|
const themes = await wpApiCall(ep);
|
|
1441
|
+
if (mode === 'ids_only') {
|
|
1442
|
+
result = json({ total: themes.length, mode: 'ids_only', ids: themes.map(t => t.stylesheet) });
|
|
1443
|
+
auditLog({ tool: name, action: 'list', target_type: 'theme', status: 'success', latency_ms: Date.now() - t0, params: { status, per_page } }); break;
|
|
1444
|
+
}
|
|
1356
1445
|
const mapped = themes.map(t => ({
|
|
1357
1446
|
stylesheet: t.stylesheet,
|
|
1358
1447
|
template: t.template,
|
|
@@ -1367,6 +1456,10 @@ export async function handleToolCall(request) {
|
|
|
1367
1456
|
requires_php: t.requires_php ?? '',
|
|
1368
1457
|
tags: t.tags?.rendered ?? t.tags ?? []
|
|
1369
1458
|
}));
|
|
1459
|
+
if (mode === 'summary') {
|
|
1460
|
+
result = json({ total: mapped.length, mode: 'summary', themes: mapped.map(t => ({ stylesheet: t.stylesheet, name: t.name, status: t.status, version: t.version })) });
|
|
1461
|
+
auditLog({ tool: name, action: 'list', target_type: 'theme', status: 'success', latency_ms: Date.now() - t0, params: { status, per_page } }); break;
|
|
1462
|
+
}
|
|
1370
1463
|
const activeTheme = mapped.find(t => t.status === 'active');
|
|
1371
1464
|
result = json({ total: mapped.length, active_theme: activeTheme ? activeTheme.name : null, themes: mapped });
|
|
1372
1465
|
auditLog({ tool: name, action: 'list', target_type: 'theme', status: 'success', latency_ms: Date.now() - t0, params: { status, per_page } });
|
|
@@ -1416,12 +1509,21 @@ export async function handleToolCall(request) {
|
|
|
1416
1509
|
validateInput(args, {
|
|
1417
1510
|
post_id: { type: 'number', required: true, min: 1 },
|
|
1418
1511
|
post_type: { type: 'string', enum: ['post', 'page'] },
|
|
1419
|
-
per_page: { type: 'number', min: 1, max: 100 }
|
|
1512
|
+
per_page: { type: 'number', min: 1, max: 100 },
|
|
1513
|
+
mode: { type: 'string', enum: ['full', 'summary', 'ids_only'] }
|
|
1420
1514
|
});
|
|
1421
|
-
const { post_id, post_type = 'post', per_page = 10 } = args;
|
|
1515
|
+
const { post_id, post_type = 'post', per_page = 10, mode = 'full' } = args;
|
|
1422
1516
|
const base = post_type === 'page' ? 'pages' : 'posts';
|
|
1423
1517
|
try {
|
|
1424
1518
|
const revisions = await wpApiCall(`/${base}/${post_id}/revisions?per_page=${per_page}&context=edit`);
|
|
1519
|
+
if (mode === 'ids_only') {
|
|
1520
|
+
result = json({ total: revisions.length, post_id, post_type, mode: 'ids_only', ids: revisions.map(r => r.id) });
|
|
1521
|
+
auditLog({ tool: name, action: 'list', target: post_id, target_type: 'revision', status: 'success', latency_ms: Date.now() - t0, params: { post_type, per_page } }); break;
|
|
1522
|
+
}
|
|
1523
|
+
if (mode === 'summary') {
|
|
1524
|
+
result = json({ total: revisions.length, post_id, post_type, mode: 'summary', revisions: revisions.map(r => ({ id: r.id, date: r.date, author: r.author })) });
|
|
1525
|
+
auditLog({ tool: name, action: 'list', target: post_id, target_type: 'revision', status: 'success', latency_ms: Date.now() - t0, params: { post_type, per_page } }); break;
|
|
1526
|
+
}
|
|
1425
1527
|
result = json({
|
|
1426
1528
|
total: revisions.length,
|
|
1427
1529
|
post_id,
|
|
@@ -4952,4 +5054,4 @@ if (process.env.NODE_ENV !== 'test') {
|
|
|
4952
5054
|
main().catch((error) => { log.error(`Fatal: ${error.message}`); process.exit(1); });
|
|
4953
5055
|
}
|
|
4954
5056
|
|
|
4955
|
-
export { server, getActiveControls, getControlSources, _testSetTarget };
|
|
5057
|
+
export { server, getActiveControls, getControlSources, _testSetTarget, getFilteredTools };
|