@j0hanz/filesystem-mcp 2.7.5 → 2.8.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 +290 -394
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +15 -7
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts +1 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/concurrency.d.ts +7 -0
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +19 -0
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/errors.d.ts +4 -3
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +5 -16
- package/dist/core/errors.js.map +1 -1
- package/dist/core/fs.d.ts +19 -11
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +168 -53
- package/dist/core/fs.js.map +1 -1
- package/dist/core/glob.d.ts +8 -0
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +192 -29
- package/dist/core/glob.js.map +1 -1
- package/dist/core/input-required.d.ts +12 -0
- package/dist/core/input-required.d.ts.map +1 -1
- package/dist/core/input-required.js +7 -2
- package/dist/core/input-required.js.map +1 -1
- package/dist/core/observability.d.ts +2 -0
- package/dist/core/observability.d.ts.map +1 -1
- package/dist/core/observability.js +7 -0
- package/dist/core/observability.js.map +1 -1
- package/dist/core/path-completer.d.ts.map +1 -1
- package/dist/core/path-completer.js +11 -30
- package/dist/core/path-completer.js.map +1 -1
- package/dist/core/path.d.ts +7 -18
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +22 -55
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts +4 -1
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +25 -16
- package/dist/core/read.js.map +1 -1
- package/dist/core/schema.d.ts +6 -1
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +17 -3
- package/dist/core/schema.js.map +1 -1
- package/dist/core/search.d.ts +7 -0
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +154 -66
- package/dist/core/search.js.map +1 -1
- package/dist/core/store.d.ts +36 -1
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +71 -6
- package/dist/core/store.js.map +1 -1
- package/dist/core/util.d.ts +37 -0
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +62 -0
- package/dist/core/util.js.map +1 -1
- package/dist/index.js +5 -16
- package/dist/index.js.map +1 -1
- package/dist/resources.d.ts +5 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +45 -22
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts +10 -5
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -8
- package/dist/server.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +93 -90
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +10 -6
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +49 -16
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete.d.ts.map +1 -1
- package/dist/tools/delete.js +39 -21
- package/dist/tools/delete.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +37 -39
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +49 -60
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/index.d.ts +2 -10
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +59 -69
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +39 -5
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts.map +1 -1
- package/dist/tools/patch.js +88 -86
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +30 -24
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +60 -46
- package/dist/tools/replace-text.js.map +1 -1
- package/dist/tools/search-text.d.ts.map +1 -1
- package/dist/tools/search-text.js +48 -64
- package/dist/tools/search-text.js.map +1 -1
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +2 -1
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/shared.d.ts +7 -5
- package/dist/transport/shared.d.ts.map +1 -1
- package/dist/transport/shared.js +8 -2
- package/dist/transport/shared.js.map +1 -1
- package/dist/transport/stdio.d.ts +4 -1
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/transport/stdio.js +46 -12
- package/dist/transport/stdio.js.map +1 -1
- package/package.json +3 -2
package/dist/core/store.d.ts
CHANGED
|
@@ -9,9 +9,37 @@ interface ResourceEntry {
|
|
|
9
9
|
expiresAt: string;
|
|
10
10
|
text: string;
|
|
11
11
|
}
|
|
12
|
+
/** The `resources/read` result that serves `entry`, and what the store measures it by. */
|
|
13
|
+
export declare function entryReadResult(entry: ResourceEntry): {
|
|
14
|
+
contents: [{
|
|
15
|
+
uri: string;
|
|
16
|
+
mimeType: string;
|
|
17
|
+
text: string;
|
|
18
|
+
}];
|
|
19
|
+
};
|
|
20
|
+
interface ResourceStoreOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Refuse an entry whose `resources/read` result encodes past this many
|
|
23
|
+
* bytes. stdio sets it: the entry is re-encoded there as one JSON string,
|
|
24
|
+
* where escaping can outgrow the bytes stored, and a link the connection
|
|
25
|
+
* can never carry back is worth no more than an entry over the cap.
|
|
26
|
+
*/
|
|
27
|
+
readonly maxReadResultBytes?: number;
|
|
28
|
+
}
|
|
12
29
|
export declare class ResourceStore {
|
|
13
30
|
#private;
|
|
14
|
-
constructor(onListChanged?: () => void);
|
|
31
|
+
constructor(onListChanged?: () => void, options?: ResourceStoreOptions);
|
|
32
|
+
/**
|
|
33
|
+
* A view sharing this store's entries that also remembers the URIs put
|
|
34
|
+
* through it, so {@link discard} can take one tool call's entries back.
|
|
35
|
+
*/
|
|
36
|
+
forCall(): ResourceStore;
|
|
37
|
+
/**
|
|
38
|
+
* Drop what was put through this view and is still cached. The put already
|
|
39
|
+
* announced the list change (and may have pruned or evicted other entries),
|
|
40
|
+
* so taking the entry back is announced too.
|
|
41
|
+
*/
|
|
42
|
+
discard(): void;
|
|
15
43
|
getEntry(uri: string): ResourceEntry;
|
|
16
44
|
putText(params: {
|
|
17
45
|
name: string;
|
|
@@ -64,6 +92,13 @@ export declare function invalidCursor(): FsError;
|
|
|
64
92
|
*/
|
|
65
93
|
export declare class PageSnapshotStore {
|
|
66
94
|
#private;
|
|
95
|
+
/**
|
|
96
|
+
* A view sharing this store's snapshots that also remembers the ones
|
|
97
|
+
* created through it, so {@link discard} can take one tool call's back.
|
|
98
|
+
*/
|
|
99
|
+
forCall(): PageSnapshotStore;
|
|
100
|
+
/** Drop the snapshots created through this view. */
|
|
101
|
+
discard(): void;
|
|
67
102
|
create(params: {
|
|
68
103
|
queryKey: string;
|
|
69
104
|
items: readonly unknown[];
|
package/dist/core/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/core/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAIjE,OAAO,EAAa,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/core/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAIjE,OAAO,EAAa,OAAO,EAAE,MAAM,aAAa,CAAC;AAqFjD,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG;IACrD,QAAQ,EAAE,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D,CAEA;AAED,UAAU,oBAAoB;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,qBAAa,aAAa;;gBAYZ,aAAa,CAAC,EAAE,MAAM,IAAI,EAAE,OAAO,GAAE,oBAAyB;IAK1E;;;OAGG;IACH,OAAO,IAAI,aAAa;IAOxB;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAQf,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAYpC,OAAO,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa;IAkCjF;;;;OAIG;IACH,IAAI,IAAI,MAAM,EAAE;CAGjB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,IAAI,EAAE,YAAY,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EAIZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,OAAO,EAAE,GAClD,kBAAkB,CAwBpB;AAID,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAKvC;AAID;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB;;IAS5B;;;OAGG;IACH,OAAO,IAAI,iBAAiB;IAO5B,oDAAoD;IACpD,OAAO,IAAI,IAAI;IAIf,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;IAW3F,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC;IAMhF,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/core/store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { ErrorCode, FsError } from "./errors.js";
|
|
3
|
-
import { MIB } from "./util.js";
|
|
3
|
+
import { jsonBytesOver, MIB } from "./util.js";
|
|
4
4
|
// ─── TTL + LRU map ───────────────────────────────────────────────────────────
|
|
5
5
|
/**
|
|
6
6
|
* A bounded, expiring map. `Map` keeps insertion order and a read re-inserts,
|
|
@@ -55,12 +55,14 @@ class TtlLru {
|
|
|
55
55
|
this.delete(oldest.value);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
+
/** True when `key` was present. */
|
|
58
59
|
delete(key) {
|
|
59
60
|
const entry = this.#byKey.get(key);
|
|
60
61
|
if (!entry)
|
|
61
|
-
return;
|
|
62
|
+
return false;
|
|
62
63
|
this.#weight -= entry.weight;
|
|
63
64
|
this.#byKey.delete(key);
|
|
65
|
+
return true;
|
|
64
66
|
}
|
|
65
67
|
/** Live keys, least recently used first. Prunes expired entries first. */
|
|
66
68
|
keys() {
|
|
@@ -76,15 +78,48 @@ const MAX_ENTRIES = 64;
|
|
|
76
78
|
const MAX_TOTAL_BYTES = 25 * MIB;
|
|
77
79
|
const MAX_ENTRY_BYTES = 10 * MIB;
|
|
78
80
|
const ENTRY_TTL_MS = 60 * 1000;
|
|
81
|
+
/** The `resources/read` result that serves `entry`, and what the store measures it by. */
|
|
82
|
+
export function entryReadResult(entry) {
|
|
83
|
+
return { contents: [{ uri: entry.uri, mimeType: entry.mimeType, text: entry.text }] };
|
|
84
|
+
}
|
|
79
85
|
export class ResourceStore {
|
|
86
|
+
// Not readonly: a call view (`forCall`) shares its store's entries.
|
|
80
87
|
#entries = new TtlLru({
|
|
81
88
|
maxEntries: MAX_ENTRIES,
|
|
82
89
|
maxWeight: MAX_TOTAL_BYTES,
|
|
83
90
|
ttlMs: ENTRY_TTL_MS,
|
|
84
91
|
});
|
|
85
92
|
#onListChanged;
|
|
86
|
-
|
|
93
|
+
#options;
|
|
94
|
+
/** A call view's own puts; `undefined` on the store itself. */
|
|
95
|
+
#putUris;
|
|
96
|
+
constructor(onListChanged, options = {}) {
|
|
87
97
|
this.#onListChanged = onListChanged;
|
|
98
|
+
this.#options = options;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A view sharing this store's entries that also remembers the URIs put
|
|
102
|
+
* through it, so {@link discard} can take one tool call's entries back.
|
|
103
|
+
*/
|
|
104
|
+
forCall() {
|
|
105
|
+
const view = new ResourceStore(this.#onListChanged, this.#options);
|
|
106
|
+
view.#entries = this.#entries;
|
|
107
|
+
view.#putUris = [];
|
|
108
|
+
return view;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Drop what was put through this view and is still cached. The put already
|
|
112
|
+
* announced the list change (and may have pruned or evicted other entries),
|
|
113
|
+
* so taking the entry back is announced too.
|
|
114
|
+
*/
|
|
115
|
+
discard() {
|
|
116
|
+
let dropped = false;
|
|
117
|
+
for (const uri of this.#putUris?.splice(0) ?? []) {
|
|
118
|
+
if (this.#entries.delete(uri))
|
|
119
|
+
dropped = true;
|
|
120
|
+
}
|
|
121
|
+
if (dropped)
|
|
122
|
+
this.#onListChanged?.();
|
|
88
123
|
}
|
|
89
124
|
getEntry(uri) {
|
|
90
125
|
const entry = this.#entries.get(uri);
|
|
@@ -99,9 +134,7 @@ export class ResourceStore {
|
|
|
99
134
|
putText(params) {
|
|
100
135
|
const entryBytes = Buffer.byteLength(params.text, 'utf8');
|
|
101
136
|
if (entryBytes > MAX_ENTRY_BYTES) {
|
|
102
|
-
|
|
103
|
-
this.#onListChanged?.();
|
|
104
|
-
throw new FsError(ErrorCode.TOO_LARGE, `Resource too large to cache (${entryBytes} bytes).`);
|
|
137
|
+
throw this.#refuse(`Resource too large to cache (${entryBytes} bytes).`);
|
|
105
138
|
}
|
|
106
139
|
const now = Date.now();
|
|
107
140
|
const entry = {
|
|
@@ -113,10 +146,23 @@ export class ResourceStore {
|
|
|
113
146
|
expiresAt: new Date(now + ENTRY_TTL_MS).toISOString(),
|
|
114
147
|
text: params.text,
|
|
115
148
|
};
|
|
149
|
+
const { maxReadResultBytes } = this.#options;
|
|
150
|
+
const readBytes = maxReadResultBytes === undefined
|
|
151
|
+
? undefined
|
|
152
|
+
: jsonBytesOver(entryReadResult(entry), maxReadResultBytes);
|
|
153
|
+
if (readBytes !== undefined) {
|
|
154
|
+
throw this.#refuse(`Resource too large to read back (${readBytes} bytes once JSON-encoded).`);
|
|
155
|
+
}
|
|
116
156
|
this.#entries.set(entry.uri, entry, entryBytes);
|
|
157
|
+
this.#putUris?.push(entry.uri);
|
|
117
158
|
this.#onListChanged?.();
|
|
118
159
|
return entry;
|
|
119
160
|
}
|
|
161
|
+
#refuse(message) {
|
|
162
|
+
if (this.#entries.prune())
|
|
163
|
+
this.#onListChanged?.();
|
|
164
|
+
return new FsError(ErrorCode.TOO_LARGE, message);
|
|
165
|
+
}
|
|
120
166
|
/**
|
|
121
167
|
* Live URIs, oldest first. Deliberately silent about the prune it runs:
|
|
122
168
|
* this is the read path `resources/list` runs through, and the caller is
|
|
@@ -181,10 +227,28 @@ const MAX_SNAPSHOTS = 32;
|
|
|
181
227
|
* the way `ResourceStore` does.
|
|
182
228
|
*/
|
|
183
229
|
export class PageSnapshotStore {
|
|
230
|
+
// Not readonly: a call view (`forCall`) shares its store's snapshots.
|
|
184
231
|
#byId = new TtlLru({
|
|
185
232
|
maxEntries: MAX_SNAPSHOTS,
|
|
186
233
|
ttlMs: ENTRY_TTL_MS,
|
|
187
234
|
});
|
|
235
|
+
/** A call view's own snapshots; `undefined` on the store itself. */
|
|
236
|
+
#createdIds;
|
|
237
|
+
/**
|
|
238
|
+
* A view sharing this store's snapshots that also remembers the ones
|
|
239
|
+
* created through it, so {@link discard} can take one tool call's back.
|
|
240
|
+
*/
|
|
241
|
+
forCall() {
|
|
242
|
+
const view = new PageSnapshotStore();
|
|
243
|
+
view.#byId = this.#byId;
|
|
244
|
+
view.#createdIds = [];
|
|
245
|
+
return view;
|
|
246
|
+
}
|
|
247
|
+
/** Drop the snapshots created through this view. */
|
|
248
|
+
discard() {
|
|
249
|
+
for (const id of this.#createdIds?.splice(0) ?? [])
|
|
250
|
+
this.#byId.delete(id);
|
|
251
|
+
}
|
|
188
252
|
create(params) {
|
|
189
253
|
const snapshotId = randomUUID();
|
|
190
254
|
this.#byId.set(snapshotId, {
|
|
@@ -192,6 +256,7 @@ export class PageSnapshotStore {
|
|
|
192
256
|
items: params.items,
|
|
193
257
|
metadata: params.metadata,
|
|
194
258
|
});
|
|
259
|
+
this.#createdIds?.push(snapshotId);
|
|
195
260
|
return snapshotId;
|
|
196
261
|
}
|
|
197
262
|
read(snapshotId, queryKey) {
|
package/dist/core/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/core/store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,MAAM;IACD,MAAM,GAAG,IAAI,GAAG,EAA2D,CAAC;IAC5E,WAAW,CAAS;IACpB,UAAU,CAAS;IACnB,MAAM,CAAS;IACxB,OAAO,GAAG,CAAC,CAAC;IAEZ,YAAY,IAA+D;QACzE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,iBAAiB,CAAC;QAC7D,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;IAC3B,CAAC;IAED,6DAA6D;IAC7D,KAAK;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QAChC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACvC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG;gBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC;IACrC,CAAC;IAED,yFAAyF;IACzF,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,4FAA4F;IAC5F,GAAG,CAAC,GAAW,EAAE,KAAQ,EAAE,MAAM,GAAG,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC;QACvB,OACE,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;YACpB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,EACvE,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACzC,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM;YACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,0EAA0E;IAC1E,IAAI;QACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;IACnB,CAAC;CACF;AAcD,MAAM,WAAW,GAAG,EAAE,CAAC;AACvB,MAAM,eAAe,GAAG,EAAE,GAAG,GAAG,CAAC;AACjC,MAAM,eAAe,GAAG,EAAE,GAAG,GAAG,CAAC;AACjC,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC;AAE/B,MAAM,OAAO,aAAa;IACf,QAAQ,GAAG,IAAI,MAAM,CAAgB;QAC5C,UAAU,EAAE,WAAW;QACvB,SAAS,EAAE,eAAe;QAC1B,KAAK,EAAE,YAAY;KACpB,CAAC,CAAC;IACM,cAAc,CAA2B;IAElD,YAAY,aAA0B;QACpC,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;IACtC,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,yEAAyE;QACzE,uDAAuD;QACvD,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;YAAE,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;QACnD,MAAM,IAAI,OAAO,CACf,SAAS,CAAC,SAAS,EACnB,uBAAuB,GAAG,kCAAkC,CAC7D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,MAAyD;QAC/D,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1D,IAAI,UAAU,GAAG,eAAe,EAAE,CAAC;YACjC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;gBAAE,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YACnD,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,gCAAgC,UAAU,UAAU,CAAC,CAAC;QAC/F,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAkB;YAC3B,GAAG,EAAE,2BAA2B,UAAU,EAAE,EAAE;YAC9C,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,YAAY;YACzC,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;YACtC,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,GAAG,YAAY,CAAC,CAAC,WAAW,EAAE;YACrD,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;CACF;AAYD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,IAAY;AACZ,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,KAAmD;IAEnD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC;QAC1B,IAAI;QACJ,QAAQ,EAAE,kBAAkB;QAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;KACrC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE;YACL,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,eAAe;YACrB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,+BAA+B,KAAK,CAAC,SAAS,EAAE;YAC7D,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,SAAS,EAAE;SACnE;KACF,CAAC;AACJ,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,OAAO,CAChB,SAAS,CAAC,aAAa,EACvB,0DAA0D,CAC3D,CAAC;AACJ,CAAC;AAED,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;;;;;;GAUG;AACH,MAAM,OAAO,iBAAiB;IACnB,KAAK,GAAG,IAAI,MAAM,CAAsC;QAC/D,UAAU,EAAE,aAAa;QACzB,KAAK,EAAE,YAAY;KACpB,CAAC,CAAC;IAEH,MAAM,CAAC,MAA2E;QAChF,MAAM,UAAU,GAAG,UAAU,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;SAC1B,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,IAAI,CAAmB,UAAkB,EAAE,QAAgB;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,KAAK,EAAE,QAAQ,KAAK,QAAQ;YAAE,MAAM,aAAa,EAAE,CAAC;QACxD,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAqB,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAa,EAAE,CAAC;IAC/E,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { randomUUID } from 'node:crypto';\n\nimport { ErrorCode, FsError } from './errors.ts';\nimport { MIB } from './util.ts';\n\n// ─── TTL + LRU map ───────────────────────────────────────────────────────────\n\n/**\n * A bounded, expiring map. `Map` keeps insertion order and a read re-inserts,\n * so the first key is always the least recently used one. Entries carry an\n * optional weight so a byte budget can be enforced next to the entry cap.\n */\nclass TtlLru<V> {\n readonly #byKey = new Map<string, { value: V; expiresAt: number; weight: number }>();\n readonly #maxEntries: number;\n readonly #maxWeight: number;\n readonly #ttlMs: number;\n #weight = 0;\n\n constructor(opts: { maxEntries: number; ttlMs: number; maxWeight?: number }) {\n this.#maxEntries = opts.maxEntries;\n this.#maxWeight = opts.maxWeight ?? Number.POSITIVE_INFINITY;\n this.#ttlMs = opts.ttlMs;\n }\n\n /** Drop every expired entry; true when at least one went. */\n prune(): boolean {\n const now = Date.now();\n const before = this.#byKey.size;\n for (const [key, entry] of this.#byKey) {\n if (entry.expiresAt <= now) this.delete(key);\n }\n return this.#byKey.size !== before;\n }\n\n /** The live value, bumped to most recently used; `undefined` when missing or expired. */\n get(key: string): V | undefined {\n const entry = this.#byKey.get(key);\n if (!entry) return undefined;\n if (entry.expiresAt <= Date.now()) {\n this.delete(key);\n return undefined;\n }\n this.#byKey.delete(key);\n this.#byKey.set(key, entry);\n return entry.value;\n }\n\n /** Insert (pruning expired first) and evict least recently used entries past either cap. */\n set(key: string, value: V, weight = 0): void {\n this.prune();\n this.delete(key);\n this.#byKey.set(key, { value, expiresAt: Date.now() + this.#ttlMs, weight });\n this.#weight += weight;\n while (\n this.#byKey.size > 0 &&\n (this.#byKey.size > this.#maxEntries || this.#weight > this.#maxWeight)\n ) {\n const oldest = this.#byKey.keys().next();\n if (oldest.done) break;\n this.delete(oldest.value);\n }\n }\n\n delete(key: string): void {\n const entry = this.#byKey.get(key);\n if (!entry) return;\n this.#weight -= entry.weight;\n this.#byKey.delete(key);\n }\n\n /** Live keys, least recently used first. Prunes expired entries first. */\n keys(): string[] {\n this.prune();\n return [...this.#byKey.keys()];\n }\n\n clear(): void {\n this.#byKey.clear();\n this.#weight = 0;\n }\n}\n\n// ─── Resource store: externalized tool results ───────────────────────────────\n\ninterface ResourceEntry {\n uri: string;\n name: string;\n mimeType: string;\n size: number;\n createdAt: string;\n expiresAt: string;\n text: string;\n}\n\nconst MAX_ENTRIES = 64;\nconst MAX_TOTAL_BYTES = 25 * MIB;\nconst MAX_ENTRY_BYTES = 10 * MIB;\nconst ENTRY_TTL_MS = 60 * 1000;\n\nexport class ResourceStore {\n readonly #entries = new TtlLru<ResourceEntry>({\n maxEntries: MAX_ENTRIES,\n maxWeight: MAX_TOTAL_BYTES,\n ttlMs: ENTRY_TTL_MS,\n });\n readonly #onListChanged: (() => void) | undefined;\n\n constructor(onListChanged?: () => void) {\n this.#onListChanged = onListChanged;\n }\n\n getEntry(uri: string): ResourceEntry {\n const entry = this.#entries.get(uri);\n if (entry) return entry;\n // Not found and expired share a remedy; only the wording differs, and an\n // expiry seen here changed the list a client may hold.\n if (this.#entries.prune()) this.#onListChanged?.();\n throw new FsError(\n ErrorCode.NOT_FOUND,\n `Resource not found: ${uri}. Re-run the tool to regenerate.`,\n );\n }\n\n putText(params: { name: string; mimeType?: string; text: string }): ResourceEntry {\n const entryBytes = Buffer.byteLength(params.text, 'utf8');\n if (entryBytes > MAX_ENTRY_BYTES) {\n if (this.#entries.prune()) this.#onListChanged?.();\n throw new FsError(ErrorCode.TOO_LARGE, `Resource too large to cache (${entryBytes} bytes).`);\n }\n const now = Date.now();\n const entry: ResourceEntry = {\n uri: `filesystem-mcp://result/${randomUUID()}`,\n name: params.name,\n mimeType: params.mimeType ?? 'text/plain',\n size: entryBytes,\n createdAt: new Date(now).toISOString(),\n expiresAt: new Date(now + ENTRY_TTL_MS).toISOString(),\n text: params.text,\n };\n this.#entries.set(entry.uri, entry, entryBytes);\n this.#onListChanged?.();\n return entry;\n }\n\n /**\n * Live URIs, oldest first. Deliberately silent about the prune it runs:\n * this is the read path `resources/list` runs through, and the caller is\n * already receiving the post-prune list.\n */\n keys(): string[] {\n return this.#entries.keys();\n }\n}\n\nexport interface JsonResourceResult {\n entry: {\n uri: string;\n size: number;\n mimeType: string;\n expiresAt: string;\n };\n link: ContentBlock;\n}\n\n/**\n * Owner of the externalize-a-payload rule: a tool whose inline response is\n * truncated publishes the full value to the resource store as pretty-printed\n * JSON and hands back the URI to reach it by, plus the link block that offers\n * it to the user.\n */\nexport function putJsonResource(\n store: ResourceStore,\n name: string,\n // `Record<string, unknown> | readonly unknown[]`, not `unknown` or `object`:\n // JSON.stringify returns undefined for undefined and for functions, and `null`\n // is excluded so non-string payloads cannot enter the store's text entry.\n value: Record<string, unknown> | readonly unknown[],\n): JsonResourceResult {\n const entry = store.putText({\n name,\n mimeType: 'application/json',\n text: JSON.stringify(value, null, 2),\n });\n\n return {\n entry: {\n uri: entry.uri,\n size: entry.size,\n mimeType: entry.mimeType,\n expiresAt: entry.expiresAt,\n },\n link: {\n type: 'resource_link',\n uri: entry.uri,\n name: entry.name,\n description: `Cached tool result; expires ${entry.expiresAt}`,\n mimeType: entry.mimeType,\n size: entry.size,\n annotations: { audience: ['user'], lastModified: entry.createdAt },\n },\n };\n}\n\n// ─── Page snapshots: paginated result sets ───────────────────────────────────\n\nexport interface PageSnapshot<T = unknown, M = unknown> {\n readonly items: readonly T[];\n readonly metadata: M;\n}\n\n/**\n * The one cursor rejection: a cursor whose snapshot expired, was evicted, was\n * already consumed past its end, or belongs to a different query. Every case\n * has the same remedy, so they share one message.\n */\nexport function invalidCursor(): FsError {\n return new FsError(\n ErrorCode.INVALID_INPUT,\n 'Invalid cursor. Request the first page without a cursor.',\n );\n}\n\nconst MAX_SNAPSHOTS = 32;\n\n/**\n * Short-lived snapshots of a completed query's full result set, so later pages\n * slice a stored array instead of re-scanning and re-sorting the filesystem.\n *\n * ponytail: bounded by snapshot count and TTL, not by bytes — MAX_SNAPSHOTS x\n * 20,000 `list` entries or MAX_SNAPSHOTS x 10,000 `search_text` matches, each\n * match retaining up to 20 more line strings at `context: 10`, order of a few\n * hundred MB held for 60s, and the HTTP leg shares one store so any caller can\n * drive it. If that shows up as memory pressure, pass a byte weight to `set`\n * the way `ResourceStore` does.\n */\nexport class PageSnapshotStore {\n readonly #byId = new TtlLru<PageSnapshot & { queryKey: string }>({\n maxEntries: MAX_SNAPSHOTS,\n ttlMs: ENTRY_TTL_MS,\n });\n\n create(params: { queryKey: string; items: readonly unknown[]; metadata?: unknown }): string {\n const snapshotId = randomUUID();\n this.#byId.set(snapshotId, {\n queryKey: params.queryKey,\n items: params.items,\n metadata: params.metadata,\n });\n return snapshotId;\n }\n\n read<T, M = undefined>(snapshotId: string, queryKey: string): PageSnapshot<T, M> {\n const entry = this.#byId.get(snapshotId);\n if (entry?.queryKey !== queryKey) throw invalidCursor();\n return { items: entry.items as readonly T[], metadata: entry.metadata as M };\n }\n\n clear(): void {\n this.#byId.clear();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/core/store.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { randomUUID } from 'node:crypto';\n\nimport { ErrorCode, FsError } from './errors.ts';\nimport { jsonBytesOver, MIB } from './util.ts';\n\n// ─── TTL + LRU map ───────────────────────────────────────────────────────────\n\n/**\n * A bounded, expiring map. `Map` keeps insertion order and a read re-inserts,\n * so the first key is always the least recently used one. Entries carry an\n * optional weight so a byte budget can be enforced next to the entry cap.\n */\nclass TtlLru<V> {\n readonly #byKey = new Map<string, { value: V; expiresAt: number; weight: number }>();\n readonly #maxEntries: number;\n readonly #maxWeight: number;\n readonly #ttlMs: number;\n #weight = 0;\n\n constructor(opts: { maxEntries: number; ttlMs: number; maxWeight?: number }) {\n this.#maxEntries = opts.maxEntries;\n this.#maxWeight = opts.maxWeight ?? Number.POSITIVE_INFINITY;\n this.#ttlMs = opts.ttlMs;\n }\n\n /** Drop every expired entry; true when at least one went. */\n prune(): boolean {\n const now = Date.now();\n const before = this.#byKey.size;\n for (const [key, entry] of this.#byKey) {\n if (entry.expiresAt <= now) this.delete(key);\n }\n return this.#byKey.size !== before;\n }\n\n /** The live value, bumped to most recently used; `undefined` when missing or expired. */\n get(key: string): V | undefined {\n const entry = this.#byKey.get(key);\n if (!entry) return undefined;\n if (entry.expiresAt <= Date.now()) {\n this.delete(key);\n return undefined;\n }\n this.#byKey.delete(key);\n this.#byKey.set(key, entry);\n return entry.value;\n }\n\n /** Insert (pruning expired first) and evict least recently used entries past either cap. */\n set(key: string, value: V, weight = 0): void {\n this.prune();\n this.delete(key);\n this.#byKey.set(key, { value, expiresAt: Date.now() + this.#ttlMs, weight });\n this.#weight += weight;\n while (\n this.#byKey.size > 0 &&\n (this.#byKey.size > this.#maxEntries || this.#weight > this.#maxWeight)\n ) {\n const oldest = this.#byKey.keys().next();\n if (oldest.done) break;\n this.delete(oldest.value);\n }\n }\n\n /** True when `key` was present. */\n delete(key: string): boolean {\n const entry = this.#byKey.get(key);\n if (!entry) return false;\n this.#weight -= entry.weight;\n this.#byKey.delete(key);\n return true;\n }\n\n /** Live keys, least recently used first. Prunes expired entries first. */\n keys(): string[] {\n this.prune();\n return [...this.#byKey.keys()];\n }\n\n clear(): void {\n this.#byKey.clear();\n this.#weight = 0;\n }\n}\n\n// ─── Resource store: externalized tool results ───────────────────────────────\n\ninterface ResourceEntry {\n uri: string;\n name: string;\n mimeType: string;\n size: number;\n createdAt: string;\n expiresAt: string;\n text: string;\n}\n\nconst MAX_ENTRIES = 64;\nconst MAX_TOTAL_BYTES = 25 * MIB;\nconst MAX_ENTRY_BYTES = 10 * MIB;\nconst ENTRY_TTL_MS = 60 * 1000;\n\n/** The `resources/read` result that serves `entry`, and what the store measures it by. */\nexport function entryReadResult(entry: ResourceEntry): {\n contents: [{ uri: string; mimeType: string; text: string }];\n} {\n return { contents: [{ uri: entry.uri, mimeType: entry.mimeType, text: entry.text }] };\n}\n\ninterface ResourceStoreOptions {\n /**\n * Refuse an entry whose `resources/read` result encodes past this many\n * bytes. stdio sets it: the entry is re-encoded there as one JSON string,\n * where escaping can outgrow the bytes stored, and a link the connection\n * can never carry back is worth no more than an entry over the cap.\n */\n readonly maxReadResultBytes?: number;\n}\n\nexport class ResourceStore {\n // Not readonly: a call view (`forCall`) shares its store's entries.\n #entries = new TtlLru<ResourceEntry>({\n maxEntries: MAX_ENTRIES,\n maxWeight: MAX_TOTAL_BYTES,\n ttlMs: ENTRY_TTL_MS,\n });\n readonly #onListChanged: (() => void) | undefined;\n readonly #options: ResourceStoreOptions;\n /** A call view's own puts; `undefined` on the store itself. */\n #putUris: string[] | undefined;\n\n constructor(onListChanged?: () => void, options: ResourceStoreOptions = {}) {\n this.#onListChanged = onListChanged;\n this.#options = options;\n }\n\n /**\n * A view sharing this store's entries that also remembers the URIs put\n * through it, so {@link discard} can take one tool call's entries back.\n */\n forCall(): ResourceStore {\n const view = new ResourceStore(this.#onListChanged, this.#options);\n view.#entries = this.#entries;\n view.#putUris = [];\n return view;\n }\n\n /**\n * Drop what was put through this view and is still cached. The put already\n * announced the list change (and may have pruned or evicted other entries),\n * so taking the entry back is announced too.\n */\n discard(): void {\n let dropped = false;\n for (const uri of this.#putUris?.splice(0) ?? []) {\n if (this.#entries.delete(uri)) dropped = true;\n }\n if (dropped) this.#onListChanged?.();\n }\n\n getEntry(uri: string): ResourceEntry {\n const entry = this.#entries.get(uri);\n if (entry) return entry;\n // Not found and expired share a remedy; only the wording differs, and an\n // expiry seen here changed the list a client may hold.\n if (this.#entries.prune()) this.#onListChanged?.();\n throw new FsError(\n ErrorCode.NOT_FOUND,\n `Resource not found: ${uri}. Re-run the tool to regenerate.`,\n );\n }\n\n putText(params: { name: string; mimeType?: string; text: string }): ResourceEntry {\n const entryBytes = Buffer.byteLength(params.text, 'utf8');\n if (entryBytes > MAX_ENTRY_BYTES) {\n throw this.#refuse(`Resource too large to cache (${entryBytes} bytes).`);\n }\n const now = Date.now();\n const entry: ResourceEntry = {\n uri: `filesystem-mcp://result/${randomUUID()}`,\n name: params.name,\n mimeType: params.mimeType ?? 'text/plain',\n size: entryBytes,\n createdAt: new Date(now).toISOString(),\n expiresAt: new Date(now + ENTRY_TTL_MS).toISOString(),\n text: params.text,\n };\n const { maxReadResultBytes } = this.#options;\n const readBytes =\n maxReadResultBytes === undefined\n ? undefined\n : jsonBytesOver(entryReadResult(entry), maxReadResultBytes);\n if (readBytes !== undefined) {\n throw this.#refuse(`Resource too large to read back (${readBytes} bytes once JSON-encoded).`);\n }\n this.#entries.set(entry.uri, entry, entryBytes);\n this.#putUris?.push(entry.uri);\n this.#onListChanged?.();\n return entry;\n }\n\n #refuse(message: string): FsError {\n if (this.#entries.prune()) this.#onListChanged?.();\n return new FsError(ErrorCode.TOO_LARGE, message);\n }\n\n /**\n * Live URIs, oldest first. Deliberately silent about the prune it runs:\n * this is the read path `resources/list` runs through, and the caller is\n * already receiving the post-prune list.\n */\n keys(): string[] {\n return this.#entries.keys();\n }\n}\n\nexport interface JsonResourceResult {\n entry: {\n uri: string;\n size: number;\n mimeType: string;\n expiresAt: string;\n };\n link: ContentBlock;\n}\n\n/**\n * Owner of the externalize-a-payload rule: a tool whose inline response is\n * truncated publishes the full value to the resource store as pretty-printed\n * JSON and hands back the URI to reach it by, plus the link block that offers\n * it to the user.\n */\nexport function putJsonResource(\n store: ResourceStore,\n name: string,\n // `Record<string, unknown> | readonly unknown[]`, not `unknown` or `object`:\n // JSON.stringify returns undefined for undefined and for functions, and `null`\n // is excluded so non-string payloads cannot enter the store's text entry.\n value: Record<string, unknown> | readonly unknown[],\n): JsonResourceResult {\n const entry = store.putText({\n name,\n mimeType: 'application/json',\n text: JSON.stringify(value, null, 2),\n });\n\n return {\n entry: {\n uri: entry.uri,\n size: entry.size,\n mimeType: entry.mimeType,\n expiresAt: entry.expiresAt,\n },\n link: {\n type: 'resource_link',\n uri: entry.uri,\n name: entry.name,\n description: `Cached tool result; expires ${entry.expiresAt}`,\n mimeType: entry.mimeType,\n size: entry.size,\n annotations: { audience: ['user'], lastModified: entry.createdAt },\n },\n };\n}\n\n// ─── Page snapshots: paginated result sets ───────────────────────────────────\n\nexport interface PageSnapshot<T = unknown, M = unknown> {\n readonly items: readonly T[];\n readonly metadata: M;\n}\n\n/**\n * The one cursor rejection: a cursor whose snapshot expired, was evicted, was\n * already consumed past its end, or belongs to a different query. Every case\n * has the same remedy, so they share one message.\n */\nexport function invalidCursor(): FsError {\n return new FsError(\n ErrorCode.INVALID_INPUT,\n 'Invalid cursor. Request the first page without a cursor.',\n );\n}\n\nconst MAX_SNAPSHOTS = 32;\n\n/**\n * Short-lived snapshots of a completed query's full result set, so later pages\n * slice a stored array instead of re-scanning and re-sorting the filesystem.\n *\n * ponytail: bounded by snapshot count and TTL, not by bytes — MAX_SNAPSHOTS x\n * 20,000 `list` entries or MAX_SNAPSHOTS x 10,000 `search_text` matches, each\n * match retaining up to 20 more line strings at `context: 10`, order of a few\n * hundred MB held for 60s, and the HTTP leg shares one store so any caller can\n * drive it. If that shows up as memory pressure, pass a byte weight to `set`\n * the way `ResourceStore` does.\n */\nexport class PageSnapshotStore {\n // Not readonly: a call view (`forCall`) shares its store's snapshots.\n #byId = new TtlLru<PageSnapshot & { queryKey: string }>({\n maxEntries: MAX_SNAPSHOTS,\n ttlMs: ENTRY_TTL_MS,\n });\n /** A call view's own snapshots; `undefined` on the store itself. */\n #createdIds: string[] | undefined;\n\n /**\n * A view sharing this store's snapshots that also remembers the ones\n * created through it, so {@link discard} can take one tool call's back.\n */\n forCall(): PageSnapshotStore {\n const view = new PageSnapshotStore();\n view.#byId = this.#byId;\n view.#createdIds = [];\n return view;\n }\n\n /** Drop the snapshots created through this view. */\n discard(): void {\n for (const id of this.#createdIds?.splice(0) ?? []) this.#byId.delete(id);\n }\n\n create(params: { queryKey: string; items: readonly unknown[]; metadata?: unknown }): string {\n const snapshotId = randomUUID();\n this.#byId.set(snapshotId, {\n queryKey: params.queryKey,\n items: params.items,\n metadata: params.metadata,\n });\n this.#createdIds?.push(snapshotId);\n return snapshotId;\n }\n\n read<T, M = undefined>(snapshotId: string, queryKey: string): PageSnapshot<T, M> {\n const entry = this.#byId.get(snapshotId);\n if (entry?.queryKey !== queryKey) throw invalidCursor();\n return { items: entry.items as readonly T[], metadata: entry.metadata as M };\n }\n\n clear(): void {\n this.#byId.clear();\n }\n}\n"]}
|
package/dist/core/util.d.ts
CHANGED
|
@@ -36,6 +36,43 @@ export declare function getMaxTextFileSize(): number;
|
|
|
36
36
|
export declare function getMaxInboundMessageBytes(): number;
|
|
37
37
|
/** Combined byte budget for one batched `read`. */
|
|
38
38
|
export declare const READ_MANY_MAX_TOTAL_BYTES: number;
|
|
39
|
+
/**
|
|
40
|
+
* Ceiling on JSON-encoded tool and resource results over stdio. The SDK's
|
|
41
|
+
* fixed 10 MiB ReadBuffer applies regardless of `--max-file-size`; escaping,
|
|
42
|
+
* duplicated metadata, and base64 can overflow it even when the source fits.
|
|
43
|
+
* A tool call that changed nothing (read-only, or a dry run) and a resource
|
|
44
|
+
* read return TOO_LARGE instead, and the stdio result store refuses an entry
|
|
45
|
+
* whose read would overflow it; HTTP is unaffected. ~256 KiB covers the
|
|
46
|
+
* JSON-RPC envelope this measure omits, including the bounded request ID.
|
|
47
|
+
* Mutating tools need pre-commit safeguards rather than a post-commit output
|
|
48
|
+
* refusal.
|
|
49
|
+
*/
|
|
50
|
+
export declare const MAX_OUTBOUND_RESULT_BYTES: number;
|
|
51
|
+
/**
|
|
52
|
+
* JSON-encoded stdio request IDs and string progress tokens, including
|
|
53
|
+
* quotes/escapes, must fit the envelope reserve: each is echoed into frames.
|
|
54
|
+
*/
|
|
55
|
+
export declare const MAX_STDIO_REQUEST_ID_BYTES = 1024;
|
|
56
|
+
/**
|
|
57
|
+
* An upper bound on `Buffer.byteLength(JSON.stringify(value))` for the plain
|
|
58
|
+
* JSON data tool and resource results are made of, from a walk that reads
|
|
59
|
+
* string lengths but never encodes a string. JSON spends at most six bytes on
|
|
60
|
+
* one UTF-16 code unit (`\uXXXX` for a control character or a lone surrogate;
|
|
61
|
+
* any other BMP character is at most three UTF-8 bytes, a surrogate pair four
|
|
62
|
+
* for its two units), so a string or key costs at most 6 × length + 2 quotes.
|
|
63
|
+
* A finite number costs its `String()` form, which is what JSON writes, and a
|
|
64
|
+
* non-finite one `null`; `true`, `false` and `null` at most five. Each array
|
|
65
|
+
* element and object member adds one separator, and a member one colon, on top
|
|
66
|
+
* of the two brackets. A member JSON would omit (undefined, a function) only
|
|
67
|
+
* overcounts. `toJSON` is followed the way JSON.stringify follows it.
|
|
68
|
+
*/
|
|
69
|
+
export declare function jsonByteBound(value: unknown): number;
|
|
70
|
+
/**
|
|
71
|
+
* `value`'s JSON-encoded size in bytes when it is over `limit`, else
|
|
72
|
+
* `undefined`. {@link jsonByteBound} clears most values without encoding them;
|
|
73
|
+
* only one it cannot clear pays for the exact `JSON.stringify` measure.
|
|
74
|
+
*/
|
|
75
|
+
export declare function jsonBytesOver(value: unknown, limit: number): number | undefined;
|
|
39
76
|
/** Default line chunk size for read continuation when no explicit range was given. */
|
|
40
77
|
export declare const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;
|
|
41
78
|
export declare const DEFAULT_SEARCH_TIMEOUT_MS = 5000;
|
package/dist/core/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,GAAG,QAAa,CAAC;AAE9B,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAER;AA6BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAKhE;AA6BD,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS1D;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MACgD,CAAC;AAE/F,eAAO,MAAM,oBAAoB,QAAoD,CAAC;AAEtF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AACD,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,QAAY,CAAC;AAEnD,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC"}
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,GAAG,QAAa,CAAC;AAE9B,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAER;AA6BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAKhE;AA6BD,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS1D;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MACgD,CAAC;AAE/F,eAAO,MAAM,oBAAoB,QAAoD,CAAC;AAEtF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AACD,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,QAAY,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,QAAuB,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,0BAA0B,OAAM,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAgBpD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI/E;AAED,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC"}
|
package/dist/core/util.js
CHANGED
|
@@ -104,6 +104,68 @@ export function getMaxInboundMessageBytes() {
|
|
|
104
104
|
}
|
|
105
105
|
/** Combined byte budget for one batched `read`. */
|
|
106
106
|
export const READ_MANY_MAX_TOTAL_BYTES = 512 * KIB;
|
|
107
|
+
/**
|
|
108
|
+
* Ceiling on JSON-encoded tool and resource results over stdio. The SDK's
|
|
109
|
+
* fixed 10 MiB ReadBuffer applies regardless of `--max-file-size`; escaping,
|
|
110
|
+
* duplicated metadata, and base64 can overflow it even when the source fits.
|
|
111
|
+
* A tool call that changed nothing (read-only, or a dry run) and a resource
|
|
112
|
+
* read return TOO_LARGE instead, and the stdio result store refuses an entry
|
|
113
|
+
* whose read would overflow it; HTTP is unaffected. ~256 KiB covers the
|
|
114
|
+
* JSON-RPC envelope this measure omits, including the bounded request ID.
|
|
115
|
+
* Mutating tools need pre-commit safeguards rather than a post-commit output
|
|
116
|
+
* refusal.
|
|
117
|
+
*/
|
|
118
|
+
export const MAX_OUTBOUND_RESULT_BYTES = 10 * MIB - 256 * KIB;
|
|
119
|
+
/**
|
|
120
|
+
* JSON-encoded stdio request IDs and string progress tokens, including
|
|
121
|
+
* quotes/escapes, must fit the envelope reserve: each is echoed into frames.
|
|
122
|
+
*/
|
|
123
|
+
export const MAX_STDIO_REQUEST_ID_BYTES = KIB;
|
|
124
|
+
/**
|
|
125
|
+
* An upper bound on `Buffer.byteLength(JSON.stringify(value))` for the plain
|
|
126
|
+
* JSON data tool and resource results are made of, from a walk that reads
|
|
127
|
+
* string lengths but never encodes a string. JSON spends at most six bytes on
|
|
128
|
+
* one UTF-16 code unit (`\uXXXX` for a control character or a lone surrogate;
|
|
129
|
+
* any other BMP character is at most three UTF-8 bytes, a surrogate pair four
|
|
130
|
+
* for its two units), so a string or key costs at most 6 × length + 2 quotes.
|
|
131
|
+
* A finite number costs its `String()` form, which is what JSON writes, and a
|
|
132
|
+
* non-finite one `null`; `true`, `false` and `null` at most five. Each array
|
|
133
|
+
* element and object member adds one separator, and a member one colon, on top
|
|
134
|
+
* of the two brackets. A member JSON would omit (undefined, a function) only
|
|
135
|
+
* overcounts. `toJSON` is followed the way JSON.stringify follows it.
|
|
136
|
+
*/
|
|
137
|
+
export function jsonByteBound(value) {
|
|
138
|
+
if (typeof value === 'string')
|
|
139
|
+
return 6 * value.length + 2;
|
|
140
|
+
if (typeof value === 'number')
|
|
141
|
+
return Number.isFinite(value) ? String(value).length : 4;
|
|
142
|
+
if (typeof value !== 'object' || value === null)
|
|
143
|
+
return 5;
|
|
144
|
+
if (typeof value.toJSON === 'function') {
|
|
145
|
+
return jsonByteBound(value.toJSON());
|
|
146
|
+
}
|
|
147
|
+
let bytes = 2;
|
|
148
|
+
if (Array.isArray(value)) {
|
|
149
|
+
for (const item of value)
|
|
150
|
+
bytes += jsonByteBound(item) + 1;
|
|
151
|
+
return bytes;
|
|
152
|
+
}
|
|
153
|
+
for (const [key, item] of Object.entries(value)) {
|
|
154
|
+
bytes += 6 * key.length + 2 + 1 + jsonByteBound(item) + 1;
|
|
155
|
+
}
|
|
156
|
+
return bytes;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* `value`'s JSON-encoded size in bytes when it is over `limit`, else
|
|
160
|
+
* `undefined`. {@link jsonByteBound} clears most values without encoding them;
|
|
161
|
+
* only one it cannot clear pays for the exact `JSON.stringify` measure.
|
|
162
|
+
*/
|
|
163
|
+
export function jsonBytesOver(value, limit) {
|
|
164
|
+
if (jsonByteBound(value) <= limit)
|
|
165
|
+
return undefined;
|
|
166
|
+
const bytes = Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
167
|
+
return bytes > limit ? bytes : undefined;
|
|
168
|
+
}
|
|
107
169
|
/** Default line chunk size for read continuation when no explicit range was given. */
|
|
108
170
|
export const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;
|
|
109
171
|
export const DEFAULT_SEARCH_TIMEOUT_MS = 5000;
|
package/dist/core/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"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","sourcesContent":["import { availableParallelism } from 'node:os';\n\nimport { cli } from './config.ts';\nimport { warnInvalidSetting } from './path-utils.ts';\n\nconst KIB = 1024;\nexport const MIB = 1024 * KIB;\n\nexport function parseEnvInt(\n envVar: string,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n return parseIntSetting(envVar, process.env[envVar], defaultValue, min, max);\n}\n\n/** Validate a raw integer setting (env var or CLI override) with a logged fallback. */\nfunction parseIntSetting(\n name: string,\n value: string | undefined,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n if (!value) {\n return defaultValue;\n }\n\n const trimmed = value.trim();\n const parsed = Number(trimmed);\n if (\n trimmed === '' ||\n Number.isNaN(parsed) ||\n !Number.isInteger(parsed) ||\n parsed < min ||\n parsed > max\n ) {\n warnInvalidSetting(name, value, `${String(min)}-${String(max)}`, defaultValue);\n return defaultValue;\n }\n return parsed;\n}\n\n/**\n * Split a comma-separated config value into trimmed, non-empty entries. Empty\n * entries are dropped so \",\" or \" \" reads as unset rather than as a list of\n * blanks. Every comma-separated env var this server accepts parses through\n * here, so they all agree on what \"configured\" means.\n */\nexport function splitCsvList(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nconst REGEXP_SYNTAX = /[\\\\^$.*+?()[\\]{}|/]/;\nconst REGEXP_OTHER_PUNCTUATORS = /[,\\-=<>#&!%:;@~'`\"]/;\nconst REGEXP_CONTROL_ESCAPES: Readonly<Record<string, string>> = {\n '\\t': 't',\n '\\n': 'n',\n '\\v': 'v',\n '\\f': 'f',\n '\\r': 'r',\n};\n\nconst hexEscape = (code: number, width: 2 | 4): string =>\n `\\\\${width === 2 ? 'x' : 'u'}${code.toString(16).padStart(width, '0')}`;\n\nfunction encodeForRegExpEscape(ch: string): string {\n if (REGEXP_SYNTAX.test(ch)) return `\\\\${ch}`;\n const control = REGEXP_CONTROL_ESCAPES[ch];\n if (control !== undefined) return `\\\\${control}`;\n const code = ch.codePointAt(0) ?? 0;\n const loneSurrogate = ch.length === 1 && code >= 0xd800 && code <= 0xdfff;\n if (!REGEXP_OTHER_PUNCTUATORS.test(ch) && !/\\s/.test(ch) && !loneSurrogate) return ch;\n if (code <= 0xff) return hexEscape(code, 2);\n return ch\n .split('')\n .map((unit) => hexEscape(unit.charCodeAt(0), 4))\n .join('');\n}\n\n/** The TC39 `RegExp.escape` algorithm, for runtimes that lack it. */\nexport function escapeRegExpFallback(input: string): string {\n let out = '';\n for (const ch of input) {\n out +=\n out === '' && /[0-9A-Za-z]/.test(ch)\n ? hexEscape(ch.charCodeAt(0), 2)\n : encodeForRegExpEscape(ch);\n }\n return out;\n}\n\n/**\n * `RegExp.escape` shipped in Node 24. Older runtimes still load the published\n * package (npm only warns on `engines`), so fall back rather than crash.\n */\nexport const escapeRegExp: (input: string) => string =\n typeof RegExp.escape === 'function' ? (input) => RegExp.escape(input) : escapeRegExpFallback;\n\nexport const PARALLEL_CONCURRENCY = Math.min(Math.max(availableParallelism(), 4), 32);\n\nexport const ROOTS_TIMEOUT_MS = 5000;\n\nexport function getMaxTextFileSize(): number {\n return parseIntSetting(\n 'FS_MAX_FILE_SIZE',\n cli.maxFileSize ?? process.env['FS_MAX_FILE_SIZE'],\n 10 * MIB,\n MIB,\n 100 * MIB,\n );\n}\n\n/**\n * Upper bound, in bytes, on one inbound JSON-RPC message on either transport.\n * Derived from the file-size limit so that a schema-valid `create` — whose\n * combined content is capped at getMaxTextFileSize() (create.ts) in UTF-16\n * units — still fits after JSON encoding. Per unit the schema counted, the\n * wire carries at most three bytes for well-formed text: a BMP character is\n * up to three UTF-8 bytes, an escaped ASCII character (newline, quote,\n * backslash, tab) is two, a surrogate pair is four bytes for two units. Hence\n * 3× the content, plus 1 MiB for the envelope, paths and sibling arguments.\n * Not covered, deliberately: C0 control characters and lone surrogates\n * (JSON-escaped to six bytes each) — the read tools already refuse such\n * content as binary, and a caller sending it only loses its own connection.\n * Past this bound HTTP answers 413 and stdio closes the connection (the\n * SDK's ReadBuffer contract), so it is also the number the instructions\n * resource and --print-config report.\n */\nexport function getMaxInboundMessageBytes(): number {\n return 3 * getMaxTextFileSize() + MIB;\n}\n/** Combined byte budget for one batched `read`. */\nexport const READ_MANY_MAX_TOTAL_BYTES = 512 * KIB;\n\n/** Default line chunk size for read continuation when no explicit range was given. */\nexport const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;\n\nexport const DEFAULT_SEARCH_TIMEOUT_MS = 5000;\n\nexport const MAX_TREE_DEPTH = 50;\nexport const DEFAULT_TREE_ENTRIES = 1000;\n\nexport const MAX_LIST_ENTRIES = 20000;\n\nexport const MAX_SEARCH_RESULTS = 10000;\nexport const DEFAULT_SEARCH_RESULTS = 100;\nexport const MAX_SEARCH_DEPTH = 100;\n\nexport const DEFAULT_SEARCH_CONTENT_RESULTS = 500;\n\n/**\n * Upper bound on array elements plus object members in one `tools/call`\n * `arguments` payload, counted by the SDK before any schema runs\n * (`McpServerOptions.maxToolInputElements`). Must clear the largest\n * schema-legal call: an `edit` batch of 5 files × 100 edits is 1517 nodes\n * and a 1000-path `read`/`delete` is ~1005, so 4096 is ~2.7× headroom.\n * Raise it in the same change that raises any of those schema caps.\n */\nexport const MAX_TOOL_INPUT_ELEMENTS = 4096;\n"]}
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"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","sourcesContent":["import { availableParallelism } from 'node:os';\n\nimport { cli } from './config.ts';\nimport { warnInvalidSetting } from './path-utils.ts';\n\nconst KIB = 1024;\nexport const MIB = 1024 * KIB;\n\nexport function parseEnvInt(\n envVar: string,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n return parseIntSetting(envVar, process.env[envVar], defaultValue, min, max);\n}\n\n/** Validate a raw integer setting (env var or CLI override) with a logged fallback. */\nfunction parseIntSetting(\n name: string,\n value: string | undefined,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n if (!value) {\n return defaultValue;\n }\n\n const trimmed = value.trim();\n const parsed = Number(trimmed);\n if (\n trimmed === '' ||\n Number.isNaN(parsed) ||\n !Number.isInteger(parsed) ||\n parsed < min ||\n parsed > max\n ) {\n warnInvalidSetting(name, value, `${String(min)}-${String(max)}`, defaultValue);\n return defaultValue;\n }\n return parsed;\n}\n\n/**\n * Split a comma-separated config value into trimmed, non-empty entries. Empty\n * entries are dropped so \",\" or \" \" reads as unset rather than as a list of\n * blanks. Every comma-separated env var this server accepts parses through\n * here, so they all agree on what \"configured\" means.\n */\nexport function splitCsvList(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nconst REGEXP_SYNTAX = /[\\\\^$.*+?()[\\]{}|/]/;\nconst REGEXP_OTHER_PUNCTUATORS = /[,\\-=<>#&!%:;@~'`\"]/;\nconst REGEXP_CONTROL_ESCAPES: Readonly<Record<string, string>> = {\n '\\t': 't',\n '\\n': 'n',\n '\\v': 'v',\n '\\f': 'f',\n '\\r': 'r',\n};\n\nconst hexEscape = (code: number, width: 2 | 4): string =>\n `\\\\${width === 2 ? 'x' : 'u'}${code.toString(16).padStart(width, '0')}`;\n\nfunction encodeForRegExpEscape(ch: string): string {\n if (REGEXP_SYNTAX.test(ch)) return `\\\\${ch}`;\n const control = REGEXP_CONTROL_ESCAPES[ch];\n if (control !== undefined) return `\\\\${control}`;\n const code = ch.codePointAt(0) ?? 0;\n const loneSurrogate = ch.length === 1 && code >= 0xd800 && code <= 0xdfff;\n if (!REGEXP_OTHER_PUNCTUATORS.test(ch) && !/\\s/.test(ch) && !loneSurrogate) return ch;\n if (code <= 0xff) return hexEscape(code, 2);\n return ch\n .split('')\n .map((unit) => hexEscape(unit.charCodeAt(0), 4))\n .join('');\n}\n\n/** The TC39 `RegExp.escape` algorithm, for runtimes that lack it. */\nexport function escapeRegExpFallback(input: string): string {\n let out = '';\n for (const ch of input) {\n out +=\n out === '' && /[0-9A-Za-z]/.test(ch)\n ? hexEscape(ch.charCodeAt(0), 2)\n : encodeForRegExpEscape(ch);\n }\n return out;\n}\n\n/**\n * `RegExp.escape` shipped in Node 24. Older runtimes still load the published\n * package (npm only warns on `engines`), so fall back rather than crash.\n */\nexport const escapeRegExp: (input: string) => string =\n typeof RegExp.escape === 'function' ? (input) => RegExp.escape(input) : escapeRegExpFallback;\n\nexport const PARALLEL_CONCURRENCY = Math.min(Math.max(availableParallelism(), 4), 32);\n\nexport const ROOTS_TIMEOUT_MS = 5000;\n\nexport function getMaxTextFileSize(): number {\n return parseIntSetting(\n 'FS_MAX_FILE_SIZE',\n cli.maxFileSize ?? process.env['FS_MAX_FILE_SIZE'],\n 10 * MIB,\n MIB,\n 100 * MIB,\n );\n}\n\n/**\n * Upper bound, in bytes, on one inbound JSON-RPC message on either transport.\n * Derived from the file-size limit so that a schema-valid `create` — whose\n * combined content is capped at getMaxTextFileSize() (create.ts) in UTF-16\n * units — still fits after JSON encoding. Per unit the schema counted, the\n * wire carries at most three bytes for well-formed text: a BMP character is\n * up to three UTF-8 bytes, an escaped ASCII character (newline, quote,\n * backslash, tab) is two, a surrogate pair is four bytes for two units. Hence\n * 3× the content, plus 1 MiB for the envelope, paths and sibling arguments.\n * Not covered, deliberately: C0 control characters and lone surrogates\n * (JSON-escaped to six bytes each) — the read tools already refuse such\n * content as binary, and a caller sending it only loses its own connection.\n * Past this bound HTTP answers 413 and stdio closes the connection (the\n * SDK's ReadBuffer contract), so it is also the number the instructions\n * resource and --print-config report.\n */\nexport function getMaxInboundMessageBytes(): number {\n return 3 * getMaxTextFileSize() + MIB;\n}\n/** Combined byte budget for one batched `read`. */\nexport const READ_MANY_MAX_TOTAL_BYTES = 512 * KIB;\n\n/**\n * Ceiling on JSON-encoded tool and resource results over stdio. The SDK's\n * fixed 10 MiB ReadBuffer applies regardless of `--max-file-size`; escaping,\n * duplicated metadata, and base64 can overflow it even when the source fits.\n * A tool call that changed nothing (read-only, or a dry run) and a resource\n * read return TOO_LARGE instead, and the stdio result store refuses an entry\n * whose read would overflow it; HTTP is unaffected. ~256 KiB covers the\n * JSON-RPC envelope this measure omits, including the bounded request ID.\n * Mutating tools need pre-commit safeguards rather than a post-commit output\n * refusal.\n */\nexport const MAX_OUTBOUND_RESULT_BYTES = 10 * MIB - 256 * KIB;\n\n/**\n * JSON-encoded stdio request IDs and string progress tokens, including\n * quotes/escapes, must fit the envelope reserve: each is echoed into frames.\n */\nexport const MAX_STDIO_REQUEST_ID_BYTES = KIB;\n\n/**\n * An upper bound on `Buffer.byteLength(JSON.stringify(value))` for the plain\n * JSON data tool and resource results are made of, from a walk that reads\n * string lengths but never encodes a string. JSON spends at most six bytes on\n * one UTF-16 code unit (`\\uXXXX` for a control character or a lone surrogate;\n * any other BMP character is at most three UTF-8 bytes, a surrogate pair four\n * for its two units), so a string or key costs at most 6 × length + 2 quotes.\n * A finite number costs its `String()` form, which is what JSON writes, and a\n * non-finite one `null`; `true`, `false` and `null` at most five. Each array\n * element and object member adds one separator, and a member one colon, on top\n * of the two brackets. A member JSON would omit (undefined, a function) only\n * overcounts. `toJSON` is followed the way JSON.stringify follows it.\n */\nexport function jsonByteBound(value: unknown): number {\n if (typeof value === 'string') return 6 * value.length + 2;\n if (typeof value === 'number') return Number.isFinite(value) ? String(value).length : 4;\n if (typeof value !== 'object' || value === null) return 5;\n if (typeof (value as { toJSON?: unknown }).toJSON === 'function') {\n return jsonByteBound((value as { toJSON: () => unknown }).toJSON());\n }\n let bytes = 2;\n if (Array.isArray(value)) {\n for (const item of value as unknown[]) bytes += jsonByteBound(item) + 1;\n return bytes;\n }\n for (const [key, item] of Object.entries(value)) {\n bytes += 6 * key.length + 2 + 1 + jsonByteBound(item) + 1;\n }\n return bytes;\n}\n\n/**\n * `value`'s JSON-encoded size in bytes when it is over `limit`, else\n * `undefined`. {@link jsonByteBound} clears most values without encoding them;\n * only one it cannot clear pays for the exact `JSON.stringify` measure.\n */\nexport function jsonBytesOver(value: unknown, limit: number): number | undefined {\n if (jsonByteBound(value) <= limit) return undefined;\n const bytes = Buffer.byteLength(JSON.stringify(value), 'utf8');\n return bytes > limit ? bytes : undefined;\n}\n\n/** Default line chunk size for read continuation when no explicit range was given. */\nexport const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;\n\nexport const DEFAULT_SEARCH_TIMEOUT_MS = 5000;\n\nexport const MAX_TREE_DEPTH = 50;\nexport const DEFAULT_TREE_ENTRIES = 1000;\n\nexport const MAX_LIST_ENTRIES = 20000;\n\nexport const MAX_SEARCH_RESULTS = 10000;\nexport const DEFAULT_SEARCH_RESULTS = 100;\nexport const MAX_SEARCH_DEPTH = 100;\n\nexport const DEFAULT_SEARCH_CONTENT_RESULTS = 500;\n\n/**\n * Upper bound on array elements plus object members in one `tools/call`\n * `arguments` payload, counted by the SDK before any schema runs\n * (`McpServerOptions.maxToolInputElements`). Must clear the largest\n * schema-legal call: an `edit` batch of 5 files × 100 edits is 1517 nodes\n * and a 1000-path `read`/`delete` is ~1005, so 4096 is ~2.7× headroom.\n * Raise it in the same change that raises any of those schema caps.\n */\nexport const MAX_TOOL_INPUT_ELEMENTS = 4096;\n"]}
|
package/dist/index.js
CHANGED
|
@@ -65,18 +65,11 @@ async function main() {
|
|
|
65
65
|
}
|
|
66
66
|
throw error;
|
|
67
67
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
await runPrintConfig({
|
|
71
|
-
allowedDirs,
|
|
72
|
-
allowCwd,
|
|
73
|
-
readOnly,
|
|
74
|
-
...(port !== undefined ? { port } : {}),
|
|
75
|
-
...(httpHost !== undefined ? { httpHost } : {}),
|
|
76
|
-
...(apiKey !== undefined ? { apiKey } : {}),
|
|
77
|
-
});
|
|
68
|
+
if (parsed.printConfig) {
|
|
69
|
+
await runPrintConfig(parsed);
|
|
78
70
|
return;
|
|
79
71
|
}
|
|
72
|
+
const { allowedDirs, allowCwd, port, readOnly } = parsed;
|
|
80
73
|
if (allowedDirs.length > 0) {
|
|
81
74
|
console.error('Allowed directories (from CLI):');
|
|
82
75
|
for (const dir of allowedDirs) {
|
|
@@ -90,17 +83,13 @@ async function main() {
|
|
|
90
83
|
console.error('Read-only mode: mutating tools disabled.');
|
|
91
84
|
}
|
|
92
85
|
const serverOptions = { allowCwd, cliAllowedDirs: allowedDirs, readOnly };
|
|
93
|
-
const runtimeConfig = {
|
|
94
|
-
...(httpHost !== undefined ? { httpHost } : {}),
|
|
95
|
-
...(apiKey !== undefined ? { apiKey } : {}),
|
|
96
|
-
};
|
|
97
86
|
if (port !== undefined) {
|
|
98
|
-
activeHttpServer = await startHttpServer(port, serverOptions,
|
|
87
|
+
activeHttpServer = await startHttpServer(port, serverOptions, parsed);
|
|
99
88
|
}
|
|
100
89
|
else {
|
|
101
90
|
registerShutdownTrigger('end');
|
|
102
91
|
registerShutdownTrigger('close');
|
|
103
|
-
activeStdioHandle = startServer(serverOptions
|
|
92
|
+
activeStdioHandle = startServer(serverOptions);
|
|
104
93
|
}
|
|
105
94
|
}
|
|
106
95
|
registerShutdownTrigger('SIGTERM');
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n// Type-only imports are erased at runtime — safe to keep static.\nimport type { StdioServerHandle } from '@modelcontextprotocol/server/stdio';\n\nimport type * as http from 'node:http';\nimport process from 'node:process';\n\nimport * as z from 'zod/v4';\n\nimport { CliExitError, parseArgs, runPrintConfig } from './cli.ts';\nimport { NO_POSITIONAL_ROOTS_GUIDANCE } from './core/config.ts';\nimport { logRuntimeFailure } from './core/observability.ts';\nimport { startHttpServer, startServer } from './transport.ts';\n\nz.config(z.locales.en());\n\nconst SHUTDOWN_TIMEOUT_MS = 5000;\nlet activeStdioHandle: StdioServerHandle | undefined;\nlet activeHttpServer: http.Server | undefined;\nlet shutdownStarted = false;\n\nfunction registerShutdownTrigger(event: NodeJS.Signals | 'end' | 'close'): void {\n const isStdinEvent = event === 'end' || event === 'close';\n const target = isStdinEvent ? process.stdin : process;\n target.once(event, () => {\n const reason = isStdinEvent ? `stdin ${event}` : event;\n void shutdown(reason, 0);\n });\n}\n\nasync function shutdown(reason: string, exitCode = 0): Promise<void> {\n if (shutdownStarted) {\n if (reason === 'SIGINT' || reason === 'SIGTERM') {\n process.exit(130);\n }\n return;\n }\n shutdownStarted = true;\n\n process.exitCode = exitCode;\n\n const timer = setTimeout(() => {\n logRuntimeFailure(\n 'shutdown_timeout',\n 'process',\n 'shutdown',\n `Shutdown timed out (${reason}), forcing exit.`,\n );\n process.exit(exitCode);\n }, SHUTDOWN_TIMEOUT_MS);\n timer.unref();\n\n try {\n try {\n await activeHttpServer?.[Symbol.asyncDispose]();\n } catch (error: unknown) {\n logRuntimeFailure('shutdown_http_error', 'process', 'shutdown', error);\n }\n try {\n await activeStdioHandle?.close();\n } catch (error: unknown) {\n logRuntimeFailure('shutdown_mcp_error', 'process', 'shutdown', error);\n }\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function main(): Promise<void> {\n let parsed: Awaited<ReturnType<typeof parseArgs>>;\n try {\n parsed = await parseArgs();\n } catch (error: unknown) {\n if (error instanceof CliExitError) {\n if (error.message.length > 0) {\n logRuntimeFailure('cli_exit', 'startup', 'parse_args', error.message);\n }\n process.exitCode = 1;\n return;\n }\n throw error;\n }\n if (parsed.printConfig) {\n await runPrintConfig(parsed);\n return;\n }\n const { allowedDirs, allowCwd, port, readOnly } = parsed;\n\n if (allowedDirs.length > 0) {\n console.error('Allowed directories (from CLI):');\n for (const dir of allowedDirs) {\n console.error(`- ${dir}`);\n }\n } else {\n console.error(NO_POSITIONAL_ROOTS_GUIDANCE);\n }\n\n if (readOnly) {\n console.error('Read-only mode: mutating tools disabled.');\n }\n\n const serverOptions = { allowCwd, cliAllowedDirs: allowedDirs, readOnly };\n\n if (port !== undefined) {\n activeHttpServer = await startHttpServer(port, serverOptions, parsed);\n } else {\n registerShutdownTrigger('end');\n registerShutdownTrigger('close');\n activeStdioHandle = startServer(serverOptions);\n }\n}\n\nregisterShutdownTrigger('SIGTERM');\nregisterShutdownTrigger('SIGINT');\n\nprocess.once('unhandledRejection', (reason: unknown) => {\n logRuntimeFailure('unhandled_rejection', 'process', 'unhandledRejection', reason);\n void shutdown('unhandledRejection', 1);\n});\n\nprocess.once('uncaughtException', (error: Error) => {\n logRuntimeFailure('uncaught_exception', 'process', 'uncaughtException', error);\n void shutdown('uncaughtException', 1);\n});\n\nmain().catch((error: unknown) => {\n logRuntimeFailure('fatal', 'startup', 'main', error);\n void shutdown('fatal', 1);\n});\n"]}
|
package/dist/resources.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface ResourceRegistrationOptions {
|
|
|
18
18
|
notifier?: ServerNotifier;
|
|
19
19
|
/** The protocol era this instance serves; omitted where the caller does not know. */
|
|
20
20
|
era?: 'legacy' | 'modern';
|
|
21
|
+
/** Only stdio has the SDK's fixed-size read buffer. */
|
|
22
|
+
transport?: 'stdio' | 'http';
|
|
21
23
|
/**
|
|
22
24
|
* Scope for shared-content cache hints. 'private' when the endpoint is
|
|
23
25
|
* API-key gated, mirroring the list hints in server.ts — a shared cache must
|
|
@@ -57,7 +59,9 @@ interface ResourceContract {
|
|
|
57
59
|
resources: Resource[];
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
|
-
|
|
62
|
+
/** The one contract with a live watcher behind it. */
|
|
63
|
+
type SubscribableContract = ResourceContract & Required<Pick<ResourceContract, 'subscribe' | 'unsubscribe'>>;
|
|
64
|
+
export declare function getResourceContracts(options: ResourceRegistrationOptions): readonly [ResourceContract, ResourceContract, SubscribableContract];
|
|
61
65
|
export declare function registerResources(deps: ResourceRegistrarDeps): {
|
|
62
66
|
dispose(): void;
|
|
63
67
|
};
|
package/dist/resources.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,gCAAgC,EAChC,SAAS,EACT,kBAAkB,EAClB,QAAQ,
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
|