Skip to main content

Amazon RDS for Oracle & Oracle Database@AWS MCP Server

AWS Labs MCP Server for Oracle Database

An AWS Labs Model Context Protocol (MCP) server for Oracle Database on AWS RDS and Oracle Database@AWS databases.

Features​

  • Direct Oracle connections via password authentication (AWS Secrets Manager)
  • SQL injection detection and Oracle-specific mutating keyword blocking
  • Read-only transaction enforcement using SET TRANSACTION READ ONLY
  • Connection pool management using python-oracledb thin mode (no Oracle Instant Client needed)
  • Support for both service_name and SID connection styles
  • TLS encryption with certificate validation by default (--ssl_encryption require)

Tools​

  • run_query — Execute SQL queries against Oracle Database
  • get_table_schema — Fetch table column information from ALL_TAB_COLUMNS
  • connect_to_database — Connect to the Oracle database configured at server startup (no parameters; the target is fixed by the operator's launch flags)
  • is_database_connected — Check if a connection exists
  • get_database_connection_info — List all cached connections

Usage​

AWS RDS for Oracle​

--db_endpoint is required for RDS (it is not auto-resolved; only ODB Autonomous endpoints are).

awslabs.oracle-mcp-server \
--connection_method ORACLE_PASSWORD \
--instance_identifier my-oracle-instance \
--db_endpoint my-instance.xxxx.rds.amazonaws.com \
--region us-east-1 \
--database ORCL \
--service_name ORCL \
--port 2484

The default --ssl_encryption require uses Oracle TCPS, which listens on port 2484 — pass --port 2484. The server's own port default is 1521 (plain TCP), so omitting --port under the default TLS mode attempts a TCPS handshake against the cleartext port and fails.

Oracle Database@AWS​

Autonomous (ADB-S / ADB-D) connect over TCPS on port 1522. The private endpoint DNS name is auto-resolved via odb:GetAutonomousDatabase, so --db_endpoint is optional and the default --ssl_encryption require applies. Pass the database as --instance_identifier (ODB ARN arn:aws:odb:...:autonomous-database/adb_... or short adb_... id); --service_name is the _high/_medium/_low service (e.g. <ocid-prefix>_<dbname>_high.adb.oraclecloud.com):

awslabs.oracle-mcp-server \
--connection_method ORACLE_PASSWORD \
--instance_identifier arn:aws:odb:us-east-1:123456789012:autonomous-database/adb_xxxxxxxxxx \
--port 1522 \
--service_name gxxxxxxxxxxxxxx_mydbname_high.adb.oraclecloud.com \
--secret_arn arn:aws:secretsmanager:us-east-1:123456789012:secret:my-readonly-user-AbCdEf \
--region us-east-1 \
--ssl_encryption require

For Exadata Dedicated, pass the cluster's SCAN DNS name as --db_endpoint on port 2484 with the PDB service name, e.g. --db_endpoint <cluster-scan>.<subnet-domain>.oraclevcn.com --service_name <pdb-service>.paas.oracle.com --port 2484 --ssl_encryption require.

Use --ssl_encryption noverify only on a trusted path (SSH/SSM tunnel, or an address that doesn't match the certificate) — it skips certificate/hostname verification.

Connection Methods​

  • ORACLE_PASSWORD — Uses credentials from AWS Secrets Manager (MasterUserSecret by default)

Using a custom Secrets Manager secret​

For Oracle Database@AWS you have to provide a secret's ARN. For AWS RDS for Oracle, by default the server discovers the RDS instance's MasterUserSecret by calling describe_db_instances. To connect as a different database user, create your own secret in AWS Secrets Manager and pass its ARN with --secret_arn:

awslabs.oracle-mcp-server \
--connection_method ORACLE_PASSWORD \
--instance_identifier my-oracle-instance \
--db_endpoint my-instance.xxxx.rds.amazonaws.com \
--region us-east-1 \
--database ORCL \
--service_name ORCL \
--port 2484 \
--secret_arn arn:aws:secretsmanager:us-east-1:123456789012:secret:my-readonly-user-AbCdEf

IAM permissions​

  • secretsmanager:GetSecretValue on the secret used for the connection — always required.
  • rds:DescribeDBInstances — required when connecting to an RDS instance without --secret_arn (to discover the MasterUserSecret), and used for the multi-tenant (CDB) safety guard. When you pass --secret_arn, this permission is optional: if it is not granted, the server logs a warning, skips the multi-tenant guard, and connects using the configured secret.
  • rds:DescribeTenantDatabases — required when connecting to a tenant database (PDB) via --tenant_database_name without --secret_arn (to discover the tenant's managed secret). When you pass --secret_arn, it is optional: if not granted, the server logs a warning, skips the tenant lookup, and connects using the configured secret.
  • odb:GetAutonomousDatabase — required for ODB Autonomous Databases (endpoint resolution). ODB always requires --secret_arn.

The secret must be a JSON object with the following keys (case variations accepted):

KeyAccepted variants
usernameusername, user, Username
passwordpassword, Password

Example secret value:

{
"username": "mcp_readonly",
"password": "UseAStrongPassword" # pragma: allowlist secret
}

Secrets are configured exclusively at startup via --secret_arn; the flag is not exposed as an MCP tool parameter, so the LLM cannot select an arbitrary secret ARN.

The --secret_arn flag may be repeated to bind different secrets to different instances:

  • --secret_arn <instance_identifier>=<arn> — bind an ARN to a specific RDS instance (matched against the instance_identifier supplied to connect_to_database).
  • --secret_arn <arn> — a bare ARN used as the default for any instance not pinned explicitly (at most one allowed).

Resolution order: per-target ARN > bare default ARN > the instance's RDS-managed MasterUserSecret. The RDS MasterUserSecret is consulted for a given instance only when that instance has no per-target ARN and no bare default is configured.

TLS / SSL​

By default the server connects with --ssl_encryption require, which encrypts the connection using Oracle TCPS and validates the server certificate against the system CA store.

For Oracle Database@AWS download the client certificate from OCI for Exadata Dedicated Databases and from AWS ODB@AWS for Autonomous Serverless databases.

  • Exadata Dedicated: Download the client certificate from the OCI Console (PDB Database > More Action > PDB Connection > Download Connection Bundle)
  • Autonomous Serverless: download the wallet with aws odb create-autonomous-database-wallet:
aws odb create-autonomous-database-wallet \
--autonomous-database-id <adb-id> \
--wallet-type REGIONAL \
--password <your-wallet-password> \
--region us-east-1

RDS Oracle certificates are signed by the Amazon RDS CA, which is not included in the default system trust store. If you see a certificate validation error on first connection, install the Amazon RDS CA bundle:

Windows (PowerShell, run as Administrator)

# Download the global RDS CA bundle
Invoke-WebRequest -Uri https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem `
-OutFile global-bundle.pem

# Import all certificates in the bundle into the Trusted Root store
certutil -addstore "Root" global-bundle.pem

macOS

# Download the global RDS CA bundle
curl -O https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem

# Import into the macOS system keychain (requires sudo)
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain global-bundle.pem

Linux (Debian/Ubuntu)

curl -O https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
sudo cp global-bundle.pem /usr/local/share/ca-certificates/amazon-rds-ca.crt
sudo update-ca-certificates

Linux (RHEL/Amazon Linux)

curl -O https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
sudo cp global-bundle.pem /etc/pki/ca-trust/source/anchors/amazon-rds-ca.pem
sudo update-ca-trust

After installing the CA cert, restart the MCP server.

Port Forwarding / SSH Tunnel​

Oracle TCPS (TLS) listens on port 2484 by default. Standard unencrypted Oracle TCP uses port 1521. When using an SSH tunnel with TLS, tunnel port 2484 (not 1521):

ssh -N \
-L 2484:my-instance.xxxx.rds.amazonaws.com:2484 \
ec2-user@<bastion-ip>

Security group requirement: the EC2 bastion's security group must be allowed as an inbound source on port 2484 in the Oracle RDS security group.

Then connect with --ssl_encryption noverify to keep TLS encryption while skipping certificate and hostname verification (the cert won't match localhost):

awslabs.oracle-mcp-server \
--connection_method ORACLE_PASSWORD \
--instance_identifier my-oracle-instance \
--db_endpoint localhost \
--port 2484 \
--region us-east-1 \
--database ORCL \
--service_name ORCL \
--ssl_encryption noverify

To disable TLS entirely (e.g., tunneling port 1521 and the SSH connection provides encryption):

awslabs.oracle-mcp-server \
--connection_method ORACLE_PASSWORD \
--instance_identifier my-oracle-instance \
--db_endpoint localhost \
--region us-east-1 \
--database ORCL \
--service_name ORCL \
--ssl_encryption off

--ssl_encryption options​

ValueBehavior
require(default) TCPS — pass --port 2484 (the server's own port default is 1521, so it is not implied by the TLS mode). Encrypts the connection and validates the server certificate against the system CA store.
noverifyTCPS — pass --port 2484. Encrypts the connection but skips certificate and hostname verification. Use when connecting via SSH tunnel or when the RDS CA is not installed locally.
offPlain TCP on port 1521. No encryption. Use only when the transport is already secured (e.g., SSH tunnel) or in isolated test environments.

Read-Only Mode​

By default the server runs in read-only mode. It applies several layers of protection before a query reaches the database:

  1. Mutating keyword blocking — rejects queries containing DML/DDL keywords (INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE, GRANT, REVOKE, AUDIT, FLASHBACK, LOCK TABLE, BEGIN, DECLARE, etc.)
  2. SQL injection pattern detection — blocks Oracle-specific injection vectors such as EXECUTE IMMEDIATE, ALTER SYSTEM/SESSION, DBMS_* / UTL_* packages, XMLTYPE XXE, alternative quoting, SYS internal tables, v$/gv$/dba_ views, HTTPURITYPE/URITYPE SSRF, CTXSYS, and CONNECT BY tautologies.
  3. Transaction control blocking — rejects COMMIT, ROLLBACK, SAVEPOINT, and SET TRANSACTION statements in read-only mode.
  4. SET TRANSACTION READ ONLY — every query is executed inside a read-only transaction, so any mutation that slips past the keyword filter is rejected by the database itself.

Limitations of application-level read-only mode​

The keyword and pattern checks are a best-effort safeguard, not a security boundary. They cannot guarantee that no mutation ever reaches the database because:

  • A PL/SQL function that performs writes internally could be invoked via a SELECT that doesn't contain any blocked keywords (e.g. SELECT my_func_that_writes() FROM DUAL).
  • Future Oracle SQL syntax or edge-case encodings might bypass the regex-based detector.

For true read-only enforcement, use a database user that only has read permissions. See Recommended: database-level read-only user below.

Write mode​

To allow write queries, pass --allow_write_query:

awslabs.oracle-mcp-server \
--connection_method ORACLE_PASSWORD \
--instance_identifier my-oracle-instance \
--db_endpoint my-instance.xxxx.rds.amazonaws.com \
--region us-east-1 \
--database ORCL \
--service_name ORCL \
--port 2484 \
--allow_write_query

For production use, create a dedicated Oracle user with only read permissions and store its credentials in Secrets Manager. This provides a hard security boundary that cannot be bypassed at the application layer.

1. Create the read-only user in Oracle​

Connect to your RDS instance as the master user and run:

-- Create a read-only user
CREATE USER mcp_readonly IDENTIFIED BY "UseAStrongPassword"; -- pragma: allowlist secret

-- Allow the user to connect
GRANT CREATE SESSION TO mcp_readonly;

-- Grant read access to specific schemas (repeat for each schema)
GRANT SELECT ANY TABLE TO mcp_readonly;

-- (Optional) Restrict to specific tables instead of SELECT ANY TABLE
-- GRANT SELECT ON hr.employees TO mcp_readonly;
-- GRANT SELECT ON hr.departments TO mcp_readonly;

-- (Optional) Allow the user to view table definitions in ALL_TAB_COLUMNS
-- This is granted implicitly when SELECT access exists on the tables.

This user can run SELECT queries and read ALL_TAB_COLUMNS but cannot INSERT, UPDATE, DELETE, CREATE, DROP, or execute PL/SQL procedures.

2. Store the credentials in Secrets Manager​

aws secretsmanager create-secret \
--name mcp/oracle/readonly \
--description "Read-only Oracle credentials for MCP server" \
--secret-string '{"username":"mcp_readonly","password":"UseAStrongPassword"}' # pragma: allowlist secret

Note the ARN in the output.

3. Start the MCP server with the custom secret​

awslabs.oracle-mcp-server \
--connection_method ORACLE_PASSWORD \
--instance_identifier my-oracle-instance \
--db_endpoint my-instance.xxxx.rds.amazonaws.com \
--region us-east-1 \
--database ORCL \
--service_name ORCL \
--port 2484 \
--secret_arn arn:aws:secretsmanager:us-east-1:123456789012:secret:mcp/oracle/readonly-AbCdEf

With this setup you get defense in depth: the application-level keyword blocker catches mistakes early and provides clear error messages, while the database-level permissions prevent any mutation even if a query slips past the detector.

Notes​

  • RDS for Oracle and Oracle Database@AWS do not support the RDS Data API; only direct connections are supported.
  • Uses python-oracledb thin mode — no Oracle Instant Client installation required.
  • Either --service_name or --sid must be provided (not both).
  • Oracle system catalog stores table names in UPPERCASE. Table names passed to get_table_schema are automatically uppercased.
  • Default port: 1521 (TCP). Oracle TCPS (TLS) typically uses port 2484 — pass --port 2484 when connecting with --ssl_encryption require or noverify.
  • Connection pools expire after 30 minutes and are automatically refreshed