MCPDBWizard

Documentation  ·  Operating

Release notes

Images are published to the GitHub Container Registry:

docker pull ghcr.io/srmadscience/mcpdbwizard:2.0.9
docker pull ghcr.io/srmadscience/mcpdbwizard:latest

Both tags point at the same image, linux/amd64 and linux/arm64. Pin the version tag in anything that matterslatest moves under you, which is what makes it convenient for a trial and wrong for a deployment.

And latest does not refresh itself. docker run never pulls an image the machine already has, so a box that pulled latest once keeps starting that build until someone pulls again — see :latest does not refresh itself. A pinned version tag cannot go stale this way, because it never moves.

For what is not fixed, see Known issues.


2.0.9 — 27 August 2026

A sparse index-by OUT collection no longer fails with ORA-01403. This affects generated DAO code, not just MCP — regenerate to pick it up.

An index-by table is sparse by nature: PL/SQL lets a routine assign element 1 and element 7 and nothing between. The generated code walks such a parameter from first to last index, so a gap made it read an index that is not there — ORA-01403: no data found, raised from inside the emitted block, naming neither the parameter nor the gap.

Which parameters were affected. Only those whose values are converted on the way out, which is where the guard was missing:

Element typeBefore
NUMBER that does not fit the numeric slot — e.g. number(30,15)failed
RAWfailed (fixed in 2.0.6)
DATE, TIMESTAMP, TIMESTAMP WITH [LOCAL] TIME ZONEalready correct
VARCHAR2, and a NUMBER that binds numericallynever affected — no conversion loop

So the realistic case is a high-precision NUMBER collection with gaps in it. If you have one and have never seen this, your routine returns a dense collection and always did.

Positions are preserved. Values come back at the indexes the routine set them at, with the gaps empty — they are not compacted to the front. Verified on Oracle 12c through 26ai.

How this was found, since it says something about the rest. The RAW case was fixed in 2.0.6 by comparing the code against its neighbours, with no test that failed without the fix — nothing in the test schema returned a sparse collection. Adding a fixture that does turned up a second unguarded type, and now holds both in place: six boxes across five Oracle releases, guard removed → the error reproduces, guard restored → it does not.

Also in this release: the documentation now explains that docker run does not re-pull :latest, so a machine that pulled it once keeps starting that build until someone pulls again.

2.0.8 — 27 August 2026

The console shows which version it is running — on the login screen, and on the right of the banner once you are signed in.

Two places because they answer for two different people. The banner tells whoever is already working here which build they are on. The login screen tells somebody handed a URL who is deciding whether that box has a fix in it — and that person may not have an account, so putting it only behind a login makes “what is deployed here?” require one, which is backwards for a fact the image tag already discloses.

The number is read from the build itself, so it cannot drift from the release. A console that confidently names the wrong version is worse than one naming none, because the only reason to read it is to decide whether a fix is present. A build that stamped no version shows dev rather than guessing something release-shaped.

Nothing else changed — no generator, no generated server, no proxy.

2.0.7 — 27 August 2026

A console release. Nothing in the generator, a generated server or the proxy changed — the tools your configs publish are identical to 2.0.6. What changed is what the console tells you when something is not configured, which is where two people lost time.

The setup instructions now show a command that works. Launching on Docker gave a docker run with no Oracle settings and said to add them from another page, which held a bare -e fragment with no command around it. Following either produced a console that starts, passes its healthcheck and refuses every Design page. The minimum is now the whole command. Also documented: passing values already in your shell with -e NAME (no =value), which keeps the password off the command line and out of shell history; that MCPDBWIZARD_ORACLE_PORT is the one you can omit; and the leading-/ on the SID, stated where it bites rather than three sections below it.

The “Oracle is not configured” message names the password. It listed the host, SID and user and said nothing about DB_PASS, so an operator who set exactly the three things they were told to set got a login failure — which reads as a wrong password rather than as one nobody asked for. The same page also decided whether a restart was needed from a check that ignored the password, so a deployment missing only DB_PASS was told to reload a variable a running process cannot be given.

