Skip to content

Commit e807fc3

Browse files
committed
Merge branch 'main' into spec-update/grounding/support-s3-sftp
2 parents 5a9751e + 5a0fafe commit e807fc3

File tree

12 files changed

+124
-6
lines changed

12 files changed

+124
-6
lines changed

.github/workflows/continuous-integration.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
7373
- name: "Slack Notification"
7474
if: ${{ github.ref == 'refs/heads/main' && failure() }}
75-
uses: slackapi/[email protected].0
75+
uses: slackapi/[email protected].1
7676
with:
7777
webhook: ${{ secrets.SLACK_WEBHOOK }}
7878
webhook-type: incoming-webhook

.github/workflows/deploy-snapshot.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040

4141
- name: "Slack Notification"
4242
if: failure()
43-
uses: slackapi/[email protected].0
43+
uses: slackapi/[email protected].1
4444
with:
4545
webhook: ${{ secrets.SLACK_WEBHOOK }}
4646
webhook-type: incoming-webhook

.github/workflows/e2e-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ jobs:
8585

8686
- name: "Slack Notification"
8787
if: failure()
88-
uses: slackapi/[email protected].0
88+
uses: slackapi/[email protected].1
8989
with:
9090
webhook: ${{ secrets.SLACK_WEBHOOK }}
9191
webhook-type: incoming-webhook

.github/workflows/fosstars-report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676

7777
- name: "Slack Notification"
7878
if: failure()
79-
uses: slackapi/[email protected].0
79+
uses: slackapi/[email protected].1
8080
with:
8181
webhook: ${{ secrets.SLACK_WEBHOOK }}
8282
webhook-type: incoming-webhook

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
<maven.compiler.proc>full</maven.compiler.proc>
6060
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
6161
<project.build.outputTimestamp>2025-04-03T13:23:00Z</project.build.outputTimestamp>
62-
<cloud-sdk.version>5.20.0</cloud-sdk.version>
62+
<cloud-sdk.version>5.21.0</cloud-sdk.version>
6363
<junit-jupiter.version>5.13.4</junit-jupiter.version>
6464
<wiremock.version>3.13.1</wiremock.version>
6565
<assertj-core.version>3.27.3</assertj-core.version>
@@ -68,7 +68,7 @@
6868
<system-stubs.version>2.1.3</system-stubs.version>
6969
<surefire.version>3.5.3</surefire.version>
7070
<springframework.version>6.2.9</springframework.version>
71-
<spring-ai.version>1.0.0</spring-ai.version>
71+
<spring-ai.version>1.0.1</spring-ai.version>
7272
<reactor-core.version>3.7.8</reactor-core.version>
7373
<dotenv-java.version>3.2.0</dotenv-java.version>
7474
<mockito.version>5.18.0</mockito.version>

sample-code/spring-app/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Finally, you can start the sample app:
2727

2828
Head to http://localhost:8080 in your browser to see all available endpoints.
2929

30+
To test the MCP integration, run with `mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=mcp"`:
31+
3032
## Run the E2E Test
3133

