进阶主题

使用 hooks 扩展模块

使用 Nuxt 和 Nitro hooks 来扩展和自定义 MCP 模块。

该工具包提供两种 hooks:

  • 构建时 hooks(作用于 NuxtHooks)—— 在 nuxt build / nuxt prepare 期间触发,可用于注册额外的定义目录。
  • 运行时 hooks(作用于 NitroRuntimeHooks)—— 在 每个请求 内、Nitro 插件中触发,用于修改解析后的配置或访问 SDK McpServer 实例。

用户监听器采用尽力而为的方式:若某个 hook 抛出错误,会通过 consola 记录日志,而 MCP 请求会继续执行。

构建时 hooks

mcp:definitions:paths

添加额外目录以扫描工具 / 资源 / prompt / handler 定义。适用于在 Nuxt 层之间共享定义,或从自定义模块中分发这些定义。

Hook 签名

nuxt.hook('mcp:definitions:paths', (paths: {
  tools: string[]
  resources: string[]
  prompts: string[]
  handlers: string[]
}) => {
  // 原地修改 paths
})

nuxt.config.ts 中使用

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/mcp-toolkit'],
  hooks: {
    'mcp:definitions:paths'(paths) {
      paths.tools.push('shared/tools')
      paths.tools.push('legacy/tools')
      paths.resources.push('shared/resources')
      paths.prompts.push('shared/prompts')
      paths.handlers.push('custom/handlers')
    },
  },
})

在自定义模块中使用

my-module.ts
export default defineNuxtModule({
  setup(options, nuxt) {
    nuxt.hook('mcp:definitions:paths', (paths) => {
      paths.tools.push('my-module/tools')
      paths.resources.push('my-module/resources')
      paths.prompts.push('my-module/prompts')
    })
  },
})

路径结构

{
  tools: string[]      // 要扫描的工具目录
  resources: string[]  // 要扫描的资源目录
  prompts: string[]    // 要扫描的 prompt 目录
  handlers: string[]   // 要扫描的 handler 目录
}

所有路径都相对于每个 Nuxt 层的 server/ 目录:

  1. 相对路径,例如 'tools',会解析为 server/tools/
  2. / 开头的绝对路径 会从项目根目录解析。
  3. 层级相关——每个 Nuxt 层都会相对于其自身的 server/ 目录解析路径。

运行时 hooks

运行时 hooks 会在 每个 MCP 请求 中,从 Nitro 服务器内部触发。可通过位于 server/plugins/Nitro 插件订阅这些 hooks。

server/plugins/mcp.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('mcp:server:created', ({ server, event }) => {
    // ...
  })
})

这些 hooks 的运行顺序如下:

H3 Event
   │
   ▼
defineMcpHandler middleware(如果有)
   │
   ▼
resolveDynamicDefinitions  ──►  mcp:config:resolved
   │
   ▼
createMcpServer            ──►  mcp:server:created
   │
   ▼
transport.handleRequest

mcp:config:resolved

在动态 tools / resources / prompts 解析器以及 enabled(event) 守卫运行之后、构建每次请求的 McpServer 之前触发。原地修改 ctx.config,即可仅为当前请求添加、移除或转换定义。

Hook 签名

import type { McpResolvedConfig } from '@nuxtjs/mcp-toolkit/server'
import type { H3Event } from 'h3'

nitroApp.hooks.hook('mcp:config:resolved', (ctx: {
  config: McpResolvedConfig
  event: H3Event
}) => {
  // 原地修改 ctx.config
})

示例:对匿名客户端隐藏管理工具

server/plugins/mcp-auth.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('mcp:config:resolved', ({ config, event }) => {
    if (!event.context.user) {
      config.tools = config.tools.filter(
        tool => !tool.tags?.includes('admin'),
      )
    }
  })
})

示例:按请求重新命名服务器

server/plugins/mcp-rebrand.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('mcp:config:resolved', ({ config, event }) => {
    const tenant = event.context.tenant?.name
    if (tenant) {
      config.name = `${tenant} MCP`
      config.instructions = `你已连接到 ${tenant} 的 MCP 服务器。`
    }
  })
})

mcp:server:created

createMcpServer 注册完所有工具 / 资源 / prompt 之后、服务器连接到传输层之前触发。接收 SDK McpServer 实例。

可用于在后期注册定义,或通过 getSdkServer(ctx.server) 访问底层低级别 Server,以安装自定义 JSON-RPC 处理器。

Hook 签名

import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { H3Event } from 'h3'

nitroApp.hooks.hook('mcp:server:created', (ctx: {
  server: McpServer
  event: H3Event
}) => {
  // 访问 SDK
})

示例:动态注册工具

server/plugins/mcp-whoami.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('mcp:server:created', ({ server, event }) => {
    server.registerTool(
      'whoami',
      { description: '返回当前用户 ID' },
      async () => ({
        content: [{
          type: 'text',
          text: String(event.context.userId ?? 'anonymous'),
        }],
      }),
    )
  })
})

示例:为底层 SDK 服务器添加监测

server/plugins/mcp-instrument.ts
import { getSdkServer } from '@nuxtjs/mcp-toolkit/server'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('mcp:server:created', ({ server }) => {
    const sdkServer = getSdkServer(server)
    sdkServer.oninitialized = () => {
      console.log('Client initialized')
    }
  })
})
需要请求级或会话级状态?请在 hook 内从 event.context 中读取——中间件(以及在 MCP 处理器之前运行的任何认证层)已经将其填充好了。

错误处理

抛出错误的监听器会被捕获,并通过 consola(标签 mcp-toolkit)报告:

[error] [mcp-toolkit] Hook "mcp:server:created" 抛出错误 — 请求继续执行

MCP 请求始终会继续进行。如果你需要将错误暴露给客户端,请改为在对应的 tool / resource / prompt handler 中抛出——该工具包会将抛出的错误(包括 H3 errors)转换为符合 MCP 规范的 isError 结果。

后续步骤

  • Middleware - 运行每个 MCP 请求的代码,包括 next() 拦截。
  • Dynamic Definitions - 从 defineMcpHandler 中动态选择 tools / resources / prompts。
  • Custom Paths - 自定义定义的扫描位置。
  • Handlers - 创建多个 MCP 端点。