MCP server

Reference for @shardflux/mcp 0.4.3, the local stdio MCP server. Client setup, environment variables, every tool and its arguments, wake, errors and updates.

Overview

@shardflux/mcp is a local stdio MCP server that gives an MCP client (Claude Code, Cursor, Codex, Claude Desktop or any other) the tools of your Shardflux workspaces. This page describes 0.4.3. A feature marked with a version, such as (0.4.0+), is not in earlier releases.

  • It runs on your machine with a project API key you supply, and translates each tool call into one @shardflux/sdk request or wait.
  • It has no agent loop of its own and keeps no local workspace directory. Its only state is one workspace handle per key, so tool tokens are reused between calls.
  • It needs Node.js 24 or later. Run it with npx -y @shardflux/mcp; the package's binary is shardflux-mcp.
Shell
SHARDFLUX_API_KEY=sfk_... npx -y @shardflux/mcp

The server speaks MCP on stdin and stdout and logs to stderr. Your MCP client starts it; you do not run it by hand except to test it.

Client setup

Each client below starts the server with npx -y @shardflux/mcp and passes the configuration as environment variables. Use a project API key whose tool permissions cover what the agent should do (see Permissions).

Claude Code

Shell
claude mcp add --env SHARDFLUX_API_KEY=sfk_... --env SHARDFLUX_TEMPLATE=python-node-browser --transport stdio shardflux \
  -- npx -y @shardflux/mcp

The -- separates Claude Code's options from the server command. The server is added to your local scope by default; --scope user makes it available in every project, and --scope project writes it to .mcp.json in the project root to share with your team. In a shared .mcp.json, reference the key from your environment instead of committing it (Claude Code expands ${VAR} in command, args and env):

JSON
{
  "mcpServers": {
    "shardflux": {
      "command": "npx",
      "args": ["-y", "@shardflux/mcp"],
      "env": {
        "SHARDFLUX_API_KEY": "${SHARDFLUX_API_KEY}",
        "SHARDFLUX_TEMPLATE": "python-node-browser"
      }
    }
  }
}

Cursor

Add the server to .cursor/mcp.json in your project, or to ~/.cursor/mcp.json for every project:

JSON
{
  "mcpServers": {
    "shardflux": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@shardflux/mcp"],
      "env": {
        "SHARDFLUX_API_KEY": "sfk_...",
        "SHARDFLUX_TEMPLATE": "python-node-browser"
      }
    }
  }
}

Codex

Shell
codex mcp add shardflux --env SHARDFLUX_API_KEY=sfk_... --env SHARDFLUX_TEMPLATE=python-node-browser -- npx -y @shardflux/mcp