3234
Trigger the [GitHub Action](https://github.com/SAP/ai-sdk-java/actions/workflows/e2e-test.yaml).

sample-code/spring-app/pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,24 @@
107107
<groupId>org.springframework.ai</groupId>
108108
<artifactId>spring-ai-client-chat</artifactId>
109109
</dependency>
110+
<dependency>
111+
<groupId>org.springframework.ai</groupId>
112+
<artifactId>spring-ai-mcp</artifactId>
113+
<scope>runtime</scope>
114+
</dependency>
115+
<dependency>
116+
<groupId>org.springframework.ai</groupId>
117+
<artifactId>spring-ai-autoconfigure-mcp-client</artifactId>
118+
<scope>runtime</scope>
119+
<exclusions>
120+
<exclusion>
121+
<!-- MCP brings 3.4.5 which conflicts with our version 3.5.0.
122+
Since we bring the necessary Spring dependencies, we can just exclude the transitive dependency here -->
123+
<groupId>org.springframework.boot</groupId>
124+
<artifactId>spring-boot-starter</artifactId>
125+
</exclusion>
126+
</exclusions>
127+
</dependency>
110128
<dependency>
111129
<groupId>org.springframework.boot</groupId>
112130
<artifactId>spring-boot-autoconfigure</artifactId>

sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,20 @@ Object toolCalling(
131131
return text.isEmpty() ? message.getToolCalls().toString() : text;
132132
}
133133

134+
@GetMapping("/mcp")
135+
Object mcp(@Nullable @RequestParam(value = "format", required = false) final String format) {
136+
val response = service.toolCallingMcp();
137+
138+
if ("json".equals(format)) {
139+
return ((OrchestrationSpringChatResponse) response)
140+
.getOrchestrationResponse()
141+
.getOriginalResponse();
142+
}
143+
final AssistantMessage message = response.getResult().getOutput();
144+
final String text = message.getText();
145+
return text.isEmpty() ? message.getToolCalls().toString() : text;
146+
}
147+
134148
@GetMapping("/chatMemory")
135149
Object chatMemory(
136150
@Nullable @RequestParam(value = "format", required = false) final String format) {

sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.sap.ai.sdk.app.services;
22

33
import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GEMINI_1_5_FLASH;
4+
import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O;
45
import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O_MINI;
56

67
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -24,11 +25,16 @@
2425
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
2526
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
2627
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
28+
import org.springframework.ai.chat.messages.SystemMessage;
29+
import org.springframework.ai.chat.messages.UserMessage;
2730
import org.springframework.ai.chat.model.ChatModel;
2831
import org.springframework.ai.chat.model.ChatResponse;
2932
import org.springframework.ai.chat.prompt.Prompt;
3033
import org.springframework.ai.chat.prompt.PromptTemplate;
3134
import org.springframework.ai.support.ToolCallbacks;
35+
import org.springframework.ai.tool.ToolCallbackProvider;
36+
import org.springframework.beans.factory.annotation.Autowired;
37+
import org.springframework.beans.factory.annotation.Value;
3238
import org.springframework.stereotype.Service;
3339
import reactor.core.publisher.Flux;
3440

@@ -40,6 +46,13 @@ public class SpringAiOrchestrationService {
4046
new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI);
4147
private final OrchestrationChatOptions defaultOptions = new OrchestrationChatOptions(config);
4248

49+
@Nullable
50+
@Autowired(required = false)
51+
ToolCallbackProvider toolCallbackProvider;
52+
53+
@Value("${spring.profiles.active:}")
54+
private String activeProfile;
55+
4356
/**
4457
* Chat request to OpenAI through the Orchestration service with a simple prompt.
4558
*
@@ -172,6 +185,41 @@ public ChatResponse toolCalling(final boolean internalToolExecutionEnabled) {
172185
return client.call(prompt);
173186
}
174187

188+
/**
189+
* Example using an MCP client to use a file system tool. Enabled via dedicated Spring profile,
190+
* since it requires an actual MCP server to run.
191+
*
192+
* @return the assistant response object
193+
*/
194+
@Nonnull
195+
public ChatResponse toolCallingMcp() {
196+
// check if spring profile is set to 'mcp'
197+
if (!activeProfile.equals("mcp")) {
198+
throw new IllegalStateException(
199+
"The 'mcp' Spring profile is not active. Set it, e.g. by passing a JVM parameter '-Dspring.profiles.active=mcp'.");
200+
}
201+
if (toolCallbackProvider == null) {
202+
throw new IllegalStateException(
203+
"No MCP clients were found. Ensure that you configured the clients correctly in the application.yaml file.");
204+
}
205+
// GPT-4o-mini doesn't work too well with the file system tool, so we use 4o here
206+
val options = new OrchestrationChatOptions(config.withLlmConfig(GPT_4O));
207+
options.setToolCallbacks(List.of(toolCallbackProvider.getToolCallbacks()));
208+
options.setInternalToolExecutionEnabled(true);
209+
210+
val sys =
211+
new SystemMessage(
212+
"""
213+
Please read through the markdown files in my file system.
214+
Ensure to first query the allowed directories.
215+
Then use any `.md` files you find to answer the user's question.
216+
Do **NOT** query for `*.md` since that doesn't work, ensure to query for `.md` instead.""");
217+
val usr = new UserMessage("How can I use Spring AI with the SAP AI SDK?");
218+
219+
val prompt = new Prompt(List.of(sys, usr), options);
220+
return client.call(prompt);
221+
}
222+
175223
/**
176224
* Chat request to OpenAI through the Orchestration service using chat memory.
177225
*
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#---
2+
spring:
3+
config.activate.on-profile:
4+
mcp
5+
ai:
6+
mcp:
7+
client:
8+
# refer to https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html#_starters
9+
type: SYNC
10+
stdio:
11+
connections:
12+
# allows file system access to the current directory
13+
# requires npx to run
14+
# refer to https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem
15+
localFS:
16+
command: npx
17+
args:
18+
- "-y"
19+
- "@modelcontextprotocol/server-filesystem"
20+
- "."
21+
env:
22+
DEBUG: "true"

0 commit comments

Comments
 (0)