Claude
Skills
Sign in
Back

manage-mcp

Included with Lifetime
$97 forever

Manage MCP servers in Nuxt with @nuxtjs/mcp-toolkit — setup, create tools/resources/prompts, organize with handlers, build interactive MCP Apps, add elicitation/logging/sessions, review, and troubleshoot.

AI Agents

What this skill does


# Manage MCP

Complete skill for managing Model Context Protocol (MCP) servers in Nuxt with [`@nuxtjs/mcp-toolkit`](https://mcp-toolkit.nuxt.dev). Setup, create tools/resources/prompts, organize with multi-handler folder convention, build interactive UI widgets (MCP Apps), wire up sessions and observability, review, and troubleshoot.

## When to Use

- **Setup**: "Setup an MCP server in my Nuxt app", "Add MCP to Nuxt"
- **Create**: "Add a tool that calculates BMI", "Expose a README as a resource", "Create a code-review prompt"
- **Organize**: "Split my MCP into admin + public endpoints", "Filter tools per user"
- **Customize**: "Add authentication to my MCP", "Log every tool call to Axiom", "Stream progress to the client"
- **Build apps**: "Add an interactive Vue widget callable from ChatGPT/Cursor"
- **Persist state**: "Remember the user across tool calls in one session"
- **Review**: "Review my MCP implementation", "Check for best practices"
- **Troubleshoot**: "My tool isn't discovered", "Auto-imports don't work", "OAuth flow keeps triggering"
- **Test**: "Eval which tools the model picks for these prompts"

---

## Setup MCP Server

### Installation

::code-group
```bash [pnpm]
pnpm add @nuxtjs/mcp-toolkit zod
```
```bash [npm]
npm install @nuxtjs/mcp-toolkit zod
```
```bash [yarn]
yarn add @nuxtjs/mcp-toolkit zod
```
```bash [bun]
bun add @nuxtjs/mcp-toolkit zod
```
::

Or in one go via the Nuxt CLI:

```bash
npx nuxt add mcp
```

Add to `nuxt.config.ts`:

```typescript [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['@nuxtjs/mcp-toolkit'],
  mcp: {
    name: 'My MCP Server',
    description: 'Read and update todos for the current user.',
  },
  nitro: {
    experimental: { asyncContext: true }, // required for useEvent / useMcpServer / useMcpLogger
  },
})
```

### Directory Structure

```
server/mcp/
├── tools/                 # Actions the AI can perform
│   └── admin/             # Subdirectory → group: 'admin'
├── resources/             # Data the AI can read
├── prompts/               # Reusable message templates
└── handlers/              # Optional: named handlers mounted at /mcp/<name>
    └── admin/
        ├── index.ts       # defineMcpHandler({ middleware: requireAdmin })
        ├── tools/         # auto-attached to /mcp/admin
        └── resources/

app/mcp/                   # Optional: MCP Apps (interactive Vue widgets)
└── palette.vue            # → app `palette`, callable from ChatGPT / Cursor
```

### Verification

1. Start the dev server: `pnpm dev`
2. Hit the endpoint: `curl http://localhost:3000/mcp` (responds to MCP JSON-RPC)
3. Open Nuxt DevTools (Shift+Alt+D) → **MCP** tab — bundled MCP Inspector for live testing.

---

## Create Tools

Tools are functions AI assistants can call. Auto-discovered from any `.ts`/`.js` file under `server/mcp/tools/`.

### Basic Tool

```typescript [server/mcp/tools/echo.ts]
import { z } from 'zod'

export default defineMcpTool({
  description: 'Echo a message back to the user',
  inputSchema: {
    message: z.string().describe('Text to echo'),
  },
  handler: async ({ message }) => {
    return `Echo: ${message}` // string is auto-wrapped into a text content item
  },
})
```

`name` and `title` are auto-derived from the filename (`echo.ts` → `name: 'echo'`, `title: 'Echo'`). Override either by setting them explicitly.

### Return Values

Handlers can return any of these — the toolkit normalizes them:

| Return | Wrapped as |
| --- | --- |
| `string` / `number` / `boolean` | `{ content: [{ type: 'text', text: String(v) }] }` |
| Plain object / array | JSON-stringified into a text content item |
| `imageResult(base64, mime)` | `{ content: [{ type: 'image', data, mimeType }] }` |
| `audioResult(base64, mime)` | `{ content: [{ type: 'audio', data, mimeType }] }` |
| Full `CallToolResult` | Passed through (use for `structuredContent`, embedded resources, multi-content) |
| Thrown error | Caught and converted to `isError: true`. `createError({ statusCode, message })` from h3 includes the status code in the response. |

```typescript
import { z } from 'zod'

export default defineMcpTool({
  description: 'Get a user by ID',
  inputSchema: { id: z.string().describe('User ID') },
  handler: async ({ id }) => {
    const user = await getUser(id)
    if (!user) throw createError({ statusCode: 404, message: 'User not found' })
    return user // plain object — auto-stringified
  },
})
```

For typed structured output, pair `outputSchema` with a `structuredContent` return:

```typescript
import { z } from 'zod'

export default defineMcpTool({
  description: 'Calculate Body Mass Index',
  inputSchema: {
    height: z.number().describe('Height in meters'),
    weight: z.number().describe('Weight in kilograms'),
  },
  outputSchema: {
    bmi: z.number(),
    category: z.string(),
  },
  handler: async ({ height, weight }) => {
    const bmi = weight / (height * height)
    const category = bmi < 18.5 ? 'underweight' : bmi < 25 ? 'normal' : bmi < 30 ? 'overweight' : 'obese'
    return { structuredContent: { bmi, category } }
  },
})
```

### Annotations & Input Examples

Behavioral hints that help clients decide whether to prompt for confirmation:

```typescript
export default defineMcpTool({
  description: 'Delete a user account',
  annotations: {
    readOnlyHint: false,
    destructiveHint: true,
    idempotentHint: true,
    openWorldHint: false,
  },
  inputSchema: { id: z.string() },
  inputExamples: [{ id: 'usr_42' }, { id: 'admin' }],
  handler: async ({ id }) => {
    await deleteUser(id)
    return `Deleted ${id}.`
  },
})
```

Common patterns: read-only → `readOnlyHint: true`; create → `idempotentHint: false`; update → `idempotentHint: true`; delete → `destructiveHint: true, idempotentHint: true`.

### Groups, Tags & Folder Inference

`group` (single) and `tags` (free-form) help organize tools and feed `listMcpTools({ group, tags })` filters. `group` is **auto-inferred from the parent folder** — `server/mcp/tools/admin/delete-user.ts` → `group: 'admin'`. Explicit `group` wins.

```typescript
export default defineMcpTool({
  group: 'admin',
  tags: ['destructive', 'user-management'],
  description: 'Delete a user account',
  // ...
})
```

### Caching

```typescript
export default defineMcpTool({
  cache: '5m',  // also accepts `Number` of ms or full Nitro cache options
  description: 'Fetch weather (cached)',
  inputSchema: { city: z.string() },
  handler: async ({ city }) => $fetch(`/api/weather?city=${city}`),
})
```

### Conditional Visibility (`enabled`)

Hide a tool per request — runs **after** middleware, so `event.context` is populated:

```typescript
export default defineMcpTool({
  enabled: event => Boolean(event.context.user),
  description: 'List the current user’s todos',
  handler: async () => listTodos(useEvent().context.user.id),
})
```

See [tool examples →](./references/tools.md).

---

## Create Resources

Resources expose **read-only data** addressable by URI. Auto-discovered from `server/mcp/resources/`.

### File Shorthand (zero handler)

```typescript [server/mcp/resources/readme.ts]
export default defineMcpResource({
  description: 'Project README file',
  file: 'README.md', // URI, MIME type, and handler auto-generated
})
```

### Standard Resource (custom handler)

```typescript [server/mcp/resources/config.ts]
export default defineMcpResource({
  description: 'Application config',
  uri: 'config:///app',
  metadata: { mimeType: 'application/json' },
  handler: async (uri) => ({
    contents: [{
      uri: uri.toString(),
      mimeType: 'application/json',
      text: JSON.stringify({ env: process.env.NODE_ENV }, null, 2),
    }],
  }),
})
```

### Template Resource (URI variables)

Pass a `ResourceTemplate` from the SDK as `uri`:

```typescript [server/mcp/resources/user.ts]
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'

export default defineMcpResource({
  description: 'Fetch a user by ID',
  uri: new ResourceTemplate('use

Related in AI Agents