进阶主题
捕获 MCP 日志和宽事件
使用 useMcpLogger() 将日志流式传递给 MCP 客户端,并捕获结构化的宽事件
useMcpLogger() 暴露了两个面向不同受众的通道:
| Channel | Goes to | API |
|---|---|---|
| Client | 终端用户 / 代理 UI(Cursor、Claude、Inspector) | log.notify(level, data) + .debug / .info / .warning / .error |
| Server | 你的开发终端以及 drains(Axiom、Sentry、OTLP,…) | log.set / log.event / log.setUser / log.setSession / log.evlog |
客户端通道始终可用。服务器通道需要 evlog/nuxt。
为我的 Nuxt MCP 服务器添加 useMcpLogger 和 evlog 风格的 wide events
设置
客户端通知开箱即用。对于服务端 wide events,请安装 evlog 并注册 evlog/nuxt:
pnpm add evlog
npm install evlog
yarn add evlog
bun add evlog
nuxt.config.ts
export default defineNuxtConfig({
modules: ['evlog/nuxt', '@nuxtjs/mcp-toolkit'],
evlog: {
env: { service: 'my-app' },
},
})
就是这样。你可以通过同一个 evlog: { … } 块来配置 drains、采样、脱敏和排除模式——完整 schema 请参阅 evlog 文档。
自动标记字段
每个 MCP wide event 在进入你的 drain 时都会预先标记:
| Field | Source |
|---|---|
mcp.transport / mcp.route / mcp.session_id / mcp.method / mcp.request_id | Transport headers |
mcp.tool / mcp.resource / mcp.prompt | JSON-RPC payload |
user.id / user.email / user.name | event.context.user(来自你的认证中间件——better-auth、API key,…) |
session.id | event.context.session.id |
service | <evlog.env.service>/mcp(或 slug 化后的 mcp.name)——会自动注入到 /mcp 和 /mcp/** |
批量 JSON-RPC 调用会将单数键(method、tool、…)切换为复数数组(methods、tools、…)。
覆盖自动 service
将你自己的值固定下来,以退出派生的 <service>/mcp:
nuxt.config.ts
evlog: {
env: { service: 'my-app' },
routes: {
'/mcp': { service: 'my-app/agents' },
'/mcp/**': { service: 'my-app/agents' },
},
}
强制开启 / 选择退出
mcp.logging 值 | 行为 |
|---|---|
undefined(默认) | 如果注册了 evlog/nuxt 则开启,否则关闭。 |
true | 断言已注册 evlog/nuxt。如果没有注册,构建会抛错。 |
false | 选择退出。log.notify(...) 仍然可用。 |
使用
useMcpLogger() 会自动导入。请在工具、资源或 prompt 处理器内部调用它:
server/mcp/tools/charge.ts
import { z } from 'zod'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpTool({
name: 'charge_card',
inputSchema: { userId: z.string(), amount: z.number().int() },
handler: async ({ userId, amount }) => {
const log = useMcpLogger('billing')
log.set({ billing: { amount } })
await log.notify.info({ msg: '开始扣款', amount })
try {
const receipt = await charge(userId, amount)
log.event('charge_completed', { receiptId: receipt.id })
return `已扣款 ${amount}。`
}
catch (err) {
log.evlog.error('扣款失败', err)
await log.notify.error({ msg: '扣款失败', error: String(err) })
throw err
}
},
})
在你的开发终端中:
INFO [my-app/mcp] POST /mcp 200 in 18ms
├─ mcp: transport=streamable-http route=/mcp method=tools/call tool=charge_card
├─ user: id=user-42 email=alice@example.com
├─ billing: amount=1000
└─ requestLogs: 0={"level":"info","message":"charge_completed",...}
API
| Method | Channel | Description |
|---|---|---|
notify(level, data, logger?) | client | 发送一个 notifications/message。如果被 logging/setLevel 过滤掉,则静默丢弃。 |
notify.debug / info / warning / error | client | 级别快捷方式。 |
set(fields) | server | 将字段合并到 wide event 中。 |
event(name, fields?) | server | 向 wide event 的 requestLogs 追加一个离散事件。 |
setUser({ id, email, name }) | server | 标记规范化的 user schema(会从 event.context.user 自动填充)。 |
setSession({ id }) | server | 标记 session.id(会从 event.context.session 自动填充)。 |
evlog | server | 底层的 RequestLogger,用于 fork、error、getContext,… |
log.notify 始终会 resolve,且不会抛错——适合用于高频热路径。若未注册 evlog/nuxt,服务器端方法会抛出 McpObservabilityNotEnabledError。
Drains
通过一个 Nitro 插件将每个 MCP wide event 发送到 Axiom、Sentry、OTLP、HyperDX、Datadog、Better Stack 或 PostHog:
server/plugins/evlog-axiom.ts
import { createAxiomDrain } from 'evlog/adapters/axiom'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})
.env
NUXT_AXIOM_TOKEN=xaat-...
NUXT_AXIOM_DATASET=mcp-server
这个 hook 是可叠加的——可以并行注册多个 drains。自定义 drain 只是注册到同一个 hook 上的 (ctx) => Promise<void>。
有关全部适配器、采样、脱敏以及如何构建自定义 drains,请参阅 evlog 文档。
useMcpLogger() 需要 nitro.experimental.asyncContext: true(自 Nuxt 3.8+ 起默认为 true)。