oracle-ai-developer-hub

OraViz MCP

OraViz MCP

Oracle AI Database, chart-ready. A minimal, visualization-first MCP server. Query, profile, plot.

Python 3.12+ Oracle AI Database 26ai Free MCP: stdio | http | sse License: MIT CI


OraViz MCP is pab1it0/adx-mcp-server reimagined for Oracle AI Database – a deliberately tiny alternative to the broad official Oracle MCP servers. It speaks SQL, profiles tables, and turns result sets into PNG charts any MCP client can show. Seven read-oriented tools, no Oracle client libraries (python-oracledb thin mode talks straight to Oracle AI Database 26ai Free or any newer release).

Two ideas shape everything:

Deploy with a dedicated Oracle reader account. Network transports require verified bearer tokens; stdio uses local OS trust. See SECURITY.md for the security boundaries and deployment checklist.

Visualization at a Glance

Bar
Revenue by region bar chart
Area / Line
Online revenue by month area chart
Vector (PCA)
Product embeddings projected to two dimensions with PCA
All images were rendered by create_chart against the demo schema in examples/demo-sales.sql.

Why OraViz?

The Context Contract

Every row-returning tool follows the same rules, and the test suite asserts them:

Rule Default Env knob
Query preview size (execute_query without max_rows) 25 rows ORACLE_MCP_PREVIEW_ROWS
Hard row cap per query (charts included) 500 rows ORACLE_MCP_MAX_ROWS
Longest cell before ... truncation 500 chars ORACLE_MCP_MAX_CELL_CHARS
Database round-trip timeout (not a whole-tool deadline) 60 s; range 1–300, cannot disable ORACLE_CALL_TIMEOUT
Result columns 64; maximum 200 ORACLE_MCP_MAX_RESULT_COLUMNS
Combined text + serialized JSON structured content / PNG limit 256,000 characters / 2,000,000 bytes Fixed
Concurrent tool calls per process 4; range 1–32 ORACLE_MCP_MAX_CONCURRENT
Metadata header per result <n> row(s) (truncated; more rows exist) \| columns: A, B –

A tool result therefore looks like this instead of a 25-dictionary JSON array:

4 row(s) | columns: REGION, REVENUE

| REGION | REVENUE |
|---|---|
| East | 573932 |
| North | 502897 |
| South | 432190 |
| West | 360759 |

Raw CLOB/NCLOB/BLOB/BFILE locators render as <LOB> without reading their contents; byte values are summarized, VECTOR(384) renders as <VECTOR(384)>, and midnight timestamps as dates. For more rows, pass max_rows explicitly – and the server still stops at the hard cap.

Numeric settings have bounded defaults (at most 5,000 rows and 4,096 characters per cell). Invalid numeric environment settings warn and fall back to defaults. Over-limit MCP responses are rejected, including when a rendered table was already truncated but the combined response exceeds the budget. Data mirrored in text and structured JSON counts in both representations. Row/output caps do not bound database CPU or total execution time; see resource limits.

Tools

Tool Returns Context cost
execute_query Read-only SQL result as a compact markdown table (preview-capped) Bounded by preview cap
list_tables Tables and views in the current (or a given) schema One small table
get_table_schema Columns, types, length, nullability, primary-key membership One small table
sample_table_data First rows of a table sample_size (default 10)
get_table_details Owner, tablespace, optimizer stats (NUM_ROWS, LAST_ANALYZED), optional exact row count Single row
profile_table Per-column stats: non-null, nulls, distinct, min, max, avg ~1 line per column
create_chart PNG chart image + short data preview Image + ≤5 preview rows

How create_chart maps columns

The first column is the x-axis (or the labels for pie and vector charts); numeric columns after it become series. That makes the chart contract simple and SQL-driven:

SELECT region, ROUND(SUM(revenue), 2) AS revenue
FROM oraviz.sales_demo
GROUP BY region
ORDER BY revenue DESC
create_chart(sql=..., chart_type="bar", title="Revenue by region")
SELECT product_name, embedding FROM oraviz.product_vectors
create_chart(sql=..., chart_type="vector", title="Product embeddings")

Quick Start

1. Choose an Oracle AI Database

OraViz connects with python-oracledb thin mode, so it runs against a hosted FreeSQL schema or a local Oracle AI Database 26ai Free container. Pick either path; steps 3 and 4 are the same for both.

