Skip to content

Commit dedbf0a

Browse files
committed
docs(mcp): call tools directly instead of self-referential HTTP
1 parent b7874b2 commit dedbf0a

1 file changed

Lines changed: 34 additions & 24 deletions

File tree

docs/server/api/ai.post.ts

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
11
import { streamText, convertToModelMessages, smoothStream, jsonSchema, stepCountIs } from 'ai'
22
import type { AnthropicLanguageModelOptions } from '@ai-sdk/anthropic'
3-
import { createMCPClient } from '@ai-sdk/mcp'
43
import { gateway } from '@ai-sdk/gateway'
4+
import { z } from 'zod'
5+
// @ts-expect-error virtual module generated by @nuxtjs/mcp-toolkit
6+
import { tools as mcpToolDefinitions } from '#nuxt-mcp-toolkit/tools.mjs'
57
import { themeIcons, cssVariableDefaults } from '../../app/utils/theme'
68

9+
function mcpToolsToAiTools() {
10+
const aiTools: Record<string, { description: string, inputSchema: ReturnType<typeof jsonSchema>, execute: (args: any) => Promise<any> }> = {}
11+
12+
for (const def of mcpToolDefinitions as any[]) {
13+
const filename = def._meta?.filename as string | undefined
14+
const name = def.name || (filename
15+
? filename.replace(/\.(ts|js|mts|mjs)$/, '').replace(/([a-z0-9])([A-Z])/g, '$1-$2').replace(/[_\s]+/g, '-').toLowerCase()
16+
: null)
17+
if (!name) continue
18+
19+
const schema = def.inputSchema
20+
? z.toJSONSchema(z.object(def.inputSchema)) as Record<string, unknown>
21+
: { type: 'object' as const, properties: {} }
22+
23+
aiTools[name] = {
24+
description: def.description || '',
25+
inputSchema: jsonSchema(schema),
26+
execute: async (args: any) => {
27+
try {
28+
return await def.handler(args, {})
29+
} catch (error: any) {
30+
return { error: error.statusCode ? `[${error.statusCode}] ${error.message}` : error.message || String(error) }
31+
}
32+
}
33+
}
34+
}
35+
36+
return aiTools
37+
}
38+
739
const applyTheme = {
840
description: 'Apply theme settings live on the docs site. Call this when users ask to change colors, radius, font, or other theme properties. Only include properties that changed.',
941
inputSchema: jsonSchema<Record<string, any>>({
@@ -298,30 +330,11 @@ export default defineEventHandler(async (event) => {
298330
}
299331
}
300332

301-
let httpClient
302-
let mcpTools
303-
try {
304-
const mcpUrl = import.meta.dev
305-
? new URL('/mcp', getRequestURL(event).origin).href
306-
: 'https://ui.nuxt.com/mcp'
307-
httpClient = await createMCPClient({
308-
transport: { type: 'http', url: mcpUrl }
309-
})
310-
mcpTools = await httpClient.tools()
311-
} catch (error) {
312-
console.error('MCP client error:', error)
313-
314-
throw createError({
315-
statusCode: 503,
316-
message: 'Unable to connect to the documentation service. Please try again later.'
317-
})
318-
}
333+
const mcpTools = mcpToolsToAiTools()
319334

320335
const abortController = new AbortController()
321336
event.node.req.on('close', () => abortController.abort())
322337

323-
const closeMcp = () => event.waitUntil(httpClient?.close())
324-
325338
const system = `You are a helpful assistant for Nuxt UI, a UI library for Nuxt and Vue. Nuxt UI includes \`@nuxt/fonts\` and \`@nuxt/icon\` as built-in dependencies — never tell users to install them separately. Use your knowledge base tools to search for relevant information before answering questions.
326339
327340
The user is using **${framework === 'vue' ? 'Vue' : 'Nuxt'}**. Tailor your answers accordingly — ${framework === 'vue' ? 'use the Vite plugin setup, Vue Router, and vite.config.ts instead of Nuxt-specific features like modules or app.config.ts. IMPORTANT: The Vite plugin auto-imports components and Nuxt UI composables, but Vue core APIs and VueUse must be explicitly imported — always include these in code examples (e.g. `import { ref, computed } from \'vue\'`, `import { useColorMode } from \'@vueuse/core\'`).' : 'use Nuxt modules, auto-imports, app.config.ts, and other Nuxt-specific features. Nuxt auto-imports Vue APIs (ref, computed, etc.), composables, and components — do not include these imports in code examples.'}
@@ -376,11 +389,8 @@ Guidelines:
376389
resetTheme,
377390
getComponentTheme
378391
},
379-
onFinish: closeMcp,
380-
onAbort: closeMcp,
381392
onError: (error) => {
382393
console.error('streamText error:', error)
383-
closeMcp()
384394
}
385395
}).toUIMessageStreamResponse()
386396
})

0 commit comments

Comments
 (0)