Skip to main content
Glama
MuhDur

oraclemcp

by MuhDur
README.md
<p align="center">
  <img src=".github/assets/hero.svg" alt="oraclemcp: governed, least-privilege Oracle Database MCP server in pure Rust" width="100%">
</p>

<p align="center">
  <a href="https://github.com/MuhDur/oraclemcp/actions/workflows/ci.yml"><img src="https://github.com/MuhDur/oraclemcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://crates.io/crates/oraclemcp"><img src="https://img.shields.io/crates/v/oraclemcp.svg" alt="crates.io"></a>
  <a href="#license"><img src="https://img.shields.io/badge/license-Apache--2.0%20OR%20MIT-blue.svg" alt="license"></a>
  <img src="https://img.shields.io/badge/unsafe-forbidden-success.svg" alt="forbid(unsafe_code)">
  <img src="https://img.shields.io/badge/tests-~3300-success.svg" alt="~3,300 tests">
  <img src="https://img.shields.io/badge/rustc-nightly--2026--05--11-orange.svg" alt="nightly-2026-05-11">
</p>

> **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. Every raw statement the agent submits is classified **before** it can reach Oracle: read tools admit only statements *proven* read-only, and non-read SQL runs only through an explicit, profile-gated path that **rolls DML back by default** and requires a preview-derived grant before commit. Session elevation is explicit, temporary, and capped by profile `max_level`. The engine-free SQL guard remains independent of PL/SQL intelligence; the default binary embeds the offline engine. All crates use `#![forbid(unsafe_code)]`.

