# Oraclemcp

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

- **Type:** MCP server
- **Install:** `agentstack add mcp-muhdur-oraclemcp`
- **Verified:** Pending review
- **Seller:** [MuhDur](https://agentstack.voostack.com/s/muhdur)
- **Installs:** 0
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [MuhDur](https://github.com/MuhDur)
- **Source:** https://github.com/MuhDur/oraclemcp

## Install

```sh
agentstack add mcp-muhdur-oraclemcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

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

`oraclemcp` is a [Model Context Protocol](https://modelcontextprotocol.io) 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](https://github.com/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.

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

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

```sh
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:

```sh
oraclemcp --json setup --profile db_ro
```

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

```sh
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:

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

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

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

Or run it directly:

```sh
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:

```sh
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`:

```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.

- **Author:** [MuhDur](https://github.com/MuhDur)
- **Source:** [MuhDur/oraclemcp](https://github.com/MuhDur/oraclemcp)
- **License:** Apache-2.0

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

## Pricing

- **Free** — Free

## Versions

- **0.1.0** — security scan: pending review — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-muhdur-oraclemcp
- Seller: https://agentstack.voostack.com/s/muhdur
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
