# Connect an agent (MCP)

> Let Claude Code, Claude Desktop, claude.ai, Cursor, VS Code or any MCP client see your Kuiper dashboard and, if you allow it, deploy, restart and change configuration. It signs in with OAuth and you can disconnect it at any time.

Web page: https://kuiper.sh/docs/mcp/

Kuiper is an MCP server. Connect an agent to it and the agent sees what the dashboard shows: projects, environments, units, deployments, builds, logs, network logs, usage and activity. If you allow it, the agent can also deploy, restart, roll back, set variables and secrets, manage domains and environments, and create or revoke API tokens.

You don't paste a token anywhere. The agent signs in with OAuth: it opens the dashboard, you approve it for one organisation with read-only or read and write access, and it keeps itself signed in from then on.

## The server

| What | Value |
| --- | --- |
| Server URL | `https://app.kuiper.sh/mcp` |
| Transport | Streamable HTTP |
| Sign-in | OAuth 2.1 with dynamic client registration and PKCE |
| Protocol versions | 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05 |

The URL is also on the dashboard: choose **MCP** in the sidebar. If you run Kuiper yourself, use your dashboard's address followed by `/mcp`.

## Set up your client

The dashboard's **MCP** page has each of these with your server URL filled in and a copy button.

### Claude Code

```sh
claude mcp add --transport http kuiper https://app.kuiper.sh/mcp
```

Then run `/mcp` in Claude Code, select **kuiper** and choose **Authenticate**. Your browser opens the dashboard to approve it.

Add `--scope user` to use Kuiper in every project, or `--scope project` to share the server with your team in `.mcp.json`. Each person still approves their own connection.

### Claude Desktop and claude.ai

1. Open **Settings**, then **Connectors**, and choose **Add custom connector**.
2. Name it `Kuiper` and paste `https://app.kuiper.sh/mcp` as the URL.
3. Choose **Connect** and approve it in the dashboard.

On Team and Enterprise plans an owner adds the connector once for the organisation. Each person then connects it for themselves.

### Cursor

Add Kuiper to `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` for one:

```json mcp.json
{
  "mcpServers": {
    "kuiper": {
      "url": "https://app.kuiper.sh/mcp"
    }
  }
}
```

Then open **Settings**, then **MCP**, and choose **Connect** next to kuiper.

### VS Code

Add Kuiper to `.vscode/mcp.json` in your workspace, or run **MCP: Add Server…** from the command palette and choose HTTP:

```json .vscode/mcp.json
{
  "servers": {
    "kuiper": {
      "type": "http",
      "url": "https://app.kuiper.sh/mcp"
    }
  }
}
```

Start the server from the file or the **MCP Servers** list. VS Code asks you to sign in, and the dashboard opens to approve it.

### Other clients

Any client that can connect to a remote MCP server over Streamable HTTP with OAuth works. Give it the server URL. It finds the sign-in details at `/.well-known/oauth-protected-resource`, registers itself, sends you to the dashboard to approve, and refreshes its own access afterwards.

A client that can't do OAuth can't connect. The MCP server never takes API tokens.

## Approving an agent

When a client connects for the first time, your browser opens a Kuiper page that asks whether to allow it. Sign in first if you aren't already. The page shows:

- the name the client gave itself, such as `Claude Code`;
- where you will be sent back to: a website's host, `127.0.0.1 (this computer)` for a desktop tool, or an app's link;
- the organisation to connect, if you are in more than one;
- the access to give it.

Only approve a request you just started. If you don't recognise the name, choose **Deny**: the client is told no and gets nothing.

Approving needs a person, so sign in with GitHub or a passkey. A dashboard signed in with an API token can't approve. A request lasts ten minutes.

### Access

| Access | Scope | The agent can |
| --- | --- | --- |
| **Read only** (the default) | `kuiper:read` | Use the read tools: projects, environments, units, deployments, builds, logs, network logs, variables, domains, usage, activity, search and the list of API tokens. Secret values are never shown. |
| **Read and write** | `kuiper:read kuiper:write` | Everything above, and the write tools: deploy, restart, roll back, set and unset variables and secrets, add and remove domains, create and sync environments, and create and revoke API tokens. |

The agent acts as you, with your role in that organisation. A member's agent can do what a member can and no more, whatever access you give it. If your role changes, the agent's access changes with it.

A read-only agent doesn't see the write tools. If it asks for one anyway, Kuiper tells the client it needs `kuiper:write`. Some clients then offer to sign in again. To give an agent write access, approve it again and choose **Read and write**: the same connection is updated.

## Tools

Every tool takes project and unit names the way the CLI does: a slug, an ID or a unique name, and `slug@env` for another environment, as in `proxima@staging`. Long answers are cut short and say so, so they fit in an agent's context.

### Read

