Docs · Agents

Use froog as an MCP server

froog is also an MCP server, so Claude Code, Codex, Cursor, Claude Desktop and any other MCP client can create, run, fork and snapshot sandboxes as tools, without shell commands. There are two ways to connect, with the same tools:

  • Local: froog mcp runs the server on stdio, signed in like the CLI (froog login, or FROOG_API_KEY in its environment).
  • Hosted: https://api.froogcorp.com/mcp (Streamable HTTP) with an API key in the header Authorization: Bearer froog_…, or, for clients that sign in through the browser (Claude's custom connectors, ChatGPT), OAuth: see Connect with a sign-in. Nothing to install.

Claude Code

$ claude mcp add froog -- froog mcp
# or hosted:
$ claude mcp add --transport http froog https://api.froogcorp.com/mcp --header "Authorization: Bearer $FROOG_API_KEY"

Add --scope user to have it in every project.

Codex

$ codex mcp add froog -- froog mcp

Hosted, in ~/.codex/config.toml (the key is read from FROOG_API_KEY):

[mcp_servers.froog]
url = "https://api.froogcorp.com/mcp"
bearer_token_env_var = "FROOG_API_KEY"

Cursor: in ~/.cursor/mcp.json (or .cursor/mcp.json in a project), one of:

{ "mcpServers": { "froog": { "command": "froog", "args": ["mcp"] } } }

{ "mcpServers": { "froog": {
    "url": "https://api.froogcorp.com/mcp",
    "headers": { "Authorization": "Bearer ${env:FROOG_API_KEY}" } } } }

Claude Desktop: Settings, Developer, Edit Config, then in claude_desktop_config.json (Claude Desktop doesn't see your shell's PATH, so give the full path that which froog prints):

{ "mcpServers": { "froog": {
    "command": "/Users/you/.local/bin/froog",
    "args": ["mcp"],
    "env": { "FROOG_API_KEY": "froog_…" } } } }

Any other client: run froog mcp as a stdio server, or point it at https://api.froogcorp.com/mcp with the Authorization header.

Connect with a sign-in (OAuth)

Claude (claude.ai and Claude Desktop: Settings, Connectors, Add custom connector), ChatGPT and other clients that can't send a header connect to https://api.froogcorp.com/mcp with just the URL. froog follows the MCP authorization spec (OAuth 2.1 with PKCE):

  1. The client gets 401 with WWW-Authenticate: Bearer resource_metadata="https://api.froogcorp.com/.well-known/oauth-protected-resource/mcp", reads that and /.well-known/oauth-authorization-server, and registers itself (POST /oauth/register, dynamic client registration, public clients only, redirect URIs over https, to localhost, or an app's own scheme).
  2. Your browser opens froog's sign-in (Google, as on the dashboard), then a page that names the client and where it sends you back, and asks what it may do: ci (the default: create sandboxes, run commands, stop and start them, delete the ones it created), full-sandbox (use existing sandboxes only) or read-only.
  3. On Allow, the client gets an access token: a new API key with that preset, labeled mcp: CLIENT NAME, that expires in 30 days. It's in froog api-key list and the dashboard like any key. Revoke it there (or the client calls POST /oauth/revoke). Its creation is in the audit log. There are no refresh tokens: after 30 days the client asks you to sign in again.

A malformed authorization request (a bad response_type, no PKCE, another resource) shows an error page. froog sends the browser back to the client's redirect URI only after you answer the consent page (with access_denied on Deny). API keys keep working as bearer tokens on the same URL. Self-hosted control planes turn OAuth on with FROOG_SERVER_MCP_OAUTH=on (it needs Google sign-in set up). While it's off, the OAuth URLs answer 404.

ToolDoes
list_sandboxesSandboxes with state, size, OS and labels. Filter by label, state, name_prefix.
create_sandboxA new sandbox (name, size, os, from a saved snapshot, env, labels, ttl_secs, setup). Waits until it runs.
sandbox_infoOne sandbox in full.
execRun a bash command (command, cwd, env, timeout_secs up to 3600). Returns the exit code, stdout and stderr.
start_sandbox, stop_sandboxStart (waits until it runs) or stop (keeps the disk).
delete_sandboxDelete for good. Needs confirm: true.
fork_sandboxCopy a sandbox, or one of its snapshots, into new_name. Waits until the copy runs.
save_snapshot, list_snapshotsSave a snapshot (with a label, a template for create_sandbox from). List a sandbox's snapshots, or the account's saved ones.
read_file, write_file, list_dirFiles in a running sandbox: text as is, binary as base64.
host_portAn HTTPS URL for a port (private with a token unless public).
desktop_linkA 10-minute link to watch and use the desktop.
prompt_agentRun a coding agent inside the sandbox on a prompt (agent, model, effort) and return its answer.
start_jobStart a bash command in the background (command, cwd, env), like froog exec --detach. Returns the job id at once.
list_jobs, job_logs, kill_jobA sandbox's background commands. One job's state, exit code and output from offset (pass the returned next_offset to read only what's new). Stop one.
  • create_sandbox, fork_sandbox and start_sandbox wait up to 120 seconds (wait: false returns at once). exec and read_file return at most 256 KiB of output (the end of a long output, the start of a long file).
  • A failed call is a tool error whose text is the API's error, {"error":{"code":…,"message":…,"request_id":…}}, so the agent sees why (not_found, api_key_action_forbidden, a full plan) and you can quote the request id.
  • Give the agent a scoped key. The tools obey the key's scope exactly like the HTTP routes they stand for: with froog api-key create agent --preset ci, an agent can create sandboxes and delete only the ones it created. With --preset read-only, it can only look.
  • Output as it runs. An exec call that sends _meta.progressToken gets its output as notifications/progress while the command runs (progress: the bytes so far, message: the new output), then the usual result. froog mcp writes them on stdout. The hosted endpoint answers that call as server-sent events when the client accepts text/event-stream.
  • The hosted endpoint is stateless: each POST gets one JSON answer (or, for a call with a progress token, one event stream that ends with the answer), with no session id (GET answers 405). It speaks MCP 2025-06-18, 2025-03-26 and 2024-11-05. Tool calls count toward the same per-IP request limit as the API.