> _An independent open-source project — not affiliated with Oracle. See [how it compares](#how-it-compares) to Oracle's own MCP servers._

### Drivers

oraclemcp connects through its **own mature, pure-Rust Oracle driver** as the **primary** path. The official `oracledb` crate from Oracle — whose crate name we handed to Oracle in a friendly handshake — is currently in **beta**, and therefore ships purely as a bounded, connect-time **fallback** for the rare case something goes awry. No Oracle Instant Client, ODPI-C, or C toolchain is required.

```mermaid
flowchart LR
    A["Connection request"] --> D["driver-cx<br/>pure-Rust · primary<br/>password · IAM · wallet · TCPS/PEM"]
    D -->|"basic-password<br/>connect fails"| O["oracledb<br/>Oracle · beta<br/>connect-time fallback"]
    D --> DB[("Oracle Database")]
    O --> DB
```

<sub>Only a failed basic-password connect ever falls back; IAM, wallet, and TCPS/PEM always stay on driver-cx, and a fallback never migrates a live session.</sub>

## At a glance

| | |
|---|---|
| **Tools** | **43 governed MCP tools** + 25 compatibility aliases, each with a real JSON Schema and MCP safety hints |
| **Safety** | fail-closed SQL classifier · 4-level ladder `READ_ONLY → READ_WRITE → DDL → ADMIN` · DML rollback-by-default · signed, hash-chained audit |
| **Auth** | username/password over TCP · IAM / OCI ADB token · TLS/TCPS + PEM · Oracle wallet (`cwallet.sso`) |
| **Oracle** | 18c · 21c · 23ai — including governed native **VECTOR** search |
| **Code** | **9 pure-Rust crates + binary** · `#![forbid(unsafe_code)]` · **~3,300 tests** + a differential fuzzer |
| **Transports** | stdio (default) + Streamable HTTP with rustls TLS/mTLS and optional OAuth |

## Quick start

One line installs or updates on macOS and Linux (works pasted in a terminal or in a non-interactive agent run):

```sh
curl -fsSL "https://raw.githubusercontent.com/MuhDur/oraclemcp/main/install.sh?$(date +%s)" | bash
```

It verifies a SHA-256 digest (plus cosign signature/provenance when cosign is present) and installs `oraclemcp` and the short `om` alias into `$HOME/.local`. Also available: **Windows** (`install.ps1`), **Docker** (`ghcr.io/muhdur/oraclemcp:latest`), and `cargo binstall oraclemcp`. Air-gapped offline install, verification postures, and service install are documented via `bash install.sh --help` and [`docs/`](docs/). No npm/npx channel is offered.

Onboard and connect a client:

```sh
oraclemcp setup --discover           # one READ_ONLY profile per tnsnames.ora entry — consent-gated, no secrets written to disk
oraclemcp doctor                     # offline diagnostics: driver, TNS/wallet, classifier, NLS
oraclemcp serve --profile db_ro --allow-no-auth    # stdio (local dev)
```

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

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

Several local MCP clients can use this same stdio configuration concurrently,
including write-capable profiles. When service state is needed, clients
automatically attach to one local broker that owns the audit writer and
database pools. Each client retains its own session level, confirmation tokens,
transaction and cancellation. The broker exits after 60 seconds without clients.
HTTP also gives each client an isolated stateful session by default.
HTTP and stdio clients share that same broker and state root. Each local client
must resolve its own configured database credentials. Credentials that differ
from the broker's receive `ORACLEMCP_BROKER_CREDENTIAL_MISMATCH` before opening a session.

Or run authenticated HTTP with a shown-once bearer, and open the local dashboard through a secret-free one-time pairing URL:

```sh
oraclemcp --json clients issue --label claude --scope oracle:read
oraclemcp serve --listen 127.0.0.1:7070 --client-credentials --profile db_ro
om dashboard
```

`doctor` output is safe to paste into agent sessions — it omits connect strings, usernames, credential references, passwords, wallet paths, IAM tokens, and server DNs while keeping structured failure classes and ORA codes.

## Why oraclemcp

- **Fail-closed by construction.** A `SELECT` an agent dreams up can never silently become a `DELETE`. Read tools admit only **proven** read-only `SELECT`/`WITH` and dictionary introspection. Non-read execution is isolated in `oracle_execute`, bounded by the profile ceiling, rollback-by-default for DML, and explicit-confirm-before-commit. *Forbidden* constructs (multi-statement batches, string-concat dynamic SQL, an unproven function call inside a SELECT) are rejected before touching Oracle, with a typed 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`). Errors are structured [`ErrorEnvelope`](crates/oraclemcp-error)s with machine-stable classes, fuzzy suggestions, and next-step hints — never bare strings. A zero-arg `oracle_capabilities` tool lets an agent discover the surface.
- **Pure Rust, no `unsafe`.** Every crate is `#![forbid(unsafe_code)]`; the fail-closed classifier is a real `sqlparser` AST classifier and 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.

## How it compares

Oracle ships an official MCP built into SQLcl; ours is independent. On the axis that decides whether you can safely point an AI agent at a database — **the guard between the agent and your data** — here is the honest, evidence-backed contrast:

| | Oracle SQLcl MCP | **oraclemcp** |
|---|---|---|
| **Default posture** | Not read-only | **Read-only, fail-closed** |
| **Write / DDL control** | No in-server gate — relies on DB grants + a non-prod replica | **In-server: classifier + `READ_ONLY→ADMIN` ladder + preview→confirm-token + rollback-by-default** |
| **Safety boundary** | Your grant hygiene — a wrong grant or a clever prompt can write | **The server itself — enforced regardless of grants** |
| **SQL handling** | Free-form, tagged `/* LLM in use */` | Free-form, but **classified before it reaches Oracle** |
| **Audit** | DB-side log table (`DBTOOLS$MCP_LOG`) | **Signed, hash-chained HMAC audit + verdict certificates** |
| **Runtime** | Java — needs the JVM + a SQLcl install | **One pure-Rust static binary — no JVM, C toolchain, or Instant Client** |

<sub>SQLcl-MCP rows reflect [Oracle's own documentation](https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/using-oracle-sqlcl-mcp-server.html) (configure a minimum-privilege user, prefer a non-prod replica, audit via `DBTOOLS$MCP_LOG`); the oraclemcp rows are enforced in this repository. Oracle is the official, supported option, and its managed Autonomous MCP takes a different curated-report approach with per-user identity — strong for locked-down read-only BI.</sub>

## Safety model

When a guarded request is refused, use the generated [refusal catalogue](docs/operations.md#refusal-catalogue)
for its typed safe next action; it never recommends bypassing the guard.

The core invariant is a **fail-closed SQL guard** — not "read-only forever." Operating levels form a ladder, `READ_ONLY < READ_WRITE < DDL < ADMIN`, surfaced through `oracle_execute`, `oracle_compile_object`, `oracle_create_or_replace`, `oracle_patch_source`, and `oracle_set_session_level`. Read-only is the **default** and the cap for unconfigured or `protected` profiles; a profile's `max_level` may permit escalation up to `ADMIN`. Every escalation is guarded:

- a **preview → confirmation-token** step-up before any non-read statement runs,
- a **temporary, TTL-bounded** elevation window,
- the **classifier still gating every statement** at the *current* level,
- **DML rolling back by default**, `protected` profiles pinned at `READ_ONLY` with an immutable ceiling, and OAuth scopes that can only *lower* the effective level,
- a **signed, append-only, HMAC-SHA256 hash-chained audit** record for every privileged action.

```mermaid
flowchart LR
    A["Agent SQL"] --> C{"Fail-closed<br/>classifier"}
    C -->|"proven READ_ONLY"| R["Read tools · rows returned"]
    C -->|"forbidden / unparseable"| X["Refuse<br/>typed ErrorEnvelope"]
    C -->|"non-read"| L{"Operating-level gate<br/>READ_ONLY → READ_WRITE → DDL → ADMIN"}
    L -->|"above level / ceiling"| X
    L -->|"within profile ceiling"| P["Preview → confirmation token"]
    P --> E["Execute"]
    E -->|"DML"| RB["Rollback by default<br/>commit only with grant"]
    R --> AU[("Signed hash-chained audit")]
    E --> AU
```

An unparseable or unclassifiable statement fails **closed**. Statements can emit a verdict certificate bound to the classified bytes and the audit record; the routine-purity law it relies on is specified in [`proofs/purity-core/PurityCore.lean`](proofs/purity-core/PurityCore.lean) and pinned to the Rust classifier by a conformance test.

## Governed dimensions

A database session is treated as a governed surface with several independent controls, each with an executable proof script:

| Dimension | What it governs | Proof |
|---|---|---|
| **Cost** | per-call `max_query_cost` + durable per-principal budget; over-ceiling estimates refused pre-execution | [`cost_gate.sh`](scripts/e2e/cost_gate.sh) |
| **Time** | `as_of` flashback reads, cross-SCN/cross-DB `oracle_diff`, historical plan timelines | [`time_diff.sh`](scripts/e2e/time_diff.sh) |
| **Egress** | profile-scoped result masking applied before rows leave the server, with mask certificates ([ADR 0008](docs/adr/0008-result-masking-policy.md)) | [`served_egress.sh`](scripts/e2e/served_egress.sh) |
| **Proof** | verdict certificates + async Rekor anchoring of audit heads ([ADR 0010](docs/adr/0010-verdict-certificate-schema.md)) | [`verdict_certificate.sh`](scripts/e2e/verdict_certificate.sh) |
| **Policy** | per-profile deny/narrow-only SQL policy that can tighten but never widen the base classifier ([ADR 0009](docs/adr/0009-policy-as-code-grammar.md)) | [`sql_policy.sh`](scripts/e2e/sql_policy.sh) |
| **Living DB** | CQN change notifications, `oracle_orient` freshness/drift, Arrow IPC output | [`living_db.sh`](scripts/e2e/living_db.sh) |
| **Vector search** | bounded, fail-closed 23ai `oracle_semantic_search` through the full policy/masking/audit path | [`governed_rag.sh`](scripts/e2e/governed_rag.sh) |
| **Fleet** | map or compare several MCP-visible profiles at once; unreachable targets become typed `UNREACHABLE`/`FAIL_CLOSED` lanes | [`fleet.sh`](scripts/e2e/fleet.sh) |
| **Reversible workspace** | native SAVEPOINT checkpoints, held DML, `oracle_undo_to`, undo-aware `oracle_preview_dml` | [`reversible.sh`](scripts/e2e/reversible.sh) |
| **Editions** | edition-based redefinition via an allowlist, persisted proposals, and an `ADMIN`-only merge | [`editions.sh`](scripts/e2e/editions.sh) |
| **Incident capture** | `om incident capture`/`replay` — redacted, deterministic bundles re-classified offline ([ADR 0011](docs/adr/0011-incident-artifact-manifest.md)) | [`incident.sh`](scripts/e2e/incident.sh) |
| **Diagnostics** | `oracle_top_queries` (free `V$SQLSTATS`) and a read-only `oracle_db_health` suite that degrades cleanly on least-privilege accounts ([ADR 0005](docs/adr/0005-awr-diagnostics-license-gating.md)) | version-matrix lanes |

What an agent sees depends on the active level and effective ceiling. At `READ_ONLY`, `tools/list` includes `oracle_execute` and `execute_approved` when the effective profile/OAuth ceiling permits `READ_WRITE`; their visibility grants no write authority. Other higher-level tools remain hidden until the session is elevated within the ceiling. A call to a not-yet-visible tool is refused with the same typed `ErrorEnvelope` as any other below-level statement.

## Tools

The tables below are generated from the server's tool registry — the same descriptors `tools/list` serves — by `scripts/docs_generate.sh` (rendered from `oraclemcp robot-docs tools --markdown`). Do not hand-edit them; edit the registry and run `bash scripts/docs_generate.sh --write`.

<!-- generated:tools -->
| Tool | Title | Purpose | Visible from | Destructive |
| --- | --- | --- | --- | --- |
| `oracle_list_profiles` | Oracle List Profiles | List configured connection profiles without exposing connect strings, usernames, or credential references. | `READ_ONLY` | no |
| `oracle_connection_info` | Oracle Connection Info | Describe the active profile, its effective MCP access (operating level, profile max_level, protected status, and whether writes are permitted now), and Oracle connection state. | `READ_ONLY` | no |
| `oracle_switch_profile` | Oracle Switch Profile | Reconnect this MCP server to another configured profile by name and report that profile's effective MCP access. | `READ_ONLY` | no |
| `oracle_set_session_level` | Oracle Set Session Level | Preview or apply a temporary session operating-level elevation within the active profile ceiling, or drop back to READ_ONLY. | `READ_ONLY` | yes |
| `oracle_query` | Oracle Query | Run a read-only SELECT with positional binds; paginated and row/byte capped. | `READ_ONLY` | no |
| `oracle_semantic_search` | Oracle Semantic Search | Run a bounded, fail-closed 23ai vector search through the same policy, semantic-resolution, masking, and audit path as oracle_query. | `READ_ONLY` | no |
| `oracle_diff` | Oracle Diff | Diff one proven read-only SELECT across two Oracle SCNs, or across two databases. | `READ_ONLY` | no |
| `oracle_preview_sql` | Oracle Preview SQL | Classify SQL without executing it and bind the exact SQL, typed binds, commit/hold mode, output caps and timeout into any confirmation. | `READ_ONLY` | no |
| `oracle_execute` | Oracle Execute | Execute one non-read SQL statement through the classifier and active profile gate; DML rolls back by default. | `READ_ONLY` | yes |
| `oracle_checkpoint` | Oracle Checkpoint | Establish a named checkpoint (a native Oracle SAVEPOINT) on this session, opening the reversible workspace: oracle_execute with hold=true and a matching preview confirmation leaves DML pending instead of rolling it back, and oracle_undo_to walks it back. | `READ_WRITE` | yes |
| `oracle_undo_to` | Oracle Undo To | Undo the reversible workspace: ROLLBACK TO SAVEPOINT <name> discards every held statement executed after that checkpoint and releases the checkpoints stacked above it, leaving the transaction open. | `READ_WRITE` | yes |
| `oracle_preview_dml` | Oracle Preview DML | Dry-run one DML statement: the server brackets it in its own savepoint, executes it, reads the rows it touched, then rolls back to that savepoint and presents the result — nothing is committed and nothing is left behind. | `READ_WRITE` | yes |
| `oracle_compile_object` | Oracle Compile Object | Preview or compile one PL/SQL/view object through the active DDL profile gate; preview is the default and execution requires the returned confirmation token. | `DDL` | yes |
| `oracle_create_or_replace` | Oracle Create Or Replace | Preview or apply one CREATE OR REPLACE statement through the classifier and active DDL profile gate. | `DDL` | yes |
| `oracle_patch_source` | Oracle Patch Source | Preview or apply an exact old_text to new_text replacement against one stored source object; preview refetches the current source and execute uses the existing DDL confirmation gate. | `DDL` | yes |
| `oracle_list_schemas` | Oracle List Schemas | List schemas that own objects visible to this session, optionally filtered by name. | `READ_ONLY` | no |
| `oracle_schema_inspect` | Oracle Schema Inspect | List objects in the current schema, one owner, or all accessible schemas, with optional type/name filters. | `READ_ONLY` | no |
| `oracle_search_objects` | Oracle Search Objects | Unified read-only object search/inspection with a detail_level. | `READ_ONLY` | no |
| `oracle_orient` | Oracle Orient | Return bounded orientation evidence: by default one cacheable snapshot for the active profile; fleet=true maps every MCP-visible profile independently with schema, version, freshness, drift, and typed UNREACHABLE/FAIL_CLOSED lane status. | `READ_ONLY` | no |
| `oracle_describe` | Oracle Describe | Describe a table/view's columns and constraint metadata. | `READ_ONLY` | no |
| `oracle_describe_index` | Oracle Describe Index | Describe one index's metadata, indexed columns, and function-based expressions. | `READ_ONLY` | no |
| `oracle_describe_trigger` | Oracle Describe Trigger | Describe one trigger's timing, event, target table, status, and body. | `READ_ONLY` | no |
| `oracle_describe_view` | Oracle Describe View | Describe one view's definition metadata and columns. | `READ_ONLY` | no |
| `oracle_get_ddl` | Oracle Get DDL | Fetch an object's DDL via DBMS_METADATA.GET_DDL (allowlisted object types). | `READ_ONLY` | no |
| `oracle_get_source` | Oracle Get Source | Fetch an object's full source text or inclusive line range from ALL_SOURCE, or fetch a view definition from ALL_VIEWS.TEXT, with a character cap. | `READ_ONLY` | no |
| `oracle_sample_rows` | Oracle Sample Rows | Read arbitrary first rows of a table or view with a hard row cap. | `READ_ONLY` | no |
| `oracle_read_clob` | Oracle Read CLOB | Read one CLOB/NCLOB/text value by key with a character cap. | `READ_ONLY` | no |
| `oracle_compile_errors` | Oracle Compile Errors | Retrieve compile errors for the current schema, an owner, or one object (ALL_ERRORS). | `READ_ONLY` | no |
| `oracle_search_source` | Oracle Search Source | Full-text search across ALL_SOURCE for a needle (row- and line-capped). | `READ_ONLY` | no |
| `oracle_plscope_inspect` | Oracle PL/Scope Inspect | Inspect PL/Scope identifier and SQL statement metadata for one PL/SQL object when ALL_IDENTIFIERS/ALL_STATEMENTS are populated. | `READ_ONLY` | no |
| `oracle_explain_plan` | Oracle Explain Plan | Explicit diagnostic-write EXPLAIN PLAN for a vetted SELECT; writes PLAN_TABLE, requires READ_WRITE plus allow_plan_table_write, and is disabled on read-only standby. | `READ_WRITE` | yes |
| `oracle_top_queries` | Oracle Top Queries | Read-only top-SQL ranked by elapsed/CPU/buffer-gets/disk-reads over the free live cursor cache (V$SQLSTATS); live calls may narrow by exact sql_id or bounded sql_text. | `READ_ONLY` | no |
| `oracle_plan_timeline` | Oracle Plan Timeline | Read-only historical optimizer plan and relative-cost timeline from AWR snapshots for one SQL ID. | `READ_ONLY` | no |
| `oracle_db_health` | Oracle Db Health | Read-only DBA health-check suite. | `READ_ONLY` | no |
| `oracle_plsql_parse` | Oracle Plsql Parse | Parse PL/SQL source with the offline plsql-intelligence lowerer and return declaration and diagnostic counts. | `READ_ONLY` | no |
| `oracle_plsql_analyze` | Oracle Plsql Analyze | Run the offline plsql-intelligence engine over a local project root and return doctor summaries. | `READ_ONLY` | no |
| `oracle_plsql_what_breaks` | Oracle Plsql What Breaks | Predict invalidation and recompilation impact for a PL/SQL ChangeSet without touching Oracle. | `READ_ONLY` | no |
| `oracle_plsql_lineage` | Oracle Plsql Lineage | Run offline dependency-lineage traversal from a logical object id in a local PL/SQL project. | `READ_ONLY` | no |
| `oracle_lineage` | Oracle Lineage | Live-verified COLUMN lineage: cross-check source-derived owner.object.column edges against the guarded Oracle catalog and mark verified, missing, or type-mismatched drift. | `READ_ONLY` | no |
| `oracle_plsql_sast` | Oracle Plsql Sast | Run the offline plsql-sast rule harness over a local PL/SQL project and return findings plus skipped-rule evidence. | `READ_ONLY` | no |
| `oracle_plsql_doc` | Oracle Plsql Doc | Extract doc comments from source or render an existing plsql-doc DocSet. | `READ_ONLY` | no |
| `oracle_plsql_live_snapshot` | Oracle Plsql Live Snapshot | Extract live Oracle dictionary rowsets and normalize them through plsql-intelligence CatalogSnapshotBuilder. | `READ_ONLY` | no |
| `oracle_plsql_blast_radius` | Oracle Plsql Blast Radius | Extract a live catalog snapshot, then run the plsql-cicd change-impact predictor for a proposed ChangeSet. | `READ_ONLY` | no |
<!-- /generated:tools -->

Every advertised tool descriptor includes a human title plus explicit MCP annotations; these hints are advisory for clients, while the fail-closed classifier and operating-level gate remain the enforcement boundary. `oracle_query` and `oracle_explain_plan` also advertise `outputSchema`, and query results keep Oracle `NUMBER` cells as strings by default (opt into `numbers_as_float=true` explicitly). Beyond `tools/*`, `initialize` advertises `resources`, `prompts`, and `completions` (protocol `2025-11-25`): `resources/list` exposes `oracle://capabilities` and `oracle://tools`, and read templates for `oracle://schema/{owner}` and `oracle://object/{owner}/{type}/{name}` route through the same safe dispatch path.

### Compatibility aliases

For migrations from shorter Oracle MCP tool surfaces, the server advertises compatibility aliases that route to the guarded `oracle_*` tools and share their classifier, validation, and operating-level behavior. `execute_approved`, `deploy_ddl`, and `read_patch_preview` are wrappers rather than plain renames.

<!-- generated:tools-aliases -->
| Alias | Routes to |
| --- | --- |
| `current_database` | `oracle_connection_info` |
| `switch_database` | `oracle_switch_profile` |
| `enable_writes` | `oracle_set_session_level` |
| `disable_writes` | `oracle_set_session_level` |
| `query` | `oracle_query` |
| `preview_sql` | `oracle_preview_sql` |
| `execute_approved` | `oracle_execute` |
| `compile_object` | `oracle_compile_object` |
| `compile_with_warnings` | `oracle_compile_object` |
| `create_or_replace` | `oracle_create_or_replace` |
| `patch_package` | `oracle_patch_source` |
| `patch_view` | `oracle_patch_source` |
| `read_patch_preview` | `oracle_patch_source` |
| `deploy_ddl` | `oracle_create_or_replace` |
| `list_objects` | `oracle_schema_inspect` |
| `list_schemas` | `oracle_list_schemas` |
| `get_schema` | `oracle_schema_inspect` |
| `describe_table` | `oracle_describe` |
| `describe_index` | `oracle_describe_index` |
| `describe_trigger` | `oracle_describe_trigger` |
| `describe_view` | `oracle_describe_view` |
| `get_ddl` | `oracle_get_ddl` |
| `get_object_source` | `oracle_get_source` |
| `get_errors` | `oracle_compile_errors` |
| `get_clob` | `oracle_read_clob` |
<!-- /generated:tools-aliases -->

## Configuration

Connection profiles live in `profiles.toml`. **No secrets are written to disk** — credentials are references resolved at runtime through `env:`, `file:`, or `keyring:`. A minimal read-only profile:

```toml
[profiles.db_ro]
connect_string = "//db.example.com:1521/FREEPDB1"
username       = "APP_RO"
credential_ref = "env:ORACLE_APP_PASSWORD"
# default_level defaults to read_only and is the ceiling for this profile;
# set max_level to permit explicit, TTL-bounded elevation up to ADMIN.
```

The full field reference — HTTP TLS/mTLS/OAuth listeners, the signed audit chain, result-masking policy, fleet/monitor profiles, TCPS/wallet and IAM/DRCP/proxy auth, and per-call timeout/SDU budgets — is in **[`docs/configuration.md`](docs/configuration.md)**.

## Documentation

- **[Installation manual](docs/install.md)** — verified installers, air-gapped installs, updates, uninstall, services, dashboard pairing, and release channels.
- **[Configuration reference](docs/configuration.md)** — every profile, auth, transport, audit, and masking field.
- **[Operating & deployment](docs/operations.md)** — containerized deployment, least-privilege account, network posture, service management (systemd/launchd/Windows), air-gapped install, and the operator runbook.
- **[TNS discovery onboarding](docs/tns-discovery-onboarding.md)** · **[Toolchain](docs/toolchain.md)** · **[Upgrade runbooks](docs/upgrading-to-0.8.0.md)** and [field-hardening notes](docs/oraclemcp-091-field-hardening-notes.md).
- **Architecture decisions:** [`docs/adr/`](docs/adr/) · **Formal proofs:** [`proofs/purity-core/`](proofs/purity-core/).

## Build from source

This branch is pinned to **`nightly-2026-05-11`** and has no stable MSRV (the pin arrives transitively through `asupersync`, and Windows needs `windows_by_handle`; see [`docs/toolchain.md`](docs/toolchain.md)). Prefer the verified release archive above; build from source only when you intend to:

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

Live database access is built in through the pure-Rust thin driver — **no Oracle Instant Client, ODPI-C, or C toolchain**. Optionally set `TNS_ADMIN` for net-service-name connections. The default build embeds the offline PL/SQL engine; `--no-default-features` omits it.

## License

Licensed under **Apache-2.0 OR MIT**. The Docker image and crates do not redistribute Oracle Instant Client.