| Tool | What it does |
| --- | --- |
| `list_projects` | The organisation's projects with their environments, unit counts and how many units are running. |
| `get_project` | One project: its environments and every unit with status, address and live deployment. |
| `list_units` | A project's units with status, address, framework, region and live deployment. |
| `get_unit` | One unit in detail: placements, node, region, resources, health check, pending restart, live and latest deployment. |
| `list_deployments` | A unit's deployments, newest first, ten to a page. |
| `get_deployment` | One deployment: status, error, image, source, the variable names it started with, and where it runs. |
| `list_builds` | A unit's recent builds: status, branch, commit, error and the deployment each produced. |
| `get_build` | One build and the end of its log. `q` filters the log, as in `@level:error`. |
| `get_logs` | A unit's logs. `q` takes the [log filter language](https://kuiper.sh/docs/finding/#the-filter-language), `tail` up to 500 lines, `since` to read on from a time. |
| `get_network` | A unit's traffic summary, or recent HTTP, DNS and connection rows, filtered like the [Network page](https://kuiper.sh/docs/network.md). |
| `get_usage` | Without a project: the organisation's spend for the month (or a `month` you name) per resource, project and unit, with month to date and the projected month end. With a project: its reserved and used CPU and memory per unit and its spend. Members see quantities; owners and admins also see amounts. |
| `list_variables` | A unit's variables, the project's shared variables and its secret names. Never secret values. |
| `list_domains` | A project's domains with their DNS and certificate status and the records still needed. |
| `list_environments` | A project's environments with branch and unit status. |
| `diff_environment` | What differs between two environments. Its keys are what `sync_environment` takes. |
| `search` | Search the organisation or one project: units, deployments, domains, variables, secret names, databases and activity. |
| `get_activity` | A project's activity log, or with `kind: agents`, what its Foundry and Station agents did over some days. |
| `list_api_tokens` | The organisation's API tokens: name, who made it, when it was last used. Never the token. |

### Write

These need **Read and write** access. Each one is recorded in the project's activity as done by you through the client, for example `secret STRIPE_KEY set by alice via Claude Code (MCP)`.

| Tool | What it does |
| --- | --- |
| `deploy` | Build the unit's repository and deploy it, or redeploy the live image with `source: redeploy`. |
| `restart_unit` | Restart a unit on its live image, applying pending variable and secret changes. |
| `rollback` | Make an earlier deployment live again. |
| `set_variable` | Set a unit variable, or a shared variable when no unit is given. Values are [templates](https://kuiper.sh/docs/variables.md), like `${{secrets.API_KEY}}`. |
| `unset_variable` | Remove a unit or shared variable. |
| `set_secret` | Set a project secret. The value is encrypted and can't be read back, by the agent or anyone else. |
| `delete_secret` | Delete a secret that no variable uses. |
| `add_domain` | Add a custom domain to a unit and get the DNS records to create. |
| `verify_domain` | Check a domain's DNS now. |
| `remove_domain` | Remove a custom domain. |
| `create_environment` | Create an environment, such as staging, as a copy of another. |
| `sync_environment` | Bring chosen differences from one environment into another. |
| `create_api_token` | Create an organisation API token. The token is shown once, in the answer. |
| `revoke_api_token` | Revoke an API token. The last active one can't be revoked. |

Kuiper also offers each project as an MCP resource (`kuiper://projects/<slug>`) and a `diagnose_unit` prompt that walks an agent through a unit's status, logs and traffic.

## Seeing and revoking access

Choose **MCP** in the sidebar, or open **API tokens**, to see your connected agents: the client's name, the organisation, the access, when it was connected and when it was last used. Owners and admins also see everyone's agents in the organisation, and can revoke them.

**Revoke** disconnects an agent at once. Its next request fails and it has to be approved again. An agent also loses access when the person who approved it leaves the organisation.

From the client's side, removing the server (for example `claude mcp remove kuiper`) forgets it locally. Revoke it in the dashboard too, to be sure.

## Security

- **Short-lived tokens.** An access token lasts an hour. The refresh token that renews it lasts 30 days and works once: each use returns a new one. If an old refresh token is ever used again, Kuiper assumes it was copied and disconnects that agent.
- **Bound to one place.** A token works only for this Kuiper server's `/mcp`, only for the organisation and person who approved it, and only with the access they chose. Kuiper stores hashes of tokens, never the tokens.
- **PKCE and checked return addresses.** Sign-in uses the authorization code flow with PKCE (S256). Kuiper sends you back only to an address the client registered: an `https://` address, `http://` on your own computer (`127.0.0.1`, `localhost` or `[::1]`, any port), or an app's own link.
- **The approval page can't be embedded** in another site, and approving needs the dashboard's own request headers, so another site can't approve for you.
- **Secrets stay write-only.** No tool returns a secret value, a database password or an API token after it was created.
- **Think about what the agent reads.** Logs, network rows and build output are written by your apps and by whoever calls them. An agent that reads them can be misled by text planted there. Give write access to agents you supervise, and have them ask before destructive changes.
- **Write access is your access.** An agent with write access can do what you can, including creating API tokens that act for the whole organisation. Prefer read only unless the agent needs to act.

## Running Kuiper yourself

The MCP server is part of the control plane; there is nothing to install or switch on. After upgrading, the control plane adds its tables when it starts. Two things to check:

- `KUIPER_PUBLIC_URL` must be the address people open the dashboard at. It is the OAuth issuer and the start of the server URL. Hostnames in `KUIPER_API_ALIASES` work as well.
- The edge sends every path on the dashboard's hostname to the control plane, so `/mcp`, `/.well-known/…` and `/oauth/…` need no extra routes. If you put another proxy in front, pass those paths and the `Host` header through.
