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”In the enterprise world, the Oracle drivers for Node.js (node-oracledb) are often more robust and easier to configure in “Thin Mode” than their Python counterparts. To leverage this within a Python-native AI framework like CrewAI, we use a Polyglot Bridge architecture.
This guide details how to deploy a Python FastMCP server that acts as a “Host,” delegating the heavy lifting of database connectivity to a specialized Node.js “Worker.”
The Architecture
Section titled “The Architecture”- CrewAI Agent: Sends instructions via the Model Context Protocol (MCP).
- Python Host (
server.py): Accepts the MCP request and spawns a Node.js subprocess. - Node.js Worker (
oracle_client.js): Connects to Oracle EBS using the officialoracledblibrary, executes the SQL, and pipes JSON back to the host. - Docker: Encapsulates both runtimes for deployment on platforms like Railway.
1. The Worker (oracle_client.js)
Section titled “1. The Worker (oracle_client.js)”This lightweight script reads arguments from Standard Input (stdin) and writes results to Standard Output (stdout). It uses Oracle’s “Thin Mode” to avoid needing the full Instant Client binaries.
const oracledb = require('oracledb');
// Enable Thin Modeoracledb.initOracleClient({ libDir: process.env.ORACLE_LIB_DIR || undefined });
async function run() { let inputBuffer = '';
// 1. Read JSON input from Python process.stdin.on('data', (chunk) => { inputBuffer += chunk; });
process.stdin.on('end', async () => { let connection; try { const args = JSON.parse(inputBuffer);
// 2. Connect to Oracle EBS // Ensure your container has network access (e.g. via NordLayer) connection = await oracledb.getConnection({ user: process.env.ORACLE_USER, password: process.env.ORACLE_PASSWORD, connectString: process.env.ORACLE_CONNECTION_STRING // Format: HOST:PORT/SERVICE });
// 3. Execute Query safely const result = await connection.execute( `SELECT invoice_id, invoice_num, invoice_date, invoice_amount FROM ap_invoices_all WHERE invoice_num = :inv_num`, [args.invoice_num], { outFormat: oracledb.OUT_FORMAT_OBJECT } );
// 4. Return Data console.log(JSON.stringify({ status: 'success', data: result.rows }));
} catch (err) { console.error(JSON.stringify({ status: 'error', message: err.message })); process.exit(1); } finally { if (connection) { try { await connection.close(); } catch (e) { console.error(e); } } } });}
run();2. The Server (server.py)
Section titled “2. The Server (server.py)”This is the entry point for the Agent. It uses FastMCP to define the tools and standard subprocess libraries to control the Node.js worker.
import sysimport jsonimport subprocessfrom fastmcp import FastMCP
# Initialize the MCP Servermcp = FastMCP("OracleEBS_Bridge")
@mcp.tool()def get_invoice_details(invoice_num: str) -> str: """ Retrieves invoice details from Oracle EBS given an Invoice Number. Useful for checking payment status or amounts. """ payload = json.dumps({"invoice_num": invoice_num})
try: # Spawn the Node.js worker process = subprocess.Popen( ["node", "oracle_client.js"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True )
# Send data and get response stdout, stderr = process.communicate(input=payload)
if process.returncode != 0: return f"Bridge Error: {stderr.strip()}"
# Parse Worker Response try: response = json.loads(stdout) if response.get("status") == "success": return json.dumps(response["data"], indent=2) else: return f"Oracle Error: {response.get('message')}" except json.JSONDecodeError: return f"Invalid JSON from worker: {stdout}"
except Exception as e: return f"System Execution Error: {str(e)}"
if __name__ == "__main__": # Ensure your container has network access (e.g. via NordLayer) # MUST bind to 0.0.0.0 for Docker networking mcp.run(transport='sse', host='0.0.0.0', port=8000)3. Dockerfile
Section titled “3. Dockerfile”To run this bridge, we need an environment containing both Python and Node.js.
# Start with a lightweight Python baseFROM python:3.11-slim
# Install system utilities and Node.jsRUN apt-get update && apt-get install -y \ curl \ gnupg \ && mkdir -p /etc/apt/keyrings \ && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ && apt-get update \ && apt-get install -y nodejs \ && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy LogicCOPY server.py oracle_client.js ./
# Install Python depsRUN pip install fastmcp uvicorn
# Install Node depsRUN npm init -y && npm install oracledb
# Expose port for Railway/External accessEXPOSE 8000
# Run the Python HostCMD ["python", "server.py"]4. Connecting the Agent
Section titled “4. Connecting the Agent”CrewAI natively supports connecting to remote MCP servers using the mcps parameter in the Agent configuration. This is cleaner than importing tool classes manually.
from crewai import Agent, Task, Crew
# 1. Define the Agent with the MCP connection# Note: Do not use 'tools=[]'. Use 'mcps=[]' for remote servers.oracle_specialist = Agent( role="Oracle ERP Specialist", goal="Retrieve financial data from legacy Oracle systems", backstory="You are a veteran database admin who can query Oracle EBS.", # Connect to the Docker container via SSE mcps=["http://localhost:8000/sse"], verbose=True)
# 2. Define the Taskaudit_task = Task( description="Get the details for Invoice #INV-2024-001 from Oracle.", expected_output="A summary of the invoice amount and date.", agent=oracle_specialist)
# 3. Run the Crewcrew = Crew( agents=[oracle_specialist], tasks=[audit_task])
result = crew.kickoff()print(result)Deployment Checks
Section titled “Deployment Checks”- Environment Variables: Ensure
ORACLE_USER,ORACLE_PASSWORD, andORACLE_CONNECTION_STRINGare set in your Docker run command or Railway dashboard. - Network: If Oracle EBS is behind a corporate firewall, your Docker container needs a VPN sidecar (like NordLayer or Tailscale) to reach the database host.
- Port Mapping: Run with
docker run -p 8000:8000 --env-file .env my-oracle-bridge.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.