oracle-mcp-server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@oracle-mcp-servershow me the columns of the employees table"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
oracle-mcp-server
Model Context Protocol server that gives AI tools (Claude, Copilot, Cursor and other MCP clients) access to Oracle Database — the Oracle sibling of tommi2day/pg-mcp-server.
Features
14 tools: schema browsing (
list_schemas,list_tables,describe_table), read-onlyquery,executefor DML/DDL/PL/SQL withDBMS_OUTPUT, and performance analysis (execution plans, top SQL, sessions, optimizer statistics; ASH/AWR and SQL Monitor only when the packs are licensed) — detailsSafe reads:
queryruns inSET TRANSACTION READ ONLYand is always rolled back, so Oracle itself rejects writes; onlySELECT/WITHare accepted (DDL would commit implicitly); results capped at 200 rows (ORA_MAX_ROWS) to keep the LLM context smallEvery Oracle connection style: host/port/service or SID, TNS alias from
tnsnames.ora, Easy Connect Plus, full descriptors and JDBC URLs; plain TCP or TCPS with wallets, private CAs and server DN pinning; node-oracledb thin mode (no client install) or thick mode (Instant Client image variant)Multi-user: bearer tokens (admin and client), one database connection per token, managed at runtime in a web admin UI (screenshots), REST API or
admincli.sh; DB and wallet passwords encrypted at restTraceable in the database: each session carries
MODULE,ACTION= tool andCLIENT_IDENTIFIER= token name (Session identification); plus a structured audit log of tool calls, sessions, rejected logins and admin actions (example)Transport & operations: stdio or Streamable HTTP/HTTPS (optional mTLS), Docker image, docker compose with an Oracle Free test database, Helm chart (existing Secrets,
TNS_ADMINfrom Secrets/ConfigMaps, ingress, HPA, non-root); only two runtime dependencies (MCP SDK,oracledb)
When to choose this server
Choose it when several people or AI clients share Oracle access through one central service — with
revocable tokens instead of database passwords in every client config, per-token database users, TCPS wallets
kept on the server, and an audit trail in both the log and V$SESSION.
Local Oracle MCP servers (e.g. SQLcl | oracle-mcp-server | |
Deployment | Process per user on the workstation (stdio) | Central service (Docker / Kubernetes) plus stdio |
Client authentication | Whoever can start it | Bearer tokens, revocable at runtime |
Credentials & wallets | On every client machine | Stay on the server; clients only get a token |
Databases per instance | Saved connections of that user | One connection per token, managed centrally |
Audit | Log table | Server log (token, tool, client IP, rejected logins) and |
When to choose something else: for a single developer working locally with SQLcl anyway, Oracle's SQLcl MCP Server needs no extra service. On Autonomous AI Database, the built-in Autonomous AI Database MCP Server exposes Select AI Agent tools with OAuth. For several database engines behind one server, look at DBHub. oracle-mcp-server focuses on secure, audited, multi-user SQL access to any Oracle Database (on-premises, cloud, container) — self-hosted, with no Oracle client install needed.
Related MCP server: Universal Database MCP Server
Contents
Tools
Tool | Description |
| Read-only |
| DML, DDL, |
| Tables, views and materialized views of a schema (default: current schema). |
| Columns, Oracle types ( |
| Schemas visible to the user (Oracle-maintained schemas hidden unless |
| Database/PDB name, service, user, server version, driver mode and whether the session uses TCPS. |
Identifiers follow Oracle rules: unquoted names are upper-cased (emp → EMP), "MixedCase" is used verbatim.
A trailing ; on SQL statements and a trailing SQL*Plus / on PL/SQL blocks are stripped automatically.
The
querytool rejects anything that is notSELECT/WITHbecause DDL would implicitly commit and end the read-only transaction. For hard guarantees, connect with a read-only database user.
Performance analysis
Tool | Description | Licence |
| Optimizer plan of a SELECT/WITH/INSERT/UPDATE/DELETE/MERGE ( | – |
| Actual plan of a cached cursor by | – |
| Top statements from | – |
| Active sessions ( | – |
| Table, index and column optimizer statistics incl. staleness, clustering factor and histograms. | – |
| Active Session History of the last N minutes grouped by sql_id, event, wait class, session or module. | Diagnostics Pack |
| DB time, DB CPU and top wait events between the first and last AWR snapshot of the last N hours. | Diagnostics Pack |
| Recent monitored executions ( | Tuning Pack |
Licensed features are disabled by default. Tools that need an Oracle management pack are neither listed to the client nor executable unless enabled:
Switch | Default | Scope |
|
|
|
|
| enables |
|
| enables |
Tokens can override the pack switches for their connection (diagnostics_pack / tuning_pack, admin UI or
admincli.sh add-token … --diagnostics-pack true), so one server can serve licensed and unlicensed databases.
If the database itself has CONTROL_MANAGEMENT_PACK_ACCESS set to NONE, the tools say so when they find no data.
The switches only control this server's tools. The generic
querytool can still read any view the database user is allowed to see — revoke access toV$ACTIVE_SESSION_HISTORY,DBA_HIST_*andV$SQL_MONITORon the database side if pack usage must be prevented technically.
Privileges: the unlicensed tools except explain_plan read dynamic performance views. Run
scripts/sql/grant_perf_privileges.sql <user> [MINIMAL|CATALOG] [diagnostics Y|N] [tuning Y|N] as SYS — in
MINIMAL mode it creates separate roles for the unlicensed tools and each pack, so licensed views are only
reachable where the pack is granted. Errors caused by missing privileges include a hint.
Full documentation of the tools, privileges and the grant script: docs/performance.md. In a PDB, awr_top_events uses the PDB's own snapshots (AWR_PDB_AUTOFLUSH_ENABLED=TRUE) and
otherwise falls back to CDB root snapshots where their statistics are visible.
Internal statements of the performance tools run with ACTION=mcp-perf:<tool> and are excluded from top_sql
(see Session identification).
Quick start
docker compose (with a local Oracle Free test database)
cp .env.example .env # set AUTH_TOKEN (openssl rand -hex 32)
docker compose up -d # Oracle Free 23ai + MCP server on http://localhost:3000
./scripts/test_token.sh "$AUTH_TOKEN"examples/tns_admin/ is mounted as TNS_ADMIN; its tnsnames.ora defines the alias FREE for the test database.
docker run
docker run -d --name oracle-mcp-server -p 3000:3000 \
-e AUTH_TOKEN=$(openssl rand -hex 32) \
-e ORA_CONNECT_STRING='jdbc:oracle:thin:@//db.example.com:1521/ORCLPDB1' \
-e ORA_USER=app -e ORA_PASSWORD=secret \
-v "$PWD/tns_admin:/opt/oracle/network/admin:ro" \
-v oracle-mcp-data:/data \
tommi2day/oracle-mcp-serveror ./scripts/run.sh (reads .env, generates ./auth_token, mounts ./tns_admin if present).
Local (stdio, Claude Desktop)
npm ci && npm run build
ORA_HOST=localhost ORA_SERVICE_NAME=FREEPDB1 ORA_USER=mcp ORA_PASSWORD=mcp_pw node dist/index.jsConnection methods
The default connection is built from ORA_* environment variables; each token can override it
(see per-token connections). The target is chosen by precedence:
# | Method | Env var(s) | Token field(s) |
1 | Free connect string / JDBC URL |
|
|
2 | TNS alias |
|
|
3 | Host / port / service |
|
|
Credentials: ORA_USER / ORA_PASSWORD (or ORA_PASSWORD_FILE).
Accepted connect strings
Input | Used as |
| Easy Connect |
| Easy Connect Plus (parameters passed through) |
| Connect descriptor |
| TNS alias |
| → Easy Connect |
| legacy SID syntax → descriptor with |
| → Easy Connect Plus (TCPS) |
| → descriptor |
| → alias, |
| credentials extracted (for tokens they are moved into the encrypted password field) |
JDBC-only driver properties (oracle.net.*, oracle.jdbc.*) are ignored with a warning.
TCPS / TLS
TCPS works with every connection method: ORA_PROTOCOL=tcps, a tcps:// connect string or
(PROTOCOL=TCPS) in a descriptor/alias.
Situation | What to provide |
Server certificate from a public CA | nothing — the system CA store is used |
Server certificate from a private CA | CA certificate(s) as |
Mutual TLS / Autonomous Database wallet |
|
Thick mode |
|
How ewallet.pem is used (thin mode):
contains a private key → passed to the driver as wallet (client certificate + trusted CAs)
contains certificates only → added to the process-wide set of trusted CAs (node-oracledb thin cannot load a certificate-only file as wallet)
Server identity checks: host name / SAN matching is on by default (ORA_SSL_SERVER_DN_MATCH=false or
token ssl_server_dn_match: false to disable), ORA_SSL_SERVER_CERT_DN / (SECURITY=(SSL_SERVER_CERT_DN=…))
pins the certificate subject.
Converting wallets for thin mode:
# PKCS#12 wallet (ewallet.p12) → PEM (keeps key and certificates)
openssl pkcs12 -in ewallet.p12 -out ewallet.pem -nodes # unencrypted
openssl pkcs12 -in ewallet.p12 -out ewallet.pem -passout pass:xyz # encrypted → ORA_WALLET_PASSWORD=xyz
# Trust-only: just the CA certificate
cp root-ca.crt tns_admin/ewallet.pemOracle client files (TNS_ADMIN)
In the image TNS_ADMIN=/opt/oracle/network/admin (declared as VOLUME). Mount your files read-only:
tns_admin/
├── tnsnames.ora # aliases (thin + thick)
├── sqlnet.ora # thick mode only (thin mode ignores sqlnet.ora)
├── ewallet.pem # thin mode wallet or trusted CA certificates
└── cwallet.sso # thick mode auto-login walletDocker:
-v ./tns_admin:/opt/oracle/network/admin:ro(files must be readable by uid 1000)Kubernetes: Secret and/or ConfigMap, combined into one directory by the Helm chart:
kubectl -n mcp create secret generic oracle-tns-admin \ --from-file=tnsnames.ora --from-file=sqlnet.ora --from-file=ewallet.pem helm install oracle-mcp ./helm/oracle-mcp-server -n mcp --set tnsAdmin.existingSecret=oracle-tns-admin …
Different tokens can use different client configurations: mount additional directories (Helm
tnsAdmin.extraSecrets) and reference them with the token fields tns_admin / wallet_location
(thin mode). In thick mode the Oracle Client reads only the global TNS_ADMIN.
Thin vs. thick mode
thin (default) | thick ( | |
Image |
|
|
Oracle Client | none | Instant Client (basic lite) |
| ✅ | ✅ |
| ❌ (use | ✅ |
Wallet |
|
|
Per-token | ✅ | ❌ (global |
Native network encryption, DB < 12.1 | ❌ | ✅ |
Outside the image set ORA_CLIENT_LIB_DIR to the Instant Client directory for thick mode.
Configuration reference
Server
Variable | Default | Description |
|
|
|
|
| HTTP(S) port |
| – | Admin bearer token; empty disables auth (dev only) |
|
| Token store |
| – | Encrypts |
|
| Name shown in MCP clients and the admin UI |
|
|
|
|
| HTTPS for the MCP endpoint |
|
| Server certificate (self-signed one is generated if missing) |
| – | CA for client certificates (enables mTLS on the MCP endpoint) |
| – | Extra SANs for the generated self-signed certificate |
Oracle
Variable | Default | Description |
| – | Free connect string / JDBC URL |
| – | Alias from |
|
| Database host |
|
| Listener port |
| – | Service name |
| – | SID (instead of service name) |
|
|
|
| – | Credentials |
| – | Read the password from a file (Docker/K8s secrets) |
| image: | Directory with |
|
| Wallet directory |
| – | Password of an encrypted |
| driver default ( | Verify server host name / DN |
| – | Expected server certificate DN |
| – | Additional trusted CA certificates (PEM bundle) |
|
|
|
| image: | Instant Client directory |
|
| Max connections per pool |
|
| Connect timeout in seconds |
|
| Row limit of the |
|
| Performance analysis tools ( |
|
| Diagnostics Pack licensed → |
|
| Tuning Pack licensed → |
Session identification
Every database session used by the server identifies the MCP server and the token, so DBAs can attribute
activity in V$SESSION, ASH, AWR, SQL Monitor and the audit trail:
| Value | Example |
|
|
|
|
|
|
| tool name; |
|
| token name ( |
|
| server name, version and MCP client IP |
|
The attributes are set on every checkout from the pool (tokens with the same effective connection share sessions) and travel with the next database call — no extra round trip. Values are cut to Oracle's limits (MODULE 48, ACTION 32, CLIENT_IDENTIFIER / CLIENT_INFO 64 bytes). An idle pooled session keeps the values of its last use.
SELECT sid, program, module, action, client_identifier, client_info
FROM v$session WHERE module = 'Prod DB (EU)';
-- ASH (Diagnostics Pack): DB time per token
SELECT client_id, COUNT(*) FROM v$active_session_history
WHERE module = 'Prod DB (EU)' GROUP BY client_id;CLIENT_IDENTIFIER also appears in unified auditing (UNIFIED_AUDIT_TRAIL.CLIENT_IDENTIFIER) and can drive
DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE to trace everything a single token does.
Tokens & per-token connections
Tokens are managed in the admin UI (/admin), via the REST API (/admin/tokens, see openapi.json)
or with scripts/admincli.sh. The plaintext token is shown once; only its SHA-256 hash is stored.
export AUTH_TOKEN=<admin token> MCP_URL=http://localhost:3000
./scripts/admincli.sh add-token reporting --host db.example.com --port 2484 --protocol tcps \
--service REPORTS --user report_ro --password secret
./scripts/admincli.sh add-token prod --tns-alias PROD_HIGH --user app --password secret
./scripts/admincli.sh add-token legacy --connect-string 'jdbc:oracle:thin:@dbhost:1521:ORCL' \
--user scott --password tiger
./scripts/admincli.sh set-conn 2 '{"tns_alias":"PROD","tns_admin":"/opt/oracle/wallets/prod","user":"app"}'Connection object fields: connect_string, tns_alias, host, port, service_name, sid, protocol,
user, password, tns_admin, wallet_location, wallet_password, ssl_server_dn_match, ssl_server_cert_dn,
diagnostics_pack, tuning_pack.
Merge rules with the default connection:
a token that sets a target (
connect_string,tns_aliasorhost) never inherits target fields of the defaultuser/passwordfall back to the default only if the token sets nousertns_admin, wallet and TLS fields fall back individually
Secrets are write-only: the API returns password_set: true instead of the password, and a PATCH without
password keeps the stored one. Tokens with the same effective connection share one connection pool.
Admin UI
Open http://<server>:3000/admin, sign in with the server URL and the AUTH_TOKEN value (leave it empty if auth
is disabled). The session is kept in sessionStorage and cleared when the browser tab is closed.
Token list — server info with the default connection and enabled performance features, then every token with status, connection (server default, host / service or TNS alias) and last use:

Editing a token changes its name, active state or connection; passwords are never shown and stay unchanged when the field is left empty. Deleting a token revokes access immediately.
Logging
All activity goes to stderr (docker logs oracle-mcp-server) in the format [timestamp] [LEVEL] [CATEGORY] …:
tool calls, MCP session start/stop, rejected logins and admin actions. LOG_LEVEL (debug / info / warn /
error, default info) sets the minimum level.
Real output at the default level info — three clients working in parallel (one of them over the TNS alias FREE
with the performance tools), an UPDATE rejected by the read-only database user of reporting-team, and four
rejected logins (disabled token, unknown token, wrong admin token, missing token). SQL text is replaced by its length:

With LOG_LEVEL=debug the full SQL text is logged, plus internals such as pool creation:

Line format, all categories and keys, and ready-to-use Filebeat / Logstash configurations: docs/logging.md.
Kubernetes / Helm
kubectl create namespace mcp
kubectl -n mcp create secret generic oracle-tns-admin --from-file=tnsnames.ora --from-file=ewallet.pem
kubectl -n mcp create secret generic oracle-credentials --from-literal=password='secret'
helm install oracle-mcp ./helm/oracle-mcp-server -n mcp \
--set oracle.tnsAlias=PROD_HIGH --set oracle.user=app \
--set oracle.existingSecret=oracle-credentials \
--set tnsAdmin.existingSecret=oracle-tns-admin \
--set auth.token=$(openssl rand -hex 32) \
--set persistence.enabled=trueKey values (see helm/oracle-mcp-server/values.yaml):
oracle.connectString/oracle.tnsAlias/oracle.host|port|serviceName|sid|protocol,oracle.useroracle.passwordororacle.existingSecret(keyspassword, optionalwalletPassword)oracle.driverMode(thickrequires the-thickimage),oracle.sslServerDnMatch,oracle.sslServerCertDn,oracle.tlsCaFileoracle.perfTools(defaulttrue),oracle.diagnosticsPack/oracle.tuningPack(defaultfalse, enable only when licensed)tnsAdmin.existingSecret,tnsAdmin.existingConfigMap,tnsAdmin.files(inline) → projected read-only volume attnsAdmin.mountPath(mode0440, readable throughfsGroup: 1000)tnsAdmin.extraSecrets→ additional wallet/tnsnames directories for per-token connectionsauth.token/auth.existingSecret,auth.storeEncryptionKey,server.tlsEnabled,tls.*,persistence.*
scripts/helm_install.sh wraps a typical install.
Claude configuration
Remote (HTTP):
{
"mcpServers": {
"oracle": {
"type": "http",
"url": "https://oracle-mcp.example.com/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}Local (stdio): see example.mcp.json.
Development
npm ci
npm run build # tsc → dist/
npm test # vitest (unit tests, no database needed)
npm run lint
npm run typecheck
./scripts/test.sh # tests in Docker, no local Node.js needed
docker build -t oracle-mcp-server . # thin image
docker build --build-arg ORACLE_THICK=true -t oracle-mcp-server:thick . # with Instant ClientSource layout: src/oracle.ts (connect strings, JDBC parsing, pool attributes, wallets — pure),
src/lib.ts (logging, auth, token store, admin API), src/perf.ts (performance tools and licence gating),
src/format.ts (result formatting), src/index.ts (MCP tools, HTTP routing, startup).
Log format: see docs/logging.md. Performance tools: docs/performance.md.
License
MIT © Tommi2Day
This server cannot be deployed
Maintenance
Related MCP Connectors
Paid remote MCP for governed database query review, SQL simulation, approvals, and audits.
Draxlr's remote MCP server connects AI assistants to your SQL databases and dashboards. Explore schemas, run read-only queries, manage saved queries and dashboards, and export results, all with row-level security so each user sees only their own data.
DBRE-grade SQL analysis inside any MCP client. No connection. No install. Paste a query.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables interaction with Oracle databases through MCP by executing SELECT queries, describing table structures, and listing available tables with secure, read-only access.38 npm2MIT
- FlicenseNot gradedqualityDmaintenanceA versatile MCP server that connects to multiple relational databases (MySQL, PostgreSQL, Oracle, SQL Server, SQLite) and enables secure read-only SQL query execution and metadata access.4-
- AlicenseBqualityCmaintenanceMCP server for accessing Oracle databases, enabling schema exploration, query execution, and performance analysis.12MIT
- AlicenseAqualityCmaintenanceMCP server for Oracle Database enabling AI assistants to explore schemas, run queries, write data, and monitor sessions via natural language. Features read-only mode by default and uses Oracle Thin mode for zero-install connectivity.1077 npm2MIT