{"record":{"id":"26546ac536462d05","repo":"apache/shenyu","slug":"tool-definition-must-be-of-type-shenyutooldefinition-got","errorCode":null,"errorMessage":"Tool definition must be of type ShenyuToolDefinition, got: ${toolDefinition.getClass().getSimpleName()}","messagePattern":"Tool definition must be of type ShenyuToolDefinition, got: (.+?)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java","lineNumber":172,"sourceCode":"            // Execute the tool call through the plugin chain\n            return executeToolCall(originExchange, chain, sessionId, configStr, input);\n\n        } catch (Exception e) {\n            LOG.error(\"Failed to process tool call for '{}': {}\", toolDefinition.name(), e.getMessage(), e);\n\n            throw new RuntimeException(\"Tool execution failed: \" + e.getMessage(), e);\n        }\n    }\n\n    /**\n     * Validates and casts the tool definition to Shenyu-specific type.\n     *\n     * @return the Shenyu tool definition\n     * @throws IllegalStateException if tool definition is not of expected type\n     */\n    private ShenyuToolDefinition validateToolDefinition() {\n        if (!(this.toolDefinition instanceof ShenyuToolDefinition)) {\n            throw new IllegalStateException(\"Tool definition must be of type ShenyuToolDefinition, got: \"\n                    + this.toolDefinition.getClass().getSimpleName());\n        }\n        return (ShenyuToolDefinition) this.toolDefinition;\n    }\n\n    /**\n     * Extracts and validates the request configuration from the tool definition.\n     *\n     * @param definition the Shenyu tool definition\n     * @return the request configuration string\n     * @throws IllegalStateException if configuration is missing or invalid\n     */\n    private String extractRequestConfig(final ShenyuToolDefinition definition) {\n        final String config = definition.requestConfig();\n        if (!StringUtils.hasText(config)) {\n            throw new IllegalStateException(\"Request configuration cannot be empty\");\n        }\n        LOG.debug(\"Using request configuration with length: {} chars\", config.length());","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java#L154-L190","documentation":"ShenyuToolCallback requires its ToolDefinition to be a ShenyuToolDefinition (the ShenYu-specific subtype exposing requestConfig). validateToolDefinition throws IllegalStateException if the injected definition is a plain or foreign ToolDefinition implementation, because the request-config extraction would otherwise fail.","triggerScenarios":"Constructing ShenyuToolCallback with a McpServerFeature.SyncToolSpecification or Spring AI ToolDefinition that is not an instance of ShenyuToolDefinition — typically when a tool is registered by code that builds a generic ToolDefinition instead of ShenyuToolDefinition.","commonSituations":"Registering MCP tools programmatically with a hand-rolled ToolDefinition; upgrading Spring AI/MCP SDK so the callback receives a differently-typed definition; forgetting to wrap the spec via the Shenyu builder.","solutions":["Create tool specs using ShenyuToolDefinition (its builder) so the definition passed to ShenyuToolCallback is the right type.","Check how the tool specification is registered and ensure no adapter converts it to a base ToolDefinition.","After dependency upgrades, confirm the MCP integration still constructs ShenyuToolDefinition instances."],"exampleFix":"// before\nToolDefinition def = ToolDefinition.builder().name(\"t\").build();\n// after\nShenyuToolDefinition def = ShenyuToolDefinition.builder().name(\"t\").requestConfig(configJson).build();","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"ShenyuToolDefinition asShenyuToolDefinition(ToolDefinition d) {\n    if (d instanceof ShenyuToolDefinition s) {\n        return s;\n    }\n    throw new IllegalArgumentException(\"Expected ShenyuToolDefinition, got \" + d.getClass().getName());\n}","tryCatchPattern":"try {\n    return callback.call(args, ctx);\n} catch (IllegalStateException e) {\n    if (e.getMessage().startsWith(\"Tool definition must be\")) {\n        // rebuild tool spec with ShenyuToolDefinition and retry registration\n    }\n    throw e;\n}","preventionTips":["Always register MCP tools via the Shenyu builder that produces ShenyuToolDefinition.","Add an assertion at tool-registration time: assert definition instanceof ShenyuToolDefinition.","Re-run tool registration tests after upgrading Spring AI / MCP SDK versions.","Avoid ad-hoc ToolDefinition adapters in registration code."],"tags":["mcp","type-mismatch","tool-definition","ai"],"backgroundTag":"type-mismatch","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}