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/sdkrequest 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 isshardflux-mcp.
SHARDFLUX_API_KEY=sfk_... npx -y @shardflux/mcpThe 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
claude mcp add --env SHARDFLUX_API_KEY=sfk_... --env SHARDFLUX_TEMPLATE=python-node-browser --transport stdio shardflux \
-- npx -y @shardflux/mcpThe -- 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):
{
"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:
{
"mcpServers": {
"shardflux": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@shardflux/mcp"],
"env": {
"SHARDFLUX_API_KEY": "sfk_...",
"SHARDFLUX_TEMPLATE": "python-node-browser"
}
}
}
}Codex
codex mcp add shardflux --env SHARDFLUX_API_KEY=sfk_... --env SHARDFLUX_TEMPLATE=python-node-browser -- npx -y @shardflux/mcpOr add it to ~/.codex/config.toml (or a project's .codex/config.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):
{
"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, asagent, the MCP client's name and version frominitialize(elseSHARDFLUX_AGENT_LABELwhen it is not the default). A pinned server fillsworkspacewith 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_limitedwithdetails.retry_after_seconds, and ahintnaming 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.
execruns each command as an execution: a fresh VM on the workspace's files. Only files under/home/userpersist between calls. The result addsexecution_id,state,tree_revisionandchanged(up to 200 paths,changed_truncated). An execution cannot be canceled: when the call's deadline passes first, the error carries itsexecution_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_forkandoperation_waitdo not apply. A pinned server lists only the tools of its workspace's mode (once it is known, elseSHARDFLUX_WORKSPACE_MODE, else processful) and sendsnotifications/tools/list_changedwhen 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 ahint).
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_keybecomes optional on every tool and defaults to the pinned key.- A different key is refused with
code: workspace_pinned(details.pinned_workspace_key). workspace_openopens (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_MSand the call's deadline. Past it the result iscode: timeoutwith theoperation_idandlast_stateof the resume or open. That operation continues server side:operation_waitkeeps waiting. - A resume or open that fails returns
code: operation_failed(retryable: trueforcapacity_unavailable; the workspace stays suspended). - With
SHARDFLUX_WAKE=offthe refusal comes back instead:conflictwithdetails.reason: workspace_not_running, orworkspace_not_runningfrom 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_resumewithwait: truesends the same request. The wake'stimingis onerequest(held)phase. - (0.4.0+)
read_file,list_filesandsearch_filesof 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 toSHARDFLUX_MCP_TOOL_TIMEOUT_MS. Waits time out just before it withcode: timeoutand theoperation_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_pendingfor at most 15 minutes. A wait that ends first returnscode: timeout, and its message names when the start gives up. A start still pending then fails:operation_failedwithdetails.error_code: capacity_unavailableandretryable: true; nothing was started. The server never retries it. - MCP
notifications/cancelledaborts 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:
{
"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_openandoperation_waitalways carry it;workspace_suspend,workspace_resumeandworkspace_forkcarry it withwait: 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. serveris the operation's own queued, run and total time plus the start or resume path;outside_server_msis 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:
{"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
debugline), 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=1orNO_UPDATE_NOTIFIER=1turns the check off.- The
@shardflux/sdkclient 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.