Option A – FreeSQL (hosted, no Docker)

FreeSQL gives you a hosted Oracle AI Database 26ai schema at no cost, with the worksheet and the connection details in one place. It is the fastest path to real data for the charts. Sessions keep the Developer Hub program identifier (devrel-developerhub-oraviz-mcp), and no wallet files are needed: python-oracledb thin mode connects over TCPS directly.

Step 1: Open FreeSQL. The worksheet interface is where you browse schema objects, run SQL, and reach the connection details.

FreeSQL worksheet interface

Step 2: Sign in. Use your Oracle account, or create one from the same page.

Oracle sign-in page for FreeSQL

Step 3: Copy the Python connection details. Open Connect to the Database and choose the Python tab. FreeSQL shows the host, port, service name, and generated password for your schema:

FreeSQL Python connection details

Put those values in the server environment, or in a private .env file (see Configuration):

ORACLE_USER=<freesql-user>
ORACLE_PASSWORD=<freesql-password>
ORACLE_DSN=tcps://db.freesql.com:2484/<freesql-service-name>

The MCP server signs in as the FreeSQL schema user directly; the owner/reader split in Option B is Docker-only.

Option B – Local Oracle AI Database 26ai Free (Docker)

# Disposable local development database; the admin password is NOT the MCP password.
read -rsp 'Database admin password: ' ORAVIZ_DB_ADMIN_PASSWORD
export ORAVIZ_DB_ADMIN_PASSWORD
docker compose up -d
unset ORAVIZ_DB_ADMIN_PASSWORD

The container takes a couple of minutes to initialize. docker ps shows (healthy) when it is ready. The listener is published only on 127.0.0.1:1530. The database stores data in a named volume. The floating database image is for this demo; pin a reviewed digest for controlled deployments.

2. (Optional) Load the demo schema

examples/demo-sales.sql creates the 96-row SALES_DEMO table and the six-row PRODUCT_VECTORS table with an 8-dimension VECTOR column.

FreeSQL: open the worksheet, paste the script, and run it. The tables land in the schema you signed in with – the same schema the MCP server reads – so no extra grants are needed.

Docker: create an owner only for setup, then give a separate reader access to the two demo tables. The SQL below uses password placeholders: replace them privately with separate generated passwords. Do not reuse the legacy passwords or broad grants in the demo script’s historical header.

docker exec -it oraviz-oracle sqlplus -L system@//localhost:1521/FREEPDB1

At the SQL prompt (admin use is limited to provisioning):

CREATE USER oraviz IDENTIFIED BY "REPLACE_WITH_OWNER_SECRET"
  DEFAULT TABLESPACE USERS QUOTA 20M ON USERS;
GRANT CREATE SESSION, CREATE TABLE TO oraviz;
CREATE USER oraviz_reader IDENTIFIED BY "REPLACE_WITH_READER_SECRET";
GRANT CREATE SESSION TO oraviz_reader;
EXIT;

Load the demo tables as the owner. The script drops and recreates these tables; use it only in this disposable schema.

docker cp examples/demo-sales.sql oraviz-oracle:/tmp/oraviz-demo-sales.sql
docker exec -it oraviz-oracle sqlplus -L oraviz@//localhost:1521/FREEPDB1

At the owner SQL prompt:

@/tmp/oraviz-demo-sales.sql
GRANT READ ON oraviz.sales_demo TO oraviz_reader;
GRANT READ ON oraviz.product_vectors TO oraviz_reader;
EXIT;

The initial DROP TABLE statements can report missing tables on a fresh schema. In the Docker setup, only ORAVIZ_READER is used by the MCP server. For production, grant READ on approved tables or reviewed views and audit inherited permissions; never use a schema owner or admin.

3. Point your MCP client at the server

Take ORACLE_USER and ORACLE_DSN from the option you chose (the Docker defaults are shown below) and inject ORACLE_PASSWORD at runtime.

