处理器
多处理器组织
通过文件夹约定自动将自动发现的定义归属到多个 MCP 处理器,并为交叉切分场景提供一个基于函数的逃生口。
当你拥有多个命名处理器(/mcp/admin、/mcp/apps、/mcp/api-v2…)时,通常希望每个工具、资源和提示都恰好落在一个位置,而不需要手动编写过滤器。
这个工具包为你提供了一种机制来归属定义,以及一个逃生口来处理其他所有情况。
A. 文件夹约定(归属的方式)
将命名处理器定义放在 server/mcp/handlers/<name>/ 下。tools/、resources/ 或 prompts/ 下的每个文件都会通过 _meta.handler 自动附加到该处理器。
server/mcp/
├── tools/ # → 默认处理器
├── resources/ # → 默认处理器
├── prompts/ # → 默认处理器
└── handlers/
├── admin/
│ ├── index.ts # defineMcpHandler({ middleware: requireAdmin })
│ ├── tools/
│ │ └── delete-user.ts # → 处理器 'admin'(自动)
│ └── prompts/
│ └── help.ts # → 处理器 'admin'(自动)
└── widgets/
├── index.ts # defineMcpHandler({})
└── tools/
└── carousel.ts # → 处理器 'widgets'(自动)
处理器的 name 会从目录名中推断,并且优先于你在 index.ts 中设置的任何内容。
即使它只有一行,
index.ts 也是必需的:export default defineMcpHandler({})。它用于注册 /mcp/<name> 路由,并允许你添加 middleware、description、experimental_codeMode 等。B. 函数形式(逃生口)
对于交叉切分的场景——“每个标记了 X 的工具”、“每个孤立项”、“除了这一组之外的所有内容”——传入一个调用 getMcp* 辅助函数之一的函数:
server/mcp/handlers/searchable/index.ts
import { defineMcpHandler, getMcpTools } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpHandler({
// 每个标记为 'searchable' 的工具,不受文件夹影响。
tools: event => getMcpTools({ event, tags: ['searchable'] }),
})
getMcpTools、getMcpResources 和 getMcpPrompts 返回原始定义对象(保留处理器和 Zod schema 不变)——这正是 defineMcpHandler 所期望的。它们接受与对应 listMcp* 方法相同的选项:event、group、tags、handler、orphansOnly。完整参考请参见 Listing definitions。
默认处理器策略
默认的 /mcp 路由会遵循 nuxt.config.ts 中的 mcp.defaultHandlerStrategy:
`'orphans'`
default
仅暴露未附加到任何命名处理器的定义。每个定义只会出现在一个地方。
`'all'`
暴露所有已发现的定义(多处理器之前的行为)。当你想在专门路由之外再提供一个“杂烩”路由时很有用。
nuxt.config.ts
export default defineNuxtConfig({
mcp: {
defaultHandlerStrategy: 'orphans', //(默认)
},
})
零成本向后兼容:当没有任何定义使用文件夹约定时,
'orphans' 的行为与 'all' 完全一致——因为每个定义都是孤立项,所以都会被暴露。升级后现有应用的行为不会改变。解析规则
工具包使用一条单一规则、按文件位置确定性地决定每个定义出现在哪里:
| 处理器配置文件 | 对于 tools | resources | prompts: undefined 的默认行为 |
|---|---|
server/mcp/index.ts(默认路由) | 遵循 defaultHandlerStrategy |
server/mcp/handlers/<name>/index.ts(文件夹处理器) | 定义归属到 <name> |
server/mcp/<name>.ts(顶层处理器) | 所有已发现的定义(向后兼容) |
| 任意(数组) | 按原样使用 |
任意(函数 (event) => T[]) | 每次请求时调用 |
“文件夹处理器”和“顶层处理器”之间的区别纯粹取决于文件所在位置——并非魔法。加载器会在文件夹处理器上注入
_meta.handler;运行时读取它以选择默认值。迁移提示
- 先从小处开始。 一次迁移一个处理器到
handlers/<name>/中。现有的tools: [...]和tools: ev => [...]会继续原样工作。 - 移除手动过滤器。 如果你之前在做
tools: allTools.filter(t => t._meta?.group === 'apps'),请把这些工具移动到handlers/apps/tools/,让系统自动为它们归属。 - 包装所有内容的处理器。 一个包装每个工具的顶层处理器(例如
mcp/codemode.ts中的代码模式包装器)会保持当前行为——顶层处理器默认使用完整池。若要过滤,请传入函数:tools: ev => getMcpTools({ event: ev, ... })。 - 强制旧行为。 将
mcp.defaultHandlerStrategy设置为'all',以便在采用文件夹处理器后,/mcp仍然暴露所有内容。
另请参见
- Listing definitions — 以编程方式访问摘要和原始定义(
listMcp*/getMcp*)。 - Sharing & practices — 处理器组织模式和最佳实践。
- Dynamic definitions — 对单个定义按请求启用的
enabled()守卫。