Jaeger AI Gateway 统一 MCP 工具路由(RFC 0008)深度解析:从 ACP 扩展方法到网关托管 MCP 服务器的架构演进
2026/9/13 18:20:50 网站建设 项目流程

Jaeger AI Gateway 统一 MCP 工具路由(RFC 0008)深度解析:从 ACP 扩展方法到网关托管 MCP 服务器的架构演进

【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger

本篇技术指南以 docs/rfc/0008-ai-gateway-mcp-tool-routing.md 为核心骨架,结合当前仓库中 AI Gateway(jaegerai包)、mcptools库与 jaeger-query 服务装配代码的源码证据,完整剖析 Jaeger 将"每一个 AI 工具调用"(遥测工具与 UI 工具)收敛到单一网关托管 MCP 分发面的设计动机、术语规约、目标架构、实现状态与后续路线图。读完本文,你将理解:为什么 RFC 0002 曾拒绝而 RFC 0008 又复活"网关托管 MCP 服务器";/api/ai/mcp/(共享端点)与/api/ai/mcp/<mcpRouteID>/(turn 作用域端点)两个挂载点如何并存;ai.mcp_base_url回环地址推断的完整判定逻辑;以及 UI 工具 fire-and-forget 分发与jaeger_mcp独立扩展退役的前因后果。


1. 背景与动机:三个叠加的结构性问题

RFC 0008 开篇用"MCP 运动"(MCP movement)概括其目标:把仓库里每一个 AI sidecar 的工具调用——无论遥测还是 UI——全部经由网关托管的单个 MCP 分发面收敛。在此之前,每个 sidecar 都重复实现同一套管道:一个拨号 Jaeger 遥测工具的 MCP 客户端、一份逐 turn 的 UI 工具处理逻辑、一个 ACP WebSocket。更严重的是,两类工具调用走了两条不同的路径

  1. 遥测工具调用完全绕过网关。每个 sidecar 内嵌自己的 MCP 客户端,直接拨号独立的jaeger_mcp:16687服务器。网关看不到这些调用,因此无法对它们进行追踪(tracing)、日志、鉴权或限流。RFC 将其称为控制反转(inversion-of-control, IoC)违规——这是核心问题。
  2. 每个 sidecar 要维护两套分发协议。UI 工具走agent → gateway → browser,通过 ACP 扩展方法_meta/jaegertracing.io/tools/call;遥测工具走 sidecar 自己的 MCP 客户端。每个 sidecar 作者都必须同时实现并理解两者。
  3. 逐 sidecar 重复造轮子。MCP 客户端与 UI 工具机制在 Python Gemini sidecar、进行中的 Claude Code 桥接(RFC 0008 引用的 PR #8631)以及任何未来的 sidecar 中被反复重写。

值得注意的范畴差异:RFC 0002 解决的是UI 工具分发;RFC 0008 的驱动问题是遥测工具分发与 sidecar 之间的重复建设——这是一个不同的、重新打开了 RFC 0002 已关闭设计决策的问题。


2. 术语规约(M4 里程碑已落地):先正名,再谈设计

RFC 0008 §2 指出,该子系统最大的可读性问题是一词多义:"session" 指代四种不同的东西,"stream" 三种,前端工具有两个名字("contextual" 和 "UI"),两个端点又被冠以想要保留给协议会话的单词。因此 §2 在给出设计之前先固定词汇表——因为后续所有内容都依赖它。

治理原则(已决):

  • "session"——仅指协议级会话:ACP sessionsession/new)与MCP 传输 sessionMcp-Session-Id,由 SDK 管理)。绝不指网关内部的逐 turn 簿记。
  • "turn"——一次聊天交换 = 一次POST /api/ai/chat= 一次 run = 一个 ACP session 生命周期。网关的逐 turn 状态使用该词。
  • "stream"——仅指到浏览器的 SSE 通道。
  • "UI tools"——前端声明、浏览器执行的工具。统一为这一个名字,不再叫 "contextual tools"。
  • 两个 MCP 挂载点命名为共享端点(shared endpoint,无 turn id,仅遥测)与turn 作用域端点(turn-scoped endpoint,逐 turn,遥测 + 该 turn 的 UI 工具)。
  • 处理器按协议层命名——HTTP handler、ACP handler、MCP middleware——而非 "dispatcher" 这类泛化动词。
  • 按路由的实现称为端点endpoint_chatendpoint_turn_mcp),由网关 HTTP handler 挂载。
  • 外部 AG-UI 标识符(threadIdrunId)保持不变——它们属于该协议。

