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
42 changes: 41 additions & 1 deletion spring-ai-modules/spring-ai-mcp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,21 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<java.version>21</java.version>
<spring-ai.version>1.1.2</spring-ai.version>
<spring-boot.version>4.0.0</spring-boot.version>
<logback.version>1.5.18</logback.version>
<junit-jupiter.version>6.0.1</junit-jupiter.version>
<spring-ai.version>2.0.0-M6</spring-ai.version>
<jackson-bom.version>3.1.2</jackson-bom.version>
<jackson-annotations.version>2.21</jackson-annotations.version>
</properties>

<profiles>
Expand All @@ -63,6 +73,12 @@
<spring.boot.mainclass>com.baeldung.springai.mcp.client.ClientApplication</spring.boot.mainclass>
</properties>
</profile>
<profile>
<id>mcp-ui</id>
<properties>
<spring.boot.mainclass>com.baeldung.springai.mcp.ui.McpUiApplication</spring.boot.mainclass>
</properties>
</profile>
</profiles>

<build>
Expand All @@ -74,11 +90,35 @@
<mainClass>${spring.boot.mainclass}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
</plugins>
</build>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-annotations.version}</version>
</dependency>
<dependency>
<groupId>tools.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>${jackson-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.baeldung.springai.mcp.client;

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.spec.McpSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
import org.springframework.ai.mcp.customizer.McpClientCustomizer;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

Expand All @@ -24,12 +26,10 @@ ChatClient chatClient(ChatModel chatModel, SyncMcpToolCallbackProvider toolCallb
}

@Bean
McpSyncClientCustomizer mcpSyncClientCustomizer() {
return (name, mcpClientSpec) -> {
mcpClientSpec.toolsChangeConsumer(tools -> {
logger.info("Detected tools changes.");
});
};
McpClientCustomizer<McpClient.SyncSpec> mcpSyncClientCustomizer() {
return (name, mcpClientSpec) ->
mcpClientSpec.toolsChangeConsumer(
tools -> logger.info("Detected tools changes."));
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.baeldung.springai.mcp.test;

import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpToolParam;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.ai.mcp.annotation.McpToolParam;
import org.springframework.stereotype.Component;

@Component
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.baeldung.springai.mcp.ui;

import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

@SpringBootApplication
class McpUiApplication {

public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(McpUiApplication.class)
.profiles("mcp-ui")
.run(args);
}

@McpTool(
title = "Say Hello",
name = "say-hello",
description = "A simple tool that returns a greeting message."
)
String sayHello() {
return "Hello from the MCP UI Application!";
}

/*
npx @modelcontextprotocol/inspector
claude mcp add --transport http sport-spinner http://localhost:3001/mcp
*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.baeldung.springai.mcp.ui;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

import org.springframework.ai.mcp.annotation.McpResource;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.ai.mcp.annotation.context.MetaProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

@Component
class SportSpinnerUI {

@Value("classpath:/static/sport-spinner.html")
private Resource sportSpinnerResource;

@McpTool(
title = "Spin Sport Wheel",
name = "spin-sport-wheel",
description = "Opens a fortune wheel that spins and randomly picks today's sport from 12 slices: swim, bike, run, bouldering, tennis, and a special triathlon (each appearing twice).",
metaProvider = SportSpinnerToolMetaProvider.class)
public String spinSportWheel() {
return "Opening the sport spinner wheel.";
}

@McpResource(
name = "Sport Spinner App Resource",
uri = "ui://sport/sport-spinner.html",
mimeType = "text/html;profile=mcp-app",
metaProvider = CspMetaProvider.class)
public String getSportSpinnerResource() throws IOException {
return sportSpinnerResource.getContentAsString(StandardCharsets.UTF_8);
}

public static final class SportSpinnerToolMetaProvider implements MetaProvider {
@Override
public Map<String, Object> getMeta() {
return Map.of("ui",
Map.of("resourceUri", "ui://sport/sport-spinner.html"));
}
}

public static final class CspMetaProvider implements MetaProvider {
@Override
public Map<String, Object> getMeta() {
return Map.of("ui",
Map.of("csp",
Map.of("resourceDomains",
List.of("https://unpkg.com"))));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

server.port=3001
spring.ai.mcp.server.protocol=STREAMABLE
Loading