Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public AsyncMcpToolCallback(McpAsyncClient mcpClient, Tool tool) {
@Override
public ToolDefinition getToolDefinition() {
return ToolDefinition.builder()
.name(this.asyncMcpClient.getClientInfo().name() + "-" + this.tool.name())
.name(McpToolUtils.prefixedToolName(this.asyncMcpClient.getClientInfo().name(), this.tool.name()))
.description(this.tool.description())
.inputSchema(ModelOptionsUtils.toJsonString(this.tool.inputSchema()))
.build();
Expand All @@ -107,7 +107,9 @@ public ToolDefinition getToolDefinition() {
@Override
public String call(String functionInput) {
Map<String, Object> arguments = ModelOptionsUtils.jsonToMap(functionInput);
return this.asyncMcpClient.callTool(new CallToolRequest(this.getToolDefinition().name(), arguments))
// Note that we use the original tool name here, not the adapted one from
// getToolDefinition
return this.asyncMcpClient.callTool(new CallToolRequest(this.tool.name(), arguments))
.map(response -> ModelOptionsUtils.toJsonString(response.content()))
.block();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ public final class McpToolUtils {
private McpToolUtils() {
}

public static String prefixedToolName(String prefix, String toolName) {

String input = prefix + "-" + toolName;

if (input == null || input.isEmpty()) {
throw new IllegalArgumentException("Input string cannot be null or empty");
}

// Replace any character that isn't alphanumeric, underscore, or hyphen with
// concatenation
String formatted = input.replaceAll("[^a-zA-Z0-9_-]", "");

// If the string is longer than 64 characters, keep the last 64 characters
if (formatted.length() > 64) {
formatted = formatted.substring(formatted.length() - 64);
}

return formatted;
}

/**
* Converts a list of Spring AI tool callbacks to MCP synchronous tool registrations.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.springframework.ai.mcp;

import java.util.Map;
import java.util.UUID;

import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
Expand All @@ -42,7 +41,9 @@
* <li>Manages JSON serialization/deserialization of tool inputs and outputs</li>
* </ul>
* <p>
* Example usage: <pre>{@code
* Example usage:
*
* <pre>{@code
* McpSyncClient mcpClient = // obtain MCP client
* Tool mcpTool = // obtain MCP tool definition
* ToolCallback callback = new McpToolCallback(mcpClient, mcpTool);
Expand Down Expand Up @@ -88,7 +89,7 @@ public SyncMcpToolCallback(McpSyncClient mcpClient, Tool tool) {
@Override
public ToolDefinition getToolDefinition() {
return ToolDefinition.builder()
.name(mcpClient.getClientInfo().name() + "-" + this.tool.name())
.name(McpToolUtils.prefixedToolName(this.mcpClient.getClientInfo().name(), this.tool.name()))
.description(this.tool.description())
.inputSchema(ModelOptionsUtils.toJsonString(this.tool.inputSchema()))
.build();
Expand All @@ -109,8 +110,12 @@ public ToolDefinition getToolDefinition() {
@Override
public String call(String functionInput) {
Map<String, Object> arguments = ModelOptionsUtils.jsonToMap(functionInput);
CallToolResult response = this.mcpClient
.callTool(new CallToolRequest(this.getToolDefinition().name(), arguments));
// Note that we use the original tool name here, not the adapted one from
// getToolDefinition
CallToolResult response = this.mcpClient.callTool(new CallToolRequest(this.tool.name(), arguments));
if (response.isError()) {
throw new IllegalStateException("Error calling tool: " + response.content());
}
return ModelOptionsUtils.toJsonString(response.content());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ void getToolDefinitionShouldReturnCorrectDefinition() {
@Test
void callShouldHandleJsonInputAndOutput() {

when(mcpClient.getClientInfo()).thenReturn(new Implementation("testClient", "1.0.0"));
// when(mcpClient.getClientInfo()).thenReturn(new Implementation("testClient",
// "1.0.0"));

when(tool.name()).thenReturn("testTool");
CallToolResult callResult = mock(CallToolResult.class);
Expand All @@ -79,7 +80,8 @@ void callShouldHandleJsonInputAndOutput() {

@Test
void callShoulIngroeToolContext() {
when(mcpClient.getClientInfo()).thenReturn(new Implementation("testClient", "1.0.0"));
// when(mcpClient.getClientInfo()).thenReturn(new Implementation("testClient",
// "1.0.0"));

when(tool.name()).thenReturn("testTool");
CallToolResult callResult = mock(CallToolResult.class);
Expand Down