openJiuwen agent-core-java 的 MCP 客户端注册门面 McpClients:ClientRegistry 动态创建机制与 clientType 归一化全解析

发布时间:2026/10/14 18:23:19
openJiuwen agent-core-java 的 MCP 客户端注册门面 McpClients:ClientRegistry 动态创建机制与 clientType 归一化全解析 人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习【免费下载链接】agent-core-javaopenJiuwen agent core Java是 openJiuwen Core的 Java版本提供AI Agent开发、运行、调优与演进相关的全套SDK能力。项目地址https://gitcode.com/openJiuwen/agent-core-java点击查看免费下载McpClients是 openJiuwen agent-core-java 中负责把 SDK 内置 MCP transport client 注册到全局ClientRegistry的注册门面它补齐了 Python 版BaseClient.__init_subclass__的自动注册语义。本文以 McpClients.md 为骨架结合 McpClients.java 等源码实现系统讲解其类结构、默认注册项、clientType归一化规则、调用链路与测试验证。读完本文你将掌握MCP client 如何在ResourceMgr、ToolManager场景下按clientType动态创建以及如何通过ClientRegistry直接构建 SSE、Stdio、Streamable HTTP、OpenAPI、Playwright 五类客户端。一、类概览MCP 客户端注册门面McpClients位于包com.openjiuwen.core.foundation.tool.mcp.client定义如下public final class McpClients这是一个最终类final class构造器为私有private McpClients()对外仅暴露静态方法属于典型的门面Facade模式。其内部持有常量CLIENT_TYPE mcp作为所有内置 MCP client 在ClientRegistry中的客户端类型标识。它的核心职责可以概括为一句话把 SDK 内置的 MCP transport client 工厂以幂等方式注册到全局ClientRegistry从而让ResourceMgr、ToolManager以及直接使用ClientRegistry的业务代码都能按照clientType动态创建 MCP client。背景说明在 Python 版 openJiuwen Core 中BaseClient.__init_subclass__会在子类定义时自动完成客户端注册而 Java 版没有__init_subclass__这类元编程机制因此需要McpClients.registerDefaults()这类显式注册入口来等价补齐该语义这一点在McpClients的 Javadoc 与FoundationToolMcpClientPackageMarker的注释中均有体现。1.1 主要方法方法返回值说明registerDefaults()void幂等注册 SDK 内置 MCP client 工厂。normalizeClientType(String clientType)String归一化用户传入的clientType用于 registry 查询。二、registerDefaults()幂等注册六个默认客户端registerDefaults()在源码中的实现如下见 McpClients.javapublic static synchronized void registerDefaults() { ClientRegistry registry ClientRegistry.getClientRegistry(); registerIfMissing(registry, sse, kwargs - new SseClient(requiredConfig(kwargs))); registerIfMissing(registry, stdio, kwargs - new StdioClient(requiredConfig(kwargs))); registerIfMissing(registry, streamable-http, kwargs - new StreamableHttpClient(requiredConfig(kwargs))); registerIfMissing(registry, streamable_http, kwargs - new StreamableHttpClient(requiredConfig(kwargs))); registerIfMissing(registry, openapi, kwargs - new OpenApiClient(requiredConfig(kwargs))); registerIfMissing(registry, playwright, kwargs - new PlaywrightClient(requiredConfig(kwargs))); }2.1 幂等性实现registerIfMissing方法声明为public static synchronized通过同步保证并发安全真正实现幂等的是私有辅助方法registerIfMissingMcpClients.javaprivate static void registerIfMissing(ClientRegistry registry, String name, ClientRegistry.ClientFactory factory) { String key CLIENT_TYPE _ name; if (!registry.listClients().contains(key)) { registry.registerClient(name, CLIENT_TYPE, factory); } }这里有一个值得注意的细节注册 key 的拼接规则是clientType _ name即mcp_sse、mcp_stdio、mcp_streamable-http等。而ClientRegistry.registerClient(name, clientType, factory)内部正是通过fullName(name, clientType)生成同样的mcp_xxx形式存入factories映射见 ClientRegistry.java。registerIfMissing通过先查listClients()再注册的方式避免了直接调用registerClient时因「重复注册抛IllegalArgumentException」而失败ClientRegistry.registerClient对已存在的 fullName 会抛出Client type xxx already registered。2.2 默认注册项与对应实现类registerDefaults()共注册6 个 key、5 个实现类streamable-http与streamable_http都指向StreamableHttpClientClientRegistrykey实现类mcp_sseSseClientmcp_stdioStdioClientmcp_streamable-httpStreamableHttpClientmcp_streamable_httpStreamableHttpClientmcp_openapiOpenApiClientmcp_playwrightPlaywrightClient各实现类均位于 client 包SseClientmcp_sse基于 SSEServer-Sent Events传输底层通过 Java SDKio.modelcontextprotocol.client.transport.HttpClientSseClientTransport实现连接时支持统一鉴权回调AuthCallback.unifiedAuthHandler。StdioClientmcp_stdio基于标准输入输出的子进程传输内置 JSON-RPC 会话JsonRpcProcessSession并带命令白名单校验默认允许java、python、python3等。StreamableHttpClientmcp_streamable-http/mcp_streamable_http基于 Streamable HTTP 传输底层使用HttpClientStreamableHttpTransport支持请求超时预算与传输断线后自动重连一次。OpenApiClientmcp_openapiOpenAPI 文件JSON/YAML驱动的 MCP 客户端会把 spec 中的paths解析为可调用的工具。PlaywrightClientmcp_playwrightPlaywright 浏览器会话类 MCP 客户端默认通过 stdio 参数启动浏览器驱动进程。2.3 工厂的 config 强校验requiredConfig每个注册的工厂都通过requiredConfig(kwargs)校验入参McpClients.javaprivate static McpServerConfig requiredConfig(MapString, Object kwargs) { Object value kwargs ! null ? kwargs.get(config) : null; if (value instanceof McpServerConfig config) { return config; } String actualType value null ? null : value.getClass().getName(); throw new IllegalArgumentException( MCP client factory requires kwargs[config] to be McpServerConfig, actual: actualType); }即MCP client 工厂要求kwargs中包含config且值必须是McpServerConfig类型否则抛出带实际类型信息的IllegalArgumentException。这一约束与文档「使用说明」中的第三条完全一致。三、normalizeClientType()clientType 归一化规则normalizeClientType(String clientType)的作用是把用户五花八门的传输类型写法大小写混用、下划线、连字符、mcp_前缀、camelCase 等统一为 registry 查询可用的标准形式。其源码如下McpClients.javapublic static String normalizeClientType(String clientType) { if (clientType null || clientType.trim().isEmpty()) { return sse; } String trimmed clientType.trim(); String camelSplit trimmed.replaceAll(([a-z])([A-Z]), $1-$2); String normalized camelSplit.toLowerCase(Locale.ROOT).replace(_, -); if (normalized.startsWith(mcp-)) { normalized normalized.substring(mcp-.length()); } return switch (normalized) { case sse - sse; case stdio - stdio; case streamable-http - streamable-http; case openapi, open-api - openapi; case playwright - playwright; default - normalized; }; }3.1 归一化处理流水线空值兜底null或纯空白字符串统一返回sse默认传输类型与McpServerConfig的默认clientType一致。去除首尾空白trim()。camelCase 拆分用正则([a-z])([A-Z])将streamableHttp拆分为streamable-Http。统一小写并替换下划线toLowerCase(Locale.ROOT).replace(_, -)将STREAMABLE_HTTP、mcp_streamable_http中的下划线全部换成连字符。剥离mcp-前缀仅当结果以mcp-开头时去掉该前缀。switch 归一化对open-api→openapi这类别名做最终归并未匹配的输入原样返回此时default - normalized。3.2 归一化对照表输入示例归一化结果null、空字符串sseSSE、mcp_SSEssestdio、mcp_stdiostdiostreamable_http、streamableHttp、STREAMABLE_HTTPstreamable-httpmcp_streamable_http、mcp-streamable-httpstreamable-httpopen-apiopenapiplaywrightplaywright值得注意的是归一化后的标准 key 是streamable-http连字符而registerDefaults()中同时注册了streamable-http与streamable_http两个别名 key因此在ClientRegistry中两种写法均可命中同一个StreamableHttpClient工厂这一点由McpClientsTest.registryFactoriesCreateConcreteClientsFromMcpServerConfig验证。四、使用说明三类调用路径文档明确了McpClients的三类使用场景结合源码可以还原出完整的调用链。4.1 ResourceMgr 与 ToolManager 路径自动确保注册ResourceMgr与ToolManager的 MCP Server 注册路径会自动确保默认 MCP client 已注册业务代码无需手动调用registerDefaults()。源码佐证如下ToolManager构造时以ToolManager::defaultCreateClient作为默认的 MCP client 工厂ToolManager.java而defaultCreateClient的完整逻辑为ToolManager.javaprivate static Object defaultCreateClient(McpServerConfig config) { try { return com.openjiuwen.core.foundation.tool.mcp.McpClientFactory.create(config); } catch (UnsupportedOperationException ignored) { McpClients.registerDefaults(); return ClientRegistry.getClientRegistry().getClient( McpClients.normalizeClientType(config.getClientType()), mcp, Map.of(config, config) ); } }这条路径的语义是优先走McpClientFactorySPI 机制基于ServiceLoader加载McpClientProvider支持扩展自定义传输类型若 SPI 层不支持该类型则回退到McpClients.registerDefaults()ClientRegistry的注册表路径。回退路径中清晰地示范了McpClients两个方法的组合用法McpClients.registerDefaults()确保内置工厂已注册McpClients.normalizeClientType(config.getClientType())把用户配置的clientType例如mcp_SSE归一化为sseClientRegistry.getClientRegistry().getClient(name, mcp, Map.of(config, config))按 key 动态创建客户端。ResourceMgr.addMcpServer(McpServerConfig)是 MCP Server 注册的入口ResourceMgr.java内部校验McpServerConfig后通过ToolManager完成客户端创建、连接与工具列表刷新。测试 ResourceMgrTest.java 展示了通过ClientRegistry.getClientRegistry().registerClient(clientType, mcp, ...)注册自定义 MCP 客户端、再经ResourceMgr.addMcpServer使用的完整链路。4.2 直接使用 ClientRegistry 路径先调用 registerDefaults()如果业务代码绕过ResourceMgr/ToolManager直接通过ClientRegistry.getClient(name, mcp, kwargs)创建 MCP client则应先调用McpClients.registerDefaults()完成工厂注册。典型写法McpClients.registerDefaults(); McpServerConfig config McpServerConfig.builder() .serverId(my-srv) .serverName(my-server) .serverPath(http://127.0.0.1:3001/mcp) .clientType(streamable-http) .build(); // 按归一化后的类型名从全局注册表创建客户端 Object client ClientRegistry.getClientRegistry().getClient( McpClients.normalizeClientType(config.getClientType()), mcp, Map.of(config, config) ); // client 为 StreamableHttpClient 实例4.3 kwargs 的 config 约束无论走哪条路径最终都会落到McpClients内部注册的工厂上因此都必须遵守同一条铁律kwargs中必须包含config键且其值必须是McpServerConfig类型。违反时抛出IllegalArgumentException消息形如MCP client factory requires kwargs[config] to be McpServerConfig, actual: null4.4 包级注册标记FoundationToolMcpClientPackageMarker另外包内还存在一个FoundationToolMcpClientPackageMarkerFoundationToolMcpClientPackageMarker.java镜像 Python 版openjiuwen/core/foundation/tool/mcp/client/__init__.py的语义其registerDefaults()直接委托给McpClients.registerDefaults()public static void registerDefaults() { McpClients.registerDefaults(); }五、McpServerConfig工厂入参的核心配置模型既然config必须是McpServerConfig理解其字段结构有助于正确构造工厂入参。McpServerConfig位于 McpServerConfig.java其关键字段如下字段类型说明serverIdString服务标识为空时默认取serverName再为空则生成无连字符的随机 UUIDserverNameString服务名称serverPathString服务地址HTTP 类传 URLstdio 类传命令路径OpenAPI 类传 spec 文件路径clientTypeString客户端类型默认值为sseparamsMapString, Object扩展参数如 stdio 的command/args/env、OpenAPI 的base_url等authHeadersMapString, String鉴权请求头authQueryParamsMapString, String鉴权 Query 参数connectTimeoutSecondsDouble连接超时秒callTimeoutSecondsDouble调用超时秒clientType字段由setClientType保证非空null时回落为sse。此外McpServerConfig.NO_TIMEOUT -1.0F是贯穿所有客户端方法的「无超时」哨兵值各客户端的connect/callTool等重载方法在未显式传超时时都会使用它。六、客户端实现差异速览六种默认注册的 MCP client 在传输层与超时语义上各有侧重理解其差异有助于选择正确的clientTypeSseClientsseconnect(int, double)返回CompletableFutureBoolean连接时先触发ToolCallEvents.TOOL_AUTH鉴权事件AuthTrigger.defaultAuth()→AuthCallback.unifiedAuthHandler默认 SSE 超时 60 秒操作失败时具备「断线重连一次」的自愈逻辑executeWithReconnectreconnectreconnectLock保证并发调用共享一次握手。StdioClientstdio通过ProcessBuilder拉起子进程并以换行分隔 JSON-RPC 通信resolveAllowedCommand提供命令白名单安全校验默认允许java/python/python3等命令且校验可执行文件的真实路径toRealPath()防止绕过白名单初始化握手协议版本为2024-11-05。StreamableHttpClientstreamable-http / streamable_http底层HttpClientStreamableHttpTransportrunWithTimeout通过有界线程池SynchronousQueue交接执行带超时的操作超时以HttpTimeoutException上抛便于上层分支处理retryAwareTimeout会把外部超时预算放大 2 倍以覆盖「重连 重试」的两段执行。OpenApiClientopenapiserverPath为 OpenAPI spec 文件路径支持多个逗号分隔connect时解析 JSON/YAML 生成工具集ToolManager内部注册表工具名优先取operationId冲突时自动追加_2、_3后缀去重超长名称截断至 64 字符。PlaywrightClientplaywright浏览器会话型客户端传输方式支持 stdioStdioClientTransport与 SSEHttpClientSseClientTransport由配置参数stdio_server_parameters等决定。七、测试验证McpClientsTestMcpClients的行为由 McpClientsTest.java 全量覆盖文档「相关测试」一节列的正是该测试类。其核心用例与文档的对应关系测试方法验证点registerDefaultsRegistersAllMcpClientFactoriesIdempotently连续调用两次registerDefaults()后ClientRegistry中同时包含mcp_sse、mcp_stdio、mcp_streamable-http、mcp_streamable_http、mcp_openapi、mcp_playwright六个 key —— 验证幂等性与双别名注册packageMarkerRegisterDefaultsDelegatesToMcpClientsFoundationToolMcpClientPackageMarker.registerDefaults()委托后mcp_sse可见registryFactoriesCreateConcreteClientsFromMcpServerConfig通过getClient(name, mcp, Map.of(config, config))依次创建出 6 个正确的实现类实例registryFactoryRejectsMissingOrWrongConfig缺config或config类型错误时抛RuntimeException消息包含MCP client factory requires kwargs[config]与McpServerConfignormalizeClientTypeSupportsCommonAliases覆盖文档归一化表中的全部输入样例此外ToolManagerTest.java 的defaultCreateClientRegistersMcpFactoriesAndNormalizesClientType从集成视角验证了ToolManager.defaultCreateClient对clientType mcp_SSE的配置能归一化并创建出SseClient且不会修改用户配置对象的clientType字段assertEquals(mcp_SSE, config.getClientType())。八、总结McpClients是 openJiuwen agent-core-java 中 MCP 客户端注册的最小而关键的入口registerDefaults()以幂等方式注册 6 个 key5 个实现类支撑ResourceMgr、ToolManager按clientType动态创建 MCP clientnormalizeClientType()提供宽容的传输类型归一化大小写、下划线、连字符、mcp_前缀、camelCase 均可默认回落为sseconfig 强约束保证工厂入参类型安全McpServerConfig是唯一合法的配置载体与 SPI 版McpClientFactory互为补充共同构成「可扩展优先、注册表兜底」的双通道客户端创建机制。实际使用时凡是直接走ClientRegistry的代码务必先调用McpClients.registerDefaults()而经由ResourceMgr/ToolManager注册 MCP Server 的路径则无需手动干预框架会自动完成默认注册与clientType归一化。赞分享人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习【免费下载链接】agent-core-javaopenJiuwen agent core Java是 openJiuwen Core的 Java版本提供AI Agent开发、运行、调优与演进相关的全套SDK能力。项目地址https://gitcode.com/openJiuwen/agent-core-java点击查看免费下载相关推荐agent-core-java InferenceAffinityModelClientFactory 详解InferenceAffinity 模型客户端的创建入口与注册机制agent core java InferenceAffinityModelClientFactory 详解InferenceAffinity 模型客户端的创人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习agent-core-java 中 DashScopeModelClientFactory 详解DashScope 模型客户端工厂的注册与创建机制agent core java 中 DashScopeModelClientFactory 详解DashScope 模型客户端工厂的注册与创建机制 本文以 c人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen Agent Core Java StreamableHttpClient 详解面向 Streamable HTTP 服务的 MCP 客户端openJiuwen Agent Core Java StreamableHttpClient 详解面向 Streamable HTTP 服务的 MCP 客户人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考