Skip to content

How to build an MCP server with Nuxt

Add an MCP server to your Nuxt app with the Nuxt MCP Toolkit. Create tools, resources, and prompt templates that AI assistants like Cursor and VS Code can connect to.

8 min read
Last updated April 7, 2026

The Model Context Protocol (MCP) lets AI assistants call functions, read data, and use prompt templates from your application. With the Nuxt MCP Toolkit (@nuxtjs/mcp-toolkit), you can add an MCP server directly to your Nuxt app and expose your application's features to tools like Cursor, VS Code, and Claude Desktop. The module handles protocol details, input validation, and auto-discovery so you can focus on building what your MCP server does.

This guide walks you through installing the module, creating tools, resources, and prompt templates, connecting your IDE, debugging with the built-in MCP Inspector, and adding authentication to protect your endpoints.

Before you begin, make sure you have:

  • A Nuxt 3.x or 4.x project.
  • Node.js 18 or higher.
  • The zod package installed in your project.

If you're working with an AI coding agent like Claude Code or Cursor, you can scaffold the starter project and hand off implementation with this prompt:

AI Prompt
You are an expert at creating MCP servers.
I'm using the Nuxt MCP starter from nuxt-modules/mcp-toolkit (apps/mcp-starter). Scaffold it with npx giget@latest gh:nuxt-modules/mcp-toolkit/apps/mcp-starter my-app, then pnpm install and pnpm dev.
The documentation can be found at https://mcp-toolkit.nuxt.dev. Use the 'site:' operator when calling your Web Search tool, so you only discover information from the docs.
Tools, resources, and prompts go under server/mcp/. Use defineMcpTool, defineMcpResource, and defineMcpPrompt from @nuxtjs/mcp-toolkit/server, with Zod for schemas — same style as the existing files.
Once my project is ready, ask me what I want this MCP server to do, then please help me implement it.

Add the agent skill to teach your AI coding agent about the toolkit:

Terminal
npx skills add https://mcp-toolkit.nuxt.dev

Use the add-mcp CLI to add the documentation MCP to your agent:

Terminal
npx add-mcp https://mcp-toolkit.nuxt.dev/mcp

The Nuxt MCP Toolkit adds an HTTP endpoint (by default at /mcp) to your Nuxt application. AI clients connect to this endpoint using the MCP protocol. The module scans your server/mcp/ directory for tool, resource, and prompt definitions, then registers them automatically. When an AI assistant sends a request, the module validates inputs with Zod, runs your handler, and returns the result.

Your project structure looks like this after setup:

File Tree
your-project/
├── server/
│ └── mcp/
│ ├── tools/ # Functions that AI assistants can call
│ ├── resources/ # Read-only data for AI context
│ └── prompts/ # Reusable prompt templates
├── nuxt.config.ts
└── package.json

Files placed in these directories are discovered and registered without any manual wiring.

Run the automatic installer, which adds the package and updates your Nuxt config:

Terminal
npx nuxt module add mcp-toolkit

If you prefer manual setup, install @nuxtjs/mcp-toolkit and its peer dependency zod:

Terminal
pnpm add @nuxtjs/mcp-toolkit zod

Then add the module to your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/mcp-toolkit'],
})

The module works with defaults that cover most cases. To customize the server name, endpoint route, or file directory, pass options under the mcp key:

nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/mcp-toolkit'],
mcp: {
name: 'My MCP Server',
route: '/mcp',
dir: 'mcp',
},
})

Here's what each option does:

OptionTypeDefaultDescription
enabledbooleantrueEnable or disable the MCP server
routestring'/mcp'HTTP route where the MCP server is accessible
namestring''Server name used in the MCP protocol handshake
versionstring'1.0.0'Server version (semantic versioning)
dirstring'mcp'Base directory for definitions, relative to server/
browserRedirectstring'/'URL to redirect browsers visiting the MCP endpoint
autoImportsbooleantrueAuto-import defineMcpTool, defineMcpResource, and other helpers
sessionsboolean | objectfalseEnable stateful session management for SSE streaming and per-session state

Tools are functions that AI assistants can call to perform actions or retrieve information. Each tool validates its input with Zod and returns a result.

Create a file in server/mcp/tools/:

server/mcp/tools/echo.ts
import { z } from 'zod'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpTool({
name: 'echo',
description: 'Echo back a message',
inputSchema: {
message: z.string().describe('The message to echo back'),
},
handler: async ({ message }) => `Echo: ${message}`,
})

The name and title fields are optional. If you omit them, the module generates both from the filename. A file named list-users.ts becomes name: 'list-users' and title: 'List Users'.

Your handler receives the validated input object and can return a string, number, boolean, object, or a full CallToolResult. For error cases, throw an error with createError from h3:

server/mcp/tools/user.ts
import { z } from 'zod'
import { createError } from 'h3'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpTool({
description: 'Look up a user by ID',
inputSchema: {
userId: z.string().describe('The user ID to look up'),
},
handler: async ({ userId }) => {
const user = await db.query.users.findFirst({
where: (users, { eq }) => eq(users.id, userId),
})
if (!user) {
throw createError({ statusCode: 404, message: 'User not found' })
}
return { name: user.name, email: user.email }
},
})

Resources expose read-only data that AI assistants can use as context. Unlike tools, resources are application-driven: the host application (or user) decides when to include resource content in a conversation, not the AI model.

The quickest way to expose a file is with the file property:

