Wednesday, July 8, 2026

How to Connect Oracle AI Private Agent Factory to Oracle EBS Using SQLcl MCP Server over SSE

How to Connect Oracle AI Private Agent Factory to Oracle EBS Using SQLcl MCP Server over SSE


Over the past few weeks, I have been hands-on with Oracle AI Private Agent Factory (PAF) on OCI, and one of the most interesting challenges I faced was connecting PAF to a classic Oracle EBS (E-Business Suite) database that sits on a private subnet. There was no documentation that covered this end-to-end, and I had to piece things together through trial, error, and a fair amount of troubleshooting.

In this blog, I am going to walk you through everything I did — from installing SQLcl on Oracle Linux 9.7, understanding why a bridge is needed between SQLcl MCP and PAF, dealing with SELinux, cross-VCN networking on OCI, and finally getting PAF to talk to your EBS database through the MCP server. If you are doing something similar, this is the guide I wish I had.


Understanding the Architecture
Before we dive into commands, let me explain what we are building and why each component exists. This is the flow:
PAF (VCN 1, Private Subnet)  →  SQLcl MCP VM (VCN 2, Private Subnet)  →  EBS Database (VCN 2, Private Subnet)

Oracle AI Private Agent Factory is an agentic AI platform that orchestrates AI agents to perform tasks — including querying databases. It communicates with external tools using the Model Context Protocol (MCP), but it expects MCP servers to be reachable over HTTP/SSE (Server-Sent Events).

SQLcl is Oracle's command-line interface for Oracle Database. From version 25.2 onwards, it ships with a built-in MCP server that you can activate with a single flag — sql -mcp. However, SQLcl's MCP server speaks stdio (standard input/output), not HTTP. This is the core challenge.


💡  PAF speaks HTTP/SSE. SQLcl MCP speaks stdio. You need a bridge between them — that bridge is mcp-proxy running on the same VM as SQLcl.


The mcp-proxy tool (Python) wraps the SQLcl stdio process and exposes it as an HTTP/SSE endpoint on port 8080. Once that endpoint is live, PAF can discover and call all five SQLcl MCP tools as if they were a native HTTP service.

The following are the OS details in my OCI environment,
OS - Oracle Linux 9.7 (OL9) OR Oracle Linux
Architecture - x86_64
RAM - Minimum 4 GB 
Disk - Boot volume at least 100 GB
Network - Private subnet 
User - oracle user with sudo access

Software Prerequisites
Java 17 or higher (JRE/JDK) — SQLcl is Java-based
SQLcl 25.2 or higher — includes the built-in MCP server
Python 3.11 or higher — required for mcp-proxy
mcp-proxy Python package — the stdio-to-SSE bridge

Section 1: Installing Java 17 on Oracle Linux 9

The first thing you need is Java 17 or higher. Do not touch the system Python or any system-managed Java — install a clean version alongside what is already on the system.

# Install OpenJDK 17
sudo dnf install java-17-openjdk -y

# Set JAVA_HOME
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk' >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# Verify
java -version

💡  On OL9, OpenJDK 17 is available directly from the default dnf repositories — no third-party repo needed.


Section 2: Installing SQLcl

Download SQLcl 25.2 or later from Oracle's official download page (requires an Oracle account). Once you have the zip file, extract it to a standard location.

# Create the install directory
mkdir -p /home/oracle/sqlcl

# Unzip (replace with your actual downloaded filename)
unzip sqlcl-25.x.x.zip -d /home/oracle/sqlcl/

# The binary is called 'sql' — not 'sqlcl'
# This is a common gotcha!
export PATH=/home/oracle/sqlcl/bin:$PATH

# Verify
sql -version

💡  The binary is named 'sql', not 'sqlcl'. This trips up almost everyone the first time. All commands going forward use 'sql', including sql -mcp.


Section 3: Saving the EBS Database Connection

The SQLcl MCP server relies on connections saved in its credential store at ~/.dbtools. You must save your EBS connection with the -savepwd flag before the MCP server can auto-connect to it.

# Launch SQLcl
/home/oracle/sqlcl/bin/sql /nolog

# Inside SQLcl — save EBS connection with password
SQL> conn -save ebs_mcp -savepwd apps/yourpassword@//ebs-db-hostname:1521/EBSDB

# Verify it saved correctly
SQL> connmgr list

SQL> exit

If your EBS database uses a tnsnames.ora entry, set TNS_ADMIN first:
export TNS_ADMIN=/home/oracle/network/admin

sql /nolog
SQL> conn -save ebs_mcp -savepwd apps/yourpassword@EBSDB



Section 4: Installing Python 3.11 and mcp-proxy

Your Oracle Linux 9 ships with Python 3.9, which is tied to system tools like dnf. Do not upgrade or replace it. Instead, install Python 3.11 alongside it — they coexist cleanly on OL9.

# Install Python 3.11
sudo dnf install python3.11 python3.11-pip -y

# Verify
python3.11 --version