Or add it to ~/.codex/config.toml (or a project's .codex/config.toml):

TOML
[mcp_servers.shardflux]
command = "npx"
args = ["-y", "@shardflux/mcp"]
tool_timeout_sec = 150

[mcp_servers.shardflux.env]
SHARDFLUX_API_KEY = "sfk_..."
SHARDFLUX_TEMPLATE = "python-node-browser"

Codex stops waiting for a tool after tool_timeout_sec (60 seconds by default). Set it above the server's per-call deadline (SHARDFLUX_MCP_TOOL_TIMEOUT_MS, 120 seconds by default), or lower that deadline, so the server's own timeout result reaches the agent.

Claude Desktop

Open Settings > Developer > Edit Config and add the server to claude_desktop_config.json (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):

JSON
{
  "mcpServers": {
    "shardflux": {
      "command": "npx",
      "args": ["-y", "@shardflux/mcp"],
      "env": {
        "SHARDFLUX_API_KEY": "sfk_...",
        "SHARDFLUX_WORKSPACE_KEY": "me/scratch",
        "SHARDFLUX_TEMPLATE": "python-node-browser"
      }
    }
  }
}

Quit and restart Claude Desktop to load the change. The server's stderr log is in Claude Desktop's MCP logs.

Configuration

The server reads its configuration from the environment when it starts. Invalid configuration exits with code 2 before serving, with one JSON line on stderr.

Variable Default Meaning
SHARDFLUX_API_KEY required Project API key, sfk_<key id>_<secret>. Its tool permissions decide which workspace tools are listed.
SHARDFLUX_API_URL https://api.shardflux.dev API origin. Must be https (plain http only for loopback hosts), without credentials, query or fragment.
SHARDFLUX_WORKSPACE_KEY none Pins one workspace (1-200 printable characters). See Pinning.
SHARDFLUX_TEMPLATE none Default template slug for workspace_open. With it, template becomes optional.
SHARDFLUX_WORKSPACE_MODE none (0.4.0+) processful or file_first: the mode workspace_open sends when the call names none (unset: the API's default, processful for a new key). A pinned server also lists the tools of this mode until its workspace exists.
SHARDFLUX_MCP_TOOL_TIMEOUT_MS 120000 Default and maximum deadline per tool call, 1000-3600000 ms. A call's timeout_ms can only shorten it.
SHARDFLUX_WAKE on on: workspace tools resume a suspended workspace, then run. off: they return the workspace_not_running refusal.
SHARDFLUX_WAKE_TIMEOUT_MS 120000 Longest wait per call for a workspace to wake or finish a transition, 1000-3600000 ms. Clamped to SHARDFLUX_MCP_TOOL_TIMEOUT_MS; each wake also ends 250 ms before the call's deadline.
SHARDFLUX_AGENT_LABEL mcp Attribution label of every tool token the server obtains (1-100 printable characters). It appears as an agent session in the console and in shard ws sessions.
SHARDFLUX_MCP_LOG_LEVEL info debug, info, warn or error.
SHARDFLUX_HTTP_KEEPALIVE unset 1 reuses HTTP connections. By default every request uses a fresh connection (Connection: close), which avoids keep-alive stalls on some Node.js releases.
SHARDFLUX_NO_UPDATE_CHECK unset (0.4.0+) 1, true, yes or on turns off the startup update check (see Updates).
NO_UPDATE_NOTIFIER unset (0.4.0+) The npm convention: any non-empty value also turns the check off.

Tools

Every tool takes an optional timeout_ms (1-3600000): the call's deadline, clamped to SHARDFLUX_MCP_TOOL_TIMEOUT_MS. Every tool that works on a workspace takes workspace_key, required unless the server is pinned. A workspace key is resolved by exact match within the API key's project, across every lifetime and purpose, so sessions, template drafts and test instances resolve too; the live workspace wins over tombstones of ended sessions with the same key.

Management tools

Tool Arguments (required in bold) Does
workspace_open workspace_key, template (optional with SHARDFLUX_TEMPLATE), cpu_millis, memory_mib, disk_gib, lifetime (persistent or session), inputs, mode (processful or file_first), wait (default true) Opens by key: creates the workspace on first use, reconnects to or resumes it afterwards, never resets it. Waits until it is ready, the template's start commands and services included, unless wait: false. inputs (0.3.0+) passes the template's text inputs, {"NAME": "value"}. mode (0.4.0+) opens a file-first workspace. Returns {workspace, ready} and timing.
workspace_list prefix, state, lifetime (persistent, session, any), purpose (standard, template_draft, template_test, any), include_deleted, limit (1-200, default 50), cursor The project's workspaces: {workspaces, next_cursor}. By default only persistent standard workspaces.
workspace_status workspace_key One workspace plus its five most recent operations.
workspace_suspend workspace_key, wait (default false), after_seconds (30-3600, 0.4.1+) Suspends the workspace. Returns the operation; with wait: true, once it has finished. With after_seconds (0.4.1+), suspend when idle instead: the workspace is suspended once it has been idle that long; the agent's next tool call on it cancels that, and a running command or a keepalive postpones it. Returns suspend_request (not_before: the earliest suspend) and a message, or operation when a suspend was already in progress. Not with wait (invalid_arguments). The server's instructions ask the agent to call it when it finishes its work. workspace_status shows a pending request as workspace.suspend_request.
workspace_resume workspace_key, wait (default false) Resumes a suspended workspace.
workspace_fork workspace_key, new_key, cpu_millis, memory_mib, disk_gib, wait (default false) Forks into a new key. Returns {operation, workspace}.
operation_wait operation_id Keeps waiting for a lifecycle operation, up to the call's deadline.
usage_summary organization_id (default: the key's organization) Usage of the organization in the current billing period: meters, allowances with their cap state, allowance_exhausted with exhausted_reason, and spend_cap (0.4.1+), the opt-in overage state, cap, charges and lines per allowance. The description tells the model about them (0.4.0+).
template_get slug, version, owner (platform or organization) (0.3.0+) A template's versions with their settings (env, inputs, start commands, services, defaults). With version, also that version's recipe in request form.
template_languages base (<slug>@<version>) (0.3.0+) The languages and versions a base offers build.languages.
send_feedback message (1-8000 characters), category (bug, confusing, missing, idea, praise, other), workspace, request_id, error_code, command (0.4.0+) Sends feedback straight to the Shardflux founder; see Feedback. Returns {id, received_at, duplicate, note}.
template_build template_slug, exactly one of file or recipe, display_name, description, publish (default false), wait (default false) (0.3.0+) Builds a version of an organization template from a recipe v2: recipe (the document) or file (a template.yaml or .json path). Local from paths are uploaded first (folders as a tar). Every local path must resolve inside the server's working directory; others are refused before any request. Unpublished unless publish: true. Returns {build, uploads}.

Deleting a workspace is deliberately not exposed to agents; use the CLI or the console.

Feedback

(0.4.0+) send_feedback goes straight to the Shardflux founder, who reads every message. The server instructions and the tool description ask the agent to call it actively while it works, not once at the end:

  • the moment a call fails unexpectedly, an error or doc is unclear, an option is missing, or it needed a workaround;
  • when its user complains or is frustrated about Shardflux or the workflow around it (slowness, cost or limits, confusing setup, a missing feature, a workaround), paraphrased, with what they were trying to do;
  • when its user asked for something Shardflux could not do, or made awkward.

Short and frequent beats polished, with the request_id and error code of the failure. The agent does not need to ask first, but tells its user it sent feedback, and leaves out private data about the user and secrets or code they did not mean to share (paraphrase, never transcripts).

  • The server adds client: shardflux-mcp/<version> and, as agent, the MCP client's name and version from initialize (else SHARDFLUX_AGENT_LABEL when it is not the default). A pinned server fills workspace with its key.
  • duplicate: true: the same message from this key in the last 24 hours; it is not emailed again.
  • Any API key may send it (no tool permission), so it is always listed. It is rate limited: 10 per 10 minutes and 50 per day per key, 200 per day per organization (rate_limited with details.retry_after_seconds, and a hint naming the wait and the fallback address, shardflux@heliosone.fi).
  • Anything shaped like an API key, token or private key is redacted before the message is stored or emailed.

Workspace tools

These are the TypeScript SDK's workspaceTools(), published with the SDK's JSON Schemas plus workspace_key and, where the SDK schema has none, timeout_ms:

Tool Permission Arguments (required in bold)
exec exec command (run with bash -lc), cwd (absolute; a relative one is an invalid_cwd error), timeout_ms, stdin
read_file files path, offset, length
write_file files path, content, append, create_parents
list_files files path, limit
search_files files (0.4.0+) path, pattern, regex, case_insensitive, include, exclude, max_matches, context_lines. Read-only.
edit_file files (0.4.0+) path, edits ({old_text, new_text, replace_all}), expected_revision
list_processes process none
signal_process process pid, signal
terminal_open pty command, rows, cols
terminal_send pty session_id, input
terminal_read pty session_id, offset, wait_ms
terminal_close pty session_id
git_clone git url, path, branch, depth
git_status git path
git_commit git path, message
browser_screenshot browser url, width, height. Returns an MCP image content block.
browser_content browser url, format (text or html)

The argument details are in the TypeScript SDK reference. For exec, timeout_ms is both the call's deadline and the command's timeout: it defaults to, and is capped by, the server's per-call deadline, and the command is killed 2 seconds before the deadline so its output comes back. (0.4.1+) A command that could not start (a cwd that is not a directory, a program not on PATH) returns exit_code: null and error (code, message, reason: "exec_failed_to_start") with the workspace's reason, as a failed file-first execution does.

Workspace tools do not open new workspaces: a key that was never opened is not_found. Call workspace_open first.

(0.4.0+) search_files searches file contents and edit_file replaces exact text; without an expected_revision, edit_file reads the file's revision first, so a concurrent change fails the edit (conflict, reason: revision_mismatch) instead of being overwritten. See Search and edit files. While the fleet is upgraded, a workspace on a host without them gets conflict, reason: host_feature_unavailable (details.feature), retryable: false, and a hint naming another way (exec with grep; read_file then write_file).

File-first workspaces

(0.4.0+) workspace_open with mode: "file_first" (or SHARDFLUX_WORKSPACE_MODE=file_first) opens a file-first workspace: a versioned file tree under /home/user with no VM between calls. It is ready at once and never suspended.

  • exec runs each command as an execution: a fresh VM on the workspace's files. Only files under /home/user persist between calls. The result adds execution_id, state, tree_revision and changed (up to 200 paths, changed_truncated). An execution cannot be canceled: when the call's deadline passes first, the error carries its execution_id, and the execution continues server side.
  • The files tools work as on a processful workspace; each change publishes the next tree revision.
  • The process, terminal, git and browser tools and workspace_suspend, workspace_resume, workspace_fork and operation_wait do not apply. A pinned server lists only the tools of its workspace's mode (once it is known, else SHARDFLUX_WORKSPACE_MODE, else processful) and sends notifications/tools/list_changed when that changes what it listed. An unpinned server lists every tool and refuses one the named workspace's mode lacks before any request (conflict, reason: not_supported_for_mode, with a hint).

Results

A successful call returns the result as JSON text content and as structuredContent. Workspace results include id, key, ready, observed_state, desired_state, template, active_operation, pending_reason, lifetime, purpose, disk_layout, idle_timeout_seconds, ended_reason, startup (0.3.0+: the state of the template's start commands and services; failed names the step, its exit code and output tail), and mode and, for a file-first workspace, tree_revision (0.4.0+), and suspend_request (0.4.1+: a pending suspend when idle, else null). Tool tokens are never returned to the model.

Permissions

tools/list shows the management tools and the workspace tools the API key's tool permissions allow (exec, files, pty, process, git, browser), read from GET /v1/me. If the server cannot read them, it lists every workspace tool and logs a warning; a call the key may not make is then refused by the API.

Choose the key's permissions to match what the agent should be able to do: for example, a key with only files gives the agent read_file, write_file and list_files and no command execution.

Account actions

Registering, signing in, organizations, projects, API keys, members, invitations, billing and plan upgrades, spend alerts and the audit export are not tools. The server authenticates with a project API key, and the API refuses a project API key for them: 403 forbidden ("API keys cannot perform this action.") on the account routes, 401 on the /v1/auth session routes. They need a person's session. (0.4.0+) The server instructions tell the agent to use the shard CLI for them (npx @shardflux/cli@latest --help; for example shard auth login, shard setup, shard billing upgrade <plan>), which signs a person in with a CLI session.

Pinning

With SHARDFLUX_WORKSPACE_KEY set, the server works on that one workspace:

  • workspace_key becomes optional on every tool and defaults to the pinned key.
  • A different key is refused with code: workspace_pinned (details.pinned_workspace_key).
  • workspace_open opens (or creates) the pinned workspace.

Pin a workspace when an agent should have one computer and nothing else, for example a per-project scratch workspace.

Wake on use

Workspace tools resume a suspended workspace, then run:

  • A suspended workspace is resumed (or the resume or open already running is joined), and the call runs once it is running. A call made during a suspend or resume waits for it to finish. The call never runs twice: the refused attempt did nothing.
  • The wait is bounded by SHARDFLUX_WAKE_TIMEOUT_MS and the call's deadline. Past it the result is code: timeout with the operation_id and last_state of the resume or open. That operation continues server side: operation_wait keeps waiting.
  • A resume or open that fails returns code: operation_failed (retryable: true for capacity_unavailable; the workspace stays suspended).
  • With SHARDFLUX_WAKE=off the refusal comes back instead: conflict with details.reason: workspace_not_running, or workspace_not_running from the cell gateway.
  • (0.4.0+) The wake is one request: the API holds the resume until the workspace runs and answers with this server's tool token, so the tool runs at once. workspace_resume with wait: true sends the same request. The wake's timing is one request(held) phase.
  • (0.4.0+) read_file, list_files and search_files of a suspended workspace whose disk a host still holds are answered from that disk without waking it: the files as they were at the suspend.
  • (0.4.0+) Every other workspace tool call first sends a wake hint without waiting for it, so a workspace its host has parked while idle starts waking, and a suspended one starts resuming, while the call is prepared.

Deadlines and cancellation

  • Every call has a deadline: timeout_ms, clamped to SHARDFLUX_MCP_TOOL_TIMEOUT_MS. Waits time out just before it with code: timeout and the operation_id; the operation continues server side. Any request still in flight at the deadline is aborted.
  • An open, resume or fork that no host can admit waits in capacity_pending for at most 15 minutes. A wait that ends first returns code: timeout, and its message names when the start gives up. A start still pending then fails: operation_failed with details.error_code: capacity_unavailable and retryable: true; nothing was started. The server never retries it.
  • MCP notifications/cancelled aborts the work behind the call: waits, requests to the API and the cell gateway, and backoff sleeps. No response is sent and the server stays up. A lifecycle operation that was already started is not canceled.

Errors

Errors are tool results, never protocol failures: isError: true and structuredContent.error = {code, message, status, request_id, retryable, operation_id, details, source}, plus reason (the refusal's details.reason) and, (0.4.0+) for the file-first refusals and host_feature_unavailable, a hint saying what to do instead. The only protocol error is an unknown tool name (InvalidParams).

(0.4.0+) An unexpected failure also carries structuredContent.feedback: a suggestion to call send_feedback with category bug, the request id and the error code. The expected flow has none: invalid_arguments, workspace_pinned, unauthenticated, validation_failed, timeout, and refusals whose error names the next step (a stale revision, an edit that does not match, a mode the workspace lacks, a host that predates the feature).

code Meaning
An API or cell gateway code The API or the workspace refused the call. source is api or cell.
invalid_arguments The arguments do not match the tool's schema (issues lists the problems), or a local path of template_build is outside the working directory. Nothing was sent.
workspace_pinned A different key than the pinned one.
not_found No workspace with that key in the project.
timeout The call's deadline or the wake timeout passed. operation_id and last_state name the operation, which continues; retryable is true.
operation_failed The operation ended failed or canceled. details.error_code (and details.reason) say why; retryable is the operation error's own flag.
upload_failed The storage refused a template_build upload (details.sha256, details.storage_code).
network_error The API or the cell gateway could not be reached (retryable: true).
protocol_error A response was not the documented shape.
internal_error An unexpected failure in the server (also logged).

A start refused because an allowance is used up is allowance_exhausted (402) with reason allowance_used (upgrade, or turn on overage), overage_paused (a plan payment is past due) or spend_cap_reached (raise the spend cap or upgrade), and details.spend_cap. An owner or billing member acts on it in the console; retrying does not help. See 402 allowance_exhausted.

Timing

A call that opens a workspace or waits says where the time went, compacted for model context:

JSON
{
  "timing": {
    "action": "open",
    "outcome": "succeeded",
    "operation_id": "01a0e5a8-3ef0-7ecb-975e-dff2d5ca6e33",
    "total_ms": 34180,
    "phases": ["request(held) 20010 ms", "capacity_pending(no_ready_host) 13520 ms", "running 590 ms", "view 42 ms", "token 61 ms"],
    "server": { "queued_ms": 33400, "run_ms": 620, "total_ms": 34020, "start_path": "warm" },
    "outside_server_ms": 160,
    "retries": 0
  }
}
  • workspace_open and operation_wait always carry it; workspace_suspend, workspace_resume and workspace_fork carry it with wait: true; workspace tools carry it when they had to wake the workspace (action: "wake").
  • Phases are in order, as phase(reason) ms. request(held) means the API held the open until the workspace was ready; capacity_pending(no_ready_host) is time spent waiting for a host.
  • server is the operation's own queued, run and total time plus the start or resume path; outside_server_ms is everything else (network, polling, reading the workspace, the tool token).
  • A failed or timed-out call carries it too, next to error.

Logging

Logs are JSON lines on stderr (stdout is the protocol), filtered by SHARDFLUX_MCP_LOG_LEVEL. Each line has time, level, service: "shardflux-mcp" and message, plus fields such as tool, outcome, ms and request_id. The first line (ready) records the version, API URL, pinned key, default template, agent label and timeouts. The API key is never logged, and anything shaped like a key is redacted. The server shuts down when the client closes stdin, or on SIGINT or SIGTERM.

Updates

(0.4.0+) At startup, after the configuration is validated, the server asks the API's GET /v1/client-versions whether @shardflux/mcp at its version is current. It sends one request (3 s timeout) in the background: the MCP handshake and tool calls never wait for it, and any failure is ignored. When the server is outdated, or below the oldest version the API still supports, it logs one warn line on stderr:

JSON
{"time":"2026-10-01T09:00:00.000Z","level":"warn","service":"shardflux-mcp","message":"@shardflux/mcp 0.4.3 is outdated: 0.5.0 is available. Update: npm install -g @shardflux/mcp@latest","status":"outdated","package":"@shardflux/mcp","current":"0.4.0","latest":"0.5.0","minimum_supported":"0.3.0","upgrade_command":"npm install -g @shardflux/mcp@latest","release_notes_url":"https://www.npmjs.com/package/@shardflux/mcp?activeTab=versions"}
  • An unsupported server says ... is no longer supported by the Shardflux API (minimum <version>). Update: ... ("status":"unsupported"). It keeps running.
  • Otherwise it stays silent (one debug line), also when the API does not answer.
  • The notice is not in the server instructions: they are fixed when the server starts, and the handshake does not wait for the check.
  • SHARDFLUX_NO_UPDATE_CHECK=1 or NO_UPDATE_NOTIFIER=1 turns the check off.
  • The @shardflux/sdk client inside the server runs no check of its own: the update to install is the server's.

Compatibility

@shardflux/mcp is below 1.0: tools and results may change in a minor release. Tool results are the API's objects summarized for model context; new fields can appear in any release.

View this page as Markdown