@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 +99 -25
- package/dist/mcp-server.d.ts +189 -45
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +1027 -195
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@skillstate/mcp)
|
|
8
8
|
[](https://www.npmjs.com/package/@skillstate/mcp)
|
|
9
|
-
[](https://github.com/vitkuz573/skillstate)
|
|
10
10
|
[](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
|
|
18
|
-
|
|
19
|
-
`
|
|
20
|
-
patch,
|
|
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":"
|
|
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
|
-
# ->
|
|
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`,
|
|
104
|
-
(plus the types `McpServerOptions`, `LaunchArgs`,
|
|
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
|
-
- `
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
123
|
-
`
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
-
|
|
133
|
-
|
|
134
|
-
- `state.
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
package/dist/mcp-server.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Readable, Writable } from 'node:stream';
|
|
2
|
-
import type { ProceduralSpec } from '@skillstate/core';
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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
|
|
51
|
-
readonly protocolVersion = "
|
|
52
|
-
/** Advertised server capabilities
|
|
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
|
-
|
|
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).
|
|
71
|
-
* stdio transport
|
|
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
|
|
76
|
-
* complete messages it contains.
|
|
77
|
-
*
|
|
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.
|
|
85
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
102
|
-
private
|
|
103
|
-
private
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
115
|
-
* (
|
|
116
|
-
*
|
|
117
|
-
* `
|
|
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
|
|
120
|
-
/**
|
|
121
|
-
private
|
|
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
|
|
128
|
-
*
|
|
129
|
-
*
|
|
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
|
|
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
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
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
|
package/dist/mcp-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|