Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions server/mcp/tools/docs/get-documentation-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.')
Expand All @@ -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 }) {
Expand Down
65 changes: 64 additions & 1 deletion server/utils/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,68 @@ import { queryCollection } from '@nuxt/content/server'

type CollectionName = Parameters<typeof queryCollection>[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}`
}
Comment thread
HugoRCD marked this conversation as resolved.

return `${prefix}/directory-structure/${rest}`
}

/**
* Fetches a page from a known content collection and renders it as markdown.
*
Expand All @@ -19,7 +81,8 @@ export async function fetchPageMarkdown(
collection: CollectionName,
path: string
): Promise<string | null> {
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

Expand Down