---
title: "Multi-handler organization"
description: "Auto-attribute auto-discovered definitions to multiple MCP handlers via folder convention, with a function-based escape hatch for cross-cutting cases."
canonical_url: "https://mcp-toolkit.nuxt.dev/handlers/organization"
---
# Multi-handler organization

> Auto-attribute auto-discovered definitions to multiple MCP handlers via folder convention, with a function-based escape hatch for cross-cutting cases.

When you have more than one named handler (`/mcp/admin`, `/mcp/apps`, `/mcp/api-v2`…) you usually want every tool, resource, and prompt to land in **exactly one place** without writing manual filters.

The toolkit gives you **one mechanism** to attribute definitions and **one escape hatch** for everything else.

## A. Folder convention (the way to attribute)

Place named-handler definitions under `server/mcp/handlers/<name>/`. Every file under `tools/`, `resources/`, or `prompts/` is auto-attached to that handler via `_meta.handler`.

```bash
server/mcp/
├── tools/                          # → default handler
├── resources/                      # → default handler
├── prompts/                        # → default handler
└── handlers/
    ├── admin/
    │   ├── index.ts                # defineMcpHandler({ middleware: requireAdmin })
    │   ├── tools/
    │   │   └── delete-user.ts      # → handler 'admin' (auto)
    │   └── prompts/
    │       └── help.ts             # → handler 'admin' (auto)
    └── widgets/
        ├── index.ts                # defineMcpHandler({})
        └── tools/
            └── carousel.ts         # → handler 'widgets' (auto)
```

The handler `name` is inferred from the directory name and **wins over** anything you set in `index.ts`.

<callout color="primary" icon="i-lucide-lightbulb">

`index.ts` is required even if it's a one-liner: `export default defineMcpHandler({})`. It's what registers the `/mcp/<name>` route and lets you add `middleware`, `description`, `experimental_codeMode`, etc.

</callout>

## B. Function form (the escape hatch)

For cross-cutting cases — "every tool tagged X", "every orphan", "everything except this group" — pass a function that calls one of the `getMcp*` helpers:

```typescript [server/mcp/handlers/searchable/index.ts]
import { defineMcpHandler, getMcpTools } from '@nuxtjs/mcp-toolkit/server'

export default defineMcpHandler({
  // Every tool tagged 'searchable', regardless of folder.
  tools: event => getMcpTools({ event, tags: ['searchable'] }),
})
```

`getMcpTools`, `getMcpResources`, and `getMcpPrompts` return the **raw** definition objects (with handlers and Zod schemas intact) — exactly what `defineMcpHandler` expects. They accept the same options as their `listMcp*` counterparts: `event`, `group`, `tags`, `handler`, `orphansOnly`. See [Listing definitions](https://mcp-toolkit.nuxt.dev/advanced/listing-definitions) for the full reference.

## Default handler strategy

The default `/mcp` route obeys `mcp.defaultHandlerStrategy` in `nuxt.config.ts`:

<field-group>
<field name="`'orphans'`" type="default">

Only definitions **not attached** to any named handler are exposed. Each definition shows up in exactly one place.

</field>

<field name="`'all'`">

Every discovered definition is exposed (the pre-multi-handler behaviour). Useful when you want a "kitchen sink" route on top of specialized ones.

</field>
</field-group>

```typescript [nuxt.config.ts]
export default defineNuxtConfig({
  mcp: {
    defaultHandlerStrategy: 'orphans', // (default)
  },
})
```

<callout color="primary" icon="i-lucide-shield-check">

**Zero-effort back-compat**: when no definition uses the folder convention, `'orphans'` behaves exactly like `'all'` — every definition is an orphan, so it's exposed. Existing apps don't change behaviour after upgrading.

</callout>

## Resolution rule

The toolkit picks where each definition shows up using a single rule, deterministic by file location:

<table>
<thead>
  <tr>
    <th>
      Handler config file
    </th>
    
    <th>
      Default behaviour for <code>
        tools | resources | prompts: undefined
      </code>
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        server/mcp/index.ts
      </code>
      
       (default route)
    </td>
    
    <td>
      Obeys <code>
        defaultHandlerStrategy
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        server/mcp/handlers/<name>/index.ts
      </code>
      
       (folder handler)
    </td>
    
    <td>
      Definitions attributed to <code>
        <name>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        server/mcp/<name>.ts
      </code>
      
       (top-level handler)
    </td>
    
    <td>
      Every discovered definition (back-compat)
    </td>
  </tr>
  
  <tr>
    <td>
      Any (array)
    </td>
    
    <td>
      Used as-is
    </td>
  </tr>
  
  <tr>
    <td>
      Any (function <code>
        (event) => T[]
      </code>
      
      )
    </td>
    
    <td>
      Called per request
    </td>
  </tr>
</tbody>
</table>

<callout color="info" icon="i-lucide-info">

The distinction between "folder handler" and "top-level handler" is purely about where the file lives — not magic. The loader injects `_meta.handler` on folder handlers; the runtime reads it to choose the default.

</callout>

## Migration tips

1. **Start small.** Move one handler at a time into `handlers/<name>/`. Existing `tools: [...]` and `tools: ev => [...]` keep working untouched.
2. **Drop manual filters.** If you were doing `tools: allTools.filter(t => t._meta?.group === 'apps')`, move those tools to `handlers/apps/tools/` and let the system attribute them automatically.
3. **Wrap-everything handlers.** A top-level handler that wraps every tool (e.g. a code-mode wrapper at `mcp/codemode.ts`) keeps its current behaviour — top-level handlers default to the full pool. To filter, pass a function: `tools: ev => getMcpTools({ event: ev, ... })`.
4. **Force the old behaviour.** Set `mcp.defaultHandlerStrategy: 'all'` to keep `/mcp` exposing everything even after you adopt folder handlers.

## See also

- [Listing definitions](https://mcp-toolkit.nuxt.dev/advanced/listing-definitions) — programmatic access to summaries and raw defs (`listMcp*` / `getMcp*`).
- [Sharing & practices](https://mcp-toolkit.nuxt.dev/handlers/sharing-practices) — handler organization patterns and best practices.
- [Dynamic definitions](https://mcp-toolkit.nuxt.dev/advanced/dynamic-definitions) — per-request `enabled()` guards on individual definitions.


## Sitemap

See the full [sitemap](https://mcp-toolkit.nuxt.dev/sitemap.md) for all pages.
