SQLcl MCP Server¶
Module: inception_mcp_servers/mcp_sqlcl/
Endpoint: http://127.0.0.1:3001/mcp/
Health: http://127.0.0.1:3001/health
Venv: .venv_mcp_sqlcl
Exposes Oracle Database operations as MCP tools via SQLcl. The server bridges AI agent tool calls to a locally running SQLcl process, enabling natural-language-driven SQL execution with full IAM identity propagation.
Exposed Tools¶
Controlled by TOOL_REGISTRY in registry.py:
| Tool | Parameters | Description |
|---|---|---|
get_token |
— | Returns the current OAuth bearer token and JWT claims from the auth context |
connect |
connection_name, model?, confirm_switch? |
Connects to a named SQLcl database connection |
execute_sql |
query: str |
Executes a SQL query and returns parsed JSON results |
schema_information |
— | Returns enriched metadata about the connected schema |
test_connection |
— | Runs a SELECT against dual to verify connectivity |
To add or remove tools, edit TOOL_REGISTRY in registry.py. Any function not listed there cannot be called.
Architecture¶
MCP Client
│
▼ HTTP POST /mcp/
┌─────────────────────────┐
│ server_oidc.py │ ← Interactive: OCIProvider (browser OAuth)
│ or server_token.py │ ← Non-interactive: OCITokenAuthMiddleware (JWT bearer)
└──────────┬──────────────┘
│ routes to registered tool
▼
┌─────────────────────────┐
│ registry.py │ ← TOOL_REGISTRY: connect, execute_sql, schema_information …
└──────────┬──────────────┘
│ calls
▼
┌─────────────────────────┐
│ tools.py │
│ Worker queue + session │
└──────────┬──────────────┘
│ stdio
▼
┌─────────────────────────┐
│ SQLcl (sql -mcp) │ ← Oracle Database
│ connect / run-sql / │
│ schema-information │
└─────────────────────────┘
SQLcl session model¶
- A single background worker task owns the SQLcl stdio
ClientSession - All tool calls submit requests through an
asyncio.Queueto the worker - The worker serializes calls to the SQLcl process — no concurrent stdio access
- On error, the session is reset and the call retried once
- Session state (active connection name) is tracked globally within the process
SQLcl native operations (called internally)¶
The underlying sql -mcp process exposes these tools, called by tools.py:
| SQLcl Tool | Used by |
|---|---|
list-connections |
connect (on failure, to hint available names) |
connect |
connect |
run-sql |
execute_sql, test_connection |
schema-information |
schema_information |
Authentication¶
Interactive mode (server_oidc.py)¶
- Provider:
fastmcp.server.auth.providers.oci.OCIProvider - Client connects with
auth="oauth"— FastMCP opens a browser for OCI IAM login - After login, tool calls are authorized by the issued access token
- Required env vars:
IDCS_DOMAIN,IDCS_CLIENT_ID,IDCS_CLIENT_SECRET
Bearer token mode (server_token.py)¶
- Middleware:
OCITokenAuthMiddleware+TokenVerifier - Client supplies
Authorization: Bearer <token>on every request - Token validated via IDCS JWKS:
https://{IDCS_DOMAIN}/admin/v1/SigningCert/jwk - Issuer:
https://identity.oraclecloud.com/ - Audience:
https://{IDCS_DOMAIN}:443 - Algorithm: RS256
- Required env vars:
IDCS_DOMAIN,MCP_ACCESS_TOKEN
Token acquisition — OCI API key exchange¶
For non-interactive clients (agents, CLI, server-side gateways) that cannot do browser OAuth, the server supports tokens obtained via OCI HTTP Signature token exchange (RFC 8693).
The client signs a POST request to the IDCS token endpoint using its OCI API key (RSA-SHA256), requesting grant_type=urn:ietf:params:oauth:grant-type:token-exchange. The returned access_token is then passed as a Bearer header on all subsequent MCP calls.
This flow is implemented in the Smart Dispatch gateway and CLI using the service_get_token auth library:
from agent_get_token import AgentTokenClient
client = AgentTokenClient(
token_url=OCI_IDENTITY_TOKEN_URL,
tenancy_ocid=OCI_IDENTITY_TENANCY_OCID,
user_ocid=OCI_IDENTITY_USER_OCID,
fingerprint=OCI_IDENTITY_FINGERPRINT,
private_key_file=OCI_IDENTITY_PRIVATE_KEY_FILE,
scope="urn:opc:idm:__myscopes__",
)
bearer_token = client.get_access_token()
Required env vars: OCI_IDENTITY_TOKEN_URL, OCI_IDENTITY_TENANCY_OCID, OCI_IDENTITY_USER_OCID, OCI_IDENTITY_FINGERPRINT, OCI_IDENTITY_PRIVATE_KEY_FILE.
See Solution Patterns — Service Token Acquisition for the full pattern.
Identity propagation to the database (ociclients.py)¶
Once a validated Bearer token is in scope, the MCP server can exchange it for a database-scoped token to preserve the caller's identity in Oracle:
# src/auth/ociclients.py
from token_factory import TokenFactory
def get_db_token(mcp_token) -> str:
factory = TokenFactory.from_env()
result = factory.get_token(mcp_token.token, "oracle-database")
return result["token"]
The TokenFactory exchanges the IDCS access token for a short-lived Oracle DB token so the database connection is opened under the end user's mapped identity — enabling VPD row-level security and a complete audit trail. Tokens are cached in memory by JTI to avoid redundant exchanges within a session.
Configuration¶
# .env
MCP_HOST_MCP_SQLCL=127.0.0.1
MCP_PORT_MCP_SQLCL=3001
SQLCL_PATH=/Applications/sqlcl/bin/sql
SQLCL_CONNECTION=<named-connection>
# OCI IAM
IDCS_DOMAIN=<domain>.identity.oraclecloud.com # bare hostname only
IDCS_CLIENT_ID=<client-id>
IDCS_CLIENT_SECRET=<client-secret>
# Token mode only
MCP_ACCESS_TOKEN=<oauth-access-token>
Running the Server¶
cd inception_mcp_servers/mcp_sqlcl
# First-time setup
.venv_mcp_sqlcl/bin/python -m pip install -r requirements.txt
# Interactive OIDC server
.venv_mcp_sqlcl/bin/python -m src.server_oidc
# Non-interactive bearer token server
.venv_mcp_sqlcl/bin/python -m src.server_token
Free the port if needed:
Testing¶
Bootstrap a token (run against server_oidc.py)¶
# server_oidc.py must be running
python -m tests.get_token
# Opens OCI browser login → writes MCP_ACCESS_TOKEN to .env
Non-interactive regression test (run against server_token.py)¶
python -m tests.test_mcp_sqlcl --url http://127.0.0.1:3001/mcp/ --limit 10
# Optional: --connection-name <name> --model <model> --access-token <token>
MCP Inspector¶
fastmcp dev --server-spec src/server_oidc.py:mcp # interactive
fastmcp dev --server-spec src/server_token.py:mcp # bearer token
SQLcl Prerequisites¶
Install SQLcl and save a named connection before starting the server:
# Download SQLcl
mkdir sqlcl && cd sqlcl
wget https://download.oracle.com/otn_software/java/sqldeveloper/sqlcl-latest.zip
unzip sqlcl-latest.zip
# Save a named connection
./bin/sql /nolog
SQL> conn -save myConn -savepwd username/password@host:1521/service
Configure SQLCL_PATH and SQLCL_CONNECTION in .env to match.
Full SQLcl MCP docs: Oracle SQLcl MCP Server
CSV Result Parsing¶
SQLcl returns query results as CSV text. tools.py parses this into structured JSON:
{
"success": true,
"data": [
{"EMPLOYEE_ID": "100", "LAST_NAME": "King", "SALARY": "24000"}
],
"rowCount": 1,
"timestamp": "2026-04-18T21:00:00",
"query": "SELECT ..."
}
Quoted CSV values and embedded commas are handled correctly by the custom CSV parser.