Start-up warns once per missing variable. One line each, naming the variable, what it is for and an example, so a line can be read on its own — instead of one sentence listing them all, which is the shape most likely to be truncated, and losing its tail loses a whole setting.

WARN  Oracle is not configured: 4 required setting(s) are not set.
WARN    MCPDBWIZARD_ORACLE_HOST is not set - the database host, e.g. db.example.com
WARN    MCPDBWIZARD_ORACLE_SID is not set - the SID, or /SERVICE for a pluggable database, e.g. /FREEPDB1
WARN    MCPDBWIZARD_ORACLE_USER is not set - the Oracle account to connect as
WARN    DB_PASS is not set - the password for that account, or DB_PASS_FILE naming a file that holds it

Admin → Audit stops understating what it protects. The page said flatly that the write-ahead spool is not encrypted. That has been optional since 2.0.5 — set MCP_AUDIT_SPOOL_KEY and it is AES-256-GCM — and the page now reports what your deployment actually does rather than asserting a constant, naming the variable either way. It matters most at MCP_AUDIT_LEVEL=VALUES, where the same paragraph tells you the queue holds tool arguments and responses.

2.0.6 — 26 August 2026

Every Oracle scalar type now crosses an index-by collection. DATE, TIMESTAMP and RAW elements of a PL/SQL index-by table used to remove the entire routine from the tool list. The gate is per routine rather than per parameter, so one such argument hid every operation that routine would have had — no error, no degraded tool, just an absence. TIMESTAMP WITH [LOCAL] TIME ZONE came out of that gate in 2.0.1; the remaining three follow here, and the gate is now empty of scalar types.

If you selected a routine in the config and could not find its tool, this was the likeliest reason. It is worth re-reading tools/list after upgrading: the tools appear with no change to your config.

What each element accepts, published in the tool schema rather than left to be guessed:

ElementCrosses asExample
DATEISO-8601 date, time optional1990-01-01 or 1990-01-01T09:30:00
TIMESTAMPISO-8601, up to 8 fractional digits2019-03-01T14:25:36.123456
RAWbase64, not hex3q2+7w== for the four bytes DE AD BE EF

Two of those carry a caveat the description now states out loud rather than leaving you to find it. An Oracle DATE has no sub-second precision, so fractional seconds are dropped — sending them is neither honoured nor an error, which is exactly the silent middle ground a caller cannot discover for themselves. And a RAW is shown in hex by SQL*Plus, by a DESCRIBE, and by most tools you will have met one in — but MCP has no binary type, so it crosses as base64. Sending hex is not rejected, because it cannot be: DEADBEEF is also valid base64 and decodes cleanly to four completely different bytes. Read the parameter description, which says so.

A sparse index-by of RAW no longer risks a NO_DATA_FOUND. The generated PL/SQL walked such a collection from first to last index without checking that each one exists, where the DATE and TIMESTAMP paths always had. PL/SQL lets a routine assign element 1 and element 7 and nothing between, so a gap raised an error naming neither the parameter nor the gap. This affects generated DAO code as well as MCP tools, so regenerate to pick it up.

Nothing else changed. No new configuration, no schema changes, and no change to any tool that already worked — the release adds tools that were previously absent and fixes the loop above.

2.0.5 — 26 August 2026

Another small one, and worth saying so plainly: nothing in the generator, the proxy or a generated server changed. The only new code is a load-testing client that ships alongside them. If you are happy on 2.0.4, the reason to move is the image metadata rather than anything the product does.

The image now says what is in it. Every published image already carried a BuildKit provenance record — that has been on by default since 2.0.0, and is not new here. What is new is an SBOM listing the contents as SPDX, one document per platform, which no earlier image has. It reads straight off the registry, without pulling anything:

