Skip to main content

MCP servers API

This page documents every MCP server endpoint on agent-engine-api. An MCP server hosts your Python tool code as a Model Context Protocol endpoint that agents and MCP clients can call. Every publish snapshots the whole tool set into a new, immutable version, so you can roll back to an exact past build. Set $CAI_API first — see the API overview.

For a guided introduction, start with the MCP servers overview.

Base URL

MCP servers are served by agent-engine-api, the core platform API.

kubectl -n cai-system port-forward svc/agent-engine-api 8080:8080 &
export CAI_API=http://localhost:8080
export PROJECT=<your-project-uuid> # platformctl projects list

Authentication and roles

Every route requires a bearer credential:

Authorization: Bearer <token-or-api-key>
RoleCan call
project memberList servers, get one server, list its tools, list its versions
project adminEverything a member can, plus create and delete a server, publish and delete tools, and roll back, yank, or unyank a version

Conventions

  • Error envelope. Every non-2xx response is {"error": "<message>", "request_id": "<id>"}.
  • The 404 rule. A request against a project you hold no grant on returns 404 — never 403 — so project existence is not discoverable.
  • Pagination. The server list takes page_size (1–200, default 50) and page_token and returns next_page_token. See the API overview.
  • Custom methods. Version actions use GCP-style :verb suffixes (:rollback, :yank, :unyank), always with POST.
  • Builds are asynchronous. Creating a server and publishing a tool both return before the build finishes. Poll the server until status is ready or failed.
  • Body caps. Create is capped at 4 KiB; a tool publish is capped at 1 MiB (it carries source code).

Endpoints at a glance

MethodPathAuthPurpose
POST/v1/projects/{projectID}/mcpserversadminCreate a server
GET/v1/projects/{projectID}/mcpserversmemberList servers
GET/v1/projects/{projectID}/mcpservers/{name}memberGet one server (poll this for status)
DELETE/v1/projects/{projectID}/mcpservers/{name}adminDelete a server
GET/v1/projects/{projectID}/mcpservers/{name}/toolsmemberList the current tool set
PUT/v1/projects/{projectID}/mcpservers/{name}/tools/{tool}adminPublish or replace one tool
DELETE/v1/projects/{projectID}/mcpservers/{name}/tools/{tool}adminRemove one tool
GET/v1/projects/{projectID}/mcpservers/{name}/versionsmemberList versions
POST/v1/projects/{projectID}/mcpservers/{name}/versions/{version}:rollbackadminPoint the server at an earlier version
POST/v1/projects/{projectID}/mcpservers/{name}/versions/{version}:yankadminRetire a version as a rollback target
POST/v1/projects/{projectID}/mcpservers/{name}/versions/{version}:unyankadminUn-retire it

The server object

List, get, and create all return this shape. A single-object response wraps it under "mcp_server".

{
"name": "weather-tools",
"visibility": "",
"expose": "",
"status": "ready",
"url": "http://weather-tools.cai-p-<project-short>.svc.cluster.local",
"version": 1,
"image": "localhost:30500/mcp/weather-tools@sha256:...",
"tool_names": ["get_forecast"],
"tool_count": 1,
"message": "",
"created_at": "2026-08-06T12:00:00Z"
}
FieldTypeMeaning
namestringThe server name you chose. Immutable
exposestring"" = cluster-local (reachable only inside the platform), "apps" = published on the internet
statusstringpending, building, deploying, ready, or failed
urlstringThe endpoint MCP clients connect to. Present once the server is serving
versionintThe version currently running
imagestringThe container image behind the running version
tool_namesstring[]The tools in the current set
tool_countintCount of the above
messagestringWhy a build or rollout failed
created_atstringRFC 3339 timestamp

Create a server

POST /v1/projects/{projectID}/mcpservers

Auth: admin. Returns 201. The server starts empty — publish a tool to give it something to serve.

