@skillstate/mcp 2.0.7 → 2.1.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 +45 -23
- package/dist/mcp-server.d.ts +58 -40
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +538 -188
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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" + 10 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,
|
|
@@ -111,29 +112,50 @@ Root path `@skillstate/mcp` exports `McpAdapter`, `McpServer`, and `launch`
|
|
|
111
112
|
the state from its own cwd.
|
|
112
113
|
- `saveMcpConfig(target, options?): Promise<string>` — atomic write.
|
|
113
114
|
- `new McpServer(options: McpServerOptions)` — `{ spec, root, name, tracker? }`.
|
|
114
|
-
- `
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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`, `spec.get` (with a ready-made
|
|
132
|
+
valid `example_state_patch`), and `spec.next` (goal/next/blockers guidance).
|
|
133
|
+
`state.merge` and `state.reset` are gone — `state.patch` validates, and
|
|
134
|
+
rollback replaces reset.
|
|
135
|
+
|
|
136
|
+
**Resources (`resources/read`):** `skillstate://state` (the full
|
|
137
|
+
`{ version, state }` envelope), `skillstate://spec`, and
|
|
138
|
+
`skillstate://summary` (compact projection). State is redacted on every read,
|
|
139
|
+
and the server conserves its own buffering so transports may split lines
|
|
140
|
+
mid-message.
|
|
126
141
|
|
|
127
142
|
## Notes
|
|
128
143
|
|
|
129
144
|
- **Zero dependencies.** `@skillstate/mcp` declares only
|
|
130
145
|
[`@skillstate/core`](../core); it uses Node's `fs`/`path`/`stream` for the
|
|
131
146
|
stdio transport and crash-safe state writes (temp sibling + fsync + rename).
|
|
132
|
-
-
|
|
133
|
-
|
|
134
|
-
- `state.
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
- Transport is newline-delimited JSON only (the MCP stdio framing);
|
|
148
|
+
`Content-Length`-framed input is not understood and errors as `-32700`.
|
|
149
|
+
- Every patch — including `state.patch` — runs `validatePatchDeep`
|
|
150
|
+
(defense-in-depth) before the ⊕ merge; an invalid patch is an `isError`
|
|
151
|
+
result carrying `error` and `field`, and nothing is written.
|
|
152
|
+
`redactSecrets` fails closed so secrets never leave the process through a
|
|
153
|
+
tool result or resource read.
|
|
154
|
+
- Checkpoints live in `<stateDir>/checkpoints/<seq>-<label>.json` sidecars
|
|
155
|
+
(atomic writes) and also pin `<path>.snapshot` via `FileStore.snapshot()`;
|
|
156
|
+
the sequence numbers derive from the sidecar catalog, so they survive
|
|
157
|
+
restarts. The session `seq` reported by `state.summary` counts writes
|
|
158
|
+
applied through the server in this session.
|
|
137
159
|
|
|
138
160
|
## Related
|
|
139
161
|
|
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,9 +17,14 @@ 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;
|
|
@@ -43,17 +47,22 @@ export interface LaunchArgs {
|
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
49
|
* The `skillstate` MCP server: a JSON-RPC 2.0 over stdio server exposing
|
|
46
|
-
* the skillstate runtime as MCP tools.
|
|
50
|
+
* the skillstate runtime as MCP tools and resources.
|
|
47
51
|
*/
|
|
48
52
|
export declare class McpServer {
|
|
49
53
|
private readonly options;
|
|
50
|
-
/** Protocol
|
|
51
|
-
readonly protocolVersion = "
|
|
52
|
-
/** Advertised server capabilities
|
|
54
|
+
/** Protocol revision advertised on `initialize` — always exactly this. */
|
|
55
|
+
readonly protocolVersion = "2026-07-28";
|
|
56
|
+
/** Advertised server capabilities. */
|
|
53
57
|
readonly capabilities: {
|
|
54
58
|
tools: {
|
|
55
59
|
listChanged: boolean;
|
|
56
60
|
};
|
|
61
|
+
resources: {};
|
|
62
|
+
logging: {};
|
|
63
|
+
prompts: {
|
|
64
|
+
listChanged: boolean;
|
|
65
|
+
};
|
|
57
66
|
};
|
|
58
67
|
/** Advertised server identity. */
|
|
59
68
|
readonly serverInfo: {
|
|
@@ -62,72 +71,81 @@ export declare class McpServer {
|
|
|
62
71
|
};
|
|
63
72
|
private buffer;
|
|
64
73
|
private running;
|
|
65
|
-
|
|
74
|
+
/** Serializes `start()` stream handling so chunk order is preserved. */
|
|
75
|
+
private chain;
|
|
76
|
+
/**
|
|
77
|
+
* Diff baselines per resolved state path: the state as of the last
|
|
78
|
+
* `state.diff` call (or, before the first look, as of the first write).
|
|
79
|
+
*/
|
|
80
|
+
private readonly prevStates;
|
|
81
|
+
/** Writes (patch/rollback) applied per resolved state path this session. */
|
|
82
|
+
private readonly writeSeq;
|
|
66
83
|
constructor(options: McpServerOptions);
|
|
67
84
|
/**
|
|
68
85
|
* Process a single (already-framed) JSON-RPC message line and return the
|
|
69
86
|
* response string, or `null` when the message needs no reply (a
|
|
70
|
-
* notification).
|
|
71
|
-
* stdio transport
|
|
87
|
+
* notification). The stateless unit entry point used by tests and the
|
|
88
|
+
* stdio transport.
|
|
72
89
|
*/
|
|
73
|
-
handleLine(line: string): string | null
|
|
90
|
+
handleLine(line: string): Promise<string | null>;
|
|
74
91
|
/**
|
|
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.
|
|
92
|
+
* Feed a raw chunk of stdin and return every newline-delimited response
|
|
93
|
+
* produced by the complete messages it contains. Partial lines are
|
|
94
|
+
* buffered until the rest arrives. Each response ends with a newline.
|
|
80
95
|
*/
|
|
81
|
-
feed(chunk: string): string[]
|
|
96
|
+
feed(chunk: string): Promise<string[]>;
|
|
82
97
|
/**
|
|
83
98
|
* 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.
|
|
99
|
+
* Resolves once the server is reading; `stop()` detaches it. Chunks are
|
|
100
|
+
* processed strictly in arrival order even though handling is async.
|
|
87
101
|
*/
|
|
88
102
|
start(input?: Readable, output?: Writable): Promise<McpServer>;
|
|
89
103
|
/** Mark the server stopped (idempotent). */
|
|
90
104
|
stop(): void;
|
|
91
105
|
/** Whether the server is currently reading from its input stream. */
|
|
92
106
|
get isRunning(): boolean;
|
|
107
|
+
/** Append one chunk to the ordered stream pipeline. */
|
|
108
|
+
private pump;
|
|
93
109
|
/** Parse raw text into a message and dispatch; `-32700` on parse error. */
|
|
94
110
|
private processRaw;
|
|
95
111
|
private processMessage;
|
|
96
112
|
private handleRequest;
|
|
97
113
|
private handleToolCall;
|
|
114
|
+
private handleResourceRead;
|
|
98
115
|
private callTool;
|
|
99
116
|
private stateGet;
|
|
100
117
|
private statePatch;
|
|
101
|
-
private
|
|
102
|
-
private
|
|
103
|
-
private
|
|
118
|
+
private stateValidate;
|
|
119
|
+
private stateDiff;
|
|
120
|
+
private stateCheckpoint;
|
|
121
|
+
private stateRollback;
|
|
122
|
+
private stateSummary;
|
|
104
123
|
private stateMetrics;
|
|
124
|
+
private specGet;
|
|
125
|
+
private specNext;
|
|
105
126
|
/** Resolve the target state file path (args override the defaults). */
|
|
106
127
|
private resolveStore;
|
|
128
|
+
/** Resolve `{ root, name }` + the confined file path in one go. */
|
|
129
|
+
private resolveRef;
|
|
107
130
|
/** Read + normalize the state, falling back to schema defaults. */
|
|
108
131
|
private loadState;
|
|
109
|
-
/**
|
|
132
|
+
/** Advance the per-path session write counter. */
|
|
133
|
+
private bumpWriteSeq;
|
|
134
|
+
/**
|
|
135
|
+
* Crash-safe synchronous write of the versioned envelope
|
|
136
|
+
* `{ version, state }`: temp sibling + fsync + rename.
|
|
137
|
+
*/
|
|
110
138
|
private writeState;
|
|
111
139
|
private toolsList;
|
|
112
140
|
private resourcesList;
|
|
113
|
-
/**
|
|
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.
|
|
118
|
-
*/
|
|
119
|
-
private takeContentLengthFrame;
|
|
120
|
-
/** Encode a response in the given framing mode. */
|
|
121
|
-
private encodeResponse;
|
|
122
141
|
private successResponse;
|
|
123
142
|
private errorResponse;
|
|
124
143
|
private textResult;
|
|
125
144
|
}
|
|
126
145
|
/**
|
|
127
|
-
* Per-project state resolution for an MCP server session
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* keep the two in sync):
|
|
146
|
+
* Per-project state resolution for an MCP server session — re-exported
|
|
147
|
+
* from `@skillstate/core` (the single source of truth shared with the
|
|
148
|
+
* OpenCode plugin and the generated hook scripts):
|
|
131
149
|
*
|
|
132
150
|
* - `cwd === home` — no single project → the global bucket
|
|
133
151
|
* `<home>/.skillstate/global/skillstate.json`;
|
|
@@ -135,7 +153,7 @@ export declare class McpServer {
|
|
|
135
153
|
*
|
|
136
154
|
* Pure path arithmetic (no filesystem access, `path.resolve` normalization).
|
|
137
155
|
*/
|
|
138
|
-
export
|
|
156
|
+
export { resolveHostStateForCwd as resolveStatePathForCwd } from '@skillstate/core';
|
|
139
157
|
/**
|
|
140
158
|
* Launch an MCP server from an argument/env config (reads
|
|
141
159
|
* `SKILLSTATE_SPEC_PATH` when not passed explicitly). State resolution is
|
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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"}
|