The Nuxt Agent is now in Beta on nuxt.com. We built it ourselves, plugged it into the site, and connected it to our docs, modules catalog, blog, and deployment guides. It replaces the Kapa AI widget we used to embed, and it does more than answer questions: it can help you get things done.
From a docs widget to a real agent
Kapa AI served us well as a docs Q&A widget for the past couple of years. It searched the docs and summarized an answer, and that was about it. But Nuxt is more than docs. There are modules, templates, deployment providers, a changelog, GitHub issues, playgrounds, and real navigation across the site.
We wanted an agent that could handle all of it, with the same design language as the rest of nuxt.com and the same content pipeline (Nuxt Content) we already run. So we built our own on top of the Nuxt MCP server we shipped last November.
Here's what the agent does today:
- Grounds answers in the official Nuxt docs and ecosystem data through MCP tools, not retrieved text chunks.
- Renders rich UI. Modules, templates, blog posts, hosting providers, and playground links come back as clickable cards instead of plain links.
- Streams everything as it runs, including tool call progress.
- Closes the loop. Feedback, voting, and issue reports flow into our internal tools so we can keep improving it.
Meet the agent
Three ways to talk to it
You can reach the agent three ways:
- A side panel pinned to the right on large screens, sliding over on smaller ones. Toggle it from the header or with ⌘I.
- An ask bar at the bottom of
/docsand/blogpages, so you can ask a question without leaving the page you're reading. - A full-screen chat at /chat for longer sessions.
It knows what page you're on
Ask "how do I customize this for my app?" while reading a doc and the agent automatically pulls that page in as context. A small "Agent is using this page" indicator in the footer makes it explicit, and you can dismiss it whenever you want.
Rich answers, not just text
Answers come back as more than text. Ask about a module and you get a module card with metadata pulled live from api.nuxt.com. Ask for starter templates and you get a row of clickable template cards. Ask about deployment and you get provider cards that link to the right guide. Need to reproduce a bug? The agent can generate a StackBlitz playground link from the conversation itself.
nuxt-ui-dashboard, nuxt-ui-saas, nuxt-ui-landing, nuxt-ui-chat, nuxt-ui-docs, nuxt-ui-portfolio) as cards you can open in one click.Feedback built in
Every assistant message has a thumbs up/down. If you want to share more (a missing piece, a wrong answer, an idea), the Report issue action opens a short form and creates a Linear ticket on our side with the conversation attached, so we have everything we need to follow up.
The agent can open that form itself, too. If you ask to "submit feedback" or "report an issue", or if the conversation starts to feel frustrated, the agent calls the report_issue tool and the same form opens inline. No button hunting required.
Conversations are saved and resume across reloads, so you can step away and pick up where you left off.
What the agent can actually do
The agent's grounding comes from the Nuxt MCP server, the same one Cursor, Claude Desktop and ChatGPT connect to. That means the Nuxt Agent and your local AI assistant share the same structured data: the official docs, the modules catalog, the blog, deployment guides, and the Nuxt repositories' changelog.
On top of that grounding, the agent has a small set of native tools that render as UI in the chat: module and template cards, hosting providers, blog posts, StackBlitz playground links, and a GitHub issue search across nuxt, nuxt-modules and nuxt-content. The agent reaches for that issue search first whenever you paste an error.
The web is available too, through Anthropic's native web_search, but only as a fallback for things the model couldn't reasonably know: a brand new Vue release, a freshly published RFC, recent ecosystem news. It's not a general-purpose search engine, and the system prompt is explicit about that. We never use web_search for anything that should be answered from the docs or the rest of the Nuxt content exposed through MCP.
Under the hood
The stack
A single Nitro handler at server/api/agent.post.ts drives everything. On the client, an @ai-sdk/vue Chat instance points at /api/agent. On the server, AI SDK v6 streamText calls claude-sonnet-4.6, with tools merged from our own MCP server (/mcp, same origin) and a few native ones (show_*, open_playground, report_issue). Chat state lives in Drizzle ORM, and evlog wraps the model for structured telemetry on tokens, cost, and tool calls.
UIMessage streaming with AI SDK v6
The whole pipeline runs on the AI SDK v6 UIMessage streaming model. The server side looks like this:
const stream = createUIMessageStream({
execute: async ({ writer }) => {
const result = streamText({
model: ai.wrap(MODEL),
maxOutputTokens: 4000,
stopWhen: stopWhenResponseComplete,
system: systemPrompt,
messages: await convertToModelMessages(messages),
tools: {
...mcpTools as ToolSet,
web_search: anthropic.tools.webSearch_20250305(),
search_github_issues: createSearchGitHubIssuesTool(event),
show_module: showModuleTool,
show_template: createShowTemplateTool(event),
show_blog_post: createShowBlogPostTool(event),
show_hosting: createShowHostingTool(event),
open_playground: openPlaygroundTool,
report_issue: reportIssueTool
},
experimental_telemetry: {
isEnabled: true,
integrations: [createEvlogIntegration(ai)]
}
})
writer.merge(result.toUIMessageStream({
sendSources: true,
originalMessages: messages,
onFinish: ({ messages: finalizedMessages }) => {
event.waitUntil(saveChat(finalizedMessages))
}
}))
}
})
Two details are worth pointing out. stopWhen: stopWhenResponseComplete is a custom predicate that ends the loop as soon as the model produces text without another tool call, with a hard ceiling at 10 steps. That avoids the classic "model loops forever on tools" failure mode. And event.waitUntil(saveChat(...)) pushes persistence outside the response lifecycle, so the stream finishes for the user right away while the chat row gets upserted in the background.
One MCP server, two consumers
The agent and external AI assistants talk to the same MCP server. That's the single most important architectural choice we made. The route handler opens an HTTP MCP client pointed at its own /mcp endpoint:
const httpClient = await createMCPClient({
transport: { type: 'http', url: `${getRequestURL(event).origin}${MCP_PATH}` }
})
const mcpTools = await httpClient.tools()
Those tools then get merged with the native UI tools into a single tools object passed to streamText. The payoff: any tool we add to the MCP server is immediately available to the Nuxt Agent and to every external assistant pointed at it, with no extra wiring. We wrote a separate post about how the MCP server itself works back in November.
Persistence, cost, and rate limiting
Chats live in a single agent_chats table, keyed by the x-chat-id header the client sends on every request. Drizzle's onConflictDoUpdate accumulates token usage, estimated cost, duration, and request count across the lifetime of a conversation. That gives us per-chat analytics for free.
Every request also goes through a small consumeAgentRateLimit helper before streaming starts. The current cap is 20 messages per day per IP fingerprint, which is enough for real use and low enough to prevent runaway costs if something starts looping.
A tight system prompt
A lot of agent quality comes from the prompt. A few rules carried most of the weight: reach for search_github_issues first whenever the user pastes an error, prefer show_module over get_module when the answer should render as a card, never call web_search unless the question is genuinely outside what the model could know, and never end a turn with only a tool call. Together those rules cut tool-spam and hallucinations enough that the agent stays focused on the task at hand.
What's next
The agent is launching in Beta. In the short term, we're focused on the basics: better answer quality, richer memory across turns, and cleaner source citations.
Further out, we want nuxt.com to feel more like an application than a static site. The next step there is user accounts. Each logged-in user gets their own session, their chat history saved and resumable across devices, and the Nuxt Agent becomes the first real building block of that more app-like nuxt.com.
We'd love your help shaping where it goes next. If the agent gets something wrong, or misses something you want, use the Report issue button inside the chat. It creates a ticket on our side with the full conversation attached, and we read every one.
The full source for nuxt.com is on GitHub, including the agent, the MCP server, and every tool covered above. The agent handler is at server/api/agent.post.ts, the native tools at server/utils/tools/, and the UI components at app/components/agent/. Take any of it as inspiration for your own apps. And if you want to build your own MCP server, the Nuxt MCP Toolkit gets you there in a few minutes.