npm.io
0.0.6 • Published 23h agoCLI

@descanto/mcp

Licence
Elastic-2.0
Version
0.0.6
Deps
2
Size
302 kB
Vulns
0
Weekly
0

@descanto/mcp

MCP (Model Context Protocol) stdio server that gives AI clients — Claude Code, Claude Desktop, Cursor, and any other MCP-compatible tool — hands-on control of Canto cloud desktops: create, wake, run commands, read/write files, fork, and stream, all as MCP tools.

Ships as a single self-contained binary (@descanto/sdk is bundled in), so there's nothing to install — point your client at npx -y @descanto/mcp.

Full docs: https://docs.descanto.com/docs/canto

Configuration

The server needs one environment variable:

Env var Required Description
CANTO_API_KEY yes canto_sk_... API key (or AuthKit JWT). The server exits immediately with a clear message if unset.
CANTO_BASE_URL no Canto API base URL. Defaults to the hosted API (https://api.descanto.com); set it only to point at a different endpoint (e.g. http://127.0.0.1:8081 for a local controld). Older published versions defaulted to localhost, so the examples below set it explicitly — safe to keep either way.

Claude Code

claude mcp add canto --env CANTO_API_KEY=canto_sk_... --env CANTO_BASE_URL=https://api.descanto.com -- npx -y @descanto/mcp
Claude Desktop / Cursor / Antigravity

These use the same mcpServers shape — add it to claude_desktop_config.json (Claude Desktop), .cursor/mcp.json (Cursor), or ~/.gemini/config/mcp_config.json (Antigravity, via … → MCP Servers → View raw config):

{
  "mcpServers": {
    "canto": {
      "command": "npx",
      "args": ["-y", "@descanto/mcp"],
      "env": {
        "CANTO_API_KEY": "canto_sk_...",
        "CANTO_BASE_URL": "https://api.descanto.com"
      }
    }
  }
}
Codex
codex mcp add canto --env CANTO_API_KEY=canto_sk_... --env CANTO_BASE_URL=https://api.descanto.com -- npx -y @descanto/mcp

or add it to ~/.codex/config.toml by hand:

[mcp_servers.canto]
command = "npx"
args = ["-y", "@descanto/mcp"]
env = { CANTO_API_KEY = "canto_sk_...", CANTO_BASE_URL = "https://api.descanto.com" }
GitHub Copilot (VS Code)

Add to .vscode/mcp.json — Copilot uses the top-level servers key and requires "type": "stdio":

{
  "servers": {
    "canto": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@descanto/mcp"],
      "env": {
        "CANTO_API_KEY": "canto_sk_...",
        "CANTO_BASE_URL": "https://api.descanto.com"
      }
    }
  }
}
OpenCode

Add to opencode.json (project) or ~/.config/opencode/opencode.json (global) — OpenCode nests servers under mcp, takes command as an array, and uses environment rather than env:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "canto": {
      "type": "local",
      "command": ["npx", "-y", "@descanto/mcp"],
      "enabled": true,
      "environment": {
        "CANTO_API_KEY": "canto_sk_...",
        "CANTO_BASE_URL": "https://api.descanto.com"
      }
    }
  }
}

Tools

Tool Description
list_desktops List desktops, optionally filtered by state. Returns {id, state, tier, billingMode, hostId} per desktop.
create_desktop Provision a desktop: tier (small/default/large), optional billing_mode (monthly/hourly), idle_timeout_secs, env (guest env vars → /root/.canto/env on first wake), setup_script (runs once on first wake).
wake_desktop Wake a hibernated desktop (desktop_id, wait default true). Typically ~1–10s.
hibernate_desktop Hibernate an awake desktop to pause billing (desktop_id, wait default true).
destroy_desktop Permanently destroy a desktop and its disk (desktop_id). Always waits; irreversible.
fork_desktop Clone a Hibernated source into count (1–20) new, also-Hibernated desktops (desktop_id, count, optional ephemeral, acknowledge_shared_state: must be true). Returns {desktopIds}.
set_idle_timeout Update how long an awake desktop sits idle before auto-hibernating (desktop_id, idle_timeout_secs; 0 disables).
exec Run a shell command on an awake desktop (desktop_id, command, optional timeout_secs). Pass detached: true to spawn a long-running command and get {processId} back immediately.
get_process Poll a detached process (desktop_id, process_id, optional tail_bytes). Returns {processId, status, exitCode, stdoutTail, stderrTail}; status is running/exited/lost.
read_file Read a file (desktop_id, path). Text if valid UTF-8 under 50KB, else base64 (capped) or a size error.
write_file Write a file (desktop_id, path, content or content_base64). Base64 is strictly validated before decoding.
get_stream_url Get a short-lived signed stream URL (desktop_id, claim: view/control, default view).
expose_port Publish a guest TCP port at a stable URL (desktop_id, port, optional public). Desktop must be awake. Returns {url, token?, port, public} — the canto_pt_... token is shown once and grants access to whatever the guest serves there.
list_ports List a desktop's port exposures (desktop_id). Returns {ports: [...]} with status bound/unbound; never includes tokens.
stop_port Stop an exposure by guest port number (desktop_id, port). The URL 404s afterwards.
usage Awake-time usage and projected cost, per desktop and org-wide (start_ms/end_ms, default last 30 days).
screenshot Capture the desktop's display as a PNG image block (desktop_id, optional region {x,y,width,height}, scale_percent 1–100). Results over 2MB are rejected with a hint to pass scale_percent — always scale down on large displays.
click Click (desktop_id, optional x/y to move first, button left/right/middle, double). Returns the final {cursor} — as does every input tool below; there is no separate cursor-read tool.
move_mouse Move the cursor to absolute coordinates (desktop_id, x, y).
drag Drag from from_x/from_y (default: current cursor) to to_x/to_y (desktop_id, optional button).
scroll Scroll the wheel at the current cursor position (desktop_id, direction up/down/left/right, amount 1–50).
type_text Type into the focused element (desktop_id, text ≤ 8KB) — click the target first.
press_key Press a key combo (desktop_id, combo: +-joined X keysyms, e.g. ctrl+shift+t, Return).
set_resolution Resize the display (desktop_id, width 640–2560, height 480–1600). Returns the size actually applied; a 501 error means this desktop's image can't resize at runtime — use screenshot's scale_percent instead.

Computer-use tools require an awake desktop (wake_desktop first). Input is never gated by noVNC control sessions — a human viewer and these tools can interleave. Input calls are not transactional: an error mid-action-batch means earlier effects already applied, so re-screenshot before retrying rather than blindly re-sending clicks.

Context protection

exec stdout/stderr and get_process tails are each truncated at 50KB in the tool result (never in the underlying API call), UTF-8-boundary-safe, with a [truncated by canto-mcp] marker — this protects the calling model's context window. For larger output, redirect to a file and fetch it with read_file, or re-run with head/tail. read_file returns text only when it's valid UTF-8 and small enough; otherwise base64, or a size error asking you to use exec.

Errors

A failed Canto API call never crashes the server. Every tool catches the error and returns an MCP tool-error (isError: true) whose message includes the API's HTTP status, title, and detail (and operation_id when relevant), so the model sees exactly what happened and can react.

License

Elastic License 2.0. Free to use, copy, modify, and self-host — you just can't offer it to third parties as a hosted or managed service, or remove the license notices.

Keywords