@shardflux/mcp 0.4.3 → 0.5.1
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 +28 -1
- package/README.md +9 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/server.d.ts +9 -1
- package/dist/server.js +31 -7
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,34 @@
|
|
|
3
3
|
Every tool, field and variable the README shows is available from the version named here. Below 1.0, a minor release
|
|
4
4
|
may break compatibility; breaking changes are marked **Breaking**. Versions before 0.3.0 were not published.
|
|
5
5
|
|
|
6
|
-
## 0.
|
|
6
|
+
## 0.5.1
|
|
7
|
+
|
|
8
|
+
Needs `@shardflux/sdk` 0.11.0 (the workspace version).
|
|
9
|
+
|
|
10
|
+
`workspace_suspend` takes `after_seconds` from 0 to 3600 (before: 30 to 3600): 0 suspends the workspace as soon as it
|
|
11
|
+
is idle (a few seconds of activity flush grace after its last work); a running command or a keepalive still postpones
|
|
12
|
+
it. The result's `message` then says `the workspace is suspended as soon as it is idle`. Out of range is still
|
|
13
|
+
`invalid_arguments` before any request.
|
|
14
|
+
|
|
15
|
+
## 0.5.0
|
|
16
|
+
|
|
17
|
+
Needs `@shardflux/sdk` 0.11.0 (the workspace version).
|
|
18
|
+
|
|
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,
|
|
21
|
+
automatically, also when a workspace tool wakes it. Files are as of the suspend; every process was restarted.
|
|
22
|
+
|
|
23
|
+
- `timing.server` of results and error results adds `memory_restored` (when the API reports it: `false` for a cold
|
|
24
|
+
boot, `true` for a memory resume) and `cold_boot_reason` (e.g. `runtime_changed`), next to `resume_path`
|
|
25
|
+
(`cold_boot`).
|
|
26
|
+
- Such a result (`workspace_resume` or `operation_wait` of the resume, `workspace_open`, or any workspace tool whose
|
|
27
|
+
call woke the workspace) carries a top-level `notice` in plain language: the workspace was resumed from its saved
|
|
28
|
+
disk, files are kept, every process was restarted, so start dev servers, databases, watchers and background jobs
|
|
29
|
+
again. An error result carries it next to `error` when the wake before the failure did that.
|
|
30
|
+
- The server instructions and the `workspace_resume` description tell the agent what `notice` and
|
|
31
|
+
`memory_restored: false` mean. `processRestartNotice()` is exported with `compactTiming()`.
|
|
32
|
+
|
|
33
|
+
## 0.4.3
|
|
7
34
|
|
|
8
35
|
Fix: the server (MCP initialize, User-Agent, update check) reports 0.4.3. 0.4.2 was published reporting 0.4.1, its previous version; the build now
|
|
9
36
|
fails when `MCP_SERVER_VERSION` differs from package.json.
|
package/README.md
CHANGED
|
@@ -238,7 +238,7 @@ The management tools:
|
|
|
238
238
|
| `workspace_list` | List the project's workspaces, with `prefix`, `state`, `lifetime`, `purpose`, `include_deleted`, `limit` and `cursor`. By default only persistent standard workspaces are listed. |
|
|
239
239
|
| `workspace_status` | One workspace plus its five most recent operations. |
|
|
240
240
|
| `workspace_suspend`, `workspace_resume` | Lifecycle operation. Returns at once unless `wait: true` (then once it finished, through the SDK's own `wait`). |
|
|
241
|
-
| `workspace_suspend` with `after_seconds` | (0.4.1) Suspend when idle: the workspace is suspended once it has been idle for `after_seconds` (
|
|
241
|
+
| `workspace_suspend` with `after_seconds` | (0.4.1) Suspend when idle: the workspace is suspended once it has been idle for `after_seconds` (0-3600; 0 = as soon as it is idle, 0.5.1+), instead of now. The description tells the agent to use it when it finishes its work, so the workspace stops using RAM soon after. Its next tool call on the workspace cancels it; a running command or a keepalive postpones it. Returns `suspend_request` (`not_before`: the earliest suspend) and a `message`; `operation` when a suspend was already in progress. Not with `wait`. `workspace_status` shows a pending request as `workspace.suspend_request`. Not for file-first workspaces. |
|
|
242
242
|
| `workspace_fork` | Fork into `new_key`. |
|
|
243
243
|
| `operation_wait` | Keep waiting for an operation. |
|
|
244
244
|
| `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. |
|
|
@@ -371,6 +371,7 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
|
|
|
371
371
|
- Phases are in order, as `phase(reason) ms`. `request(held)` means the API held the open until the workspace was
|
|
372
372
|
ready. `capacity_pending(no_ready_host)` is time spent waiting for a host.
|
|
373
373
|
- `server` is the operation's own queued, run and total time, plus the start or resume path the cell reported.
|
|
374
|
+
A resume also has `memory_restored` (0.5.0+) when the API reports it, and `cold_boot_reason` when it is false.
|
|
374
375
|
- `outside_server_ms` is everything else: network, polling, reading the workspace, the tool token.
|
|
375
376
|
- A failed or timed-out call carries it too, next to `error` (`{error, timing}`).
|
|
376
377
|
- `workspace_open` uses the SDK's waited open: the API holds the request until the workspace is ready and answers
|
|
@@ -398,6 +399,13 @@ suspended. Workspace results carry `mode` and, for file-first workspaces, `tree_
|
|
|
398
399
|
suspended).
|
|
399
400
|
- With `SHARDFLUX_WAKE=off` the API's refusal comes back instead (`conflict`,
|
|
400
401
|
`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
|
|
406
|
+
`timing.server.resume_path: "cold_boot"`, `memory_restored: false`, `cold_boot_reason` (e.g. `runtime_changed`)
|
|
407
|
+
and a `notice` the agent reads: to start its dev servers, databases, watchers and background jobs again. The
|
|
408
|
+
server instructions and the `workspace_resume` description say so too.
|
|
401
409
|
- Tool tokens are never returned to the model.
|
|
402
410
|
- **Keys, not ids.** Tools take workspace keys, resolved by exact match within the key's project.
|
|
403
411
|
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `shardflux-mcp` (bin.ts); `createShardfluxMcpServer()` builds the same server for any
|
|
5
5
|
* MCP transport.
|
|
6
6
|
*/
|
|
7
|
-
export { ALL_TOOL_PERMISSIONS, MCP_PACKAGE, MCP_SERVER_VERSION, checkServerVersion, compactTiming, createShardfluxMcpServer, errorResult, feedbackSuggestion, okResult, sdkToolDefinitions, summarizeBuild, summarizeOperation, summarizeTemplate, summarizeWorkspace } from './server.js';
|
|
7
|
+
export { ALL_TOOL_PERMISSIONS, MCP_PACKAGE, MCP_SERVER_VERSION, checkServerVersion, compactTiming, createShardfluxMcpServer, errorResult, feedbackSuggestion, okResult, processRestartNotice, sdkToolDefinitions, summarizeBuild, summarizeOperation, summarizeTemplate, summarizeWorkspace } from './server.js';
|
|
8
8
|
export type { Logger, ObjectSchema, ServerOptions } from './server.js';
|
|
9
9
|
export { ConfigError, DEFAULT_TOOL_TIMEOUT_MS, DEFAULT_WAKE_TIMEOUT_MS, MAX_TOOL_TIMEOUT_MS, apiUrlFrom, loadConfig } from './config.js';
|
|
10
10
|
export type { McpConfig } from './config.js';
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `shardflux-mcp` (bin.ts); `createShardfluxMcpServer()` builds the same server for any
|
|
5
5
|
* MCP transport.
|
|
6
6
|
*/
|
|
7
|
-
export { ALL_TOOL_PERMISSIONS, MCP_PACKAGE, MCP_SERVER_VERSION, checkServerVersion, compactTiming, createShardfluxMcpServer, errorResult, feedbackSuggestion, okResult, sdkToolDefinitions, summarizeBuild, summarizeOperation, summarizeTemplate, summarizeWorkspace } from "./server.js";
|
|
7
|
+
export { ALL_TOOL_PERMISSIONS, MCP_PACKAGE, MCP_SERVER_VERSION, checkServerVersion, compactTiming, createShardfluxMcpServer, errorResult, feedbackSuggestion, okResult, processRestartNotice, sdkToolDefinitions, summarizeBuild, summarizeOperation, summarizeTemplate, summarizeWorkspace } from "./server.js";
|
|
8
8
|
export { ConfigError, DEFAULT_TOOL_TIMEOUT_MS, DEFAULT_WAKE_TIMEOUT_MS, MAX_TOOL_TIMEOUT_MS, apiUrlFrom, loadConfig } from "./config.js";
|
|
9
9
|
export { ToolError, describeToolError, hostFeatureHint, modeHint, redact } from "./errors.js";
|
|
10
10
|
export { main, stderrLogger } from "./main.js";
|
package/dist/server.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ 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.
|
|
6
|
+
export declare const MCP_SERVER_VERSION = "0.5.1";
|
|
7
7
|
/** The package this server is distributed as: its entry in GET /v1/client-versions (contracts §30.4). */
|
|
8
8
|
export declare const MCP_PACKAGE = "@shardflux/mcp";
|
|
9
9
|
export declare const ALL_TOOL_PERMISSIONS: readonly ToolName[];
|
|
@@ -192,6 +192,8 @@ export declare function compactTiming(t: LifecycleTiming): {
|
|
|
192
192
|
total_ms: number;
|
|
193
193
|
phases: string[];
|
|
194
194
|
server: {
|
|
195
|
+
cold_boot_reason?: string | undefined;
|
|
196
|
+
memory_restored?: boolean | undefined;
|
|
195
197
|
resume_path?: string | undefined;
|
|
196
198
|
warm_fallback?: string | undefined;
|
|
197
199
|
start_path?: string | undefined;
|
|
@@ -205,6 +207,12 @@ export declare function compactTiming(t: LifecycleTiming): {
|
|
|
205
207
|
action: import("@shardflux/sdk").LifecycleAction;
|
|
206
208
|
outcome: import("@shardflux/sdk").TimingOutcome;
|
|
207
209
|
};
|
|
210
|
+
/**
|
|
211
|
+
* (0.5.0) The `notice` of a result whose resume or wake booted the workspace's saved disk instead of restoring its memory
|
|
212
|
+
* (`resume_path` `cold_boot`, `memory_restored` false): plain language an agent acts on, since the dev servers, watchers
|
|
213
|
+
* and background jobs it started are gone. Undefined for any other timing, including one whose memory_restored is unknown.
|
|
214
|
+
*/
|
|
215
|
+
export declare function processRestartNotice(t: LifecycleTiming): string | undefined;
|
|
208
216
|
export declare function okResult(value: unknown): CallToolResult;
|
|
209
217
|
/**
|
|
210
218
|
* `timing` (optional): where the failed open/wait/wake spent its time, next to `error`. `extra` (0.4.0): more sibling
|
package/dist/server.js
CHANGED
|
@@ -53,7 +53,7 @@ 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.
|
|
56
|
+
export const MCP_SERVER_VERSION = '0.5.1';
|
|
57
57
|
/** The package this server is distributed as: its entry in GET /v1/client-versions (contracts §30.4). */
|
|
58
58
|
export const MCP_PACKAGE = '@shardflux/mcp';
|
|
59
59
|
const USER_AGENT = `shardflux-mcp/${MCP_SERVER_VERSION} shardflux-sdk-ts/${SDK_VERSION}`;
|
|
@@ -222,12 +222,32 @@ export function compactTiming(t) {
|
|
|
222
222
|
...(s.startPath ? { start_path: s.startPath } : {}),
|
|
223
223
|
...(s.warmFallback ? { warm_fallback: s.warmFallback } : {}),
|
|
224
224
|
...(s.resumePath ? { resume_path: s.resumePath } : {}),
|
|
225
|
+
// 0.5.0: false when the resume booted the saved disk (processes restarted); absent when the API did not say.
|
|
226
|
+
...(typeof s.memoryRestored === 'boolean' ? { memory_restored: s.memoryRestored } : {}),
|
|
227
|
+
...(s.coldBootReason ? { cold_boot_reason: s.coldBootReason } : {}),
|
|
225
228
|
}
|
|
226
229
|
: null,
|
|
227
230
|
outside_server_ms: ms(t.outsideServerMs),
|
|
228
231
|
retries: t.retries.length,
|
|
229
232
|
};
|
|
230
233
|
}
|
|
234
|
+
/** What a cold boot's reason means, for the agent (the cell's cold_boot_reason values). */
|
|
235
|
+
const COLD_BOOT_REASONS = {
|
|
236
|
+
runtime_changed: "the platform's VM runtime changed since the workspace was suspended, so its memory snapshot could not be restored",
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* (0.5.0) The `notice` of a result whose resume or wake booted the workspace's saved disk instead of restoring its memory
|
|
240
|
+
* (`resume_path` `cold_boot`, `memory_restored` false): plain language an agent acts on, since the dev servers, watchers
|
|
241
|
+
* and background jobs it started are gone. Undefined for any other timing, including one whose memory_restored is unknown.
|
|
242
|
+
*/
|
|
243
|
+
export function processRestartNotice(t) {
|
|
244
|
+
const s = t.server;
|
|
245
|
+
if (s?.resumePath !== 'cold_boot' || s.memoryRestored !== false)
|
|
246
|
+
return undefined;
|
|
247
|
+
const why = s.coldBootReason ? ` (${s.coldBootReason}: ${COLD_BOOT_REASONS[s.coldBootReason] ?? 'the memory snapshot could not be restored'})` : '';
|
|
248
|
+
return (`The workspace was resumed from its saved disk, not from memory${why}. Its files are as they were when it was suspended, ` +
|
|
249
|
+
'but every process was restarted, as after a reboot: start again any dev server, database, watcher or background job you had running, and open new terminals.');
|
|
250
|
+
}
|
|
231
251
|
export function okResult(value) {
|
|
232
252
|
if (isObject(value) && value.mime_type === 'image/png' && typeof value.data_base64 === 'string') {
|
|
233
253
|
const { data_base64: data, ...meta } = value;
|
|
@@ -284,6 +304,7 @@ const INSTRUCTIONS = [
|
|
|
284
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.',
|
|
285
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.',
|
|
286
306
|
'Opens, waits and wakes add timing (phases, server queued/run time) saying where the time went.',
|
|
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.',
|
|
287
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).',
|
|
288
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.',
|
|
289
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).',
|
|
@@ -630,9 +651,9 @@ export function createShardfluxMcpServer(config, opts = {}) {
|
|
|
630
651
|
workspace_key: workspaceKeyProp(pinned),
|
|
631
652
|
after_seconds: {
|
|
632
653
|
type: 'integer',
|
|
633
|
-
minimum:
|
|
654
|
+
minimum: 0,
|
|
634
655
|
maximum: 3600,
|
|
635
|
-
description: 'Suspend once the workspace has been idle this many seconds (
|
|
656
|
+
description: 'Suspend once the workspace has been idle this many seconds (0-3600; 0 = as soon as it is idle) instead of now. Omit to suspend now.',
|
|
636
657
|
},
|
|
637
658
|
wait: { type: 'boolean', description: 'Wait until the suspend finishes (default false). Not with after_seconds.' },
|
|
638
659
|
}, keyRequired),
|
|
@@ -660,12 +681,12 @@ export function createShardfluxMcpServer(config, opts = {}) {
|
|
|
660
681
|
operation: r.operation ? summarizeOperation(r.operation) : null,
|
|
661
682
|
workspace: summarizeWorkspace(ws.data),
|
|
662
683
|
message: r.suspendRequest
|
|
663
|
-
? `Suspend scheduled: the workspace is suspended once it has been idle for ${r.suspendRequest.after_seconds} s (not before ${r.suspendRequest.not_before}). Your next tool call on it cancels this; a running command or keepalive postpones it.`
|
|
684
|
+
? `Suspend scheduled: the workspace is suspended ${r.suspendRequest.after_seconds === 0 ? 'as soon as it is idle' : `once it has been idle for ${r.suspendRequest.after_seconds} s`} (not before ${r.suspendRequest.not_before}). Your next tool call on it cancels this; a running command or keepalive postpones it.`
|
|
664
685
|
: `A suspend is already in progress (operation ${r.operation.id}, ${r.operation.state}); nothing was scheduled.`,
|
|
665
686
|
};
|
|
666
687
|
},
|
|
667
688
|
},
|
|
668
|
-
lifecycleTool('workspace_resume', 'Resume workspace', 'Resume a suspended workspace. Returns the resume operation (with wait: once finished, and its timing). Not for file-first workspaces (never suspended).', 'resume',
|
|
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',
|
|
669
690
|
// Through the server's handle: a waited resume is held until the workspace runs (contracts §22.6) and the handle
|
|
670
691
|
// keeps the view and this server's tool token, so the next workspace tool starts at once.
|
|
671
692
|
(ws, o) => ws.resume({ ...o, agentLabel: config.agentLabel })),
|
|
@@ -994,7 +1015,8 @@ export function createShardfluxMcpServer(config, opts = {}) {
|
|
|
994
1015
|
try {
|
|
995
1016
|
const value = await Promise.race([running, aborted]);
|
|
996
1017
|
log('info', 'tool call', { tool: name, outcome: 'ok', ms: Date.now() - started });
|
|
997
|
-
|
|
1018
|
+
const notice = call.timed && call.timing ? processRestartNotice(call.timing) : undefined;
|
|
1019
|
+
return okResult(call.timed && call.timing && isObject(value) ? { ...value, timing: compactTiming(call.timing), ...(notice ? { notice } : {}) } : value);
|
|
998
1020
|
}
|
|
999
1021
|
catch (err) {
|
|
1000
1022
|
if (extra.signal.aborted) {
|
|
@@ -1032,7 +1054,9 @@ export function createShardfluxMcpServer(config, opts = {}) {
|
|
|
1032
1054
|
: suggestsFeedback(info)
|
|
1033
1055
|
? { feedback: feedbackSuggestion(info) }
|
|
1034
1056
|
: {};
|
|
1035
|
-
|
|
1057
|
+
// A wake that booted the saved disk before the failure still restarted every process: said next to the error.
|
|
1058
|
+
const notice = call.timed && call.timing ? processRestartNotice(call.timing) : undefined;
|
|
1059
|
+
return errorResult(info, call.timed && call.timing ? compactTiming(call.timing) : undefined, { ...siblings, ...(notice ? { notice } : {}) });
|
|
1036
1060
|
}
|
|
1037
1061
|
});
|
|
1038
1062
|
return server;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shardflux/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
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.
|
|
49
|
+
"@shardflux/sdk": "^0.11.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@eslint/js": "10.0.1",
|