进阶主题

向用户请求输入:启发式询问

使用 useMcpElicitation() 向用户请求结构化输入,或将他们发送到一个 URL。

什么是启发式询问?

启发式询问允许服务器在请求进行中向已连接的客户端请求额外信息。MCP 规范定义了两种模式:

  • 表单模式 — 向用户展示一个结构化表单,并根据你定义的 schema 验证响应。
  • URL 模式 (spec 2025-11-25) — 将用户重定向到外部页面(登录、支付、账户验证等),并在他们返回后继续。

添加 MCP 启发式询问(表单与 URL)

何时使用启发式询问

使用场景模式
消歧输入(“你指的是哪个项目?”)带枚举的 form
在执行破坏性操作前确认confirm
交互式收集缺失的必需参数form
在提交前收集可选元数据(优先级、标签等)form
通过外部页面提示登录或支付url
触发 OAuth 同意流程url
启发式询问是由客户端驱动的。即使你在服务端调用它,真正渲染表单或打开 URL 的也是客户端。务必处理用户拒绝或客户端不支持所请求模式的情况。

useMcpElicitation()

自动导入。必须在工具、资源或提示处理器中调用。

server/mcp/tools/release.ts
import { z } from 'zod'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'

export default defineMcpTool({
  name: 'create_release',
  description: '在询问发布通道后创建一个新版本',
  inputSchema: {
    name: z.string(),
  },
  handler: async ({ name }) => {
    const elicit = useMcpElicitation()

    const result = await elicit.form({
      message: `为 "${name}" 选择一个发布通道`,
      schema: {
        channel: z.enum(['stable', 'beta', 'canary']).describe('发布通道'),
        notify: z.boolean().default(true).describe('通知订阅者'),
      },
    })

    if (result.action !== 'accept') {
      return `发布已取消(${result.action})。`
    }

    return `已在 ${result.content.channel} 上创建 "${name}"(notify=${result.content.notify})。`
  },
})

Zod 形状会被转换为符合规范限制的 JSON Schema,响应会根据相同的形状进行验证,并且 result.content 的类型是完全推断好的。

API

方法描述
form({ message, schema })请求结构化输入。schema 是一个 Zod 原始形状(与 inputSchema 格式相同)。返回 { action, content? }
url({ message, url })打开外部 URL。返回 { action }
confirm(message)便捷的是/否提示。返回 boolean(只有用户接受并确认时才为 true)。
`supports('form''url')`

action 的值为 'accept' | 'decline' | 'cancel' 之一。只有当 action === 'accept' 时,content 字段才会存在。

Schema 限制(表单模式)

MCP 规范将启发式询问请求限制为仅包含原始属性的扁平对象,以便任何客户端都能将其渲染为表单。工具包会在请求时强制执行这一限制,并在你违反时抛出 McpElicitationError('invalid-schema')

允许:

  • z.string(), z.number(), z.boolean(), z.string().email(), z.number().int()
  • z.enum([...]) — 单选下拉
  • z.array(z.enum([...])) — 多选
  • .describe(...), .default(...), .optional()

不允许:

  • 嵌套的 z.object({ ... })
  • z.array(z.number()) 或任何非字符串枚举数组
  • z.record(...), z.tuple(...), z.union(...), z.discriminatedUnion(...)

需要更丰富的输入?可以拆分为多次启发式询问调用,或者通过常规的 inputSchema 接收数据。

Confirm 帮助器

server/mcp/tools/delete-project.ts
import { z } from 'zod'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'

export default defineMcpTool({
  name: 'delete_project',
  description: '在与用户确认后删除项目',
  inputSchema: { id: z.string() },
  handler: async ({ id }) => {
    const elicit = useMcpElicitation()

    if (!await elicit.confirm(`永久删除项目 ${id}`)) {
      return '已中止。'
    }

    await deleteProject(id)
    return `已删除 ${id}`
  },
})

confirm() 基于带有单个 confirm: z.boolean() 字段的 form() 构建,因此它继承了相同的能力检查和拒绝处理。

URL 模式

根据规范,URL 模式是可选启用的——客户端必须在其能力中声明 elicitation.url。使用 supports('url') 进行优雅分支:

server/mcp/tools/connect-github.ts
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'

export default defineMcpTool({
  name: 'connect_github',
  description: '连接用户的 GitHub 账户',
  inputSchema: {},
  handler: async () => {
    const elicit = useMcpElicitation()

    if (!elicit.supports('url')) {
      return '请打开 https://app.example.com/settings/github 连接你的账户,然后再试一次。'
    }

    const result = await elicit.url({
      message: '授权该集成',
      url: 'https://app.example.com/oauth/github/start',
    })

    return result.action === 'accept'
      ? 'GitHub 已连接。'
      : `用户未完成流程(${result.action})。`
  },
})
URL 模式的启发式询问会在用户机器上触发重定向——仅应将其用于可信端点。MCP 规范建议在回调中配合使用来源/协议验证

能力检查

始终处理客户端未声明启发式询问能力的情况——许多客户端(以及自托管中继)会暴露工具但不支持它。

const elicit = useMcpElicitation()

if (!elicit.supports('form')) {
  return '此工具需要一个交互式客户端(Cursor、Claude Desktop 等)。'
}

当你在不支持的客户端上调用 form()url() 时,该可组合函数会抛出带有 code: 'unsupported'McpElicitationError,这样你就可以优雅地恢复:

import { McpElicitationError } from '@nuxtjs/mcp-toolkit/server'

try {
  await elicit.form({ message: '', schema: {} })
}
catch (err) {
  if (err instanceof McpElicitationError && err.code === 'unsupported') {
    return '客户端不支持启发式询问——将回退到默认值。'
  }
  throw err
}

要求

useMcpElicitation() 需要:
  • nitro.experimental.asyncContext 设置为 true(自 Nuxt 3.8+ 起默认)
  • 客户端在初始化期间声明了 elicitation 能力

下一步

  • Sessions — 在后续工具调用之间存储用户的启发式询问回答。
  • Middleware — 根据上下文控制哪些工具需要启发式询问。
  • Dynamic definitions — 使用 enabled 守卫隐藏工具,而不是交互式询问。
  • Logging — 在宽事件上记录启发式询问结果(accept / decline / cancel)。