# Install mcp-proxy using Python 3.11
python3.11 -m pip install --user mcp-proxy

# Add local bin to PATH
echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# Verify
mcp-proxy --version

💡  mcp-proxy requires Python 3.10 or higher. If you try to install it with the system Python 3.9, it will fail with 'No matching distribution found'. Always use python3.11 -m pip install for this package.


Why mcp-proxy and What It Does

Let me explain this clearly because I got confused about this myself. There are several bridge tools in the MCP ecosystem — mcp-remote, mcp-proxy, supergateway — and they all sound similar but do different things.

For our use case — wrapping SQLcl MCP (stdio) and exposing it as HTTP/SSE for PAF — mcp-proxy (Python) is the correct tool. It spawns sql -mcp as a child process, handles stdio communication internally, and exposes a clean SSE endpoint on port 8080 that PAF can register and call.

Section 5: Creating the Wrapper Script and systemd Service

The Wrapper Script
Create a shell script that sets all required environment variables and launches mcp-proxy with the correct arguments. Pay attention to using absolute paths — systemd does not load your .bashrc, so relative paths and ~ expansions will not work.

cat > /usr/local/bin/sqlmcpserver.sh << 'EOF'
#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export TNS_ADMIN=/home/oracle/network/admin
export PATH=/home/oracle/.local/bin:/usr/lib/jvm/java-17-openjdk/bin:/home/oracle/sqlcl/bin:$PATH

exec /home/oracle/.local/bin/mcp-proxy \
  --port=8080 \
  --host=0.0.0.0 \
  -- /home/oracle/sqlcl/bin/sql -mcp
EOF

chmod +x /usr/local/bin/sqlmcpserver.sh


💡  Keep your scripts in /usr/local/bin rather than /home/oracle. Oracle Linux 9 runs SELinux in Enforcing mode by default, and scripts in home directories carry the user_home_t context which systemd refuses to execute (you get status=203/EXEC). Scripts in /usr/local/bin automatically get the bin_t context that systemd accepts.


The systemd Service
Create a systemd unit file to manage the service lifecycle — auto-start on boot, auto-restart on crash, and proper logging via journald.

sudo vi /etc/systemd/system/sqlcl-mcp.service
[Unit]
Description=SQLcl MCP HTTP/SSE Server for PAF
After=network.target

[Service]
Type=simple
User=oracle
ExecStart=/usr/local/bin/sqlmcpserver.sh
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable sqlcl-mcp
sudo systemctl start sqlcl-mcp
sudo systemctl status sqlcl-mcp

A healthy service output looks like this:
Active: active (running) since Wed 2026-07-01 06:20:07 GMT
Main PID: 318054 (mcp-proxy)
INFO: Uvicorn running on http://0.0.0.0:8080
INFO: Application startup complete.

💡  Watch for 'http://0.0.0.0:8080' specifically. If you see '127.0.0.1:8080', add --host=0.0.0.0 to your mcp-proxy command — otherwise PAF cannot reach it from outside the VM.

Section 6: SELinux — The Silent Blocker

This deserves its own section because it cost me a significant amount of troubleshooting time. Oracle Linux 9 runs SELinux in Enforcing mode by default. If your script is in /home/oracle, systemd will refuse to execute it with status=203/EXEC — even if the file has execute permissions and a valid shebang.

The quick way to diagnose this:
getenforce
# Enforcing

ls -lZ /home/oracle/sqlmcpserver.sh
# -rwxr-xr-x. oracle oinstall unconfined_u:object_r:user_home_t:s0

The context user_home_t is the problem. The solution is simply to move your script to /usr/local/bin where it automatically gets the bin_t context that systemd trusts. Alternatively, if you must keep it in the home directory, you can relabel it:

sudo dnf install policycoreutils-python-utils -y
sudo semanage fcontext -a -t bin_t "/home/oracle/sqlmcpserver.sh"
sudo restorecon -v /home/oracle/sqlmcpserver.sh

💡  Never disable SELinux permanently to work around this. It is there for a reason — especially on a VM that holds database credentials. Fix the context, not the policy.

OS Firewall on SQLcl VM
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports

Verify Connectivity End-to-End
# From SQLcl VM — confirm EBS DB is reachable
nc -zv <ebs-db-private-ip> 1521

# From PAF VM — confirm SQLcl MCP SSE endpoint is reachable
curl -N http://<sqlcl-vm-private-ip>:8080/sse

Section 7: Registering SQLcl MCP in Oracle AI Private Agent Factory

Once the SSE endpoint is confirmed reachable from the PAF VM, registering it in PAF is straightforward.
Open PAF UI → Settings → MCP Servers → Add
Name: SQLcl-EBS-MCP
Endpoint URL: http://<sqlcl-vm-private-ip>:8080/sse
Transport: SSE
Authentication: None (for private subnet deployments)
Click Connect — status should change to Connected

Once connected, PAF will automatically discover all five SQLcl MCP tools:



Thanks & Regards,
Chandan Tanwani

No comments: