diff --git a/server/mcp/tools/docs/get-documentation-page.ts b/server/mcp/tools/docs/get-documentation-page.ts index 69017e886..bfad4a51e 100644 --- a/server/mcp/tools/docs/get-documentation-page.ts +++ b/server/mcp/tools/docs/get-documentation-page.ts @@ -5,30 +5,35 @@ export default defineMcpTool({ WHEN TO USE: Use this tool when you know the EXACT path to a documentation page. Common use cases: - User asks for a specific page: "Show me the introduction page" → /docs/4.x/getting-started/introduction -- User asks about a known topic with a dedicated page - You found a relevant path from list_documentation_pages and want the full content -WHEN NOT TO USE: If you don't know the exact path and need to search/explore, use list_documentation_pages first. +WHEN NOT TO USE: If you don't know the exact path and need to search/explore, use list_documentation_pages first (always pass a search term). COMMON PAGES (Nuxt 4.x): Getting Started: - "/docs/4.x/getting-started/introduction" - main intro - "/docs/4.x/getting-started/installation" - setup - "/docs/4.x/getting-started/upgrade" - migration from v3 +- "/docs/4.x/getting-started/error-handling" - error handling Core Concepts: - "/docs/4.x/guide/concepts/rendering" - SSR/CSR/SSG modes - "/docs/4.x/guide/concepts/auto-imports" - auto-imports - "/docs/4.x/guide/concepts/server-engine" - server features -Directory Structure: -- "/docs/4.x/guide/directory-structure/composables" - composables -- "/docs/4.x/guide/directory-structure/components" - components -- "/docs/4.x/guide/directory-structure/pages" - routing +Directory Structure (Nuxt 4 — app dirs live under directory-structure/app/): +- "/docs/4.x/directory-structure" - overview +- "/docs/4.x/directory-structure/app/composables" - composables +- "/docs/4.x/directory-structure/app/components" - components +- "/docs/4.x/directory-structure/app/pages" - routing +- "/docs/4.x/directory-structure/app/middleware" - route middleware +- "/docs/4.x/directory-structure/app/plugins" - plugins +- "/docs/4.x/directory-structure/app/layouts" - layouts +- "/docs/4.x/directory-structure/app/utils" - auto-imported utils +- "/docs/4.x/directory-structure/server" - server directory -Common Issues: -- "/docs/4.x/guide/going-further/debugging" - debugging -- "/docs/4.x/guide/going-further/error-handling" - errors`, +Debugging: +- "/docs/4.x/guide/going-further/debugging" - debugging`, inputSchema: { path: z.string().describe('The path to the documentation page (e.g., /docs/4.x/getting-started/introduction)'), sections: z.array(z.string()).optional().describe('Specific h2 section titles to return (e.g., ["Usage", "API"]). If omitted, returns full documentation.') @@ -39,7 +44,8 @@ Common Issues: }, inputExamples: [ { path: '/docs/4.x/getting-started/introduction' }, - { path: '/docs/4.x/guide/directory-structure/components', sections: ['Usage'] } + { path: '/docs/4.x/directory-structure/app/components', sections: ['Usage'] }, + { path: '/docs/4.x/directory-structure' } ], cache: '30m', async handler({ path, sections }) { diff --git a/server/utils/mcp.ts b/server/utils/mcp.ts index 37092d5ba..9252b7bc1 100644 --- a/server/utils/mcp.ts +++ b/server/utils/mcp.ts @@ -5,6 +5,68 @@ import { queryCollection } from '@nuxt/content/server' type CollectionName = Parameters[1] +/** App-scoped dirs that live under `directory-structure/app/` in Nuxt 4+ docs. */ +const APP_SCOPED_DIRECTORIES = new Set([ + 'assets', + 'components', + 'composables', + 'layouts', + 'middleware', + 'pages', + 'plugins', + 'utils', + 'app', + 'app-config', + 'error' +]) + +/** + * Maps legacy documentation paths to current content paths. + * + * nuxt.com route rules redirect old URLs in the browser, but MCP resolves + * content by path via `queryCollection` and does not follow HTTP redirects. + */ +export function resolveDocumentationPath(path: string): string { + if (path === '/docs/4.x/guide/going-further/error-handling' || path === '/docs/5.x/guide/going-further/error-handling') { + return path.replace('/guide/going-further/error-handling', '/getting-started/error-handling') + } + + const resolved = path.replace( + /^(\/docs\/(?:3\.x|4\.x|5\.x))\/guide\/directory-structure$/, + '$1/directory-structure' + ) + + const legacyMatch = resolved.match(/^(\/docs\/(3\.x|4\.x|5\.x))\/guide\/directory-structure\/(.+)$/) + if (!legacyMatch) { + return resolved + } + + const prefix = legacyMatch[1] + const version = legacyMatch[2] + const rest = legacyMatch[3] + if (!prefix || !version || !rest) { + return resolved + } + + if (version === '3.x') { + return rest.startsWith('app/') + ? `${prefix}/directory-structure/${rest.slice(4)}` + : `${prefix}/directory-structure/${rest}` + } + + if (rest.startsWith('app/') || rest.startsWith('guide/')) { + return `${prefix}/directory-structure/${rest.replace(/^guide\//, '')}` + } + + const topLevel = rest.split('/')[0]! + if (APP_SCOPED_DIRECTORIES.has(topLevel)) { + // `rest === 'app'` → `.../app/app` (canonical content path for app.vue) + return `${prefix}/directory-structure/app/${rest}` + } + + return `${prefix}/directory-structure/${rest}` +} + /** * Fetches a page from a known content collection and renders it as markdown. * @@ -19,7 +81,8 @@ export async function fetchPageMarkdown( collection: CollectionName, path: string ): Promise { - const page = await queryCollection(event, collection).path(path).first() as + const resolvedPath = resolveDocumentationPath(path) + const page = await queryCollection(event, collection).path(resolvedPath).first() as | { title?: string, description?: string, body?: { value?: MinimarkNode[] }, links?: unknown[], meta?: { links?: unknown[] } } | null