# MCP Server for AI Agents

DanubeData runs an [MCP](https://modelcontextprotocol.io) server, so an AI agent can operate your infrastructure through typed tools instead of learning the REST API first. Nearly every operation of the [public API](https://docs.danubedata.ro/api-overview) is a tool: `vps_create`, `database_list`, `storage_buckets_create` and a couple of hundred more ([the exceptions](#how-calls-work) are file uploads and partner sub-accounts). The tools are generated from the same specification as the API reference, so a new API endpoint becomes a tool as soon as it ships.

Each tool call is a call to the REST API, made with your API token. An agent connected over MCP can do exactly what that token can do, and nothing more.

> **Early access.** The MCP server is being opened account by account. If `https://danubedata.ro/mcp` answers 404 for you, ask support to enable it.

- **Endpoint:** `https://danubedata.ro/mcp`
- **Transport:** Streamable HTTP
- **Authentication:** an API token, sent as `Authorization: Bearer YOUR_TOKEN`

## Set It Up from the Console

Open **Security**, **API tokens**. Once the MCP server is open to your account, the tab has an **AI agents** card. **Connect an agent** walks you through the setup: it creates a token for the agent, lets you choose the tools, and writes the configuration for Claude Code, Cursor, VS Code, Codex or OpenCode, ready to copy. The rest of this page is the same setup by hand.

## Before You Start

Create an API token for the agent on **Security**, **API tokens** (see [Authentication](https://docs.danubedata.ro/api-authentication)):

- Give the agent a token of its own, so you can see what it did and revoke it without touching anything else.
- Choose **This project only** unless the agent needs several projects.
- Tick only the permissions it needs. A token with `vps:read` but not `vps:write` can look at servers and cannot change them. Leave out the password permissions (`vps:credentials`, `database:credentials` and the like) unless the agent has to read passwords.

Keep the token in an environment variable, not in a file you commit. The Cursor, OpenCode, Codex and `.mcp.json` examples below read it from `DANUBEDATA_TOKEN`; VS Code asks for the token instead, and the `claude mcp add` command takes it in place of `YOUR_TOKEN`. Set the variable for your user account, then restart the client:

- **macOS and Linux:** add `export DANUBEDATA_TOKEN="your-token"` to your shell profile (`~/.zshrc` or `~/.bashrc`).
- **Windows:** run `setx DANUBEDATA_TOKEN "your-token"` in a terminal.

## Connect Your Client

### Claude Code

```bash
claude mcp add --transport http danubedata https://danubedata.ro/mcp \
  --header "Authorization: Bearer YOUR_TOKEN"
```

Claude Code keeps the header with the server. Add `--scope user` to use the server in every project, then type `/mcp` in Claude Code: `danubedata` should show **Connected**.

Or, in a project's `.mcp.json`, where `${DANUBEDATA_TOKEN}` is read from the environment when Claude Code starts:

```json
{
  "mcpServers": {
    "danubedata": {
      "type": "http",
      "url": "https://danubedata.ro/mcp",
      "headers": { "Authorization": "Bearer ${DANUBEDATA_TOKEN}" }
    }
  }
}
```

### Cursor

In `~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project:

```json
{
  "mcpServers": {
    "danubedata": {
      "url": "https://danubedata.ro/mcp",
      "headers": { "Authorization": "Bearer ${env:DANUBEDATA_TOKEN}" }
    }
  }
}
```

The console's **Connect an agent** also has an **Add to Cursor** link, which adds the same entry after Cursor asks you to confirm.

### VS Code

In `.vscode/mcp.json`. VS Code asks for the token the first time the server starts, and keeps it:

```json
{
  "inputs": [
    { "type": "promptString", "id": "danubedata-token", "description": "DanubeData API token", "password": true }
  ],
  "servers": {
    "danubedata": {
      "type": "http",
      "url": "https://danubedata.ro/mcp?toolsets=vps,database",
      "headers": { "Authorization": "Bearer ${input:danubedata-token}" }
    }
  }
}
```

VS Code sends at most 128 tools with a request, so choose [toolsets](#choose-the-tools) there.

### OpenCode

In `opencode.json`:

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "danubedata": {
      "type": "remote",
      "url": "https://danubedata.ro/mcp",
      "headers": { "Authorization": "Bearer {env:DANUBEDATA_TOKEN}" }
    }
  }
}
```

### Codex CLI

In `~/.codex/config.toml`:

```toml
[mcp_servers.danubedata]
url = "https://danubedata.ro/mcp"
bearer_token_env_var = "DANUBEDATA_TOKEN"
```

### Other Clients

Any MCP client that speaks Streamable HTTP and lets you set a request header works: point it at `https://danubedata.ro/mcp` and send `Authorization: Bearer YOUR_TOKEN`. A client that runs in a web page sends its site's `Origin` header, which the server refuses unless the site is DanubeData's own.

Clients that connect only through OAuth, such as custom connectors in claude.ai and ChatGPT, cannot connect yet: the server takes an API token.

## Choose the Tools

With no options, the server lists every tool your account can use. That is a lot of text for a model to carry: some clients (Claude Code among them) load tools only when they are needed, others send all of them with every request. Add `toolsets` to the URL to list only some groups:

```
https://danubedata.ro/mcp?toolsets=vps,database,snapshots
```

| Toolset | Covers |
|---|---|
| `vps` | VPS instances, block volumes, images and plans |
| `database` | Databases, their replicas and settings, parameter groups |
| `cache` | Redis, Valkey and Dragonfly instances |
| `queue` | RabbitMQ instances |
| `storage` | Buckets, bucket policies and access keys |
| `serverless` | Rapids containers, runs, schedules and volumes |
| `static_sites` | Static sites, builds and domains |
| `apps` | Managed apps |
| `kubernetes` | Managed Kubernetes clusters |
| `registry` | Container registry repositories and tags |
| `snapshots` | VPS, database and cache snapshots, recovery options |
| `firewalls` | Firewalls and their rules |
| `ssh_keys` | SSH keys |
| `monitoring` | Uptime checks, metric and resource alerts, notifications, webhooks |
| `account` | Your projects, account limits, approvals, operations, support tickets, search |

`toolsets=all` is the same as no option. A name the server does not know is refused with the list of valid ones. Products that are not open to your account do not appear, whatever you select.

## Read-Only Mode

Add `read_only=1` to list only the tools that read:

```
https://danubedata.ro/mcp?toolsets=vps,database&read_only=1
```

The server enforces it: a tool that changes something is not listed and cannot be called. Read-only still includes reading credentials if the token holds a password permission, so pair it with a token without those permissions when that matters.

## Choose a Project

A call acts on the project the token is locked to. An account-wide token acts on your current project; to pick another one, send its ID in an `X-Team-Id` header from the client configuration, as with the [REST API](https://docs.danubedata.ro/api-authentication#selecting-a-project-with-an-account-wide-token). The `user_teams` tool lists your projects.

## How Calls Work

- **Results.** A result starts with the HTTP status and the API path, such as `201 Created · POST /api/v1/ssh-keys`, followed by the API's JSON answer unchanged.
- **Errors.** A refused call comes back as an error result with the status, one line on what to do next, and the API's answer. A 422 names the fields to fix; a 403 means the token lacks the permission or the project does not allow it.
- **Background work.** Many changes answer `202 Accepted` and finish in the background. Agents should poll the resource or `operations_get` until it settles rather than repeat the change.
- **Retries.** Tools whose API route accepts an `Idempotency-Key` take an `idempotency_key` argument: send the same key when retrying a create, and the first answer is replayed instead of a second resource being made.
- **Rate limits.** Each tool call counts as one request against your project's [API rate limit](https://docs.danubedata.ro/api-rate-limits). The MCP endpoint itself accepts 300 requests a minute per token.
- **Long answers** are cut after about 80 KB, with a note; use the tool's paging or filter arguments for less.
- **Not available over MCP:** uploading a ZIP to deploy a container or a static site build (use the [CLI](https://docs.danubedata.ro/cli-overview)), and managing partner sub-accounts (use the [REST API](https://docs.danubedata.ro/api-overview)).

## Troubleshooting

| Symptom | Cause |
|---|---|
| `404` from `https://danubedata.ro/mcp` | The MCP server is not enabled for your account yet. Ask support. |
| `401` | The token is missing, mistyped, expired or revoked, or the header is not `Authorization: Bearer YOUR_TOKEN`. |
| `403` with "Requests from this origin may not use the MCP server" | The client runs in a web page and sent its site's `Origin` header. Connect from a client that runs outside the browser. |
| A tool call fails with `403` | The token lacks the permission for that call. Edit the token on **Security**, **API tokens**, or create one with it. |
| A tool call fails with `402` | The project needs a payment method before it can do this. Add one under **Billing**. |
| `429`, or a tool call fails with `429` | The token sent more than 300 requests a minute to the endpoint, or the calls used up your project's [API rate limit](https://docs.danubedata.ro/api-rate-limits). Wait, then retry. |
| `400` with "Unknown toolset" or "Unknown parameter" | A name in `toolsets` is misspelt, or the URL has a parameter other than `toolsets` and `read_only` (`readonly=1`, say). The error lists what is valid. |
| A product's tools are missing | The product is not open to your account, or its toolset is not in the URL. |
| The client warns about too many tools | Narrow the list with `toolsets`. |