FieldTypeRequiredDefaultRules
namestringyesLowercase DNS label, 40 characters or fewer: ^[a-z]([a-z0-9-]{0,38}[a-z0-9])?$
exposestringno"""" (cluster-local) or "apps" (published)
{"name": "weather-tools", "expose": ""}
Only these two fields are read

The create endpoint decodes name and expose and nothing else. Any other field you send — including scaling settings — is silently ignored. There is no API on this surface for keeping an instance warm.

Errors:

StatusMessage
400missing or invalid 'name' (must be a lowercase DNS label, <=40 chars)
400'expose' must be "" (cluster-local) or "apps"
409an mcp server named <n> already exists in this project

List servers

GET /v1/projects/{projectID}/mcpservers

Auth: member. Response 200: {"mcpservers": [...], "next_page_token": "..."}.

Get one server

GET /v1/projects/{projectID}/mcpservers/{name}

Auth: member. Response 200: {"mcp_server": {...}}. This is the endpoint you poll after a create or a publish — watch status move from building to deploying to ready, or to failed with an explanation in message.

Delete a server

DELETE /v1/projects/{projectID}/mcpservers/{name}

Auth: admin. Response 200: {"name": "weather-tools", "deleted": true}.

List tools

GET /v1/projects/{projectID}/mcpservers/{name}/tools

Auth: member. Response 200:

{"server": "weather-tools",
"tools": [{"name": "get_forecast", "description": "Forecast.", "handler": "import crusoe_mcp as crusoe\n...",
"schema": {}, "credential_keys": [], "credentialKeys": [],
"updated_at": "2026-08-06T12:00:00Z"}]}

credential_keys and credentialKeys carry the same value; the second spelling exists for backward compatibility.

Publish a tool

PUT /v1/projects/{projectID}/mcpservers/{name}/tools/{tool}

Auth: admin. Body capped at 1 MiB. Returns 202 — the publish is accepted and a build starts.

The tool name in the path must match ^[a-z][a-z0-9_]{0,62}$ (a lowercase identifier, no leading underscore). It becomes the file tools/<name>.py inside the built image.

FieldTypeRequiredNotes
handlerstringyesThe tool's Python source — a complete module using the @crusoe.tool() decorator
descriptionstringnoShown to agents and MCP clients
schemaobjectnoThe tool's input schema
credentialKeys / credential_keysstring[]noNames of credentials the tool needs. Either spelling is accepted

A minimal handler:

import crusoe_mcp as crusoe

@crusoe.tool()
def get_forecast(city: str) -> dict:
"""Forecast."""
return {"city": city}

Response 202:

{"server": "weather-tools", "name": "get_forecast", "published": true,
"build_id": "2f6f...-uuid",
"note": "building a new immutable version from the server's current tool set"}

Errors:

StatusMessage
400missing 'handler': the tool's Python source (a complete @crusoe.tool module)
400invalid tool name (must be a lowercase identifier not starting with '_')

Every publish snapshots the whole current tool set into a new version pinned to an image digest — not just the tool you changed.

Delete a tool

DELETE /v1/projects/{projectID}/mcpservers/{name}/tools/{tool}

Auth: admin. Returns 202 and rebuilds the server without that tool:

{"server": "weather-tools", "name": "get_forecast", "deleted": true,
"build_id": "...", "note": "rebuilding the server without this tool"}

A tool that is not in the set returns 404.

List versions

GET /v1/projects/{projectID}/mcpservers/{name}/versions

Auth: member. Response 200:

{"server": "weather-tools",
"versions": [{"version": 2, "image_digest": "sha256:...", "credential_keys": [],
"tool_names": ["get_forecast"], "tool_count": 1,
"current": true, "created_at": "..."},
{"version": 1, "image_digest": "sha256:...", "credential_keys": [],
"tool_names": [], "tool_count": 0, "current": false,
"yanked": true, "yanked_at": "...", "created_at": "..."}]}
FieldTypeMeaning
versionintThe version number, counting up from 1
image_digeststringThe exact image this version built. Present once the build succeeded
tool_names, tool_countThe tool set captured in this version
credential_keysstring[]Credential names the version's tools declared
currentbooltrue for the version the server runs right now
yanked, yanked_atPresent only on retired versions
created_atstringRFC 3339 timestamp

Roll back to a version

POST /v1/projects/{projectID}/mcpservers/{name}/versions/{version}:rollback

Auth: admin. Re-points the server at that version's recorded image. There is no rebuild, so it is fast and reproduces exactly what ran before.

Response 200:

{"server": "weather-tools", "version": 1, "image": "localhost:30500/mcp/weather-tools@sha256:...",
"rolled_back": true,
"note": "re-pointed the server at version <n>'s recorded image; no rebuild"}

Errors:

StatusMessage
400version must be a positive integer
404no such version
409version <n> is yanked (retired); unyank it or publish a new version instead
409version <n> has no recorded image to roll back to

Yank and unyank a version

POST /v1/projects/{projectID}/mcpservers/{name}/versions/{version}:yank
POST /v1/projects/{projectID}/mcpservers/{name}/versions/{version}:unyank

Auth: admin. Yanking retires a version as a rollback target without deleting it or its history — use it to mark a build you never want to return to. Unyanking reverses that.

Response 200: {"server": "weather-tools", "version": 1, "yanked": true} (or false for unyank).

Errors:

StatusMessage
409version <n> is the one the server currently runs; roll to another version before yanking it
404unknown method on a version. The custom methods are POST .../versions/{version}:rollback, :yank and :unyank (any other :verb)

What a deployed server gets

A running MCP server receives these environment variables: CAI_API_URL and CAI_PROJECT_ID as plain values, and CAI_PROJECT_KEY and CAI_MCP_AUTH_TOKEN as references to Kubernetes Secrets. The per-server bearer token has the form cai_mcp_ followed by 64 hexadecimal characters; it is minted once and reused across redeploys, so a client you configured keeps working after a publish or a rollback. The request timeout on a server is 300 seconds.

See Connect agents and clients.

Audit actions

Every state change writes a row to the project audit log (GET /v1/projects/{projectID}/audit, readable by any project member):

ActionWritten by
mcpserver.createCreate a server
mcpserver.deleteDelete a server
mcpserver.tool.publishPublish or replace a tool
mcpserver.tool.deleteDelete a tool
mcpserver.version.rollbackRoll back
mcpserver.version.yankYank
mcpserver.version.unyankUnyank

Status codes

CodeWhen
200Read, delete, rollback, yank, unyank
201Server created
202Tool publish or tool delete accepted — a build is running
400Validation: server name, expose, tool name, missing handler, non-positive version
401Missing or invalid credential
403You hold a grant but not the project admin role
404Not found, or not yours — deliberately identical. Also unknown :verb suffixes on a version
409Name already taken; yanked or imageless rollback target; yanking the running version
429Per-principal rate limit
500internal error