Skip to content

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

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.


We will build a Dockerized MCP (Model Context Protocol) Server that exposes Oracle EBS data as tool-use functions for your agents.

  • 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)
  1. Network Access: Your container must be able to reach the Oracle Host/Port (typically 1521).
  2. Service Name: The unique Service ID (SID) or Service Name of your EBS database.
  3. Credentials: A read-only service account (e.g., APPS_READ).

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 os
import oracledb
from fastmcp import FastMCP
# Initialize the MCP Server
mcp = FastMCP("oracle-ebs")
# Database Configuration
# In production, load these from secure environment variables
DB_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()

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 base
FROM python:3.11-slim
# Set working directory
WORKDIR /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 code
COPY server.py .
# EXPOSE the port for Railway/Docker networking
# This is crucial for the MCP client to attach
EXPOSE 8000
# Run the server
CMD ["python", "server.py"]

Terminal window
docker build -t ebs-mcp .
docker run -p 8000:8000 --env-file .env ebs-mcp

In your CrewAI project (or agent_config.yaml), you configure the agent to look for this MCP server.

from crewai import Agent, Task, Crew
from crewai_tools import MCPTool
# Connect to the Dockerized MCP Server
ebs_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
)
  1. 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.
  2. ORA-12514: TNS:listener does not currently know of service: You are using the wrong Service Name/SID.
    • Fix: Check your tnsnames.ora on a working machine to verify the Service Name (e.g., VIS, PROD).
  3. 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 libaio1 in the Dockerfile and download the Oracle Instant Client zip files.

AgentRetrofit Bridging the gap between 2025 AI and 1995 Infrastructure.


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

Transparency: This page may contain affiliate links.