diff --git a/docs/content/docs/1.getting-started/7.ai/1.mcp.md b/docs/content/docs/1.getting-started/7.ai/1.mcp.md index e96aead498..ec06adbef7 100644 --- a/docs/content/docs/1.getting-started/7.ai/1.mcp.md +++ b/docs/content/docs/1.getting-started/7.ai/1.mcp.md @@ -28,33 +28,47 @@ The Nuxt UI MCP server provides the following tools organized by category: ### Search Tools -- **`search_components`**: Search components by name, description, or category. With no params, lists all components -- **`search_composables`**: Search composables by name or description. With no params, lists all composables -- **`search_icons`**: Search for icons across Iconify collections (defaults to `lucide`). Returns icon names in the `i-{prefix}-{name}` format used by Nuxt UI +- **`search-components`**: Search components by name, description, or category. With no params, lists all components +- **`search-composables`**: Search composables by name or description. With no params, lists all composables +- **`search-icons`**: Search for icons across Iconify collections (defaults to `lucide`). Returns icon names in the `i-{prefix}-{name}` format used by Nuxt UI ### Component Tools -- **`get_component`**: Retrieves component documentation and details. Supports a `sections` parameter (`usage`, `examples`, `api`, `theme`, `changelog`) to fetch only specific parts and reduce response size -- **`get_component_metadata`**: Retrieves detailed metadata for a component including props, slots, and events (lightweight, no documentation content) +- **`get-component`**: Retrieves component documentation and details. Supports a `sections` parameter (`usage`, `examples`, `api`, `theme`, `changelog`) to fetch only specific parts and reduce response size +- **`get-component-metadata`**: Retrieves detailed metadata for a component including props, slots, and events (lightweight, no documentation content) ### Documentation Tools -- **`search_documentation`**: Search documentation pages by title, description, or section. With no params, lists all pages. Use `section` to filter (e.g., `"getting-started"`, `"components"`) -- **`get_documentation_page`**: Retrieves documentation page content by URL path. Supports a `headings` parameter to fetch only specific h2 sections (e.g., `["Usage", "API"]`) and reduce response size +- **`search-documentation`**: Search documentation pages by title, description, or section. With no params, lists all pages. Use `section` to filter (e.g., `"getting-started"`, `"components"`) +- **`get-documentation-page`**: Retrieves documentation page content by URL path. Supports a `headings` parameter to fetch only specific h2 sections (e.g., `["Usage", "API"]`) and reduce response size ### Template Tools -- **`list_templates`**: Lists all available Nuxt UI templates with optional framework filtering -- **`get_template`**: Retrieves template details and setup instructions +- **`list-templates`**: Lists all available Nuxt UI templates with optional framework filtering +- **`get-template`**: Retrieves template details and setup instructions ### Example Tools -- **`list_examples`**: Lists all available UI examples and code demonstrations -- **`get_example`**: Retrieves specific UI example implementation code and details +- **`list-examples`**: Lists all available UI examples and code demonstrations +- **`get-example`**: Retrieves specific UI example implementation code and details ### Migration Tools -- **`get_migration_guide`**: Retrieves version-specific migration guides and upgrade instructions +- **`get-migration-guide`**: Retrieves version-specific migration guides and upgrade instructions + +## Limit available tools + +Set the optional `X-MCP-Tools` HTTP header to a comma-separated list of tool names to expose only the tools your assistant needs. This helps to reduce the context defined in your assistant. + +Use the exact kebab-case MCP tool names, for example: + +``` +{ + "X-MCP-Tools": "search-components,get-component" +} +``` + +No header exposes all tools, an empty header exposes no tools, and an unknown tool name returns a configuration error. ## Available Prompts @@ -319,12 +333,17 @@ For more details, see [Adding MCP servers for GitHub Copilot CLI](https://docs.g "servers": { "nuxt-ui": { "type": "http", - "url": "https://ui.nuxt.com/mcp" + "url": "https://ui.nuxt.com/mcp", + "headers": { + "X-MCP-Tools": "search-components,get-component" // Optional: Limit available tools to reduce context + } } } } ``` +Remove the `headers` entry to make all Nuxt UI MCP tools available. + ### Windsurf #### Setup Instructions: diff --git a/docs/server/mcp/index.ts b/docs/server/mcp/index.ts new file mode 100644 index 0000000000..a89fa2a799 --- /dev/null +++ b/docs/server/mcp/index.ts @@ -0,0 +1,58 @@ +import { defineMcpHandler, getMcpTools } from '@nuxtjs/mcp-toolkit/server' + +export default defineMcpHandler({ + async tools(event) { + const tools = await getMcpTools({ event }) + const requestedTools = getHeader(event, 'x-mcp-tools') + + if (requestedTools === undefined) { + return tools + } + + const requestedToolNames = getRequestedToolNames(requestedTools) + const availableToolNames = getAvailableToolNames(tools) + + const unknownNames = requestedToolNames.filter(requestedToolName => !availableToolNames.has(requestedToolName)) + if (unknownNames.length) { + throw createError({ + statusCode: 400, + statusMessage: `Unknown MCP tool${unknownNames.length > 1 ? 's' : ''}: ${unknownNames.join(', ')}` + }) + } + + return tools.filter(tool => requestedToolNames.includes(getToolName(tool) || '')) + } +}) + +function getAvailableToolNames(tools: Awaited>) { + const names = new Set() + + for (const tool of tools) { + const name = getToolName(tool) + if (name) { + names.add(name) + } + } + + return names +} + +function getToolName(tool: Awaited>[number]) { + if (tool.name) { + return tool.name + } + + const filename = tool._meta?.filename + + if (typeof filename !== 'string') { + return + } + + return filename.replace('.ts', '').toLowerCase() +} + +function getRequestedToolNames(requestedTools: string) { + return Array.from( + new Set(requestedTools.split(',').map((name: string) => name.trim()).filter((name: string) => Boolean(name))) + ) +}