@shardflux/mcp 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,27 @@
1
1
  # Changelog
2
2
 
3
- Every tool, field and variable the README shows is available from the version named here. Below 1.0, a minor release
4
- may break compatibility; breaking changes are marked **Breaking**. Versions before 0.3.0 were not published.
3
+ Every tool, field and variable the README shows is available from the version named here. Breaking changes ship in
4
+ minor releases and are marked **Breaking**. Versions before 0.3.0 were not published.
5
+
6
+ ## 0.5.2
7
+
8
+ Needs `@shardflux/sdk` 0.11.1 (the workspace version).
9
+
10
+ Wording: the server instructions, tool descriptions and error hints say what to do, without internals (no behaviour
11
+ change).
12
+
13
+ - `send_feedback` goes to the Shardflux team (`note`: `Delivered to the Shardflux team. Keep sending feedback as you
14
+ work.`); the instructions and description ask agents to also pass on what their user asks for (a capability, an
15
+ option or a smoother workflow) and their ideas. `error_code` shows `e.g. template_not_found`.
16
+ - The instructions: a queued start (open, resume, fork) has a deadline 15 minutes after it was created; past it, it
17
+ fails with code `operation_failed`, `details.error_code` `capacity_unavailable` and `retryable` true. Nothing was
18
+ started, so send it again. The `capacity_unavailable` message ends `The start passed its deadline and nothing was
19
+ started; send it again.`
20
+ - `exec` on a file-first workspace and the instructions say `an execution runs to completion` (before: "cannot be
21
+ canceled"); the timeout message says `(a file-first execution runs to completion)`.
22
+ - Hints: `no_execution_host`: `The execution cannot be placed right now and nothing ran; retry in a few seconds.`
23
+ `host_feature_unavailable`: `search_files is not available for this workspace. Search with exec instead, ...
24
+ Retrying does not help.` (the same for `edit_file` and other tools). `mode_not_available` drops "yet".
5
25
 
6
26
  ## 0.5.1
7
27
 
@@ -16,8 +36,7 @@ it. The result's `message` then says `the workspace is suspended as soon as it i
16
36
 
17
37
  Needs `@shardflux/sdk` 0.11.0 (the workspace version).
18
38
 
19
- A resume that restarted processes says so (cold boot, contracts §12). When the platform's VM runtime changed after a
20
- suspend and no host can restore the memory snapshot, the cell resumes the workspace by booting its saved disk,
39
+ A resume that restarted processes says so (cold boot). After a platform runtime change, the cell resumes the workspace by booting its saved disk,
21
40
  automatically, also when a workspace tool wakes it. Files are as of the suspend; every process was restarted.
22
41
 
23
42
  - `timing.server` of results and error results adds `memory_restored` (when the API reports it: `false` for a cold
@@ -59,7 +78,7 @@ Opt-in overage with a spend cap (through `@shardflux/sdk` 0.10.0):
59
78
  - A start refused with 402 `allowance_exhausted` comes back with `reason` `allowance_used`, `overage_paused` or
60
79
  `spend_cap_reached` and `details.spend_cap` (errors already carried `details`).
61
80
 
62
- Suspend when idle (contracts §20.6):
81
+ Suspend when idle:
63
82
 
64
83
  - `workspace_suspend` takes an optional `after_seconds` (30-3600): suspend when idle instead of now. The workspace is
65
84
  suspended once it has been idle that long; the agent's next tool call on it cancels that, and a running command or a
@@ -69,11 +88,11 @@ Suspend when idle (contracts §20.6):
69
88
  - Workspace summaries (`workspace_status`, `workspace_list`, `workspace_open`) carry `suspend_request`: the pending
70
89
  request, or null.
71
90
 
72
- ## 0.4.0 (not yet published; npm `latest` is 0.3.1)
91
+ ## 0.4.0
73
92
 
74
93
  Needs `@shardflux/sdk` 0.9.0 (the workspace version).
75
94
 
76
- File-first workspaces (contracts §29):
95
+ File-first workspaces:
77
96
 
78
97
  - `workspace_open` takes `mode` (`processful` | `file_first`); `SHARDFLUX_WORKSPACE_MODE` sets the default. Workspace
79
98
  results (`workspace_open`, `workspace_status`, `workspace_list`) carry `mode` and, for file-first workspaces,
@@ -92,7 +111,7 @@ File-first workspaces (contracts §29):
92
111
  `current_tree_revision`), `outside_tree_root`, `execution_in_progress`, `execution_id_reused`, `no_execution_host`.
93
112
  - The server instructions describe file-first workspaces.
94
113
 
95
- Waking a suspended workspace is one request (contracts §22.6, through `@shardflux/sdk` 0.9.0):
114
+ Waking a suspended workspace is one request (through `@shardflux/sdk` 0.9.0):
96
115
 
97
116
  - A workspace tool on a suspended workspace wakes it with one held resume that returns the running workspace and this
98
117
  server's tool token (its agent label), then runs the tool; the wake's `timing` is one `request(held)` phase. An API
@@ -100,7 +119,7 @@ Waking a suspended workspace is one request (contracts §22.6, through `@shardfl
100
119
  - `workspace_resume` with `wait` goes through the server's workspace handle: one held request, after which the handle
101
120
  holds the view and the token, so the next workspace tool starts without a token request.
102
121
 
103
- File tools and the wake hint (contracts §26):
122
+ File tools and the wake hint:
104
123
 
105
124
  - `search_files` (read-only) and `edit_file` come from the SDK's `workspaceTools()` with its schemas, plus
106
125
  `workspace_key` and `timeout_ms`, filtered by the `files` permission. The server instructions describe them.
@@ -108,19 +127,19 @@ File tools and the wake hint (contracts §26):
108
127
  fire-and-forget; a suspended workspace starts resuming in the background, bounded like any wake by
109
128
  `SHARDFLUX_WAKE_TIMEOUT_MS` and the call's deadline), except `read_file`, `list_files` and `search_files`.
110
129
  - `read_file`, `list_files` and `search_files` of a suspended workspace whose disk a host still holds are answered
111
- from that disk without resuming it (contracts §26.4), also as the first call after the suspend: the API now issues
130
+ from that disk without resuming it, also as the first call after the suspend: the API now issues
112
131
  tool tokens for suspended workspaces. Every other tool still resumes it. With an older API they resume it as before.
113
- - A workspace on a host that predates search and patches (contracts §26.7, while the fleet is upgraded):
132
+ - A workspace without search and patches:
114
133
  `search_files` and `edit_file` return `conflict`, `reason: host_feature_unavailable` (`details.feature`
115
134
  `file_search` or `file_patch`), `retryable: false`, with a `hint` naming another way (`exec` with `grep`;
116
- `read_file` then `write_file`). They work once the workspace runs on an upgraded host. `hostFeatureHint()` is
135
+ `read_file` then `write_file`). `hostFeatureHint()` is
117
136
  exported next to `modeHint()`.
118
137
  - New refusals come back as error results with their `details.reason`: `revision_mismatch` (with
119
138
  `current_revision`), `edit_not_found` / `edit_ambiguous` (with `index`), `host_capacity` (retryable). A read of a
120
139
  sleeping workspace its disk cannot answer (`offline_unavailable`, `offline_budget`) wakes it like any
121
140
  `workspace_not_running`; `offline_changed` (503) is retried.
122
141
 
123
- Version check (contracts §30.4; needs an API that serves `GET /v1/client-versions`):
142
+ Version check (needs an API that serves `GET /v1/client-versions`):
124
143
 
125
144
  - Version check at startup: after the configuration is validated, the server asks `GET /v1/client-versions` (through
126
145
  its own fetch, the SDK's `checkClientVersion`: one request, 3 s timeout, never awaited by the protocol) whether
@@ -141,7 +160,7 @@ Version check (contracts §30.4; needs an API that serves `GET /v1/client-versio
141
160
  authenticates with a project API key, which the API refuses for account actions (403 `forbidden` on the account
142
161
  routes, 401 on the `/v1/auth` session routes); they need a person's session (`sfu_...`), which the CLI signs in.
143
162
 
144
- ### send_feedback: feedback straight to the founder (needs an API with POST /v1/feedback)
163
+ ### send_feedback: feedback straight to the Shardflux team (needs an API with POST /v1/feedback)
145
164
 
146
165
  - New management tool `send_feedback` (`message`, `category`: bug, confusing, missing, idea, praise or other; optional
147
166
  `workspace`, `request_id`, `error_code`, `command`). It calls the SDK's `sendFeedback()` with `client:
@@ -149,8 +168,8 @@ Version check (contracts §30.4; needs an API that serves `GET /v1/client-versio
149
168
  `SHARDFLUX_AGENT_LABEL`); a pinned server fills `workspace` with its key. Result: `{id, received_at, duplicate,
150
169
  note}`. Always listed (any API key may send feedback).
151
170
  - The server instructions and the tool description ask the agent to call it actively while it works: the moment a
152
- call fails unexpectedly, an error or doc is confusing, something is missing or slow, or it needed a workaround, and
153
- when its user is frustrated about Shardflux or asked for something it could not do (paraphrased, without private
171
+ call fails unexpectedly, an error or doc is confusing, something is missing, or it needed a workaround, and
172
+ when its user asks for something new (paraphrased, without private
154
173
  data; the agent tells its user it sent feedback).
155
174
  - Failed calls carry a `feedback` field next to `error`, except the expected flow: `invalid_arguments`,
156
175
  `workspace_pinned`, `unauthenticated`, `validation_failed`, `timeout`, and refusals whose error names the next step
@@ -198,8 +217,7 @@ Needs `@shardflux/sdk` 0.7.0 or later. New dependency: `yaml` (template.yaml).
198
217
 
199
218
  Needs `@shardflux/sdk` 0.6.2 (the workspace version).
200
219
 
201
- The API no longer lets a start (open, resume, fork) wait in `capacity_pending` forever: one that no host could admit
202
- 15 minutes after it began fails with `capacity_unavailable` (retryable; nothing was started, a suspended workspace
220
+ The API no longer lets a start (open, resume, fork) wait in `capacity_pending` forever: one still queued 15 minutes after it began fails with `capacity_unavailable` (retryable; nothing was started, a suspended workspace
203
221
  stays suspended).
204
222
 
205
223
  - Failed-operation errors (`code: operation_failed`) carry `retryable`, the operation error's own flag, and
package/README.md CHANGED
@@ -14,8 +14,8 @@ with the scoped project API key you give it. Each tool call becomes one SDK requ
14
14
 
15
15
  Documentation: <https://docs.shardflux.dev/reference/mcp>.
16
16
 
17
- > **Early access.** Shardflux is in early access, and this server is below 1.0: tools and results
18
- > may still change in a minor release.
17
+ > **Compatibility.** The API is versioned (`/v1`). Breaking changes ship only in minor releases and are marked
18
+ > **Breaking** in the changelog.
19
19
 
20
20
  Requires Node.js 24 or later and a Shardflux project API key (`sfk_...`, from the Shardflux console).
21
21
 
@@ -174,7 +174,7 @@ environment `SHARDFLUX_API_KEY`.
174
174
  | `SHARDFLUX_WAKE_TIMEOUT_MS` | Longest wait per tool call for a workspace to wake or finish a transition, 1000-3600000 ms. Default 120000. It is clamped to `SHARDFLUX_MCP_TOOL_TIMEOUT_MS`, and each wake also ends 250 ms before the call's own deadline. |
175
175
  | `SHARDFLUX_AGENT_LABEL` | Attribution label of every tool token this server obtains. Default `mcp`. It appears as an agent session in `GET /v1/workspaces/{id}/agent-sessions`, the console and `shard ws sessions`. |
176
176
  | `SHARDFLUX_MCP_LOG_LEVEL` | `debug`, `info` (default), `warn` or `error`. Logs are JSON lines on **stderr**; stdout is the protocol. The key is never logged, and anything key-shaped is redacted. |
177
- | `SHARDFLUX_HTTP_KEEPALIVE=1` | Reuse HTTP connections. By default every request uses a fresh connection (`Connection: close`), because on some Node.js releases a request sent on a reused keep-alive connection can stall until the request timeout. |
177
+ | `SHARDFLUX_HTTP_KEEPALIVE=1` | Reuse HTTP connections. By default every request uses a fresh connection (`Connection: close`). |
178
178
  | `SHARDFLUX_NO_UPDATE_CHECK` | (0.4.0) `1`, `true`, `yes` or `on` turns off the startup version check (see "Updates"). |
179
179
  | `NO_UPDATE_NOTIFIER` | (0.4.0) The npm convention: any non-empty value also turns the check off. |
180
180
 
@@ -189,11 +189,9 @@ working directory: the directory your MCP client starts it in.
189
189
 
190
190
  ## Feedback: agents are asked to use `send_feedback` while they work
191
191
 
192
- **(0.4.0+)** The `send_feedback` tool sends a message straight to the Shardflux founder, who reads every one. The
192
+ **(0.4.0+)** The `send_feedback` tool sends a message straight to the Shardflux team, who read every one. The
193
193
  server instructions and the tool description ask the agent to call it actively during its work, not once at the end:
194
- the moment a call fails unexpectedly, an error or doc is confusing, something is missing or slow, or it needed a
195
- workaround; when its user complains or is frustrated about Shardflux or the workflow around it (paraphrased, with what
196
- they were trying to do); and when its user asked for something Shardflux could not do, or made awkward. Short and
194
+ the moment a call fails unexpectedly, an error or doc is confusing, something is missing, or it needed a workaround; when its user asks for a capability, an option or a smoother workflow (paraphrased, with what they were trying to do); and when its user has an idea for how Shardflux could fit their work better. Short and
197
195
  frequent beats polished, with the `request_id` and error code from the error. The agent tells its user it sent
198
196
  feedback, and leaves out private data about the user and secrets or code they did not mean to share (paraphrase, never
199
197
  transcripts). Failed calls carry a ready-made suggestion in a `feedback` field (see "Contract").
@@ -242,9 +240,7 @@ The management tools:
242
240
  | `workspace_fork` | Fork into `new_key`. |
243
241
  | `operation_wait` | Keep waiting for an operation. |
244
242
  | `usage_summary` | The organization's usage summary for the current period: meters, allowances with their cap state, `allowance_exhausted` with `exhausted_reason`, and (0.4.1+, an API with opt-in overage) `spend_cap`: the overage state, cap, charges, lines per allowance and the date the cap is projected to be reached. |
245
-
246
- | `usage_summary` | The organization's usage summary for the current period. |
247
- | `send_feedback` | (0.4.0) Feedback straight to the Shardflux founder: `message`, `category`, and optional `workspace`, `request_id`, `error_code`, `command`. See [Feedback](#feedback-agents-are-asked-to-use-send_feedback-while-they-work). |
243
+ | `send_feedback` | (0.4.0) Feedback straight to the Shardflux team: `message`, `category`, and optional `workspace`, `request_id`, `error_code`, `command`. See [Feedback](#feedback-agents-are-asked-to-use-send_feedback-while-they-work). |
248
244
  | `template_get` | (0.3.0) A template's versions with their settings (env, inputs, start commands, services, defaults); with `version`, that version's recipe in request form. |
249
245
  | `template_languages` | (0.3.0) The languages and versions a base (`<slug>@<version>`) offers `build.languages`. |
250
246
  | `template_build` | (0.3.0) Build a version of an organization template from a recipe v2: `recipe` (the document) or `file` (a template.yaml or .json path). File entries may name local `from` paths, 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`; `wait: true` follows the build within the call's deadline. |
@@ -259,24 +255,23 @@ The SDK's workspace tools come from `workspaceTools()`:
259
255
  - `browser_screenshot`, `browser_content`
260
256
 
261
257
  They are published with **the SDK's JSON Schemas verbatim**, plus `workspace_key` and, where the
262
- SDK schema has none, `timeout_ms`. They are filtered by the key's tool permissions (if the server
263
- cannot read them, for example because the API is unreachable, it lists every workspace tool and logs
264
- a warning). `exec`'s `cwd` is an absolute path (default `/home/user`): a relative one is an error result with
265
- `details.reason: invalid_cwd` whose message names the absolute path it likely means, and a command that could not
266
- start (a `cwd` that is not a directory, a program not on `PATH`) returns `exit_code: null` with `error: { code,
267
- message, reason: "exec_failed_to_start" }` naming the workspace's reason (0.4.1+). `browser_screenshot` returns an MCP image content block. `search_files` is annotated read-only; `edit_file` replaces
268
- exact text and, without an `expected_revision`, reads the file's revision first so a concurrent change fails the
258
+ SDK schema has none, `timeout_ms`. They are filtered by the key's tool permissions when the server
259
+ can read them; otherwise it lists every workspace tool. `exec`'s `cwd` is an absolute path (default
260
+ `/home/user`): a relative one is an error result with `details.reason: invalid_cwd` whose message names the absolute
261
+ path it likely means, and a command that could not start (a `cwd` that is not a directory, a program not on `PATH`)
262
+ returns `exit_code: null` with `error: { code, message, reason: "exec_failed_to_start" }` naming the workspace's
263
+ reason (0.4.1+). `browser_screenshot` returns an MCP image content block. `search_files` is annotated read-only;
264
+ `edit_file` replaces exact text and, without an `expected_revision`, reads the file's revision first so a concurrent change fails the
269
265
  edit (`conflict`, `details.reason: revision_mismatch`) instead of being overwritten.
270
266
 
271
- Each workspace tool call first sends the wake hint (`POST /wake-hint`, without waiting for it): a workspace its host
272
- has parked starts restoring while the call is prepared, and a suspended one starts resuming. `read_file`,
273
- `list_files` and `search_files` send none (0.4.0+): a suspended workspace whose disk its host still holds is read,
267
+ Each workspace tool call first sends the wake hint (`POST /wake-hint`, without waiting for it): a parked workspace
268
+ starts restoring while the call is prepared, and a suspended one starts resuming. `read_file`,
269
+ `list_files` and `search_files` send none (0.4.0+): a suspended workspace is read from its saved disk,
274
270
  listed and searched there without resuming it (the state at suspension).
275
271
 
276
- While the fleet is being upgraded a workspace may run on a host that predates `search_files` and `edit_file`: they
277
- fail with `conflict`, `reason: host_feature_unavailable` (`details.feature` `file_search` or `file_patch`),
278
- `retryable: false` and a `hint` naming another way (`exec` with `grep`; `read_file` then `write_file`). It lasts
279
- until the workspace runs on an upgraded host.
272
+ If search or patches are not available for a workspace, `search_files` and `edit_file` fail with `conflict`,
273
+ `reason: host_feature_unavailable` (`details.feature` `file_search` or `file_patch`), `retryable: false` and a `hint`
274
+ naming the fallback (`exec` with `grep`; `read_file` then `write_file`).
280
275
 
281
276
  Deleting a workspace is deliberately not exposed to agents; use the
282
277
  [CLI](https://www.npmjs.com/package/@shardflux/cli) or the console.
@@ -295,13 +290,13 @@ the state of the template's start commands and services (`failed` names the step
295
290
 
296
291
  ### File-first workspaces (0.4.0)
297
292
 
298
- `workspace_open` with `mode: "file_first"` (or `SHARDFLUX_WORKSPACE_MODE=file_first`) opens a file-first workspace
299
- (contracts §29): a versioned file tree under /home/user with no VM between calls. It is ready at once and never
293
+ `workspace_open` with `mode: "file_first"` (or `SHARDFLUX_WORKSPACE_MODE=file_first`) opens a file-first workspace:
294
+ a versioned file tree under /home/user with no VM between calls. It is ready at once and never
300
295
  suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_revision`.
301
296
 
302
297
  - `exec` runs each command as an execution: a fresh VM on the workspace's files. Only files under /home/user persist
303
298
  between calls. The result adds `execution_id`, `state`, `tree_revision` and `changed` (the paths the command added,
304
- modified or deleted, up to 200, with `changed_truncated`). An execution cannot be canceled: when the call's
299
+ modified or deleted, up to 200, with `changed_truncated`). An execution runs to completion: when the call's
305
300
  deadline passes first, the error carries its `execution_id` and the execution continues server side.
306
301
  - The files tools work as on a processful workspace. Each change publishes the next tree revision.
307
302
  - The process, terminal, git and browser tools and `workspace_suspend`, `workspace_resume`, `workspace_fork` and
@@ -311,17 +306,17 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
311
306
  `notifications/tools/list_changed`. An unpinned server lists every tool; calling one the named workspace's mode
312
307
  lacks returns an error with `reason: "not_supported_for_mode"` and a `hint`, before any request.
313
308
  - Errors of file-first workspaces carry `reason` and a `hint`: `not_supported_for_mode`, `mode_mismatch` (a key's
314
- mode never changes), `mode_not_available` (the deployment does not offer file-first workspaces), `layout_unsupported`
309
+ mode never changes), `mode_not_available` (the account does not have file-first workspaces), `layout_unsupported`
315
310
  (a legacy template), `tree_revision_mismatch` (with `current_tree_revision`), `outside_tree_root`,
316
- `execution_in_progress` (another execution holds the workspace), `execution_id_reused` and `no_execution_host` (no
317
- host had room, also after the SDK's retries with the same execution id; nothing ran).
311
+ `execution_in_progress` (another execution holds the workspace), `execution_id_reused` and `no_execution_host`
312
+ (the execution cannot be placed right now; retried with the same id; nothing ran).
318
313
 
319
314
  ```json
320
315
  {
321
316
  "mcpServers": {
322
317
  "shardflux": {
323
- "command": "node",
324
- "args": ["/path/to/packages/mcp/src/bin.ts"],
318
+ "command": "npx",
319
+ "args": ["-y", "@shardflux/mcp"],
325
320
  "env": { "SHARDFLUX_API_KEY": "sfk_...", "SHARDFLUX_WORKSPACE_KEY": "me/agent-files", "SHARDFLUX_TEMPLATE": "python-node-browser", "SHARDFLUX_WORKSPACE_MODE": "file_first" }
326
321
  }
327
322
  }
@@ -343,25 +338,23 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
343
338
  `allowance_used` (upgrade, or turn on overage), `overage_paused` (a plan payment is past due) or
344
339
  `spend_cap_reached` (raise the spend cap or upgrade), and `details.spend_cap`. An owner or billing member acts
345
340
  on it in the console; retrying does not help.
346
-
347
341
  - **(0.4.0+)** A failed call has a `feedback` field next to `error`: a one-sentence suggestion to call
348
342
  `send_feedback` with category `bug`, the `request_id` and the error code. Not for `invalid_arguments`,
349
343
  `workspace_pinned` or `unauthenticated`. A failed `send_feedback` carries a `hint` instead (when to retry, or the
350
344
  email address).
351
- - **Starts wait for capacity for at most 15 minutes (0.2.1+).** An open, resume or fork that no host can admit waits
352
- in `capacity_pending`; a wait that ends first returns `code: timeout`, and its message names when the start gives
353
- up. A start still pending then fails: `operation_failed`, `details.error_code: capacity_unavailable`,
354
- `retryable: true`, and a message telling the agent that nothing was started and it may retry later. The server
355
- never retries it itself. The server instructions say the same.
345
+ - **Start deadlines (0.2.1+).** A queued open, resume or fork (`capacity_pending`) has a deadline 15 minutes after
346
+ it was created; a wait that ends first returns `code: timeout`, and its message names the deadline. A start still
347
+ pending then fails: `operation_failed`, `details.error_code: capacity_unavailable`, `retryable: true`, and a
348
+ message telling the agent that nothing was started and it can retry. The server instructions say the same.
356
349
  - **Timing (0.2.0+).** A call that opens a workspace or waits says where the time went, from the SDK's lifecycle
357
350
  timing, compact for model context:
358
351
 
359
352
  ```json
360
353
  "timing": {
361
- "action": "open", "outcome": "succeeded", "operation_id": "01a0e5a8-3ef0-7ecb-975e-dff2d5ca6e33", "total_ms": 34180,
362
- "phases": ["request(held) 20010 ms", "capacity_pending(no_ready_host) 13520 ms", "running 590 ms", "view 42 ms", "token 61 ms"],
363
- "server": { "queued_ms": 33400, "run_ms": 620, "total_ms": 34020, "start_path": "warm" },
364
- "outside_server_ms": 160, "retries": 0
354
+ "action": "resume", "outcome": "succeeded", "operation_id": "01a0ead6-bd61-737a-ae88-66e2d01a6e25", "total_ms": 413,
355
+ "phases": ["request 218 ms", "queued 195 ms"],
356
+ "server": { "queued_ms": 51, "run_ms": 290, "total_ms": 341, "resume_path": "local_cache" },
357
+ "outside_server_ms": 72, "retries": 0
365
358
  }
366
359
  ```
367
360
 
@@ -369,7 +362,7 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
369
362
  `workspace_fork` carry it with `wait: true`. Workspace tools carry it when they had to wake the workspace
370
363
  (`action: "wake"`). Other results do not.
371
364
  - Phases are in order, as `phase(reason) ms`. `request(held)` means the API held the open until the workspace was
372
- ready. `capacity_pending(no_ready_host)` is time spent waiting for a host.
365
+ ready. `capacity_pending(no_ready_host)` is time queued to start.
373
366
  - `server` is the operation's own queued, run and total time, plus the start or resume path the cell reported.
374
367
  A resume also has `memory_restored` (0.5.0+) when the API reports it, and `cold_boot_reason` when it is false.
375
368
  - `outside_server_ms` is everything else: network, polling, reading the workspace, the tool token.
@@ -378,7 +371,7 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
378
371
  with the first tool token, which the next tool call reuses.
379
372
  - **Deadlines.** Every call has one: `timeout_ms`, clamped to the ceiling.
380
373
  - Waits time out just before the deadline with `code: timeout` and the `operation_id`; the
381
- operation continues server side (a start waiting for capacity for at most 15 minutes).
374
+ operation continues server side (a queued start until its 15-minute deadline).
382
375
  - Any request still in flight at the deadline is aborted.
383
376
  - `exec`'s command timeout is capped at the deadline minus 2 s, so its output comes back.
384
377
  - **Cancellation.** MCP `notifications/cancelled` aborts the SDK work behind the call: waits,
@@ -386,7 +379,7 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
386
379
  the server stays up. The lifecycle operation itself is not canceled.
387
380
  - **Workspace tools wake suspended workspaces, but do not open new ones.** A workspace that was
388
381
  never opened is `not_found`: call `workspace_open` first. Reads (`read_file`, `list_files`,
389
- `search_files`) of a suspended workspace whose disk a host still holds are answered from that disk
382
+ `search_files`) of a suspended workspace are answered from its saved disk
390
383
  without waking it.
391
384
  - A suspended workspace is resumed (or the resume or open already running is joined), and the
392
385
  call runs once it is running. A call made during a suspend or resume waits for it to finish.
@@ -399,10 +392,10 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
399
392
  suspended).
400
393
  - With `SHARDFLUX_WAKE=off` the API's refusal comes back instead (`conflict`,
401
394
  `details.reason: workspace_not_running`, or `workspace_not_running` from the cell gateway).
