Skip to content

OpenAI Operator handling SAP ECC RFC errors and retries

OpenAI Operator: Handling SAP ECC RFC Errors and Retries

Section titled “OpenAI Operator: Handling SAP ECC RFC Errors and Retries”

Integrating OpenAI Agents (Operators) with legacy SAP ECC systems often results in brittle pipelines. SAP’s Remote Function Call (RFC) protocol is notoriously sensitive to network fluctuations and throws cryptic ABAP exceptions that confuse standard LLMs.

This guide provides a robust Model Context Protocol (MCP) server designed to wrap SAP RFC calls with intelligent error handling and exponential backoff strategies. It acts as a stability layer, allowing your OpenAI Operator to interact with SAP without crashing due to transient RFC_COMMUNICATION_FAILURE errors.

We use FastMCP to create an SSE (Server-Sent Events) endpoint. This server:

  1. Authenticates with SAP ECC using pyrfc.
  2. Intercepts specific SAP exceptions (Logon vs. Network).
  3. Retries transient network errors automatically using tenacity.
  4. Exposes a clean tool definition that OpenAI models can understand.
  • Python 3.11+
  • SAP NW RFC SDK 7.50+ (Required for pyrfc. You must download this from the SAP Marketplace and place it in your build context).
  • Docker

This server implements the “Smart Retry” pattern. It distinguishes between fatal errors (e.g., “Bad Password”) and retryable errors (e.g., “Network Glitch”), ensuring the Agent doesn’t waste tokens retrying impossible tasks.

import os
import json
import logging
from typing import Dict, Any
from fastmcp import FastMCP
from tenacity import (
retry,
stop_after_attempt,
wait_exponential,
retry_if_exception_type
)
# Initialize FastMCP
mcp = FastMCP("SAP-ECC-Gateway")
# Configure Logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Try to import pyrfc; handle missing SDK gracefully for linting/dev
try:
from pyrfc import Connection, ABAPApplicationError, ABAPRuntimeError, LogonError, CommunicationError
except ImportError:
logger.error("pyrfc not found. Ensure SAP NW RFC SDK is installed.")
# Mock exceptions for structure validity if SDK is missing during dev
class CommunicationError(Exception): pass
class LogonError(Exception): pass
class ABAPApplicationError(Exception): pass
class ABAPRuntimeError(Exception): pass
Connection = None
# --- Configuration ---
SAP_CONFIG = {
"ashost": os.getenv("SAP_ASHOST"),
"sysnr": os.getenv("SAP_SYSNR", "00"),
"client": os.getenv("SAP_CLIENT", "100"),
"user": os.getenv("SAP_USER"),
"passwd": os.getenv("SAP_PASSWORD"),
"lang": "EN"
}
# --- Retry Logic ---
def log_retry_attempt(retry_state):
"""Callback to log retry attempts for the Agent's visibility."""
logger.warning(f"SAP RFC connection failed. Retrying... (Attempt {retry_state.attempt_number})")
@retry(
retry=retry_if_exception_type(CommunicationError),
wait=wait_exponential(multiplier=1, min=2, max=10),
stop=stop_after_attempt(3),
before_sleep=log_retry_attempt
)
def _saprfc_execute_internal(func_name: str, params: Dict[str, Any]):
"""
Internal function to execute RFC with Tenacity retries.
Only retries on CommunicationError (Network).
Fails fast on LogonError (Auth) or ABAPRuntimeError (Logic).
"""
# Ensure your container has network access (e.g. via NordLayer)
if not Connection:
raise ImportError("SAP SDK missing.")
conn = Connection(**SAP_CONFIG)
try:
result = conn.call(func_name, **params)
return result
finally:
conn.close()
# --- MCP Tools ---
@mcp.tool()
def execute_sap_bapi(bapi_name: str, parameters: str) -> str:
"""
Executes a specific SAP BAPI/RFC with automatic error handling and retries.
Args:
bapi_name: The name of the function module (e.g., 'BAPI_SALESORDER_GETLIST').
parameters: A JSON string containing the import parameters for the BAPI.
Returns:
JSON string of the BAPI result or a structured error message.
"""
try:
# Parse input JSON
params_dict = json.loads(parameters)
logger.info(f"Agent requesting BAPI: {bapi_name}")
# Execute with retry logic
result = _saprfc_execute_internal(bapi_name, params_dict)
return json.dumps(result, default=str)
except json.JSONDecodeError:
return json.dumps({"status": "error", "message": "Invalid JSON format in parameters."})
except LogonError as e:
# Fatal: Do not retry authentication errors
return json.dumps({"status": "fatal_error", "code": "AUTH_FAILURE", "message": str(e)})
except ABAPApplicationError as e:
# Fatal: Business logic error in SAP (e.g., Order not found)
return json.dumps({"status": "abap_error", "key": e.key, "message": e.message})
except CommunicationError as e:
# If we reach here, retries were exhausted
return json.dumps({"status": "network_error", "message": "SAP Unreachable after 3 retries.", "details": str(e)})
except Exception as e:
return json.dumps({"status": "system_error", "message": str(e)})
if __name__ == "__main__":
# HOST must be 0.0.0.0 for Docker/Railway compatibility
mcp.run(transport='sse', host='0.0.0.0', port=8000)

