{"record":{"id":"1fd593e165c8ad61","repo":"alibaba/spring-ai-alibaba","slug":"remote-server-config-is-null","errorCode":null,"errorMessage":"Remote server config is null","messagePattern":"Remote server config is null","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-boot-starters/spring-ai-alibaba-starter-config-nacos/src/main/java/com/alibaba/cloud/ai/agent/nacos/tools/NacosMcpGatewayToolCallback.java","lineNumber":434,"sourceCode":"\t\t\t\t\targs = objectMapper.readValue(input, Map.class);\n\t\t\t\t\tlogger.info(\"[call] parsed args: {}\", args);\n\t\t\t\t}\n\t\t\t\tcatch (Exception e) {\n\t\t\t\t\tlogger.error(\"[call] Failed to parse input to args\", e);\n\t\t\t\t\t// 如果解析失败，尝试作为单个参数处理\n\t\t\t\t\targs.put(\"input\", input);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString protocol = this.toolDefinition.getProtocol();\n\t\t\tif (protocol == null) {\n\t\t\t\tthrow new IllegalStateException(\"Protocol is null\");\n\t\t\t}\n\n\t\t\tif (\"mcp-sse\".equalsIgnoreCase(protocol)) {\n\t\t\t\tMcpServerRemoteServiceConfig remoteServerConfig = this.toolDefinition.getRemoteServerConfig();\n\t\t\t\tif (remoteServerConfig == null) {\n\t\t\t\t\tthrow new IllegalStateException(\"Remote server config is null\");\n\t\t\t\t}\n\t\t\t\treturn handleMcpStreamProtocol(args, remoteServerConfig, protocol);\n\t\t\t}\n\t\t\telse if (\"mcp-streamable\".equalsIgnoreCase(protocol)) {\n\n\t\t\t\tlogger.error(\"[call] Unsupported protocol: {}\", protocol);\n\t\t\t\treturn \"Error: Unsupported protocol \" + protocol;\n\t\t\t\t// McpServerRemoteServiceConfig remoteServerConfig =\n\t\t\t\t// this.toolDefinition.getRemoteServerConfig();\n\t\t\t\t// if (remoteServerConfig == null) {\n\t\t\t\t// throw new IllegalStateException(\"Remote server config is null\");\n\t\t\t\t// }\n\t\t\t\t// return handleMcpStreamableProtocol(args, remoteServerConfig, protocol);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlogger.error(\"[call] Unsupported protocol: {}\", protocol);\n\t\t\t\treturn \"Error: Unsupported protocol \" + protocol;\n\t\t\t}","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-boot-starters/spring-ai-alibaba-starter-config-nacos/src/main/java/com/alibaba/cloud/ai/agent/nacos/tools/NacosMcpGatewayToolCallback.java#L416-L452","documentation":"NacosMcpGatewayToolCallback.call() routes MCP tool invocations by protocol. When the protocol resolves to 'mcp-sse', the callback requires the tool definition to carry a McpServerRemoteServiceConfig (service ref, export path, etc.). If that nested config object is absent, the gateway has no remote server to route the call to, so it throws this IllegalStateException instead of failing later with a confusing NPE.","triggerScenarios":"Calling ToolCallback.call() on a gateway callback whose ToolDefinition was built without remoteServerConfig while toolDefinition.protocol() equals 'mcp-sse' (case-insensitive).","commonSituations":"A Nacos-registered MCP service was converted into a tool definition without its remote server config block (missing mcpServerRemoteServiceConfig in the MCP server metadata pulled from Nacos), or tool definitions were hand-constructed/copied and the remoteServerConfig field was dropped.","solutions":["Verify the MCP service metadata in Nacos includes the remote server config (serviceRef/exportPath) and re-publish the service so the tool definition is regenerated with it.","Check the code path that builds the ToolDefinition for the gateway and ensure setRemoteServerConfig / the builder field is populated before registering the callback.","If the tool is genuinely not remote-capable, filter it out before call() instead of invoking it."],"exampleFix":"// before\nToolDefinition def = ToolDefinition.builder().name(name).description(desc).inputSchema(schema).build();\n// after\nToolDefinition def = ToolDefinition.builder().name(name).description(desc).inputSchema(schema)\n    .remoteServerConfig(mcpServerRemoteServiceConfig) // must be set for mcp-sse protocol\n    .build();","handlingStrategy":"validation","validationCode":"if (callback.getToolDefinition().getRemoteServerConfig() == null) {\n    throw new IllegalArgumentException(\"Tool definition missing remoteServerConfig for MCP gateway call\");\n}","typeGuard":"McpServerRemoteServiceConfig cfg = toolDefinition.getRemoteServerConfig();\nif (cfg == null || cfg.getServiceRef() == null) { /* skip or repair this tool */ }","tryCatchPattern":"try { return callback.call(args); }\ncatch (IllegalStateException e) {\n    if (e.getMessage().contains(\"Remote server config is null\")) { log.warn(\"skip tool: no remote config\"); return fallback; }\n    throw e;\n}","preventionTips":["Always populate remoteServerConfig when building MCP gateway tool definitions","Validate Nacos MCP metadata completeness before registering callbacks","Add a startup check that logs tools missing remote configs"],"tags":["nacos","mcp","null-config","illegal-state"],"backgroundTag":"missing-required-config-field","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}