Microsoft Semantic Kernel with SAP ECC BAPI calls (Python)
Connecting Microsoft Semantic Kernel to SAP ECC via BAPI (Python)
Section titled “Connecting Microsoft Semantic Kernel to SAP ECC via BAPI (Python)”As enterprises adopt AI orchestration layers like Microsoft Semantic Kernel, the challenge shifts from “how do we prompt the AI?” to “how does the AI execute actions on our 20-year-old ERP?”
Semantic Kernel excels at planning and chaining “Skills” (or Plugins). However, it has no native understanding of the proprietary SAP RFC (Remote Function Call) protocol used by SAP ECC (Enterprise Central Component) and S/4HANA systems.
This guide provides a “Retrofit” solution: wrapping SAP BAPI calls in a Model Context Protocol (MCP) server. This allows Semantic Kernel (and other agent frameworks) to interact with SAP as if it were a modern REST API, without rewriting legacy ABAP code.
🏗️ The Architecture
Section titled “🏗️ The Architecture”We will build a dedicated “SAP Bridge” container.
- The Agent: Microsoft Semantic Kernel (Python/C#) running in your application.
- The Protocol: MCP (Model Context Protocol). The Agent connects to our bridge via SSE (Server-Sent Events).
- The Bridge: A
FastMCPserver running in Docker. - The Driver:
pyrfc(SAP NetWeaver SDK wrapper) handling the low-level binary communication with SAP.
🛠️ Prerequisites
Section titled “🛠️ Prerequisites”Before deploying the container, you must obtain the proprietary SAP libraries. You cannot pip install these from public repositories due to licensing.
- SAP NetWeaver RFC SDK: Download
nwrfc750P_*-*.zipfrom the SAP ONE Support Launchpad. - Files Needed: Extract the SDK. You specifically need the
lib(Linux shared libraries) andincludefolders.
💻 The Bridge Code (server.py)
Section titled “💻 The Bridge Code (server.py)”This server exposes a tool to fetch Sales Orders. It manages the connection pool to SAP, handles the RFC binary handshake, and returns clean JSON to the agent.
from fastmcp import FastMCPfrom pyrfc import Connection, ABAPApplicationError, ABAPRuntimeError
# Initialize the MCP Servermcp = FastMCP("SAP ECC BAPI Service")
def get_sap_connection(): """ Establishes a connection to the SAP ECC instance using environment variables.
# Ensure your container has network access (e.g. via NordLayer) # The SAP server is often behind a corporate firewall. """ import os
# These would typically come from environment variables in a production setup params = { 'ashost': os.getenv('SAP_HOST', 'sap-ecc-prod.example.com'), 'sysnr': os.getenv('SAP_SYSNR', '00'), 'client': os.getenv('SAP_CLIENT', '100'), 'user': os.getenv('SAP_USER'), 'passwd': os.getenv('SAP_PASSWORD'), 'lang': 'EN' }
try: conn = Connection(**params) return conn except Exception as e: raise RuntimeError(f"Failed to connect to SAP: {str(e)}")
@mcp.tool()def get_sales_orders(customer_id: str, max_rows: int = 10) -> str: """ Fetches a list of sales orders for a specific customer from SAP ECC.
Args: customer_id: The 10-digit SAP Customer Number (e.g., '0000012345'). max_rows: Maximum number of orders to return. """ try: conn = get_sap_connection()
# Zero-pad customer ID if necessary (SAP stores them as 10 chars) kunnr_padded = customer_id.zfill(10)
# Call the standard BAPI: BAPI_SALESORDER_GETLIST # This is a standard RFC module available in almost all ECC systems. result = conn.call( "BAPI_SALESORDER_GETLIST", CUSTOMER_NUMBER=kunnr_padded, SALES_ORGANIZATION="1000", # Example Sales Org MATERIAL="", # Optional filter DOCUMENT_DATE={ "SIGN": "I", "OPTION": "BT", "LOW": "20230101", "HIGH": "20251231" } )
# BAPI_SALESORDER_GETLIST returns 'SALES_ORDERS' table orders = result.get('SALES_ORDERS', [])
# Limit results and format relevant fields output = [] for order in orders[:max_rows]: output.append({ "order_id": order.get('SD_DOC'), "date": order.get('DOC_DATE'), "value": order.get('NET_VAL'), "currency": order.get('CURRENCY'), "status": order.get('DOC_STATUS') })
conn.close() return str(output)
except ABAPApplicationError as e: return f"SAP Application Error: {e.key} - {e.message}" except ABAPRuntimeError as e: return f"SAP Runtime Error: {e}" except Exception as e: return f"Unexpected Error: {str(e)}"
if __name__ == "__main__": mcp.run()🐳 Dockerfile
Section titled “🐳 Dockerfile”This Dockerfile is critical. It installs the SAP SDK headers and libraries into the Linux system paths so pyrfc can compile and link against them during installation.
Directory Structure:
/my-project ├── server.py ├── Dockerfile ├── requirements.txt └── nwrfcsdk/ <-- You must unzip SAP SDK here ├── lib/ └── include/Dockerfile:
# Use official Python runtime as a parent imageFROM python:3.11-slim
# Set environment variables for SAP SDKENV SAPNWRFC_HOME=/usr/local/sap/nwrfcsdkENV LD_library_path=$SAPNWRFC_HOME/lib
# Create directory structure for SAP SDKWORKDIR /usr/local/sap
# Copy the SAP NetWeaver SDK from your local machine to the container# NOTE: You must have the 'nwrfcsdk' folder in the same directory as this DockerfileCOPY nwrfcsdk ./nwrfcsdk
# Register the library path so Linux can find the SAP .so filesRUN echo "$SAPNWRFC_HOME/lib" > /etc/ld.so.conf.d/nwrfcsdk.conf \ && ldconfig
# Set working directory for the appWORKDIR /app
# Install system dependencies required for building the Python wheelRUN apt-get update && apt-get install -y \ gcc \ g++ \ make \ && rm -rf /var/lib/apt/lists/*
# Install Python dependencies# 'pyrfc' requires the SDK files we just copied to build successfullyRUN pip install --no-cache-dir pyrfc fastmcp uvicorn
# Copy the server codeCOPY server.py .
# Expose port 8000 for Railway/Cloud compatibilityEXPOSE 8000
# Run the MCP serverCMD ["python", "server.py"]🔌 Connecting to Semantic Kernel
Section titled “🔌 Connecting to Semantic Kernel”Once your Docker container is running (e.g., on http://localhost:8000), you can connect it to your Semantic Kernel agent using an MCP Client implementation.
While Semantic Kernel handles the planning, it needs an “MCP Connector” to talk to the SSE stream exposed by FastMCP.
- Start the Docker Container:
Terminal window docker run -p 8000:8000 --env-file .env my-sap-bridge - Agent Logic: The agent will see the
get_sales_orderstool. When a user asks “Check the latest orders for customer 100”, the Semantic Kernel planner will:- Recognize the intent matches
get_sales_orders. - Extract “100” as the
customer_id. - Send the execution request to port 8000.
- Receive the JSON list of orders from SAP.
- Recognize the intent matches
This architecture keeps your SAP credentials safely inside the Docker container, exposing only a high-level “Skill” to the AI agent.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.