Claude Desktop / Cursor (uvx from a reviewed commit) Replace `REVIEWED_COMMIT_SHA` with an audited commit ID. Inject `ORACLE_PASSWORD` into the MCP host's protected process environment; it is intentionally absent from the shareable client configuration. ```json { "mcpServers": { "oraviz": { "command": "uvx", "args": ["--from", "git+https://github.com/jasperan/oraviz-mcp@REVIEWED_COMMIT_SHA", "oraviz-mcp"], "env": { "ORACLE_USER": "oraviz_reader", "ORACLE_DSN": "localhost:1530/FREEPDB1" } } } } ```
Local checkout (development) ```json { "mcpServers": { "oraviz": { "command": "uv", "args": ["--directory", "/path/to/oraviz-mcp", "run", "oraviz-mcp"], "env": { "ORACLE_USER": "oraviz_reader", "ORACLE_DSN": "localhost:1530/FREEPDB1" } } } } ```
Docker ```bash docker build -t oraviz-mcp . ``` ```json { "mcpServers": { "oraviz": { "command": "docker", "args": ["run", "--rm", "-i", "--network", "host", "-e", "ORACLE_USER", "-e", "ORACLE_PASSWORD", "-e", "ORACLE_DSN", "oraviz-mcp"], "env": { "ORACLE_USER": "oraviz_reader", "ORACLE_DSN": "localhost:1530/FREEPDB1" } } } } ``` `--network host` is a Linux local-demo convenience for reaching the loopback database. For a deployment, use a dedicated private network and restrict egress as described in [SECURITY.md](/oracle-ai-developer-hub/apps/oraviz-mcp/SECURITY.html). Inject the reader password at runtime, and use a reviewed image digest when distributing the image.

4. Ask for a chart

“Profile ORAVIZ.SALES_DEMO, then chart total revenue by region from ORAVIZ.SALES_DEMO.”

The model will call profile_table, pick a chart type, run create_chart, and you get a rendered image back.

Configuration