概念与标识符对照表:

概念原命名提议命名
服务器铸造的逐 turn 路由 id(URL 路径段 + 注册表键)mcpSessionID{sessionID}mcpRouteID{mcpRouteID}
前端/浏览器执行的工具"contextual tools" 与 "UI tools" 并用UI tools(单一名字)
无状态遥测挂载点"session-free endpoint"共享端点
逐 turn 挂载点"session-scoped endpoint"turn 作用域端点
ACP session idACPSessionId不变(真正的 session)
MCP 传输 session idMcp-Session-Id不变(SDK 所有)

代码组件(文件与类型)映射(RFC 原文 §2 表格,均已在当前仓库落地为 M4 命名):

文件 → 类型角色问题提议命名
session_streams.gosessionStreams,session逐 turn 注册表 + 逐 turn 状态"session"+"stream" 粘在同一张 map 上turn_registry.goturnRegistry,turnState
contextual_tools.goContextualToolsStore扩展方法 UI 工具存储(遗留路径)"contextual" 是 "UI" 的第二个名字随扩展方法在 M6 删除;此前沿用 UI-tools 词汇
handler.goHandler(+RegisterRoutes)网关HTTP handler泛化 "handler";routes.go只有 5 行http_handler.go(吸收routes.go
handler.goChatHandlerchat 端点/api/ai/chat它是端点而非 "handler"endpoint_chat.gochatEndpoint
mcp_endpoint.gomcpSessionHandlerturn 作用域 MCP 端点"session";且它不是全部 MCPendpoint_turn_mcp.goturnScopedEndpoint
dispatcher.gonewDispatcher入站ACP handler"dispatcher" 过度抬高普通 handleracp_handler.goacpHandler
mcp_ui_tools.gouiDispatchMiddlewareMCP middleware"dispatch" 是泛化动词uiToolsMiddleware(内部dispatch*助手同理)
streaming_client.gostreamingClient到浏览器的 SSE 写入器"stream" 保留给 SSE,因此一致不变
routes.goHandler上的 5 行RegisterRoutes整个文件只为放一个方法删除——并入http_handler.go
translation.gotrace_propagation.gows_adapter.goAG-UI↔ACP 翻译、追踪传播、WebSocket 适配不变

mcpRouteID而非turnID/runID的理由:后两者与客户端侧的runId冲突(三个逐 turn id 生命周期相同,仅凭来源与顺序区分);streamID/streamKey会加重已饱和的 "stream" 词族;任何含 "session" 的命名都会与两个真实协议会话冲突。mcpRouteID按职责命名——路由某个 turn 的 MCP 回调——且不与任何现有用法重叠。callbackIDrendezvousID是可选同义词。

为什么现在改名是廉价的(RFC 原文论证):mcpSessionID{sessionID}路径段自 PR #8910 就已存在,但端点处于休眠状态——其 URL 在 PR #9009 之前未向任何 sidecar 公布,也没有 sidecar 消费这些名字——因此外部没有东西绑定它们。一旦 URL 公布并形成 sidecar 契约(M6),改名就会变贵。当前仓库代码已按新命名落地,例如 turn_registry.go 中的mcpRouteID参数、register/get方法。


3. 修正先前的记录:基线架构与两个被推翻的前提

RFC 0008 §3 指出先前的记录(RFC 0002、内部 Tool Routing Design doc、issue #8890)中已有不准确或从未准确的表述,并逐条修正。

3.1 运动开始前的基线架构

RFC 0008 §3.1 给出基线架构图:

Browser ──[AG-UI / HTTP+SSE]──► Gateway (jaeger-query :16686) ──[ACP / WebSocket]──► Sidecar ──► LLM │ │ UI tool call: Telemetry tool call: agent → gateway via ACP ext-method sidecar dials jaeger_mcp:16687 _meta/jaegertracing.io/tools/call DIRECTLY — bypasses the gateway (fire-and-forget; browser executes (gateway is blind to these calls) the side effect off the SSE stream)
  • chat 端点(POST /api/ai/chat)桥接 AG-UI ↔ ACP(RFC 0002 的设计,源码见 endpoint_chat.go)。
  • UI 工具(此遗留路径上叫 contextual tools):浏览器在每次聊天请求的RunAgentInput.tools中逐 turn 声明;网关将该快照附加到NewSessionRequest.Meta(命名空间键jaegertracing.io/contextual-tools,名称加ui_前缀)通告给 sidecar;sidecar 将其注册为 LLM 可调用工具。LLM 调用时,sidecar 经 ACP 扩展方法_meta/jaegertracing.io/tools/call分派回网关;网关 fire-and-forget 确认,并在浏览器 SSE 流上并行发出TOOL_CALL_*事件,由浏览器执行实际副作用(PR #8423,常量定义见 acp_handler.go 中的ExtMethodJaegerToolCall = "_meta/jaegertracing.io/tools/call"UIToolPrefix = "ui_")。
  • 遥测工具:由独立jaeger_mcpOTel 扩展在:16687端口提供服务,每个 sidecar 直接拨号。

3.2 RFC 0002 曾拒绝网关托管 MCP 服务器;更广泛的遥测问题使其复活

RFC 0002 §5.2 拒绝逐 turn 网关托管 MCP 服务器,§5.4 选择 ACP 扩展方法,驱动因素有二:(1)最小化数据流数量——复用唯一打开的 ACP WebSocket 而非新增连接;(2)假设 ACP 正是为此而生——agent 能在同一 ACP 连接上回送工具调用(即 MCP-over-ACP 将提供的能力)。

两个前提此后都发生了偏移:

  • (2) 是错的。如今 ACP 没有可用的同连接工具调用路径:MCP-over-ACP 是 UNSTABLE、未完成的草案(见 §3.3)。"复用 WebSocket 是干净选择"的假设不成立。
  • (1) 变得更糟而非更好。经由网关托管 MCP 服务器走 HTTP 路由工具调用增加了一条数据流——一条回到进程内的第二条连接,即便在localhost上——与最小化数据流相反。配置成本大多可避免:当整个往返都在回环上时网关推断回环 base URL(§4.3),只有其他拓扑需要ai.mcp_base_url覆盖。但额外的连接本身仍然存在。

因此网关托管服务器在 RFC 0002 自己的标准上并非严格获胜——这是有意的权衡,由一个RFC 0002 未解决的问题来证明其合理性:遥测工具调用完全绕过网关(§1),而目标是把网关放到每一个工具调用的路径上。与 RFC 0002 反对意见的调和点是:问题从来不是 agent 是否拨号一个 MCP 服务器——MCP 就是 agent 消费工具的方式——而是拨号哪一个:直接拨号jaeger_mcp(或任何外部服务器)让网关失明(违规),而拨号网关自己的MCP 端点则让网关保持在每次调用的路径上,能够看见、追踪、把关流量。接受额外数据流与 URL 配置,是这种可见性的代价。

行动项:RFC 0002 获得指向本 RFC 的横幅说明,注明其 Alternative B 在更广泛的问题陈述下复活;其历史分析原样保留(当前仓库中 docs/rfc/0002-ai-gateway-contextual-tools.md 顶部确实已加上 superseded note)。

3.3 MCP-over-ACP:ACP SDK 的真实状态

"一切保持在单条 WebSocket 上"的传输(MCP-over-ACP)很重要,因为整个"先 HTTP、后 ACP"的计划依赖它。它只存在于GoACP SDK(coder/acp-go-sdk)中——网关正是用 Go SDK。已发布的 Gemini sidecar 是 Python,进行中的 Claude Code sidecar(PR #8631)是运行在claude-agent-acp上的 Node.js——两者的 ACP SDK 都不提供该隧道。因此它是未来机会,而非今天任何 sidecar 可走的路径。在 Go SDK 中它也是 UNSTABLE、未完成的:固定的 v0.13.0 与 #8890 引用的 v0.13.5 在该特性上完全一致,且都只提供握手脚手架,而非即用隧道

  • mcp/connect/mcp/disconnect:仅握手管道。SDK 在 agent 侧生成发送器(AgentSideConnection.UnstableConnectMcp/UnstableDisconnectMcp),在 client 侧生成入站分派分支——但分派委托给消费者必须实现的可选接口方法(UnstableConnectMcp/UnstableDisconnectMcp),未实现则返回MethodNotFound。SDK 提供路由,不提供行为
  • mcp/message:仅类型。这是承载实际 MCP 载荷(tools/listtools/call)的方法。SDK 只有其类型与常量——与 connect/disconnect 不同,没有生成的发送器、没有分派分支。消费者必须用底层SendRequest/通知原语手工驱动。
  • 所有三者均显式标记UNSTABLE——每个都带有生成注释"not part of the spec yet, and may be removed or changed at any point"McpServer.Http是稳定的;Stdio是强制的)。

对设计的两点后果:

  1. 两端都有真功夫,没有一端"就绪"。网关必须自行实现 connect/disconnect 接口方法与整个mcp/message桥;而两个 sidecar 根本无法消费该传输,因为它只存在于 Go SDK 中。网关侧可行——POC PR #8854 用一个分派文件实现——但"先 HTTP"在两端都避免了实现工作,而不仅是 sidecar 侧。
  2. 传输是移动靶。MCP-over-ACP 是开放的草案 RFD,SDK 标记"随时可能移除或更改"。因此HTTP——稳定的 MCP 传输——是持久默认,MCP-over-ACP 是待 RFD 落定且两端实现后再添加的优化,而非把所有东西迁移过去的目的地。这与先前记录中"HTTP = 垫脚石,ACP = 目的地"的框架正好相反。

4. 目标设计:一个网关托管的 MCP 分发面

4.1 单一分发面:网关自己实现 MCP 方法

网关托管一个 MCP 服务器并自己实现MCP 方法(不是HTTP 重定向或透明代理):

MCP 方法行为
initialize通告tools能力。
tools/list返回遥测工具+调用方 turn 的 UI 工具(名称冲突时 UI 胜出)。
tools/call按名称路由。UI 工具:在浏览器 SSE 流上发出其TOOL_CALL_*事件(浏览器执行副作用),并立即向调用方返回合成的 "dispatched" 结果——网关不等浏览器(fire-and-forget)。其他任何名称:执行遥测工具并返回真实结果。

UI 与遥测的判定在网关的tools/call处理中只做一次——即叠加在 MCP 服务器上的 UI 工具中间件——网关完全观察它(M3 为其提供追踪)。sidecar 停止重复实现工具管道(到jaeger_mcp的工具翻译桥与独立的 UI 工具路径),只需把 MCP 客户端指向单一网关 URL。

源码证据mcptools.NewServer在 server.go 中注册 9 个遥测工具:get_servicesget_span_namessearch_tracesget_span_detailsget_trace_errorsget_trace_topologyget_critical_pathget_service_dependenciesread_skill(内含渐进式披露的 skill playbook,见 mcptools/README.md)。UI 工具由 mcp_ui_tools.go 的uiToolsMiddleware以接收中间件(receiving middleware)方式叠加:tools/list时把该 turn 的 UI 工具追加到遥测列表(同名时 UI 遮蔽遥测,appendUITools模拟AddTool的按名替换语义);tools/call时若名字属于该 turn 声明的 UI 工具,则调用emitUIToolCall返回合成 ack;其余调用穿透到遥测处理器。该中间件还顺带做了防御:空参数返回IsErrorCallToolResult而非 panic,畸形 UI 工具被跳过并记日志。

4.2 共享端点与 turn 作用域端点

查询端口上有两个挂载点:

/api/ai/mcp/ → shared — telemetry tools only; stateless; for external MCP clients (Cursor, IDEs) /api/ai/mcp/<mcpRouteID>/ → turn-scoped — telemetry + this turn's UI tools; for the sidecar mid-chat

turn 作用域路径携带逐 turn id(mcpRouteID),网关据此查该 turn 的 UI 工具快照与 SSE 流。共享端点替代独立的jaeger_mcp:16687,面向外部 MCP 客户端(Cursor、IDE 以及其他非 Jaeger 自家聊天 sidecar 的 AI 工具)。让 id 可选,正是一个实现服务两类受众的关键。

源码证据:共享端点由 server.go 的registerMCPTools挂载(mcptools.NewHandler+r.Handle(prefix+"/", ...));turn 作用域端点在 endpoint_turn_mcp.go 中定义路由常量:

const ( routeMCPPrefix = "/api/ai/mcp/" routeTurnMCPNoSlash = routeMCPPrefix + "{mcpRouteID}" routeTurnMCP = routeTurnMCPNoSlash + "/" )

同时注册带斜杠与不带斜杠两种形态是刻意的:若缺 no-slash 模式,拨号/api/ai/mcp/<id>(无尾斜杠)的客户端会落入共享子树模式而非 turn 作用域处理器。turnScopedEndpoint.ServeHTTP先查turnRegistry.get(mcpRouteID),未知或过期 id 返回 404("非活跃 turn"是客户端错误);随后剥离前缀、把mcpRouteID存入请求 context,再委托给mcptools.WrapHTTP包装的流式 HTTP 处理器。turn 注册表本身在 turn_registry.go 中实现:register铸造 UUID 路由 id 并返回幂等 closer(聊天处理器 defer 调用),get返回 nil 表示非活跃 turn。

4.3 传输选型:HTTP 为永久传输,MCP-over-ACP 推迟

turn 作用域端点通过HTTP(标准 streamable-HTTP MCP)提供,这是永久传输:它适用于任何会说 MCP 的 agent,没有替换计划。第二条传输 MCP-over-ACP 经评估后推迟为未来增强(§6)。

URL 公布:网关在NewSessionRequest.mcpServers中公布 URL,但仅当 agent 在其InitializeResponse中通告了mcpCapabilities.http——向无法消费的 agent 公布一种传输会让其会话失败。源码见 endpoint_chat.go 的announceMCPmcpRouteID == "" || h.mcpBaseURL == "" || !caps.McpCapabilities.Http时返回空列表,否则返回type: "http"name: "jaeger"url: h.mcpBaseURL + h.basePath + routeMCPPrefix + mcpRouteID + "/"McpServer

决策——整个往返都在回环上时推断回环 base URL;否则要求配置。ai.mcp_base_urlsidecar回拨网关的地址。未设置时,网关推断自己的回环地址,但仅当往返的每一段都成立:

  • ai.agent_url是回环地址(sidecar 同地部署,其localhost即网关的);
  • 查询服务器绑定到回环或通配符(绑定到单一具体接口如10.0.0.5:16686时回环上无人应答);
  • TLS 关闭(服务器证书的 SAN 是操作者拨号网关所用名称,几乎从不覆盖回环主机,推断出的https://URL 会在 sidecar 侧证书校验失败)。

这覆盖了常见的单主机部署,零配置。通配符绑定公布为localhost;回环绑定按原样公布(在双栈主机上localhost可能解析到网关未绑定的地址族)。任一段不满足,网关什么都不公布,而非公布一个有充分理由怀疑是错误的地址。

两个被否决的极端:无条件localhost 默认会向远端 sidecar(其agent_url非回环)公布一个坏地址,失败要等 agent 在 turn 中途拨号才暴露;无默认/一律不公布虽安全,却让每个同地部署都不得不配置网关本可轻松推断的地址。把推断限制在完整往返成立时,保留了二者安全的一半。

源码证据:完整实现位于 flags.go 的resolveMCPBaseURL(显式base_url优先;tlsEnabled!isLoopbackURL(AgentURL)boundHost非回环/非通配符、端口为0或空均返回"")、loopbackAnnounceHost(通配符 →localhost,回环 → 原样,具体非回环接口 →"")、isLoopbackURLhostIP(解析而非字符串匹配 "localhost",接受LOCALHOST/etc/hosts别名;localhost短路为 127.0.0.1;名称解析受hostLookupTimeout = 2s约束以免拖慢启动)。对应测试在 flags_test.go。该函数在 server.go 中以aiCfg.resolveMCPBaseURL(ctx, queryOpts.HTTP.NetAddr.Endpoint, queryOpts.HTTP.TLS.HasValue())调用——因为推断需要查询 HTTP 端点与 TLS 设置,它们属于QueryOptions而非AIConfig

限制——转发回环。路径上存在转发器时回环可达性不对称:容器中以-p 127.0.0.1:16688:16688发布、或经kubectl port-forward到达的 sidecar,网关在localhost可达,但 sidecar 自己的localhost是另一个网络命名空间,网关不在那里监听。配置中没有任何东西能区分这与真正的同地部署——两者字面上都是agent_url: ws://localhost:16688——因此这类部署必须设置ai.mcp_base_url。sidecar 的拨号随后失败;agent 是报告失败并在无工具状态下继续,还是直接使该 turn 失败,取决于具体 agent——这也是在session/new时记录已公布 URL 的理由,使原因无论哪种情况都可见。

可达性探针(M8,提议中)。网关可在启动时探测解析后的 base URL(推断或配置),类似 ACP agent 健康探针,使拼写错误或未绑定端口在启动时而非 turn 中途暴露。但探针运行在网关上,只能确认网关能到达该 URL,而非sidecar能——它在上述转发回环情形下会通过,而该情形恰恰是推断无法检测的。确认 sidecar 可达需要 agent 侧信号——例如该端点是否在 turn 中真的被拨号过。

传输对比表(🟢 好 / 🟡 部分 / 🔴 差):

判据扩展方法(遗留,已退役)HTTP(选定)MCP-over-ACP(推迟)
复用唯一打开的连接🟢🔴 二次 HTTP 回拨进进程🟢
一条路径覆盖遥测与 UI🔴 仅 UI🟢🟢
网关观察/把关每次调用(IoC)🟡 仅 UI🟢🟢
今天 sidecar SDK 能说🟢🟢🔴 Python/Node SDK 缺此能力
无需配置外部可达 URL🟢🟡 全回环部署可推断;否则ai.mcp_base_url🟢
标准 MCPtools/call(无自定义方法)🔴 Jaeger 自定义_meta/…/tools/call🟢🟢
稳定线缆契约🟡 Jaeger 定义:稳定但专用🟢 标准、稳定🔴 UNSTABLE 草案

HTTP 是唯一标准、稳定今天 sidecar 能说的选项;扩展方法退役(§4.4),MCP-over-ACP 推迟(§6)。

4.4 UI 工具分发今天即 fire-and-forget(实现限制),扩展方法退役

今天网关分发 UI 工具的方式:在浏览器 SSE 流上发出TOOL_CALL_*事件,并立即向调用方返回合成 ack——旧路径返回{acknowledged: true}acp_handler.gohandleJaegerToolCall),MCP 路径返回"…dispatched to the browser"CallToolResult(mcp_ui_tools.go 的emitUIToolCall)。网关不等待、也无法接收浏览器的结果。

这是当前实现的属性,而非 UI 工具或 AG-UI 的属性。浏览器↔网关这一段是单次POST /api/ai/chat,其响应是单向 SSE 流(服务器→浏览器);没有通道让浏览器在 turn 中途回传值,RFC 0002 §6.6 刻意选择合成 ack 而非构建回传通道(POST /api/ai/tool-result端点 + 每次调用 rendezvous 状态)。AG-UI 本身支持返回结构化结果的前端工具调用(外加 HITL 中断与共享状态同步),所以get_current_viewportread_selection这类查询型 UI 工具并非范畴性不可能——只是在浏览器→网关结果通道存在前不受支持。这超出本 RFC 范围(§6)。

agent→网关传输与此完全正交。在任何传输上,网关对 UI 工具调用返回 ack、对遥测调用返回真实结果,因此从 agent 侧看,调用无论哪种方式都是同步的——扩展方法的{acknowledged: true}是普通请求/响应,仅因没有浏览器结果可返回才只做 ack,而非传输承载不了结果。从 ACP 扩展方法切换到 MCPtools/call因此不触及浏览器段MCPtools/call路由取代自定义扩展方法——二者不共存:Gemini sidecar 迁移(M6)后,扩展方法路径——其在 ACP handler 中的 case 加上ContextualToolsStore——被移除。

源码证据acp_handler.gohandleJaegerToolCall的完整校验链——JSON 反序列化失败、空sessionId、空name、纯前缀名(ui_剥空)、未在前端快照中注册的名字,一律返回acp.NewInvalidParams;成功则记录 Info 日志(会话 id、剥前缀后的名字、前缀名、参数字节数——不记录参数内容以防 PII/超长/日志噪声,完整参数仅在 Debug 级输出)并返回{result: {acknowledged: true}, isError: false}。前缀缺失时不拒绝而是告警放行,以兼容分阶段前缀铺设期间的旧 sidecar。

4.5jaeger_mcp并入查询扩展

遥测工具不再位于独立jaeger_mcp扩展中;它们并入查询扩展,成为mcptools库(PR #8894)。这直接遵循 §4.1:

  • 将 UI 工具作为 MCP 工具提供,需要一个网关内部的 MCP 服务器——一个能访问逐 turn UI 状态的服务器——而网关位于查询扩展中。因此无论有没有jaeger_mcp,网内 MCP 服务器都必须存在。
  • 独立jaeger_mcp扩展(ADR-002)对查询扩展有运行时依赖:它通过GetExtension(host)在启动时获取QueryService。独立扩展边界除了那次耦合什么也没买到。
  • 既然网内 MCP 服务器反正需要,独立扩展就是冗余的。把jaeger_mcp的工具处理器并入查询扩展(它们本就把*querysvc.QueryService作为参数),消除了跨扩展耦合——mcptools.NewServer现在直接接收QueryService——并让一个 MCP 实现(mcptools库)同时支撑共享与 turn 作用域端点(今天经由两个服务器实例;合并为一个属清理里程碑 M7)。

独立扩展及其:16687监听器退役;查询端口(:16686)上的共享/api/ai/mcp/挂载点取代它们服务外部 MCP 客户端(Cursor、IDE 等非 Jaeger 自家聊天 sidecar 的 AI 工具)。

源码证据mcptools包文档明确写道"Package mcptools provides the Jaeger telemetry MCP tools as a reusable library";NewServer(telset, queryAPI, cfg)直接接收*querysvc.QueryService(注释解释这正是为了消除对 jaegerquery 扩展包的主机查找依赖与 import 环);NewHandlerNewServer+WrapHTTP的薄组合,供共享端点使用;WrapHTTP不自行绑定监听器,返回的 handler 由调用方挂到现有 mux。配置默认值在 config.go:DefaultMaxSpanDetailsPerRequest = 20DefaultMaxSearchResults = 100DefaultMaxReadFileSize = 512 * 1024mcpSessionTimeout = 5 * time.Minute(SSE 恢复与流 id 关联所需)。

4.6 外部 MCP 服务器:直通(pass-through),而非代理

网关在进程内服务 Jaeger 自己的遥测(§4.5);它代理其他 MCP 服务器。ACP 的NewSessionRequest.mcpServers是列表,因此对操作者可能配置的任何外部 MCP 服务器,架构立场是直通——网关公布它们,agent 直接拨号——而非经网关路由。经网关代理外部服务器以使其工具调用同样受到追踪与把关,是可能的未来增强(§6)。外部服务器配置与代理今天均未构建。


5. 实现状态与路线图

RFC 0008 顶部状态表完整罗列了各里程碑(截至文档更新时):

里程碑内容状态
M0基线:UI 工具经 ACP 扩展方法;遥测经独立jaeger_mcp:16687✅ 运动前已发布 — PR #8423
M1jaeger_mcp并入查询扩展:退役独立扩展;其工具成为mcptools库并直接接收QueryService✅ 完成 — PR #8894
M2turn 作用域 MCP 端点/api/ai/mcp/<id>/+ turn 注册表(PR #8910),随后经其提供遥测+逐 turn UI 工具(PR #8973)——在 URL 公布前保持休眠✅ 完成
M3工具调用可观测性:GenAI span 属性 + 网关↔sidecar 追踪传播✅ 完成 — PR #8942
M4术语清理——应用 §2(移除session/stream过载;UI 工具单一名字;重命名端点/注册表/id)✅ 完成 — PR #9017
M5HTTPai.mcp_base_url)向 sidecar 公布 turn 作用域端点✅ 完成 — PR #9009
M6将 Gemini sidecar 迁移到网关 MCP URL;删除其专用jaeger_mcp桥与扩展方法路径⏳ 待办
M7将共享与 turn 作用域挂载合并到一个mcp.Server(今天两个实例;turn 作用域中间件在无 turn 时已降级为仅遥测)⏳ 提议(清理)
M7.1收割共享端点的 MCP 会话(mcptools.WrapHTTP返回裸http.Handler,共享挂载无 closer,会话比查询服务器活得久;turn 作用域挂载已在 PR #9009 获得)⏳ 提议(清理)
M7.2UI 工具ai.enable_mcp解耦(该门控在 M6 移除 ACP 路径后会让enable_mcp: false+ 聊天开启的部署没有任何 UI 工具;需在 M6 前定夺)⏳ 提议
M8启动时探测ai.mcp_base_url可达性再公布(类似 ACP agent 健康探针)⏳ 提议
Claude Code sidecar(平行轨道;消费同一 URL)⏳ 进行中 — PR #8631

Spike(不并入主线):POC PR #8854 端到端验证了两种传输。

当前仓库中的源码对应

  • M1/M2 已落地mcptools库(server.go、config.go);jaegerai包中turnRegistryturnScopedEndpointuiToolsMiddleware与路由常量均已按 M4 命名(turn_registry.go、endpoint_turn_mcp.go、mcp_ui_tools.go、http_handler.go)。
  • M3 已落地mcptools的追踪/指标中间件(middleware.go)为tools/call生成gen_ai.tool.call.arguments/resultspan 属性(经maxSpanAttrChars = 65536截断,标记前置于首以在 UI 预览中幸存、且不伪装成可解析 JSON;按 rune 边界截断以保住 UTF-8),并记录jaeger.mcp.tool.calls计数器与jaeger.mcp.tool.duration直方图;网关侧injectTraceContextIntoMeta(trace_propagation.go)在 ACP prompt 边界按 SEP-414 约定注入traceparent/tracestate/baggage(复用jtracer已安装的全局复合传播器),sidecar 提取后即把自身 agentic-loop span 挂到该请求 span 之下,而非另起一条断开的 trace。
  • M5 已落地announceMCP(endpoint_chat.go)+resolveMCPBaseURL(flags.go)+ server.go 的装配;Handler.Close收割 turn 作用域端点的 MCP 会话(ServerSession.Close是 SDK 唯一暴露的拆除入口,Sessions()在锁下克隆快照,迭代中关闭安全)。
  • M7.1 现状佐证:共享端点的registerMCPTools直接返回http.Handler,无 closer 逻辑——正是 RFC 提议让mcptools拥有拆除逻辑的原因。

配置示例(当前仓库 cmd/jaeger/config.yaml)

extensions: jaeger_query: ai: agent_url: ws://localhost:16688 # 聊天 sidecar 的 ACP WebSocket 地址 # 在查询端口的 /api/ai/mcp/ 进程内提供 Jaeger 遥测 MCP 工具 # (取代已退役的独立 jaeger_mcp 扩展)。存在即启用端点;移除该块即禁用。 # 可选键:base_url(sidecar 回拨网关的地址,见 resolveMCPBaseURL 的推断逻辑), # skills_dir(操作者自定义 skill 目录,由 read_skill 在 custom/ 下提供)。 mcp: {}

全回环部署无需base_url;容器发布端口或kubectl port-forward的 sidecar 必须显式设置它。


6. 明确排除在外的未来增强

RFC 0008 §6 列出经考虑并刻意推迟的项,每项决策所在章节均已注明:

  • MCP-over-ACP 传输(§4.3、§3.3)。让工具流量保持在单条 WebSocket 上有吸引力,但该传输是 UNSTABLE、未完成的草案 RFD,可能永远不会定稿;其mcp/message桥未实现;没有任何 sidecar SDK 支持。HTTP 是永久传输;仅当 RFD 被接受且 SDK 实现后才可能重新评估。
  • 查询型 UI 工具(§4.4)。向 LLM 返回数据的 UI 工具(读 viewport、选择、表单值)。网关↔sidecar 协议中没有任何东西阻止它们;它们需要浏览器→网关结果通道与 UI 侧集成,应属未来的独立 RFC。
  • 网关代理外部 MCP 服务器(§4.6)。外部 MCP 服务器走直通——agent 直接拨号。经网关路由它们以使其工具调用同样受追踪与把关,是可能的后续增强。

7. 结论与参考脉络

RFC 0008 是一次针对既有 AI 网关工具路由决策的系统性再架构:以"网关位于每一次工具调用路径上"(IoC)为核心原则,把遥测工具从绕过网关的独立jaeger_mcp收编进查询扩展的mcptools库,把 UI 工具从 Jaeger 私有 ACP 扩展方法迁移到标准 MCPtools/call,并以共享/turn 作用域双端点 + 回环 base URL 推断覆盖了"外部 MCP 客户端"与"自家聊天 sidecar"两类受众,同时用一套严格术语规约(M4)消除了子系统内 "session/stream/handler/dispatcher" 的词汇过载。截至本文,M1–M5 已在当前仓库源码中落地,M6(Gemini sidecar 迁移与扩展方法退役)是下一个关键节点。

建议继续阅读的仓库资料:

  • docs/rfc/0008-ai-gateway-mcp-tool-routing.md — 本文依据的 RFC 原文(含完整状态表与附录来源)
  • docs/rfc/0002-ai-gateway-contextual-tools.md — 被本 RFC 部分取代的历史决策(顶部带 superseded banner)
  • docs/rfc/0012-mcp-server-extension.md — 原始 MCP 服务器扩展提案(含 9 个遥测工具的输入/输出 JSON 示例与jaeger_mcp目录结构)
  • docs/adr/002-mcp-server.md — MCP 服务器扩展的决策记录
  • jaegerai/README.md — AI Gateway 包的架构与组件说明(含 mermaid 图)
  • mcptools/README.md — MCP 端点启用、skills_dir自定义 skill 编写指南
  • flags.go、server.go —ai.mcp_base_url推断与端点装配源码
  • middleware.go — M3 工具调用可观测性实现

【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询