Documentation
¶
Overview ¶
目录层(批次对标 ZCode「连接时一次 tools/list、注册面全量」adapters/src/mcp): 每条连接建立后仅首次取目录走一次线上 ListTools,此后 Tools() 命中内存缓存; 连接被 evict(列举失败/探活死亡/自愈摘除)时目录随连接一并失效,重建连接后 重新列举。第三方 server 工具面在连接生命周期内视为静态——工具集变化需重连 生效,换来每次专家装配/直调不再全量走线(此前 einomcp.GetTools 每次 Tools() 都发一轮 ListTools)。
注解(annotations)随目录同源提取,供并发治理与风险分级消费(对标 ZCode readOnlyHint/idempotentHint/destructiveHint → 风险级 + concurrentSafe 映射)。
envref.go —— ServerConfig 头部值的 ${VAR} 环境引用展开(token 不落配置 文件的纪律件;收编自 argus toolpool,v0.10.39)。未定义变量保留原样并经 onMissing 上报——显式可见优于静默空值。
连接租约面(批次五十三自 bianque 对标 ZCode pool 沉淀):探活 + 闲置回收。
语义裁定——ZCode 原型是「引用计数归零 + 30s 惰性关闭」,本池不可照搬:工具对象 (eino tool)持有连接且生命周期调用方不可见,refcount 无处挂钩。借用模型的等价 租约 = **闲置回收**(ReapIdle/StartIdleReaper)+ **调用期透明重建**(既有 evict + lazy 建连)。在途工具调用经 leaseUse 登记 inFlight,回收据此跳过在用连接 (无须再要求 maxIdle 显著大于最大工具超时)。
请求侧上下文透传(批次五十六 B,对标 ZCode request-context:请求侧 _meta 携带 trace/workspace 上下文,adapters/src/mcp/index.ts:1767-1770): Pool.RequestMeta 在每笔工具调用的 ctx 上取值,经 metaClient 装饰器注入 req.Params.Meta(stdio 与 HTTP 同走 JSON-RPC params,server 侧通用)。 池不感知业务键——metaFn 由装配方提供(bianque 侧从 ctx 取 session/trace/caller)。
errorAsObservation MCP isError 降级器(2026-09-21 k8s 106 审计 P1):eino-ext 把 MCP isError:true 的工具结果当调用 error 上抛,ReAct 直接以 NodeRunError 炸掉整个 agent 步骤——LLM 没有机会看到错误并修正参数(实弹:k8sgpt get-resource 猜错 pod 名 → 专家整步 degraded)。本装饰器把这类错误降级为文本观察回传 LLM(是否修正参数/ 换路径由 LLM 自行决定);传输层等其他错误原样上抛。仅 mcp.Pool.Tools() 出口启用。
Package mcp MCP 工具池:MCP server 连接管理 → eino tool.BaseTool(对齐 eino-ext components/tool/mcp 的能力面,补连接生命周期与白名单池化)。
定位(对应 Argus design §9.2 方向 A):R3/builtin agent 需要 diff 之外的上下文 时,把外部 MCP server 的工具挂进 agent 工具表(工单系统/内部文档/linter 等)。
用法:
pool := mcp.NewPool(
mcp.ServerConfig{Name: "docs", URL: "http://docs.svc/mcp",
Headers: map[string]string{"Authorization": "Bearer " + tok}},
mcp.ServerConfig{Name: "lint", Command: []string{"lint-mcp", "serve"}},
)
defer pool.Close()
res, err := pool.Tools(ctx, []mcp.ToolSpec{{Server: "docs", Allow: []string{"search_docs"}}})
// res.Tools = 装配成功的工具;res.Failed = 被容忍的失败面(server → 原因)
安全模型:stdio 子进程环境走白名单透传(绝不继承密钥,经 procx.ChildEnv 单一纪律);工具白名单按 spec 收敛(未声明的 server/工具不暴露给 agent)。
有线 schema 保真通道(v0.12.5):mcp-go 类型化 Tool 反序列化丢 anyOf—— ToolArgumentsSchema 仅 $defs/type/properties/required/additionalProperties 五类键, outputSchema 的 anyOf/oneOf 联合声明经类型化 tools/list 即残废(inputSchema 同理)。 建连路径本就自建 transport(dialStdio/dialHTTP),句柄入 connState 后 tools/list 走原始 JSON-RPC 自解析,双 schema 无损;transport 未登记(测试注入 InProcess client 等)自动回落类型化通道,outputSchema 置空——宁缺勿假。
工具出参 schema 能力面(v0.12.5,MCP 2025-06-18 outputSchema):目录层经原始 tools/list 无损透传(rawlist.go),invokableTool 以可选接口暴露——eino tool.BaseTool 为封闭接口,schema 进不了 ToolInfo,故走类型断言。断言失败=该 工具无声明(typed 回退/第三方未声明),消费方按「未声明」处理。
Index ¶
- Constants
- Variables
- func CallMeta(ctx context.Context) map[string]any
- func ExpandEnvRef(v string, onMissing func(name string)) string
- func ExpandEnvRefs(headers map[string]string, onMissing func(name string)) map[string]string
- func IsToolResultError(err error) bool
- func OutputSchemaOf(t tool.BaseTool) (json.RawMessage, bool)
- func UnwrapMCPText(raw string) string
- func WithCallMeta(ctx context.Context, meta map[string]any) context.Context
- func WrapErrorAsObservation(tools []tool.BaseTool) []tool.BaseTool
- type OAuthClientConfig
- type OutputSchemer
- type Pool
- func (p *Pool) Close()
- func (p *Pool) Hints(server string) map[string]ToolHints
- func (p *Pool) PingServer(ctx context.Context, name string) error
- func (p *Pool) ReapIdle(maxIdle time.Duration) int
- func (p *Pool) Servers() []string
- func (p *Pool) StartIdleReaper(interval, maxIdle time.Duration)
- func (p *Pool) Tools(ctx context.Context, specs []ToolSpec) (ToolsResult, error)
- type ServerConfig
- type ToolHints
- type ToolResultError
- type ToolSpec
- type ToolsResult
Constants ¶
const DefaultProbeTimeout = 5 * time.Second
DefaultProbeTimeout 探活独立预算(批次五十六 B:ping 是轻量协议方法,不该吃 连接级 30s 预算;对标 ZCode MCP_PING_TIMEOUT_MS=5s)。
const DefaultRequestTimeout = 10 * time.Minute
DefaultRequestTimeout HTTP 传输单请求兜底缺省。
const DefaultTimeout = 30 * time.Second
DefaultTimeout 连接与初始化的缺省超时。
Variables ¶
var ErrNotConnected = errors.New("mcp: server 无缓存连接(未建连或已被回收)")
ErrNotConnected PingServer 探活时该 server 无缓存连接——懒建连模型下属正常态 (与「有连接但已死」区分,探活无意义不算失败)。
Functions ¶
func CallMeta ¶
CallMeta 从 ctx 取调用元数据;无注入返回 nil(metaClient 对空不注入 _meta)。 返回值可直接作 Pool.RequestMeta:pool.RequestMeta = mcp.CallMeta。
func ExpandEnvRef ¶
ExpandEnvRef 展开字符串中的 ${VAR} 环境引用。已定义取环境值;未定义保留 原样(扫描位置跳过本标记,防死循环)并回调 onMissing。
func ExpandEnvRefs ¶
ExpandEnvRefs 对声明式头部表逐值做 ${VAR} 展开(nil/空表原样返回 nil)。 onMissing 可 nil(未定义变量仍保留原样,只是无告警出口)。
func IsToolResultError ¶
IsToolResultError err 链上是否存在工具业务失败(isError:true)。
func OutputSchemaOf ¶
func OutputSchemaOf(t tool.BaseTool) (json.RawMessage, bool)
OutputSchemaOf 取工具的声明 outputSchema(尽力:装饰器链各自转发;断言失败或 未声明返回 false)。
func UnwrapMCPText ¶
UnwrapMCPText 解 MCP 工具返回的信封取内层文本;非信封形态原样返回——快照/ 结果进证据条目时,snippet 配额应留给有效数据而非包装层。 信封两种形态:content[].text(标准工具结果,人类可读文本优先);缺失时 structuredContent(较新 MCP 规范的结构化结果)序列化为 JSON——直接把结构化 对象丢给 LLM 优于丢弃。
func WithCallMeta ¶
WithCallMeta 在 ctx 上注入任务级调用元数据(Runner/任务层调用一次,随 ctx 流入该任务全部 MCP 工具调用)。
Types ¶
type OAuthClientConfig ¶
type OAuthClientConfig struct {
ClientID string // 空=依赖 IdP DCR(handler.RegisterClient 由调用方驱动)
ClientSecret string
Scopes []string
MetadataURL string // 空=从 base URL .well-known 发现
RedirectURI string // 授权回调落点(无头服务端=固定平台端点)
TokenStore client.TokenStore
}
OAuthClientConfig URL 传输的 OAuth2 装配输入(映射 mcp-go client.OAuthConfig; TokenStore 必填——缺省会落内存 store,进程内自愈场景重连后丢授权态)。
type OutputSchemer ¶
type OutputSchemer interface {
OutputSchema() json.RawMessage
}
OutputSchemer 工具出参 schema 的可选能力接口。
type Pool ¶
type Pool struct {
// OnError 建连/列举失败回调(nil 安全)。返回错误不中断其余 server——
// 部分失败容忍:可用的工具照常返回,失败的 server 由调用方经钩子观测。
OnError func(server string, err error)
// RequestMeta 每笔工具调用 _meta 注入来源(nil 安全;批次五十六 B,对标 ZCode
// request-context)。metaFn 在调用方 ctx 上取值,池不感知业务键;经 metaClient
// 装饰器注入 req.Params.Meta(见 meta.go)。返回 nil/空 = 不注入。
RequestMeta func(ctx context.Context) map[string]any
// Elicitation server→client 征集输入处理器(mcp-go v1.1 SEP-2322):工具执行中
// server 反向请求补充输入(表单/URL 模式)时回调;nil 时该类请求得到
// "no elicitation handler configured" 错误(调用失败呈现,与不声明能力的服务端
// 行为一致)。池级单点——建连/自愈 redial/Initialize 能力声明共用。
ElicitationHandler client.ElicitationHandler
// contains filtered or unexported fields
}
Pool MCP 连接池:lazy 建连(首次 Tools 时)、连接缓存复用、Close 全量回收。 闲置回收/探活租约面见 lease.go(ReapIdle/StartIdleReaper/PingServer)。
func NewPool ¶
func NewPool(cfgs ...ServerConfig) *Pool
NewPool 创建连接池(建连延后到 Tools 调用时——允许启动期 MCP server 未就绪)。 Name 为空的配置忽略;重名保留先到者(后者忽略)——配置错误尽早收敛为确定性 行为而非静默覆盖。
func (*Pool) Close ¶
func (p *Pool) Close()
Close 关闭全部缓存连接并拒绝后续建连(幂等);停止闲置回收协程。Close 后池不可复用。 锁序:先在 p.mu 内标记 closed 并摘出各 connState 指针,释放后再逐个收连接—— 绝不在持 p.mu 时抢 st.mu(与 dialAndRegister 的写入路径交叉会死锁)。
func (*Pool) Hints ¶
Hints 目录注解快照(server 名 → 工具名 → 注解)。server 未配置或未列举 → nil。 供装饰层做并发治理与风险分级;返回副本,调用方可自由持有。
func (*Pool) PingServer ¶
PingServer 探活指定 server 的缓存连接。死亡连接立即摘除出缓存并关闭,下次 调用透明重建——HTTP server 被停掉不派发断连回调,「无声死亡」只有显式探活 才能暴露(ZCode pool 同款问题与解法)。
探活方法按建连协商的协议代际分流(v0.10.29 审计修复——mcp-go v1.1 起现代 协议 2026-07-28 移除了 ping RPC,Client.Ping 在该代际是 no-op,曾让探活 虚报存活且 touch 给死连接续命、闲置回收永远收不走):
- legacy(≤2025-11-25):协议 ping;
- modern(2026-07-28+):改发真实轻量 RPC(tools/list)——比 ping 重, 但这是该代际下唯一能证明连接活着的信号。
返回 ErrNotConnected = 无缓存连接(不算失败);其余错误 = 探活失败(连接已摘除)。
func (*Pool) ReapIdle ¶
ReapIdle 关闭闲置超过 maxIdle 的缓存连接,返回回收数。闲置时钟 = 最近一次 建连/取工具/探活成功。被回收的 server 下次调用透明重建(evict+lazy 建连)。 在途工具调用(leaseUse 登记)中的连接不回收——原先契约要求 maxIdle 显著大于 最大工具超时,现在由在途计数直接挡下。
func (*Pool) StartIdleReaper ¶
StartIdleReaper 启动后台闲置回收协程:每 interval 周期执行 ReapIdle(maxIdle)。 Close 停止;重复启动幂等(只起一个)。
type ServerConfig ¶
type ServerConfig struct {
// Name 逻辑名:ToolSpec.Server 引用。
Name string
// Command stdio 传输:可执行 + 参数(子进程由本包启动与回收)。
Command []string
// Env stdio 子进程环境白名单:条目为纯变量名(如 "GITHUB_TOKEN")时按名从
// 当前进程透传;含 "=" 时按 KEY=VALUE 字面透传。未列出的变量一律不继承
// (基础集见 procx.ChildEnv),防止本进程密钥泄漏给外部 MCP server。
Env []string
// URL HTTP 传输(streamable http)。
URL string
// Headers HTTP 请求头(如 Bearer 鉴权)。
Headers map[string]string
// Timeout 连接 + Initialize 超时(默认 30s)。仅作用于建连期(dial +
// Initialize 的 ctx 预算),不约束后续请求——HTTP 传输的每请求兜底见
// RequestTimeout。
Timeout time.Duration
// RequestTimeout HTTP 传输的单请求兜底上限(默认 10 分钟)。mcp-go 的
// WithHTTPTimeout 直接设 httpClient.Timeout、作用于该连接的每一次请求
// (含 tools/call)——此前误用连接级 Timeout(30s)会让任何长工具调用
// 在 HTTP 传输下永远失败(v0.10.29 审计修复);工具调用的正常时长控制
// 归调用方 ctx,这里只是挂死兜底。stdio 不受影响。
RequestTimeout time.Duration
// ProbeTimeout 探活(PingServer)独立预算(默认 5s;租约面 lease.go——
// 探活是轻量协议 ping,不该吃连接级 30s 预算)。
ProbeTimeout time.Duration
// OAuth URL 模式 OAuth2 客户端配置(批次五十六 C,对标 ZCode oauth 全栈;
// 仅 URL 传输生效)。非 nil 时 dial 走 NewOAuthStreamableHttpClient——401 触发
// client.OAuthAuthorizationRequiredError(errors.As 可穿透池的 %w 包装),
// 调用方经 client.GetOAuthHandler 取 handler 驱动授权码+PKCE 流程;
// TokenStore 由调用方提供(进程内缓存失效后重 dial 仍能取回已授权 token)。
OAuth *OAuthClientConfig
}
ServerConfig 单个 MCP server 连接声明。 stdio(Command 非空)与 HTTP(URL 非空)二选一;都空视为无效配置(跳过并告警)。
type ToolHints ¶
ToolHints 工具注解归一(mcp.ToolAnnotation 子集,nil 视为规范缺省)。
func (ToolHints) ConcurrentSafe ¶
ConcurrentSafe 读读可并行(readOnly+idempotent,对标 ZCode concurrentSafe); 缺省注解按非安全处理——保守。
type ToolResultError ¶
type ToolResultError struct {
// contains filtered or unexported fields
}
ToolResultError MCP 工具执行了但业务失败(CallToolResult.IsError=true)。 v0.11 起导出:包外消费方(经 agentkit/mcp 组装工具表的调用方)可 errors.As 穿透 %w 包装直达本类型取 Payload,无须对 Error() 文案做文本匹配——多错误 聚合文本的 marker 分类不可靠。Error() 文案是历史识别锚点(observe 兜底与 既有测试消费),改文案须同步;新代码优先 errors.As / IsToolResultError。
func (*ToolResultError) Error ¶
func (e *ToolResultError) Error() string
func (*ToolResultError) Payload ¶
func (e *ToolResultError) Payload() []byte
Payload 原始 CallToolResult JSON(observe 层剥 content[].text 用)。
type ToolSpec ¶
type ToolSpec struct {
// Server ServerConfig.Name。
Server string
// Allow 工具名白名单;空 = 该 server 全部工具。
Allow []string
}
ToolSpec 工具白名单:某 server 的哪些工具暴露给 agent。
type ToolsResult ¶
type ToolsResult struct {
// Tools 装配成功的工具(跨 server 并行拉取,按 specs 序稳定)。
Tools []tool.BaseTool
// Failed server → 被容忍的失败原因(建连/列举失败、白名单无一命中)。
// 失败连接已摘除,下次调用透明重建。空 = 全部成功。OnError 仍发射同源
// 通知(遥测钩子),失败面的程序化消费一律走这里。
Failed map[string]error
}
ToolsResult Tools 的结果面(v0.10.38 破坏式:此前返回 []BaseTool,部分失败只 经 OnError 侧面观测——调用方在返回值层面无法区分「失败被容忍」与「真的没 工具」,只能把失败当空表静默降级)。