Skip to content

Integrating CrewAI with SAP ECC using Node.js and `node-rfc`

Integrating CrewAI with SAP ECC using Node.js and node-rfc

Section titled “Integrating CrewAI with SAP ECC using Node.js and node-rfc”

While Python is the lingua franca of AI, the Node.js ecosystem often has superior drivers for certain legacy protocols. SAP is a prime example: the node-rfc library is widely considered more robust, easier to install, and better maintained than its Python counterparts for specific asynchronous workflows.

This guide implements a Polyglot Bridge. We run a Python FastMCP server that acts as the “Brain,” which delegates the actual “Muscle” work of talking to SAP to a lightweight Node.js worker process.

  1. CrewAI Agent: Connects directly to the MCP Server via SSE using the mcps parameter.
  2. Python Host (server.py): Accepts the request, validates it, and spawns a Node.js subprocess.
  3. Node.js Worker (sap_client.js): Reads JSON from stdin, executes the RFC call using node-rfc, and prints the result to stdout.

This script handles the raw RFC communication. It reads a JSON payload from standard input, connects to SAP, runs the BAPI/RFC, and pipes the result back.

sap_client.js
const noderfc = require("node-rfc");
const fs = require('fs');
// 1. Read the full input payload from STDIN
const inputData = fs.readFileSync(0, 'utf-8');
let request;
try {
request = JSON.parse(inputData);
} catch (e) {
console.error(JSON.stringify({ error: "Invalid JSON input" }));
process.exit(1);
}
// 2. Configure the SAP Client using Environment Variables
const client = new noderfc.Client({
ashost: process.env.SAP_HOST,
sysnr: process.env.SAP_SYSNR,
client: process.env.SAP_CLIENT,
user: process.env.SAP_USER,
passwd: process.env.SAP_PASSWORD,
lang: "EN"
});
(async () => {
try {
// 3. Connect to SAP
await client.open();
// 4. Invoke the Remote Function Call (RFC)
// usage: client.call("BAPI_NAME", { PARAM: "VALUE" })
const result = await client.call(request.rfcName, request.params || {});
// 5. Write success output to STDOUT
console.log(JSON.stringify({ status: "success", data: result }));
} catch (err) {
// 6. Handle SAP Errors
console.error(JSON.stringify({
status: "error",
message: err.message,
code: err.code
}));
} finally {
// Close connection if open
if (client.alive) {
await client.close();
}
}
})();

package.json:

{
"name": "sap-bridge",
"version": "1.0.0",
"dependencies": {
"node-rfc": "^3.0.0"
}
}

This FastMCP server exposes the tool to your Agent. It acts as the controller, ensuring the Node process is called correctly and securing the environment.

server.py
import subprocess
import json
import os
from fastmcp import FastMCP
# Initialize the MCP Server
mcp = FastMCP("SAP Polyglot Bridge")
@mcp.tool()
def call_sap_rfc(rfc_name: str, params: str = "{}") -> str:
"""
Executes an SAP RFC (Remote Function Call) via a Node.js bridge.
Args:
rfc_name: The name of the SAP BAPI/RFC (e.g., 'BAPI_USER_GET_DETAIL').
params: A JSON string containing the import parameters for the RFC.
"""
# payload to send to Node.js
try:
parsed_params = json.loads(params)
except json.JSONDecodeError:
return "Error: 'params' must be a valid JSON string."
payload = json.dumps({
"rfcName": rfc_name,
"params": parsed_params
})
# Ensure your container has network access (e.g. via NordLayer)
# This is critical for reaching on-prem SAP instances from the cloud.
try:
# Spawn the Node.js worker
process = subprocess.Popen(
["node", "sap_client.js"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
cwd=os.getcwd()
)
# Pass data via stdin and get result
stdout, stderr = process.communicate(input=payload)
if stderr and not stdout:
return f"Bridge Error: {stderr.strip()}"
# Parse the JSON response from Node.js
# We look for the last line in case of extraneous logs
lines = stdout.strip().split('\n')
last_line = lines[-1] if lines else "{}"
return last_line
except Exception as e:
return f"System Error: {str(e)}"
if __name__ == '__main__':
# Bind to 0.0.0.0 to support Docker networking
mcp.run(transport='sse', host='0.0.0.0', port=8000)

requirements.txt:

fastmcp==0.4.1
uvicorn==0.27.1

This is the most critical part. You cannot simply pip install SAP connectivity. You must inject the proprietary SAP NetWeaver RFC SDK into the container.

Prerequisite: Download SAP NW RFC SDK 7.50 (Linux x86_64) from the SAP Support Portal. Extract it so you have a folder named nwrfcsdk next to your Dockerfile.

# Use a slim Python base
FROM python:3.11-slim
# 1. Install System Dependencies & Node.js
# We use curl to fetch the specific Node version setup
RUN apt-get update && apt-get install -y \
curl \
build-essential \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*
# 2. Setup Application Directory
WORKDIR /app
# 3. Inject SAP Proprietary SDK
# You must provide the 'nwrfcsdk' folder in your build context!
COPY nwrfcsdk /usr/local/sap/nwrfcsdk
# 4. Configure Linker Paths for SAP
ENV SAPNWRFC_HOME=/usr/local/sap/nwrfcsdk
ENV LD_LIBRARY_PATH=$SAPNWRFC_HOME/lib
# 5. Install Python Dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 6. Install Node.js Dependencies
COPY package.json .
# npm install might require build-essential for binding compilation
RUN npm install
# 7. Copy Application Code
COPY server.py .
COPY sap_client.js .
# 8. Network Exposure
# Required for Railway/Docker connectivity
EXPOSE 8000
# 9. Launch the Server
CMD ["python", "server.py"]

Once your Docker container is running (e.g., on http://localhost:8000), connect your CrewAI agent using the mcps parameter. This allows the agent to discover and use the call_sap_rfc tool dynamically.

from crewai import Agent, Task, Crew
# 1. Define the Agent with direct MCP access
# If running in Docker locally, use localhost. If on Railway, use the public URL.
sap_expert = Agent(
role='SAP Integration Specialist',
goal='Retrieve user data from SAP ECC',
backstory='You are an expert in legacy ERP systems. You use the SAP Polyglot Bridge to fetch data.',
mcps=["http://localhost:8000/sse"], # Connects to the server.py inside Docker
verbose=True
)
# 2. Define the Task
# The agent will automatically find 'call_sap_rfc' from the MCP server
fetch_user_task = Task(
description="Fetch details for SAP user 'JDOE' using BAPI_USER_GET_DETAIL. Ensure params are a valid JSON string.",
agent=sap_expert,
expected_output="JSON details of the user retrieved from SAP."
)
# 3. Run the Crew
crew = Crew(agents=[sap_expert], tasks=[fetch_user_task])
result = crew.kickoff()
print(result)

By isolating the SAP node-rfc logic in a subprocess, you gain the stability of the Node.js SAP ecosystem while keeping your primary Agentic logic in Python. The Docker container encapsulates the complex library dependencies (LD_LIBRARY_PATH, SAP SDK), making deployment to platforms like Railway or AWS ECS seamless.


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

Transparency: This page may contain affiliate links.