docker buildx imagetools inspect ghcr.io/srmadscience/mcpdbwizard:2.0.5 --format '{{ json .SBOM }}'

The question this exists for is the one that arrives on the morning of the next Log4Shell — is the bad version in your image? — and it is now a search you can run yourself in a minute instead of a mail to us. Verifying the image has the commands, including how to search the SBOM for a named package.

And it is honest about what it is not. The image is not signed: there is no cosign or Sigstore signature, so nothing here identifies the publisher. The provenance is self-attested, produced on a developer machine by the same party that publishes the image, which makes it a record rather than independent evidence. It is genuinely useful for what is in it and what was it built from, and it should not be quoted as more than that. The docs page says so at greater length, deliberately.

Base images are pinned by digest. Both stages of the Dockerfile name an exact digest rather than a floating tag, so two builds of the same commit contain the same operating system — and the provenance reports what was chosen rather than whatever the upstream tag happened to point at that morning.

You can load-test your own server without an LLM. A new client ships in the image and can drive any published tool at a controlled rate for a bounded time — the RPC level, with no model in the loop, so a run is repeatable and costs nothing:

docker exec mcpdbwizard java -cp '/app/lib/*' com.mcpdbwizard.loadtest.McpLoad \
    --url http://127.0.0.1:8080/mcp/<config> --token <api-token> --list

--list prints every tool the server publishes with its input schema and a skeleton workload entry, which is the starting point for a real run: --workload work.json --for 5m --rate 200 --threads 8. It works against the proxy, as above, or against a generated server’s own port.

It reports latency twice, and the second number is the one that matters. Service time is send-to-receive; scheduled latency is from the moment a call was due to start. Drive a server past what it can sustain and service time stays flat and reassuring while scheduled latency climbs into seconds — the queue is the whole answer, and a tool reporting only the first would tell you everything is fine. It also states achieved rate against target, so a run that fell behind says so.

A workload entry can carry "check": "unique", which verifies that every call returned a different value and that the values span exactly as many numbers as there were calls. That is what separates a fast server from a broken one returning a cached answer, and a throughput figure alone cannot.

Two notes from using it. Prefer --for over a call count for anything long: throughput climbs steeply for the first minute while the JVM compiles, the pool grows and Oracle caches cursors, so a count sized from a five-second sample is measuring warm-up — which is also what --warmup 30s is for. And pass -Dlogback.configurationFile= pointing at a quiet config, or logback defaults to DEBUG and buries the results under a line per call.

Two more known issues, both documented rather than fixed: a DML SQL statement of your own cannot hand back a generated key, and there is no support for TNS aliases, wallets, TLS to the database, RAC or SCAN. Both are on Known issues with what a caller actually sees and the way round each.

2.0.4 — 25 August 2026

A small release, and worth saying so: one metadata fix in the image and one line of copy off the Runtime page. Nothing in the generator, the proxy or a generated server changed, so there is no reason to move unless you read image labels.

The image now describes itself correctly. It carried four OCI labels and inherited two more without meaning to. eclipse-temurin is Ubuntu-based, Ubuntu’s image sets title, description, created and version, and a label that is not overridden is inherited rather than absent — so every release up to and including 2.0.3 announced

org.opencontainers.image.version = 26.04
org.opencontainers.image.created = 2026-08-17T09:02:45

Ubuntu’s release number in the field tooling reads to identify ours, and a build date eight days before the image was pushed. Both now say what they should, alongside six more: vendor, licenses, url, documentation, title and description. The four that vary per build are also published as manifest annotations, so a registry UI can read them without pulling.

docker inspect --format '{{json .Config.Labels}}' ghcr.io/srmadscience/mcpdbwizard:2.0.4

licenses reads LicenseRef-Proprietary. The repository has no single licence — the generator and the runtime library under app/ are Apache-2.0, the console under web/ is proprietary — and the image ships both halves, so the artifact as distributed is proprietary. SPDX has no registered identifier for all-rights-reserved, and naming Apache-2.0 here would have overstated the grant.