402
- - **A resume can restart processes (0.5.0+).** A resume normally restores memory and running processes. When the
403
- platform's VM runtime changed after the suspend and no host can restore that memory snapshot, the resume boots
404
- the workspace's saved disk instead (a cold boot), also when a workspace tool wakes it. The call still runs, and
405
- files are as of the suspend, but every process was restarted. The result (or the error result) then carries
395
+ - **Detecting a cold resume (0.5.0+).** A resume restores memory and running processes. After a platform runtime
396
+ update, a resume can boot from the saved disk instead of restoring memory (a cold boot), also when a workspace
397
+ tool wakes it; check `memory_restored`. The call still runs with the files as of the suspend, and processes start
398
+ fresh. The result (or the error result) then carries
406
399
  `timing.server.resume_path: "cold_boot"`, `memory_restored: false`, `cold_boot_reason` (e.g. `runtime_changed`)
407
400
  and a `notice` the agent reads: to start its dev servers, databases, watchers and background jobs again. The
408
401
  server instructions and the `workspace_resume` description say so too.
package/dist/errors.d.ts CHANGED
@@ -22,18 +22,18 @@ export interface ToolErrorInfo {
22
22
  details?: Record<string, unknown>;
23
23
  issues?: string[];
24
24
  last_state?: string;
25
- /** What to do instead (0.4.0): refusals that concern a workspace's mode (contracts §29) or a host that predates a tool (§26.7). */
25
+ /** What to do instead (0.4.0): refusals that concern a workspace's mode or a tool that is not available for the workspace. */
26
26
  hint?: string;
27
27
  }