Variable Description Default
ORACLE_USER Dedicated reader with CREATE SESSION and object READ grants (required); known admins rejected –
ORACLE_PASSWORD Password for the user (required) –
ORACLE_HOST Database hostname localhost
ORACLE_PORT Listener port 1521
ORACLE_SERVICE Service name FREEPDB1
ORACLE_DSN Full EZConnect descriptor or connect string (FreeSQL: tcps://db.freesql.com:2484/<service>); overrides host/port/service –
ORACLE_CONFIG_DIR Wallet config directory (Autonomous Database / mTLS) –
ORACLE_WALLET_LOCATION Wallet location –
ORACLE_WALLET_PASSWORD Wallet password –
ORACLE_MCP_PREVIEW_ROWS Preview size, 1–5,000, also capped by max rows 25
ORACLE_MCP_MAX_ROWS Hard row cap per query, 1–5,000 500
ORACLE_MCP_MAX_CELL_CHARS Per-cell truncation limit, 1–4,096 500
ORACLE_MCP_MAX_RESULT_COLUMNS Result column cap, 1–200 64
ORACLE_MCP_MAX_CONCURRENT Admitted calls per process, 1–32 4
ORACLE_MCP_ALLOWED_TOOLS Comma-separated exact names from the seven tools above; empty/unknown entries reject startup All seven
ORACLE_CONNECT_TIMEOUT TCP connect timeout, seconds 10
ORACLE_CALL_TIMEOUT Per database round-trip timeout, seconds, 1–300; cannot disable 60
ORACLE_MCP_SERVER_TRANSPORT stdio (default), http, sse, streamable-http stdio
ORACLE_MCP_BIND_HOST Bind host for network transports 127.0.0.1
ORACLE_MCP_BIND_PORT Bind port for network transports 8080
ORACLE_MCP_AUTH_ISSUER Required network JWT issuer, HTTPS URL –
ORACLE_MCP_AUTH_AUDIENCE Required network JWT resource audience –
ORACLE_MCP_AUTH_JWKS_URL Required network signing-key endpoint, HTTPS URL –
ORACLE_MCP_AUTH_REQUIRED_SCOPES Required network scopes, space-separated (e.g. oraviz:read) –
ORACLE_MCP_AUTH_BASE_URL Optional public HTTPS base URL for discovery, excluding /mcp and /sse –
LOG_FORMAT json (default) or console for human-readable logs json
LOG_LEVEL structlog level number 20 (INFO)

Copy .env.template to .env with umask 077 – the server loads it via python-dotenv. Keep it untracked, never source it as shell code, and use process environment injection for literal secrets containing ${...}. Existing process environment settings take precedence.

All network transports (http, sse, streamable-http) require auth, including loopback and proxy use. Bearer tokens need a valid RS256 signature, issuer, audience, expiry, nonempty signed sub, applicable nbf, and required scopes. All authenticated users share the configured Oracle principal; this is not per-user or multitenant data authorization. TLS, private networking, gateway limits, audit retention, and host approval for sensitive reads/exports remain deployment responsibilities. See the network configuration example.

Architecture

oraviz-mcp
  src/oraviz_mcp/
    server.py          # FastMCP app, config, Oracle client, validation, the 7 tools
    charts.py          # pure matplotlib rendering (bar/line/area/scatter/pie/histogram/vector -> PNG bytes)
    main.py            # entry point: env validation, transport selection
  tests/
    test_config.py     # config dataclasses + env parsing
    test_validation.py # SQL guard, identifiers, formatting, result rendering
    test_charts.py     # every chart type + validation errors
    test_server_tools.py  # all tools against a scripted fake cursor
    test_main.py       # entry point
    integration/       # live Oracle tests (skipped without ORAVIZ_TEST_DSN)
  examples/demo-sales.sql
  docs/testing.md

Data flow: the MCP client calls a tool -> server.py validates (validate_query / validate_table_name) -> python-oracledb thin connection -> rows are narrowed (fetch cap) -> either rendered as a compact table (render_rows) or handed to charts.py -> the model receives text, or an image plus a short preview.

Development

uv sync --extra dev          # install everything (matplotlib, fastmcp, oracledb, pytest)
uv run pytest                # hermetic unit suite (~98% line coverage; the 90% gate is enforced)
uv run pytest -k chart       # focus on one area

# Live tests create/drop scratch objects: use a separate disposable test owner,
# not the MCP reader. Inject ORAVIZ_TEST_PASSWORD privately before this command.
ORAVIZ_TEST_DSN=localhost:1530/FREEPDB1 \
ORAVIZ_TEST_USER=oraviz \
uv run pytest tests/integration -v --no-cov

docker build -t oraviz-mcp . # container build (multi-stage, non-root)

See docs/testing.md and tests/README.md for the testing story.

OraViz vs. the official Oracle MCP servers

Oracle ships rich, general-purpose MCP servers – SQLcl’s built-in MCP server (run-sql, connect, schema-information, …), ORDS MCP, the OCI Database Tools MCP, and the reference servers in oracle/mcp. Use those when you need breadth: DDL, transactions, RAC, RAG pipelines.

OraViz is the opposite bet: seven tools, read-only SQL, and a hard focus on turning data into pictures without flooding the model’s context. If you want the database operated, use the official servers. If you want the database seen, use this one.

Benchmarks

Historical snapshot: the numbers and figures below were captured before security hardening. They are retained unchanged and have not been recomputed for the current schemas or limits. Current guarantees and limitations are in SECURITY.md.

We measured the tokens an agent must process to answer the same questions through OraViz and through the official SQLcl MCP server, against the same 26ai Free database (tiktoken cl100k_base: tool schemas plus every tool result):

Stage OraViz SQLcl MCP Savings
Tool schemas (read once per session) 862 2,139 59.7%
Schema discovery 69 354 80.5%
Full 96-row dump 825 2,136 61.4%
Whole workflow (5 questions) 2,337 5,006 53.3%

The 10-row sample step trades ~55% more framing tokens than raw CSV, and that overhead cannot grow with the result size. Rendering the aggregate as a chart costs 123 text tokens plus the PNG image. Full methodology, step-by-step numbers, and reproduction commands: benchmarks/. A live showcase with the end-to-end query demo is at jasperan.github.io/oraviz-mcp.

Credits

License

MIT – see LICENSE.

ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT ANY REVIEW. USE AT YOUR OWN RISK.


[![GitHub](https://img.shields.io/badge/GitHub-jasperan-181717?style=for-the-badge&logo=github&logoColor=white)](https://github.com/jasperan)  [![LinkedIn](https://img.shields.io/badge/LinkedIn-jasperan-0077B5?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/jasperan/)  [![Oracle](https://img.shields.io/badge/Oracle_AI_Database-26ai_Free-F80000?style=for-the-badge&logo=oracle&logoColor=white)](https://www.oracle.com/database/free/)