> ## 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.

# Projects & access

> A Cloud project is one hosted MCP endpoint fronting your databases.

A **project** is one hosted MCP endpoint that fronts one or more databases. Your agent connects to the project's endpoint; Midplane proxies each call to the right database under that database's [policy](/docs/how-it-works). This page covers the whole access surface in the dashboard: projects and databases, the agents and machine tokens that connect to them, and the team roles that decide who can manage what.

## Connect Postgres

From the dashboard, choose **Connect Postgres**. You provide:

* **A Postgres URL** — a `postgres://` or `postgresql://` connection string. Midplane encrypts it with your region's [KMS key](/docs/cloud/security) and never persists the plaintext.
* **A database name** — the agent-facing alias for this first database (the value the agent passes as `database:`). Leave it blank to derive the alias from the DSN. Must be 1–32 lowercase letters, digits, `_`, or `-`, starting with a letter.
* **A default access level** — the starting [table access](/docs/concepts/table-access) for this database. Defaults to `read`.

When you connect, Midplane attaches the database to a project, shows you the project's MCP endpoint, and mints one **default machine token** (shown once). The fastest way to wire an agent is the tokenless [OAuth flow](/docs/agents/overview) — no secret to copy.

<Warning>
  Always paste a database role with the least privilege your agent needs. Midplane enforces policy on top of your database's own permissions — it's the second layer, not a replacement for a scoped database role.
</Warning>

<Tip>
  Every new account starts with an empty **Default** project. Connecting your first database fills it in. With a single project the dashboard takes you straight into it; the project list appears once you have more than one.
</Tip>

## The dashboard

The projects list shows each project's **databases**, **active agents**, and **last query** time; the stats deep-link into the matching pane, and a project with no connected agent shows a **connect an agent** nudge instead of a dead "0". Click a card to open the project workspace, where everything for that project lives in one place:

* **Database** — the per-database [policy](/docs/policies/overview) (table access and guardrails), its [test panel](/docs/policies/overview#test-before-you-ship), and the credential, with a strip to switch between or add databases.
* **Exposure** — scan a database for likely PII and [mask sensitive columns](/docs/concepts/masking).
* **Agents** — connect an agent over [OAuth](/docs/agents/overview) or mint a [machine token](#machine-tokens); see connected agents and revoke them.
* **Settings** — rename, [pause](#pause-a-project), or delete the project.

Each project shows a **status** — **Ready**, **Paused**, or **Action needed** — that answers whether it will serve an MCP query right now. Because the engine spawns on demand, a connected, unpaused project reads **Ready** even while its engine is idle. Open the status for a **Test connection** check (a read-only `SELECT 1` that wakes the engine and confirms it answers) and the **audit log** freshness — the time of the last audit event that reached the dashboard, so you can confirm rows are landing.

## Manage databases on a project

A project starts with one database but can front several. Each has its own agent-facing **name** (`main`, `analytics`, …), its own encrypted credential, and its own [table access](/docs/concepts/table-access) and [column masking](/docs/concepts/masking) policy. All database management lives in the workspace's **Database** tab: **add** a database from the strip (name, Postgres URL, default access level — the name must be unique on the project and follow the 1–32-char rule), rename it (the agent's alias changes with it), or remove it. **The last database on a project can't be removed** — add another first, or delete the whole project from **Settings**. A project holds up to **10 databases** on Cloud (unlimited on self-host) — a fixed ceiling, identical on every plan, since a project's databases share one engine, so adding one carries no compute cost. At the ceiling the dashboard points you to a [second project](#one-project-or-several), not an upgrade — upgrading wouldn't raise it.

### One project, or several?

Keep databases together in one project when an agent should reach them as a set under a single endpoint and you want to govern them together — an app database alongside a separate analytics or reporting database, say. Split into a **separate project** when they should stay isolated: a project is the isolation boundary, with its own [MCP endpoint](#find-your-endpoint-url), [policy](/docs/policies/overview), [machine tokens](#machine-tokens), per-agent [consent scope](#per-agent-database-scope), and [audit stream](/docs/concepts/audit-trail). Different environments are the clearest case — you generally don't want `prod` and `staging` behind one endpoint and one policy surface.

## Rotate a database credential

To swap the credential behind a database — after a password rotation, or when moving to a new role — open the database's credential pane and paste a new Postgres URL under **Rotate connection string**.

* The new DSN is re-encrypted under your region's KMS key, replacing the old ciphertext.
* **The MCP endpoint URL stays the same** — your agent's config doesn't change.
* In-flight sessions are torn down so the new credentials take effect on the next agent request.

The pane shows when the credential was last rotated. You can run a **reachability test** on a saved or new DSN before committing it.

## Pause a project

Pausing is a reversible kill switch between "revoke every agent" and "delete the project." It **rejects agent requests** with a distinct "project paused by owner" error and drops any in-flight session, while the agents, the URL, and the policy stay intact. Resume restores service on the next request.

* Pause or resume from the project header or the workspace **Settings** tab. Pause asks for confirmation; resume is one click.
* A paused project shows an amber **Paused** state everywhere its status appears.
* Use it to stop an agent immediately during an incident without throwing away its credentials or policy.

## Find your endpoint URL

A project's MCP endpoint has the form:

```
https://{eu|us}.midplane.ai/mcp
```

with `eu` or `us` depending on your [region](/docs/cloud/overview#regions-and-data-residency). This is the tokenless **OAuth** endpoint — an address, not a secret, so it's safe to copy and share. To target a specific project from a client that reaches several, use `https://{eu|us}.midplane.ai/mcp/<projectId>` (the project id isn't a secret either). For headless callers that can't sign in through a browser, mint a [machine token](#machine-tokens) and present it as a `Bearer` credential. See [connect your agent](/docs/agents/overview).

## Delete a project

Deleting a project (from its **Settings**) stops the MCP endpoint, removes the encrypted credential rows, and revokes every agent on it. Audit history stays in the dashboard for your retention window, for compliance.

## Machine tokens

The default way to connect is tokenless [OAuth](/docs/agents/overview). Reach for a **machine token** only for headless callers — CI jobs, scheduled tasks, services — that can't sign in through a browser. A machine token is a prefixed, checksummed secret minted per agent against a project, so you can tell live from test at a glance:

* `mp_live_…` — a production token.
* `mp_test_…` — a staging or dev token.

The prefix and a short suffix show in the dashboard list (for example `mp_live_…_a3f2`) so you can recognize a token without the full secret ever being stored. Present it to the project's MCP endpoint as an `Authorization: Bearer mp_live_…` header (preferred — keeps the secret out of the URL), or for clients that can't set a header, in the path: `https://{eu|us}.midplane.ai/mcp/<token>`.

### Mint a token

From the project's **Agents** section, choose **Create a machine token**. You set:

* **A name** — label it for the CI job or service it's for.
* **An expiry** — `30 days`, `90 days`, `365 days`, or **never expires**.
* **Scope** — which of the project's databases the token can reach, and `read` or `read_write` for each. Every database defaults to `read` — see [per-agent scope](#per-agent-database-scope).

<Warning>
  **The plaintext is shown once.** When you mint a token, the full secret appears a single time — copy it immediately. Midplane stores only an HMAC digest, never the plaintext. If you lose it, revoke the token and mint a new one.
</Warning>

### Revoke a token

Revoke a token from its row in the project's **Agents** list. Revocation is **immediate** — the next request that token makes is rejected. A token also reaches end-of-life on its own: past its expiry it stops working and the dashboard marks it expired. Deleting the whole project revokes all of its tokens at once.

<Note>
  The **Agents** list shows *every* connected agent — interactive [OAuth](/docs/agents/overview) clients (Cursor, Claude, ChatGPT) as well as machine tokens — each with the databases it can reach and a status badge. Revoke works the same for both. OAuth agents never expire, so they only ever read as active, stale, or revoked.
</Note>

### What's tracked, and why one token per agent

For each token the dashboard tracks **created by** and **created at**, **scope**, **expiry**, **last used** (time and source IP), and **status** (active, expiring soon, stale, revoked, or expired). Every audit row also carries the ID of the token that issued the query, so the [audit trail](/docs/concepts/audit-trail) can answer "what did this token do?" — and you can filter the audit list by token. That's why per-agent tokens matter: one token per agent means one audit stream per agent, and cutting off one agent never disturbs the others.

Your plan caps how many active tokens you can hold; the dashboard warns you **before** you hit the cap rather than failing on submit. See [plans](/docs/cloud/plans) for the current limits.

## Team roles

A Cloud account has one team, and every teammate holds one role that decides what the dashboard lets them do.

* **Owner** — full control, including billing. Exactly one owner per account; ownership isn't transferable in the dashboard today.
* **Admin** — manages projects, databases, policies, tokens, and members — everything except billing and ownership.
* **Member** — connects their own agent over OAuth and runs queries within policy. Sensitive management is reserved for owners and admins, but a member is never stranded: they consent once, get a scoped credential, and query without an admin in the loop.

| Capability                         | Member | Admin | Owner |
| ---------------------------------- | :----: | :---: | :---: |
| Connect an agent (OAuth)           |    ✓   |   ✓   |   ✓   |
| Run queries                        |    ✓   |   ✓   |   ✓   |
| View audit log                     |    —   |   ✓   |   ✓   |
| Mint & revoke tokens               |    —   |   ✓   |   ✓   |
| Create & edit projects & databases |    —   |   ✓   |   ✓   |
| Edit policy & masking              |    —   |   ✓   |   ✓   |
| Manage members & invites           |    —   |   ✓   |   ✓   |
| Manage billing                     |    —   |   —   |   ✓   |

### Invite a teammate

<Tabs>
  <Tab title="Cloud">
    An owner or admin invites a teammate by email from **Settings → Members**, assigning **Admin** or **Member** on the invite. Midplane sends the email and also gives you a copyable invite link as a fallback. Invites respect your plan's seat cap — on the [Free plan](/docs/cloud/plans) the owner is the only seat, so inviting prompts an upgrade — and invite links expire after 7 days.
  </Tab>

  <Tab title="Self-host">
    Self-host has no email delivery, so the owner creates an invite and shares the copyable link out of band. Self-host is uncapped: there's no seat limit. See the [quickstart](/docs/quickstart) to stand up the engine.
  </Tab>
</Tabs>

### Per-agent database scope

Roles decide what a *teammate* can do in the dashboard. A second, independent layer decides what each *agent credential* can reach — least privilege by default. When an agent connects over OAuth you consent once and choose which of the project's databases it can reach and whether each is `read` or `read_write`; a [machine token](#machine-tokens) is scoped the same way at creation, every database defaulting to `read`. An OAuth agent with no grant is **denied** — the scope fails closed.

<Note>
  Per-agent scope is layered on top of the project's [policy](/docs/cloud/security), not a replacement for it. The policy is the ceiling for every agent on the project; the per-agent grant narrows one credential below that ceiling. See [table access](/docs/concepts/table-access) for the `read` / `read_write` model and [connect your agent](/docs/agents/overview) for the OAuth flow.
</Note>

### Manage your account

Your **account** is your personal sign-in, separate from workspace settings. Open it from the account menu to see your profile, **change your password** (only if you signed in with email and password), and **sign out**. Deleting your account depends on your role:

* **Sole owner** — deletes the whole workspace and everything in it: projects, databases, tokens, and audit history. This can't be undone; cancel any active subscription in [Billing](/docs/cloud/plans) first.
* **Owner with other members** — you can't delete until the workspace has no other members, so it's never left ownerless.
* **Admin or member** — deleting removes you from the workspace and erases your sign-in; the workspace and its data stay.

Self-host has a single implicit owner, so the self-hosted account page omits the delete / leave controls.

<Columns cols={2}>
  <Card title="Connect your agent" icon="plug" href="/docs/agents/overview" horizontal>
    Wire an MCP client over OAuth, or a headless caller with a token.
  </Card>

  <Card title="Plans & seats" icon="users" href="/docs/cloud/plans" horizontal>
    How seats, projects, and tokens are capped on Free, Pro, and Team.
  </Card>
</Columns>
