# POST /v1/mcp-servers

> Register one: URL, auth (none, or headers using ${secret:NAME} or ${user_secret:NAME}), and its tool policy floor: tools never run and tools that always need approval, for every deployment in both modes.

In [MCP servers and secrets API](https://vestra.ai/developers/reference/api/mcp-servers-and-secrets.md): Your MCP servers and the tools they offer, and the secrets Bash uses but never shows.

Send `Authorization: Bearer vk_test_...` on every call.

Register one: URL, auth (none, or headers using ${secret:NAME} or ${user_secret:NAME}), and its tool policy floor: tools never run and tools that always need approval, for every deployment in both modes. A deployment version's own tool policy adds to the floor and never removes from it.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in every value.

## Request

Headers:

- `Idempotency-Key` (`string`): Send the same key to retry this call safely for 24 hours.

Body:

- `name` (`string`, required): Its name, as people read it, 1 to 80 characters. Limits: 1 to 80 characters. Example: `Travel desk`.
- `slug` (`string`, required): The name Bash calls it by: lowercase letters, digits and hyphens, starting with a letter, up to 40. Example: `travel-desk`.
- `url` (`string (URL)`, required): The server's public https URL, up to 2,048 characters. Limits: at most 2048 characters. Example: `https://mcp.example.com/travel`.
- `auth` (`object`): How Bash signs in to it. Default `{ "type": "none" }`. Default: `{"type":"none"}`. One of: `{ type }`, `{ type, headers }`, `{ type }`.
- `tools` (`object`): Which of its tools Bash never calls (`deny`), and which wait for the person's yes (`require_approval`), in every deployment that uses the server. A deployment may add to either list, never take from it. Default: no rule. Default: `{"deny":[],"require_approval":[]}`.
  - `deny` (`string[]`): Tools of the server Bash never calls, by name, at most 200. Every deployment that names the server runs under it. Default: `[]`. Limits: at most 200 items.
  - `require_approval` (`string[]`): Tools of the server that always wait for the person's yes, by name, at most 200. Default: `[]`. Limits: at most 200 items.

## Answers 201

- `object` (`"mcp_server"`): Always `mcp_server`.
- `id` (`string`): The MCP server's id. Example: `mcp_8f2k`.
- `slug` (`string`): The name Bash calls it by.
- `name` (`string`): Its name, as people read it.
- `url` (`string`): The server's https URL.
- `auth` (`object`): How Bash signs in to it.
  - `type` (`"none" | "headers"`): `none`, or `headers` sent with every call.
  - `headers` (`map of string`, may be absent): The headers, as written: a credential shows as its `${secret:NAME}`, never its value.
- `tools` (`object`): Which of its tools Bash never calls (`deny`), and which wait for the person's yes (`require_approval`), in every deployment that uses the server. A deployment may add to either list, never take from it.
  - `deny` (`string[]`): Tools of the server Bash never calls, by name, at most 200. Every deployment that names the server runs under it.
  - `require_approval` (`string[]`): Tools of the server that always wait for the person's yes, by name, at most 200.
- `secrets` (`string[]`): The company secrets it needs (`${secret:NAME}`), by name: store each with PUT /v1/secrets/{name}.
- `user_secrets` (`string[]`): The secrets each user needs (`${user_secret:NAME}`), by name: store each user's with PUT /v1/secrets/{name} and their `user_id`.
- `created_at` (`string`): When it was made, as an ISO 8601 date and time in UTC.
- `updated_at` (`string`): When it last changed, as an ISO 8601 date and time in UTC.

## Code

**HTTP**

```bash
curl -X POST "$VESTRA_BASE_URL/v1/mcp-servers" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Travel desk",
    "slug": "travel-desk",
    "url": "https://mcp.example.com/travel"
  }'
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.
