@skillstate/mcp 2.0.7 → 2.2.0

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/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@skillstate/mcp)](https://www.npmjs.com/package/@skillstate/mcp)
8
8
  [![node](https://img.shields.io/node/v/@skillstate/mcp)](https://www.npmjs.com/package/@skillstate/mcp)
9
- [![Tests](https://img.shields.io/badge/tests-873%20passing-brightgreen)](https://github.com/vitkuz573/skillstate)
9
+ [![Tests](https://img.shields.io/badge/tests-1165%20passing-brightgreen)](https://github.com/vitkuz573/skillstate)
10
10
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/vitkuz573/skillstate/blob/main/LICENSE)
11
11
 
12
12
  </div>
@@ -14,10 +14,11 @@
14
14
  ---
15
15
 
16
16
  `@skillstate/mcp` exposes the skillstate runtime ([`@skillstate/core`](../core))
17
- as a **Model Context Protocol** server over stdio (JSON-RPC 2.0). It reuses the
18
- paper-exact core directly — `mergeState`, `createInitialState`,
19
- `validatePatchDeep`, `migrate`, `redactSecrets` — so any MCP client can read,
20
- patch, merge, and reset the execution state as tools.
17
+ as a **Model Context Protocol** server (protocol revision `2026-07-28`) over
18
+ stdio (JSON-RPC 2.0, newline-delimited). It reuses the paper-exact core
19
+ directly — `mergeState`, `createInitialState`, `validatePatchDeep`, `migrate`,
20
+ `redactSecrets` — so any MCP client can read, patch, checkpoint, and roll back
21
+ the execution state as tools and resources.
21
22
 
22
23
  > **@non-paper** — the server is additive; no MCP exists in arXiv 2608.26263v3.
23
24
  > Unlike the prompting adapters, MCP is runtime **access**, not prompting, so
@@ -50,7 +51,7 @@ const server = new McpServer({
50
51
  root: '.',
51
52
  name: '.skillstate.json',
52
53
  });
53
- const response = server.handleLine(
54
+ const response = await server.handleLine(
54
55
  JSON.stringify({
55
56
  jsonrpc: '2.0', id: 1, method: 'tools/call',
56
57
  params: { name: 'state.get', arguments: {} },
@@ -92,17 +93,17 @@ first (see the `@skillstate/opencode` README for a sample). Verify with:
92
93
 
93
94
  ```bash
94
95
  opencode debug config # mcp.skillstate appears in the resolved config
95
- echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}
96
+ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}
96
97
  {"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
97
98
  | node packages/mcp/bin/mcp.js
98
- # -> serverInfo {"name":"skillstate","version":"1.0.0"} + 6 tools
99
+ # -> protocolVersion "2026-07-28" + 14 tools
99
100
  ```
100
101
 
101
102
  ## API / Exports
102
103
 
103
- Root path `@skillstate/mcp` exports `McpAdapter`, `McpServer`, and `launch`
104
- (plus the types `McpServerOptions`, `LaunchArgs`, `FrameMode`, `JsonRpcRequest`,
105
- `McpToolResult`, and `McpConfigOptions`).
104
+ Root path `@skillstate/mcp` exports `McpAdapter`, `McpServer`, `launch`, and
105
+ `PROTOCOL_VERSION` (plus the types `McpServerOptions`, `LaunchArgs`,
106
+ `JsonRpcRequest`, `McpToolResult`, `ToolAnnotations`, and `McpConfigOptions`).
106
107
 
107
108
  - `new McpAdapter()` — `name = 'mcp'`.
108
109
  - `generateMcpConfig(target, options?): string` — a deterministic,
@@ -110,30 +111,103 @@ Root path `@skillstate/mcp` exports `McpAdapter`, `McpServer`, and `launch`
110
111
  `.launcherPath`, `.env`). No state path is embedded — the server resolves
111
112
  the state from its own cwd.
112
113
  - `saveMcpConfig(target, options?): Promise<string>` — atomic write.
113
- - `new McpServer(options: McpServerOptions)` — `{ spec, root, name, tracker? }`.
114
- - `handleLine(line): string | null` — process one already-framed JSON-RPC
115
- message.
116
- - `feed(chunk): string[]` — consume streamed stdin, handling both
117
- newline-delimited JSON-RPC and `Content-Length`-framed messages.
114
+ - `new McpServer(options: McpServerOptions)` — `{ spec, root, name, agent?, tracker? }`.
115
+ - `protocolVersion` is always `'2026-07-28'`; `initialize` answers exactly
116
+ that regardless of what the client requested (per the MCP spec the client
117
+ decides whether it can work with the server's revision).
118
+ - `handleLine(line): Promise<string | null>` — process one already-framed
119
+ JSON-RPC message.
120
+ - `feed(chunk): Promise<string[]>` — consume streamed stdin
121
+ (newline-delimited JSON-RPC; partial lines are buffered).
118
122
  - `start(input?, output?): Promise<McpServer>` / `stop()` / `get isRunning()`.
119
123
  - `launch(args?): Promise<McpServer>` — resolves the spec from args or env
120
124
  and starts a stdio server; the state always resolves from the server's cwd.
121
125
 
122
- **Tools:** `state.get`, `state.patch`, `state.merge` (schema-validated),
123
- `state.reset`, `spec.get`, `state.metrics`. **Resource:** `skillstate://state`.
124
- State is redacted on every read, and the server conserves its own buffering so
125
- transports may split frames mid-message.
126
+ **Tools:** `state.get`, `state.patch` (the single write op — validates via
127
+ `validatePatchDeep`, returns `{ state, changes, warnings }`), `state.validate`
128
+ (dry-run), `state.diff` (changes since the last call, `{ full: true }` for
129
+ before/after), `state.checkpoint` (named sidecar snapshot),
130
+ `state.rollback` (restore from a checkpoint), `state.summary` (compact
131
+ orientation + session info), `state.metrics`, `state.finalize` (the agent's
132
+ "I am done" lifecycle marker), `spec.get` (with a ready-made
133
+ valid `example_state_patch`), `spec.next` (goal/next/blockers guidance),
134
+ plus the AGENT tools `agent.list` / `agent.read` / `agent.merge`.
135
+ `state.merge` and `state.reset` are gone — `state.patch` validates, and
136
+ rollback replaces reset.
137
+
138
+ **Multi-agent (2.2.0).** Every state tool accepts `{ agent }` (sanitized
139
+ `[A-Za-z0-9_-]`, ≤ 64) scoping the file to
140
+ `<stateDir>/agents/<agentId>/<name>`; the server default comes from the
141
+ `SKILLSTATE_AGENT_ID` env (`launch`) or the `McpServerOptions.agent`
142
+ constructor option; the default `''` is the main agent. All writes
143
+ (`state.patch`, `state.rollback`, `state.checkpoint`, `agent.merge`) run
144
+ under `withStateLock` — a cross-process lockfile at `<state>.lock` with
145
+ stale-TTL takeover — so 2-3 concurrent agent processes never interleave
146
+ state writes. The `state.diff` baseline is persisted to
147
+ `<stateDir>/.diff-baseline.json` (atomic, under the lock) — the
148
+ "since your last look" semantics is now consistent across processes.
149
+ The agent tools: `agent.list` scans `<stateDir>/agents/` and returns
150
+ `{ agents: [{ id, statePath, exists, status, lastActivityAt, staleness,
151
+ ageMs, summary, lastModified }] }` (light summary: keys + size, no values);
152
+ `agent.read` returns a sub-agent's state read-only; `agent.merge` folds a
153
+ sub-agent copy into the main state under the lock — keys only in the sub
154
+ state are taken, nested objects merge recursively, conflicting scalars
155
+ follow `keep: 'main'` (default) or `'sub'` (schema defaults count as
156
+ "never set"), and the sub copy is NOT deleted — it is marked `mergedAt`
157
+ (history) and its session sidecar flips to `status: 'merged'`.
158
+
159
+ **Session lifecycle (2.3.0).** The state envelope belongs to the
160
+ procedure; the session lifecycle lives in a separate sidecar next to
161
+ every state file — `<stateDir>/.session-meta.json` (agent scopes:
162
+ `agents/<id>/.session-meta.json`), written atomically under its own
163
+ `withStateLock`:
164
+
165
+ - `launch()` stamps `{ status: 'running', startedAt, agentId,
166
+ protocolVersion }` — a new launch overwrites any previous
167
+ `interrupted`/`completed` marker (a fresh run has begun).
168
+ - Every state write (`state.patch` / `state.rollback` /
169
+ `state.checkpoint` / `agent.merge`) refreshes `lastActivityAt`,
170
+ debounced to at most one sidecar write per 5 s; a broken sidecar never
171
+ fails a state write.
172
+ - `state.finalize { status: 'completed' | 'failed', result? }` is the
173
+ agent's own "I am done" signal — it writes `{ status, finishedAt,
174
+ result }` so `agent.list`/`state.summary` show a finished session
175
+ instead of a running/interrupted one.
176
+ - SIGINT/SIGTERM flush `status: 'interrupted'` + re-pin the diff baseline
177
+ to the surviving state, then exit 130 (`installShutdown` from
178
+ `@skillstate/core`; terminal statuses recorded by the agent are never
179
+ clobbered). Embedders that own the process pass
180
+ `installInterruptHandler: false`.
181
+ - Staleness (`STALE_MS` = 5 min in `@skillstate/core`):
182
+ `active` — fresh running session or a terminal status; `stale` —
183
+ `running` with no writes for 5 min (the provider died without a
184
+ signal); `orphan` — no (or corrupt) sidecar. `agent.list` adds `ageMs`
185
+ for running sessions; `state.summary` adds `status`/`lastActivityAt`/
186
+ `staleness` to its `session` object.
187
+
188
+ **Resources (`resources/read`):** `skillstate://state` (the full
189
+ `{ version, state }` envelope), `skillstate://spec`, and
190
+ `skillstate://summary` (compact projection). State is redacted on every read,
191
+ and the server conserves its own buffering so transports may split lines
192
+ mid-message.
126
193
 
127
194
  ## Notes
128
195
 
129
196
  - **Zero dependencies.** `@skillstate/mcp` declares only
130
197
  [`@skillstate/core`](../core); it uses Node's `fs`/`path`/`stream` for the
131
198
  stdio transport and crash-safe state writes (temp sibling + fsync + rename).
132
- - Both newline-delimited JSON-RPC and `Content-Length`-framed (LSP-style)
133
- messages are accepted; responses echo the framing that triggered them.
134
- - `state.merge` runs `validatePatchDeep` (defense-in-depth) before the ⊕ merge;
135
- `state.patch` applies the raw ⊕ merge. `redactSecrets` fails closed so
136
- secrets never leave the process through a tool result.
199
+ - Transport is newline-delimited JSON only (the MCP stdio framing);
200
+ `Content-Length`-framed input is not understood and errors as `-32700`.
201
+ - Every patch — including `state.patch` — runs `validatePatchDeep`
202
+ (defense-in-depth) before the ⊕ merge; an invalid patch is an `isError`
203
+ result carrying `error` and `field`, and nothing is written.
204
+ `redactSecrets` fails closed so secrets never leave the process through a
205
+ tool result or resource read.
206
+ - Checkpoints live in `<stateDir>/checkpoints/<seq>-<label>.json` sidecars
207
+ (atomic writes) and also pin `<path>.snapshot` via `FileStore.snapshot()`;
208
+ the sequence numbers derive from the sidecar catalog, so they survive
209
+ restarts. The session `seq` reported by `state.summary` counts writes
210
+ applied through the server in this session.
137
211
 
138
212
  ## Related
139
213
 
@@ -1,8 +1,7 @@
1
1
  import type { Readable, Writable } from 'node:stream';
2
- import type { ProceduralSpec } from '@skillstate/core';
3
- import type { TokenTracker } from '@skillstate/core';
4
- /** MCP stdio framing modes the server can speak. */
5
- export type FrameMode = 'jsonl' | 'content-length';
2
+ import type { ProceduralSpec, TokenTracker } from '@skillstate/core';
3
+ /** The single MCP protocol revision this server speaks (initialize answer). */
4
+ export declare const PROTOCOL_VERSION = "2026-07-28";
6
5
  /** A JSON-RPC request object (id may be a number, string, or null). */
7
6
  export interface JsonRpcRequest {
8
7
  jsonrpc?: string;
@@ -18,14 +17,26 @@ export interface McpToolResult {
18
17
  }>;
19
18
  isError?: boolean;
20
19
  }
20
+ /** MCP tool annotations (the hints hosts surface in tool UIs). */
21
+ export interface ToolAnnotations {
22
+ readOnlyHint: boolean;
23
+ destructiveHint: boolean;
24
+ }
21
25
  /** Options for {@link McpServer}. */
22
26
  export interface McpServerOptions {
23
- /** Procedural spec: drives `spec.get`, schema validation, and reset defaults. */
27
+ /** Procedural spec: drives `spec.get`/`spec.next`, schema validation, and state defaults. */
24
28
  spec: ProceduralSpec;
25
29
  /** State file root directory (confined by `resolveStatePath`). */
26
30
  root: string;
27
31
  /** State file name (confined by `resolveStatePath`). */
28
32
  name: string;
33
+ /**
34
+ * Default agent scope: `''` (the main agent) targets the plain state
35
+ * file; a non-empty id (sanitized `[A-Za-z0-9_-]`, ≤64) targets
36
+ * `agents/<id>/skillstate.json` inside the same bucket. Every tool call
37
+ * may still override the scope via `{ agent }` (and/or `{ root, name }`).
38
+ */
39
+ agent?: string;
29
40
  /** Optional token tracker for the `state.metrics` tool. */
30
41
  tracker?: TokenTracker;
31
42
  }
@@ -37,23 +48,38 @@ export interface LaunchArgs {
37
48
  root?: string;
38
49
  /** State file name override; defaults to the per-project state file name. */
39
50
  name?: string;
51
+ /** Default agent scope; falls back to the `SKILLSTATE_AGENT_ID` env. */
52
+ agent?: string;
40
53
  tracker?: TokenTracker;
41
54
  input?: Readable;
42
55
  output?: Writable;
56
+ /**
57
+ * Wire the SIGINT/SIGTERM interrupt handler that flushes
58
+ * `{ status: 'interrupted' }` + the diff baseline before exiting
59
+ * (default `true`). In-process embedders that own the process and
60
+ * manage their own teardown pass `false` — the handler exits the
61
+ * PROCESS, which is wrong for embedded servers.
62
+ */
63
+ installInterruptHandler?: boolean;
43
64
  }
44
65
  /**
45
66
  * The `skillstate` MCP server: a JSON-RPC 2.0 over stdio server exposing
46
- * the skillstate runtime as MCP tools.
67
+ * the skillstate runtime as MCP tools and resources.
47
68
  */
48
69
  export declare class McpServer {
49
70
  private readonly options;
50
- /** Protocol version advertised by the server on `initialize`. */
51
- readonly protocolVersion = "2024-11-05";
52
- /** Advertised server capabilities (a minimal subset). */
71
+ /** Protocol revision advertised on `initialize` — always exactly this. */
72
+ readonly protocolVersion = "2026-07-28";
73
+ /** Advertised server capabilities. */
53
74
  readonly capabilities: {
54
75
  tools: {
55
76
  listChanged: boolean;
56
77
  };
78
+ resources: {};
79
+ logging: {};
80
+ prompts: {
81
+ listChanged: boolean;
82
+ };
57
83
  };
58
84
  /** Advertised server identity. */
59
85
  readonly serverInfo: {
@@ -62,72 +88,175 @@ export declare class McpServer {
62
88
  };
63
89
  private buffer;
64
90
  private running;
65
- private frameMode;
91
+ /** Serializes `start()` stream handling so chunk order is preserved. */
92
+ private chain;
93
+ /**
94
+ * Diff baselines are persisted to disk (`.diff-baseline.json` next to
95
+ * each state file, under the cross-process lock) — the "since your last
96
+ * look" semantics stays, but is now CONSISTENT BETWEEN PROCESSES: the
97
+ * former in-memory per-server Map made two servers diff against
98
+ * different baselines.
99
+ */
100
+ /** Writes (patch/rollback) applied per resolved state path this session. */
101
+ private readonly writeSeq;
102
+ /** Debounce clock for `.session-meta.json` activity stamps (per meta path). */
103
+ private readonly lastActivityWrite;
104
+ /** Uninstall closure for the SIGINT/SIGTERM interrupt handler (if wired). */
105
+ private uninstallShutdown;
106
+ /** The `{ source, handler }` pair attached by `start()` (removed by `stop()`). */
107
+ private attached;
66
108
  constructor(options: McpServerOptions);
109
+ /**
110
+ * The state DIRECTORY the server session owns (its sidecars —
111
+ * `.session-meta.json`, the diff baseline — live next to the default
112
+ * state file; agent-scoped calls keep their per-agent directories).
113
+ */
114
+ private get sessionDir();
115
+ /**
116
+ * Stamp the session sidecar for `dir` with `lastActivityAt: now`,
117
+ * debounced to one write per {@link ACTIVITY_DEBOUNCE_MS} per directory
118
+ * (state writes stay the hot path). The meta sidecar is best-effort
119
+ * orchestration metadata: a failed write is swallowed — a broken
120
+ * sidecar never fails a state write. The write itself runs under the
121
+ * meta file's own `withStateLock` (never the state lock — no deadlock).
122
+ */
123
+ private touchActivity;
124
+ /**
125
+ * Wire the @non-paper shutdown seam: SIGINT/SIGTERM best-effort flush
126
+ * the session sidecar to `status: "interrupted"` + re-pin the diff
127
+ * baseline to the surviving state (the next process starts diffing from
128
+ * the post-crash state, not from a pre-crash baseline), then exit with
129
+ * the conventional 130. Terminal statuses (`completed` / `failed` /
130
+ * `merged`) recorded by the agent itself are never clobbered — hosts
131
+ * SIGTERM their servers after a clean finalize too. Idempotent; returns
132
+ * an uninstall closure for embedders/tests.
133
+ */
134
+ installInterruptHandler(): () => void;
135
+ /** Detach the SIGINT/SIGTERM handler (embedders/tests owning the process). */
136
+ detachInterruptHandler(): void;
67
137
  /**
68
138
  * Process a single (already-framed) JSON-RPC message line and return the
69
139
  * response string, or `null` when the message needs no reply (a
70
- * notification). This is the stateless unit entry point used by the
71
- * stdio transport; `feed` drives it for framed/streamed input.
140
+ * notification). The stateless unit entry point used by tests and the
141
+ * stdio transport.
72
142
  */
73
- handleLine(line: string): string | null;
143
+ handleLine(line: string): Promise<string | null>;
74
144
  /**
75
- * Feed a raw chunk of stdin and return every response produced by the
76
- * complete messages it contains. Handles BOTH newline-delimited JSON-RPC
77
- * and `Content-Length`-framed messages; partial frames are buffered until
78
- * the rest arrives. Responses are framed like the message that triggered
79
- * them.
145
+ * Feed a raw chunk of stdin and return every newline-delimited response
146
+ * produced by the complete messages it contains. Partial lines are
147
+ * buffered until the rest arrives. Each response ends with a newline.
80
148
  */
81
- feed(chunk: string): string[];
149
+ feed(chunk: string): Promise<string[]>;
82
150
  /**
83
151
  * Attach the server to a stdin/stdout pair (defaults to `process`).
84
- * Resolves once the server is reading; `stop()` detaches it. The server
85
- * conserves its own buffered state, so real transports may hand over
86
- * chunks that split mid-frame.
152
+ * Resolves once the server is reading; `stop()` detaches it. Chunks are
153
+ * processed strictly in arrival order even though handling is async.
87
154
  */
88
155
  start(input?: Readable, output?: Writable): Promise<McpServer>;
89
- /** Mark the server stopped (idempotent). */
156
+ /**
157
+ * Mark the server stopped (idempotent): detaches the input listener
158
+ * attached by `start()` so embedded servers release their streams.
159
+ */
90
160
  stop(): void;
91
161
  /** Whether the server is currently reading from its input stream. */
92
162
  get isRunning(): boolean;
163
+ /** Append one chunk to the ordered stream pipeline. */
164
+ private pump;
93
165
  /** Parse raw text into a message and dispatch; `-32700` on parse error. */
94
166
  private processRaw;
95
167
  private processMessage;
96
168
  private handleRequest;
97
169
  private handleToolCall;
170
+ private handleResourceRead;
98
171
  private callTool;
99
172
  private stateGet;
100
173
  private statePatch;
101
- private stateMerge;
102
- private stateReset;
103
- private specGet;
174
+ private stateValidate;
175
+ private stateDiff;
176
+ private stateCheckpoint;
177
+ private stateRollback;
178
+ private stateSummary;
104
179
  private stateMetrics;
180
+ /**
181
+ * `state.finalize` — the agent's own "I am done" signal. Writes the
182
+ * session sidecar `{ status, finishedAt, result }` under the meta lock
183
+ * and returns the recorded lifecycle so the orchestrator (or a later
184
+ * `agent.read`/`agent.list`) sees it. `result` is an optional free-text
185
+ * outcome; invalid statuses are rejected before anything is written.
186
+ */
187
+ private stateFinalize;
188
+ private specGet;
189
+ private specNext;
105
190
  /** Resolve the target state file path (args override the defaults). */
106
191
  private resolveStore;
192
+ /**
193
+ * The effective agent scope for a call: `{ agent }` wins, then the
194
+ * server default ({@link McpServerOptions.agent} — set from the
195
+ * `SKILLSTATE_AGENT_ID` env by {@link launch}). `''` = main agent.
196
+ */
197
+ private effectiveAgent;
198
+ /** Resolve `{ root, name, agent }` + the confined file path in one go. */
199
+ private resolveRef;
200
+ /** Required + sanitized `{ agent }` for the agent.* tools. */
201
+ private requireAgent;
202
+ /** State file path for a REQUIRED sub-agent id (read-only views). */
203
+ private agentStore;
204
+ /**
205
+ * `agent.list`: scan `<root>/agents/` and project each sub-agent state
206
+ * copy — id, statePath, exists, lastModified, lifecycle (status,
207
+ * lastActivityAt, staleness, ageMs) and a LIGHT summary (top-level keys
208
+ * + size only, no values). Non-directory entries and ids outside
209
+ * `[A-Za-z0-9_-]{1,64}` are skipped; a missing agents directory yields
210
+ * an empty list. The lifecycle comes from the agent dir's
211
+ * `.session-meta.json` sidecar: `orphan` when it is missing/corrupt,
212
+ * `stale` when a `running` session has not written anything for the
213
+ * core `STALE_MS` threshold (5 min — the provider died without a
214
+ * signal), `active` otherwise. A `running` agent reports its `ageMs`
215
+ * since the last
216
+ * activity so the main agent can tell "finished" from "died mid-run"
217
+ * at a glance.
218
+ */
219
+ private agentList;
220
+ /** `agent.read`: a sub-agent's state, READ-ONLY (the main agent peeks). */
221
+ private agentRead;
222
+ /**
223
+ * `agent.merge`: fold a sub-agent's state into the MAIN state under the
224
+ * cross-process lock (conflicting scalars resolved by `keep: 'main'` —
225
+ * the default — or `'sub'`; nested objects recurse; `null` deletes).
226
+ * The sub state is NOT deleted (history): it is marked with `mergedAt`.
227
+ * Returns `{ agent, keep, state, changes }`.
228
+ */
229
+ private agentMerge;
107
230
  /** Read + normalize the state, falling back to schema defaults. */
108
231
  private loadState;
109
- /** Crash-safe synchronous write: temp sibling + fsync + rename. */
232
+ /** Advance the per-path session write counter. */
233
+ private bumpWriteSeq;
234
+ /**
235
+ * Crash-safe synchronous write of the versioned envelope
236
+ * `{ version, state }`: temp sibling + fsync + rename.
237
+ */
110
238
  private writeState;
111
- private toolsList;
112
- private resourcesList;
113
239
  /**
114
- * Parse a `Content-Length`-framed message from the front of the buffer
115
- * (the caller has already asserted the buffer begins with a valid
116
- * `Content-Length:` header). Returns the body + consumed length, or
117
- * `'incomplete'` if the payload has not all arrived.
240
+ * The DIFF BASELINE for a state file, persisted at
241
+ * `<stateDir>/.diff-baseline.json` (stateDir = the state file's
242
+ * directory — per state file, since agent scopes live in their own
243
+ * `agents/<id>/` directories). `null` = no baseline yet.
118
244
  */
119
- private takeContentLengthFrame;
120
- /** Encode a response in the given framing mode. */
121
- private encodeResponse;
245
+ private readBaseline;
246
+ /** Crash-safe synchronous write of the diff baseline (atomic rename). */
247
+ private writeBaseline;
248
+ /** `.diff-baseline.json` lives next to its state file. */
249
+ private baselinePathFor;
250
+ private toolsList;
251
+ private resourcesList;
122
252
  private successResponse;
123
253
  private errorResponse;
124
254
  private textResult;
125
255
  }
126
256
  /**
127
- * Per-project state resolution for an MCP server session. Semantics are a
128
- * zero-dep mirror of `resolveStatePathForCwd` in `@skillstate/opencode`
129
- * (kept local: `@skillstate/mcp` depends only on `@skillstate/core` —
130
- * keep the two in sync):
257
+ * Per-project state resolution for an MCP server session — re-exported
258
+ * from `@skillstate/core` (the single source of truth shared with the
259
+ * OpenCode plugin and the generated hook scripts):
131
260
  *
132
261
  * - `cwd === home` — no single project → the global bucket
133
262
  * `<home>/.skillstate/global/skillstate.json`;
@@ -135,16 +264,31 @@ export declare class McpServer {
135
264
  *
136
265
  * Pure path arithmetic (no filesystem access, `path.resolve` normalization).
137
266
  */
138
- export declare function resolveStatePathForCwd(cwd: string, home: string): string;
267
+ export { resolveHostStateForCwd as resolveStatePathForCwd } from '@skillstate/core';
139
268
  /**
140
269
  * Launch an MCP server from an argument/env config (reads
141
270
  * `SKILLSTATE_SPEC_PATH` when not passed explicitly). State resolution is
142
271
  * ALWAYS per-project from the server's `process.cwd()`:
143
272
  * `<cwd>/.skillstate/skillstate.json` (the global bucket when cwd === home).
144
- * Hosts that launch local MCP servers with the project as cwd therefore get
145
- * per-project state without any baked path. Explicit `args.root`/`args.name`
146
- * remain available for in-process embedding. Defaults to the canonical
147
- * InterCode CTF spec.
273
+ * AGENT SCOPE: a non-empty `SKILLSTATE_AGENT_ID` env (or `args.agent`)
274
+ * scopes the default state file to `agents/<id>/skillstate.json` — host
275
+ * configs set the env per server instance when a sub-agent needs an
276
+ * isolated copy; the default is `''` (the main agent) and every tool call
277
+ * can still override via `{ agent }`. Hosts that launch local MCP servers
278
+ * with the project as cwd therefore get per-project state without any
279
+ * baked path. Explicit `args.root`/`args.name` remain available for
280
+ * in-process embedding. Defaults to the canonical InterCode CTF spec.
281
+ *
282
+ * SESSION LIFECYCLE (release 2.3.0): launch stamps the session sidecar
283
+ * `<stateDir>/.session-meta.json` with `{ status: 'running', startedAt,
284
+ * agentId, protocolVersion }` — overwriting any previous
285
+ * `interrupted`/`completed` marker (a new launch means a fresh run) — and
286
+ * wires the SIGINT/SIGTERM handler that flushes
287
+ * `{ status: 'interrupted' }` + the diff baseline before exiting. The
288
+ * agent is expected to call `state.finalize` at the end of its procedure.
289
+ * In-process embedders that do not own the process can pass
290
+ * `installInterruptHandler: false` (tests) or call
291
+ * `server.detachInterruptHandler()` afterwards.
148
292
  */
149
293
  export declare function launch(args?: LaunchArgs): Promise<McpServer>;
150
294
  //# sourceMappingURL=mcp-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAOtD,OAAO,KAAK,EAAE,cAAc,EAA0B,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,oDAAoD;AACpD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,gBAAgB,CAAC;AAEnD,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,IAAI,EAAE,cAAc,CAAC;IACrB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,SAAS;IAYR,OAAO,CAAC,QAAQ,CAAC,OAAO;IAXpC,iEAAiE;IACjE,QAAQ,CAAC,eAAe,gBAAgB;IACxC,yDAAyD;IACzD,QAAQ,CAAC,YAAY;QAAK,KAAK;YAAI,WAAW;;MAAY;IAC1D,kCAAkC;IAClC,QAAQ,CAAC,UAAU;QAAK,IAAI;QAAgB,OAAO;MAAY;IAE/D,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAsB;IAEvC,YAA6B,OAAO,EAAE,gBAAgB,EAAI;IAE1D;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMtC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqC5B;IAED;;;;;OAKG;IACG,KAAK,CACT,KAAK,CAAC,EAAE,QAAQ,EAChB,MAAM,CAAC,EAAE,QAAQ,GAChB,OAAO,CAAC,SAAS,CAAC,CAWpB;IAED,4CAA4C;IAC5C,IAAI,IAAI,IAAI,CAEX;IAED,qEAAqE;IACrE,IAAI,SAAS,IAAI,OAAO,CAEvB;IAMD,2EAA2E;IAC3E,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,cAAc;IA2BtB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,cAAc;IA6BtB,OAAO,CAAC,QAAQ;IAmBhB,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,YAAY;IAgBpB,uEAAuE;IACvE,OAAO,CAAC,YAAY;IAMpB,mEAAmE;IACnE,OAAO,CAAC,SAAS;IASjB,mEAAmE;IACnE,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,SAAS;IA+DjB,OAAO,CAAC,aAAa;IAcrB;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAyB9B,mDAAmD;IACnD,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,UAAU;CAGnB;AAyBD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxE;AAED;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAYlE"}
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}