Connect CrewAI to Oracle EBS via Node.js `oracledb`
Connect CrewAI to Oracle EBS via Node.js oracledb
Section titled “Connect CrewAI to Oracle EBS via Node.js oracledb”Slug: connect-crewai-oracle-ebs-node-oracledb
The Knowledge Gap
Section titled “The Knowledge Gap”A common architectural challenge when modernizing “Big Iron” is the language mismatch. You might have a robust Node.js infrastructure for your web layers, or you might be searching for “Node.js Oracle EBS integration” because that is your team’s strength. However, CrewAI is a Python-native framework.
While you can write MCP servers in Node.js (using the node-oracledb driver), this adds unnecessary serialization overhead when your agent (CrewAI) is already running in Python.
For the AgentRetrofit standard, we bridge this gap by using Python’s oracledb (the direct equivalent of node-oracledb, maintained by the same Oracle team) wrapped in a FastMCP server. This gives you the same high-performance “Thin Mode” connectivity you expect from Node, but in a native format that CrewAI can consume instantly without context switching.
This guide provides the production-ready “Glue Code” to connect CrewAI to Oracle E-Business Suite (EBS) using the modern Thin driver.
🛠️ Integration Blueprint
Section titled “🛠️ Integration Blueprint”We will build a Dockerized MCP (Model Context Protocol) Server that exposes Oracle EBS data as tool-use functions for your agents.
The Stack
Section titled “The Stack”- Agent Framework: CrewAI (Python)
- Protocol: Model Context Protocol (MCP) via
fastmcp - Driver:
python-oracledb(Thin mode - no heavy Instant Client required) - Legacy System: Oracle EBS (12.x or Fusion)
Prerequisites
Section titled “Prerequisites”- Network Access: Your container must be able to reach the Oracle Host/Port (typically 1521).
- Service Name: The unique Service ID (SID) or Service Name of your EBS database.
- Credentials: A read-only service account (e.g.,
APPS_READ).
👨💻 The “Glue Code”
Section titled “👨💻 The “Glue Code””1. The MCP Server (server.py)
Section titled “1. The MCP Server (server.py)”This server uses the fastmcp library to declare tools that CrewAI can automatically discover and use. We use the “Thin” mode of oracledb for maximum container compatibility.
import osimport oracledbfrom fastmcp import FastMCP
# Initialize the MCP Servermcp = FastMCP("oracle-ebs")
# Database Configuration# In production, load these from secure environment variablesDB_USER = os.getenv("ORACLE_USER", "APPS")DB_PASSWORD = os.getenv("ORACLE_PASSWORD", "welcome1")DB_DSN = os.getenv("ORACLE_DSN", "ebs.example.com:1521/VIS")
def get_connection(): """Establishes a connection to the Oracle EBS database.""" # Ensure your container has network access (e.g. via NordLayer) try: connection = oracledb.connect( user=DB_USER, password=DB_PASSWORD, dsn=DB_DSN ) return connection except oracledb.Error as e: raise RuntimeError(f"Oracle Connection Failed: {e}")
@mcp.tool()def get_ebs_user_details(username: str) -> str: """ Retrieves user details from Oracle EBS (FND_USER table) based on username. Useful for validating employee IDs or checking account status. """ conn = get_connection() cursor = conn.cursor()
try: # Querying the standard FND_USER table in EBS sql = """ SELECT user_id, user_name, description, email_address, end_date FROM fnd_user WHERE user_name = :uname """ cursor.execute(sql, [username]) row = cursor.fetchone()
if row: return (f"User Found: ID={row[0]}, Name={row[1]}, " f"Desc={row[2]}, Email={row[3]}, EndDate={row[4]}") else: return f"No user found with username: {username}"
except oracledb.Error as e: return f"Database Error: {e}" finally: cursor.close() conn.close()
@mcp.tool()def check_inventory_level(item_number: str, organization_code: str) -> str: """ Checks on-hand quantity for an item in a specific inventory org. Target Tables: MTL_SYSTEM_ITEMS_B, MTL_ONHAND_QUANTITIES_DETAIL """ conn = get_connection() cursor = conn.cursor()
try: sql = """ SELECT sum(moq.transaction_quantity), msi.description FROM mtl_onhand_quantities_detail moq JOIN mtl_system_items_b msi ON moq.inventory_item_id = msi.inventory_item_id AND moq.organization_id = msi.organization_id JOIN mtl_parameters mp ON moq.organization_id = mp.organization_id WHERE msi.segment1 = :item_num AND mp.organization_code = :org_code GROUP BY msi.description """ cursor.execute(sql, [item_number, organization_code]) row = cursor.fetchone()
if row: return f"Item: {item_number} ({row[1]}) | On-Hand Qty: {row[0]}" else: return f"Item {item_number} not found in Org {organization_code} or no stock."
except oracledb.Error as e: return f"Database Error: {e}" finally: cursor.close() conn.close()
@mcp.tool()def run_custom_query(sql_query: str) -> str: """ Executes a read-only SQL query against the EBS database. WARNING: Use with caution. Restricted to SELECT statements. """ # Basic safety check if not sql_query.strip().upper().startswith("SELECT"): return "Security Alert: Only SELECT statements are allowed."
conn = get_connection() cursor = conn.cursor()
try: # Limiting rows to prevent context window overflow cursor.execute(sql_query) rows = cursor.fetchmany(10)
if not rows: return "Query returned no results."
result_str = "Query Results (First 10 rows):\n" for row in rows: result_str += str(row) + "\n" return result_str
except oracledb.Error as e: return f"SQL Execution Error: {e}" finally: cursor.close() conn.close()
if __name__ == "__main__": mcp.run()2. The Container (Dockerfile)
Section titled “2. The Container (Dockerfile)”We use a slim Python image. Note that python-oracledb in Thin mode does not require the heavy Oracle Instant Client libraries, keeping our image size small (~200MB) compared to the usual 1GB+ for “Thick” driver images.
# Use a lightweight Python baseFROM python:3.11-slim
# Set working directoryWORKDIR /app
# Install dependencies# fastmcp: The MCP server framework# oracledb: The official Oracle driver (Thin mode by default)RUN pip install --no-cache-dir fastmcp oracledb
# Copy the server codeCOPY server.py .
# EXPOSE the port for Railway/Docker networking# This is crucial for the MCP client to attachEXPOSE 8000
# Run the serverCMD ["python", "server.py"]🚀 Deployment & Connection
Section titled “🚀 Deployment & Connection”1. Build and Run
Section titled “1. Build and Run”docker build -t ebs-mcp .docker run -p 8000:8000 --env-file .env ebs-mcp2. Connect CrewAI
Section titled “2. Connect CrewAI”In your CrewAI project (or agent_config.yaml), you configure the agent to look for this MCP server.
from crewai import Agent, Task, Crewfrom crewai_tools import MCPTool
# Connect to the Dockerized MCP Serverebs_tool = MCPTool( name="Oracle EBS Connector", url="http://localhost:8000" # Or your Railway/Cloud URL)
ebs_specialist = Agent( role='EBS ERP Specialist', goal='Retrieve inventory and user data from the legacy Oracle system', backstory='You are a veteran ERP admin who knows the FND_USER and MTL tables by heart.', tools=[ebs_tool], verbose=True)
task = Task( description='Check the inventory level for Item AS54888 in Org M1.', agent=ebs_specialist)⚠️ Common Integration Errors
Section titled “⚠️ Common Integration Errors”DPY-6005: cannot connect to database: This usually means the container cannot see the Oracle host.- Fix: Use
network_mode: "host"for local testing, or ensure your VPN (e.g., NordLayer, Tailscale) is active in the container environment.
- Fix: Use
ORA-12514: TNS:listener does not currently know of service: You are using the wrong Service Name/SID.- Fix: Check your
tnsnames.oraon a working machine to verify the Service Name (e.g.,VIS,PROD).
- Fix: Check your
- Thick Mode Requirements: If you are using very old Oracle DB versions (11gR2 or older) or advanced features (TAF), you might need “Thick” mode.
- Fix: You will need to install
libaio1in the Dockerfile and download the Oracle Instant Client zip files.
- Fix: You will need to install
AgentRetrofit Bridging the gap between 2025 AI and 1995 Infrastructure.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.