第五节:MCP Elicitation 用户交互
交互请求处理与通知

作者:小学子 📚 | 日期:2026年4月2日 | 第七阶段 · 模块七


# 第七阶段 · 模块七 · 第五节:MCP Elicitation 用户交互

核心问题

什么是 MCP Elicitation?Claude Code 如何处理 MCP 服务器的用户交互请求?交互请求的完整流程是什么?如何处理超时和取消?


◇ 本节位置


        Claude Code 全局架构
        
        ┌─────────────────────────────────────────────────────────────────────┐
        │  服务层(services/mcp/)                                            │
        │                                                                      │
        │  MCP Elicitation ← 本节                                            │
        │  ├── 用户交互请求                                                   │
        │  ├── 通知处理                                                       │
        │  └── 钩子集成                                                       │
        └─────────────────────────────────────────────────────────────────────┘
        


一、Elicitation 概述

1.1 什么是 Elicitation

源码位置:`src/services/mcp/elicitationHandler.ts`

Elicitation 是 MCP 协议中服务器向用户请求信息或确认的机制,允许 MCP 服务器在需要用户输入时暂停执行。


        ┌─────────────────────────────────────────────────────────────────────┐
        │  MCP Elicitation 流程                                                 │
        │                                                                      │
        │  MCP Server ──► ElicitRequest ──► Claude Code ──► 用户确认            │
        │                    │                        │                         │
        │                    │◄───── ElicitResult ─────┘                         │
        └─────────────────────────────────────────────────────────────────────┘
        

1.2 与工具调用的区别


        ┌─────────────────────────────────────────────────────────────────────┐
        │  Elicitation vs 工具调用                                             │
        │                                                                      │
        │  工具调用:                                                         │
        │  - Claude 自动执行                                                  │
        │  - 无需用户介入                                                     │
        │  - 适合确定性操作                                                   │
        │                                                                      │
        │  Elicitation:                                                       │
        │  - 需要用户确认或输入                                               │
        │  - Claude 等待用户响应                                              │
        │  - 适合需要人工决策的场景                                            │
        └─────────────────────────────────────────────────────────────────────┘
        

1.3 五问分析

问 1:Elicitation 的典型使用场景?


        // 典型场景:
        
        // 1. 确认操作
        // "确定要删除这些文件吗?"
        // - destructive operation confirmation
        
        // 2. 输入敏感信息
        // "请输入 API 密钥:"
        // - API key input
        
        // 3. 选择确认
        // "选择部署环境:production/staging"
        // - option selection
        
        // 4. 表单填写
        // "请填写配置信息"
        // - structured input
        


二、Elicitation 类型

2.1 请求参数

源码位置:`src/services/mcp/elicitationHandler.ts`


        // ElicitRequestParams
        export type ElicitationRequestEvent = {
          serverName: string
          requestId: string | number
          params: ElicitRequestParams
          signal: AbortSignal              // 用于取消
          respond: (response: ElicitResult) => void  // 响应回调
          waitingState?: ElicitationWaitingState  // URL 模式专用
          onWaitingDismiss?: (action: 'dismiss' | 'retry' | 'cancel') => void
          completed?: boolean  // 服务器确认完成
        }
        
        // 等待状态配置
        export type ElicitationWaitingState = {
          actionLabel: string  // 按钮标签,如 "Retry now" 或 "Skip confirmation"
          showCancel?: boolean  // 是否显示取消按钮(错误重试流程)
        }
        

2.2 Elicitation 模式


        // 两种 Elicitation 模式
        
        type ElicitRequestParams = {
          message: string
          requestedSchema?: {...}
          defaultValue?: unknown
          mode?: 'form' | 'url'  // 新增模式字段
        }
        
        // form 模式:传统的表单填写
        // url 模式:URL 确认(如"打开外部链接")
        

2.3 结果类型


        // ElicitResult
        type ElicitResult =
          | { action: 'accept'; value: unknown }  // 用户接受了
          | { action: 'reject' }                    // 用户拒绝了
          | { action: 'cancel' }                    // 用户取消了
        
        // 注意:对于错误重试(-32042),'accept' 是空操作
        // 重试由 onWaitingDismiss 驱动
        


三、处理流程

3.1 请求接收

