Article·  

Introducing the Nuxt Agent

A first-party AI agent on nuxt.com, grounded in the official documentation and the Nuxt ecosystem, replacing our Kapa AI widget with a deeply integrated, fully agentic experience.
Hugo Richard

Hugo Richard

@hugorcd

Sébastien Chopin

Sébastien Chopin

@Atinux

Today we're rolling out the Nuxt Agent (Beta) on nuxt.com: a first-party AI agent that lives right inside the site, deeply wired into our documentation, modules catalog, blog, deployment guides, and the wider Nuxt ecosystem. It replaces the third-party Kapa AI widget we used to ship, and takes the experience from "answer a question" to "help me get things done".

Open the agent anywhere on nuxt.com with ⌘I (or Ctrl+I), or jump straight into the full-screen experience at /chat.

From a docs widget to a first-party agent

For the past couple of years, the Kapa AI widget served as our docs Q&A experience. It did one thing well: search the docs and summarize an answer. But the Nuxt experience is more than just docs. It's modules, templates, deployment providers, the changelog, GitHub issues, playgrounds, and real navigation into the site.

We wanted something that could do all of that in one place, with the same design language as the rest of nuxt.com, the same content pipeline (Nuxt Content), and the same infrastructure we already run. So we built our own, on top of the Nuxt MCP server we shipped last November.

The result is an agent that:

  • Grounds every answer in the official Nuxt documentation and ecosystem data, through structured MCP tools rather than retrieved text chunks.
  • Renders rich UI: modules, templates, blog posts, hosting providers, and playground links come back as cards you can click, not as plain links buried in prose.
  • Streams everything end to end, with tool call progress visible as it runs.
  • Owns the loop: feedback, voting, and issue reporting flow directly into our internal tools so we can improve the agent over time.

Meet the agent

Three ways to talk to it

The agent is available everywhere on nuxt.com:

  • As a side panel pinned to the right on large screens, slide-over on smaller ones. Toggle it from the header or with ⌘I.
  • As a floating ask bar on /docs and /blog pages: a bottom-centered "Ask anything…" input that lets you jump into the agent without taking your eyes off the page.
  • As a full-screen chat at /chat for longer sessions.

It knows what page you're on

If you're reading a doc and ask "how do I customize this for my app?", the agent already knows which page you mean and pulls it in as context. A small "Agent is using this page" indicator in the footer makes it explicit, and you can dismiss it any time.

Rich answers, not just text

The agent doesn't just talk — it shows. When you ask about a module, it renders a module card with metadata pulled live from api.nuxt.com. Ask for starter templates and you'll get clickable template cards. Ask about deployment and you'll get provider cards linking to the right guide. Need to reproduce a bug? The agent can generate a StackBlitz playground link straight from the conversation.

Try asking: "Show me official starter templates" — you'll get the full lineup (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 to let us know how it went. And if you'd like 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 also trigger that flow on its own. If you ask to "submit feedback" or "report an issue", or if the conversation tilts toward frustration, the agent calls the report_issue tool and the same form opens inline — no need to go hunt for a button.

Conversations are persisted and resumed 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 our own Nuxt MCP server — the exact same one Cursor, Claude Desktop or ChatGPT connect to. That means the Nuxt Agent and your local AI assistant are looking at the same structured data: the official docs, the modules catalog, the blog, deployment guides, and the Nuxt repositories' changelog.

On top of that grounding, it has a set of native tools that render as UI inside 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 this one first when you paste an error).

The web is available too, via Anthropic's native web_search, but strictly as a fallback for things the model couldn't possibly know — the latest Vue release, a freshly published RFC, recent ecosystem news. It's not a general-purpose search engine, and the system prompt is explicit about it: 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. The client is an @ai-sdk/vue Chat instance pointed at /api/agent; the server uses AI SDK v6 streamText against claude-sonnet-4.6, with tools merged from our own MCP server (/mcp, same origin) and a handful of native ones (show_*, open_playground, report_issue…). Chat state is persisted with Drizzle ORM, and evlog wraps the model for structured telemetry — tokens, cost, tool calls.

UIMessage streaming with AI SDK v6

The whole pipeline is built on the AI SDK v6 UIMessage streaming model. On the server:

server/api/agent.post.ts
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))
      }
    }))
  }
})

The two details worth highlighting: stopWhen: stopWhenResponseComplete is a custom predicate that ends the loop as soon as the model produces text without a new tool call (with a hard ceiling of 10 steps), avoiding the classic "model loops forever on tools" failure mode. And event.waitUntil(saveChat(...)) pushes persistence outside the response lifecycle — the stream finishes for the user immediately, the chat row is upserted asynchronously.

One MCP server, two consumers

The most important architectural decision is that the agent and external AI assistants talk to the same MCP server. The route handler opens an HTTP MCP client pointed at its own /mcp endpoint:

server/api/agent.post.ts
const mcpUrl = import.meta.dev
  ? `http://localhost:3000${MCP_PATH}`
  : `${getRequestURL(event).origin}${MCP_PATH}`

const httpClient = await createMCPClient({
  transport: { type: 'http', url: mcpUrl }
})
const mcpTools = await httpClient.tools()

Those tools are then merged with the native UI tools into a single tools object passed to streamText. The nice consequence: any new tool we add to the MCP server is instantly available to the Nuxt Agent — and to every external assistant pointed at it — without any extra wiring. If you want to dig into how the MCP server itself is built, we wrote a dedicated blog post back in November.

Persistence, cost, and rate limiting

Chats are persisted in a single agent_chats table keyed by the x-chat-id header the client sends on every request. We use Drizzle's onConflictDoUpdate to accumulate token usage, estimated cost, duration, and request count across the lifetime of a conversation — per-chat analytics at zero runtime cost.

To keep things sustainable, every request goes through a small consumeAgentRateLimit helper before streaming starts. The current cap is 20 messages per day per IP fingerprint — enough for real use, low enough to prevent runaway costs from accidental loops.

A tight system prompt

A lot of agent quality comes from the prompt. The most impactful rules: always reach for search_github_issues first when the user pastes an error, prefer show_module over get_module for anything that 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 just a tool call. Together they cut tool-spam and hallucinations down dramatically and make the agent feel like it actually knows what it's doing.

What's next

The agent is launching in Beta. The near-term focus is on the basics: improving answer quality, giving the agent richer memory across turns, and tightening source citations.

Looking a bit further, we want nuxt.com to become more of an actual application than a static site. The next step there is user accounts: each logged-in user with their own session, their full chat history saved and resumable across devices, and the Nuxt Agent as the first real building block of that more app-like nuxt.com.

We'd love your help shaping it. If the agent gets something wrong, or misses a feature you'd want, use the Report issue button right inside the chat — it creates a ticket on our side with the full conversation attached, and we read every one.

Try the Nuxt Agent now: open it with ⌘I, use the ask bar on any docs page, or head to /chat for the full-screen experience.

The complete source code for nuxt.com — including the agent, the MCP server, and all the tools described above — is available on GitHub. The agent handler lives at server/api/agent.post.ts, the native tools at server/utils/tools/, and the UI components at app/components/agent/. Feel free to use it as inspiration for your own apps — and if you want to build your own MCP server, the Nuxt MCP Toolkit makes it a few-minute job.