28
28
  export declare function redact(text: string, key?: string): string;
29
29
  /**
30
- * What the model can do about a refusal that concerns a workspace's mode (contracts §29.7, §29.8), from its
30
+ * What the model can do about a refusal that concerns a workspace's mode, from its
31
31
  * details.reason and details; undefined for every other refusal.
32
32
  */
33
33
  export declare function modeHint(reason: string | undefined, details?: Record<string, unknown>): string | undefined;
34
34
  /**
35
- * 409 conflict host_feature_unavailable (contracts §26.7; 0.4.0): the workspace runs on a host that predates the tool,
36
- * until it runs on an upgraded host (minutes to hours). Not retryable, so the hint names another way to do it now.
35
+ * 409 conflict host_feature_unavailable (0.4.0): the tool is not available for this workspace. Not retryable, so the
36
+ * hint names the fallback that works now.
37
37
  */
38
38
  export declare function hostFeatureHint(reason: string | undefined, details?: Record<string, unknown>): string | undefined;
39
39
  export interface DescribeContext {
package/dist/errors.js CHANGED
@@ -3,8 +3,8 @@
3
3
  * content is `{ "error": { code, message, ... } }` with the API's closed error
4
4
  * code, never as protocol failures, so the calling model can react to them.
5
5
  * API and cell refusals add `reason` (their details.reason) and, for refusals
6
- * that concern a workspace's mode (file-first workspaces, contracts §29) or a
7
- * host that predates a tool (host_feature_unavailable, contracts §26.7), a
6
+ * that concern a workspace's mode (file-first workspaces) or a
7
+ * tool that is not available for the workspace (host_feature_unavailable), a
8
8
  * `hint` saying what to do instead.
9
9
  */
10
10
  import { OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TemplateUploadError, ToolArgumentError, TreeRevisionMismatchError } from '@shardflux/sdk';
@@ -31,7 +31,7 @@ const str = (v) => (typeof v === 'string' && v !== '' ? v : undefined);
31
31
  /** The tools a file-first workspace has, for hints. */
32
32
  const FILE_FIRST_TOOLS_TEXT = 'Run commands with exec and use read_file, write_file, list_files, search_files and edit_file for files; only files under /home/user persist between exec calls.';
33
33
  /**
34
- * What the model can do about a refusal that concerns a workspace's mode (contracts §29.7, §29.8), from its
34
+ * What the model can do about a refusal that concerns a workspace's mode, from its
35
35
  * details.reason and details; undefined for every other refusal.
36
36
  */
37
37
  export function modeHint(reason, details = {}) {
@@ -52,7 +52,7 @@ export function modeHint(reason, details = {}) {
52
52
  : "A workspace's mode never changes: open this key with the mode it was created with, or use another key.";
53
53
  }
54
54
  case 'mode_not_available':
55
- return 'This deployment does not offer file-first workspaces yet: open the workspace with mode "processful" (or without mode).';
55
+ return 'This deployment does not offer file-first workspaces: open the workspace with mode "processful" (or without mode).';
56
56
  case 'layout_unsupported':
57
57
  return mode === 'file_first'
58
58
  ? 'File-first workspaces run on layered template versions and this one is not: pick another template (template_get lists its versions and disk_layouts) or open with mode "processful".'
@@ -66,26 +66,26 @@ export function modeHint(reason, details = {}) {
66
66
  case 'execution_id_reused':
67
67
  return 'That execution id was already used for a different command; call exec again (each call uses a new execution id).';
68
68
  case 'no_execution_host':
69
- return 'No host has room to run the command now and nothing ran; retry in a few seconds.';
69
+ return 'The execution cannot be placed right now and nothing ran; retry in a few seconds.';
70
70
  default:
71
71
  return undefined;
72
72
  }
73
73
  }
74
74
  /**
75
- * 409 conflict host_feature_unavailable (contracts §26.7; 0.4.0): the workspace runs on a host that predates the tool,
76
- * until it runs on an upgraded host (minutes to hours). Not retryable, so the hint names another way to do it now.
75
+ * 409 conflict host_feature_unavailable (0.4.0): the tool is not available for this workspace. Not retryable, so the
76
+ * hint names the fallback that works now.
77
77
  */
78
78
  export function hostFeatureHint(reason, details = {}) {
79
79
  if (reason !== 'host_feature_unavailable')
80
80
  return undefined;
81
- const later = 'It will work once the workspace runs on an upgraded host; retrying now does not help.';
81
+ const retry = 'Retrying does not help.';
82
82
  switch (details.feature) {
83
83
  case 'file_search':
84
- return `This workspace's host does not support search_files yet. Search with exec instead, e.g. grep -rn -- PATTERN PATH (add -i to ignore case, -E for a regular expression). ${later}`;
84
+ return `search_files is not available for this workspace. Search with exec instead, e.g. grep -rn -- PATTERN PATH (add -i to ignore case, -E for a regular expression). ${retry}`;
85
85
  case 'file_patch':
86
- return `This workspace's host does not support edit_file yet. Use read_file, change the text, then write_file with the whole new content. ${later}`;
86
+ return `edit_file is not available for this workspace. Use read_file, change the text, then write_file with the whole new content. ${retry}`;
87
87
  default:
88
- return `This workspace's host does not support this tool yet. ${later}`;
88
+ return `This tool is not available for this workspace. ${retry}`;
89
89
  }
90
90
  }
91
91
  export function describeToolError(err, context = {}) {
@@ -101,7 +101,7 @@ export function describeToolError(err, context = {}) {
101
101
  retryable: err.retryable,
102
102
  source: err.source,
103
103
  ...(err.operationId ? { operation_id: err.operationId } : {}),
104
- // No execution_id: a refusal answers before anything ran, and the execution id stays unused (contracts §29.8).
104
+ // No execution_id: a refusal answers before anything ran, and the execution id stays unused.
105
105
  ...(err instanceof TreeRevisionMismatchError && err.currentTreeRevision !== null ? { current_tree_revision: err.currentTreeRevision } : {}),
106
106
  ...(err.details ? { details: err.details } : {}),
107
107
  ...(hint ? { hint } : {}),
@@ -115,18 +115,18 @@ export function describeToolError(err, context = {}) {
115
115
  return { code: 'timeout', message: `${err.message} Call operation_wait with this operation_id to keep waiting.`, operation_id: err.operationId, last_state: err.lastState, retryable: true };
116
116
  }
117
117
  if (err instanceof OperationFailedError) {
118
- // retryable: the operation error's own flag. capacity_unavailable (no host could admit the start before its
119
- // deadline) is retryable: nothing was started, so the agent may try again later.
118
+ // retryable: the operation error's own flag. capacity_unavailable (the start passed its deadline) is retryable:
119
+ // nothing was started, so the agent may send it again.
120
120
  const opDetails = err.operation.error?.details;
121
121
  const reason = typeof opDetails === 'object' && opDetails !== null ? opDetails.reason : undefined;
122
122
  const details = { ...(err.errorCode ? { error_code: err.errorCode } : {}), ...(typeof reason === 'string' ? { reason } : {}) };
123
- const hint = err.errorCode === 'capacity_unavailable' ? '. No host could admit it before its deadline and nothing was started; you may retry later.' : '';
123
+ const hint = err.errorCode === 'capacity_unavailable' ? '. The start passed its deadline and nothing was started; send it again.' : '';
124
124
  return { code: 'operation_failed', message: `${err.message}${hint}`, operation_id: err.operationId, retryable: err.retryable, ...(Object.keys(details).length ? { details } : {}) };
125
125
  }
126
126
  if (named(err, 'TimeoutError')) {
127
- // A file-first execution cannot be canceled: it runs to its end server side and then publishes what it changed.
127
+ // A file-first execution runs to completion server side and then publishes what it changed.
128
128
  const continues = context.executionId
129
- ? `; execution ${context.executionId} continues server side (a file-first execution cannot be canceled): the files it changes are published when it ends, and the next exec or file change on this workspace waits for it`
129
+ ? `; execution ${context.executionId} continues server side (a file-first execution runs to completion): the files it changes are published when it ends, and the next exec or file change on this workspace waits for it`
130
130
  : context.operationId
131
131
  ? '; the operation continues server side (use operation_wait)'
132
132
  : '';
package/dist/http.d.ts CHANGED
@@ -1,13 +1,6 @@
1
1
  /**
2
- * The fetch given to the SDK: the runtime's fetch with `Connection: close` on
3
- * every request (API and cell gateway).
4
- *
5
- * Why: Node 26.7's bundled undici (8.9.0) intermittently stalls a request
6
- * sent on a reused keep-alive connection until an unrelated timer fires (up to
7
- * ~30 s, the SDK's request timeout, after which the SDK retries). Reproduced
8
- * with bare fetch against both node:http and Fastify servers; Node 22 (undici
9
- * 6.28) and `Connection: close` do not stall. A fresh connection per request
10
- * costs one TCP/TLS handshake, which is negligible for these call rates.
11
- * Set SHARDFLUX_HTTP_KEEPALIVE=1 to use the runtime's keep-alive pooling.
2
+ * The fetch given to the SDK: the runtime's fetch (API and cell gateway). By
3
+ * default every request uses a fresh connection (`Connection: close`);
4
+ * SHARDFLUX_HTTP_KEEPALIVE=1 reuses connections.
12
5
  */
13
6
  export declare function makeFetch(env: Record<string, string | undefined>, base?: typeof fetch): typeof fetch;
package/dist/http.js CHANGED
@@ -1,16 +1,10 @@
1
1
  /**
2
- * The fetch given to the SDK: the runtime's fetch with `Connection: close` on
3
- * every request (API and cell gateway).
4
- *
5
- * Why: Node 26.7's bundled undici (8.9.0) intermittently stalls a request
6
- * sent on a reused keep-alive connection until an unrelated timer fires (up to
7
- * ~30 s, the SDK's request timeout, after which the SDK retries). Reproduced
8
- * with bare fetch against both node:http and Fastify servers; Node 22 (undici
9
- * 6.28) and `Connection: close` do not stall. A fresh connection per request
10
- * costs one TCP/TLS handshake, which is negligible for these call rates.
11
- * Set SHARDFLUX_HTTP_KEEPALIVE=1 to use the runtime's keep-alive pooling.
2
+ * The fetch given to the SDK: the runtime's fetch (API and cell gateway). By
3
+ * default every request uses a fresh connection (`Connection: close`);
4
+ * SHARDFLUX_HTTP_KEEPALIVE=1 reuses connections.
12
5
  */
13
6
  export function makeFetch(env, base = fetch) {
7
+ // Why a fresh connection by default (Node 26 keep-alive measurements): docs/progress/startup-latency.md.
14
8
  if (env.SHARDFLUX_HTTP_KEEPALIVE === '1')
15
9
  return base;
16
10
  return (input, init) => {
package/dist/server.d.ts CHANGED
@@ -3,8 +3,8 @@ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
3
3
  import type { ClientVersionStatus, JsonSchema, LifecycleTiming, Operation, TemplateBuild, TemplateDetail, TemplateOwner, ToolName, WorkspaceMode, WorkspaceTool, WorkspaceView } from '@shardflux/sdk';
4
4
  import type { McpConfig } from './config.js';
5
5
  import type { ToolErrorInfo } from './errors.js';
6
- export declare const MCP_SERVER_VERSION = "0.5.1";
7
- /** The package this server is distributed as: its entry in GET /v1/client-versions (contracts §30.4). */
6
+ export declare const MCP_SERVER_VERSION = "0.5.2";
7
+ /** The package this server is distributed as: its entry in GET /v1/client-versions. */
8
8
  export declare const MCP_PACKAGE = "@shardflux/mcp";
9
9
  export declare const ALL_TOOL_PERMISSIONS: readonly ToolName[];
10
10
  export type ObjectSchema = JsonSchema & {
@@ -185,7 +185,7 @@ export declare function summarizeOperation(o: Operation): {
185
185
  /**
186
186
  * The SDK's LifecycleTiming, compact enough for model context: where an open, a wait or a wake spent its time. Phases
187
187
  * are `phase(reason) ms` in order (`request(held)`: the server held the open; `capacity_pending(no_ready_host)`:
188
- * waiting for a host); `server` is the operation's own queued/run/total time; `outside_server_ms` is the rest
188
+ * queued); `server` is the operation's own queued/run/total time; `outside_server_ms` is the rest
189
189
  * (network, polling, view and token).
190
190
  */
191
191
  export declare function compactTiming(t: LifecycleTiming): {
@@ -239,7 +239,7 @@ export interface ServerOptions {
239
239
  versionCheck?: boolean;
240
240
  }
241
241
  /**
242
- * The startup version check (0.4.0; contracts §30.4): one GET <api>/v1/client-versions through the server's fetch
242
+ * The startup version check (0.4.0): one GET <api>/v1/client-versions through the server's fetch
243
243
  * (the SDK's checkClientVersion: 3 s timeout, never throws) for @shardflux/mcp at MCP_SERVER_VERSION. Outdated or
244
244
  * unsupported: one `warn` line whose message is the notice (`@shardflux/mcp 0.4.0 is outdated: 0.5.0 is available.
245
245
  * Update: <upgrade command>`) plus the status fields. Anything else (current; unknown: not listed, `latest` null while
package/dist/server.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * - templates (0.3.0): template_get (versions, settings, a version's recipe), template_languages and
9
9
  * template_build (a recipe v2 object or a template.yaml path inside the
10
10
  * server's working directory; local `from` paths are uploaded by the SDK);
11
- * - send_feedback (0.4.0): feedback straight to the Shardflux founder; the instructions, its description and
11
+ * - send_feedback (0.4.0): feedback straight to the Shardflux team; the instructions, its description and
12
12
  * the `feedback` field of error results ask agents to use it while they work;
13
13
  * - the SDK's workspace tools (`workspaceTools()`: exec, files, processes,
14
14
  * terminal, git, browser), published with the SDK's own JSON Schemas plus
@@ -21,14 +21,14 @@
21
21
  * call first sends the wake hint (the SDK tool runner's `workspace.hint()`,
22
22
  * fire-and-forget) so a parked workspace restores while the call is prepared,
23
23
  * except read_file, list_files and search_files, which a sleeping workspace
24
- * answers from its disk without waking (contracts §26.4). Each call has a deadline
24
+ * answers from its disk without waking. Each call has a deadline
25
25
  * (`timeout_ms`, clamped to SHARDFLUX_MCP_TOOL_TIMEOUT_MS) and MCP cancellation
26
26
  * (notifications/cancelled -> `extra.signal`) aborts the underlying SDK
27
27
  * request or wait. Failures come back as `isError` tool results carrying the
28
28
  * API error code. stdout is the protocol; logs go to stderr. Opens, waits and
29
29
  * wakes carry the SDK's lifecycle timing, compacted (`compactTiming`).
30
30
  *
31
- * File-first workspaces (0.4.0; contracts §29: `workspace_open` `mode:
31
+ * File-first workspaces (0.4.0: `workspace_open` `mode:
32
32
  * "file_first"`) have files and executions only. A pinned server lists the
33
33
  * tools of its workspace's mode (the key's mode once it resolves, else
34
34
  * SHARDFLUX_WORKSPACE_MODE, else processful) and sends
@@ -53,8 +53,8 @@ import { parse as parseYaml } from 'yaml';
53
53
  import { DEFAULT_WAKE_TIMEOUT_MS } from "./config.js";
54
54
  import { ToolError, describeToolError, redact } from "./errors.js";
55
55
  import { makeFetch } from "./http.js";
56
- export const MCP_SERVER_VERSION = '0.5.1';
57
- /** The package this server is distributed as: its entry in GET /v1/client-versions (contracts §30.4). */
56
+ export const MCP_SERVER_VERSION = '0.5.2';
57
+ /** The package this server is distributed as: its entry in GET /v1/client-versions. */
58
58
  export const MCP_PACKAGE = '@shardflux/mcp';
59
59
  const USER_AGENT = `shardflux-mcp/${MCP_SERVER_VERSION} shardflux-sdk-ts/${SDK_VERSION}`;
60
60
  export const ALL_TOOL_PERMISSIONS = ['exec', 'files', 'pty', 'process', 'git', 'browser'];
@@ -202,7 +202,7 @@ function isObject(v) {
202
202
  /**
203
203
  * The SDK's LifecycleTiming, compact enough for model context: where an open, a wait or a wake spent its time. Phases
204
204
  * are `phase(reason) ms` in order (`request(held)`: the server held the open; `capacity_pending(no_ready_host)`:
205
- * waiting for a host); `server` is the operation's own queued/run/total time; `outside_server_ms` is the rest
205
+ * queued); `server` is the operation's own queued/run/total time; `outside_server_ms` is the rest
206
206
  * (network, polling, view and token).
207
207
  */
208
208
  export function compactTiming(t) {
@@ -268,7 +268,7 @@ const FEEDBACK_EMAIL = 'shardflux@heliosone.fi';
268
268
  * When to use send_feedback, for the agent: the same words as `shard feedback --help`, in the server instructions and
269
269
  * the tool description. It has to make an agent actually call it while it works.
270
270
  */
271
- const FEEDBACK_USE = 'Call it actively while you work, not once at the end. Send a short note the moment: something fails unexpectedly, an error or doc is unclear, an option is missing, or you needed a workaround; your user complains or is frustrated about Shardflux or the workflow around it (slowness, cost or limits, confusing setup, a missing feature, a workaround): paraphrase it, with what they were trying to do; your user asked for something Shardflux could not do, or made awkward. Short and frequent beats polished; include the request_id and error code (failed calls carry a ready-made "feedback" field). No need to ask first, but tell your user you sent it. Leave out private data about the user and secrets or code they did not mean to share; paraphrase, never paste transcripts.';
271
+ const FEEDBACK_USE = 'Call it actively while you work, not once at the end. Send a short note the moment: something fails unexpectedly, an error or doc is unclear, an option is missing, or you needed a workaround; your user asks for a capability, an option or a smoother workflow: paraphrase it, with what they were trying to do; your user has an idea for how Shardflux could fit their work better. Short and frequent beats polished; include the request_id and error code (failed calls carry a ready-made "feedback" field). No need to ask first, but tell your user you sent it. Leave out private data about the user and secrets or code they did not mean to share; paraphrase, never paste transcripts.';
272
272
  /**
273
273
  * No send_feedback suggestion for the expected flow: argument, configuration or credential problems of the call itself,
274
274
  * a wait that gave up while the work continues, and refusals whose error names the exact next step (the same list as
@@ -302,18 +302,18 @@ const INSTRUCTIONS = [
302
302
  'Call workspace_open first (it creates the workspace on first use and reconnects afterwards, never resetting it), then use exec, read_file, write_file and the other workspace tools with the same workspace_key.',
303
303
  'Lifecycle calls return operations; operation_wait keeps waiting. Workspace tools resume a suspended workspace on use (read_file, list_files and search_files read its disk without resuming it when they can); if that takes too long the error (code timeout) names the operation to pass to operation_wait. Errors are tool results with error.code from the Shardflux API.',
304
304
  'When you finish your work on a workspace, call workspace_suspend with after_seconds (e.g. 60): it is suspended once idle that long, so it stops using RAM; your next tool call on it cancels that.',
305
- 'A start (open, resume, fork) that no host can admit waits for capacity for at most 15 minutes, then fails with code operation_failed, details.error_code capacity_unavailable and retryable true: nothing was started; retry later if you still need it. retryable false means retrying will not help.',
305
+ 'A queued start (open, resume, fork) has a deadline 15 minutes after it was created; past it, it fails with code operation_failed, details.error_code capacity_unavailable and retryable true. Nothing was started, so send it again. retryable false means retrying will not help.',
306
306
  'Opens, waits and wakes add timing (phases, server queued/run time) saying where the time went.',
307
307
  'A resume (also the automatic one when a tool call finds the workspace suspended) normally restores memory and running processes. If a result carries notice and timing.server.memory_restored false (resume_path cold_boot), the workspace booted from its saved disk instead: files are kept, but every process was restarted, so start your dev servers and background jobs again.',
308
308
  'Templates: template_get shows a template’s versions and settings (the inputs workspace_open takes); template_languages lists what a base offers build.languages; template_build builds a new version from a recipe v2 (unpublished unless publish is true).',
309
309
  'search_files finds text in files under a directory; edit_file replaces exact text in a file (each old_text must occur once) and returns the new revision, which you can pass as expected_revision to the next edit_file of that file so a change made by someone else is detected.',
310
310
  'File-first workspaces (workspace_open mode "file_first"; results show mode and tree_revision) keep only files: there is no VM between calls, each exec runs in a fresh VM, and only files under /home/user persist between exec calls (install dependencies there, e.g. a virtualenv, and start servers within the command that needs them).',
311
- 'An exec result on a file-first workspace carries the paths it changed (changed) and the new tree_revision; an execution cannot be canceled. Process, terminal, git and browser tools and workspace_suspend, workspace_resume and workspace_fork do not apply to them (error reason not_supported_for_mode).',
311
+ 'An exec result on a file-first workspace carries the paths it changed (changed) and the new tree_revision; an execution runs to completion. Process, terminal, git and browser tools and workspace_suspend, workspace_resume and workspace_fork do not apply to them (error reason not_supported_for_mode).',
312
312
  'Account-level actions are not tools of this server: it works inside one project with a project API key, which the API refuses for them. Registering, signing in, organizations, projects, API keys, members, billing and plan upgrades, spend alerts and audit export are done with the shard CLI: `npx @shardflux/cli@latest --help` (for example `shard auth login`, `shard setup`, `shard billing upgrade <plan>`). A person still opens the verification email and pays on the Checkout page the CLI prints.',
313
- `Feedback: send_feedback goes straight to the Shardflux founder, who reads every message. ${FEEDBACK_USE}`,
313
+ `Feedback: send_feedback goes straight to the Shardflux team, who read every message. ${FEEDBACK_USE}`,
314
314
  ].join(' ');
315
315
  /**
316
- * The startup version check (0.4.0; contracts §30.4): one GET <api>/v1/client-versions through the server's fetch
316
+ * The startup version check (0.4.0): one GET <api>/v1/client-versions through the server's fetch
317
317
  * (the SDK's checkClientVersion: 3 s timeout, never throws) for @shardflux/mcp at MCP_SERVER_VERSION. Outdated or
318
318
  * unsupported: one `warn` line whose message is the notice (`@shardflux/mcp 0.4.0 is outdated: 0.5.0 is available.
319
319
  * Update: <upgrade command>`) plus the status fields. Anything else (current; unknown: not listed, `latest` null while
@@ -421,7 +421,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
421
421
  * Wake on use for a workspace's tools: `workspace.wake()` within the SDK's transition budget (SHARDFLUX_WAKE_TIMEOUT_MS)
422
422
  * and 250 ms short of the call's deadline, so a wake that runs out reports the operation (OperationTimeoutError) rather
423
423
  * than a bare deadline. null when SHARDFLUX_WAKE=off: the refusal (workspace_not_running) comes back instead. The
424
- * wake's held resume (contracts §22.6) returns the token of this server's tools (its agent label), so the woken call
424
+ * wake's held resume returns the token of this server's tools (its agent label), so the woken call
425
425
  * runs at once with it.
426
426
  */
427
427
  const wakeFor = (ws) => config.wake === false
@@ -480,7 +480,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
480
480
  /**
481
481
  * The workspace a key names, across every lifetime and purpose (sessions, drafts and test instances are hidden from
482
482
  * the default list), preferring the live workspace over tombstones: an ended session leaves a tombstone with the same
483
- * key, and the key then opens a new workspace (contracts §19.11). Null when the key names none.
483
+ * key, and the key then opens a new workspace. Null when the key names none.
484
484
  */
485
485
  const lookup = async (key, opts2) => {
486
486
  const cached = handles.get(key);
@@ -544,7 +544,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
544
544
  {
545
545
  name: 'workspace_open',
546
546
  title: 'Open workspace',
547
- description: 'Open a persistent workspace by key: creates it from the template on first use, reconnects (or resumes) it afterwards, never resets it. Waits until it is ready unless wait is false; on timeout the start continues server side (operation_wait), for at most 15 minutes while it waits for capacity. The result’s timing says where the time went. mode "file_first" opens a file-first workspace (files only, ready at once; see mode).',
547
+ description: 'Open a persistent workspace by key: creates it from the template on first use, reconnects (or resumes) it afterwards, never resets it. Waits until it is ready unless wait is false; on timeout the start continues server side (operation_wait); a queued start has a deadline 15 minutes after it was created. The result’s timing says where the time went. mode "file_first" opens a file-first workspace (files only, ready at once; see mode).',
548
548
  inputSchema: obj({
549
549
  workspace_key: workspaceKeyProp(pinned),
550
550
  template: {
@@ -687,7 +687,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
687
687
  },
688
688
  },
689
689
  lifecycleTool('workspace_resume', 'Resume workspace', 'Resume a suspended workspace. Returns the resume operation (with wait: once finished, and its timing). A notice in the result (timing.server.memory_restored false) means it booted from its saved disk: files kept, processes restarted. Not for file-first workspaces (never suspended).', 'resume',
690
- // Through the server's handle: a waited resume is held until the workspace runs (contracts §22.6) and the handle
690
+ // Through the server's handle: a waited resume is held until the workspace runs and the handle
691
691
  // keeps the view and this server's tool token, so the next workspace tool starts at once.
692
692
  (ws, o) => ws.resume({ ...o, agentLabel: config.agentLabel })),
693
693
  {
@@ -838,13 +838,13 @@ export function createShardfluxMcpServer(config, opts = {}) {
838
838
  management.push({
839
839
  name: 'send_feedback',
840
840
  title: 'Send feedback',
841
- description: `Send feedback straight to the Shardflux founder, who reads every message. ${FEEDBACK_USE} Categories: bug (something failed or behaved wrongly), confusing (an error, doc, name or output was unclear or misleading), missing (a capability, option or template you needed does not exist), idea, praise (something worked well), other. Rate limited per API key (rate_limited with details.retry_after_seconds); the same message within 24 hours is recorded once (duplicate: true).`,
841
+ description: `Send feedback straight to the Shardflux team, who read every message. ${FEEDBACK_USE} Categories: bug (something failed or behaved wrongly), confusing (an error, doc, name or output was unclear or misleading), missing (a capability, option or template you needed does not exist), idea, praise (something worked well), other. Rate limited per API key (rate_limited with details.retry_after_seconds); the same message within 24 hours is recorded once (duplicate: true).`,
842
842
  inputSchema: obj({
843
843
  message: { type: 'string', minLength: 1, maxLength: FEEDBACK_MESSAGE_MAX_LENGTH, description: 'What you did, what happened and what you expected (1-8000 characters). Leave secrets out.' },
844
844
  category: { type: 'string', enum: [...FEEDBACK_CATEGORIES], description: 'bug, confusing, missing, idea, praise or other.' },
845
845
  workspace: { type: 'string', minLength: 1, maxLength: 200, description: pinned ? `Workspace key or id it is about (default "${pinned}").` : 'Workspace key or id it is about.' },
846
- request_id: { type: 'string', minLength: 1, maxLength: 200, description: 'request_id from the error, so the founder can find the logs.' },
847
- error_code: { type: 'string', minLength: 1, maxLength: 100, description: 'The error code seen (error.code, or details.error_code of a failed operation), e.g. capacity_unavailable.' },
846
+ request_id: { type: 'string', minLength: 1, maxLength: 200, description: 'request_id from the error, so the team can find the logs.' },
847
+ error_code: { type: 'string', minLength: 1, maxLength: 100, description: 'The error code seen (error.code, or details.error_code of a failed operation), e.g. template_not_found.' },
848
848
  command: { type: 'string', minLength: 1, maxLength: 2000, description: 'The tool call (name and arguments) or command that led to it.' },
849
849
  }, ['message', 'category']),
850
850
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
@@ -870,7 +870,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
870
870
  id: r.id,
871
871
  received_at: r.receivedAt,
872
872
  duplicate: r.duplicate,
873
- note: r.duplicate ? 'The same message was already received in the last 24 hours; it was not emailed again.' : 'Delivered to the Shardflux founder. Keep sending feedback as you work.',
873
+ note: r.duplicate ? 'The same message was already received in the last 24 hours; it was not emailed again.' : 'Delivered to the Shardflux team. Keep sending feedback as you work.',
874
874
  };
875
875
  },
876
876
  });
@@ -883,7 +883,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
883
883
  */
884
884
  const execDescription = (sdkDescription, listing) => {
885
885
  if (listing === 'file_first')
886
- return `${sdkDescription}${execDeadline} An execution cannot be canceled: if the deadline passes first it continues server side (the error names its execution_id) and the next exec waits for it.`;
886
+ return `${sdkDescription}${execDeadline} An execution runs to completion: if the deadline passes first it continues server side (the error names its execution_id) and the next exec waits for it.`;
887
887
  if (listing === 'processful')
888
888
  return `${sdkDescription}${execDeadline}`;
889
889
  return `${sdkDescription} On a file-first workspace (workspace_open mode "file_first") each call instead runs in a fresh VM on the workspace’s files: only files under /home/user persist between calls, and the result adds execution_id, state, tree_revision and changed (the paths the command added, modified or deleted).${execDeadline}`;
@@ -921,7 +921,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
921
921
  }
922
922
  const ws = await resolve(key, { signal: call.signal });
923
923
  // The SDK's tools for the workspace's mode: a file-first workspace has exec (as executions) and the file tools.
924
- // A file-first execution cannot be canceled; its id is kept so a deadline error can name it.
924
+ // A file-first execution runs to completion; its id is kept so a deadline error can name it.
925
925
  const tool = workspaceTools(ws, {
926
926
  agentLabel: config.agentLabel,
927
927
  tools: [...ALL_TOOL_PERMISSIONS],
@@ -1045,7 +1045,7 @@ export function createShardfluxMcpServer(config, opts = {}) {
1045
1045
  ...(info.request_id ? { request_id: info.request_id } : {}),
1046
1046
  ...(info.execution_id ? { execution_id: info.execution_id } : {}),
1047
1047
  });
1048
- // A failure suggests send_feedback; a failed send_feedback says how to still reach the founder.
1048
+ // A failure suggests send_feedback; a failed send_feedback says how to still reach the Shardflux team.
1049
1049
  const s = info.details?.retry_after_seconds;
1050
1050
  const siblings = name === 'send_feedback'
1051
1051
  ? info.code === 'invalid_arguments'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shardflux/mcp",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "mcpName": "dev.shardflux/mcp",
5
5
  "type": "module",
6
6
  "description": "Shardflux MCP server (stdio): give Claude, Cursor, Codex or any MCP client persistent cloud workspaces to run commands, edit files, use git and a browser in. Uses your scoped Shardflux API key.",
@@ -46,7 +46,7 @@
46
46
  "@modelcontextprotocol/sdk": "1.30.0",
47
47
  "yaml": "2.9.1",
48
48
  "zod": "4.6.5",
49
- "@shardflux/sdk": "^0.11.0"
49
+ "@shardflux/sdk": "^0.11.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@eslint/js": "10.0.1",