MCP servers
isolance runs your MCP server in a sandbox and serves it at an authenticated endpoint, https://mcp.caa.sh/<server-id>. MCP clients connect with a client token. For each request, the gateway checks the token and the tools it may call, records a trace, and passes the request to your server. The server stops when it's idle and starts again on the next request.
A server serves MCP over Streamable HTTP on a port of its sandbox. It can run:
A command on the Default image (Alpine Linux with bash, coreutils, Node and Docker).
Your own image from
registry.caas.sh, with its own entrypoint or a command you give.
The server's sandbox is an ordinary sandbox of your workspace: you manage its size, outbound hosts, labels, files and snapshots like any other (see Sandboxes), and it carries the label caas.sh/mcp-server with the server's ID, which a connector's Reaches can name to give the server an API key without it ever holding the key. You can also add a server to a sandbox you already have.
Sandboxes run on a prepaid balance, so add funds before you deploy.
Deploy from the console
Open MCP servers and choose Deploy server.
Under 1. Choose an image, choose Default or Your image.
With Default, enter the server's Command, which serves MCP over HTTP on the port you give below.
With Your image, enter the image's digest reference,
registry.caas.sh/<tenant-id>/my-server@sha256:…, with the digest thatdocker pushprints. Command is optional. It replaces the image's entrypoint and command.Enter the Port the server listens on for MCP over HTTP.
Under 2. Configure, you can enter a Name: 1 to 63 lowercase letters, digits and hyphens, unique among the workspace's servers. Its sandbox gets the same name when it's free (see Names). Without one, the server is named
mcp-and 12 hex digits. Choose a Size. The default is M (1/2 vCPU, 4 GiB of memory, 8 GB of disk), and a server on your own image needs M or larger.In Allowed outbound hosts, list the hosts the server calls, one per line, or
*for every host. Everything else is blocked, and an empty list reaches no host.Leave Keep state between restarts checked to keep
/workspacebetween restarts.Choose Deploy server.
The server's page opens with its Initial client token. Copy it now, because it isn't shown again. This token can call every tool and never expires. To give each client its own token, see Client tokens.
Deploy with the CLI
First set up the caas CLI with an API key, as The CLI describes. caas mcp create creates a server in a new sandbox of its own.
Your own image
Push the image to your workspace's namespace of registry.caas.sh first, as Images and the registry describes. --image takes a name with a tag or a digest, or default for the Default image. The CLI resolves a tag to its digest and deploys that digest.
Give the port the server listens on with --http. The gateway sends requests to the path /mcp on that port, or to --path.
caas mcp create --image server:1.2.3 --http 3000 --command "node http.js"Without --egress, the server's sandbox reaches every host. To allow only the hosts it calls, pass --egress once per host.
The port must be from 1024 to 65535; it needn't be one of the sandbox's ports, which the gateway doesn't use. Listing it with --port as well also gives it its provider's own URL, where the provider's access control applies and client tokens don't guard it. --command replaces the image's entrypoint and command. Without it, the server runs the image's own entrypoint and command. A server on your own image needs size M or larger.
In a sandbox you already have
--sandbox adds a server to an existing sandbox of the workspace, by its ID or name, which runs it itself:
caas mcp create --sandbox web --http 3000 --path /mcp --name web-toolsThe sandbox keeps its own configuration, so the sandbox options below don't apply. A sandbox hosts at most one server. Deleting such a server leaves the sandbox.
Options
--name NAME names the server: 1 to 63 lowercase letters, digits and hyphens, unique among the workspace's servers. A new sandbox takes the sandbox options of caas sandboxes create (see Fields), such as:
| Option | What it does |
|---|---|
--size SIZE | xs, s, m (the default), l, xl, 2xl, or custom with --vcpu, --memory and --disk. |
--egress HOST | A host the server may reach. Repeat it for each host. |
--env NAME=VALUE | A variable for the server. |
--idle-stop MINUTES | Stop after this many minutes without activity, from 5 to 240. The default is 15. |
--no-volumes | Keep nothing in /workspace between restarts. |
The CLI prints the endpoint, the sandbox the server runs in, and the secret of each token it minted: Initial client, unless a server file declares its own. This is the only time a secret is shown.
From a server file
One YAML or JSON file can declare a whole server, client tokens included:
server: search-mcp
source:
image: search-mcp:1
port: 8080
size: m
state:
persistent: true
idle: stop after 30m
tokens:
- name: laptop-claude
tools: [search.query, search.fetch]
- name: ci-pipeline
tools: [search.query]
expires: 30d
egress: [api.search.example.com]caas mcp create -f search-mcp.yaml| Field | Value |
|---|---|
server | Required. The server's name, unique in the workspace. |
source | image: IMAGE, an optional command, and the port the server serves MCP over HTTP on. |
size | A size name, xs to 2xl, or "<vCPU> vCPU · <GiB> GiB · <GB> GB". isolance rounds a written size up to one it can run, and the CLI prints a note when it does. |
state | persistent: true (the default) or false. |
idle | How long the server runs idle before it stops, such as stop after 30m or stop after 2h: 5 minutes to 4 hours. |
tokens | Up to 20 client tokens. Each has a name, optional tools (every tool when left out) and an optional expires from 1d to 365d (never when left out). |
egress | The hosts the server may reach: ["*"] for every host, [] for none. Without it, the server reaches every host. |
A file that starts with { is read as JSON with the same fields. The YAML can't use tabs, anchors, block scalars or more than one document.
The CLI checks the whole file before it sends anything. Then it creates the server and its tokens in one request, and prints the endpoint and each token's secret, once. You can add sandbox options the file doesn't declare, such as --label and --env.
In the console, Deploy from a server file on the Deploy a server page takes the same file, pasted or chosen. There, an image must be a digest reference; deploy a file that names a tag with the CLI, which pins it.
Connect a client
You can find the endpoint URL under Endpoint on the server's page, in the CLI's output (caas mcp get <server>), and as url in the API's view of the server. The ID in it is opaque: it doesn't reveal your workspace.
Clients connect over Streamable HTTP and send a client token as a bearer token:
Authorization: Bearer cmcp_…The server's page shows a configuration to add to an MCP client:
{
"mcpServers": {
"search-mcp": {
"url": "https://mcp.caa.sh/<server-id>",
"headers": { "Authorization": "Bearer $CAAS_MCP_TOKEN" }
}
}
}Put the client token in place of $CAAS_MCP_TOKEN, unless your client fills in environment variables there. In Claude Code, for example:
claude mcp add --transport http search-mcp https://mcp.caa.sh/<server-id> --header "Authorization: Bearer <client-token>"To check the endpoint from a terminal, send an initialize request:
curl -i https://mcp.caa.sh/<server-id> \
-H "Authorization: Bearer $CAAS_MCP_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'If the server is stopped, the first request starts it and can take a while, as Idle stop and wake explains.
The gateway applies these limits:
A request body can be up to 4 MiB.
Each endpoint takes about 1,200 requests a minute. Beyond that, the gateway answers
429.The gateway waits up to 100 seconds for your server to start answering a request. After that, the client gets
504.A request that sends an
Originheader other thanhttps://mcp.caa.shgets403, so a web page on another site can't call the endpoint from a browser. MCP clients that aren't browsers send noOrigin.
Client tokens
A client token gives access to one server's endpoint and nothing else. Its secret starts with cmcp_ and is shown only once, when the token is created. isolance stores only a hash of it.
A deploy from the console's form, or with
--imageor--sandbox, creates one token, Initial client. It can call every tool and never expires.A deploy from a server file creates the tokens its
tokenslist declares instead, or Initial client if the list is empty or missing.
Give each client its own token. Each client's calls then show under its name, and you can revoke one client's token without affecting the others.
To create a token in the console:
On the server's page, under Client tokens, choose New token.
Enter a Client name. Under Allowed tools, list the tools it may call, one per line, or leave it empty to allow every tool.
Choose Create token, and copy the token. It expires in 90 days.
With the CLI:
caas mcp tokens create search-mcp --name "Claude Desktop"
caas mcp tokens create search-mcp --name ci-pipeline --expires-days 30 --allow-tool search.query
caas mcp tokens list search-mcp
caas mcp tokens revoke search-mcp <token-id>Each command takes the server's name or ID. --expires-days takes 1 to 365, and the default is 90. tokens list shows each token's ID, name, state (active, expired or revoked), allowed tools, and counts of its calls and errors. On the server's page, the Client tokens list shows when each token was created and last used, and how many requests it made.
Revoking a token takes effect at once: its next request gets 401. It doesn't stop the server or affect other tokens. To revoke a token in the console, choose Revoke beside it.
A server can have up to 20 tokens in force at once. Revoked tokens stay listed for 30 days.
With the API, a token can expire up to a year ahead, or never:
curl https://api.caas.sh/v1/mcp-servers/search-mcp/tokens \
-H "Authorization: Bearer <api-key>" \
-H "Content-Type: application/json" \
-d '{"label": "ci-pipeline", "allowedTools": ["search.query"]}'The answer is the token with its secret. expiresAt takes a time in milliseconds since the Unix epoch, from a minute to a year ahead. Without it, the token never expires. A GET to the same path lists the tokens, and DELETE …/tokens/<token-id> revokes one.
Tool grants
A token can be limited to the tools you name: up to 32 exact, case-sensitive tool names. A limited token:
Can call only those tools with
tools/call. A call to any other tool gets403, and its trace reads Tool not granted.Sees only those tools in
tools/list.Can still use resources, prompts and the other methods that aren't tool calls.
Can't use the
tasks/methods.
A token without a list can call every tool. In a server file or the API, an empty list allows no tool calls. The gateway checks a grant before a request reaches your server or wakes it. This works the same for every kind of server, your own image included.
You can't change a token's grant. Instead, create a token with the grant you want, then revoke the old one.
Traces and health
On a server's page, Request traces lists its newest requests. Each one shows:
the time
the tool called, or the MCP method
the caller: the name of the client token
the duration, highlighted at 1 second or more
the result: OK, Error with the HTTP status, Tool not granted, Timed out, or Blocked at gateway
Blocked at gateway is a request refused for its token: it had no token, an invalid one, or an expired or revoked one. Its caller reads no token or invalid token, or names the expired or revoked token. During a burst of refused requests, only a sample of them is traced, but the blocked count includes them all. Traces record the names of methods and tools, never their arguments or results. caas mcp traces <server> prints the newest 100, and caas mcp stats <server> the last 24 hours.
MCP servers shows the last 24 hours across all your servers: requests, requests blocked at the gateway, and the cost of the gateway requests. For each server it shows requests, median latency, and the share of requests that failed with a 5xx status.
A running server's status is Healthy, or Slow while its latency is up. A server is slow when both of these hold:
Its median latency over the last hour is at least twice the median of the day before, and at least 500 ms.
The last hour and the day before each had at least 20 requests.
The server's page then names the tool, or the method, with the most slow calls.
For what the server process itself prints, see Runtime output on its page, or run caas sandboxes logs <sandbox> with its sandbox's name.
Idle stop and wake
By default, a server's sandbox stops after 15 minutes without activity. You can set from 5 minutes to 4 hours, with --idle-stop, the server file's idle, or caas sandboxes update <sandbox> --idle-stop. Each request that passes the gateway's checks and reaches your server resets the timer. A response that's still streaming keeps the server running until the response ends. While the server is stopped, it bills no vCPU, memory or disk, only the storage of its snapshots.
The next message a client sends starts the server again:
The request waits up to a minute for the sandbox to start. If it's still starting after that, the client gets
503withRetry-After: 5, and should retry.Requests refused for their token never start the server, and neither do calls to tools the token isn't granted.
If the server's process exits, the next request starts it again. A server process runs for a day at most, and the next request after that starts it again too.
After a failed start, requests get
503withRetry-Afterfor 1 minute, or for 4 minutes after a second failure in a row. After three failed starts in a row, they get409until you start the server yourself.
A stopped server doesn't keep processes or sessions. If you kept state, /workspace is restored, but clients start new MCP sessions.
If you stop a server yourself, requests don't start it again. They get 409 until you start it. You can stop, start or delete a server from its row's menu in MCP servers. With the CLI, stop and start its sandbox, and delete the server:
caas sandboxes stop search-mcp
caas sandboxes start search-mcp
caas mcp delete search-mcp
caas mcp delete search-mcp --keep-sandboxDeleting a server ends its endpoint and tokens, and deletes the sandbox its create made, unless --keep-sandbox. A server added to an existing sandbox always leaves the sandbox. Deleting the sandbox deletes its server.
Servers also stop when the workspace's balance reaches $0, and stay stopped until funds arrive. See Billing and usage.
Outbound access
A server reaches only the hosts its sandbox's outbound hosts allow. You set them when you deploy, and you can change them later with Edit on the server page's Outbound policy card, which opens its sandbox's network page. While a fresh workspace sets up, cloning a repository and installing package.json dependencies, the server can also reach the repository's host and registry.npmjs.org. A command that runs npx needs registry.npmjs.org among the hosts itself. To call an API that needs a key, don't put the key in the server. Instead, add the API as a connector whose Reaches picks the server's sandbox (caas.sh/mcp-server=<server ID>, or a label of your own), and isolance adds the key to the server's outbound requests.
isolance's own servers in your sandboxes
The AI tools in your sandboxes can also call MCP servers you don't deploy: each connector's that has one, such as GitHub's or Linear's (see What a sandbox sees), and isolance's own, browser for the Browser connector, whose tools drive a web browser signed in with the sandbox's user's logins. They take no client token: isolance adds the sandbox's access to each on its way out, so nothing in the sandbox holds a key, and they serve only your sandboxes, never clients elsewhere.
Change a server
caas mcp update renames a server, or changes the port and path it serves on.
caas mcp update search-mcp --name search
caas mcp update web-tools --http 3001 --path /rpcA server's port and path apply at the next request. A server's sandbox can't change. With the API, PATCH /v1/mcp-servers/<server> with { "name" } or { "transport" }.
Use the API
Every path below is under https://api.caas.sh/v1, with an API key as Authorization: Bearer <api-key>. <server> is a server's ID or name.
| Request | Does |
|---|---|
POST /mcp-servers | Creates a server: { "name", "sandbox" or "sandboxId", "transport", "tokens" }. Send an Idempotency-Key. |
GET /mcp-servers | Lists servers, newest first, with nextCursor |
GET /mcp-servers/<server> | Reads one: its url, sandbox, transport, state and stats |
PATCH /mcp-servers/<server> | Renames it or changes its transport |
DELETE /mcp-servers/<server> | Deletes it, and its own sandbox unless ?keepSandbox=true |
GET, POST /mcp-servers/<server>/tokens | Lists or mints client tokens |
DELETE /mcp-servers/<server>/tokens/<token-id> | Revokes a token |
GET /mcp-servers/<server>/traces | The newest 100 request traces |
GET /mcp-servers/<server>/stats | The gateway's last 24 hours |
sandbox is a new sandbox's create, exactly as POST /v1/sandboxes takes it. transport is { "http": { "port": 3000, "path": "/mcp" } }, the port the sandbox's server listens on and the path the gateway sends requests to (/mcp by default).
curl https://api.caas.sh/v1/mcp-servers \
-H "Authorization: Bearer $CAAS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "name": "search-mcp", "sandbox": { "image": "registry.caas.sh/<tenant-id>/search-mcp@sha256:…", "egress": ["api.search.example.com"] },
"transport": { "http": { "port": 8080 } } }'The answer, 201, is the server with tokens, each minted token with its secret, once. A repeat with the same key answers the server without its tokens.
Pricing
The MCP Gateway bills per request. That price includes the endpoint, authentication, routing, persistence and observability. A request with a valid client token counts whatever its result, including a tool call the grant denied. Requests the gateway refuses for their token, their size or the rate limit are free.
The server's sandbox bills like any sandbox. On Native:
| Meter | Price | When |
|---|---|---|
| CPU | On the pricing page | While the server runs, whether it's busy or waiting |
| Memory | On the pricing page | While the server runs |
| Disk | On the pricing page | While the server runs |
| Egress | On the pricing page | Outbound data, including the responses your server sends to clients |
| Snapshot storage | On the pricing page | For each snapshot you keep |
A server that stops when idle pays nothing for CPU, memory and disk while it's stopped. On your own Daytona, Freestyle, Maritime or E2B account, that provider bills the server's compute and egress instead, and the gateway's requests still bill here.
See pricing for every rate, and Billing and usage for how usage draws down the balance.