# 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`.

```sh
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](#permissions)).

### Claude Code

```sh
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

```sh
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](#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](#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](#pinning). 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](#file-first-workspaces). 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](https://docs.shardflux.dev/concepts/lifecycle.md#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](https://docs.shardflux.dev/limits.md#overage-opt-in) 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](#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](https://docs.shardflux.dev/reference/cli.md) 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](https://docs.shardflux.dev/reference/typescript.md#agent-tools). 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](https://docs.shardflux.dev/guides/files.md#agent-tools-search_files-and-edit_file). 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](https://docs.shardflux.dev/concepts/file-first.md): 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](https://docs.shardflux.dev/reference/cli.md#accounts-and-sign-in) 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](https://docs.shardflux.dev/concepts/lifecycle.md#idle-running-workspaces-are-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](https://docs.shardflux.dev/reference/errors.md) | 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](https://docs.shardflux.dev/reference/errors.md#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.
