Skip to main content

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.

info

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:

Setting up mcp.run

You'll need an mcp.run account and session ID. Here's how to get started:

  1. Run this command in your terminal:
    npx --yes -p @dylibso/mcpx@latest gen-session
  2. Your browser will open to complete authentication through GitHub
  3. After authenticating, return to your terminal and save the provided session key
Security Note

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:

  • fetch - For making HTTP requests
  • eval-js - For sandboxed JavaScript code evaluation

Install both servlets by:

  1. Visiting each URL
  2. Clicking the Install button
  3. 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

  1. 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"
  2. 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:

  1. Use fetch to download the C code
  2. Translate the C code to JavaScript
  3. Execute the translated code using eval-js
  4. 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.