From acdbfc6c5aef80459a4026d57e24b117ca5088b0 Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Mon, 17 Nov 2025 16:03:10 +0100 Subject: [PATCH 1/3] feat(mcp): nuxt ui agent as a mcp tool --- .../docs/1.getting-started/7.ai/1.mcp.md | 8 +++++ docs/server/api/search.ts | 27 +++++++++++++--- docs/server/routes/mcp.ts | 31 +++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) 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 de063f23c8..23d9f6c222 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 @@ -54,6 +54,14 @@ The Nuxt UI MCP server provides the following tools organized by category: - **`get_migration_guide`**: Retrieves version-specific migration guides and upgrade instructions +### Assistant Tools + +- **`ask_nuxt_ui_agent`**: Ask the Nuxt UI assistant for a concise direct answer (returns only the final content, no intermediate steps). + +::note{icon="i-lucide-info"} +Disabled when using the Agent from the Nuxt UI website. +:: + ## Available Prompts The Nuxt UI MCP server provides guided prompts for common workflows: diff --git a/docs/server/api/search.ts b/docs/server/api/search.ts index f7860f4211..e88c7ded4a 100644 --- a/docs/server/api/search.ts +++ b/docs/server/api/search.ts @@ -1,10 +1,10 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' -import { streamText, convertToModelMessages, stepCountIs } from 'ai' +import { streamText, convertToModelMessages, stepCountIs, generateText } from 'ai' import { experimental_createMCPClient } from '@ai-sdk/mcp' import { gateway } from '@ai-sdk/gateway' export default defineEventHandler(async (event) => { - const { messages } = await readBody(event) + const { messages, stream = true } = await readBody(event) const httpTransport = new StreamableHTTPClientTransport( new URL(import.meta.dev ? 'http://localhost:3000/mcp' : 'https://ui.nuxt.com/mcp') @@ -14,9 +14,9 @@ export default defineEventHandler(async (event) => { }) const tools = await httpClient.tools() - return streamText({ + const options = { model: gateway('anthropic/claude-sonnet-4.5'), - maxOutputTokens: 10000, + maxOutputTokens: 10_000, system: `You are a helpful assistant for Nuxt UI, a UI library for Nuxt and Vue. Use your knowledge base tools to search for relevant information before answering questions. Guidelines: @@ -41,6 +41,25 @@ Guidelines: `, messages: convertToModelMessages(messages), stopWhen: stepCountIs(6), + } + + // If not streaming, it's called from the MCP route as a tool. + if (stream === false) { + const { text } = await generateText({ + ...options, + tools, + }) + + await httpClient.close() + + return text + } + + // Remove the ask_nuxt_ui_agent tool to avoid infinite loops + delete tools['ask_nuxt_ui_agent'] + + return streamText({ + ...options, tools, onFinish: async () => { await httpClient.close() diff --git a/docs/server/routes/mcp.ts b/docs/server/routes/mcp.ts index dfc509195f..e6ddd1e972 100644 --- a/docs/server/routes/mcp.ts +++ b/docs/server/routes/mcp.ts @@ -5,6 +5,7 @@ import { z } from 'zod/v3' import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js' +import type { UIMessage } from 'ai' function createServer() { const server = new McpServer({ @@ -413,6 +414,36 @@ function createServer() { } ) + server.registerTool( + 'ask_nuxt_ui_agent', + { + title: 'Ask Nuxt UI Agent', + description: 'Asks the Nuxt UI agent a question', + inputSchema: { + // @ts-expect-error - need to wait for support for zod 4, this works correctly just a type mismatch from zod 3 to zod 4 (https://github.com/modelcontextprotocol/typescript-sdk/pull/869) + query: z.string().describe('The question to ask the agent') + } + }, + async (params: { query: string }) => { + const result = await $fetch('/api/search', { + method: 'POST', + timeout: 60_000, + body: { + stream: false, + messages: [{ + role: 'user', + parts: [{ type: 'text', text: params.query }] + }] satisfies Array> } + }) + + console.log(result) + + return { + content: [{ type: 'text' as const, text: result }] + } + } + ) + return server } From ba4fdb5f4b1134aa656aff6f7ccecbed6e771cda Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Mon, 17 Nov 2025 16:06:49 +0100 Subject: [PATCH 2/3] chore: lint --- docs/server/api/search.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/server/api/search.ts b/docs/server/api/search.ts index e88c7ded4a..bd86b33d28 100644 --- a/docs/server/api/search.ts +++ b/docs/server/api/search.ts @@ -40,14 +40,14 @@ Guidelines: - Format responses in a conversational way, not as documentation sections. `, messages: convertToModelMessages(messages), - stopWhen: stepCountIs(6), + stopWhen: stepCountIs(6) } // If not streaming, it's called from the MCP route as a tool. if (stream === false) { const { text } = await generateText({ ...options, - tools, + tools }) await httpClient.close() @@ -55,7 +55,7 @@ Guidelines: return text } - // Remove the ask_nuxt_ui_agent tool to avoid infinite loops + // Remove the ask_nuxt_ui_agent tool to avoid infinite loops delete tools['ask_nuxt_ui_agent'] return streamText({ From 7cd42a2e528b5e163291f94df523f25e6d392884 Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Mon, 17 Nov 2025 16:14:34 +0100 Subject: [PATCH 3/3] chore: remove ask nuxt ui agent tool when using api/search endpoint --- docs/server/api/search.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/server/api/search.ts b/docs/server/api/search.ts index bd86b33d28..ab4607c792 100644 --- a/docs/server/api/search.ts +++ b/docs/server/api/search.ts @@ -14,6 +14,9 @@ export default defineEventHandler(async (event) => { }) const tools = await httpClient.tools() + // Remove the ask_nuxt_ui_agent tool to avoid infinite loops + delete tools['ask_nuxt_ui_agent'] + const options = { model: gateway('anthropic/claude-sonnet-4.5'), maxOutputTokens: 10_000, @@ -55,9 +58,6 @@ Guidelines: return text } - // Remove the ask_nuxt_ui_agent tool to avoid infinite loops - delete tools['ask_nuxt_ui_agent'] - return streamText({ ...options, tools,