AgentStack
MCP unreviewed Apache-2.0 Self-run

Oraclemcp

mcp-muhdur-oraclemcp · by MuhDur

Engine-free, read-only Oracle Database MCP server with a fail-closed SQL guard (unofficial).

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add mcp-muhdur-oraclemcp

Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.

Are you the author of Oraclemcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.

About

> Governed, least-privilege Oracle Database access for AI agents — in pure Rust.

oraclemcp is a Model Context Protocol server that gives an AI agent governed, least-privilege access to an Oracle database: schema introspection, DDL, compile errors, source search, ad-hoc read queries, plan analysis, and an explicit profile-gated execution path for non-read SQL. Every raw statement the agent submits is classified before it can reach Oracle. Read tools only admit statements proven read-only; oracle_execute only runs statements permitted by the active profile/session level, rolls DML back by default, and requires a preview-derived confirmation token before commit. Session elevation is explicit, temporary, and capped by profile max_level. The core is engine-free and #![forbid(unsafe_code)].

> An independent open-source project; not affiliated with Oracle. For Oracle's own MCP servers, see oracle/mcp._

Why oraclemcp

  • Fail-closed by construction. A SELECT that an agent dreams up should never silently turn into a DELETE. Each raw statement runs through the hardened classifier. Read tools admit only proven read-only SELECT/WITH and dictionary introspection. Non-read execution is isolated in oracle_execute, bounded by profile max_level/default_level, rollback-by-default for DML, and explicit-confirm-before-commit. Temporary elevation through oracle_set_session_level can never exceed the profile ceiling. Forbidden constructs (multi-statement batches, string-concat dynamic SQL, an unproven function call inside a SELECT) are rejected before touching the database, with an OperatingLevelTooLow or ForbiddenStatement envelope and a suggested safe alternative.
  • Agent-first UX. Every tool ships a real JSON Schema, title, and explicit MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) so clients do not infer unsafe defaults. Errors are structured [ErrorEnvelope](crates/oraclemcp-error)s with machine-stable classes, fuzzy suggestions, and next-step hints, not bare strings. A zero-arg oracle_capabilities tool lets an agent discover the surface; MCP resources expose the capability/tool documents plus schema/object read templates; and an offline build degrades to a RuntimeStateRequired contract instead of crashing.
  • Pure Rust, no unsafe. Every crate is #![forbid(unsafe_code)]; the fail-closed classifier carries a differential cargo-fuzz target.
  • Two transports. stdio (default) and Streamable HTTP (--listen) with

fail-closed auth defaults, optional OAuth bearer enforcement, and native rustls TLS/mTLS.

Quick start

