Skip to main content
Glama
index.ts9.79 kB
/** * Cookbook Tools Registration * * Tools for discovering and loading plugin-specific AI guidance cookbooks. * * @package WP_Navigator_MCP * @since 2.1.0 */ import { toolRegistry, ToolCategory } from '../../tool-registry/index.js'; import { discoverCookbooks, getCookbook, matchCookbooksToPlugins, type LoadedSkillCookbook, type PluginInfo, } from '../../cookbook/index.js'; /** * Register cookbook tools */ export function registerCookbookTools() { // ============================================================================ // wpnav_list_cookbooks - List available cookbooks // ============================================================================ toolRegistry.register({ definition: { name: 'wpnav_list_cookbooks', description: 'List all available plugin cookbooks with metadata. Cookbooks provide AI guidance for specific WordPress plugins like Gutenberg, Elementor, etc. Returns slug, name, version, source (bundled/project), and version requirements.', inputSchema: { type: 'object', properties: {}, required: [], }, }, handler: async (_args, _context) => { const { cookbooks, sources } = discoverCookbooks(); const cookbookList = Array.from(cookbooks.entries()).map(([slug, cookbook]) => { const skillCookbook = cookbook as LoadedSkillCookbook; return { slug, name: cookbook.plugin.name, description: skillCookbook.skillFrontmatter?.description || null, version: cookbook.cookbook_version, source: cookbook.source, min_plugin_version: cookbook.plugin.min_version || null, max_plugin_version: cookbook.plugin.max_version || null, requires_wpnav_pro: skillCookbook.skillFrontmatter?.['requires-wpnav-pro'] || null, has_skill_body: !!skillCookbook.skillBody, allowed_tools_count: skillCookbook.allowedTools?.length || 0, }; }); return { content: [ { type: 'text', text: JSON.stringify( { total: cookbookList.length, cookbooks: cookbookList, sources: { bundled: sources.bundled.length, project: sources.project.length, }, }, null, 2 ), }, ], }; }, category: ToolCategory.COOKBOOK, }); // ============================================================================ // wpnav_load_cookbook - Load full cookbook content by slug // ============================================================================ toolRegistry.register({ definition: { name: 'wpnav_load_cookbook', description: 'Load a cookbook by slug to get AI guidance for a specific plugin. Returns the full SKILL.md markdown body, parsed frontmatter metadata, and allowed-tools list. Use this when working with plugins like Elementor or WooCommerce.', inputSchema: { type: 'object', properties: { slug: { type: 'string', description: 'Plugin slug (e.g., "gutenberg", "elementor")', }, }, required: ['slug'], }, }, handler: async (args, context) => { const { slug } = args as { slug: string }; if (!slug || typeof slug !== 'string') { return { content: [ { type: 'text', text: JSON.stringify({ error: 'Missing required parameter: slug' }), }, ], isError: true, }; } const cookbook = getCookbook(slug); if (!cookbook) { return { content: [ { type: 'text', text: JSON.stringify({ error: `Cookbook not found: ${slug}`, available: discoverCookbooks().cookbooks.size, hint: 'Use wpnav_list_cookbooks to see available cookbooks', }), }, ], isError: true, }; } const skillCookbook = cookbook as LoadedSkillCookbook; const result = { slug: cookbook.plugin.slug, name: cookbook.plugin.name, version: cookbook.cookbook_version, source: cookbook.source, plugin: { min_version: cookbook.plugin.min_version || null, max_version: cookbook.plugin.max_version || null, }, // SKILL.md specific fields skill_body: skillCookbook.skillBody || null, frontmatter: skillCookbook.skillFrontmatter || null, allowed_tools: skillCookbook.allowedTools || [], }; return { content: [ { type: 'text', text: context.clampText(JSON.stringify(result, null, 2)), }, ], }; }, category: ToolCategory.COOKBOOK, }); // ============================================================================ // wpnav_match_cookbooks - Match cookbooks to active plugins // ============================================================================ toolRegistry.register({ definition: { name: 'wpnav_match_cookbooks', description: 'Match available cookbooks against active WordPress plugins. Fetches active plugins from WordPress and returns cookbooks that match, with version compatibility status. Use this to discover which plugin guidance is available for the current site.', inputSchema: { type: 'object', properties: { plugins: { type: 'array', items: { type: 'object', properties: { slug: { type: 'string' }, version: { type: 'string' }, name: { type: 'string' }, }, required: ['slug'], }, description: 'Optional: Provide plugin list directly. If not provided, fetches active plugins from WordPress.', }, }, required: [], }, }, handler: async (args, context) => { const { plugins: providedPlugins } = args as { plugins?: PluginInfo[] }; const { wpRequest } = context; let plugins: PluginInfo[]; if (providedPlugins && Array.isArray(providedPlugins)) { // Use provided plugin list plugins = providedPlugins; } else { // Fetch active plugins from WordPress try { const wpPlugins = await wpRequest('/wp/v2/plugins?status=active'); if (!Array.isArray(wpPlugins)) { return { content: [ { type: 'text', text: JSON.stringify({ error: 'Failed to fetch plugins from WordPress', hint: 'Provide plugins array directly or check WordPress connection', }), }, ], isError: true, }; } // Extract slug and version from WordPress plugin response // WordPress plugin slugs are in format "plugin-dir/plugin-file.php" plugins = wpPlugins.map((p: any) => { // Extract directory name as slug (e.g., "elementor/elementor.php" -> "elementor") const pluginFile = p.plugin || ''; const slug = pluginFile.split('/')[0] || pluginFile; return { slug, version: p.version, name: p.name?.rendered || p.name || slug, status: p.status, }; }); } catch (error) { return { content: [ { type: 'text', text: JSON.stringify({ error: 'Failed to fetch plugins from WordPress', details: error instanceof Error ? error.message : String(error), hint: 'Provide plugins array directly', }), }, ], isError: true, }; } } // Discover available cookbooks const { cookbooks, sources } = discoverCookbooks(); // Match cookbooks to plugins const matches = matchCookbooksToPlugins(cookbooks, plugins); // Build response const result = { total_plugins: plugins.length, matched_cookbooks: matches.length, matches: matches.map((m) => { const skillCookbook = m.cookbook as LoadedSkillCookbook; return { plugin: { slug: m.plugin.slug, version: m.plugin.version, name: m.plugin.name, }, cookbook: { slug: m.cookbook.plugin.slug, name: m.cookbook.plugin.name, version: m.cookbook.cookbook_version, source: m.cookbook.source, min_version: m.cookbook.plugin.min_version || null, max_version: m.cookbook.plugin.max_version || null, }, compatible: m.compatible, reason: m.reason || null, has_skill_body: !!skillCookbook.skillBody, allowed_tools: skillCookbook.allowedTools || [], }; }), unmatched_plugins: plugins .filter((p) => !matches.some((m) => m.plugin.slug === p.slug)) .map((p) => p.slug), available_cookbooks: { bundled: sources.bundled, project: sources.project, }, }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; }, category: ToolCategory.COOKBOOK, }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/littlebearapps/wp-navigator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server