@j0hanz/filesystem-mcp 2.7.6 → 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/dist/cli.d.ts +1 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.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 +11 -11
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +108 -43
- 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 +0 -2
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +12 -29
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts +1 -1
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +13 -14
- 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.map +1 -1
- package/dist/core/search.js +104 -51
- 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 +34 -5
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +59 -5
- 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 +9 -6
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +7 -8
- package/dist/server.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +8 -19
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +8 -7
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +48 -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 +24 -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 -11
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -9
- 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 +5 -1
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts.map +1 -1
- package/dist/tools/patch.js +3 -6
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +31 -40
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +23 -32
- 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.js +1 -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 +45 -12
- package/dist/transport/stdio.js.map +1 -1
- package/package.json +3 -2
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/**\n * Ceiling on the JSON-encoded size of one `read` result. The MCP SDK's stdio\n * transport caps a message at a fixed 10 MiB ReadBuffer regardless of\n * `--max-file-size`, so a file under the size limit can still overflow it once\n * escaped; `read` returns TOO_LARGE instead. ~256 KiB covers the JSON-RPC\n * envelope this measure omits.\n */\nexport const MAX_OUTBOUND_RESULT_BYTES = 10 * MIB - 256 * 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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"}
|
package/dist/resources.js
CHANGED
|
@@ -2,8 +2,10 @@ import { ProtocolError, ProtocolErrorCode, ResourceNotFoundError, ResourceTempla
|
|
|
2
2
|
import { ErrorCode, formatUnknownErrorMessage, isFsError } from "./core/errors.js";
|
|
3
3
|
import { decodeFileUriPath, encodeFileUriPath, extractPath, FILESYSTEM_FILE_URI_TEMPLATE, } from "./core/file-uri.js";
|
|
4
4
|
import { GuardedFileSystem } from "./core/fs.js";
|
|
5
|
-
import { Logger } from "./core/observability.js";
|
|
5
|
+
import { Logger, voidSend } from "./core/observability.js";
|
|
6
6
|
import { suggestPaths } from "./core/path-completer.js";
|
|
7
|
+
import { entryReadResult } from "./core/store.js";
|
|
8
|
+
import { getMaxTextFileSize, jsonBytesOver, MAX_OUTBOUND_RESULT_BYTES } from "./core/util.js";
|
|
7
9
|
import { createWatcherRegistry, MAX_WATCHERS, } from "./core/watcher-registry.js";
|
|
8
10
|
import { buildSectionsRecord, INSTRUCTIONS_SUMMARY, INSTRUCTIONS_URI, renderSections, } from "./instructions.js";
|
|
9
11
|
/**
|
|
@@ -97,7 +99,23 @@ function createFilesystemResource(options) {
|
|
|
97
99
|
}
|
|
98
100
|
await options.pathGuard.validateExistingPath(rawPath);
|
|
99
101
|
const fs = new GuardedFileSystem(options.pathGuard);
|
|
102
|
+
const stdio = options.transport === 'stdio';
|
|
103
|
+
if (stdio) {
|
|
104
|
+
// Text encodes to at least its own bytes and base64 to 4/3 of them,
|
|
105
|
+
// so a file over the limit is refused before it is read. One past the
|
|
106
|
+
// file size limit is left to readRaw, whose refusal names that limit:
|
|
107
|
+
// HTTP would not serve it either.
|
|
108
|
+
const { stats } = await fs.stat(rawPath, { signal: ctx.mcpReq.signal });
|
|
109
|
+
if (stats.size > MAX_OUTBOUND_RESULT_BYTES && stats.size <= getMaxTextFileSize()) {
|
|
110
|
+
throw tooLargeForStdio(stats.size, true);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
100
113
|
const readResult = await fs.readRaw(rawPath, { signal: ctx.mcpReq.signal });
|
|
114
|
+
if (stdio && readResult.isBinary) {
|
|
115
|
+
const base64Bytes = 4 * Math.ceil(readResult.content.length / 3);
|
|
116
|
+
if (base64Bytes > MAX_OUTBOUND_RESULT_BYTES)
|
|
117
|
+
throw tooLargeForStdio(base64Bytes, true);
|
|
118
|
+
}
|
|
101
119
|
return {
|
|
102
120
|
contents: [
|
|
103
121
|
{
|
|
@@ -233,15 +251,15 @@ function createResultResource(options) {
|
|
|
233
251
|
}
|
|
234
252
|
throw err;
|
|
235
253
|
}
|
|
236
|
-
return
|
|
237
|
-
contents: [{ uri: entry.uri, mimeType: entry.mimeType, text: entry.text }],
|
|
238
|
-
};
|
|
254
|
+
return entryReadResult(entry);
|
|
239
255
|
},
|
|
240
256
|
};
|
|
241
257
|
}
|
|
242
258
|
// ═══════════════════════════════════════════════════════════════
|
|
243
259
|
// export contracts
|
|
244
260
|
// ═══════════════════════════════════════════════════════════════
|
|
261
|
+
// The file template last and typed as subscribable, so the registrar can
|
|
262
|
+
// wire its subscribe handlers from the same list it registers.
|
|
245
263
|
export function getResourceContracts(options) {
|
|
246
264
|
return [
|
|
247
265
|
createInstructionsResource(options),
|
|
@@ -267,10 +285,25 @@ function fsErrorToProtocolError(error) {
|
|
|
267
285
|
...(suggestion !== undefined ? { suggestion } : {}),
|
|
268
286
|
});
|
|
269
287
|
}
|
|
270
|
-
|
|
288
|
+
/**
|
|
289
|
+
* The refusal for a result stdio's fixed-size reader cannot carry. `atLeast`
|
|
290
|
+
* when `bytes` is a lower bound known before the result was encoded.
|
|
291
|
+
*/
|
|
292
|
+
function tooLargeForStdio(bytes, atLeast = false) {
|
|
293
|
+
const suggestion = 'Read smaller slices/pages with tools, or connect over HTTP.';
|
|
294
|
+
return new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource result too large to return (${atLeast ? 'at least ' : ''}${String(bytes)} bytes once JSON-encoded, over the ${String(MAX_OUTBOUND_RESULT_BYTES)}-byte stdio message limit). ${suggestion}`, { code: ErrorCode.TOO_LARGE, suggestion });
|
|
295
|
+
}
|
|
296
|
+
function wrapRead(contract, transport) {
|
|
271
297
|
return async (uri, variables, ctx) => {
|
|
272
298
|
try {
|
|
273
|
-
|
|
299
|
+
const result = await contract.read(uri, variables, ctx);
|
|
300
|
+
if (transport === 'stdio') {
|
|
301
|
+
// Cache/file bytes exclude outer JSON escaping and binary base64.
|
|
302
|
+
const encodedBytes = jsonBytesOver(result, MAX_OUTBOUND_RESULT_BYTES);
|
|
303
|
+
if (encodedBytes !== undefined)
|
|
304
|
+
throw tooLargeForStdio(encodedBytes);
|
|
305
|
+
}
|
|
306
|
+
return result;
|
|
274
307
|
}
|
|
275
308
|
catch (error) {
|
|
276
309
|
// The SDK brands ProtocolError with `static [Symbol.hasInstance]`, so this
|
|
@@ -309,13 +342,8 @@ function owns(contract, uri) {
|
|
|
309
342
|
}
|
|
310
343
|
export function registerResources(deps) {
|
|
311
344
|
const server = deps.server;
|
|
312
|
-
const
|
|
313
|
-
const file =
|
|
314
|
-
const resourceContracts = [
|
|
315
|
-
createInstructionsResource(options),
|
|
316
|
-
createResultResource(options),
|
|
317
|
-
file,
|
|
318
|
-
];
|
|
345
|
+
const resourceContracts = getResourceContracts({ ...deps, readOnly: deps.readOnly ?? false });
|
|
346
|
+
const file = resourceContracts[2];
|
|
319
347
|
for (const contract of resourceContracts) {
|
|
320
348
|
const config = {
|
|
321
349
|
...(contract.title !== undefined ? { title: contract.title } : {}),
|
|
@@ -329,10 +357,10 @@ export function registerResources(deps) {
|
|
|
329
357
|
list: contract.list,
|
|
330
358
|
...(contract.complete ? { complete: contract.complete } : {}),
|
|
331
359
|
});
|
|
332
|
-
server.registerResource(contract.name, template, config, wrapRead(contract));
|
|
360
|
+
server.registerResource(contract.name, template, config, wrapRead(contract, deps.transport));
|
|
333
361
|
}
|
|
334
362
|
else if (contract.uri) {
|
|
335
|
-
server.registerResource(contract.name, contract.uri, config, (uri, ctx) => wrapRead(contract)(uri, {}, ctx));
|
|
363
|
+
server.registerResource(contract.name, contract.uri, config, (uri, ctx) => wrapRead(contract, deps.transport)(uri, {}, ctx));
|
|
336
364
|
}
|
|
337
365
|
}
|
|
338
366
|
// `resources/subscribe`/`unsubscribe` are 2025-era-only verbs; a modern
|
|
@@ -360,13 +388,8 @@ export function registerResources(deps) {
|
|
|
360
388
|
deps.notifier.resourceUpdated(updatedUri);
|
|
361
389
|
return;
|
|
362
390
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
void server.server.sendResourceUpdated(updatePayload).catch((err) => {
|
|
366
|
-
Logger.debug('resource update not delivered', {
|
|
367
|
-
uri: updatedUri,
|
|
368
|
-
error: formatUnknownErrorMessage(err),
|
|
369
|
-
});
|
|
391
|
+
voidSend(server.server.sendResourceUpdated({ uri: updatedUri }), 'resource update', {
|
|
392
|
+
uri: updatedUri,
|
|
370
393
|
});
|
|
371
394
|
};
|
|
372
395
|
server.server.setRequestHandler('resources/subscribe', async (req) => {
|
package/dist/resources.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CacheHint,\n CompleteResourceTemplateCallback,\n McpServer,\n ReadResourceResult,\n Resource,\n ResourceUpdatedNotificationParams,\n Role,\n ServerContext,\n ServerNotifier,\n SubscribeRequestParams,\n UnsubscribeRequestParams,\n} from '@modelcontextprotocol/server';\nimport {\n ProtocolError,\n ProtocolErrorCode,\n ResourceNotFoundError,\n ResourceTemplate,\n UriTemplate,\n} from '@modelcontextprotocol/server';\n\nimport type { FsError } from './core/errors.ts';\nimport { ErrorCode, formatUnknownErrorMessage, isFsError } from './core/errors.ts';\nimport {\n decodeFileUriPath,\n encodeFileUriPath,\n extractPath,\n FILESYSTEM_FILE_URI_TEMPLATE,\n} from './core/file-uri.ts';\nimport { GuardedFileSystem } from './core/fs.ts';\nimport { Logger } from './core/observability.ts';\nimport { suggestPaths } from './core/path-completer.ts';\nimport type { PathGuard } from './core/path.ts';\nimport type { ResourceStore } from './core/store.ts';\nimport {\n createWatcherRegistry,\n MAX_WATCHERS,\n type WatcherRegistry,\n} from './core/watcher-registry.ts';\nimport {\n buildSectionsRecord,\n INSTRUCTIONS_SUMMARY,\n INSTRUCTIONS_URI,\n renderSections,\n} from './instructions.ts';\n\n// ═══════════════════════════════════════════════════════════════\n// shared\n// ═══════════════════════════════════════════════════════════════\n\nexport interface ResourceRegistrationOptions {\n resourceStore: ResourceStore;\n pathGuard: PathGuard;\n /** Mirrors the `--read-only` gate so the instructions match the tools actually registered. */\n readOnly: boolean;\n /**\n * Shared file-watcher registry for the modern (per-request) HTTP leg. When set,\n * the resource uses this handler-scoped registry instead of creating its own\n * per-server one, so file-change watchers persist across requests and publish\n * to the shared ServerEventBus. Omitted (per-server registry) on legacy/stdio.\n */\n watcherRegistry?: WatcherRegistry;\n /** Modern-leg typed notification publisher. */\n notifier?: ServerNotifier;\n /** The protocol era this instance serves; omitted where the caller does not know. */\n era?: 'legacy' | 'modern';\n /**\n * Scope for shared-content cache hints. 'private' when the endpoint is\n * API-key gated, mirroring the list hints in server.ts — a shared cache must\n * not store an authenticated response body. Defaults to 'public'.\n */\n cacheScope?: 'public' | 'private';\n}\n\ntype ResourceRegistrarDeps = Omit<ResourceRegistrationOptions, 'pathGuard' | 'readOnly'> & {\n readonly server: McpServer;\n readonly pathGuard: PathGuard;\n readonly readOnly?: boolean;\n};\n\n/**\n * A filesystem failure that reads as not-found on the resource wire, for both\n * `resources/read` and `resources/subscribe`. ACCESS_DENIED folds in\n * deliberately: masking whether an out-of-root or denylisted path exists is the\n * security-correct answer, and the `FsError` message still carries the real\n * cause to the client.\n */\nfunction isNotFoundish(error: unknown): error is FsError {\n return (\n isFsError(error) &&\n (error.code === ErrorCode.NOT_FOUND || error.code === ErrorCode.ACCESS_DENIED)\n );\n}\n\n// ═══════════════════════════════════════════════════════════════\n// contract\n// ═══════════════════════════════════════════════════════════════\n\ninterface ResourceContract {\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n cacheHint?: CacheHint;\n annotations?: {\n audience?: Role[];\n priority?: number;\n };\n /** A fixed, enumerable URI (e.g. internal://instructions)... */\n uri?: string;\n /** ...or a URI template, optionally with a completer for its variable. */\n uriTemplate?: string;\n complete?: { path: CompleteResourceTemplateCallback };\n readonly read: (\n uri: URL,\n variables: Record<string, string | string[]>,\n ctx: ServerContext,\n ) => Promise<ReadResourceResult> | ReadResourceResult;\n readonly subscribe?: (\n uri: string,\n notify: (uri: string) => void,\n ) => Promise<boolean | undefined> | boolean | undefined;\n readonly unsubscribe?: (uri: string) => void;\n readonly destroy?: () => void;\n readonly list?: (\n ctx: ServerContext,\n ) => Promise<{ resources: Resource[] }> | { resources: Resource[] };\n}\n\n/** The one contract with a live watcher behind it. */\ntype SubscribableContract = ResourceContract &\n Required<Pick<ResourceContract, 'subscribe' | 'unsubscribe'>>;\n\n// ═══════════════════════════════════════════════════════════════\n// instructions\n// ═══════════════════════════════════════════════════════════════\n\nfunction createInstructionsResource(options: ResourceRegistrationOptions): ResourceContract {\n const text = renderSections(buildSectionsRecord(options.readOnly));\n return {\n name: 'filesystem-mcp-instructions',\n title: 'Server Instructions',\n description: INSTRUCTIONS_SUMMARY,\n mimeType: 'text/markdown',\n uri: INSTRUCTIONS_URI,\n annotations: { audience: ['assistant'], priority: 0.8 },\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 300_000 },\n read(uri) {\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'text/markdown',\n text,\n },\n ],\n };\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// filesystem\n// ═══════════════════════════════════════════════════════════════\n\nfunction createFilesystemResource(options: ResourceRegistrationOptions): SubscribableContract {\n const registry = options.watcherRegistry ?? createWatcherRegistry();\n // Only the per-server (legacy/stdio) registry is owned by this resource and\n // destroyed on dispose; the shared modern-leg registry is owned by the host\n // and torn down at server shutdown.\n const ownsRegistry = options.watcherRegistry === undefined;\n // The URIs this connection holds a `resources/subscribe` lease for. The wire\n // verb is per-URI and idempotent — a second subscribe is the same\n // subscription, and one unsubscribe ends it — but the registry ref-counts\n // leases (the HTTP listen leg needs that). Without this set a double\n // subscribe took two leases that one unsubscribe could not release, and an\n // unsubscribe for a URI never subscribed released a lease this connection\n // never took, dropping a watcher some other holder still wanted.\n const leasedUris = new Set<string>();\n // Subscribes mid-acquire, keyed by URI. Two concurrent subscribes for the\n // same URI must share one acquire: both passing the `leasedUris` check\n // before either finished took two registry leases that the single set entry\n // could release only once — on the shared modern registry that orphan lease\n // pinned a watcher until process shutdown.\n const inflight = new Map<string, Promise<boolean | undefined>>();\n\n return {\n name: 'filesystem-mcp-file',\n title: 'Workspace File',\n description: 'Read a file from the workspace. Subscribe to get updates when the file changes.',\n uriTemplate: FILESYSTEM_FILE_URI_TEMPLATE,\n annotations: { audience: ['assistant'], priority: 0.8 },\n // Without this the SDK falls back to its conservative `{ ttlMs: 0,\n // cacheScope: 'private' }`, making every file read uncacheable — and this\n // is the one resource with a live invalidation channel, so TTL and the\n // watcher compose exactly as the spec's caching section describes. The TTL\n // stays short because a non-subscriber gets no invalidation signal at all.\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 5_000 },\n\n // No `list`: the template registers with `list: undefined` — readable but\n // not enumerable. Listing each allowed root as a concrete resource\n // duplicated the template and grew resources/list linearly with roots;\n // `list_roots` owns root discovery.\n\n async read(uri, _variables, ctx: ServerContext) {\n ctx.mcpReq.signal.throwIfAborted();\n // Decode via extractPath — the same decoder resources/subscribe uses — so\n // both consumers are symmetric with buildFileResourceUri's encoding. The\n // {+path} template variable arrives still percent-encoded, so validating\n // it directly would treat \"c%3A/proj/a.ts\" as a literal relative path;\n // no fallback to it here, or the two branches would validate different\n // strings. extractPath only fails past the template match on undecodable\n // percent-encoding, which is the caller's malformed URI.\n const rawPath = extractPath(uri.href);\n if (rawPath === undefined) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed file URI: path is not valid percent-encoding',\n );\n }\n await options.pathGuard.validateExistingPath(rawPath);\n const fs = new GuardedFileSystem(options.pathGuard);\n const readResult = await fs.readRaw(rawPath, { signal: ctx.mcpReq.signal });\n\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: readResult.mimeType || 'application/octet-stream',\n ...(readResult.isBinary\n ? { blob: readResult.content.toString('base64') }\n : { text: readResult.content.toString('utf-8') }),\n },\n ],\n };\n },\n\n complete: {\n // Both ends speak the `{+path}` form (see encodeFileUriPath), not raw OS\n // paths: the partial arriving here is whatever this returned last, so the\n // decode mirrors the encode. An undecodable partial is matched as typed.\n path: async (value) => {\n const suggestions = await suggestPaths(\n options.pathGuard,\n decodeFileUriPath(value) ?? value,\n );\n return suggestions.map(encodeFileUriPath);\n },\n },\n\n subscribe(uri, notify) {\n // Already subscribed on this connection: the watcher is live and the sink\n // is registered, so this is a no-op success rather than a second lease.\n if (leasedUris.has(uri)) return undefined;\n // A subscribe for this URI is mid-acquire: share its outcome instead of\n // taking a second lease the lease set cannot represent.\n const pending = inflight.get(uri);\n if (pending) return pending;\n\n const acquire = async (): Promise<boolean | undefined> => {\n const result = await registry.acquire(options.pathGuard, uri, notify, {\n markSubscribe: true,\n });\n if (result.ok) {\n leasedUris.add(uri);\n return undefined;\n }\n\n if (result.reason === 'bad-uri') {\n throw new ResourceNotFoundError(uri, `Cannot subscribe: not a filesystem URI`);\n }\n if (result.reason === 'invalid-path') {\n const err = result.error;\n if (isNotFoundish(err)) {\n throw new ResourceNotFoundError(uri, `Cannot subscribe to ${uri}: ${err.message}`);\n }\n if (isFsError(err)) throw fsErrorToProtocolError(err);\n Logger.warn(\n `Unexpected error validating path for watcher ${uri}: ${formatUnknownErrorMessage(err)}`,\n );\n throw err;\n }\n // Unsubscribed (or the registry destroyed) mid-await: the caller already\n // asked for this to stop, so there is nothing to reject.\n if (result.reason === 'stale') return undefined;\n // capped / attach-failed: `false` tells the handler to reject, since\n // undefined would report success with no watcher attached.\n return false;\n };\n\n const promise = acquire().finally(() => inflight.delete(uri));\n inflight.set(uri, promise);\n return promise;\n },\n\n unsubscribe(uri) {\n // Only release a lease this connection actually took.\n if (!leasedUris.delete(uri)) return;\n registry.release(uri);\n },\n\n destroy() {\n // Release what this connection still holds before the registry goes: on\n // the shared (modern) registry nothing else would ever end these leases.\n for (const uri of leasedUris) registry.release(uri);\n leasedUris.clear();\n if (ownsRegistry) registry.destroy();\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// result\n// ═══════════════════════════════════════════════════════════════\n\nfunction createResultResource(options: ResourceRegistrationOptions): ResourceContract {\n return {\n name: 'filesystem-mcp-result',\n title: 'Cached Tool Result',\n description:\n 'Ephemeral cached tool output. Listed via resources/list; entries expire after the cache TTL.',\n mimeType: 'application/json',\n uriTemplate: 'filesystem-mcp://result/{id}',\n annotations: { audience: ['assistant'], priority: 0.3 },\n cacheHint: { cacheScope: 'private', ttlMs: 60_000 },\n list() {\n const store = options.resourceStore;\n const uris = store.keys(); // prunes expired first\n const resources: Resource[] = [];\n for (const uri of uris) {\n try {\n const entry = store.getEntry(uri);\n resources.push({\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: ['assistant'], priority: 0.3, lastModified: entry.createdAt },\n });\n } catch (err) {\n // An entry may expire between keys() and getEntry; skip it.\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) continue;\n throw err;\n }\n }\n // Recent-first: keys() returns insertion order, so the newest is last.\n resources.reverse();\n return { resources };\n },\n read(uri, variables) {\n const { id } = variables;\n if (typeof id !== 'string' || id.length === 0) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed result URI: missing {id}. Use the exact resourceUri returned by the tool.',\n );\n }\n\n let entry;\n try {\n entry = options.resourceStore.getEntry(uri.toString());\n } catch (err) {\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) {\n throw new ResourceNotFoundError(\n uri.toString(),\n 'Cached result not found or expired. Re-run the tool to regenerate.',\n );\n }\n throw err;\n }\n return {\n contents: [{ uri: entry.uri, mimeType: entry.mimeType, text: entry.text }],\n };\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// export contracts\n// ═══════════════════════════════════════════════════════════════\n\nexport function getResourceContracts(options: ResourceRegistrationOptions): ResourceContract[] {\n return [\n createInstructionsResource(options),\n createResultResource(options),\n createFilesystemResource(options),\n ];\n}\n\n// ═══════════════════════════════════════════════════════════════\n// registrar\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * A caller-traceable FsError as the SDK's InvalidParams error. The Problem's\n * identity fields ride the SDK's error.data channel: `message` stays the\n * wire message, and code/path/suggestion give clients a machine-readable\n * view. Keyed by `code`, so it cannot collide with the\n * ResourceNotFoundError convention (data: { uri } and nothing else).\n */\nfunction fsErrorToProtocolError(error: FsError): ProtocolError {\n const { code, path, suggestion } = error.problem;\n return new ProtocolError(ProtocolErrorCode.InvalidParams, error.message, {\n code,\n ...(path !== undefined ? { path } : {}),\n ...(suggestion !== undefined ? { suggestion } : {}),\n });\n}\n\nfunction wrapRead(contract: ResourceContract) {\n return async (uri: URL, variables: Record<string, string | string[]>, ctx: ServerContext) => {\n try {\n return await contract.read(uri, variables, ctx);\n } catch (error) {\n // The SDK brands ProtocolError with `static [Symbol.hasInstance]`, so this\n // holds across separately bundled SDK copies and realms.\n if (error instanceof ProtocolError) throw error;\n // A missing or out-of-root path is a not-found, not a malformed request.\n // The SDK puts ResourceNotFoundError on the wire as -32602 with\n // `data.uri`, which is what clients match on; -32002 is the older code\n // they also accept, and this SDK never emits it.\n if (isNotFoundish(error)) {\n throw new ResourceNotFoundError(uri.toString(), error.message);\n }\n // A remaining FsError (NOT_FILE, TOO_LARGE, ...) traces to the\n // caller-supplied URI; anything else is a server-side failure and must\n // not be blamed on the request.\n if (isFsError(error)) throw fsErrorToProtocolError(error);\n throw new ProtocolError(ProtocolErrorCode.InternalError, formatUnknownErrorMessage(error));\n }\n };\n}\n\n/**\n * Whether `contract` answers for `uri`, decided by the template matcher the SDK\n * dispatches `resources/read` with, so `resources/subscribe` and\n * `resources/read` agree on which resource owns a URI. Not\n * `checkResourceAllowed`: that is an RFC 8707 audience check comparing\n * `URL.origin`, which is the opaque `\"null\"` for every custom scheme, so it\n * matched `filesystem-mcp://result/…` — and any other scheme — to the file\n * template.\n */\nfunction owns(contract: ResourceContract, uri: string): boolean {\n if (contract.uri !== undefined) return uri === contract.uri;\n return (\n contract.uriTemplate !== undefined && new UriTemplate(contract.uriTemplate).match(uri) !== null\n );\n}\n\nexport function registerResources(deps: ResourceRegistrarDeps): { dispose(): void } {\n const server = deps.server;\n const options = { ...deps, readOnly: deps.readOnly ?? false };\n const file = createFilesystemResource(options);\n const resourceContracts = [\n createInstructionsResource(options),\n createResultResource(options),\n file,\n ];\n\n for (const contract of resourceContracts) {\n const config = {\n ...(contract.title !== undefined ? { title: contract.title } : {}),\n ...(contract.description !== undefined ? { description: contract.description } : {}),\n ...(contract.mimeType !== undefined ? { mimeType: contract.mimeType } : {}),\n ...(contract.annotations !== undefined ? { annotations: contract.annotations } : {}),\n ...(contract.cacheHint !== undefined ? { cacheHint: contract.cacheHint } : {}),\n };\n\n if (contract.uriTemplate) {\n const template = new ResourceTemplate(contract.uriTemplate, {\n list: contract.list,\n ...(contract.complete ? { complete: contract.complete } : {}),\n });\n\n server.registerResource(contract.name, template, config, wrapRead(contract));\n } else if (contract.uri) {\n server.registerResource(contract.name, contract.uri, config, (uri: URL, ctx: ServerContext) =>\n wrapRead(contract)(uri, {}, ctx),\n );\n }\n }\n\n // `resources/subscribe`/`unsubscribe` are 2025-era-only verbs; a modern\n // server answers `-32601 Method not found` for them, so registering these\n // handlers on a modern-era instance would dispatch to code no request can\n // reach. A legacy instance on the HTTP leg (the one built with a `notifier`)\n // lives for one request: a subscription accepted there would take a watcher\n // lease for an instance that is gone before the file changes. Leave the\n // handlers off so the SDK refuses the verb, and no lease is taken. The\n // predicate mirrors the capability gate in server.ts exactly, so the\n // handler is registered if and only if `resources.subscribe` is advertised.\n // sunset(SEP-2577): removal trigger in docs/adr/002-legacy-protocol-paths-sunset.md.\n const legacyHttp = deps.era === 'legacy' && deps.notifier !== undefined;\n if (deps.era !== 'modern' && !legacyHttp) {\n server.server.assertCanSetRequestHandler('resources/subscribe');\n server.server.assertCanSetRequestHandler('resources/unsubscribe');\n\n // One stable callback for the whole registrar, NOT one per subscribe. The\n // registry holds these in a Set keyed by identity, so a fresh closure per\n // request made a second `resources/subscribe` for the same URI register a\n // second sink: one file change then sent N notifications, and `unsubscribe`\n // (which only ends a lease) removed none of them. Subscribe is per-URI on\n // the wire, so the sink must be too — this makes it idempotent.\n const notifyUpdated = (updatedUri: string): void => {\n if (deps.notifier) {\n deps.notifier.resourceUpdated(updatedUri);\n return;\n }\n const updatePayload: ResourceUpdatedNotificationParams = { uri: updatedUri };\n // A failed notify means the connection went away; nothing to recover.\n void server.server.sendResourceUpdated(updatePayload).catch((err: unknown) => {\n Logger.debug('resource update not delivered', {\n uri: updatedUri,\n error: formatUnknownErrorMessage(err),\n });\n });\n };\n\n server.server.setRequestHandler(\n 'resources/subscribe',\n async (req: { params: SubscribeRequestParams }) => {\n const { uri } = req.params;\n if (!owns(file, uri)) {\n // A resource that exists but has no watcher (the instructions text,\n // a cached result) is NOT a not-found: reporting it as one told\n // clients a URI they can list and read does not exist.\n if (resourceContracts.some((contract) => owns(contract, uri))) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Resource ${uri} does not support subscriptions; only ${FILESYSTEM_FILE_URI_TEMPLATE} does. Read it again for the current contents.`,\n );\n }\n throw new ResourceNotFoundError(uri, `Resource not found: ${uri}`);\n }\n if ((await file.subscribe(uri, notifyUpdated)) === false) {\n // InternalError for want of anything better: ProtocolErrorCode has\n // no resource-limit member, and the message already names the\n // actionable cause.\n throw new ProtocolError(\n ProtocolErrorCode.InternalError,\n `Subscription rejected: no watcher attached (watcher limit ${MAX_WATCHERS} reached, or fs.watch failed to start).`,\n );\n }\n return {};\n },\n );\n\n server.server.setRequestHandler(\n 'resources/unsubscribe',\n (req: { params: UnsubscribeRequestParams }) => {\n if (owns(file, req.params.uri)) file.unsubscribe(req.params.uri);\n return {};\n },\n );\n }\n\n return {\n dispose(): void {\n for (const contract of resourceContracts) {\n if (contract.destroy) {\n contract.destroy();\n }\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CacheHint,\n CompleteResourceTemplateCallback,\n McpServer,\n ReadResourceResult,\n Resource,\n Role,\n ServerContext,\n ServerNotifier,\n SubscribeRequestParams,\n UnsubscribeRequestParams,\n} from '@modelcontextprotocol/server';\nimport {\n ProtocolError,\n ProtocolErrorCode,\n ResourceNotFoundError,\n ResourceTemplate,\n UriTemplate,\n} from '@modelcontextprotocol/server';\n\nimport type { FsError } from './core/errors.ts';\nimport { ErrorCode, formatUnknownErrorMessage, isFsError } from './core/errors.ts';\nimport {\n decodeFileUriPath,\n encodeFileUriPath,\n extractPath,\n FILESYSTEM_FILE_URI_TEMPLATE,\n} from './core/file-uri.ts';\nimport { GuardedFileSystem } from './core/fs.ts';\nimport { Logger, voidSend } from './core/observability.ts';\nimport { suggestPaths } from './core/path-completer.ts';\nimport type { PathGuard } from './core/path.ts';\nimport type { ResourceStore } from './core/store.ts';\nimport { entryReadResult } from './core/store.ts';\nimport { getMaxTextFileSize, jsonBytesOver, MAX_OUTBOUND_RESULT_BYTES } from './core/util.ts';\nimport {\n createWatcherRegistry,\n MAX_WATCHERS,\n type WatcherRegistry,\n} from './core/watcher-registry.ts';\nimport {\n buildSectionsRecord,\n INSTRUCTIONS_SUMMARY,\n INSTRUCTIONS_URI,\n renderSections,\n} from './instructions.ts';\n\n// ═══════════════════════════════════════════════════════════════\n// shared\n// ═══════════════════════════════════════════════════════════════\n\nexport interface ResourceRegistrationOptions {\n resourceStore: ResourceStore;\n pathGuard: PathGuard;\n /** Mirrors the `--read-only` gate so the instructions match the tools actually registered. */\n readOnly: boolean;\n /**\n * Shared file-watcher registry for the modern (per-request) HTTP leg. When set,\n * the resource uses this handler-scoped registry instead of creating its own\n * per-server one, so file-change watchers persist across requests and publish\n * to the shared ServerEventBus. Omitted (per-server registry) on legacy/stdio.\n */\n watcherRegistry?: WatcherRegistry;\n /** Modern-leg typed notification publisher. */\n notifier?: ServerNotifier;\n /** The protocol era this instance serves; omitted where the caller does not know. */\n era?: 'legacy' | 'modern';\n /** Only stdio has the SDK's fixed-size read buffer. */\n transport?: 'stdio' | 'http';\n /**\n * Scope for shared-content cache hints. 'private' when the endpoint is\n * API-key gated, mirroring the list hints in server.ts — a shared cache must\n * not store an authenticated response body. Defaults to 'public'.\n */\n cacheScope?: 'public' | 'private';\n}\n\ntype ResourceRegistrarDeps = Omit<ResourceRegistrationOptions, 'pathGuard' | 'readOnly'> & {\n readonly server: McpServer;\n readonly pathGuard: PathGuard;\n readonly readOnly?: boolean;\n};\n\n/**\n * A filesystem failure that reads as not-found on the resource wire, for both\n * `resources/read` and `resources/subscribe`. ACCESS_DENIED folds in\n * deliberately: masking whether an out-of-root or denylisted path exists is the\n * security-correct answer, and the `FsError` message still carries the real\n * cause to the client.\n */\nfunction isNotFoundish(error: unknown): error is FsError {\n return (\n isFsError(error) &&\n (error.code === ErrorCode.NOT_FOUND || error.code === ErrorCode.ACCESS_DENIED)\n );\n}\n\n// ═══════════════════════════════════════════════════════════════\n// contract\n// ═══════════════════════════════════════════════════════════════\n\ninterface ResourceContract {\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n cacheHint?: CacheHint;\n annotations?: {\n audience?: Role[];\n priority?: number;\n };\n /** A fixed, enumerable URI (e.g. internal://instructions)... */\n uri?: string;\n /** ...or a URI template, optionally with a completer for its variable. */\n uriTemplate?: string;\n complete?: { path: CompleteResourceTemplateCallback };\n readonly read: (\n uri: URL,\n variables: Record<string, string | string[]>,\n ctx: ServerContext,\n ) => Promise<ReadResourceResult> | ReadResourceResult;\n readonly subscribe?: (\n uri: string,\n notify: (uri: string) => void,\n ) => Promise<boolean | undefined> | boolean | undefined;\n readonly unsubscribe?: (uri: string) => void;\n readonly destroy?: () => void;\n readonly list?: (\n ctx: ServerContext,\n ) => Promise<{ resources: Resource[] }> | { resources: Resource[] };\n}\n\n/** The one contract with a live watcher behind it. */\ntype SubscribableContract = ResourceContract &\n Required<Pick<ResourceContract, 'subscribe' | 'unsubscribe'>>;\n\n// ═══════════════════════════════════════════════════════════════\n// instructions\n// ═══════════════════════════════════════════════════════════════\n\nfunction createInstructionsResource(options: ResourceRegistrationOptions): ResourceContract {\n const text = renderSections(buildSectionsRecord(options.readOnly));\n return {\n name: 'filesystem-mcp-instructions',\n title: 'Server Instructions',\n description: INSTRUCTIONS_SUMMARY,\n mimeType: 'text/markdown',\n uri: INSTRUCTIONS_URI,\n annotations: { audience: ['assistant'], priority: 0.8 },\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 300_000 },\n read(uri) {\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'text/markdown',\n text,\n },\n ],\n };\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// filesystem\n// ═══════════════════════════════════════════════════════════════\n\nfunction createFilesystemResource(options: ResourceRegistrationOptions): SubscribableContract {\n const registry = options.watcherRegistry ?? createWatcherRegistry();\n // Only the per-server (legacy/stdio) registry is owned by this resource and\n // destroyed on dispose; the shared modern-leg registry is owned by the host\n // and torn down at server shutdown.\n const ownsRegistry = options.watcherRegistry === undefined;\n // The URIs this connection holds a `resources/subscribe` lease for. The wire\n // verb is per-URI and idempotent — a second subscribe is the same\n // subscription, and one unsubscribe ends it — but the registry ref-counts\n // leases (the HTTP listen leg needs that). Without this set a double\n // subscribe took two leases that one unsubscribe could not release, and an\n // unsubscribe for a URI never subscribed released a lease this connection\n // never took, dropping a watcher some other holder still wanted.\n const leasedUris = new Set<string>();\n // Subscribes mid-acquire, keyed by URI. Two concurrent subscribes for the\n // same URI must share one acquire: both passing the `leasedUris` check\n // before either finished took two registry leases that the single set entry\n // could release only once — on the shared modern registry that orphan lease\n // pinned a watcher until process shutdown.\n const inflight = new Map<string, Promise<boolean | undefined>>();\n\n return {\n name: 'filesystem-mcp-file',\n title: 'Workspace File',\n description: 'Read a file from the workspace. Subscribe to get updates when the file changes.',\n uriTemplate: FILESYSTEM_FILE_URI_TEMPLATE,\n annotations: { audience: ['assistant'], priority: 0.8 },\n // Without this the SDK falls back to its conservative `{ ttlMs: 0,\n // cacheScope: 'private' }`, making every file read uncacheable — and this\n // is the one resource with a live invalidation channel, so TTL and the\n // watcher compose exactly as the spec's caching section describes. The TTL\n // stays short because a non-subscriber gets no invalidation signal at all.\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 5_000 },\n\n // No `list`: the template registers with `list: undefined` — readable but\n // not enumerable. Listing each allowed root as a concrete resource\n // duplicated the template and grew resources/list linearly with roots;\n // `list_roots` owns root discovery.\n\n async read(uri, _variables, ctx: ServerContext) {\n ctx.mcpReq.signal.throwIfAborted();\n // Decode via extractPath — the same decoder resources/subscribe uses — so\n // both consumers are symmetric with buildFileResourceUri's encoding. The\n // {+path} template variable arrives still percent-encoded, so validating\n // it directly would treat \"c%3A/proj/a.ts\" as a literal relative path;\n // no fallback to it here, or the two branches would validate different\n // strings. extractPath only fails past the template match on undecodable\n // percent-encoding, which is the caller's malformed URI.\n const rawPath = extractPath(uri.href);\n if (rawPath === undefined) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed file URI: path is not valid percent-encoding',\n );\n }\n await options.pathGuard.validateExistingPath(rawPath);\n const fs = new GuardedFileSystem(options.pathGuard);\n const stdio = options.transport === 'stdio';\n if (stdio) {\n // Text encodes to at least its own bytes and base64 to 4/3 of them,\n // so a file over the limit is refused before it is read. One past the\n // file size limit is left to readRaw, whose refusal names that limit:\n // HTTP would not serve it either.\n const { stats } = await fs.stat(rawPath, { signal: ctx.mcpReq.signal });\n if (stats.size > MAX_OUTBOUND_RESULT_BYTES && stats.size <= getMaxTextFileSize()) {\n throw tooLargeForStdio(stats.size, true);\n }\n }\n const readResult = await fs.readRaw(rawPath, { signal: ctx.mcpReq.signal });\n if (stdio && readResult.isBinary) {\n const base64Bytes = 4 * Math.ceil(readResult.content.length / 3);\n if (base64Bytes > MAX_OUTBOUND_RESULT_BYTES) throw tooLargeForStdio(base64Bytes, true);\n }\n\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: readResult.mimeType || 'application/octet-stream',\n ...(readResult.isBinary\n ? { blob: readResult.content.toString('base64') }\n : { text: readResult.content.toString('utf-8') }),\n },\n ],\n };\n },\n\n complete: {\n // Both ends speak the `{+path}` form (see encodeFileUriPath), not raw OS\n // paths: the partial arriving here is whatever this returned last, so the\n // decode mirrors the encode. An undecodable partial is matched as typed.\n path: async (value) => {\n const suggestions = await suggestPaths(\n options.pathGuard,\n decodeFileUriPath(value) ?? value,\n );\n return suggestions.map(encodeFileUriPath);\n },\n },\n\n subscribe(uri, notify) {\n // Already subscribed on this connection: the watcher is live and the sink\n // is registered, so this is a no-op success rather than a second lease.\n if (leasedUris.has(uri)) return undefined;\n // A subscribe for this URI is mid-acquire: share its outcome instead of\n // taking a second lease the lease set cannot represent.\n const pending = inflight.get(uri);\n if (pending) return pending;\n\n const acquire = async (): Promise<boolean | undefined> => {\n const result = await registry.acquire(options.pathGuard, uri, notify, {\n markSubscribe: true,\n });\n if (result.ok) {\n leasedUris.add(uri);\n return undefined;\n }\n\n if (result.reason === 'bad-uri') {\n throw new ResourceNotFoundError(uri, `Cannot subscribe: not a filesystem URI`);\n }\n if (result.reason === 'invalid-path') {\n const err = result.error;\n if (isNotFoundish(err)) {\n throw new ResourceNotFoundError(uri, `Cannot subscribe to ${uri}: ${err.message}`);\n }\n if (isFsError(err)) throw fsErrorToProtocolError(err);\n Logger.warn(\n `Unexpected error validating path for watcher ${uri}: ${formatUnknownErrorMessage(err)}`,\n );\n throw err;\n }\n // Unsubscribed (or the registry destroyed) mid-await: the caller already\n // asked for this to stop, so there is nothing to reject.\n if (result.reason === 'stale') return undefined;\n // capped / attach-failed: `false` tells the handler to reject, since\n // undefined would report success with no watcher attached.\n return false;\n };\n\n const promise = acquire().finally(() => inflight.delete(uri));\n inflight.set(uri, promise);\n return promise;\n },\n\n unsubscribe(uri) {\n // Only release a lease this connection actually took.\n if (!leasedUris.delete(uri)) return;\n registry.release(uri);\n },\n\n destroy() {\n // Release what this connection still holds before the registry goes: on\n // the shared (modern) registry nothing else would ever end these leases.\n for (const uri of leasedUris) registry.release(uri);\n leasedUris.clear();\n if (ownsRegistry) registry.destroy();\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// result\n// ═══════════════════════════════════════════════════════════════\n\nfunction createResultResource(options: ResourceRegistrationOptions): ResourceContract {\n return {\n name: 'filesystem-mcp-result',\n title: 'Cached Tool Result',\n description:\n 'Ephemeral cached tool output. Listed via resources/list; entries expire after the cache TTL.',\n mimeType: 'application/json',\n uriTemplate: 'filesystem-mcp://result/{id}',\n annotations: { audience: ['assistant'], priority: 0.3 },\n cacheHint: { cacheScope: 'private', ttlMs: 60_000 },\n list() {\n const store = options.resourceStore;\n const uris = store.keys(); // prunes expired first\n const resources: Resource[] = [];\n for (const uri of uris) {\n try {\n const entry = store.getEntry(uri);\n resources.push({\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: ['assistant'], priority: 0.3, lastModified: entry.createdAt },\n });\n } catch (err) {\n // An entry may expire between keys() and getEntry; skip it.\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) continue;\n throw err;\n }\n }\n // Recent-first: keys() returns insertion order, so the newest is last.\n resources.reverse();\n return { resources };\n },\n read(uri, variables) {\n const { id } = variables;\n if (typeof id !== 'string' || id.length === 0) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed result URI: missing {id}. Use the exact resourceUri returned by the tool.',\n );\n }\n\n let entry;\n try {\n entry = options.resourceStore.getEntry(uri.toString());\n } catch (err) {\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) {\n throw new ResourceNotFoundError(\n uri.toString(),\n 'Cached result not found or expired. Re-run the tool to regenerate.',\n );\n }\n throw err;\n }\n return entryReadResult(entry);\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// export contracts\n// ═══════════════════════════════════════════════════════════════\n\n// The file template last and typed as subscribable, so the registrar can\n// wire its subscribe handlers from the same list it registers.\nexport function getResourceContracts(\n options: ResourceRegistrationOptions,\n): readonly [ResourceContract, ResourceContract, SubscribableContract] {\n return [\n createInstructionsResource(options),\n createResultResource(options),\n createFilesystemResource(options),\n ];\n}\n\n// ═══════════════════════════════════════════════════════════════\n// registrar\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * A caller-traceable FsError as the SDK's InvalidParams error. The Problem's\n * identity fields ride the SDK's error.data channel: `message` stays the\n * wire message, and code/path/suggestion give clients a machine-readable\n * view. Keyed by `code`, so it cannot collide with the\n * ResourceNotFoundError convention (data: { uri } and nothing else).\n */\nfunction fsErrorToProtocolError(error: FsError): ProtocolError {\n const { code, path, suggestion } = error.problem;\n return new ProtocolError(ProtocolErrorCode.InvalidParams, error.message, {\n code,\n ...(path !== undefined ? { path } : {}),\n ...(suggestion !== undefined ? { suggestion } : {}),\n });\n}\n\n/**\n * The refusal for a result stdio's fixed-size reader cannot carry. `atLeast`\n * when `bytes` is a lower bound known before the result was encoded.\n */\nfunction tooLargeForStdio(bytes: number, atLeast = false): ProtocolError {\n const suggestion = 'Read smaller slices/pages with tools, or connect over HTTP.';\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Resource result too large to return (${atLeast ? 'at least ' : ''}${String(bytes)} bytes once JSON-encoded, over the ${String(MAX_OUTBOUND_RESULT_BYTES)}-byte stdio message limit). ${suggestion}`,\n { code: ErrorCode.TOO_LARGE, suggestion },\n );\n}\n\nfunction wrapRead(contract: ResourceContract, transport: ResourceRegistrationOptions['transport']) {\n return async (uri: URL, variables: Record<string, string | string[]>, ctx: ServerContext) => {\n try {\n const result = await contract.read(uri, variables, ctx);\n if (transport === 'stdio') {\n // Cache/file bytes exclude outer JSON escaping and binary base64.\n const encodedBytes = jsonBytesOver(result, MAX_OUTBOUND_RESULT_BYTES);\n if (encodedBytes !== undefined) throw tooLargeForStdio(encodedBytes);\n }\n return result;\n } catch (error) {\n // The SDK brands ProtocolError with `static [Symbol.hasInstance]`, so this\n // holds across separately bundled SDK copies and realms.\n if (error instanceof ProtocolError) throw error;\n // A missing or out-of-root path is a not-found, not a malformed request.\n // The SDK puts ResourceNotFoundError on the wire as -32602 with\n // `data.uri`, which is what clients match on; -32002 is the older code\n // they also accept, and this SDK never emits it.\n if (isNotFoundish(error)) {\n throw new ResourceNotFoundError(uri.toString(), error.message);\n }\n // A remaining FsError (NOT_FILE, TOO_LARGE, ...) traces to the\n // caller-supplied URI; anything else is a server-side failure and must\n // not be blamed on the request.\n if (isFsError(error)) throw fsErrorToProtocolError(error);\n throw new ProtocolError(ProtocolErrorCode.InternalError, formatUnknownErrorMessage(error));\n }\n };\n}\n\n/**\n * Whether `contract` answers for `uri`, decided by the template matcher the SDK\n * dispatches `resources/read` with, so `resources/subscribe` and\n * `resources/read` agree on which resource owns a URI. Not\n * `checkResourceAllowed`: that is an RFC 8707 audience check comparing\n * `URL.origin`, which is the opaque `\"null\"` for every custom scheme, so it\n * matched `filesystem-mcp://result/…` — and any other scheme — to the file\n * template.\n */\nfunction owns(contract: ResourceContract, uri: string): boolean {\n if (contract.uri !== undefined) return uri === contract.uri;\n return (\n contract.uriTemplate !== undefined && new UriTemplate(contract.uriTemplate).match(uri) !== null\n );\n}\n\nexport function registerResources(deps: ResourceRegistrarDeps): { dispose(): void } {\n const server = deps.server;\n const resourceContracts = getResourceContracts({ ...deps, readOnly: deps.readOnly ?? false });\n const file = resourceContracts[2];\n\n for (const contract of resourceContracts) {\n const config = {\n ...(contract.title !== undefined ? { title: contract.title } : {}),\n ...(contract.description !== undefined ? { description: contract.description } : {}),\n ...(contract.mimeType !== undefined ? { mimeType: contract.mimeType } : {}),\n ...(contract.annotations !== undefined ? { annotations: contract.annotations } : {}),\n ...(contract.cacheHint !== undefined ? { cacheHint: contract.cacheHint } : {}),\n };\n\n if (contract.uriTemplate) {\n const template = new ResourceTemplate(contract.uriTemplate, {\n list: contract.list,\n ...(contract.complete ? { complete: contract.complete } : {}),\n });\n\n server.registerResource(contract.name, template, config, wrapRead(contract, deps.transport));\n } else if (contract.uri) {\n server.registerResource(contract.name, contract.uri, config, (uri: URL, ctx: ServerContext) =>\n wrapRead(contract, deps.transport)(uri, {}, ctx),\n );\n }\n }\n\n // `resources/subscribe`/`unsubscribe` are 2025-era-only verbs; a modern\n // server answers `-32601 Method not found` for them, so registering these\n // handlers on a modern-era instance would dispatch to code no request can\n // reach. A legacy instance on the HTTP leg (the one built with a `notifier`)\n // lives for one request: a subscription accepted there would take a watcher\n // lease for an instance that is gone before the file changes. Leave the\n // handlers off so the SDK refuses the verb, and no lease is taken. The\n // predicate mirrors the capability gate in server.ts exactly, so the\n // handler is registered if and only if `resources.subscribe` is advertised.\n // sunset(SEP-2577): removal trigger in docs/adr/002-legacy-protocol-paths-sunset.md.\n const legacyHttp = deps.era === 'legacy' && deps.notifier !== undefined;\n if (deps.era !== 'modern' && !legacyHttp) {\n server.server.assertCanSetRequestHandler('resources/subscribe');\n server.server.assertCanSetRequestHandler('resources/unsubscribe');\n\n // One stable callback for the whole registrar, NOT one per subscribe. The\n // registry holds these in a Set keyed by identity, so a fresh closure per\n // request made a second `resources/subscribe` for the same URI register a\n // second sink: one file change then sent N notifications, and `unsubscribe`\n // (which only ends a lease) removed none of them. Subscribe is per-URI on\n // the wire, so the sink must be too — this makes it idempotent.\n const notifyUpdated = (updatedUri: string): void => {\n if (deps.notifier) {\n deps.notifier.resourceUpdated(updatedUri);\n return;\n }\n voidSend(server.server.sendResourceUpdated({ uri: updatedUri }), 'resource update', {\n uri: updatedUri,\n });\n };\n\n server.server.setRequestHandler(\n 'resources/subscribe',\n async (req: { params: SubscribeRequestParams }) => {\n const { uri } = req.params;\n if (!owns(file, uri)) {\n // A resource that exists but has no watcher (the instructions text,\n // a cached result) is NOT a not-found: reporting it as one told\n // clients a URI they can list and read does not exist.\n if (resourceContracts.some((contract) => owns(contract, uri))) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Resource ${uri} does not support subscriptions; only ${FILESYSTEM_FILE_URI_TEMPLATE} does. Read it again for the current contents.`,\n );\n }\n throw new ResourceNotFoundError(uri, `Resource not found: ${uri}`);\n }\n if ((await file.subscribe(uri, notifyUpdated)) === false) {\n // InternalError for want of anything better: ProtocolErrorCode has\n // no resource-limit member, and the message already names the\n // actionable cause.\n throw new ProtocolError(\n ProtocolErrorCode.InternalError,\n `Subscription rejected: no watcher attached (watcher limit ${MAX_WATCHERS} reached, or fs.watch failed to start).`,\n );\n }\n return {};\n },\n );\n\n server.server.setRequestHandler(\n 'resources/unsubscribe',\n (req: { params: UnsubscribeRequestParams }) => {\n if (owns(file, req.params.uri)) file.unsubscribe(req.params.uri);\n return {};\n },\n );\n }\n\n return {\n dispose(): void {\n for (const contract of resourceContracts) {\n if (contract.destroy) {\n contract.destroy();\n }\n }\n },\n };\n}\n"]}
|
package/dist/server.d.ts
CHANGED
|
@@ -36,14 +36,17 @@ export declare function createServer(options?: ServerOptions, extraDeps?: {
|
|
|
36
36
|
pageStore?: PageSnapshotStore;
|
|
37
37
|
/** The protocol era this instance serves; omitted where the caller does not know. */
|
|
38
38
|
era?: 'legacy' | 'modern';
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Only `stdio` has the SDK's fixed 10 MiB ReadBuffer, so only there are
|
|
41
|
+
* results capped: any tool call that changed nothing, any resource read,
|
|
42
|
+
* and a result-store entry by the size of its read (define.ts,
|
|
43
|
+
* resources.ts, store.ts).
|
|
44
|
+
*/
|
|
40
45
|
transport?: 'stdio' | 'http';
|
|
41
46
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* constructor argument and is exposed on its public `options` field — a
|
|
45
|
-
* bearer secret has no business being reachable from a tool handler.
|
|
47
|
+
* `private` when the HTTP host requires a bearer key, so a shared cache
|
|
48
|
+
* never serves one tenant's listing to another. Default `public`.
|
|
46
49
|
*/
|
|
47
|
-
|
|
50
|
+
cacheScope?: 'private' | 'public';
|
|
48
51
|
}): Promise<FilesystemServerContext>;
|
|
49
52
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAgBlE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,mBAAmB,IAAI,IAAI,CAAC;CAC7B;AAED,wBAAsB,YAAY,CAChC,OAAO,GAAE,aAAkB,EAC3B,SAAS,CAAC,EAAE;IACV,0EAA0E;IAC1E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,qFAAqF;IACrF,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAgBlE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,mBAAmB,IAAI,IAAI,CAAC;CAC7B;AAED,wBAAsB,YAAY,CAChC,OAAO,GAAE,aAAkB,EAC3B,SAAS,CAAC,EAAE;IACV,0EAA0E;IAC1E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,qFAAqF;IACrF,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CACnC,GACA,OAAO,CAAC,uBAAuB,CAAC,CA+IlC"}
|
package/dist/server.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import packageJson from '../package.json' with { type: 'json' };
|
|
3
3
|
import { requestStateCodec } from "./core/input-required.js";
|
|
4
|
-
import { Logger } from "./core/observability.js";
|
|
4
|
+
import { Logger, voidSend } from "./core/observability.js";
|
|
5
5
|
import { PathGuard } from "./core/path.js";
|
|
6
6
|
import { PageSnapshotStore, ResourceStore } from "./core/store.js";
|
|
7
|
-
import { MAX_TOOL_INPUT_ELEMENTS } from "./core/util.js";
|
|
7
|
+
import { MAX_OUTBOUND_RESULT_BYTES, MAX_TOOL_INPUT_ELEMENTS } from "./core/util.js";
|
|
8
8
|
import { INSTRUCTIONS_SUMMARY, INSTRUCTIONS_URI } from "./instructions.js";
|
|
9
9
|
import { registerPrompts } from "./prompts.js";
|
|
10
10
|
import { registerResources } from "./resources.js";
|
|
@@ -33,7 +33,7 @@ export async function createServer(options = {}, extraDeps) {
|
|
|
33
33
|
const capabilities = {
|
|
34
34
|
resources: { subscribe: !legacyHttp, listChanged: !legacyHttp },
|
|
35
35
|
};
|
|
36
|
-
const cacheScope = extraDeps?.
|
|
36
|
+
const cacheScope = extraDeps?.cacheScope ?? 'public';
|
|
37
37
|
const serverConfig = {
|
|
38
38
|
capabilities,
|
|
39
39
|
enforceStrictCapabilities: true,
|
|
@@ -92,17 +92,16 @@ export async function createServer(options = {}, extraDeps) {
|
|
|
92
92
|
Logger.debug('Unhandled client notification', { method: notification.method });
|
|
93
93
|
return Promise.resolve();
|
|
94
94
|
};
|
|
95
|
-
// A failed send means the connection went away; nothing to recover.
|
|
96
95
|
const resourceStore = extraDeps?.resourceStore ??
|
|
97
96
|
new ResourceStore(() => {
|
|
98
97
|
if (extraDeps?.notifier) {
|
|
99
98
|
extraDeps.notifier.resourcesChanged();
|
|
100
99
|
return;
|
|
101
100
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
101
|
+
voidSend(server.server.sendResourceListChanged(), 'resource list_changed');
|
|
102
|
+
},
|
|
103
|
+
// A tool hands out no link that this connection's resources/read refuses.
|
|
104
|
+
extraDeps?.transport === 'stdio' ? { maxReadResultBytes: MAX_OUTBOUND_RESULT_BYTES } : {});
|
|
106
105
|
const pageStore = extraDeps?.pageStore ?? new PageSnapshotStore();
|
|
107
106
|
const pathGuard = extraDeps?.pathGuard ?? new PathGuard(options);
|
|
108
107
|
// Recompute once per guard, keyed on the guard's own state rather than on
|