@Tool returnDirect Attribute Ignored by DefaultToolCallingManager in MCP Integration
Bug description
When integrating an MCP service with Spring AI, the returnDirect
attribute in @Tool
annotations is being ignored during execution. The DefaultToolCallingManager
fails to retain this parameter in method metadata, causing MCP services to always perform an extra LLM call even when returnDirect = true
.
Comment From: sunyuhan1998
In my opinion, this attribute currently cannot take effect on the MCP server side, for the following reasons:
The normal workflow of the returnDirect
attribute is as follows:
- The model parses the list of tools and decides whether to invoke a tool and which one to invoke based on the user's prompt.
- Execute the tool call.
- All model implementations in Spring AI support the
returnDirect
attribute. If this attribute is set totrue
, the tool call will not be sent to the LLM, but instead returned directly to the user; otherwise, the result of the tool execution will be sent to the model.
This workflow presents some issues in the MCP scenario:
All tools are defined on the MCP server side, and the MCP client obtains the tool definitions from the server and requests the server to invoke the corresponding tool.
However, according to the MCP specification for tools: listing-tools, the tool itself does not have an attribute like returnDirect
, It has only a few attributes, such as name
, description
, and inputSchema
:
{
"name": "get_weather",
"description": "Get current weather information for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or zip code"
}
},
"required": ["location"]
}
}
As a result, the client has no way of knowing whether the tool should be returned directly to the user without being sent to the LLM.
In the following code, we can see that when the MCP server exposes the tool definitions, it converts ToolCallback
into the MCP-standard McpSchema.Tool
, at which point the returnDirect
property is lost:
https://github.com/spring-projects/spring-ai/blob/161c437556a3f79761c8d8991f1fa22f68cbc78e/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java#L167-L188
Then, when the client attempts to execute SyncMcpToolCallback
or AsyncMcpToolCallback
, since it does not (and cannot) implement the ToolMetadata getToolMetadata()
method, it invokes the default implementation of the ToolCallback
interface:
https://github.com/spring-projects/spring-ai/blob/161c437556a3f79761c8d8991f1fa22f68cbc78e/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java#L40-L42
In this default implementation, the returnDirect
attribute is set to false:
https://github.com/spring-projects/spring-ai/blob/161c437556a3f79761c8d8991f1fa22f68cbc78e/spring-ai-model/src/main/java/org/springframework/ai/tool/metadata/DefaultToolMetadata.java#L31-L34
Therefore, I believe that at this point in time, unless the MCP protocol itself supports this attribute and the Java SDK for MCP also provides support for it, this capability cannot be supported in the MCP scenario.
What do you think? I'm looking forward to your thoughts. @tzolov
Comment From: sunyuhan1998
the same issue: #3400
Comment From: chinaxwl
可以看到MCP的tool还有一个annotations的扩展属性,是否可以将returnDirect赋值到这个属性里呢?
Comment From: sunyuhan1998
可以看到MCP的tool还有一个annotations的扩展属性,是否可以将returnDirect赋值到这个属性里呢?
So far, I'm afraid not, because Spring AI's support for MCP is based on the Java SDK implementation of MCP. As of now, this SDK does not support the annotations
attribute in Tools.
Comment From: sunyuhan1998
I've submitted an issue and PR in MCP's Java-sdk to try to move this forward: https://github.com/modelcontextprotocol/java-sdk/issues/312
Comment From: sunyuhan1998
Hi @mylater @chinaxwl ! There's good news, the previous issue and PR about expecting support for Tool
's annotations
property in MCP's java-sdk: https://github.com/modelcontextprotocol/java-sdk/issues/312 has been passed and merged, I think When the next version of MCP's java-sdk is released (maybe v0.11.0?), we will be able to integrate it in Spring AI and use it to solve the problem that we are discussing in the current issue, I will keep an eye on MCP's java-sdk release, and submit a PR to Spring AI as soon as it is available!
Comment From: babywait
In my opinion, this attribute currently cannot take effect on the MCP server side, for the following reasons:
The normal workflow of the
returnDirect
attribute is as follows:
- The model parses the list of tools and decides whether to invoke a tool and which one to invoke based on the user's prompt.
- Execute the tool call.
- All model implementations in Spring AI support the
returnDirect
attribute. If this attribute is set totrue
, the tool call will not be sent to the LLM, but instead returned directly to the user; otherwise, the result of the tool execution will be sent to the model.This workflow presents some issues in the MCP scenario:
All tools are defined on the MCP server side, and the MCP client obtains the tool definitions from the server and requests the server to invoke the corresponding tool. However, according to the MCP specification for tools: listing-tools, the tool itself does not have an attribute like
returnDirect
, It has only a few attributes, such asname
,description
, andinputSchema
:
{ "name": "get_weather", "description": "Get current weather information for a location", "inputSchema": { "type": "object", "properties": { "location": { "type": "string", "description": "City name or zip code" } }, "required": ["location"] } }
As a result, the client has no way of knowing whether the tool should be returned directly to the user without being sent to the LLM.
In the following code, we can see that when the MCP server exposes the tool definitions, it converts
ToolCallback
into the MCP-standardMcpSchema.Tool
, at which point thereturnDirect
property is lost:spring-ai/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java
Lines 167 to 188 in 161c437
public static McpServerFeatures.SyncToolSpecification toSyncToolSpecification(ToolCallback toolCallback, MimeType mimeType) {
var tool = new McpSchema.Tool(toolCallback.getToolDefinition().name(), toolCallback.getToolDefinition().description(), toolCallback.getToolDefinition().inputSchema());
return new McpServerFeatures.SyncToolSpecification(tool, (exchange, request) -> { try { String callResult = toolCallback.call(ModelOptionsUtils.toJsonString(request), new ToolContext(Map.of(TOOL_CONTEXT_MCP_EXCHANGE_KEY, exchange))); if (mimeType != null && mimeType.toString().startsWith("image")) { return new McpSchema.CallToolResult(List .of(new McpSchema.ImageContent(List.of(Role.ASSISTANT), null, callResult, mimeType.toString())), false); } return new McpSchema.CallToolResult(List.of(new McpSchema.TextContent(callResult)), false); } catch (Exception e) { return new McpSchema.CallToolResult(List.of(new McpSchema.TextContent(e.getMessage())), true); } }); } Then, when the client attempts to execute
SyncMcpToolCallback
orAsyncMcpToolCallback
, since it does not (and cannot) implement theToolMetadata getToolMetadata()
method, it invokes the default implementation of theToolCallback
interface:spring-ai/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java
Lines 40 to 42 in 161c437
default ToolMetadata getToolMetadata() { return ToolMetadata.builder().build(); } In this default implementation, the
returnDirect
attribute is set to false:Lines 31 to 34 in 161c437
public static final class Builder {
private boolean returnDirect = false;
Therefore, I believe that at this point in time, unless the MCP protocol itself supports this attribute and the Java SDK for MCP also provides support for it, this capability cannot be supported in the MCP scenario.
What do you think? I'm looking forward to your thoughts. @tzolov
I am facing the problem: I want the value of returnDirect attribute in SyncMcpToolCallback is true. My opinion: the value of returnDirect attribute should be determined by the mcp client. because: a mcp server can be used by anyone in any application, different application scenario has different purpose.
Comment From: sunyuhan1998
I am facing the problem: I want the value of returnDirect attribute in SyncMcpToolCallback is true. My opinion: the value of returnDirect attribute should be determined by the mcp client. because: a mcp server can be used by anyone in any application, different application scenario has different purpose.
I think, regardless of whether the client decides to directly return the result of a tool call to the user, the server should still correctly return the returnDirect
attribute to the client. This is because returnDirect
is one of the inherent properties of the tool itself. As for the behavior after it is returned to the client, we can make further decisions on the client side.