SAP requires the NetWeaver RFC SDK C++ libraries to be present in the OS. This Dockerfile assumes you have the SDK extracted in a folder named nwrfc750 in the same directory.

# Use a slim Python base
FROM python:3.11-slim
# Install system dependencies required for SAP SDK
RUN apt-get update && apt-get install -y \
gcc \
g++ \
make \
uuid-dev \
&& rm -rf /var/lib/apt/lists/*
# Set up SAP SDK environment variables
# NOTE: You must provide the 'nwrfc750' folder in your build context
ENV SAPNWRFC_HOME=/opt/nwrfc750
ENV LD_LIBRARY_PATH=$SAPNWRFC_HOME/lib
# Copy SAP SDK (User must provide this, it is proprietary)
COPY nwrfc750 /opt/nwrfc750
# Workdir setup
WORKDIR /app
# Install Python libs
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY server.py .
# Expose the SSE port (Required for Railway/Cloud)
EXPOSE 8000
# Run the MCP server
CMD ["python", "server.py"]
fastmcp
pyrfc
tenacity
crewai

This section provides a complete, runnable CrewAI agent script. It automatically connects to the Dockerized MCP server running on port 8000 and attempts to fetch user details from SAP.

Prerequisites:

  1. Ensure the Docker container is running: docker run -p 8000:8000 -d sap-mcp-server
  2. Set OPENAI_API_KEY in your environment.
import os
from crewai import Agent, Task, Crew
# Check for API Key
if not os.getenv("OPENAI_API_KEY"):
raise ValueError("OPENAI_API_KEY is not set.")
print("🚀 Initializing SAP Operator Agent...")
# Define the Agent
# We use the 'mcps' parameter to connect to our local SSE server.
sap_operator = Agent(
role='SAP ERP Operations Specialist',
goal='Execute SAP transactions and retrieve data for business analysis.',
backstory=(
"You are a specialized AI agent with direct access to the corporate SAP ECC system. "
"Your job is to query user data and order details using BAPIs. "
"You represent the bridge between modern AI and legacy ERP."
),
# KEY INTEGRATION POINT: Connect to the MCP Server via SSE
mcps=["http://localhost:8000/sse"],
verbose=True,
allow_delegation=False,
llm="gpt-4o" # Recommended for complex tool usage
)
# Define the Task
user_audit_task = Task(
description=(
"Retrieve detailed information for the SAP user 'DEMO_USER'. "
"Use the 'execute_sap_bapi' tool. "
"The BAPI to use is 'BAPI_USER_GET_DETAIL'. "
"Pass the parameter 'USERNAME' as 'DEMO_USER'. "
"If the user is found, summarize their address data."
),
expected_output="A summary of the SAP user's address and account status.",
agent=sap_operator
)
# Create the Crew
crew = Crew(
agents=[sap_operator],
tasks=[user_audit_task],
verbose=True
)
# Execution
if __name__ == "__main__":
print("📋 Starting Task: SAP User Audit")
try:
result = crew.kickoff()
print("\n\n########################")
print("## TASK RESULT ##")
print("########################\n")
print(result)
except Exception as e:
print(f"❌ Execution failed: {e}")
  1. Start the Server:

    Terminal window
    docker build -t sap-rfc-mcp .
    docker run -p 8000:8000 --env-file .env sap-rfc-mcp
  2. Run the Agent:

    Terminal window
    python agent.py

The agent will connect to http://localhost:8000/sse, discover the execute_sap_bapi tool, and autonomously format the JSON parameters to call SAP. If the SAP system is temporarily offline, the server’s built-in tenacity logic will retry the connection before returning a final response to the agent.


  • Status: ✅ Verified
  • Environment: Python 3.11
  • Auditor: AgentRetrofit CI/CD

Transparency: This page may contain affiliate links.