+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/model-context-protocol/talent-arena/mcp-weather-server/pom.xml b/model-context-protocol/talent-arena/mcp-weather-server/pom.xml
new file mode 100644
index 0000000..0bb924a
--- /dev/null
+++ b/model-context-protocol/talent-arena/mcp-weather-server/pom.xml
@@ -0,0 +1,83 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.3.6
+
+
+
+ com.example
+
+ mcp-ta-weather-server
+ 0.0.1-SNAPSHOT
+
+ Spring AI MCP StdIO and WebFluxSSE Sample
+ Sample Spring Boot application demonstrating MCP server usage
+
+
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-bom
+ ${sprign-ai.version}
+ pom
+ import
+
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-server-webflux-spring-boot-starter
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
+ Central Portal Snapshots
+ central-portal-snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+
+ false
+
+
+ true
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/model-context-protocol/talent-arena/mcp-weather-server/src/main/java/org/springframework/ai/mcp/sample/server/McpServerApplication.java b/model-context-protocol/talent-arena/mcp-weather-server/src/main/java/org/springframework/ai/mcp/sample/server/McpServerApplication.java
new file mode 100644
index 0000000..17a2b13
--- /dev/null
+++ b/model-context-protocol/talent-arena/mcp-weather-server/src/main/java/org/springframework/ai/mcp/sample/server/McpServerApplication.java
@@ -0,0 +1,29 @@
+package org.springframework.ai.mcp.sample.server;
+
+import java.util.stream.Stream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.ai.tool.ToolCallbackProvider;
+import org.springframework.ai.tool.method.MethodToolCallbackProvider;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class McpServerApplication {
+
+ private static final Logger logger = LoggerFactory.getLogger(McpServerApplication.class);
+
+ public static void main(String[] args) {
+ SpringApplication.run(McpServerApplication.class, args);
+ }
+
+ @Bean
+ public ToolCallbackProvider openLibraryTools(WeatherTools weatherTools) {
+ var toolCallbackProvider = MethodToolCallbackProvider.builder().toolObjects(weatherTools).build();
+ logger.info("Tools: " + Stream.of(toolCallbackProvider.getToolCallbacks()).map(tc -> tc.getName()).toList());
+ return toolCallbackProvider;
+ }
+}
diff --git a/model-context-protocol/talent-arena/mcp-weather-server/src/main/java/org/springframework/ai/mcp/sample/server/WeatherTools.java b/model-context-protocol/talent-arena/mcp-weather-server/src/main/java/org/springframework/ai/mcp/sample/server/WeatherTools.java
new file mode 100644
index 0000000..5bddb8e
--- /dev/null
+++ b/model-context-protocol/talent-arena/mcp-weather-server/src/main/java/org/springframework/ai/mcp/sample/server/WeatherTools.java
@@ -0,0 +1,60 @@
+/*
+* Copyright 2025 - 2025 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.springframework.ai.mcp.sample.server;
+
+import java.time.LocalDateTime;
+
+import org.slf4j.Logger;
+
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.ai.tool.annotation.ToolParam;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestClient;
+
+@Service
+public class WeatherTools {
+
+ private static final Logger logger = org.slf4j.LoggerFactory.getLogger(WeatherTools.class);
+
+ private final RestClient restClient;
+
+ public WeatherTools() {
+ this.restClient = RestClient.create();
+ }
+
+public record WeatherResponse(Current current) {
+ public record Current(LocalDateTime time, int interval, double temperature_2m) {
+ }
+}
+
+@Tool(description = "Get the temperature (in celsius) for a specific location")
+public WeatherResponse getTemperature(@ToolParam(description = "The location latitude") double latitude,
+ @ToolParam(description = "The location longitude") double longitude,
+ @ToolParam(description = "The city name") String city) {
+
+ WeatherResponse response = restClient
+ .get()
+ .uri("https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}¤t=temperature_2m",
+ latitude, longitude)
+ .retrieve()
+ .body(WeatherResponse.class);
+
+ logger.info("Check temparature for {}. Lat: {}, Lon: {}. Temp: {}", city, latitude, longitude,
+ response.current);
+
+ return response;
+}
+}
diff --git a/model-context-protocol/talent-arena/mcp-weather-server/src/main/resources/application.properties b/model-context-protocol/talent-arena/mcp-weather-server/src/main/resources/application.properties
new file mode 100644
index 0000000..77b27f2
--- /dev/null
+++ b/model-context-protocol/talent-arena/mcp-weather-server/src/main/resources/application.properties
@@ -0,0 +1,10 @@
+# NOTE: You must disable the banner and the console logging
+# to allow the STDIO transport to work !!!
+spring.main.banner-mode=off
+logging.pattern.console=
+
+logging.file.name=./model-context-protocol/talent-arena/mcp-weather-server/target/mcp-weahter-server.log
+
+spring.ai.mcp.server.name=my-mcp-server
+spring.ai.mcp.server.version=0.0.1
+
diff --git a/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/ClientStdio.java b/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/ClientStdio.java
new file mode 100644
index 0000000..c1b3ade
--- /dev/null
+++ b/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/ClientStdio.java
@@ -0,0 +1,42 @@
+/*
+* Copyright 2024 - 2024 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.springframework.ai.mcp.sample.client;
+
+import io.modelcontextprotocol.client.transport.ServerParameters;
+import io.modelcontextprotocol.client.transport.StdioClientTransport;
+
+/**
+ * With stdio transport, the MCP server is automatically started by the client. But you
+ * have to build the server jar first:
+ *
+ *
+ * ./mvnw clean install -DskipTests
+ *
+ */
+public class ClientStdio {
+
+ public static void main(String[] args) {
+
+ var stdioParams = ServerParameters.builder("java")
+ .args("-Dspring.ai.mcp.server.stdio=true", "-Dspring.main.web-application-type=none",
+ "-Dlogging.pattern.console=", "-jar",
+ "model-context-protocol/talent-arena/mcp-weather-server/target/mcp-ta-weather-server-0.0.1-SNAPSHOT.jar")
+ .build();
+
+ new SampleClient(new StdioClientTransport(stdioParams)).run();
+ }
+
+}
diff --git a/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/ClientWebFluxSse.java b/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/ClientWebFluxSse.java
new file mode 100644
index 0000000..aad7265
--- /dev/null
+++ b/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/ClientWebFluxSse.java
@@ -0,0 +1,32 @@
+/*
+* Copyright 2024 - 2024 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.springframework.ai.mcp.sample.client;
+
+import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
+
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * @author Christian Tzolov
+ */
+public class ClientWebFluxSse {
+
+ public static void main(String[] args) {
+ var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
+ new SampleClient(transport).run();
+ }
+
+}
diff --git a/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/SampleClient.java b/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/SampleClient.java
new file mode 100644
index 0000000..91ea7ee
--- /dev/null
+++ b/model-context-protocol/talent-arena/mcp-weather-server/src/test/java/org/springframework/ai/mcp/sample/client/SampleClient.java
@@ -0,0 +1,60 @@
+/*
+* Copyright 2024 - 2024 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.springframework.ai.mcp.sample.client;
+
+import java.util.Map;
+
+import io.modelcontextprotocol.client.McpClient;
+import io.modelcontextprotocol.spec.ClientMcpTransport;
+import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
+import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
+import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
+import io.modelcontextprotocol.spec.McpSchema.ListPromptsResult;
+import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
+import io.modelcontextprotocol.spec.McpSchema.ReadResourceRequest;
+
+/**
+ * @author Christian Tzolov
+ */
+
+public class SampleClient {
+
+ private final ClientMcpTransport transport;
+
+ public SampleClient(ClientMcpTransport transport) {
+ this.transport = transport;
+ }
+
+ public void run() {
+
+ var client = McpClient.sync(this.transport).build();
+
+ client.initialize();
+
+ client.ping();
+
+ // List and demonstrate tools
+ ListToolsResult toolsList = client.listTools();
+ System.out.println("Available Tools = " + toolsList);
+
+ CallToolResult weather = client.callTool(new CallToolRequest("getTemperature", Map.of("latitude", "52.377956", "longitude", "4.897070", "city", "Amsterdam")));
+ System.out.println("Weather = " + weather);
+
+ client.closeGracefully();
+
+ }
+
+}
diff --git a/model-context-protocol/weather/manual-webflux-server/pom.xml b/model-context-protocol/weather/manual-webflux-server/pom.xml
index 8101dab..51c76c9 100644
--- a/model-context-protocol/weather/manual-webflux-server/pom.xml
+++ b/model-context-protocol/weather/manual-webflux-server/pom.xml
@@ -19,7 +19,7 @@
Sample Spring Boot application demonstrating MCP client and server usage
- 0.7.0-SNAPSHOT
+ 0.7.0
diff --git a/pom.xml b/pom.xml
index 99a57c4..19184a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,8 @@
model-context-protocol/web-search/brave-starter
model-context-protocol/web-search/brave-chatbot
+ model-context-protocol/talent-arena/brave-chatbot
+ model-context-protocol/talent-arena/mcp-weather-server
agentic-patterns