functionalscript 0.39.0 → 0.40.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 +1 -1
- package/fjs/bnf/descent/module.f.d.ts +43 -2
- package/fjs/bnf/descent/module.f.js +37 -12
- package/fjs/bnf/descent/proof.f.d.ts +1 -0
- package/fjs/bnf/descent/proof.f.js +73 -32
- package/fjs/bnf/ll1/module.f.js +2 -2
- package/fjs/cas/evo/module.f.d.ts +1 -1
- package/fjs/cas/evo/module.f.js +1 -1
- package/fjs/cas/evo/proof.f.js +1 -1
- package/fjs/ci/config/module.f.d.ts +7 -4
- package/fjs/ci/config/module.f.js +20 -5
- package/fjs/ci/module.f.js +12 -5
- package/fjs/ci/nix/module.f.d.ts +61 -0
- package/fjs/ci/nix/module.f.js +92 -0
- package/fjs/ci/nix/proof.f.d.ts +23 -0
- package/fjs/ci/nix/proof.f.js +109 -0
- package/fjs/ci/node/module.f.d.ts +23 -1
- package/fjs/ci/node/module.f.js +47 -3
- package/fjs/ci/proof.f.d.ts +2 -0
- package/fjs/ci/proof.f.js +47 -9
- package/fjs/dev/module.f.d.ts +1 -0
- package/fjs/dev/module.f.js +13 -2
- package/fjs/dev/update/module.f.d.ts +11 -0
- package/fjs/dev/update/module.f.js +20 -0
- package/fjs/dev/update/proof.f.d.ts +7 -0
- package/fjs/dev/update/proof.f.js +35 -0
- package/fjs/djs/ast/module.f.d.ts +47 -0
- package/fjs/djs/ast/module.f.js +9 -0
- package/fjs/djs/tokenizer/module.f.js +2 -2
- package/fjs/djs/tokenizer/proof.f.d.ts +1 -0
- package/fjs/djs/tokenizer/proof.f.js +70 -16
- package/fjs/effects/node/module.d.ts +3 -3
- package/fjs/effects/node/module.f.d.ts +17 -7
- package/fjs/effects/node/module.f.js +22 -0
- package/fjs/effects/node/module.js +12 -12
- package/fjs/effects/node/proof.f.d.ts +1 -0
- package/fjs/effects/node/proof.f.js +14 -1
- package/fjs/effects/node/virtual/module.f.js +1 -1
- package/fjs/emergent_testing/all.test.js +2 -1
- package/fjs/emergent_testing/module.f.d.ts +3 -3
- package/fjs/emergent_testing/module.f.js +8 -10
- package/fjs/emergent_testing/proof.f.js +2 -2
- package/fjs/emergent_testing/scenarios/thenable.pass.js +1 -1
- package/fjs/fsc/module.f.js +4 -4
- package/fjs/fsm/module.f.js +1 -1
- package/fjs/js/tokenizer/module.f.d.ts +1 -0
- package/fjs/js/tokenizer/module.f.js +13 -6
- package/fjs/{cas/mcp → mcp/cas}/module.f.d.ts +3 -25
- package/fjs/{cas/mcp → mcp/cas}/module.f.js +18 -58
- package/fjs/mcp/evo/module.f.d.ts +27 -0
- package/fjs/{cas/evo/mcp → mcp/evo}/module.f.js +9 -9
- package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.js +7 -7
- package/fjs/mcp/module.f.d.ts +54 -237
- package/fjs/mcp/module.f.js +55 -258
- package/fjs/mcp/proof.f.d.ts +43 -32
- package/fjs/mcp/proof.f.js +508 -200
- package/fjs/media/nix/module.f.d.ts +30 -0
- package/fjs/media/nix/module.f.js +166 -0
- package/fjs/media/nix/proof.f.d.ts +32 -0
- package/fjs/media/nix/proof.f.js +127 -0
- package/fjs/module.f.js +1 -1
- package/fjs/protocol/json_rpc/module.f.d.ts +114 -0
- package/fjs/{media/json/rpc → protocol/json_rpc}/module.f.js +3 -3
- package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.js +3 -3
- package/fjs/protocol/mcp/module.f.d.ts +239 -0
- package/fjs/protocol/mcp/module.f.js +272 -0
- package/fjs/protocol/mcp/proof.f.d.ts +34 -0
- package/fjs/protocol/mcp/proof.f.js +208 -0
- package/fjs/{mcp → protocol/mcp}/stdio/module.f.d.ts +5 -5
- package/fjs/{mcp → protocol/mcp}/stdio/module.f.js +11 -11
- package/fjs/{mcp → protocol/mcp}/stdio/proof.f.js +9 -9
- package/fjs/types/range_map/module.f.d.ts +14 -13
- package/fjs/types/range_map/module.f.js +18 -13
- package/fjs/types/range_map/proof.f.js +26 -39
- package/fjs/types/range_set/module.f.d.ts +5 -0
- package/fjs/types/range_set/module.f.js +16 -0
- package/fjs/types/range_set/proof.f.d.ts +1 -0
- package/fjs/types/range_set/proof.f.js +19 -0
- package/package.json +5 -5
- package/fjs/cas/evo/mcp/module.f.d.ts +0 -27
- package/fjs/cas/mcp/proof.f.d.ts +0 -45
- package/fjs/cas/mcp/proof.f.js +0 -545
- package/fjs/ci/playwright/module.f.d.ts +0 -2
- package/fjs/ci/playwright/module.f.js +0 -25
- package/fjs/media/json/rpc/module.f.d.ts +0 -114
- /package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.d.ts +0 -0
- /package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.d.ts +0 -0
- /package/fjs/{mcp → protocol/mcp}/stdio/proof.f.d.ts +0 -0
|
@@ -27,7 +27,7 @@ leftSquareBracket, reverseSolidus, rightSquareBracket, lowLine,
|
|
|
27
27
|
latinSmallLetterRange, latinSmallLetterA, latinSmallLetterB, latinSmallLetterE, latinSmallLetterF, latinSmallLetterN, latinSmallLetterR, latinSmallLetterT, latinSmallLetterU,
|
|
28
28
|
//
|
|
29
29
|
leftCurlyBracket, rightCurlyBracket, dollarSign } from '../../text/ascii/module.f.js';
|
|
30
|
-
import { todo } from '../../asserts/module.f.js';
|
|
30
|
+
import { todo, assertEq } from '../../asserts/module.f.js';
|
|
31
31
|
const { fromCharCode } = String;
|
|
32
32
|
const rangeOneNine = range('19');
|
|
33
33
|
const rangeSetNewLine = [
|
|
@@ -111,21 +111,20 @@ const rangeMapMerge = def => merge({
|
|
|
111
111
|
equal: strictEqual,
|
|
112
112
|
def,
|
|
113
113
|
});
|
|
114
|
-
const rangeFunc = r => f => def => fromRange(def)(
|
|
114
|
+
const rangeFunc = r => f => def => fromRange(def)(f)(r);
|
|
115
115
|
const scanRangeOp = def => f => [f(def), scanRangeOp(def)];
|
|
116
116
|
const reduceRangeMap = def => a => {
|
|
117
117
|
const rm = scan(scanRangeOp(def))(a);
|
|
118
118
|
return toArray(listReduce(rangeMapMerge(def))(empty)(rm));
|
|
119
119
|
};
|
|
120
|
-
const scanRangeSetOp = def => f => r => [fromRange(def)(
|
|
120
|
+
const scanRangeSetOp = def => f => r => [fromRange(def)(f)(r), scanRangeSetOp(def)(f)];
|
|
121
121
|
const rangeSetFunc = rs => f => def => {
|
|
122
122
|
const rm = scan(scanRangeSetOp(def)(f))(rs);
|
|
123
123
|
return toArray(listReduce(rangeMapMerge(def))(empty)(rm));
|
|
124
124
|
};
|
|
125
125
|
const create = (def) => (a) => {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
return v => c => x(c)(i)(v)(c);
|
|
126
|
+
const x = get(def)(reduceRangeMap(def)(a));
|
|
127
|
+
return v => c => x(c)(v)(c);
|
|
129
128
|
};
|
|
130
129
|
const digitToBigInt = d => BigInt(d - digit0);
|
|
131
130
|
const startNumber = digit => ({ s: 1n, m: digitToBigInt(digit), f: 0, es: 1, e: 0 });
|
|
@@ -527,6 +526,14 @@ export const tokenize = input => path => {
|
|
|
527
526
|
return flat(scan(flat([input /*satisfies List<CharCodeOrEof>*/, [null]])));
|
|
528
527
|
};
|
|
529
528
|
export const proof = {
|
|
529
|
+
// `getOperatorToken` is only ever called with a value already confirmed to be
|
|
530
|
+
// a known operator (`hasOperatorToken`, or a single char from `rangeOpStart`),
|
|
531
|
+
// so its `??` fallback is unreachable through `tokenize`. Call it directly
|
|
532
|
+
// with a non-operator string to cover that branch.
|
|
533
|
+
getOperatorTokenInvalid: () => {
|
|
534
|
+
const result = getOperatorToken('@');
|
|
535
|
+
assertEq(result.kind, 'error');
|
|
536
|
+
},
|
|
530
537
|
throw: {
|
|
531
538
|
// union throws when two distinct non-default handlers are merged for the same range;
|
|
532
539
|
// this path is unreachable through the public API (no overlapping ranges in practice).
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { type Effect } from '../../effects/module.f.ts';
|
|
2
1
|
import { type MemOp } from '../../effects/memory/module.f.ts';
|
|
3
|
-
import { type
|
|
4
|
-
import { type
|
|
5
|
-
import { type
|
|
6
|
-
import { type Cache } from '../evo/module.f.ts';
|
|
2
|
+
import { type ToolEntry } from '../../protocol/mcp/module.f.ts';
|
|
3
|
+
import { type FileCasOperation } from '../../cas/module.f.ts';
|
|
4
|
+
import { type Cache } from '../../cas/evo/module.f.ts';
|
|
7
5
|
import type { Key } from '../../effects/memory/module.f.ts';
|
|
8
6
|
/** Arguments for `cas_add`: content to store, with optional encoding type. */
|
|
9
7
|
export declare const casAddArgs: {
|
|
@@ -27,23 +25,3 @@ export declare const casListArgs: {};
|
|
|
27
25
|
* rescan.
|
|
28
26
|
*/
|
|
29
27
|
export declare const casToolRegistry: (home: string) => (cacheKey: Key<Cache>) => readonly ToolEntry<FileCasOperation | MemOp>[];
|
|
30
|
-
/**
|
|
31
|
-
* MCP handlers for `FileCas` plus the Evo API (`fjs/cas/evo`) layered on it,
|
|
32
|
-
* bound to `home` and an already-built Evo cache slot (see `initEvo`).
|
|
33
|
-
*/
|
|
34
|
-
export declare const casMcpHandlers: (home: string) => (cacheKey: Key<Cache>) => McpHandlers<FileCasOperation | MemOp>;
|
|
35
|
-
/**
|
|
36
|
-
* Static MCP configuration for the CAS server: advertises the `tools`
|
|
37
|
-
* capability, identifies the server, and pins the protocol version.
|
|
38
|
-
*/
|
|
39
|
-
export declare const casConfig: McpConfig;
|
|
40
|
-
/**
|
|
41
|
-
* Runs the combined CAS + Evo MCP server over stdio: scans `~/.cas/` once to
|
|
42
|
-
* build the Evo subject/head cache (`initEvo`), allocates the session-state
|
|
43
|
-
* slot, builds the `mcpStep` for the merged tool registry, and drives the
|
|
44
|
-
* read → parse → dispatch → write loop until stdin EOF.
|
|
45
|
-
*/
|
|
46
|
-
export declare const casMcpServer: (home: string) => Effect<Read | Write | MemOp | FileCasOperation, void>;
|
|
47
|
-
export declare const proof: {
|
|
48
|
-
casMcpServer: () => void;
|
|
49
|
-
};
|
|
@@ -1,28 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* store a blob and get back its hash, fetch a blob by hash, and enumerate
|
|
7
|
-
* what is stored — without shelling out to the `cas` CLI. The store itself
|
|
2
|
+
* The `cas_add` / `cas_get` / `cas_list` tool registry: maps `Cas<O>`
|
|
3
|
+
* operations onto MCP tools, so an agent that speaks MCP can store a blob and
|
|
4
|
+
* get back its hash, fetch a blob by hash, and enumerate what is stored —
|
|
5
|
+
* without shelling out to the `cas` CLI. The store itself
|
|
8
6
|
* (`fjs/cas/module.f.ts`) stays transport-agnostic; this is an additional
|
|
9
|
-
* front end alongside the CLI `main`.
|
|
10
|
-
* `fjs/
|
|
11
|
-
* (
|
|
12
|
-
*
|
|
13
|
-
* startup (`initEvo`).
|
|
7
|
+
* front end alongside the CLI `main`. This registry is one of the tool sets
|
|
8
|
+
* `fjs/mcp/module.f.ts` composes into the FJS MCP server — see that module
|
|
9
|
+
* for the full tool table (including `fjs/mcp/evo`'s `evo_*` tools) and the
|
|
10
|
+
* server entry point.
|
|
14
11
|
*
|
|
15
12
|
* ## Tools
|
|
16
13
|
*
|
|
17
|
-
* | Tool | args
|
|
18
|
-
*
|
|
19
|
-
* | `cas_add` | `{ content, type? }`
|
|
20
|
-
* | `cas_get` | `{ hash, content?: boolean }`
|
|
21
|
-
* | `cas_list` | `{}`
|
|
22
|
-
* | `evo_list` | `{}` | `e.list()` | subjects, one per line |
|
|
23
|
-
* | `evo_head` | `{ subject }` | `e.head(...)` | head hashes, one per line |
|
|
24
|
-
* | `evo_revision` | `{ hash }` | `e.revision(...)`| the revision, as JSON |
|
|
25
|
-
* | `evo_add` | `{ parents, snapshot?, subject?, archived? }` | `e.add(...)` | hash (cBase32) |
|
|
14
|
+
* | Tool | args | action | result |
|
|
15
|
+
* |----------------|--------------------------------|------------------|--------------------------------------|
|
|
16
|
+
* | `cas_add` | `{ content, type? }` | `c.write(...)` | hash (cBase32) |
|
|
17
|
+
* | `cas_get` | `{ hash, content?: boolean }` | `c.read(key)` | JSON `{length,mimeType,type[,uri][,text\|blob]}` |
|
|
18
|
+
* | `cas_list` | `{}` | `c.list()` | hashes, one per line |
|
|
26
19
|
*
|
|
27
20
|
* ## `cas_add` input encoding
|
|
28
21
|
*
|
|
@@ -35,7 +28,7 @@
|
|
|
35
28
|
*
|
|
36
29
|
* Inline content (either encoding) is capped at 128 KiB (`maxLength`). There is
|
|
37
30
|
* no MCP route for larger content — the server never opens a local path named by
|
|
38
|
-
* the client (see the invariant in `fjs/
|
|
31
|
+
* the client (see the invariant in `fjs/mcp/README.md`); large files go
|
|
39
32
|
* through the `cas` CLI (`cas add <path>`) instead, run directly by the user.
|
|
40
33
|
*
|
|
41
34
|
* ## `cas_get` output
|
|
@@ -111,7 +104,7 @@
|
|
|
111
104
|
import { string, option, or, boolean } from '../../types/rtti/module.f.js';
|
|
112
105
|
import { stringify } from '../../media/json/module.f.js';
|
|
113
106
|
import { pure, step } from '../../effects/module.f.js';
|
|
114
|
-
import {
|
|
107
|
+
import {} from '../../effects/memory/module.f.js';
|
|
115
108
|
import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.js';
|
|
116
109
|
import { decode as base64Decode, encode as base64Encode } from '../../basen/base64/module.f.js';
|
|
117
110
|
import { tryUtf8 } from '../../text/module.f.js';
|
|
@@ -119,16 +112,13 @@ import { detectStream } from '../../media/type/module.f.js';
|
|
|
119
112
|
import { detect as detectDialect } from '../../media/module.f.js';
|
|
120
113
|
import { maxLengthBytes } from '../../types/bit_vec/module.f.js';
|
|
121
114
|
import { ok } from '../../types/result/module.f.js';
|
|
122
|
-
import {} from '../../
|
|
123
|
-
import {
|
|
124
|
-
import { mcpStep, uninitializedState, toolEntry, fromRegistry, errorResult, okResult, } from '../../mcp/module.f.js';
|
|
125
|
-
import { collectRead, fileCas } from '../module.f.js';
|
|
115
|
+
import { toolEntry, errorResult, okResult, } from '../../protocol/mcp/module.f.js';
|
|
116
|
+
import { collectRead, fileCas } from '../../cas/module.f.js';
|
|
126
117
|
import { fromVec } from '../../text/utf8/module.f.js';
|
|
127
118
|
import { identity } from '../../types/function/module.f.js';
|
|
128
119
|
import { sha256 } from '../../crypto/sha2/module.f.js';
|
|
129
120
|
import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.js';
|
|
130
|
-
import {
|
|
131
|
-
import { evoToolRegistry } from '../evo/mcp/module.f.js';
|
|
121
|
+
import { syncRevision } from '../../cas/evo/module.f.js';
|
|
132
122
|
// ── Argument schemas (declared once, used for both inputSchema and validate) ─────
|
|
133
123
|
/** Arguments for `cas_add`: content to store, with optional encoding type. */
|
|
134
124
|
export const casAddArgs = {
|
|
@@ -237,33 +227,3 @@ export const casToolRegistry = (home) => (cacheKey) => {
|
|
|
237
227
|
toolEntry('cas_list', 'List all stored content hashes (cBase32), one per line.', casListArgs, () => step(c.list(), hashes => pure(okResult(hashes.map(vecToCBase32).join('\n'))))),
|
|
238
228
|
];
|
|
239
229
|
};
|
|
240
|
-
// ── Handlers ────────────────────────────────────────────────────────────────────
|
|
241
|
-
/**
|
|
242
|
-
* MCP handlers for `FileCas` plus the Evo API (`fjs/cas/evo`) layered on it,
|
|
243
|
-
* bound to `home` and an already-built Evo cache slot (see `initEvo`).
|
|
244
|
-
*/
|
|
245
|
-
export const casMcpHandlers = (home) => (cacheKey) => fromRegistry([...casToolRegistry(home)(cacheKey), ...evoToolRegistry(evo(fileCas(sha256)(home))(cacheKey))]);
|
|
246
|
-
// ── Session configuration ───────────────────────────────────────────────────────
|
|
247
|
-
/**
|
|
248
|
-
* Static MCP configuration for the CAS server: advertises the `tools`
|
|
249
|
-
* capability, identifies the server, and pins the protocol version.
|
|
250
|
-
*/
|
|
251
|
-
export const casConfig = {
|
|
252
|
-
serverInfo: { name: 'functionalscript-cas', version: '0.30.0' },
|
|
253
|
-
capabilities: { tools: {} },
|
|
254
|
-
protocolVersion: '2024-11-05',
|
|
255
|
-
};
|
|
256
|
-
// ── Server ──────────────────────────────────────────────────────────────────────
|
|
257
|
-
/**
|
|
258
|
-
* Runs the combined CAS + Evo MCP server over stdio: scans `~/.cas/` once to
|
|
259
|
-
* build the Evo subject/head cache (`initEvo`), allocates the session-state
|
|
260
|
-
* slot, builds the `mcpStep` for the merged tool registry, and drives the
|
|
261
|
-
* read → parse → dispatch → write loop until stdin EOF.
|
|
262
|
-
*/
|
|
263
|
-
export const casMcpServer = (home) => step(initEvo(fileCas(sha256)(home)), cacheKey => step(create(uninitializedState), sessionKey => stdioTransport(mcpStep(casConfig)(casMcpHandlers(home)(cacheKey))(sessionKey))));
|
|
264
|
-
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
265
|
-
export const proof = {
|
|
266
|
-
// casMcpServer is never called in integration tests because it drives a
|
|
267
|
-
// real stdio server; call it here to cover its Effect-building body.
|
|
268
|
-
casMcpServer: () => { casMcpServer('/'); },
|
|
269
|
-
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Operation } from '../../effects/module.f.ts';
|
|
2
|
+
import { type MemOp } from '../../effects/memory/module.f.ts';
|
|
3
|
+
import { type ToolEntry } from '../../protocol/mcp/module.f.ts';
|
|
4
|
+
import { type Evo } from '../../cas/evo/module.f.ts';
|
|
5
|
+
/** Arguments for `evo_list`: none. */
|
|
6
|
+
export declare const evoListArgs: {};
|
|
7
|
+
/** Arguments for `evo_head`: the subject whose current heads are requested. */
|
|
8
|
+
export declare const evoHeadArgs: {
|
|
9
|
+
readonly subject: import("../../types/rtti/module.f.ts").String;
|
|
10
|
+
};
|
|
11
|
+
/** Arguments for `evo_revision`: the hash of the revision to read. */
|
|
12
|
+
export declare const evoRevisionArgs: {
|
|
13
|
+
readonly hash: import("../../types/rtti/module.f.ts").String;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Arguments for `evo_add`: a new revision, per `fjs/cas/evo`'s
|
|
17
|
+
* `RevisionData` — every field of it the caller supplies, i.e. all but
|
|
18
|
+
* `generation`, which the server computes.
|
|
19
|
+
*/
|
|
20
|
+
export declare const evoAddArgs: {
|
|
21
|
+
readonly parents: import("../../types/rtti/module.f.ts").Type1<"array", import("../../types/rtti/module.f.ts").String>;
|
|
22
|
+
readonly snapshot: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
23
|
+
readonly subject: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
24
|
+
readonly archived: import("../../types/rtti/module.f.ts").Or<readonly [true, undefined]>;
|
|
25
|
+
};
|
|
26
|
+
/** Registry of all Evo tools, bound to an `Evo<O>`. */
|
|
27
|
+
export declare const evoToolRegistry: <O extends Operation>(e: Evo<O>) => readonly ToolEntry<O | MemOp>[];
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* content, so the JSON-RPC serializer escapes it a second time on the way
|
|
28
28
|
* out and a modest result can encode to a much longer line (a subject of
|
|
29
29
|
* quote characters is the worst case). A response whose encoded line exceeds
|
|
30
|
-
* the transport cap is not lost: `fjs/mcp/stdio` retries with a small
|
|
30
|
+
* the transport cap is not lost: `fjs/protocol/mcp/stdio` retries with a small
|
|
31
31
|
* `-32603` body carrying the request's `id`, so every request still gets a
|
|
32
32
|
* response and the process never crashes. That is the transport's contract
|
|
33
33
|
* for every tool — `cas_get` has proofs for the same double-escaping path —
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* known only by encoding it, which is the transport's job, and guessing from
|
|
36
36
|
* an unencoded size is exactly the size estimate that must never be made.
|
|
37
37
|
*
|
|
38
|
-
* These tools are not served by their own process: `fjs/
|
|
38
|
+
* These tools are not served by their own process: `fjs/mcp` (the same
|
|
39
39
|
* server as `cas_add`/`cas_get`/`cas_list`) builds one `Evo<O>` from its own
|
|
40
40
|
* `Cas<O>` and cache slot, concatenates `evoToolRegistry` onto its own
|
|
41
41
|
* registry, and serves everything from that single process — one `~/.cas/`
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
*
|
|
44
44
|
* @module
|
|
45
45
|
*/
|
|
46
|
-
import { string, option, array } from '
|
|
47
|
-
import { pure, step } from '
|
|
48
|
-
import {} from '
|
|
49
|
-
import { toolEntry, errorResult, okResult, } from '
|
|
50
|
-
import { stringify } from '
|
|
51
|
-
import { identity } from '
|
|
52
|
-
import {} from '
|
|
46
|
+
import { string, option, array } from '../../types/rtti/module.f.js';
|
|
47
|
+
import { pure, step } from '../../effects/module.f.js';
|
|
48
|
+
import {} from '../../effects/memory/module.f.js';
|
|
49
|
+
import { toolEntry, errorResult, okResult, } from '../../protocol/mcp/module.f.js';
|
|
50
|
+
import { stringify } from '../../media/json/module.f.js';
|
|
51
|
+
import { identity } from '../../types/function/module.f.js';
|
|
52
|
+
import {} from '../../cas/evo/module.f.js';
|
|
53
53
|
// ── Argument schemas (declared once, used for both inputSchema and validate) ─────
|
|
54
54
|
/** Arguments for `evo_list`: none. */
|
|
55
55
|
export const evoListArgs = {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { assert, assertEq } from '
|
|
2
|
-
import { fileCas } from '../../module.f.js';
|
|
3
|
-
import { sha256 } from '
|
|
4
|
-
import { emptyState, virtual } from '
|
|
5
|
-
import { vec8 } from '
|
|
6
|
-
import { vecToCBase32 } from '
|
|
7
|
-
import { initEvo, evo } from '
|
|
1
|
+
import { assert, assertEq } from '../../asserts/module.f.js';
|
|
2
|
+
import { fileCas } from '../../cas/module.f.js';
|
|
3
|
+
import { sha256 } from '../../crypto/sha2/module.f.js';
|
|
4
|
+
import { emptyState, virtual } from '../../effects/node/virtual/module.f.js';
|
|
5
|
+
import { vec8 } from '../../types/bit_vec/module.f.js';
|
|
6
|
+
import { vecToCBase32 } from '../../basen/cbase32/module.f.js';
|
|
7
|
+
import { initEvo, evo } from '../../cas/evo/module.f.js';
|
|
8
8
|
import { evoToolRegistry } from './module.f.js';
|
|
9
9
|
const home = '/home/user';
|
|
10
10
|
const findEntry = (registry, name) => {
|
package/fjs/mcp/module.f.d.ts
CHANGED
|
@@ -1,239 +1,56 @@
|
|
|
1
|
-
import { unknown, type Unknown } from '../media/json/module.f.ts';
|
|
2
|
-
import type { Ts } from '../types/rtti/ts/module.f.ts';
|
|
3
|
-
import { type Operation, type Effect } from '../effects/module.f.ts';
|
|
4
|
-
import { type Key, type MemOp } from '../effects/memory/module.f.ts';
|
|
5
|
-
import { type Response } from '../media/json/rpc/module.f.ts';
|
|
6
|
-
import type { Type } from '../types/rtti/module.f.ts';
|
|
7
|
-
/** Name + version pair sent in `initialize` requests and responses. */
|
|
8
|
-
export declare const implementation: {
|
|
9
|
-
readonly name: import("../types/rtti/module.f.ts").String;
|
|
10
|
-
readonly version: import("../types/rtti/module.f.ts").String;
|
|
11
|
-
};
|
|
12
|
-
export type Implementation = Ts<typeof implementation>;
|
|
13
|
-
/** Server capabilities advertised in the `initialize` response. */
|
|
14
|
-
export declare const serverCapabilities: {
|
|
15
|
-
readonly tools: import("../types/rtti/module.f.ts").Or<readonly [{
|
|
16
|
-
readonly listChanged: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").Boolean, undefined]>;
|
|
17
|
-
}, undefined]>;
|
|
18
|
-
};
|
|
19
|
-
export type ServerCapabilities = Ts<typeof serverCapabilities>;
|
|
20
|
-
/** Params for the `initialize` request. */
|
|
21
|
-
export declare const initializeParams: {
|
|
22
|
-
readonly protocolVersion: import("../types/rtti/module.f.ts").String;
|
|
23
|
-
readonly capabilities: typeof unknown;
|
|
24
|
-
readonly clientInfo: {
|
|
25
|
-
readonly name: import("../types/rtti/module.f.ts").String;
|
|
26
|
-
readonly version: import("../types/rtti/module.f.ts").String;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
export type InitializeParams = Ts<typeof initializeParams>;
|
|
30
|
-
/** Result for the `initialize` request. */
|
|
31
|
-
export declare const initializeResult: {
|
|
32
|
-
readonly protocolVersion: import("../types/rtti/module.f.ts").String;
|
|
33
|
-
readonly capabilities: {
|
|
34
|
-
readonly tools: import("../types/rtti/module.f.ts").Or<readonly [{
|
|
35
|
-
readonly listChanged: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").Boolean, undefined]>;
|
|
36
|
-
}, undefined]>;
|
|
37
|
-
};
|
|
38
|
-
readonly serverInfo: {
|
|
39
|
-
readonly name: import("../types/rtti/module.f.ts").String;
|
|
40
|
-
readonly version: import("../types/rtti/module.f.ts").String;
|
|
41
|
-
};
|
|
42
|
-
readonly instructions: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
43
|
-
};
|
|
44
|
-
export type InitializeResult = Ts<typeof initializeResult>;
|
|
45
|
-
/** Plain-text content item returned by a tool call. */
|
|
46
|
-
export declare const textContent: {
|
|
47
|
-
readonly type: 'text';
|
|
48
|
-
readonly text: import("../types/rtti/module.f.ts").String;
|
|
49
|
-
};
|
|
50
|
-
export type TextContent = Ts<typeof textContent>;
|
|
51
|
-
/**
|
|
52
|
-
* A binary resource carried inside an {@link embeddedResource}: a base64
|
|
53
|
-
* `blob`, an addressing `uri`, and an optional `mimeType`. This is MCP's
|
|
54
|
-
* `BlobResource` shape — the idiomatic way to return typed binary content so a
|
|
55
|
-
* `mimeType` travels alongside the bytes and clients know how to route them.
|
|
56
|
-
*/
|
|
57
|
-
export declare const blobResource: {
|
|
58
|
-
readonly uri: import("../types/rtti/module.f.ts").String;
|
|
59
|
-
readonly mimeType: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
60
|
-
readonly blob: import("../types/rtti/module.f.ts").String;
|
|
61
|
-
};
|
|
62
|
-
export type BlobResource = Ts<typeof blobResource>;
|
|
63
|
-
/** An `EmbeddedResource` content item wrapping a {@link blobResource}. */
|
|
64
|
-
export declare const embeddedResource: {
|
|
65
|
-
readonly type: 'resource';
|
|
66
|
-
readonly resource: {
|
|
67
|
-
readonly uri: import("../types/rtti/module.f.ts").String;
|
|
68
|
-
readonly mimeType: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
69
|
-
readonly blob: import("../types/rtti/module.f.ts").String;
|
|
70
|
-
};
|
|
71
|
-
};
|
|
72
|
-
export type EmbeddedResource = Ts<typeof embeddedResource>;
|
|
73
|
-
/**
|
|
74
|
-
* A single item in a `tools/call` result's `content` array: either plain
|
|
75
|
-
* {@link textContent} or an {@link embeddedResource} for typed binary. The
|
|
76
|
-
* `image` and `audio` variants are not modelled yet.
|
|
77
|
-
*/
|
|
78
|
-
export declare const contentItem: import("../types/rtti/module.f.ts").Or<[{
|
|
79
|
-
readonly type: 'text';
|
|
80
|
-
readonly text: import("../types/rtti/module.f.ts").String;
|
|
81
|
-
}, {
|
|
82
|
-
readonly type: 'resource';
|
|
83
|
-
readonly resource: {
|
|
84
|
-
readonly uri: import("../types/rtti/module.f.ts").String;
|
|
85
|
-
readonly mimeType: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
86
|
-
readonly blob: import("../types/rtti/module.f.ts").String;
|
|
87
|
-
};
|
|
88
|
-
}]>;
|
|
89
|
-
export type ContentItem = Ts<typeof contentItem>;
|
|
90
|
-
/**
|
|
91
|
-
* A tool descriptor returned by `tools/list`.
|
|
92
|
-
* `inputSchema` is a JSON Schema object — use `toJsonSchema` to derive it from
|
|
93
|
-
* an rtti schema.
|
|
94
|
-
*/
|
|
95
|
-
export declare const tool: {
|
|
96
|
-
readonly name: import("../types/rtti/module.f.ts").String;
|
|
97
|
-
readonly description: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
98
|
-
readonly inputSchema: typeof unknown;
|
|
99
|
-
};
|
|
100
|
-
export type Tool = Ts<typeof tool>;
|
|
101
|
-
/**
|
|
102
|
-
* Params for the `tools/list` request. `cursor` is an opaque pagination token
|
|
103
|
-
* from a previous `ToolsListResult.nextCursor`.
|
|
104
|
-
*/
|
|
105
|
-
export declare const toolsListParams: {
|
|
106
|
-
readonly cursor: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
107
|
-
};
|
|
108
|
-
export type ToolsListParams = Ts<typeof toolsListParams>;
|
|
109
|
-
export declare const toolsListResult: {
|
|
110
|
-
readonly tools: import("../types/rtti/module.f.ts").Type1<"array", {
|
|
111
|
-
readonly name: import("../types/rtti/module.f.ts").String;
|
|
112
|
-
readonly description: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
113
|
-
readonly inputSchema: typeof unknown;
|
|
114
|
-
}>;
|
|
115
|
-
readonly nextCursor: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
116
|
-
};
|
|
117
|
-
export type ToolsListResult = Ts<typeof toolsListResult>;
|
|
118
|
-
export declare const toolsCallParams: {
|
|
119
|
-
readonly name: import("../types/rtti/module.f.ts").String;
|
|
120
|
-
readonly arguments: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").Type1<"record", typeof unknown>, undefined]>;
|
|
121
|
-
};
|
|
122
|
-
export type ToolsCallParams = Ts<typeof toolsCallParams>;
|
|
123
|
-
export declare const toolsCallResult: {
|
|
124
|
-
readonly content: import("../types/rtti/module.f.ts").Type1<"array", import("../types/rtti/module.f.ts").Or<[{
|
|
125
|
-
readonly type: 'text';
|
|
126
|
-
readonly text: import("../types/rtti/module.f.ts").String;
|
|
127
|
-
}, {
|
|
128
|
-
readonly type: 'resource';
|
|
129
|
-
readonly resource: {
|
|
130
|
-
readonly uri: import("../types/rtti/module.f.ts").String;
|
|
131
|
-
readonly mimeType: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").String, undefined]>;
|
|
132
|
-
readonly blob: import("../types/rtti/module.f.ts").String;
|
|
133
|
-
};
|
|
134
|
-
}]>>;
|
|
135
|
-
readonly isError: import("../types/rtti/module.f.ts").Or<readonly [import("../types/rtti/module.f.ts").Boolean, undefined]>;
|
|
136
|
-
};
|
|
137
|
-
export type ToolsCallResult = Ts<typeof toolsCallResult>;
|
|
138
|
-
/** Per-method handlers for a hello-world MCP tool server. */
|
|
139
|
-
export type McpHandlers<O extends Operation> = {
|
|
140
|
-
readonly toolsList: (params: ToolsListParams) => Effect<O, ToolsListResult>;
|
|
141
|
-
readonly toolsCall: (params: ToolsCallParams) => Effect<O, ToolsCallResult>;
|
|
142
|
-
};
|
|
143
1
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
2
|
+
* The FJS MCP server: the composition root the `fjs mcp` / `m` CLI command
|
|
3
|
+
* runs. Session configuration (`McpConfig`), the top-level entry point
|
|
4
|
+
* (`casMcpServer`, wiring `mcpStep` + `stdioTransport` from
|
|
5
|
+
* `fjs/protocol/mcp/`), and the composed tool registry — nothing CAS- or
|
|
6
|
+
* Evo-specific lives here, it only knows about tool *registries*, not what is
|
|
7
|
+
* in them, so a future non-CAS tool set can land as a new sibling of
|
|
8
|
+
* `fjs/mcp/cas/` and `fjs/mcp/evo/` without touching either of them.
|
|
9
|
+
*
|
|
10
|
+
* ## Tools
|
|
11
|
+
*
|
|
12
|
+
* | Tool | args | action | result |
|
|
13
|
+
* |----------------|------------------------------------------------|------------------|--------------------------------------|
|
|
14
|
+
* | `cas_add` | `{ content, type? }` | `c.write(...)` | hash (cBase32) |
|
|
15
|
+
* | `cas_get` | `{ hash, content?: boolean }` | `c.read(key)` | JSON `{length,mimeType,type[,uri][,text\|blob]}` |
|
|
16
|
+
* | `cas_list` | `{}` | `c.list()` | hashes, one per line |
|
|
17
|
+
* | `evo_list` | `{}` | `e.list()` | subjects, one per line |
|
|
18
|
+
* | `evo_head` | `{ subject }` | `e.head(...)` | head hashes, one per line |
|
|
19
|
+
* | `evo_revision` | `{ hash }` | `e.revision(...)`| the revision, as JSON |
|
|
20
|
+
* | `evo_add` | `{ parents, snapshot?, subject?, archived? }` | `e.add(...)` | hash (cBase32) |
|
|
21
|
+
*
|
|
22
|
+
* `cas_add`/`cas_get`/`cas_list` are `fjs/mcp/cas` — see that module for tool
|
|
23
|
+
* documentation (input encoding, output shape, error convention). `evo_*` is
|
|
24
|
+
* `fjs/mcp/evo`. Both tool sets run in one process, sharing one `~/.cas/`
|
|
25
|
+
* store and one in-memory Evo cache scanned once at startup (`initEvo`).
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import { type Effect } from '../effects/module.f.ts';
|
|
30
|
+
import { type MemOp } from '../effects/memory/module.f.ts';
|
|
31
|
+
import { type Read, type Write } from '../effects/node/module.f.ts';
|
|
32
|
+
import { type McpConfig, type McpHandlers } from '../protocol/mcp/module.f.ts';
|
|
33
|
+
import { type FileCasOperation } from '../cas/module.f.ts';
|
|
34
|
+
import { type Cache } from '../cas/evo/module.f.ts';
|
|
35
|
+
import type { Key } from '../effects/memory/module.f.ts';
|
|
36
|
+
/**
|
|
37
|
+
* MCP handlers for `FileCas` (`fjs/mcp/cas`) plus the Evo API (`fjs/mcp/evo`)
|
|
38
|
+
* layered on it, bound to `home` and an already-built Evo cache slot (see
|
|
39
|
+
* `initEvo`).
|
|
40
|
+
*/
|
|
41
|
+
export declare const casMcpHandlers: (home: string) => (cacheKey: Key<Cache>) => McpHandlers<FileCasOperation | MemOp>;
|
|
42
|
+
/**
|
|
43
|
+
* Static MCP configuration for the CAS server: advertises the `tools`
|
|
44
|
+
* capability, identifies the server, and pins the protocol version.
|
|
45
|
+
*/
|
|
46
|
+
export declare const casConfig: McpConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Runs the combined CAS + Evo MCP server over stdio: scans `~/.cas/` once to
|
|
49
|
+
* build the Evo subject/head cache (`initEvo`), allocates the session-state
|
|
50
|
+
* slot, builds the `mcpStep` for the merged tool registry, and drives the
|
|
51
|
+
* read → parse → dispatch → write loop until stdin EOF.
|
|
52
|
+
*/
|
|
53
|
+
export declare const casMcpServer: (home: string) => Effect<Read | Write | MemOp | FileCasOperation, void>;
|
|
54
|
+
export declare const proof: {
|
|
55
|
+
casMcpServer: () => void;
|
|
154
56
|
};
|
|
155
|
-
/**
|
|
156
|
-
* Creates a type-safe tool entry that binds an RTTI schema with a handler.
|
|
157
|
-
*
|
|
158
|
-
* The builder validates arguments at runtime using the RTTI and passes pre-validated
|
|
159
|
-
* arguments (typed as `Ts<T>`) to the handler. This eliminates manual validation
|
|
160
|
-
* boilerplate and type assertions.
|
|
161
|
-
*
|
|
162
|
-
* @param name - The tool name (used in `tools/call` requests)
|
|
163
|
-
* @param description - Human-readable description for `tools/list`
|
|
164
|
-
* @param inputRtti - Runtime type info for input validation
|
|
165
|
-
* @param handle - Handler receiving validated arguments of type `Ts<inputRtti>`
|
|
166
|
-
* @returns A `ToolEntry` ready to be added to a registry
|
|
167
|
-
*/
|
|
168
|
-
export declare const toolEntry: <T extends Type, O extends Operation>(name: string, description: string, inputRtti: T, handle: (args: Ts<T>) => Effect<O, ToolsCallResult>) => ToolEntry<O>;
|
|
169
|
-
/**
|
|
170
|
-
* Helper to create a successful single-text-block tool result.
|
|
171
|
-
*
|
|
172
|
-
* @param text - The text to return to the client
|
|
173
|
-
* @returns A `ToolsCallResult` with the text content
|
|
174
|
-
*/
|
|
175
|
-
export declare const okResult: (text: string) => ToolsCallResult;
|
|
176
|
-
/**
|
|
177
|
-
* Helper to create a tool-level error result with plain text explanation.
|
|
178
|
-
*
|
|
179
|
-
* @param text - The error message to return to the client
|
|
180
|
-
* @returns A `ToolsCallResult` with `isError: true` and the text explanation
|
|
181
|
-
*/
|
|
182
|
-
export declare const errorResult: (text: string) => ToolsCallResult;
|
|
183
|
-
/**
|
|
184
|
-
* Builds `McpHandlers` from a registry of tool entries.
|
|
185
|
-
*
|
|
186
|
-
* This factory generates `toolsList` and `toolsCall` handlers that work with a
|
|
187
|
-
* declarative registry, eliminating boilerplate. The `toolsList` handler converts
|
|
188
|
-
* entries into MCP `Tool` descriptors, and `toolsCall` dispatches by name and
|
|
189
|
-
* delegates to the appropriate handler.
|
|
190
|
-
*
|
|
191
|
-
* @param registry - Array of tool entries
|
|
192
|
-
* @returns Complete `McpHandlers` ready for use with `mcpStep`
|
|
193
|
-
*/
|
|
194
|
-
export declare const fromRegistry: <O extends Operation>(registry: readonly ToolEntry<O>[]) => McpHandlers<O>;
|
|
195
|
-
/** Top-level handler: maps a raw JSON value to a JSON-RPC response (or `null` for notifications). */
|
|
196
|
-
export type Handle<O extends Operation> = (value: Unknown) => Effect<O, Response | null>;
|
|
197
|
-
/** MCP error -32002: the client called a method before `initialize`. */
|
|
198
|
-
export declare const notInitialized: import("../types/rtti/ts/module.f.ts").StructTs<{
|
|
199
|
-
readonly code: import("../types/rtti/module.f.ts").Number;
|
|
200
|
-
readonly message: import("../types/rtti/module.f.ts").String;
|
|
201
|
-
readonly data: import("../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
|
|
202
|
-
}>;
|
|
203
|
-
/** State carried before the peer sends `initialize`. */
|
|
204
|
-
export type Uninitialized = readonly ['uninitialized'];
|
|
205
|
-
/** State after `initialize` response was sent but before `notifications/initialized` arrives. */
|
|
206
|
-
export type Initializing = readonly ['initializing'];
|
|
207
|
-
/** State carried after a successful `initialize` exchange. */
|
|
208
|
-
export type InitializedState = true;
|
|
209
|
-
/** The three phases of an MCP session. */
|
|
210
|
-
export type McpSessionState = Uninitialized | Initializing | readonly ['initialized', InitializedState];
|
|
211
|
-
/** Initial session state — always start here. */
|
|
212
|
-
export declare const uninitializedState: McpSessionState;
|
|
213
|
-
/** Static configuration supplied by the server implementer. */
|
|
214
|
-
export type McpConfig = {
|
|
215
|
-
readonly serverInfo: Implementation;
|
|
216
|
-
readonly capabilities: ServerCapabilities;
|
|
217
|
-
readonly protocolVersion: string;
|
|
218
|
-
};
|
|
219
|
-
/**
|
|
220
|
-
* State-machine step for an MCP session using memory effects.
|
|
221
|
-
*
|
|
222
|
-
* Given configuration, handlers, and a memory key holding the session state,
|
|
223
|
-
* returns a function `(value) => Effect<MemOp | O, Response | null>`.
|
|
224
|
-
*
|
|
225
|
-
* Rules:
|
|
226
|
-
* - `ping` returns an empty success regardless of session state; non-object
|
|
227
|
-
* params → -32602.
|
|
228
|
-
* - `initialize` is accepted only while uninitialized; a second call returns -32600.
|
|
229
|
-
* On success the state moves to `initializing`, not `initialized`.
|
|
230
|
-
* - `notifications/initialized` (no `id`) transitions `initializing` → `initialized`;
|
|
231
|
-
* a malformed one (non-object params) is ignored and the session stays gated;
|
|
232
|
-
* other notifications are silently ignored in any state.
|
|
233
|
-
* - Any other method before `notifications/initialized` → error -32002 (not initialized).
|
|
234
|
-
* - Methods gated by a capability (e.g. `tools/list`) → -32601 when the capability
|
|
235
|
-
* is absent.
|
|
236
|
-
* - `tools/list` params (an optional pagination `cursor`) are validated and passed
|
|
237
|
-
* to the handler; invalid params → -32602.
|
|
238
|
-
*/
|
|
239
|
-
export declare const mcpStep: ({ protocolVersion, capabilities, serverInfo, }: McpConfig) => <O extends Operation>(handlers: McpHandlers<O>) => (stateKey: Key<McpSessionState>) => (value: Unknown) => Effect<MemOp | O, Response | null>;
|