Off the Runtime page: a footnote telling you a config’s MCP server option must be YES and its target JVM at least 17. That is a Design-tab concern stated on the wrong page — by the time a config is listed on Runtime the question is settled, and a start that fails for either reason says so at the moment it fails.

2.0.3 — 25 August 2026

You can see a running server’s tools without leaving the console. The Runtime page now shows each server’s real tools/list response — the payload itself, on the page, not behind a control you have to know to click. A long list is capped with the whole thing offered as a file, and the same view is available from the command line with --list-tools. Servers started automatically at boot are listed too, and a server generated before this existed is simply not asked rather than being reported as broken.

Start-up says what is not configured. A container missing a required Oracle variable used to start, serve the console and fail only when something touched the database. It now logs a single line naming exactly which of the host, SID, user and password are unset, and what will and will not work until they are. It still starts — deliberately, because the console has to be able to stop a running server during an outage — but it no longer does so silently.

Prometheus service discovery at /sd/mcp, so a scrape config can find the running servers instead of being edited each time one starts. The metrics port is now published in the hand-written docker run in the docs as well as in the compose file, and the docs say plainly that each MCP server needs a metrics port of its own.

The Runtime page no longer returns a 500 while a config is being regenerated.

Also in this release: the Descriptions tab moved onto the same Design model as the other tabs, and a demo hotel-booking schema is checked in so the quickstart has something to point at.

2.0.2 — 21 August 2026

The object list says how many descriptions an object carries, so a table with four written and one missing is visible at a glance rather than by opening each one.

A foreign-key child lookup names the child table in its description. It previously described the relationship without saying which table came back, which made two lookups on the same parent hard to tell apart.

2.0.1 — 21 August 2026

Zoned timestamps cross MCP, with their zone. A PL/SQL index-by collection of TIMESTAMP WITH TIME ZONE or TIMESTAMP WITH LOCAL TIME ZONE used to remove the entire routine from the tool list — the gate is per routine, so one such parameter hid every operation. Two things were wrong underneath: the value crossed JDBC through a text shuttle whose conversion masks carried no zone at all, so an offset was rejected outright and a zone-less value came back with the server’s zone silently applied. Both directions now use a zone-aware mask, verified live on the 12c and 23ai lines preserving both +05:30 and Asia/Calcutta.

A date crossing MCP means what the caller sent. A DATE was rendered as an epoch instant in UTC, so a row Oracle holds as 18:38 was reported as 17:38 — a wrong wall-clock time rather than a differently-spelled one, on data an agent reads as a departure time. Dates now parse and render against the server’s own clock, and a bare 1980-01-01 is accepted where it previously was not.

A config author can write the server’s instructions — the text a model reads before it reads any tool. It is prepended to the generated inventory rather than replacing it, and the box has room to type in.

The Design tabs commit a tick as you make it. The per-tab Save button is gone: selecting an object saves it. Settings also show their default, so an empty box is no longer ambiguous between “unset” and “set to the default”.

The audit settings are grouped, the two closed choices are dropdowns rather than free text, and a bad value gets an error page instead of a stack trace.

API documentation for com.mcpdbwizard.pub is published alongside this site at /javadoc/.

2.0.0 — 20 August 2026

The first release to carry a version number. Everything before it was built and run from source, so there is no upgrade path to describe and no earlier image to compare against.

What it stamped: the web console with its Design and Runtime halves, the /mcp/<config> proxy with accounts and a per-config access matrix, generated MCP servers over Streamable HTTP, the audit trail, Prometheus metrics, and pooled DAO factories.

Two things landed close enough to the stamp to be worth naming, because both changed how a record parameter behaves and both were failures you could not see:

The field names are the generated Java ones rather than the Oracle column names, which is issue 3 on the known-issues page.