fix(mcp): register remote MCP tools when discovery succeeds - #1080
Open
yoruuuchan wants to merge 1 commit into
Open
fix(mcp): register remote MCP tools when discovery succeeds#1080yoruuuchan wants to merge 1 commit into
yoruuuchan wants to merge 1 commit into
Conversation
Remote MCP servers connected and cached their tools, yet the AI never saw them. Registration is what puts a plugin into MCPManager's server cache, which feeds the system prompt's "Available packages" section and package auto-activation, but it only ran from the startup batch. The MCP management screen has its own discovery path, MCPRepository.getRemoteToolNames: it connects, lists tools and writes them into server_status.json, which is exactly the reported "green status plus cachedTools but no AI tools" state, and it registered nothing. The startup batch also registered only after jobs.awaitAll(), so a stalled or failing plugin kept every other plugin, remote ones included, out of the tool list. Split the per-plugin work out of registerToolsForLoadedPlugins into an idempotent registerToolsForPlugin, call it from remote discovery once the tools are known while leaving disabled servers alone, and register each plugin in MCPStarter as soon as it verifies instead of after the fan-out. Fixes AAswordman#907
This was referenced Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
远程 MCP(
type: remote,connectionType: httpStream)握手成功、server_status.json里也写进了cachedTools,但工具从来不出现在 AI 的可调用列表里。根因
一个插件要让 AI 看见,必须进
MCPManager.serverConfigCache——SystemPromptConfig的 "Available packages" 段落和AIToolHandler.getToolExecutorOrActivate的包自动激活都读这里。而写进这个缓存的只有一个地方:MCPRepository.registerToolsForLoadedPlugins→MCPManager.registerServer。其它所有"把插件跑起来"的路径都只调registerRuntime,连接是通的,但对 AI 不可见。两条具体的断链:
MCP 管理页有自己的一条发现路径。
MCPRepository.getRemoteToolNames(MCPConfigScreen的LaunchedEffect对每个远程插件都会调)会registerRuntime+getOrCreateSession+listTools+cacheServerTools——绿灯和cachedTools就是这么来的——然后什么都不注册。issue 里"绿灯 + 有缓存 + AI 看不到"的状态正是这条路径的产物。启动批次的注册在整个扇出之后。
MCPStarter.startAllDeployedPlugins里registerToolsForVerifiedPlugins排在jobs.awaitAll()后面,所以只要有一个插件卡住或失败,其它插件(包括已经连上的远程插件)就一起拿不到注册。远程插件的注册被本地插件的部署/bridge 流程绑架了。改动
MCPRepository:把registerToolsForLoadedPlugins的单插件逻辑拆成幂等的registerToolsForPlugin(pluginId)(已注册的工具跳过),批量版本改成循环调用它。MCPRepository.getRemoteToolNames:发现(缓存命中或实时握手)拿到工具名之后,对已启用的远程插件调一次registerToolsForPlugin。被禁用的服务只用于展示,不注册。MCPStarter:把注册挪进processPlugin,插件一验证通过就注册,删掉awaitAll()之后的批量注册。generateMissingDescriptions保持在批次末尾不动。本地插件的行为没有变化:
processPlugin对 local/remote 是同一段代码,注册函数本身也没改逻辑,只是调用时机提前到了每个插件自己完成的那一刻。验证方式
Windows 上跑不了完整 Gradle 构建(缺 NDK/CMake 与 terminal submodule),所以用了三层验证。
1. 生命周期 A/B(复现 + 修复)。 用 Python 按签名从
MCPRepository.kt里逐字抠出getRemoteToolNames/discoverRemoteToolNames/registerToolsForLoadedPlugins/registerToolsForPlugin/getToolsForPlugin/createRuntimeDescriptor,同一个抽取器分别跑git show upstream/dev:...和本分支,配上MCPLocalServer/MCPManager/AIToolHandler的桩(MCPManager.registerServer/registerRuntime的语义按真实实现照抄),用 gradle 自带的 kotlinc 编译后运行。场景 1 = "用户打开 MCP 管理页,远程服务握手成功":
baseline 那一行就是 issue 描述的状态:工具缓存写进去了,AI 侧空的。另外四个场景在两边都跑:缓存命中的发现、被禁用的服务(两边都不注册,保持隐藏)、重复触发(3 次
getRemoteToolNames+ 1 次批量注册 →registerTool仍然只调 2 次,服务器条目 1 个,没有因为描述符不等而丢会话)、以及启动批次路径(两边都正常注册,回归保护)。2. 新增单测。
app/src/test/java/.../core/tools/mcp/McpServerRegistrationTest.kt,锁住 #907 里被混淆的那条边界:只registerRuntime不等于对 AI 可见;registerServer之后 endpoint 能被取到;重复注册只留一个条目;相同元数据重建出的Remote描述符相等(registerRuntime靠这个判断才不会在每次刷新时把活着的会话关掉);unregisterServer能撤回。本地用真实的MCPManager/McpRuntimeDescriptor/MCPServerConfig编译运行:OK (5 tests)。3. 解析检查。 两个改动文件分别与
upstream/dev版本做 kotlinc 错误多重集 diff(项目符号在孤立编译里本就无法解析,看的是增量):MCPRepository.kt:unresolved reference 'MCPLocalServer'+1(discoverRemoteToolNames新增的显式参数类型)、unresolved reference 'disabled'+1(新增的!metadata.disabled)、cannot infer type for this parameter−2(去掉了那个空转的runBlocking包装)。没有新的错误种类。MCPStarter.kt:unresolved reference 'MCPRepository'+1(processPlugin新增参数类型)、unresolved reference 'registerToolsForPlugin'+1。其余完全一致。ci/script/check_localizations.py、check_repo_hygiene.py、check_markdown_links.py对upstream/dev...HEAD均为errors=0 warnings=0。一并发现但没有改的
startAllDeployedPlugins里initBridge()排在插件扇出之前,且整段在同一个 try 里。只要有本地插件,远程插件就得等本地 bridge / 终端环境先跑完;#907 的关联反馈里贴的启动日志正是"只见到 127.0.0.1:32145 的 bridge 尝试,之后再无任何远程 endpoint 请求"。把远程插件从 bridge 初始化上解耦需要重排这段启动流程,terminal submodule 在我这边没有 checkout,initializeEnvironment()到底会不会抛我无法确认,所以没有动,留给维护者判断。另外MCPStarter.startPlugin/verifyPlugins目前全仓没有调用点,也一并保持原样。