server/mcp/resources/readme.ts
import { defineMcpResource } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpResource({
name: 'readme',
description: 'Project README file',
file: 'README.md',
})

The module handles URI generation, MIME type detection, and file reading automatically.

For custom data sources, define a uri and handler manually:

server/mcp/resources/db.ts
import { defineMcpResource } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpResource({
name: 'db-schema',
description: 'Database schema definition',
uri: 'app://schema/database',
mimeType: 'application/json',
handler: async (uri) => {
const schema = await getDbSchema()
return {
contents: [{
uri: uri.toString(),
text: JSON.stringify(schema, null, 2),
mimeType: 'application/json',
}],
}
},
})

Use resources for files, configs, database schemas, and logs. Use tools when the AI needs to perform an action or modify state.

Prompts are reusable message templates that appear in your IDE when you type / in the chat. They standardize how you interact with AI assistants for common tasks.

server/mcp/prompts/code-review.ts
import { z } from 'zod'
import { defineMcpPrompt } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpPrompt({
name: 'review-code',
title: 'Code Review',
description: 'Generate a code review prompt',
inputSchema: {
code: z.string().describe('Code to review'),
language: z.string().describe('Programming language'),
focus: z.enum(['performance', 'security', 'style', 'all']).default('all'),
},
handler: async ({ code, language, focus }) => {
const focusArea = focus === 'all'
? 'performance, security, and style'
: focus
return `Please review this ${language} code focusing on ${focusArea}:\n\n\`\`\`${language}\n${code}\n\`\`\``
},
})

Handlers can return a plain string (auto-wrapped as a user message) or a full GetPromptResult with a messages array for multi-turn conversations. Prompt arguments must be strings since the MCP protocol requires it.

Start your Nuxt dev server, then connect your AI assistant to the MCP endpoint.

Use the add-mcp CLI to add your MCP to Claude Code, OpenCode, and more.

Terminal
npx add-mcp http://localhost:3000/mcp

Add the server to ~/.cursor/mcp.json:

.cursor/mcp.json
{
"mcpServers": {
"my-nuxt-app": {
"url": "http://localhost:3000/mcp"
}
}
}

Add the server to .vscode/mcp.json in your project:

.vscode/mcp.json
{
"servers": {
"my-nuxt-app": {
"type": "http",
"url": "http://localhost:3000/mcp"
}
}
}

Replace my-nuxt-app with your project name and update the URL if you changed the route or port.

The module includes an integration with the MCP Inspector, a visual debugging tool built into Nuxt DevTools. To use it:

  1. Enable DevTools in your Nuxt config:
nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/mcp-toolkit'],
devtools: { enabled: true },
})
  1. Open Nuxt DevTools and navigate to the MCP Inspector tab under the Server section.
  2. Click Launch Inspector to browse your tools, resources, and prompts, test them with custom parameters, and view request/response history.

The inspector connects to your MCP server automatically with the correct configuration.

To secure your MCP endpoints, add Bearer token validation through the module's middleware system.

server/mcp/index.ts
import { getHeader } from 'h3'
export default defineMcpHandler({
middleware: async (event) => {
const authHeader = getHeader(event, 'authorization')
if (!authHeader?.startsWith('Bearer ')) {
return
}
const token = authHeader.slice(7)
const user = await validateToken(token)
if (user) {
event.context.user = user
event.context.userId = user.id
}
},
})

MCP middleware should not throw errors when authentication is missing or invalid. Throwing a 401 causes MCP clients to enter OAuth discovery mode, looking for .well-known/oauth-* endpoints that don't exist. Instead, set context when auth succeeds and let requests continue otherwise. Your tools can then check for user context:

server/mcp/tools/todo.ts
import { useEvent, createError } from 'h3'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpTool({
description: 'List todos for the authenticated user',
inputSchema: {},
handler: async () => {
const event = useEvent()
const userId = event.context.userId as string
if (!userId) {
throw createError({
statusCode: 401,
message: 'Authentication required. Provide a valid API key.',
})
}
return await db.query.todos.findMany({
where: (todos, { eq }) => eq(todos.userId, userId),
})
},
})

To use useEvent() inside tool handlers, enable asyncContext in your Nuxt config:

nuxt.config.ts
export default defineNuxtConfig({
nitro: {
experimental: {
asyncContext: true,
},
},
})

Configure MCP clients to send the token in request headers (e.g., Cursor):

~/.cursor/mcp.json
{
"mcpServers": {
"my-app": {
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer your_api_key_here"
}
}
}
}

If you visit the MCP endpoint in a browser, you'll be redirected to the browserRedirect URL (by default /). MCP endpoints are designed for programmatic access by AI clients, not browsers. Use the MCP Inspector or your IDE to interact with the server.

Confirm that autoImports isn't set to false in your MCP config. If you've disabled auto-imports, import helpers explicitly:

import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'

Make sure your tool files are in the correct directory (server/mcp/tools/ by default) and that each file has a default export using defineMcpTool. Restart your dev server after adding new files.

Enable asyncContext in your Nitro configuration. Without it, useEvent() won't have access to the current request context inside async handlers.


Build knowledge agents without embeddings

Learn how you can build a Nuxt-based knowledge agent with Vercel Sandbox, Chat SDK, and AI SDK. No embeddings, no vector DB.

Learn more

Was this helpful?

supported.

Read related documentation

Explore more Nuxt guides

No related guides available.