Nuxt 4 template for building production-ready MCP servers. Learn MCP patterns by querying this server itself.
A Nuxt MCP server that teaches you how to build MCP servers with Nuxt. Query it to:
- Get working code examples for MCP patterns
- Generate complete Nuxt MCP projects
- Debug common MCP setup issues
- Learn best practices through guided workflows
- Node.js 18+ (
fnm use 24) - pnpm
- OpenAI API key (for evaluations)
pnpm install
cp .env.example .env # Add OPENAI_API_KEY
pnpm devMCP server runs at: http://localhost:3000/mcp
npx @modelcontextprotocol/inspector http://localhost:3000/mcp # MCP Inspector
pnpm eval # Run evaluations
pnpm eval:ui # View resultsClean separation: Protocol → Handlers → Data
server/
routes/mcp.ts # MCP protocol (tools, resources, prompts)
api/mcp/
get-pattern.get.ts # Pattern examples handler
create-nuxt-project.get.ts # Project generator
debug-setup.get.ts # Debug helper
utils/
schemas.ts # Zod schemas (autoimported)
tests/mcp/
mcp.eval.ts # Evaluations
Following MCP spec:
- Resources - Static data indexes (patterns, examples)
- Tools - Parameterized operations (get_pattern, create_nuxt_project, debug_setup)
- Prompts - Guided workflows (scaffold_for_api, add_tool)
Browse available data:
resource://nuxt-mcp-starter/patterns- Index of 6 MCP patterns (list-search, caching, validation, error-handling, pagination, auth)resource://nuxt-mcp-starter/examples- Example projects (Recipe API, Database, File Search)
Core functionality:
get_pattern - Get MCP implementation patterns with working code
- Params:
pattern(list-search | caching | validation | error-handling | pagination | auth),format(code | explanation | both) - Returns: Code examples + explanations for Nuxt MCP patterns
create_nuxt_project - Generate complete Nuxt MCP project
- Params:
data_source(api | database | file | custom),use_case(description),auth_required(boolean) - Returns: Complete project code with handlers, schemas, MCP registration, evaluations
debug_setup - Troubleshoot MCP setup issues
- Params:
issue(tools-not-showing | cors-error | schema-validation | transport-setup | general),error_message(optional) - Returns: Diagnosis, solutions, troubleshooting steps
Guided workflows:
scaffold_for_api - Step-by-step guide for wrapping an API with MCP
- Param:
api_description(e.g., "GitHub REST API") - Returns: 7-step guide from generation to deployment
add_tool - Guide for adding a tool to existing MCP server
- Param:
tool_purpose(what the tool should do) - Returns: Step-by-step implementation guide
User: I want to create a Nuxt MCP that queries a recipe API
MCP: [Uses create_nuxt_project with data_source: 'api', use_case: 'recipe API']
[Returns complete project code with:
- Zod schemas for validation
- API handler with caching
- MCP tool registration
- Evaluation test cases]
User: Show me how to implement caching in my MCP
MCP: [Uses get_pattern with pattern: 'caching']
[Returns code example + explanation of caching strategy]
User: My tools aren't showing up in Claude Desktop
MCP: [Uses debug_setup with issue: 'tools-not-showing']
[Returns 6 troubleshooting steps with specific fixes]
Following MCP SDK guidelines:
Consolidate related operations into single tools:
// ❌ BAD: 3 separate tools
list_items()
get_item(id)
search_items(query)
// ✅ GOOD: 1 consolidated tool
get_items({ id?, query? })
// No params → list all
// id param → get specific
// query param → search- Concise tool descriptions (20-30 words)
- High-signal responses, not data dumps
- Human-readable identifiers (names over IDs)
- Pagination for large datasets
// ❌ BAD
throw createError({ statusCode: 404, message: 'Not found' })
// ✅ GOOD
const available = items.map(i => i.id).join(', ')
throw createError({
statusCode: 404,
message: `Item '${id}' not found. Available: ${available}`
})Always include annotations:
server.registerTool('tool_name', {
// ...
annotations: {
readOnlyHint: true, // Read-only operation
destructiveHint: false, // Non-destructive
idempotentHint: true, // Repeatable with same result
openWorldHint: false, // Internal data (true for external APIs)
}
})Define schemas in server/utils/schemas.ts:
export const ToolSchema = z.object({
query: z.string().describe('Search query'),
limit: z.number().min(1).max(100).optional().default(10),
category: z.enum(['all', 'active', 'archived']).optional(),
})Use in handlers AND tool registration:
// Handler
const args = await getValidatedQuery(event, ToolSchema.parse)
// MCP registration
server.registerTool('tool', { inputSchema: ToolSchema, ... })Cache expensive operations:
defineCachedEventHandler(async (event) => {
// Handler logic
}, {
maxAge: 60 * 60, // 1 hour for stable content
getKey: (event) => {
const { query } = getQuery(event)
return `tool-${query || 'default'}`
}
})When to cache:
- Static content: 24h
- Reference data: 1h
- User-specific: 5min
- Real-time: Don't cache
- Authenticated:
maxAge: 0
server/utils/schemas.ts:
import { z } from 'zod'
export const SearchSchema = z.object({
query: z.string().describe('Search term'),
limit: z.number().min(1).max(100).optional().default(10),
})server/api/mcp/search.get.ts:
export default defineCachedEventHandler(async (event) => {
const { query, limit } = await getValidatedQuery(event, SearchSchema.parse)
const results = await yourDataSource.search(query)
return {
query,
results: results.slice(0, limit),
total: results.length
}
}, {
maxAge: 60 * 5,
getKey: (event) => {
const { query } = getQuery(event)
return `search-${query}`
}
})server/routes/mcp.ts:
server.registerTool('search', {
title: 'Search Items',
description: 'Search items by query term with pagination',
inputSchema: SearchSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
}, async (args: any) => {
const data = await $fetch('/api/mcp/search', { query: args })
return {
content: [{
type: 'text',
text: JSON.stringify(data, null, 2)
}]
}
})tests/mcp/mcp.eval.ts:
evalite('Search Tool', {
data: async () => [{
input: 'Search for recipes containing chocolate',
expected: [{ toolName: 'search', args: { query: 'chocolate' } }],
}],
task: async (input) => {
const mcpClient = await createMCPClient({
transport: { type: 'http', url: 'http://localhost:3000/mcp' }
})
const result = await generateText({
model,
prompt: input,
tools: await mcpClient.tools()
})
return result.toolCalls
},
scorers: [toolCallAccuracy]
})Use get_pattern tool to see full implementations.
Consolidate list/get/search into one tool with optional params.
Cache responses with custom keys and TTL based on data volatility.
Zod schemas for runtime validation and TypeScript inference.
Actionable errors with suggestions and available options.
Handle large datasets with limit/offset and hasMore metadata.
API key validation with env var fallback and clear error messages.
Standard Nuxt deployment with MCP route at /mcp:
# Vercel
vercel deploy
# NuxtHub
npx nuxthub deploy
# Netlify
pnpm buildConfigure Claude Desktop (~/.config/claude/claude_desktop_config.json):
{
"mcpServers": {
"nuxt-mcp-starter": {
"url": "https://your-domain.com/mcp"
}
}
}This repository runs monthly evaluations automatically via GitHub Actions to ensure MCP server quality over time.
Prerequisites:
-
Deploy to Vercel (or your preferred platform):
vercel deploy --prod
After deployment, note your production URL (e.g.,
https://your-app.vercel.app) -
Configure GitHub Secrets (Settings → Secrets and variables → Actions):
OPENAI_API_KEY: Your OpenAI API keyMCP_URL: Your deployed MCP endpoint (e.g.,https://your-app.vercel.app/mcp)
-
The workflow runs automatically on the 1st of each month at 2am UTC
Manual Trigger:
# Via GitHub UI
Actions → Monthly Evals → Run workflow
# Via GitHub CLI
gh workflow run evals.ymlWhat it does:
- Runs evaluations against your deployed MCP server
- Tests all 19 evaluation cases
- Enforces 85% score threshold (fails if below)
- Displays results in GitHub Actions Summary
- Uploads results as artifacts (retained for 90 days)
- Creates an issue if evaluations fail
Local Development:
# Run evals against local server (default: http://localhost:3000/mcp)
pnpm eval
# Run evals against deployed server
MCP_URL=https://your-app.vercel.app/mcp pnpm evalView Results:
- Actions tab → Monthly Evals → Latest run → Artifacts
- Download
eval-results-*to see detailed scores
Current Score: ~94% (17 tests, scores vary due to LLM non-determinism)
server/
routes/mcp.ts # MCP server + tool/resource/prompt registration
api/mcp/
get-pattern.get.ts # Pattern examples with code
create-nuxt-project.get.ts # Project generator
debug-setup.get.ts # Debug helper
utils/
schemas.ts # Zod schemas (autoimported globally)
tests/mcp/
mcp.eval.ts # Evaluation test suites
Use debug_setup tool for common issues:
Tools not showing in Claude Desktop:
- Verify server running at
http://localhost:3000/mcp - Check
claude_desktop_config.jsonhas correct URL - Restart Claude Desktop completely
- Test with MCP Inspector
Schema validation errors:
- Ensure schema matches between handler and registration
- Use
inputSchema(notargsSchema) inregisterTool - Test schema separately:
Schema.safeParse({ ... })
Transport/connection issues:
- Use
StreamableHTTPServerTransportfor HTTP - Add cleanup:
event.node.res.on('close', () => { transport.close(); server.close() }) - Read body:
await readBody(event)
MIT