- channelbridge: 拨号失败的 WARN 不再带原始 dial 错误(含 ?access_token=), URLCause 剥壳 + 内嵌地址 MaskQuery 打码 + HostOf 保留目标来源;控制流零改动 - browser/cdp.go: CDP error.message 只有像协议枚举(ErrorToken)才出镜,其余只留字节数 - mcp: 第三方 server 的错误文本默认不带,只留 server/工具/JSON-RPC 码/message 长度, 分类链(errors.Is/As、context.Canceled)经 Unwrap 保留 - channel/adapter/flowgate: 审查输出摘要由 40 字节改 40 rune(防切 UTF-8)并过 RedactText 四处均补了会随回退变红的用例(变异已实测)。
72 lines
3.2 KiB
Go
72 lines
3.2 KiB
Go
package mcp
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
sdkjsonrpc "github.com/modelcontextprotocol/go-sdk/jsonrpc"
|
|
)
|
|
|
|
// 本文件是「第三方 MCP server 的错误怎么写进本包错误」的**唯一**收敛点。
|
|
//
|
|
// 为什么需要它:MCP server 是第三方进程/远端服务,它的错误文本是**上游自由文本** ——
|
|
// 它可以把刚收到的工具参数、读到的文件内容、甚至是它自己的系统提示词塞进
|
|
// `error.message`。原样经 SDK error 进本包错误,那条文本就会随 `errors.Is` 分类链
|
|
// 一路进日志、进管理面板、进模型可见视图。与 trace.UpstreamError 同一口径:
|
|
// **默认不带自由文本**,只留可分类的骨架。
|
|
//
|
|
// 留下的骨架(够定位,不够回显):
|
|
// - 哪台 server / 哪把工具 —— 由调用点的 %w 文案给(名字来自配置与 tools/list,
|
|
// 不是错误文本);
|
|
// - JSON-RPC code —— 协议枚举(-32601 method not found、-32602 invalid params …),
|
|
// 是本文件唯一允许出镜的「对端给的值」,且以数字渲染;
|
|
// - message 的**字节数** —— 能区分「对端给了句话」与「对端什么都没说」,不带那句话。
|
|
|
|
// upstreamFailure 是一次第三方 server 失败在本包错误链里的形态。它的 Error() 只
|
|
// 渲染 code 与长度;原始原因挂在 cause 上,只供 errors.Is / errors.As 分类。
|
|
//
|
|
// 为什么不直接把 SDK 的 err 用 %w 包上:%w 只加前缀,被包的那条文本仍然在
|
|
// err.Error() 里 —— 包一层不是修复,只是换个位置泄漏(同 trace.URLCause 的口径)。
|
|
type upstreamFailure struct {
|
|
// code 是 JSON-RPC 错误码;hasCode 为 false 表示这条错误不是线上响应里的
|
|
// 结构化错误(传输层失败、SDK 内部错误、ctx 取消等),此时不带任何码。
|
|
code int64
|
|
hasCode bool
|
|
// length 是被扣下的自由文本的字节数。
|
|
length int
|
|
// cause 只用于分类,绝不参与 Error()。
|
|
cause error
|
|
}
|
|
|
|
// Error 渲染一行只含 code / 长度的诊断;自由文本一个字节都不出。
|
|
func (f *upstreamFailure) Error() string {
|
|
if f.hasCode {
|
|
return fmt.Sprintf("mcp server error: code=%d message_bytes=%d", f.code, f.length)
|
|
}
|
|
return fmt.Sprintf("mcp server error: %T message_bytes=%d", f.cause, f.length)
|
|
}
|
|
|
|
// Unwrap 让 errors.Is/As 仍然看得见原始原因(context.Canceled、
|
|
// context.DeadlineExceeded、*jsonrpc.Error 的码…),而 Error() 绝不渲染它。
|
|
// 少了它,取消会被上层当成普通工具失败继续跑。
|
|
func (f *upstreamFailure) Unwrap() error { return f.cause }
|
|
|
|
// upstreamError 把一次第三方 server 失败收敛成**不带自由文本**的错误。
|
|
//
|
|
// 收 code 用的 errors.As 会遍历整条链:SDK 常把线上错误再包一层(fmt 包装、
|
|
// JSON-RPC 信封),所以不能只断言最外层。
|
|
func upstreamError(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
failure := &upstreamFailure{cause: err, length: len(err.Error())}
|
|
var wire *sdkjsonrpc.Error
|
|
if errors.As(err, &wire) {
|
|
failure.code = wire.Code
|
|
failure.hasCode = true
|
|
// WireError.Error() 就是 Message 本身,所以这里量的正是被扣下的那段文本。
|
|
failure.length = len(wire.Message)
|
|
}
|
|
return failure
|
|
}
|