AI is one of the hottest topics in the technology industry today. To explore its capabilities within the SAP ecosystem, I decided to build a simple MCP server with an AI API and deploy the solution on SAP BTP.
MCP acts as a lightweight bridge layer between the SAPUI5 frontend and the OpenAI API. Instead of calling OpenAI directly from the UI, all requests are routed through the MCP server. This approach improves security, keeps the API key hidden on the backend, and allows us to easily extend the logic later with SAP-specific processing, logging, or integration with SAP BTP services.
This example uses OpenAI for simplicity. In enterprise scenarios, the same architectural approach can be adapted to SAP AI services or other LLM providers.
The system architecture is decomposed as follows:
AI API (LLM)MCP serverSAP BTP applicationSAPUI5 frontend
1) AI API
For the AI API, I chose the OpenAI LLM API. To access the API, a subscription is required. However, the same approach can be applied to any other LLM provider by implementing a similar layer.
Open the API keys menu and create a new key. You should securely store the secret key for later use, as it will not be shown again.
2) SAP BTP Platform
The next step is setting up the platform for the project.
Create a new SAP BTP Trial instance.
Here are the guidelines for account creation:
https://developers.sap.com/tutorials/hcp-create-trial-account..html
At this point, we have all the necessary components for development.
We have SAP BTP instance, Cloud Foundry environment: API endpoint, Org name, Org ID and SAP Business application studio.
3) MCP server setup
The next step is the MCP server setup.
Make sure that the Cloud Foundry CLI is installed.
If not, you can refer to the following resources:
https://help.sap.com/docs/btp/sap-business-technology-platform/download-and-install-cloud-foundry-command-line-interface
https://github.com/cloudfoundry/cli#downloads
Visual Studio Code is used for development.
Create a folder called MCP-Server and add the following files:
MCP-SERVER
– manifest.yml
– mta.yml
– package.json
– server.js
– xs-security.json
The source code is as follows:
manifest.json
In the manifest file, the OpenAI API key can be specified, but it is recommended to use Cloud Foundry commands instead for better security:
Push the environment variable:
cf set-env mcp-server OPENAI_API_KEY “sk-proj-*****”
and restart the mcp server:
cf restage mcp-server
applications:
– name: mcp-server
memory: 256M
buildpacks:
– nodejs_buildpack
command: npm start
random-route: true
env:
OPENAI_API_KEY: “sk-proj-…”
mta.yml
_schema-version: “3.3.0”
ID: mcp-server
version: 1.0.0
modules:
– name: mcp-server
type: nodejs
path: .
parameters:
memory: 256M
requires:
– name: mcp-xsuaa
properties:
OPENAI_API_KEY: “YOUR_KEY_HERE”
resources:
– name: mcp-xsuaa
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
package.json
{
“name”: “mcp-server”,
“version”: “1.0.0”,
“scripts”: {
“start”: “node server.js”
},
“dependencies”: {
“express”: “^4.19.2”,
“openai”: “^4.0.0”,
“@sap/xsenv”: “^3.4.0”
}
}
Server.js
Here I define that the AI bot should act as an SAP ABAP RAP expert, so I set the appropriate instructions:
const express = require(“express”);
const xsenv = require(“@sap/xsenv”);
const OpenAI = require(“openai”);
const app = express();
app.use(express.json());
/**
* =========================
* SAFE XSUAA LOADING
* =========================
*/
let xsuaa;
try {
const services = xsenv.getServices({ xsuaa: { tag: “xsuaa” } });
xsuaa = services.xsuaa;
console.log(“XSUAA found”);
} catch (e) {
console.log(“XSUAA not bound – running without auth”);
}
/**
* =========================
* HEALTH CHECK (IMPORTANT)
* =========================
*/
app.get(“/health”, (req, res) => {
res.json({
status: “OK”,
xsuaa: !!xsuaa
});
});
/**
* =========================
* OPENAI CLIENT
* =========================
*/
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
/**
* =========================
* MCP ENDPOINT
* =========================
*/
app.post(“/mcp/chat”, async (req, res) => {
try {
const { prompt } = req.body;
if (!prompt) {
return res.status(400).json({ error: “prompt required” });
}
const result = await client.chat.completions.create({
model: “gpt-4.1-mini”,
messages: [
{
role: “system”,
content: “You are SAP ABAP RAP debugging assistant.”
},
{
role: “user”,
content: prompt
}
]
});
res.json({
answer: result.choices[0].message.content
});
} catch (err) {
console.error(err);
res.status(500).json({ error: err.message });
}
});
/**
* =========================
* START SERVER (CF SAFE)
* =========================
*/
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(“MCP server running on port”, port);
});
xs-security.json
{
“xsappname”: “mcp-server”,
“tenant-mode”: “dedicated”,
“scopes”: [
{
“name”: “$XSAPPNAME.invoke”,
“description”: “Invoke MCP API”
}
],
“role-templates”: [
{
“name”: “MCPInvoker”,
“scope-references”: [
“$XSAPPNAME.invoke”
]
}
]
}
Next steps are deployment.
Login to the Cloud foundry:
cf login
…and provide all necessary credentials.
Deployment MCP server:
cf push mcp-server — random route
XSUAA creation:
cf create-service xsuaa application mcp-xsuaa -c xs-security.json
Alternative solution is create XSUAA using SAP BTP and “Authorization and Trust Management Service”.
Binding the application with XSUAA:
cf bind-service mcp-server XSUAA
cf restage mcp-server
For demonstration purposes, the XSUAA configuration was intentionally simplified to make the setup easier to follow.
Checking the apps:
cf apps
Checking the services:
cf services
The server is ready and we can check how it works using PowerShell
Checking the MCP server responces.
The mcp-server URL you may find at SAP BTP at Application overview.
Invoke-RestMethod `
>> -Uri “https://mcp-server-{…}hana.ondemand.com/health”
Checking the LLM:
Invoke-RestMethod `
>> -Method Post `
>> -Uri “https://mcp-server-{…}.hana.ondemand.com/mcp/chat” `
>> -Body ‘{ “prompt”: “Explain RAP behavior definition” }’ `
>> -ContentType “application/json”
4) Frontend
With the MCP server in place, the next step is to build the frontend.
Open SAP Business Application Studio and create a development space.
https://developers.sap.com/tutorials/appstudio-devspace-create..html
Then, generate a basic SAP Fiori application named MCP_CLIENT, using MAIN as the main view.
For simplicity, I will skip the MVC explanation and focus on the implementation of the view and controller and simple one page application.
main.view.xml
<mvc:View
controllerName=”mcpclient.controller.Main”
xmlns:mvc=”sap.ui.core.mvc”
xmlns=”sap.m”>
<Page
id=”mainPage”
title=”MCP Chat”>
<content>
<List
id=”chatList”
items=”{chat>/messages}”
growing=”true”
growingScrollToLoad=”true”
class=”chatList”>
<CustomListItem id=”chatListItem”>
<HBox
id=”chatHBox”
width=”100%”>
<VBox
id=”chatVBox”
class=”{chat>bubbleClass}”
width=”70%”>
<Text
id=”roleText”
text=”{chat>role}”
class=”roleText” />
<Text
id=”messageText”
text=”{chat>text}”
wrapping=”true”
class=”messageText” />
</VBox>
</HBox>
</CustomListItem>
</List>
</content>
<footer>
<Toolbar id=”chatToolbar”>
<TextArea
id=”promptInput”
width=”100%”
rows=”2″
placeholder=”Ask MCP server…” />
<Button
id=”sendButton”
text=”Send”
type=”Emphasized”
press=”.onSend” />
</Toolbar>
</footer>
</Page>
</mvc:View>
main.controller.js
sap.ui.define([
“sap/ui/core/mvc/Controller”,
“sap/ui/model/json/JSONModel”,
“sap/m/MessageToast”
], function (Controller, JSONModel, MessageToast) {
“use strict”;
return Controller.extend(“mcpclient.controller.Main”, {
onInit: function () {
this.getView().setModel(new JSONModel({
messages: []
}), “chat”);
},
onSend: async function () {
const oView = this.getView();
const oModel = oView.getModel(“chat”);
const oInput = oView.byId(“promptInput”);
const sPrompt = (oInput.getValue() || “”).trim();
if (!sPrompt) {
MessageToast.show(“Enter prompt”);
return;
}
const aMessages = oModel.getProperty(“/messages”) || [];
// 👉 add user message
aMessages.push({
role: “USER”,
text: sPrompt
});
oModel.setProperty(“/messages”, aMessages);
oInput.setValue(“”);
this.getView().setBusy(true);
try {
// 🔥 SINGLE SOURCE OF TRUTH
const response = await fetch(“/mcp/chat”, {
method: “POST”,
headers: {
“Content-Type”: “application/json”
},
body: JSON.stringify({
prompt: sPrompt
})
});
const rawText = await response.text();
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${rawText}`);
}
let data;
try {
data = JSON.parse(rawText);
} catch (e) {
data = { answer: rawText };
}
const answer =
data.answer ||
data.response ||
data.text ||
rawText;
// 👉 add MCP message
aMessages.push({
role: “MCP”,
text: answer
});
oModel.setProperty(“/messages”, aMessages);
} catch (e) {
aMessages.push({
role: “MCP”,
text: “Error: ” + (e.message || e)
});
oModel.setProperty(“/messages”, aMessages);
MessageToast.show(“MCP call failed”);
}
this.getView().setBusy(false);
}
});
});
style.css
.chatList {
padding: 10px;
}
.userBubble {
background: #0070f2;
color: white;
padding: 10px;
border-radius: 12px;
margin: 5px;
}
.mcpBubble {
background: #eaecee;
color: #1d2d3e;
padding: 10px;
border-radius: 12px;
margin: 5px;
}
.roleText {
font-size: 11px;
opacity: 0.7;
margin-bottom: 4px;
}
.messageText {
white-space: pre-wrap;
word-break: break-word;
}
To avoid CORS issues, the ui5.yaml should look as follows:
specVersion: “4.0”
metadata:
name: mcpclient
type: application
server:
customMiddleware:
– name: fiori-tools-proxy
afterMiddleware: compression
configuration:
ignoreCertErrors: true
ui5:
path:
– /resources
– /test-resources
url: https://ui5.sap.com
backend:
– path: /mcp
url: https://mcp-server-{…….}.hana.ondemand.com
Let’s start the application.
Users can enter questions and receive responses from the LLM. As expected, the responses are mainly focused on ABAP RAP, as defined in the MCP server configuration.
5) Conclusion
This project demonstrates how modern AI capabilities can be integrated into the SAP ecosystem using SAP BTP and Cloud Foundry.
What we achieved:
Connected a SAPUI5 frontend with an AI-powered backendIntroduced an MCP server as a bridge between SAP and the OpenAI APIBuilt a foundation for an intelligent SAP assistant
This solution can be extended in multiple directions to evolve into an enterprise-ready AI assistant within the SAP landscape. For example, the MCP server can be enhanced into an SAP ABAP RAP assistant, capable of generating or explaining RAP business objects and service definitions. It can also be integrated with SAP backend systems, enabling interaction with real business data such as orders, materials, or finance objects.
From a broader perspective, this is not just a prototype, but a starting point for building a real SAP AI assistant that understands business context, ABAP RAP patterns, and system-specific knowledge.
AI is one of the hottest topics in the technology industry today. To explore its capabilities within the SAP ecosystem, I decided to build a simple MCP server with an AI API and deploy the solution on SAP BTP.MCP acts as a lightweight bridge layer between the SAPUI5 frontend and the OpenAI API. Instead of calling OpenAI directly from the UI, all requests are routed through the MCP server. This approach improves security, keeps the API key hidden on the backend, and allows us to easily extend the logic later with SAP-specific processing, logging, or integration with SAP BTP services.This example uses OpenAI for simplicity. In enterprise scenarios, the same architectural approach can be adapted to SAP AI services or other LLM providers.The system architecture is decomposed as follows:AI API (LLM)MCP serverSAP BTP applicationSAPUI5 frontend 1) AI APIFor the AI API, I chose the OpenAI LLM API. To access the API, a subscription is required. However, the same approach can be applied to any other LLM provider by implementing a similar layer.Open the API keys menu and create a new key. You should securely store the secret key for later use, as it will not be shown again. 2) SAP BTP PlatformThe next step is setting up the platform for the project.Create a new SAP BTP Trial instance.Here are the guidelines for account creation:https://developers.sap.com/tutorials/hcp-create-trial-account..htmlAt this point, we have all the necessary components for development.We have SAP BTP instance, Cloud Foundry environment: API endpoint, Org name, Org ID and SAP Business application studio. 3) MCP server setupThe next step is the MCP server setup.Make sure that the Cloud Foundry CLI is installed.If not, you can refer to the following resources:https://help.sap.com/docs/btp/sap-business-technology-platform/download-and-install-cloud-foundry-command-line-interfacehttps://github.com/cloudfoundry/cli#downloadsVisual Studio Code is used for development.Create a folder called MCP-Server and add the following files:MCP-SERVER- manifest.yml- mta.yml- package.json- server.js- xs-security.jsonThe source code is as follows:manifest.jsonIn the manifest file, the OpenAI API key can be specified, but it is recommended to use Cloud Foundry commands instead for better security:Push the environment variable:cf set-env mcp-server OPENAI_API_KEY “sk-proj-*****”and restart the mcp server:cf restage mcp-server applications:
– name: mcp-server
memory: 256M
buildpacks:
– nodejs_buildpack
command: npm start
random-route: true
env:
OPENAI_API_KEY: “sk-proj-…” mta.yml_schema-version: “3.3.0”
ID: mcp-server
version: 1.0.0
modules:
– name: mcp-server
type: nodejs
path: .
parameters:
memory: 256M
requires:
– name: mcp-xsuaa
properties:
OPENAI_API_KEY: “YOUR_KEY_HERE”
resources:
– name: mcp-xsuaa
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: applicationpackage.json{
“name”: “mcp-server”,
“version”: “1.0.0”,
“scripts”: {
“start”: “node server.js”
},
“dependencies”: {
“express”: “^4.19.2”,
“openai”: “^4.0.0”,
“@sap/xsenv”: “^3.4.0”
}
}Server.jsHere I define that the AI bot should act as an SAP ABAP RAP expert, so I set the appropriate instructions:const express = require(“express”);
const xsenv = require(“@sap/xsenv”);
const OpenAI = require(“openai”);
const app = express();
app.use(express.json());
/**
* =========================
* SAFE XSUAA LOADING
* =========================
*/
let xsuaa;
try {
const services = xsenv.getServices({ xsuaa: { tag: “xsuaa” } });
xsuaa = services.xsuaa;
console.log(“XSUAA found”);
} catch (e) {
console.log(“XSUAA not bound – running without auth”);
}
/**
* =========================
* HEALTH CHECK (IMPORTANT)
* =========================
*/
app.get(“/health”, (req, res) => {
res.json({
status: “OK”,
xsuaa: !!xsuaa
});
});
/**
* =========================
* OPENAI CLIENT
* =========================
*/
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
/**
* =========================
* MCP ENDPOINT
* =========================
*/
app.post(“/mcp/chat”, async (req, res) => {
try {
const { prompt } = req.body;
if (!prompt) {
return res.status(400).json({ error: “prompt required” });
}
const result = await client.chat.completions.create({
model: “gpt-4.1-mini”,
messages: [
{
role: “system”,
content: “You are SAP ABAP RAP debugging assistant.”
},
{
role: “user”,
content: prompt
}
]
});
res.json({
answer: result.choices[0].message.content
});
} catch (err) {
console.error(err);
res.status(500).json({ error: err.message });
}
});
/**
* =========================
* START SERVER (CF SAFE)
* =========================
*/
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(“MCP server running on port”, port);
});xs-security.json{
“xsappname”: “mcp-server”,
“tenant-mode”: “dedicated”,
“scopes”: [
{
“name”: “$XSAPPNAME.invoke”,
“description”: “Invoke MCP API”
}
],
“role-templates”: [
{
“name”: “MCPInvoker”,
“scope-references”: [
“$XSAPPNAME.invoke”
]
}
]
}Next steps are deployment.Login to the Cloud foundry:cf login…and provide all necessary credentials.Deployment MCP server:cf push mcp-server — random routeXSUAA creation:cf create-service xsuaa application mcp-xsuaa -c xs-security.jsonAlternative solution is create XSUAA using SAP BTP and “Authorization and Trust Management Service”.Binding the application with XSUAA:cf bind-service mcp-server XSUAA
cf restage mcp-serverFor demonstration purposes, the XSUAA configuration was intentionally simplified to make the setup easier to follow.Checking the apps:cf appsChecking the services:cf servicesThe server is ready and we can check how it works using PowerShellChecking the MCP server responces.The mcp-server URL you may find at SAP BTP at Application overview.Invoke-RestMethod `
>> -Uri “https://mcp-server-{…}hana.ondemand.com/health” Checking the LLM:Invoke-RestMethod `
>> -Method Post `
>> -Uri “https://mcp-server-{…}.hana.ondemand.com/mcp/chat” `
>> -Body ‘{ “prompt”: “Explain RAP behavior definition” }’ `
>> -ContentType “application/json” 4) FrontendWith the MCP server in place, the next step is to build the frontend.Open SAP Business Application Studio and create a development space.https://developers.sap.com/tutorials/appstudio-devspace-create..htmlThen, generate a basic SAP Fiori application named MCP_CLIENT, using MAIN as the main view.For simplicity, I will skip the MVC explanation and focus on the implementation of the view and controller and simple one page application.main.view.xml<mvc:View
controllerName=”mcpclient.controller.Main”
xmlns:mvc=”sap.ui.core.mvc”
xmlns=”sap.m”>
<Page
id=”mainPage”
title=”MCP Chat”>
<content>
<List
id=”chatList”
items=”{chat>/messages}”
growing=”true”
growingScrollToLoad=”true”
class=”chatList”>
<CustomListItem id=”chatListItem”>
<HBox
id=”chatHBox”
width=”100%”>
<VBox
id=”chatVBox”
class=”{chat>bubbleClass}”
width=”70%”>
<Text
id=”roleText”
text=”{chat>role}”
class=”roleText” />
<Text
id=”messageText”
text=”{chat>text}”
wrapping=”true”
class=”messageText” />
</VBox>
</HBox>
</CustomListItem>
</List>
</content>
<footer>
<Toolbar id=”chatToolbar”>
<TextArea
id=”promptInput”
width=”100%”
rows=”2″
placeholder=”Ask MCP server…” />
<Button
id=”sendButton”
text=”Send”
type=”Emphasized”
press=”.onSend” />
</Toolbar>
</footer>
</Page>
</mvc:View>main.controller.jssap.ui.define([
“sap/ui/core/mvc/Controller”,
“sap/ui/model/json/JSONModel”,
“sap/m/MessageToast”
], function (Controller, JSONModel, MessageToast) {
“use strict”;
return Controller.extend(“mcpclient.controller.Main”, {
onInit: function () {
this.getView().setModel(new JSONModel({
messages: []
}), “chat”);
},
onSend: async function () {
const oView = this.getView();
const oModel = oView.getModel(“chat”);
const oInput = oView.byId(“promptInput”);
const sPrompt = (oInput.getValue() || “”).trim();
if (!sPrompt) {
MessageToast.show(“Enter prompt”);
return;
}
const aMessages = oModel.getProperty(“/messages”) || [];
// 👉 add user message
aMessages.push({
role: “USER”,
text: sPrompt
});
oModel.setProperty(“/messages”, aMessages);
oInput.setValue(“”);
this.getView().setBusy(true);
try {
// 🔥 SINGLE SOURCE OF TRUTH
const response = await fetch(“/mcp/chat”, {
method: “POST”,
headers: {
“Content-Type”: “application/json”
},
body: JSON.stringify({
prompt: sPrompt
})
});
const rawText = await response.text();
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${rawText}`);
}
let data;
try {
data = JSON.parse(rawText);
} catch (e) {
data = { answer: rawText };
}
const answer =
data.answer ||
data.response ||
data.text ||
rawText;
// 👉 add MCP message
aMessages.push({
role: “MCP”,
text: answer
});
oModel.setProperty(“/messages”, aMessages);
} catch (e) {
aMessages.push({
role: “MCP”,
text: “Error: ” + (e.message || e)
});
oModel.setProperty(“/messages”, aMessages);
MessageToast.show(“MCP call failed”);
}
this.getView().setBusy(false);
}
});
});style.css.chatList {
padding: 10px;
}
.userBubble {
background: #0070f2;
color: white;
padding: 10px;
border-radius: 12px;
margin: 5px;
}
.mcpBubble {
background: #eaecee;
color: #1d2d3e;
padding: 10px;
border-radius: 12px;
margin: 5px;
}
.roleText {
font-size: 11px;
opacity: 0.7;
margin-bottom: 4px;
}
.messageText {
white-space: pre-wrap;
word-break: break-word;
}To avoid CORS issues, the ui5.yaml should look as follows:specVersion: “4.0”
metadata:
name: mcpclient
type: application
server:
customMiddleware:
– name: fiori-tools-proxy
afterMiddleware: compression
configuration:
ignoreCertErrors: true
ui5:
path:
– /resources
– /test-resources
url: https://ui5.sap.com
backend:
– path: /mcp
url: https://mcp-server-{…….}.hana.ondemand.comLet’s start the application.Users can enter questions and receive responses from the LLM. As expected, the responses are mainly focused on ABAP RAP, as defined in the MCP server configuration. 5) ConclusionThis project demonstrates how modern AI capabilities can be integrated into the SAP ecosystem using SAP BTP and Cloud Foundry.What we achieved:Connected a SAPUI5 frontend with an AI-powered backendIntroduced an MCP server as a bridge between SAP and the OpenAI APIBuilt a foundation for an intelligent SAP assistantThis solution can be extended in multiple directions to evolve into an enterprise-ready AI assistant within the SAP landscape. For example, the MCP server can be enhanced into an SAP ABAP RAP assistant, capable of generating or explaining RAP business objects and service definitions. It can also be integrated with SAP backend systems, enabling interaction with real business data such as orders, materials, or finance objects.From a broader perspective, this is not just a prototype, but a starting point for building a real SAP AI assistant that understands business context, ABAP RAP patterns, and system-specific knowledge. Read More Technology Blog Posts by Members articles
#SAP
#SAPTechnologyblog