This branch is pinned to nightly-2026-05-11. The thin-native line has no stable MSRV because asupersync 0.3.4 uses nightly-only language features (#![feature(try_trait_v2)] and try_trait_v2_residual); the pinned oracledb 0.5.0 driver itself is stable-clean. The repository's rust-toolchain.toml selects the pin for local builds; direct cargo install users should use the same toolchain.

rustup toolchain install nightly-2026-05-11 --component rustfmt --component clippy

Live database access is built in through the pure-Rust thin oracledb driver:

cargo +nightly-2026-05-11 install oraclemcp

Runtime requirements for live database access:

  • Optionally TNS_ADMIN pointing at a directory with tnsnames.ora if you connect by net-service name.

No Oracle Instant Client, ODPI-C library, or C toolchain is required by the driver.

Use oraclemcp --json doctor to verify the binary and offline setup, and oraclemcp --json doctor --profile to add live connectivity, authentication, role/open-mode, standby, and privilege checks. Doctor output is safe to paste into agent sessions: it omits connect strings, usernames, credential_ref values, passwords, proxy identities, wallet passwords, IAM tokens, wallet paths, and server DNs while keeping structured failure classes and ORA codes visible.

Generate generic local setup templates for profiles, wrappers, and MCP client snippets:

oraclemcp --json setup --profile db_ro

Docker: a ready-to-run thin-driver image, published to GHCR and listed in the MCP registry on release as io.github.MuhDur/oraclemcp. Mount a profiles config and pass the credential the profile's credential_ref expects:

docker run -i --rm \
  -v "$HOME/.config/oraclemcp:/root/.config/oraclemcp:ro" \
  -e ORACLE_APP_PASSWORD \
  ghcr.io/muhdur/oraclemcp:0.4.0          # MCP over stdio, against the configured profile

docker run -i --rm ghcr.io/muhdur/oraclemcp:0.4.0   # tool surface only (no DB)

> The Docker image and crates are Apache-2.0 OR MIT and do not redistribute Oracle Instant Client.

Wire it into an MCP client (e.g. Claude Desktop) over stdio:

{
  "mcpServers": {
    "oracle": {
      "command": "oraclemcp",
      "args": ["serve", "--allow-no-auth"]
    }
  }
}

For Codex-style TOML config, the same command is:

[mcp_servers.oracle]
command = "oraclemcp"
args = ["serve", "--allow-no-auth"]

Or run it directly:

oraclemcp serve                      # stdio (default); --allow-no-auth for local dev
oraclemcp serve --listen 127.0.0.1:7070 --allow-no-auth   # local HTTP dev only
oraclemcp --json setup --profile db_ro    # generic onboarding templates
oraclemcp capabilities               # the advertised tool surface + feature tiers (JSON)
oraclemcp --json profiles            # configured profile names and non-secret metadata
oraclemcp doctor                     # offline diagnostics (thin driver, TNS/wallet, classifier, NLS)
oraclemcp doctor --profile dev_ro    # include live connectivity/auth/role/privilege checks
oraclemcp info                       # build info: version, tools, transports, thin DB
oraclemcp robot-docs guide           # compact in-binary guide for agents

--json is a visible alias for --robot-json and keeps stdout as a single machine-readable JSON object.

The Streamable HTTP transport (--listen) fails closed. It starts only when OAuth bearer enforcement is configured or --allow-no-auth is supplied, and it refuses any non-loopback bind unless ORACLEMCP_HTTP_ALLOW_REMOTE=1 is set. OAuth configuration can come from profiles.toml or CLI flags:

export ORACLEMCP_OAUTH_HS256_SECRET='replace-with-a-long-random-secret'
oraclemcp serve --listen 127.0.0.1:7070 \
  --oauth-resource http://127.0.0.1:7070/mcp \
  --oauth-issuer https://issuer.example.com \
  --oauth-authorization-server https://issuer.example.com \
  --oauth-required-scope oracle:read \
  --oauth-hs256-secret-ref env:ORACLEMCP_OAUTH_HS256_SECRET \
  --http-allowed-host 127.0.0.1:7070 \
  --http-allowed-origin https://client.example.com

When OAuth is enabled, /.well-known/oauth-protected-resource stays public, /mcp requires a valid bearer token, and granted oracle:* scopes lower the request's effective operating ceiling monotonically. oracle:read caps the request at READ_ONLY, oracle:write/oracle:execute at READ_WRITE, oracle:ddl at DDL, and oracle:admin at ADMIN; none of them can raise a profile above its max_level, and protected profiles remain READ_ONLY.

Native TLS uses rustls when [http.tls] or --tls-cert / --tls-key are configured. Adding [http.tls.client_ca_path] or --mtls-client-ca requires client certificates (mTLS) verified against that CA. Server-only TLS encrypts the transport but is not application authentication, so /mcp still needs OAuth or an explicit --allow-no-auth development opt-in. Non-loopback binds require ORACLEMCP_HTTP_ALLOW_REMOTE=1 even with TLS.

Connection profiles are resolved from layered configuration (oraclemcp-config); select one with serve --profile .

Connection profiles

> See also: [oraclemcp.example.toml](oraclemcp.example.toml) is a fully > annotated, copy-pasteable config showing every field with its default; > [docs/configuration.md](docs/configuration.md) is the canonical field > reference (types, defaults, precedence, the operating-level ladder, the > mcp_exposed opt-out, auth modes, and base inheritance).

For live database access, create ~/.config/oraclemcp/profiles.toml:

schema_version = 1
default_profile = "dev_ro"

[http]
allowed_hosts = ["127.0.0.1:7070"]
allowed_origins = ["https://client.example.com"]
json_response = true
stateful = false

[http.oauth]
resource = "http://127.0.0.1:7070/mcp"
allowed_issuers = ["https://issuer.example.com"]
authorization_servers = ["https://issuer.example.com"]
required_scopes = ["oracle:read"]
hs256_secret_ref = "env:ORACLEMCP_OAUTH_HS256_SECRET"

# Optional native HTTPS / mTLS listener.
# [http.tls]
# cert_chain_path = "/path/to/server-chain.pem"
# private_key_path = "/path/to/server-key.pem"
# client_ca_path = "/path/to/client-ca.pem"  # require mTLS client certs

[[profiles]]
name = "dev_ro"
description = "Read-only development database"
connect_string = "localhost:1521/FREEPDB1"
username = "APP_READONLY"
credential_ref = "env:ORACLE_APP_PASSWORD"
max_level = "READ_ONLY"
default_level = "READ_ONLY"
require_signed_tools = true
# Optional Oracle per-round-trip timeout. Tool calls can override it with
# timeout_seconds where advertised.
call_timeout_seconds = 30
# Optional thin Session Data Unit request. Validated as 512..=65535 bytes.
sdu = 32768
login_statements = [
  "ALTER SESSION SET NLS_LANGUAGE = english",
  "ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:ALL'",
]
# Optional trusted local setup, authored by the profile owner and never by the
# agent. Use for session-local initialization that is not an ALTER SESSION.
trusted_session_statements = [
  "BEGIN DBMS_OUTPUT.ENABLE(500000); END;",
]

[profiles.oci]
# Optional TCPS/wallet fields. Prefer these named fields over raw
# connect_string query parameters when the value should be validated or redacted.
wallet_location = "/etc/oracle/wallet"
wallet_password_ref = "env:WALLET_PASSWORD"
ssl_server_dn_match = true
ssl_server_cert_dn = "CN=dbhost.example.com"
use_sni = true

# Optional proxy authentication. If enabled, `credential_ref` belongs to
# `proxy_user`; omit top-level `username` or set it to the same value.
# The database needs: ALTER USER  GRANT CONNECT THROUGH 
# [profiles.proxy_auth]
# proxy_user = "MCP_PROXY"
# target_schema = "APP_OWNER"

# Optional DRCP server routing. Prefer these named fields over raw
# connect_string query parameters so inheritance, validation, and redaction stay
# predictable. This is separate from [profiles.pool], which controls local
# client-side reuse.
[profiles.drcp]
pooled = true
connection_class = "ORACLE_MCP_AGENTS"
purity = "reuse"

# Optional local client-side pool for stateless metadata/catalog reads.
# User SQL, LOB/sample reads, DBMS_OUTPUT, transactions, and session state stay
# on the pinned main session.
# [profiles.pool]
# max_size = 4
# min_idle = 1
# acquire_timeout_secs = 5
# statement_cache_size = 50

# Optional driver-level application context, applied during thin logon. Values
# can carry tenant/session identifiers, so list_profiles and diagnostics redact
# them. If inherited, setting entries here replaces the base list; omit to
# inherit or set app_context = [] in the profile table to clear it.
[[profiles.app_context]]
namespace = "ORACLEMCP_CTX"
key = "tenant_id"
value = "tenant-123"

[[profiles.app_context]]
namespace = "ORACLEMCP_CTX"
key = "request_id"
value = "req-456"

[profiles.session_identity]
# Optional: all values are profile-local and are not shown by list_profiles.
# oracle_connection_info reports the session-visible fields for verification.
# Edition selection is applied during thin authentication before user SQL.
# edition = "ORA$BASE"
program = "oraclemcp"
machine = "local-workstation"
os_user = "local-operator"
terminal = "agent"
driver_name = "oraclemcp"
module = "oraclemcp"
action = "inspect"
client_identifier = "agent"
client_info = "local-workstation"

max_level is the profile ceiling; default_level is the starting session level and must not exceed that ceiling. call_timeout_seconds is an optional Oracle per-round-trip timeout for the physical connection; read/write/compile tools that expose timeout_seconds can override it for one call. Both settings bound individual Oracle round trips, not the total wall-clock time of a multi-round-trip operation. login_statements and login_script are for profile-local session policy only and are restricted to allowlisted ALTER SESSION SET ... parameters. trusted_session_statements are an explicit profile-owner escape hatch for local session initialization such as DBMS_APPLICATION_INFO, application contexts, or DBMS_OUTPUT; they are never accepted from agent tool calls, and they keep environment-specific conventions in private config rather than in the open-source core. The oracle_connection_info tool also reports diagnostic fields such as os_user, program, machine, terminal, and client_driver when the database exposes them. The Rust thin backend can set the connect-time client identity fields (program, machine, os_user, terminal, and driver_name) from profile config. It also applies module, action, client_identifier, and client_info after connect through Oracle session APIs, so operators can keep driver identity and DBMS session attributes separate. require_signed_tools = true requires HMAC signatures for operator-defined custom tools on that profile; protected = true implies the same policy.

A few further profile keys are optional:

  • base = "other_profile": inherit from another profile and override only the

keys you set. Inheritance is resolved before validation, so a child still honors the effective max_level ceiling.

  • [profiles.pool]: local client-side connection reuse settings

(max_size, min_idle, acquire_timeout_secs, statement_cache_size). This enables the hybrid runtime strategy: catalog and metadata tools such as schema/object/source inspection use a bounded stateless read pool, while agent queries, sampled rows, LOB reads, DDL/write previews, transactions, savepoints, temp tables, package globals, login setup, session identity, and DBMS_OUTPUT stay on the pinned main session. statement_cache_size is passed to the thin driver's bounded per-connection statement cache; omit it to keep the driver default. This is separate from DRCP server routing.

  • [profiles.oci]: OCI-specific connection settings for the underlying driver.

For TCPS/wallet connections, named fields are available for wallet_location, wallet_password_ref, ssl_server_dn_match, ssl_server_cert_dn, and use_sni. Use the named fields for values that should inherit through profiles, be redacted from diagnostics, or be validated by strict config parsing.

  • sdu = 32768: optional thin driver Session Data Unit request size. Values are

validated as 512..=65535; omit it to keep the driver's negotiated default.

  • [profiles.drcp]: Database Resident Connection Pooling server routing.

pooled = true appends server=pooled; connection_class maps to pool_connection_class; purity = "reuse" | "new" maps to pool_purity. Existing connect_string query parameters such as wallet_location are preserved and DRCP parameters are appended with &. Prefer these named fields over raw DRCP query parameters when the values should inherit, validate, and be covered by redaction tests.

  • [profiles.proxy_auth]: thin proxy authentication. proxy_user is the

account that authenticates with credential_ref; target_schema is the Oracle user granted CONNECT THROUGH. The connect username, if present, must match proxy_user.

  • [[profiles.app_context]]: driver-level application context triples sent

during thin logon. Use typed namespace / key / `

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.