Denied queries
A denial is usually correct: Midplane is doing its job. But if a query you expected to succeed was denied, the reason string names the rule and the table. Match it to one of these.My SELECT was denied
My SELECT was denied
The likely cause: the table is set to
deny. A table marked deny allows neither reads nor writes. Check your policy — if the table’s access is deny (or your default is deny and the table isn’t listed), reads are blocked. Set it to read (or read_write) to allow SELECTs. See table access.My write was denied
My write was denied
Writes deny by default. To allow a write, mark the table Note that
read_write in your policy:- Cloud (dashboard)
- Self-host (YAML)
In the project’s Database pane, add a per-table override under Table access and set it to
read_write, then Save. Cloud hot-reloads the change.read_write grants ordinary DML only. Side-effecting statements — COPY, LOCK, CALL, NOTIFY, SET — deny regardless of YAML, because the policy can’t grant a per-table target for them.My agent says a write is "awaiting approval"
My agent says a write is "awaiting approval"
Not a denial — the write is held for a human and hasn’t run. Approve or deny it from Approvals in the dashboard; the agent then re-runs the identical statement to collect the result.Two things trip people up. The grant is bound to the exact
sql and intent, so an agent that paraphrases its retry opens a second request instead of collecting the first — the held result hands both strings back for exactly this reason. And a request nobody answers expires after 30 minutes, after which the agent has to ask again.Agent connection problems
My client shows the Midplane server with no tools
My client shows the Midplane server with no tools
The client’s OAuth session lapsed and it’s asking you to sign in again — some clients (Cursor in particular) render that as a connected server with an empty tool list rather than a prompt. Re-run the client’s sign-in for the Midplane server; the consent screen pre-selects your current grant, so nothing about the scope changes.A connected agent stays connected as long as it’s used: the session refreshes itself on every call. The clock only runs while the client is quiet, and it takes 90 days of no activity to lapse. An agent you revoked also stops working immediately — check the project’s Agents pane before re-authorizing.
Claude Code can't connect
Claude Code can't connect
The most common cause is a missing transport flag. Claude Code defaults to stdio and will treat your URL as a launcher command, failing silently. Add Verify it registered with
--transport http:claude mcp list. See Claude Code setup.Claude Desktop rejects my localhost URL
Claude Desktop rejects my localhost URL
Claude Desktop’s Custom Connectors UI enforces HTTPS and rejects This requires
http://localhost (and won’t accept a self-signed cert against localhost). For a local self-host instance, use the mcp-remote shim in the config file instead:npx (Node.js) on your PATH. Restart Claude Desktop after editing the file. Hosted Midplane (an HTTPS endpoint) uses Custom Connectors normally. See Claude Desktop setup.Connecting to your Postgres
Agent query fails with 'self signed certificate'
Agent query fails with 'self signed certificate'
Your database presents a self-signed or private-CA TLS certificate and its DSN uses
sslmode=require (or prefer). Since engine 0.15.0 Midplane follows libpq’s TLS semantics, where require encrypts without verifying the certificate — so a self-signed cert connects. On an older engine, require was upgraded to full verification and rejected the cert with a bare self signed certificate the moment an agent ran its first query.- Encryption is enough (a self-signed or private cert) —
sslmode=requireconnects on0.15.0+; upgrade the engine if you’re behind. - You want the cert verified — add
sslrootcert=/path/to/ca.crtto the DSN and Midplane verifies against that CA (likeverify-ca).verify-ca/verify-fullare unchanged. - Encryption isn’t needed (a private, trusted network) —
sslmode=disableturns TLS off.
sslmode behaves exactly as before. On Cloud the managed engine already runs 0.15.0+, so this applies to whichever database you connect.Boot failures
npx midplane exits immediately
npx midplane exits immediately
The standalone engine needs Node 22.16+ or 24+ — its audit log uses the
node:sqlite builtin — or Bun 1.3+. On anything older the binary refuses to start and names the requirement, rather than failing partway through with a stack trace from whichever dependency reached a newer builtin first.Check with node --version. If your MCP client launches with a different Node than your shell (a common cause when the client was installed by a version manager), give the config an absolute path to a supported npx.Boot fails with an unset ${VAR}
Boot fails with an unset ${VAR}
Midplane interpolates
${ENV_VAR} references in databases[].url so DSNs stay out of your YAML. If a referenced variable isn’t set in the environment, the URL can’t resolve and the server fails to boot.Check that every variable named in your policy file is present in your --env-file (or compose environment). For example, a policy with url: ${PROD_DATABASE_URL} needs PROD_DATABASE_URL defined.You can structurally validate a policy offline without the variables set — midplane policy validate <file> doesn’t fail on an unset ${VAR}, so it checks the YAML shape independent of the environment. The variable only has to resolve at server boot.When in doubt, read the audit log.
midplane audit tail streams every attempt and decision, including the rule that denied — so you can see exactly what your agent sent and why it was blocked. See the self-host audit log.FAQ
Short, grounded answers to the questions that come up most.Can Midplane see my data?
Can Midplane see my data?
Yes — and we don’t claim otherwise. Midplane logs every query as the audit record, and results pass through its memory on the way back to your agent (never persisted). On self-host none of this leaves your environment; on Cloud, operators with database access can read audit data until retention expires. See what we claim, and what we don’t.
Does it replace my database roles?
Does it replace my database roles?
No. Midplane is a second layer. It enforces policy on top of your database’s own permissions — it doesn’t replace role-based access control. If the connection string belongs to a privileged role, Midplane operates on top of those permissions. Give the agent a least-privilege database role as well. See the threat model.
Can an agent wipe or drop a table I marked read_write?
Can an agent wipe or drop a table I marked read_write?
No, not by default.
read_write grants row writes; whole-table destruction is a separate net. The dangerous_statement guardrails — on by default since 0.9.0 — deny a DELETE/UPDATE with no WHERE clause and any DROP/TRUNCATE/ALTER, regardless of table access, so granting a write can’t be escalated into DELETE FROM orders or DROP TABLE orders. They’re configurable per flag in the guardrails block if you really need to allow one.Which databases does it support?
Which databases does it support?
Postgres, since
0.1.0 — it’s the only dialect supported today. The dialect: key accepts only postgres. See the policy engine.What does it cost?
What does it cost?
Self-host is free — MIT open source, no hosted dependency. Midplane Cloud has a Free tier plus Pro and Team plans, gating on projects, MCP tokens, and seats rather than query volume. The policy engine, audit logging, and credential isolation are on every tier. See plans.
Is there telemetry?
Is there telemetry?
Self-host sends anonymous, aggregate telemetry by default — no SQL, no table or column names, no query results, no credentials. Disable it with
MIDPLANE_TELEMETRY=0 or DO_NOT_TRACK=1; inspect what’s sent with MIDPLANE_TELEMETRY=debug. See telemetry.Get help
If the fixes above don’t cover it:- Cloud
- Self-host & community
Email support@midplane.ai — also the Email support link in the dashboard sidebar. A human reads and answers.
Still stuck?
Denial reasons reference
Every denial message Midplane returns, and what to do about it.
How Midplane works
The parse → policy → audit → execute pipeline behind every decision.