源码位置:`src/services/mcp/elicitationHandler.ts`


        // 接收 ElicitRequest
        async function handleElicitRequest(params: ElicitRequestParams) {
          // 1. 解析请求参数
          const { message, requestedSchema, defaultValue } = params
        
          // 2. 创建等待状态
          const waitingState: ElicitationWaitingState = {
            actionLabel: '确认',
            showCancel: true
          }
        
          // 3. 显示 UI 并等待用户响应
          const result = await showElicitationUI(message, waitingState)
        
          // 4. 返回结果给 MCP 服务器
          return result
        }
        

3.2 UI 显示


        // 显示 Elicitation UI
        
        interface ElicitationUIOptions {
          message: string
          requestedSchema?: Schema
          defaultValue?: unknown
          waitingState: ElicitationWaitingState
        }
        
        async function showElicitationUI(options: ElicitationUIOptions): Promise<ElicitResult> {
          // 1. 渲染模态框
          renderElicitationModal(options)
          
          // 2. 等待用户响应
          const userResponse = await waitForUserResponse()
          
          // 3. 返回结果
          return userResponse
        }
        

3.3 完整流程图


        ┌─────────────────────────────────────────────────────────────────────┐
        │  Elicitation 完整流程                                                │
        │                                                                      │
        │  1. MCP Server 发送 ElicitRequest                                   │
        │     ↓                                                               │
        │  2. Claude Code 接收并验证请求                                        │
        │     ↓                                                               │
        │  3. 执行 elicitation 钩子(可选)                                     │
        │     ↓                                                               │
        │  4. 显示 UI 给用户                                                  │
        │     ↓                                                               │
        │  5. 用户响应(接受/拒绝/取消)                                        │
        │     ↓                                                               │
        │  6. 执行 result 钩子(可选)                                         │
        │     ↓                                                               │
        │  7. 返回结果给 MCP Server                                           │
        └─────────────────────────────────────────────────────────────────────┘
        


四、等待状态

4.1 等待状态配置

源码位置:`src/services/mcp/elicitationHandler.ts`


        export type ElicitationWaitingState = {
          /** 按钮标签 */
          actionLabel: string
        
          /** 是否显示取消按钮 */
          showCancel?: boolean
        
          /** 错误消息(用于重试流程)*/
          errorMessage?: string
        }
        

4.2 状态配置示例


        // 确认操作
        const confirmState: ElicitationWaitingState = {
          actionLabel: '确认删除',
          showCancel: true
        }
        
        // 输入表单
        const inputState: ElicitationWaitingState = {
          actionLabel: '提交',
          showCancel: true
        }
        
        // 错误重试
        const retryState: ElicitationWaitingState = {
          actionLabel: '重试',
          showCancel: true,
          errorMessage: '输入无效,请重试'
        }
        

4.3 用户操作处理


        // 用户操作
        
        // 1. accept - 用户点击确认/提交
        // - 返回 { action: 'accept', value: userInput }
        
        // 2. reject - 用户拒绝
        // - 返回 { action: 'reject' }
        // - 通常用于 destructive operations
        
        // 3. cancel - 用户取消
        // - 返回 { action: 'cancel' }
        // - 关闭模态框,不做任何改变
        


五、钩子集成

5.1 钩子执行

源码位置:`src/services/mcp/elicitationHandler.ts`


        // 执行 elicitation 钩子
        import {
          executeElicitationHooks,
          executeElicitationResultHooks
        } from '../../utils/hooks.js'
        
        // 在显示 UI 前执行钩子
        const modifiedParams = await executeElicitationHooks(params)
        
        // 在返回结果前执行钩子
        const modifiedResult = await executeElicitationResultHooks(result)
        

5.2 钩子用途


        ┌─────────────────────────────────────────────────────────────────────┐
        │  Elicitation 钩子用途                                                 │
        │                                                                      │
        │  1. 日志记录                                                         │
        │     - 记录所有交互请求                                               │
        │                                                                      │
        │  2. 访问控制                                                         │
        │     - 拦截敏感的 Elicitation                                        │
        │                                                                      │
        │  3. 参数验证                                                         │
        │     - 修改或验证用户输入                                            │
        │                                                                      │
        │  4. 自动化处理                                                       │
        │     - 根据条件自动响应                                              │
        └─────────────────────────────────────────────────────────────────────┘
        

5.3 五问分析

