Integrating Spring AI with mcp.run
This tutorial guides you through connecting your Spring AI application with mcp.run's tool ecosystem. You'll learn how to create a chat interface that can interact with external tools and APIs through mcp.run.
Spring AI provides flexible AI model integration. While we will walk through using OpenAI in this guide, you can easily adapt these instructions for other models supported by Spring AI's chat model interface.
Find complete source code for both OpenAI and Ollama implementations in the mcpx4j repository examples directory. The Java code remains identical between versions - you'll only need to adjust configuration parameters and dependencies to switch between models.
For a video walkthrough:
Prerequisites
Before starting, ensure you have the following:
- A JDK installed on your system. We recommend using SDKMAN! for the installation
- A GitHub Account for mcp.run authentication
Additionally, if you want to use OpenAI as the LLM:
- An OpenAI Account with API access
- An OpenAI API Key
Setting up mcp.run
You'll need an mcp.run account and session ID. Here's how to get started:
- Run this command in your terminal:
npx --yes -p @dylibso/mcpx@latest gen-session
- Your browser will open to complete authentication through GitHub
- After authenticating, return to your terminal and save the provided session key
Keep your mcp.run session ID and OpenAI API key secure. Never commit these credentials to code repositories or expose them publicly.
Required Tools
This tutorial requires two mcp.run servlets:
Install both servlets by:
- Visiting each URL
- Clicking the
Install
button - Verifying they appear in your install profile
Project Setup
Create a new Spring project:
-
Navigate to start.spring.io
-
Choose a language and a build tool. For this example, we will use Java and Maven.
-
Pick a Group, Artifact, Name and a root package name. In this example we will use:
<groupId>com.dylibso.mcpx4j.examples</groupId>
<artifactId>spring-ai-mcpx4j-openai</artifactId>
<version>999-SNAPSHOT</version>
<name>spring-ai-mcpx4j-openai</name>The package name will be
com.dylibso.mcpx4j.examples
. -
Then add OpenAI to the dependencies, clicking the button at the top right corner.
-
Finally, click the Generate button at the bottom.
-
Download the result and unpack at a place of your choice.
-
Then add the dependency to MCPX4J.
MCPX4J is currently available on JitPack. Configure the JitPack repo and the dependency as follows:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
...
</repositories>
<dependencies>
<dependency>
<groupId>com.github.dylibso.mcpx4j</groupId>
<artifactId>core</artifactId>
<version>${mcpx4j.version}</version>
</dependency>
...
</dependencies>
Define the Configuration Parameters
Make sure the src/main/resources/application.properties
looks as follows:
spring.application.name=openai
spring.main.web-application-type=none
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.options.model=gpt-4o-mini
mcpx.api-key=${MCP_RUN_SESSION_ID}
mcpx.base-url=https://www.mcp.run
mcpx.profile-id=default
The system will look for OPENAI_API_KEY
and MCP_RUN_SESSION_ID
in your environment variables. You can either let it use these environment
variables or directly replace the placeholders
${OPENAI_API_KEY}
and ${MCP_RUN_SESSION_ID}
with your actual values.
Creating the mcp.run Function adapter
To expose tools in Spring AI, you'll use the FunctionCallback
interface.
Here's how to implement it using mcpx
tools:
package com.dylibso.mcpx4j.examples;
import com.dylibso.mcpx4j.examples.core.McpxTool;
import org.springframework.ai.model.function.FunctionCallback;
import java.util.logging.Logger;
public class McpxToolFunctionCallback implements FunctionCallback {
private final McpxTool tool;
McpxToolFunctionCallback(McpxTool tool) { this.tool = tool; }
@Override
public String getName() { return tool.name(); }
@Override
public String getDescription() { return tool.description(); }
@Override
public String getInputTypeSchema() { return tool.inputSchema(); }
@Override
public String call(String functionInput) {
Logger.getLogger(tool.name())
.info("invoking Wasm MCP.RUN function, "+functionInput);
// Splice the function invocation into the JSON representation
// the MCPX tool expects.
String adapted = """
{
"method":"tools/call",
"params": {
"name": "%s",
"arguments" : %s
}
}""".formatted(tool.name(), functionInput);
return tool.call(adapted);
}
}
Create a Spring Boot Application
Finally, create an Application class to wire everything together:
package com.dylibso.mcpx4j.examples;
import com.dylibso.mcpx4j.core.Mcpx;
import com.dylibso.mcpx4j.core.McpxServlet;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Collection;
@SpringBootApplication
public class OpenAiMcpx4jApplication {
public static void main(String[] args) {
SpringApplication.run(OpenAiMcpx4jApplication.class, args);
}
// Load the configuration values from application.properties
@Value("${mcpx.api-key}")
private String apiKey;
@Value("${mcpx.base-url}")
private String baseUrl;
@Value("${mcpx.profile-id}")
private String profileId;
@Bean
public CommandLineRunner startup(
ChatClient.Builder chatClientBuilder,
ConfigurableApplicationContext context) {
// Instantiate a new Mcpx client with the configuration values.
var mcpx = Mcpx.forApiKey(apiKey).withBaseUrl(baseUrl).build();
// Refresh the installed servlets definitions from mcp.run.
// This will load the configuration once.
// You can schedule this invocation periodically to refresh
// such configuration.
mcpx.refreshInstallations(profileId);
// Instantiate each servlet and expose it as a `FunctionCallback`
var functions = functionsFromMcpxServlets(mcpx.servlets());
return args -> {
var chatClient = chatClientBuilder
// Load the `FunctionCallback`s into the chat interface
.defaultFunctions(functions)
// Define system prompt for AI behavior
// Note: this can be whatever you want,
// but it's recommended to give the LLM
// as much context as you can here while
// remaining generic for your use case.
.defaultSystem("""
You are a helpful AI assistant with access to various external tools and APIs. Your goal is to complete tasks thoroughly and autonomously by making full use of these tools. Here are your core operating principles:
1. Take initiative - Don't wait for user permission to use tools. If a tool would help complete the task, use it immediately.
2. Chain multiple tools together - Many tasks require multiple tool calls in sequence. Plan out and execute the full chain of calls needed to achieve the goal.
3. Handle errors gracefully - If a tool call fails, try alternative approaches or tools rather than asking the user what to do.
4. Make reasonable assumptions - When tool calls require parameters, use your best judgment to provide appropriate values rather than asking the user.
5. Show your work - After completing tool calls, explain what you did and show relevant results, but focus on the final outcome the user wanted.
6. Be thorough - Use tools repeatedly as needed until you're confident you've fully completed the task. Don't stop at partial solutions.
Your responses should focus on results rather than asking questions. Only ask the user for clarification if the task itself is unclear or impossible with the tools available.
""")
.build();
System.out.println("Chat started. Type 'exit' to quit.");
while (true) {
String input = System.console().readLine("YOU: ");
if (input.equals("exit")) {
System.out.println("Goodbye!");
break;
}
if (input.isBlank()) { continue; }
System.out.println(
"ASSISTANT: " + chatClient.prompt(input).call().content());
}
context.close();
};
}
// Converts the servlets into FunctionCallbacks.
McpxToolFunctionCallback[] functionsFromMcpxServlets(
Collection<McpxServlet> servlets) {
return servlets.stream()
.flatMap(servlet -> servlet.tools().values().stream())
.map(McpxToolFunctionCallback::new)
.toArray(McpxToolFunctionCallback[]::new);
}
}
Running the Application
-
If in the previous step you have decided to use environment variables, set your environment variables:
export OPENAI_API_KEY="your-openai-key-here"
export MCP_RUN_SESSION_ID="your-mcp-session-here" -
Start the application:
mvn spring-boot:run
Testing the Integration
Try this example prompt to test the tool chaining capability:
I want to know what would happen if i put the string "Hello, mcp.run!" into this hash function https://gist.githubusercontent.com/MohamedTaha98/ccdf734f13299efb73ff0b12f7ce429f/raw/ab9593d5195a1643388cfc99d03a4fd96a094a5c/djb2%2520hash%2520function.c
The assistant will automatically:
- Use
fetch
to download the C code - Translate the C code to JavaScript
- Execute the translated code using
eval-js
- Return the hash value:
-2106085175
You should see output similar to this:
Assistant: The result of hashing the string "Hello, mcp.run!" using the provided djb2 hash function is `-2106085175`.
This demonstrates how the assistant can chain multiple tools together without explicit instructions, showcasing the power of the integration.
Support
If you get stuck and need some help, please reach out! Visit our support page to learn how best to get in touch.