I am currently experimenting with MCP as part of a small Oracle development lab. The first goal is simple: let Codex work with an Oracle Database through SQLcl, while keeping the connection path and execution boundaries explicit.
This post covers three things:
The setup shown here is based on the configuration used in my current taskhub-oracle-codex-lab project.

MCP, or Model Context Protocol, defines a standard way for an AI client to discover and call tools exposed by an external server.
In this setup:
Instead of giving the model a database password in a prompt, Codex calls tools exposed by SQLcl. SQLcl then performs the database operation using a connection that already exists in its local connection store.

Oracle documents SQLcl MCP tools for listing saved connections, connecting and disconnecting, inspecting schema information, running SQL and PL/SQL, and running SQLcl-specific commands.
Official documentation:
Codex can define MCP servers in:
~/.codex/config.toml
This is the user-level Codex configuration file. Codex uses it for settings such as model defaults, approvals, sandbox behavior, trusted projects, and MCP server definitions; project-specific overrides can also be placed in a repository-level .codex/config.toml.
OpenAI documentation:
The same MCP configuration is shared between the Codex CLI and IDE integration.
For example, a remote MCP server can be configured with a URL:
[mcp_servers.openaiDeveloperDocs]
url = "https://developers.openai.com/mcp"
A local MCP server is different. Codex starts a local process and communicates with it over stdio.
That is the model used for SQLcl in this lab.
The project itself is marked as trusted in my Codex configuration:
[projects."/home/sagivba-adm/src/TaskHub_Codex_MCP_Starter/taskhub-oracle-codex-lab"]
trust_level = "trusted"
The SQLcl MCP server is configured separately:
[mcp_servers.sqlcl-taskhub-lab]
command = "/home/sagivba-adm/.local/bin/sql"
args = ["-R", "1", "-mcp"]
[mcp_servers.sqlcl-taskhub-lab.tools.connections_list]
approval_mode = "approve"
[mcp_servers.sqlcl-taskhub-lab.tools.connect]
approval_mode = "approve"
[mcp_servers.sqlcl-taskhub-lab.tools.schema_information]
approval_mode = "approve"
[mcp_servers.sqlcl-taskhub-lab.tools.sql_run]
approval_mode = "approve"
[mcp_servers.sqlcl-taskhub-lab.tools.sqlcl_run]
approval_mode = "approve"
[mcp_servers.sqlcl-taskhub-lab.tools.disconnect]
approval_mode = "approve"
There are three parts here that are worth looking at separately.
The command is:
command = "/home/sagivba-adm/.local/bin/sql"
args = ["-R", "1", "-mcp"]
The -mcp argument starts SQLcl as an MCP server.
The -R option controls how much of SQLcl is available to the MCP server.
For a new MCP setup, I recommend starting with the most restrictive level and relaxing it only when a specific requirement justifies it.
R4 is the safest starting point and is also the default for the SQLcl MCP server when no -R option is specified. It blocks host commands, script execution, configuration changes, and other potentially sensitive SQLcl operations.
R1 is less restrictive. It still blocks operating-system-level commands such as HOST, but allows SQL scripts to be executed with commands such as @ and @@. This is the level I currently use in this lab because the Agent needs to work with project SQL scripts while still being prevented from executing host commands.
R0 removes the SQLcl restrictions entirely, including restrictions on host and script execution. It should therefore be used only when full SQLcl functionality is genuinely required and the surrounding environment is sufficiently isolated and controlled.
A useful way to think about the three levels is:
R4 -> Most restrictive. Recommended starting point.
R1 -> Allows script execution, but blocks host/OS commands.
R0 -> Unrestricted. Allows all commands.
Start with R4, verify what the Agent can accomplish, and move to R1 or eventually R0 only when a required operation is actually blocked.
Oracle documentation:
The configuration also sets:
approval_mode = "approve"
for every SQLcl MCP tool enabled in this lab.
For example:
[mcp_servers.sqlcl-taskhub-lab.tools.sql_run]
approval_mode = "approve"
I currently use approval for:
connections_list
connect
schema_information
sql_run
sqlcl_run
disconnect
This is intentional. While experimenting with an Agent that can reach a database, I prefer to see the requested operation before it is executed rather than immediately moving to unattended execution.
SQLcl does not require Codex to receive a username and password directly.
Instead, SQLcl MCP relies on saved SQLcl connections.
The local SQLcl connection store is under:
~/.dbtools
This directory is used by SQLcl for its local connection store and related SQL Developer/SQLcl configuration data. For MCP specifically, SQLcl reads the preconfigured saved connections from this store, and those connections can be managed with the CONNECT and CONNMGR commands.
Oracle documentation:
Connections can be managed from SQLcl using CONNMGR.
For example:
connmgr list
and:
connmgr show <connection-name>
For a saved connection to be usable by an MCP client, Oracle requires its password to be saved. A connection can be created with -save and -savepwd, for example:
conn -save agent-dev -savepwd agent_user/password@//dbhost:1521/service
Oracle states that saved passwords are stored securely rather than as plain text.
This is better than putting database credentials in prompts, project files, or the Codex MCP configuration.
It should not, however, be treated as a replacement for workstation security. The connection store exists on the machine, and the Agent can use a saved connection without interactively entering its password.
I also would not assume that copying ~/.dbtools to another machine is harmless. The Oracle documentation describes secure credential storage, but I have not found a documented guarantee that copying the connection store can never result in credential reuse. That is something worth testing separately rather than making assumptions about it.
The first useful MCP operation is to discover which SQLcl connections are available and then open one of them.

At a high level, the discovery flow is:
Codex
|
| connections_list
v
SQLcl MCP Server
|
| saved connections
v
SQLcl Connection Manager
Once a connection is selected, Codex asks SQLcl MCP to open it:
Codex
|
| connect
v
SQLcl MCP Server
|
| selected saved connection
v
Oracle Database
Once connected, Codex can request operations through SQLcl MCP, such as schema inspection or SQL execution.
Oracle documents the corresponding SQLcl capabilities as:
list-connectionsconnectdisconnectschema-informationrun-sqlrun-sqlclIn my Codex configuration, the tool-specific approval entries appear as:
connections_list
connect
disconnect
schema_information
sql_run
sqlcl_run
The important point is that Codex is calling a defined MCP tool rather than receiving an unrestricted database credential and inventing its own connection mechanism.
After updating ~/.codex/config.toml, the configured MCP servers can be checked with:
codex mcp list
A useful first test is intentionally simple: ask Codex to show the Oracle connections available through SQLcl.
That lets us verify the MCP path before allowing any SQL execution.
It is easy to say that an AI Agent should not have access to production.
That is true, but it is not enough.
A shared DEV or integration environment can still contain:
An Agent deleting a central table or modifying a large amount of data can still create a serious incident even when the database is not production.
For this reason, I prefer to begin with the most restrictive SQLcl configuration that still supports the task, keep explicit tool approvals enabled while experimenting, and expand capabilities only when the lab actually requires them.