> ## Documentation Index
> Fetch the complete documentation index at: https://midplane.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Connect a database and get a protected MCP endpoint for your agent — hosted on Cloud, or self-hosted with one Docker command.

Midplane puts a safety layer between your agent and your Postgres database. Stand it up two ways: **Cloud** (hosted, fastest) or **self-host** (your own infrastructure, one Docker command). Either way, by the end you'll have a protected MCP endpoint.

<Tabs>
  <Tab title="Cloud">
    <Steps>
      <Step title="Sign up">
        Go to [app.midplane.ai](https://app.midplane.ai) and create an account with email and password, or Google.
      </Step>

      <Step title="Choose a region">
        Pick **EU** or **US** — it sets where your credentials and audit log live.

        <Warning>
          Your region is **immutable** — chosen once at sign-up, and every project inherits it. Pick the jurisdiction your data needs to stay in.
        </Warning>
      </Step>

      <Step title="Connect Postgres">
        Every account starts with an empty **Default** [project](/docs/cloud/projects) — an MCP endpoint fronting one or more databases. Click **Connect Postgres** and paste a connection string. Give Midplane a scoped, least-privilege role, not a superuser DSN. Pick a **default access level** and leave it at `read` to stay read-only; grant writes per table later.

        No database handy? Click **Try the sample database** to load a hosted, read-only demo Postgres in one click and watch an agent query through Midplane.
      </Step>

      <Step title="Connect your agent">
        Your project has a stable HTTPS endpoint, `https://{eu|us}.midplane.ai/mcp`. Paste it into your agent's MCP config — you connect over **tokenless OAuth** (register, sign in once, no secret to copy), and Cursor, Claude Code, and Claude Desktop all connect the standard way. See [connect your agent](/docs/agents/overview).

        <Note>
          For CI or headless callers, mint a **machine token** instead — that token *is* a secret, shown only once. See [tokens](/docs/cloud/projects#machine-tokens).
        </Note>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Self-host">
    **Prerequisite: Docker** (with Compose v2) — the only one.

    The fastest way is to let the coding agent on this machine boot it. Paste this — it runs the two commands, then hands the security-sensitive choices (the secret backup, the database role) back to you rather than deciding them:

    ```text Setup prompt theme={null}
    Set up a local Midplane self-host deployment on this machine.

    1. Clone https://github.com/midplaneai/midplane, then from the repo root run
       `./bin/self-host up`. Show me the output and the dashboard URL it prints —
       normally http://localhost:3000, or the next free port if 3000 is busy (the
       script auto-selects one and keeps BETTER_AUTH_URL matching). If the bundled
       Postgres port 5432 is taken, set POSTGRES_PORT to a free port in the
       generated .env.self-host and re-run.
    2. The command generates `.env.self-host` with fresh secrets. Don't edit,
       rotate, or regenerate any value in it. Once the stack is up, tell me to back
       that file up somewhere safe: its KMS key and token pepper encrypt every
       stored credential, so losing or changing them is unrecoverable.
    3. Then stop and hand back to me — I'll open http://localhost:3000, sign up
       (the first email becomes the owner), and connect my Postgres with a scoped,
       least-privilege role. Don't enter database credentials for me.
    ```

    Prefer to run the commands yourself? Here's each step:

    <Steps>
      <Step title="Clone and run">
        ```bash theme={null}
        git clone https://github.com/midplaneai/midplane && cd midplane
        ./bin/self-host up            # → dashboard at http://localhost:3000
        ```

        One command creates `.env.self-host` with generated secrets, pulls the image, and brings up Postgres and the dashboard. `MIDPLANE_SELF_HOST=1` is preset; see the [deploy guide](/docs/self-host/deploy) for every variable and run method.

        <Warning>
          **Back up `.env.self-host` and never rotate its secrets once data exists** — the KMS key encrypts project DSNs and the token pepper backs machine tokens, so regenerating either bricks every stored credential. The script never regenerates a value that's already set.
        </Warning>
      </Step>

      <Step title="Sign up and connect a project">
        Open `http://localhost:3000` and **sign up once** — the first email + password becomes the owner. Then **connect a Postgres database** to create your first [project](/docs/cloud/projects). Give it a scoped, least-privilege role; Midplane encrypts the DSN with your local key. Pick a starting [table access](/docs/concepts/table-access) level (`read` by default).

        <Note>
          Inside the container, `localhost` is the container. For a Postgres on your host, use `host.docker.internal` (Docker Desktop) in the DSN, or put the database on the same Docker network.
        </Note>
      </Step>

      <Step title="Connect your agent">
        Point your agent at the project's `/mcp` endpoint on `http://localhost:3000`, over the dashboard's [OAuth flow](/docs/agents/overview) — or mint a machine token for headless callers. See [connect your agent](/docs/agents/overview).

        <Note>
          Claude Desktop rejects `http://localhost` (it requires HTTPS) — use the `mcp-remote` shim, shown in the per-agent guide. Cursor and Claude Code connect to the localhost URL directly.
        </Note>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Try it

Ask your agent to **list the tables** in your database — an allowed read that goes straight through.

Now ask it to **change a single row** in one of those tables. The agent issues a bounded `UPDATE … WHERE`, and Midplane denies it before it runs: under the [`table_access`](/docs/concepts/table-access) rule, writes deny by default — even a one-row, surgical change — until you grant `read_write` on that table. Nothing is modified, and the attempt is audited.

Open the **audit log** in the dashboard: both events are there — the allowed read and the denied write, each tagged with the rule that decided it, and the denial recorded **before** the query would have run. See [the audit trail](/docs/concepts/audit-trail).

## Next steps

<Columns cols={3}>
  <Card title="Connect your agent" icon="plug" href="/docs/agents/overview">
    Verified MCP configs for Cursor, Claude Code, and Claude Desktop.
  </Card>

  <Card title="Write a policy" icon="shield-check" href="/docs/policies/overview">
    Grant the reads and writes your agent actually needs.
  </Card>

  <Card title="Cloud vs self-host" icon="scale-balanced" href="/docs/cloud-vs-self-host">
    Which to run, and how they compare.
  </Card>
</Columns>
