使用说明
hitl mcp
仅询问代理工作流的HITL MCP服务器。
目录
背景
在许多Agent系统中,Agent最终会达到无法自主继续的地步:
- 一个人必须批准或拒绝一个决定
- 一个人必须从几个候选人中选择一个
- 人类必须提供缺失的业务输入
- 工作流必须暂停,直到手动确认到达
在没有专用HITL层的情况下,这些工作流通常使用特别提示、侧通道UI或自定义回调协议来实现。这造成了三个反复出现的问题:
- Agent和应用程序不共享“待定人工输入”的稳定模型。
- 人工答案很难与请求它们的确切代理运行和会话相关联。
- 等待、部分提交、取消和恢复语义在客户端之间变得不一致。
hitl-mcp 通过为人类问题暴露一个狭窄、明确的契约来解决这个问题:
- 代理创建问题
- 客户或运营商用户界面阅读未决问题
- 人类回答、跳过或取消它们
- 代理在调用者作用域的状态机上等待,直到工作完成
什么是hitl mcp
hitl-mcp 是一个 面向问题的人在环服务器 基于MCP和HTTP构建。
它在相同的底层状态上提供两个访问表面:
- MCP工具 用于代理和代理平台
- HTTP API 用于操作员控制台、后端或自定义审批UI
公共抽象是有意的小:
- 公共单位是
question - 问题属于a 呼叫者范围
- 答案可以逐步提交
- 等待被建模为范围级操作,而不是问题级长轮询
该服务器专为代理作为发起者,但由人类完成部分决策循环的工作流而设计。
目标
- 为Agent工作流提供稳定、最小的HITL合同。
- 通过以下方式明确呼叫者隔离
agent_identity和agent_session_id. - 支持同一调用者范围内的多个未决问题。
- 支持部分进度,而不是强制一次性提交最终结果。
- 允许MCP和HTTP客户端在相同的基础问题状态上操作。
- 保持存储可插拔,以便本地开发可以使用内存,生产可以使用Redis。
- 通过健康状况、准备状态、指标和结构化日志来观察操作行为。
非目标
- 不是通用的工作流引擎。
- 不是用于审批控制台的UI框架。
- 不是任务队列或事件总线。
- 不是通用的表单生成器。
- 不是用于访问控制和审阅者分配的策略引擎。
- 不是用于任意业务流程的持久编排平台。
hitl-mcp 仅管理问题状态、调用者范围、等待语义和答案提交。
核心模型
公共单位:问题
从外部来看,系统只暴露 question.
一个问题是:
- 服务器生成
question_id - 一
type - 提示元数据,例如
title,description,tags,以及extra - 为了
single_choice,每个选项还可以声明followup_fields - 可选
default_answer - 一种状态,例如
pending,answered,skipped,或cancelled
支持的问题类型:
single_choicemulti_choicetextbooleanrange
呼叫者范围
每个操作的范围如下:
agent_identityagent_session_id
该范围是以下内容的隔离边界:
- 提出问题
- 列出未决问题
- 等待进展
- 提交答案
- 取消提问
只要两个代理的调用者范围不同,它们就可以提出相同的问题而不会发生冲突。
内部团体与公共API
存储层可能仍然在内部使用分组,但分组是一个实现细节。
公众API有意质疑第一:
- 创建问题
- 获取未决问题
- 提交答案
question_id - 取消由
question_id - 在示波器上等待
部分提交
答案不需要一批到达。
服务器接受增量进度:
- 现在回答一个问题
- 稍后回答另一个问题
- 明确跳过可选问题
- 继续等待,直到范围完成
应答有效载荷保持向后兼容:
- 现有呼叫者可以继续发送
{ value } - 当选择
single_choice选项声明followup_fields,呼叫者可以发送{ value, fields } - 必需的
followup_fields根据所选选项进行验证
超时自动响应
问题组还可以在创建时定义超时行为。
- 呼叫者可以设置
timeout_seconds - 如果省略,服务器将使用
HITL_PENDING_DEFAULT_TIMEOUT_SECONDS - 每个问题都可以设置
default_answer - 如果省略,服务器将按问题类型导出一个
超时的未决问题将自动以已解决的默认答案回答。
- 在
memory模式下,这适用于通过进程内超时工作器进行单进程部署 - 在
redis模式,这通过Redis支持的超时处理跨实例工作
衍生默认值为:
single_choice:第一个选项值multi_choice:第一个选项值作为单个元素数组boolean:truerange:range_constraints.mintext:"none"
超时生成的答案保持正常 answered 状态并添加元数据,例如:
auto_response_atis_timeout_auto_response
等待模式
hitl_wait 等效作用域级等待行为支持两种模式:
terminal_only:仅当作用域没有未决问题时返回progressive:每次状态更改后返回,然后让调用者再次等待
terminal_only 对于线性工作流来说更简单。 progressive 当调用者需要对每个中间更新做出反应时,效果更好。
什么时候 hitl_wait 通过MCP调用,它还会在等待过程中每30秒发出一次进度通知。
快速开始
安装
git clone <your-repo-url>
cd hitl-mcp
npm install
在开发中运行
npm run dev
默认本地绑定:
- HTTP基本URL:
http://0.0.0.0:3000 - MCP基本URL:
http://0.0.0.0:3000/mcp - HTTP API前缀:
/api/v1
使用Docker运行
塑造形象:
docker build -t hitl-mcp .
使用内存存储运行:
docker run --rm -p 3000:3000 \
-e MCP_URL=http://localhost:3000 \
hitl-mcp
使用Redis运行:
docker run --rm -p 3000:3000 \
-e MCP_URL=http://localhost:3000 \
-e HITL_STORAGE=redis \
-e HITL_REDIS_URL=redis://host.docker.internal:6379 \
hitl-mcp
使用环境变量从源代码运行
export MCP_URL=http://localhost:3000
npm run dev
最小创建请求
curl -X POST "http://localhost:3000/api/v1/questions" \
-H "Content-Type: application/json" \
-H "x-agent-identity: agent/example" \
-H "x-agent-session-id: session-123" \
-d '{
"title": "Outline confirmation",
"questions": [
{
"type": "single_choice",
"title": "Confirm the current outline",
"options": [
{ "value": "approved", "label": "Approved" },
{
"value": "revise",
"label": "Revise",
"followup_fields": [
{
"id": "comment",
"type": "text",
"label": "Revision comment",
"required": true,
"description": "Explain what should change."
}
]
}
]
}
]
}'
互动是如何运作的
本节描述了预期的运行时流,无论调用者是使用MCP工具还是HTTP API。
顺序1:标准提问->等待->回答->完成
- Agent在其调用者范围内创建一个或多个问题。
- 代理人立即致电
hitl_wait. - UI或后端获取同一调用者范围的未决问题。
- 人类回答一个或多个悬而未决的问题。
- 代理在作用域上等待。
- 当作用域中没有未决问题时,wait将返回最终结果。
序列2:部分提交
- 代理创建多个问题。
- 人类只回答了一个子集。
- 服务器会保留这些答案,并保留剩余的问题。
- 代理人可以继续等待。
- 其他提交将继续,直到范围完成。
序列3:渐进式等待
HITL_WAIT_MODE=progressive- 代理人打电话来
hitl_wait. - 范围内的任何回答、跳过或取消都会唤醒服务员。
- 等待结果报告了
question_ids变了。 - 代理决定是继续等待还是对中间更新采取行动。
序列4:取消
- 呼叫者取消范围内的一个问题或所有未决问题。
- 服务器更新作用域状态并通知服务员。
- 如果没有悬而未决的问题,范围就变成了终点。
序列5:超时自动响应
- Agent创建具有显式或隐式超时行为的问题。
- 在Redis模式下,服务器会安排挂起的组进行超时处理。
- 如果在截止日期之前没有收到人工响应,Redis支持的超时工作器会自动回答剩余的未决问题。
- worker发布一个作用域更新,以便任何实例上的服务员都可以醒来。
hitl_wait返回已完成的范围快照,其中包含受影响问题的超时元数据。
范围语义
等待总是 范围级别 操作。
这是故意的:
- 一次代理运行可能有多个未决问题
- 代理通常关心工作流是否可以继续
- 作用域级等待避免了按问题分段的同步逻辑
操作规则:
- 之后每
hitl_ask,下一个HITL工具调用必须是hitl_wait - 不要治疗
hitl_ask作为完成 - 不要替代
hitl_get_pending_questions对于第一篇帖子,请稍候
MCP工具
hitl-mcp 公开以下MCP工具:
hitl_ask
为当前调用者范围创建一个或多个问题。
输入形状:
{
"title": "Release decision",
"description": "Human approval required before deploy",
"ttl_seconds": 3600,
"timeout_seconds": 900,
"questions": [
{
"type": "boolean",
"title": "Approve deployment?",
"default_answer": { "value": true }
}
]
}
笔记:
question_id不得由呼叫者提供- 服务器生成
question_id - 一个请求可以创建多个问题
- 明确的
default_answer推荐 - 如果
default_answer如果省略,服务器将导出一个 - 如果
timeout_seconds如果省略,服务器将使用配置的默认超时
hitl_wait
在当前调用者作用域上等待。
典型响应字段:
statusis_terminalchanged_question_idspending_questionsresolved_questionsanswered_question_idsskipped_question_idscancelled_question_idsis_complete
resolved_questions 包含完整的已解决问题对象及其最终状态和任何存储的答案。
当超时自动化产生结果时,解决的问题还可能包括:
auto_response_atis_timeout_auto_response
当通过MCP呼叫时, hitl_wait 还发送 notifications/progress 每30秒更新一次,同时仍在等待。
hitl_get_pending_questions
返回当前调用者作用域中的所有未决问题。
hitl_submit_answers
提交新答案和可选跳过。
输入形状:
{
"answers": {
"q_01JXYZ...": { "value": true }
},
"skipped_question_ids": ["q_01JABC..."],
"idempotency_key": "idem-1"
}
笔记:
answers可能包含未决问题的任何子集- 可选问题可以明确跳过
- 必填问题不能跳过
- 提交在服务器端累积
hitl_cancel_questions
取消范围内选定的未决问题或所有未决问题。
输入形状:
{
"question_ids": ["q_01JXYZ..."],
"reason": "no longer needed"
}
或者:
{
"cancel_all": true
}
hitl_get_question
通过以下方式获取一个问题 question_id.
HTTP API
HTTP控制平面主要用于操作员UI、后端服务和故障排除。
响应信封
所有HTTP响应都使用相同的信封:
{
"request_id": "http-request-id",
"success": true,
"data": {},
"error": null
}
失败时:
{
"request_id": "http-request-id",
"success": false,
"data": {},
"error": {
"code": "QUESTION_NOT_FOUND",
"message": "question not found",
"details": null
}
}
标题和标识规则
对于问题API,服务器要求在每个调用者范围的请求上都有一个会话头:
- 默认会话标头:
x-agent-session-id
身份规则:
- 发送
x-agent-identity在每个调用者范围的请求上
操作上:
- 服务器读取
agent_identity直接来自x-agent-identity
GET /api/v1/healthz
返回活动状态。
GET /api/v1/readyz
返回准备状态。
这是对Redis支持的生产部署的正确探测。
GET /api/v1/metrics
返回进程内指标快照。
POST /api/v1/questions
为当前调用者范围创建一个或多个问题。
请求正文:
{
"title": "Release decision",
"description": "Human approval required before deploy",
"ttl_seconds": 3600,
"timeout_seconds": 900,
"questions": [
{
"type": "single_choice",
"title": "Confirm the current outline",
"options": [
{ "value": "approved", "label": "Approved" },
{
"value": "revise",
"label": "Revise",
"followup_fields": [
{
"id": "comment",
"type": "text",
"label": "Revision comment",
"required": true,
"description": "Explain what should change."
}
]
}
],
"default_answer": { "value": "approved" }
},
{
"type": "text",
"title": "Anything to note?",
"required": false,
"default_answer": { "value": "none" }
}
]
}
支持的问题有效载荷:
single_choice随着optionssingle_choice.options[].followup_fields用于特定选项的文本后续输入multi_choice随着optionstext可选的text_constraintsbooleanrange随着range_constraints
其他创建时间字段:
timeout_seconds:组的可选正整数超时default_answer:Redis超时自动响应使用的每个问题的可选回退答案
GET /api/v1/questions/pending
返回当前调用者作用域的所有未决问题。
POST /api/v1/questions/answers
提交已回答和跳过的问题。
请求正文:
{
"answers": {
"q_01JXYZ...": {
"value": "revise",
"fields": {
"comment": "Please add recovery conditions."
}
}
},
"skipped_question_ids": ["q_01JABC..."],
"idempotency_key": "idem-1"
}
行为:
- 接受部分进展
- 保持累积作用域状态
- 成功提交后唤醒范围服务员
- 继续接受普通
{ value }不需要后续字段时的答案
典型错误代码:
QUESTION_NOT_FOUNDANSWER_VALIDATION_FAILED
POST /api/v1/questions/cancel
取消当前呼叫者范围内的未决问题。
请求正文:
{
"question_ids": ["q_01JXYZ..."],
"reason": "no longer needed"
}
或者:
{
"cancel_all": true
}
GET /api/v1/questions/:question_id
通过以下方式获取一个问题 question_id.
如果问题不存在,服务器将返回 404 随着 QUESTION_NOT_FOUND.
运行时配置
配置源和优先级
配置按以下顺序加载:
- 内置默认值
config/hitl-mcp.yaml.env- 过程环境变量
后来的来源会取代以前的来源。
环境变量
代码库当前支持以下环境变量。
| 变量 | 默认值 | 目的 | 何时更改 |
|---|---|---|---|
PORT |
3000 |
回退HTTP端口。相当于 HITL_HTTP_PORT 当设置时。 |
仅在运行时注入时更改 PORT 或者当平台需要固定端口环境时。 |
MCP_URL |
http://0.0.0.0:3000 |
MCP服务器元数据使用的公共基URL。 | 更改任何非本地部署,以便MCP客户端接收可访问的外部URL |
HITL_SERVER_NAME |
hitl-mcp |
MCP服务器名称元数据。 | 将此服务器嵌入到其他产品标识下时进行更改。 |
HITL_SERVER_VERSION |
0.1.0 |
MCP服务器版本元数据。 | 发布具有显式运行时版本的打包版本时进行更改。 |
HITL_HTTP_HOST |
0.0.0.0 |
HTTP绑定主机。 | 仅当您有意只进行环回绑定或使用其他接口时才进行更改。 |
HITL_HTTP_PORT |
3000 |
显式HTTP端口。覆盖默认端口。 | 使用自定义端口映射更改本地多服务设置或生产平台。 |
HITL_HTTP_API_PREFIX |
/api/v1 |
HTTP控制平面路由的前缀。 | 仅当您需要将API装载到不同的路径段之后时才更改。 |
HITL_STORAGE |
memory |
存储后端选择: memory 或 redis。 |
设置为 redis 用于多进程或持久部署。 |
HITL_REDIS_URL |
redis://127.0.0.1:6379 |
Redis连接URL。 | 需要时 HITL_STORAGE=redis 外部本地违约。 |
HITL_REDIS_PREFIX |
hitl |
Redis密钥前缀。 | 当多个环境共享一个Redis实例时更改。 |
HITL_TTL_SECONDS |
604800 |
新创建的问题集的默认TTL。 | 更改以使未决问题保留与您的业务SLA保持一致。 |
HITL_ANSWERED_RETENTION_SECONDS |
2592000 |
应答状态的保留窗口。 | 当可审计性或存储压力需要不同的保留期时,请进行更改。 |
HITL_PENDING_MAX_WAIT_SECONDS |
0 |
一次等待呼叫的最长持续时间。 0 意味着没有超时限制。 |
更改何时需要有界等待以进行工作进程调度或请求生命周期控制。 |
HITL_WAIT_MODE |
terminal_only |
作用域等待行为: terminal_only 或 progressive。 |
设置为 progressive 当调用者必须对每个中间更新做出反应时。 |
HITL_PENDING_DEFAULT_TIMEOUT_SECONDS |
900 |
新创建的待决问题组的默认超时时间为 timeout_seconds 省略。 |
当人工响应SLA因环境而异时,会发生变化。 |
HITL_PENDING_TIMEOUT_POLL_INTERVAL_SECONDS |
5 |
Redis超时工作器的轮询间隔。与精确超时边界的微小偏差是可以接受的。 | 更改您想要更严格或更宽松的超时触发粒度。 |
HITL_AGENT_SESSION_HEADER |
x-agent-session-id |
用于读取的标头名称 agent_session_id. |
与使用其他会话标头的现有网关或客户端集成时进行更改。 |
HITL_CREATE_CONFLICT_POLICY |
error |
在配置界面中创建冲突策略。 | 保持默认值。当前代码验证并加载它,但它当前未被请求处理程序应用。 |
HITL_LOG_LEVEL |
info |
结构化日志记录级别: debug, info, warn, error |
提高或降低详细程度,以满足调试和生产噪音要求。 |
HITL_ENABLE_METRICS |
true |
在配置中启用指标收集。 | 保持启用状态,除非您有意将可观测性开销降至最低。 |
YAML示例
http:
host: 0.0.0.0
port: 3000
apiPrefix: /api/v1
storage:
kind: redis
redis:
url: redis://127.0.0.1:6379
keyPrefix: hitl
ttl:
defaultSeconds: 604800
answeredRetentionSeconds: 2592000
pending:
maxWaitSeconds: 0
waitMode: terminal_only
defaultTimeoutSeconds: 900
timeoutPollIntervalSeconds: 5
agentIdentity:
sessionHeader: x-agent-session-id
createConflictPolicy: error
observability:
logLevel: info
enableMetrics: true
配置建议
本地开发
HITL_STORAGE=memory- 发送
x-agent-identity来自您的客户或测试工具 - 保持
HITL_WAIT_MODE=terminal_only - 超时自动响应在单进程内存模式下可用
共享开发或暂存
HITL_STORAGE=redis- 设置一个真实
MCP_URL - 使用独特的
HITL_REDIS_PREFIX - 审查
HITL_PENDING_DEFAULT_TIMEOUT_SECONDS - 确保上游呼叫者始终提供
x-agent-identity
生产
HITL_STORAGE=redis- 集
MCP_URL指向外部可访问的URL - 电线准备就绪
/api/v1/readyz - 明确审查TTL和保留值
- 明确查看超时和轮询值
- 确保上游呼叫者始终提供
x-agent-identity
它在内部是如何工作的
作用域状态机
hitl-mcp 在调用者范围级别保持问题进度。
每个状态更改操作都会更新一个范围快照,其中包含:
- 未决问题
- 已解决的问题,包含完整的问题有效载荷和答案(如果可用)
- 已回答的问题ID
- 跳过的问题ID
- 已取消的问题ID
- 更改了问题ID
- 完成状态
此快照是等待结果的真实来源。
服务员通知模型
服务器保存一个由调用者作用域键入的进程内服务员注册表。
当收到回复或取消时:
- 存储已更新
- 计算新的作用域快照
- 通知了那个炉灶的服务员
hitl_wait根据配置的等待模式进行解析
在Redis模式下,超时自动响应也会产生服务员通知。超时工作器通过Redis pub/sub发布作用域更新,因此当另一个实例执行自动响应时,在一个实例上被阻止的服务员仍然可以醒来。
在内存模式下,超时自动响应通过相同的进程内进程生成服务员通知 Waiter 用于应答和取消事件。
存储选择
存在两种存储模式:
- 记忆:简单,流程本地化,适合测试和本地开发
- 瑞迪斯:跨流程耐用,适合实际部署
这两种模式都启用超时自动响应,但范围不同:
- 记忆:单进程超时工作程序和直接服务员通知
- 瑞迪斯:跨实例超时调度和分布式唤醒
Redis模式还使用:
- 一个用于定时调度的Redis排序集
- 短寿命分布式锁,避免多实例部署中的双重处理
- Redis pub/sub跨实例通知服务员
如果在运行时初始化期间选择了Redis但不可用,服务器将回退到内存存储并记录警告。
这种回退在本地开发中很有用,但生产环境应将其视为配置错误的信号。
身份处理
对于HTTP问题API:
- 会话标识来自
HITL_AGENT_SESSION_HEADER - 呼叫者身份来自
x-agent-identity
对于MCP工具调用:
- 服务器从请求上下文将调用者作用域注入MCP工具状态
- 工具层读取
agent_identity和agent_session_id从注射状态
建筑
运行时有五个主要层。
1.配置层
从默认值、YAML加载并验证配置, .env,以及运行时环境变量。
2.服务器层
构建MCP服务器和HTTP应用程序,连接中间件,并注册路由和工具。
3.服务层
HitlService 定义以下操作行为:
- 创建
- 列表待定
- 等待
- 提交答案
- 取消
- 获取问题
这是主要的应用边界。
4.存储层
提供以下存储库实现:
- 内存开发和测试
- Redis支持的持久性
Redis实现还具有超时调度、分布式锁获取和超时驱动的默认应答持久性。
5.观测层
提供:
- 结构化日志
- 请求ID
- 准备就绪检查
- 度量快照
请求流摘要
对于HTTP:
- 调用者上下文中间件解析身份和会话
- 路由处理程序验证输入和调用
HitlService - 存储库更新状态
- 回复被包裹在标准信封中
对于MCP:
- 检查MCP请求上下文
- 调用者作用域被注入到工具状态中
- 工具处理程序委托给
HitlService - 工具输出反映了最新的作用域状态
运营
健康和准备
- 生活:
GET /api/v1/healthz - 准备就绪:
GET /api/v1/readyz - 韵律学:
GET /api/v1/metrics
使用就绪性而非活性来保护Redis支持的生产流量。
日志记录
服务器发出结构化的请求和错误日志。
集 HITL_LOG_LEVEL=debug 在对请求流或存储库行为进行故障排除时。
指标
指标通过以下方式以JSON快照的形式公开 /api/v1/metrics.
当前的实现跟踪操作信号,如等待时间和待处理计数。
常见部署检查
在声明部署正常之前,请验证:
MCP_URL匹配外部可访问的URL。- 暴露的HTTP端口与您的运行时或入口映射匹配。
HITL_STORAGE=redis与可访问的Redis实例配对。- 上游呼叫者发送
x-agent-identity以及所配置的会话报头。 /api/v1/healthz,/api/v1/readyz,以及/api/v1/metrics所有人都按照预期做出了回应。
项目结构
.
├── config/ # YAML configuration examples
├── docs/
│ ├── api/ # MCP tool and HTTP API reference docs
│ └── runbooks/ # Operational runbooks
├── src/
│ ├── config/ # Config schema, defaults, loaders
│ ├── core/ # HitlService application logic
│ ├── domain/ # Domain types, schemas, validators
│ ├── http/ # Hono routes, middleware, response helpers
│ ├── mcp/ # MCP tool registration and caller-scope helpers
│ ├── observability/ # Logging and metrics
│ ├── state/ # Waiter and state-machine helpers
│ └── storage/ # In-memory and Redis repositories
├── tests/ # Unit and integration coverage
├── Dockerfile # Production-oriented container build
└── index.ts # Runtime entrypoint