问 1:如何实现自动接受某些 Elicitation?


        // 自动接受钩子
        
        async function autoAcceptHook(event: ElicitationRequestEvent): Promise<ElicitResult | null> {
          // 检查条件
          if (event.params.message.includes('API密钥') && hasStoredApiKey()) {
            // 自动返回存储的值
            return {
              action: 'accept',
              value: { apiKey: getStoredApiKey() }
            }
          }
          
          // 不自动处理,继续正常流程
          return null
        }
        


六、通知处理

6.1 服务器通知

源码位置:`src/services/mcp/channelNotification.ts`


        // MCP 服务器可以发送通知
        type Notification =
          | { type: 'message'; message: string }     // 信息提示
          | { type: 'progress'; progress: number }   // 进度更新
          | { type: 'complete' }                     // 完成通知
        

6.2 通知处理


        // 处理服务器通知
        
        async function handleNotification(notification: Notification) {
          switch (notification.type) {
            case 'message':
              showToast(notification.message)
              break
              
            case 'progress':
              updateProgressBar(notification.progress)
              break
              
            case 'complete':
              hideProgressBar()
              break
          }
        }
        
        // 执行通知钩子
        executeNotificationHooks(notification)
        


七、超时和取消

7.1 AbortSignal


        // 使用 AbortSignal 处理取消
        
        async function handleElicitRequest(params: ElicitRequestParams) {
          const { signal, ...rest } = params
          
          // 监听取消信号
          signal?.addEventListener('abort', () => {
            // 清理 UI
            hideElicitationModal()
            // 返回取消结果
            respond({ action: 'cancel' })
          })
          
          // 正常处理
          ...
        }
        

7.2 超时处理


        // 超时处理
        
        async function handleWithTimeout(
          params: ElicitRequestParams,
          timeoutMs: number = 60000
        ): Promise<ElicitResult> {
          const timeoutPromise = new Promise<ElicitResult>((resolve) => {
            setTimeout(() => {
              resolve({ action: 'cancel' })
            }, timeoutMs)
          })
          
          const responsePromise = handleElicitRequest(params)
          
          return Promise.race([responsePromise, timeoutPromise])
        }
        

7.3 长时间等待


        ┌─────────────────────────────────────────────────────────────────────┐
        │  处理长时间等待                                                      │
        │                                                                      │
        │  1. 显示进度                                                        │
        │     - 使用 notification 机制显示进度                                  │
        │                                                                      │
        │  2. 心跳保活                                                        │
        │     - 定期 ping MCP server                                           │
        │                                                                      │
        │  3. 用户可取消                                                       │
        │     - 提供取消按钮                                                   │
        │     - 发送 abort signal                                              │
        └─────────────────────────────────────────────────────────────────────┘
        


八、思考题

思考题 1:Elicitation 和普通工具调用的区别?

答案


        // 工具调用:
        // - Claude 根据参数自动执行
        // - 无需用户介入
        // - 返回执行结果
        
        // Elicitation:
        // - 需要用户确认或输入
        // - Claude 等待用户响应
        // - 用户可以拒绝或取消
        


思考题 2:如何处理长时间等待的 Elicitation?

答案


        // 方法:
        
        // 1. AbortSignal
        // - 服务器可发送取消信号
        // - 客户端监听并处理
        
        // 2. 超时机制
        // - 设置最大等待时间
        // - 超时后自动取消
        
        // 3. 进度通知
        // - 使用 notification 显示进度
        // - 让用户了解当前状态
        
        // 4. 后台处理
        // - 在后台继续处理
        // - 用户可做其他事
        


思考题 3:Elicitation 可以嵌套吗?

答案


        // 嵌套处理:
        
        // 1. 技术上支持
        // - 可以打开新的 Elicitation
        // - 模态框堆叠
        
        // 2. 用户体验问题
        // - 多个模态框堆叠混乱
        // - 用户容易困惑
        
        // 3. 推荐做法
        // - 扁平化处理
        // - 串行等待,而非嵌套
        // - 或者使用不同的 UI 形式(如 sidebar)
        


九、延伸阅读

资源说明
`src/services/mcp/elicitationHandler.ts`Elicitation 处理
`src/services/mcp/channelNotification.ts`通知处理


十、下节预告

第八章我们将进入 调试与日志

- 日志系统架构

- 调试模式详解

- 常见问题诊断