@j0hanz/filesystem-mcp 1.19.0 → 2.0.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 +333 -838
- package/dist/cli-help.d.ts +3 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +169 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli.d.ts +28 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +168 -123
- package/dist/cli.js.map +1 -0
- package/dist/core/concurrency.d.ts +70 -0
- package/dist/core/concurrency.d.ts.map +1 -0
- package/dist/core/concurrency.js +173 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/config.d.ts +19 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +11 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/cursor.d.ts +23 -0
- package/dist/core/cursor.d.ts.map +1 -0
- package/dist/core/cursor.js +62 -0
- package/dist/core/cursor.js.map +1 -0
- package/dist/core/errors.d.ts +96 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +356 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/file-uri.d.ts +24 -0
- package/dist/core/file-uri.d.ts.map +1 -0
- package/dist/core/file-uri.js +52 -0
- package/dist/core/file-uri.js.map +1 -0
- package/dist/core/fmt.d.ts +38 -0
- package/dist/core/fmt.d.ts.map +1 -0
- package/dist/core/fmt.js +109 -0
- package/dist/core/fmt.js.map +1 -0
- package/dist/core/fs.d.ts +108 -0
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/fs.js +243 -0
- package/dist/core/fs.js.map +1 -0
- package/dist/core/glob.d.ts +41 -0
- package/dist/core/glob.d.ts.map +1 -0
- package/dist/core/glob.js +399 -0
- package/dist/core/glob.js.map +1 -0
- package/dist/core/input-required.d.ts +147 -0
- package/dist/core/input-required.d.ts.map +1 -0
- package/dist/core/input-required.js +261 -0
- package/dist/core/input-required.js.map +1 -0
- package/dist/core/mime.d.ts +15 -0
- package/dist/core/mime.d.ts.map +1 -0
- package/dist/core/mime.js +188 -0
- package/dist/core/mime.js.map +1 -0
- package/dist/core/observability.d.ts +10 -0
- package/dist/core/observability.d.ts.map +1 -0
- package/dist/core/observability.js +76 -0
- package/dist/core/observability.js.map +1 -0
- package/dist/core/page-store.d.ts +41 -0
- package/dist/core/page-store.d.ts.map +1 -0
- package/dist/core/page-store.js +74 -0
- package/dist/core/page-store.js.map +1 -0
- package/dist/core/path-completer.d.ts +10 -0
- package/dist/core/path-completer.d.ts.map +1 -0
- package/dist/core/path-completer.js +227 -0
- package/dist/core/path-completer.js.map +1 -0
- package/dist/core/path-discovery.d.ts +8 -0
- package/dist/core/path-discovery.d.ts.map +1 -0
- package/dist/core/path-discovery.js +105 -0
- package/dist/core/path-discovery.js.map +1 -0
- package/dist/core/path-utils.d.ts +9 -0
- package/dist/core/path-utils.d.ts.map +1 -0
- package/dist/core/path-utils.js +121 -0
- package/dist/core/path-utils.js.map +1 -0
- package/dist/core/path.d.ts +148 -0
- package/dist/core/path.d.ts.map +1 -0
- package/dist/core/path.js +674 -0
- package/dist/core/path.js.map +1 -0
- package/dist/core/primitives.d.ts +23 -0
- package/dist/core/primitives.d.ts.map +1 -0
- package/dist/core/primitives.js +54 -0
- package/dist/core/primitives.js.map +1 -0
- package/dist/core/read.d.ts +74 -0
- package/dist/core/read.d.ts.map +1 -0
- package/dist/core/read.js +562 -0
- package/dist/core/read.js.map +1 -0
- package/dist/core/schema.d.ts +139 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +345 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/search.d.ts +100 -0
- package/dist/core/search.d.ts.map +1 -0
- package/dist/core/search.js +248 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/sensitive.d.ts +6 -0
- package/dist/core/sensitive.d.ts.map +1 -0
- package/dist/core/sensitive.js +142 -0
- package/dist/core/sensitive.js.map +1 -0
- package/dist/core/store.d.ts +57 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +162 -0
- package/dist/core/store.js.map +1 -0
- package/dist/core/util.d.ts +26 -0
- package/dist/core/util.d.ts.map +1 -0
- package/dist/core/util.js +66 -0
- package/dist/core/util.js.map +1 -0
- package/dist/core/watcher-registry.d.ts +68 -0
- package/dist/core/watcher-registry.d.ts.map +1 -0
- package/dist/core/watcher-registry.js +312 -0
- package/dist/core/watcher-registry.js.map +1 -0
- package/dist/http-policy.d.ts +121 -0
- package/dist/http-policy.d.ts.map +1 -0
- package/dist/http-policy.js +340 -0
- package/dist/http-policy.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -41
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +71 -0
- package/dist/instructions.js.map +1 -0
- package/dist/prompts.d.ts +8 -6
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +74 -168
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +73 -9
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +392 -163
- package/dist/resources.js.map +1 -0
- package/dist/server.d.ts +62 -1
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +139 -1
- package/dist/server.js.map +1 -0
- package/dist/tools/batch.d.ts +89 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +157 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/create.d.ts +2 -0
- package/dist/tools/create.d.ts.map +1 -0
- package/dist/tools/create.js +142 -0
- package/dist/tools/create.js.map +1 -0
- package/dist/tools/define.d.ts +110 -0
- package/dist/tools/define.d.ts.map +1 -0
- package/dist/tools/define.js +394 -0
- package/dist/tools/define.js.map +1 -0
- package/dist/tools/delete-file.d.ts +2 -4
- package/dist/tools/delete-file.d.ts.map +1 -0
- package/dist/tools/delete-file.js +330 -87
- package/dist/tools/delete-file.js.map +1 -0
- package/dist/tools/diff.d.ts +2 -0
- package/dist/tools/diff.d.ts.map +1 -0
- package/dist/tools/diff.js +94 -0
- package/dist/tools/diff.js.map +1 -0
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +442 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/index.d.ts +26 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list.d.ts +2 -0
- package/dist/tools/list.d.ts.map +1 -0
- package/dist/tools/list.js +291 -0
- package/dist/tools/list.js.map +1 -0
- package/dist/tools/move.d.ts +2 -0
- package/dist/tools/move.d.ts.map +1 -0
- package/dist/tools/move.js +321 -0
- package/dist/tools/move.js.map +1 -0
- package/dist/tools/patch.d.ts +2 -0
- package/dist/tools/patch.d.ts.map +1 -0
- package/dist/tools/patch.js +155 -0
- package/dist/tools/patch.js.map +1 -0
- package/dist/tools/progress.d.ts +52 -0
- package/dist/tools/progress.d.ts.map +1 -0
- package/dist/tools/progress.js +172 -0
- package/dist/tools/progress.js.map +1 -0
- package/dist/tools/read.d.ts +2 -4
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +398 -152
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/replace-in-files.d.ts +2 -4
- package/dist/tools/replace-in-files.d.ts.map +1 -0
- package/dist/tools/replace-in-files.js +435 -264
- package/dist/tools/replace-in-files.js.map +1 -0
- package/dist/tools/roots.d.ts +2 -4
- package/dist/tools/roots.d.ts.map +1 -0
- package/dist/tools/roots.js +29 -52
- package/dist/tools/roots.js.map +1 -0
- package/dist/tools/search-content.d.ts +2 -4
- package/dist/tools/search-content.d.ts.map +1 -0
- package/dist/tools/search-content.js +262 -228
- package/dist/tools/search-content.js.map +1 -0
- package/dist/tools/search-files.d.ts +2 -4
- package/dist/tools/search-files.d.ts.map +1 -0
- package/dist/tools/search-files.js +162 -161
- package/dist/tools/search-files.js.map +1 -0
- package/dist/tools/stat.d.ts +2 -4
- package/dist/tools/stat.d.ts.map +1 -0
- package/dist/tools/stat.js +202 -62
- package/dist/tools/stat.js.map +1 -0
- package/dist/transport/http.d.ts +5 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +276 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/shared.d.ts +45 -0
- package/dist/transport/shared.d.ts.map +1 -0
- package/dist/transport/shared.js +65 -0
- package/dist/transport/shared.js.map +1 -0
- package/dist/transport/stdio.d.ts +32 -0
- package/dist/transport/stdio.d.ts.map +1 -0
- package/dist/transport/stdio.js +279 -0
- package/dist/transport/stdio.js.map +1 -0
- package/dist/transport.d.ts +5 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +4 -0
- package/dist/transport.js.map +1 -0
- package/package.json +37 -46
- package/dist/assets/logo.svg +0 -35
- package/dist/completions.d.ts +0 -2
- package/dist/completions.js +0 -572
- package/dist/config.d.ts +0 -119
- package/dist/config.js +0 -31
- package/dist/lib/abort.d.ts +0 -7
- package/dist/lib/abort.js +0 -81
- package/dist/lib/constants.d.ts +0 -42
- package/dist/lib/constants.js +0 -313
- package/dist/lib/errors.d.ts +0 -28
- package/dist/lib/errors.js +0 -301
- package/dist/lib/file-operations/core.d.ts +0 -53
- package/dist/lib/file-operations/core.js +0 -144
- package/dist/lib/file-operations/metadata.d.ts +0 -77
- package/dist/lib/file-operations/metadata.js +0 -878
- package/dist/lib/file-operations/search.d.ts +0 -46
- package/dist/lib/file-operations/search.js +0 -1194
- package/dist/lib/file-operations/traversal.d.ts +0 -40
- package/dist/lib/file-operations/traversal.js +0 -310
- package/dist/lib/fs-helpers.d.ts +0 -46
- package/dist/lib/fs-helpers.js +0 -606
- package/dist/lib/globs.d.ts +0 -2
- package/dist/lib/globs.js +0 -19
- package/dist/lib/logger.d.ts +0 -27
- package/dist/lib/logger.js +0 -91
- package/dist/lib/observability.d.ts +0 -33
- package/dist/lib/observability.js +0 -373
- package/dist/lib/paths.d.ts +0 -35
- package/dist/lib/paths.js +0 -634
- package/dist/lib/resource-store.d.ts +0 -28
- package/dist/lib/resource-store.js +0 -195
- package/dist/lib/utils.d.ts +0 -7
- package/dist/lib/utils.js +0 -37
- package/dist/lib/zod-codecs.d.ts +0 -2
- package/dist/lib/zod-codecs.js +0 -18
- package/dist/pkg-info.d.ts +0 -7
- package/dist/pkg-info.js +0 -9
- package/dist/resources/generated-instructions.d.ts +0 -1
- package/dist/resources/generated-instructions.js +0 -105
- package/dist/resources/tool-catalog.d.ts +0 -2
- package/dist/resources/tool-catalog.js +0 -78
- package/dist/resources/tool-info.d.ts +0 -9
- package/dist/resources/tool-info.js +0 -206
- package/dist/resources/workflows.d.ts +0 -1
- package/dist/resources/workflows.js +0 -72
- package/dist/schemas.d.ts +0 -505
- package/dist/schemas.js +0 -676
- package/dist/server/bootstrap.d.ts +0 -6
- package/dist/server/bootstrap.js +0 -563
- package/dist/server/event-store.d.ts +0 -18
- package/dist/server/event-store.js +0 -71
- package/dist/server/roots-manager.d.ts +0 -28
- package/dist/server/roots-manager.js +0 -227
- package/dist/server/task-store.d.ts +0 -10
- package/dist/server/task-store.js +0 -91
- package/dist/tools/apply-patch.d.ts +0 -4
- package/dist/tools/apply-patch.js +0 -235
- package/dist/tools/calculate-hash.d.ts +0 -4
- package/dist/tools/calculate-hash.js +0 -187
- package/dist/tools/contract.d.ts +0 -50
- package/dist/tools/contract.js +0 -1
- package/dist/tools/create-directory.d.ts +0 -4
- package/dist/tools/create-directory.js +0 -68
- package/dist/tools/diff-files.d.ts +0 -4
- package/dist/tools/diff-files.js +0 -136
- package/dist/tools/edit-file.d.ts +0 -4
- package/dist/tools/edit-file.js +0 -256
- package/dist/tools/icons.d.ts +0 -15
- package/dist/tools/icons.js +0 -24
- package/dist/tools/list-directory.d.ts +0 -4
- package/dist/tools/list-directory.js +0 -218
- package/dist/tools/move-file.d.ts +0 -4
- package/dist/tools/move-file.js +0 -178
- package/dist/tools/read-multiple.d.ts +0 -4
- package/dist/tools/read-multiple.js +0 -207
- package/dist/tools/shared.d.ts +0 -172
- package/dist/tools/shared.js +0 -599
- package/dist/tools/stat-many.d.ts +0 -4
- package/dist/tools/stat-many.js +0 -104
- package/dist/tools/task-support.d.ts +0 -29
- package/dist/tools/task-support.js +0 -472
- package/dist/tools/tree.d.ts +0 -4
- package/dist/tools/tree.js +0 -102
- package/dist/tools/write-file.d.ts +0 -4
- package/dist/tools/write-file.js +0 -60
- package/dist/tools.d.ts +0 -5
- package/dist/tools.js +0 -54
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { CLIENT_CAPABILITIES_META_KEY, fromJsonSchema, isInputRequiredResult, } from '@modelcontextprotocol/server';
|
|
2
|
+
import * as z from 'zod/v4';
|
|
3
|
+
import { ErrorCode, formatUnknownErrorMessage, Problem } from '../core/errors.js';
|
|
4
|
+
import { plainMessage } from '../core/fmt.js';
|
|
5
|
+
import { GuardedFileSystem } from '../core/fs.js';
|
|
6
|
+
import { confirmInput, multiSelectInput, pendingRoundTrip, readAcceptedConfirm, readAcceptedMultiChoice, } from '../core/input-required.js';
|
|
7
|
+
import { Logger } from '../core/observability.js';
|
|
8
|
+
import { isSamePath } from '../core/path-utils.js';
|
|
9
|
+
import { McpProgressSink, ProgressSession } from './progress.js';
|
|
10
|
+
function toToolCtx(ctx, deps) {
|
|
11
|
+
if (!ctx) {
|
|
12
|
+
const signal = new AbortController().signal;
|
|
13
|
+
return {
|
|
14
|
+
signal,
|
|
15
|
+
fs: new GuardedFileSystem(deps.pathGuard),
|
|
16
|
+
pageStore: deps.pageStore,
|
|
17
|
+
resourceStore: deps.resourceStore,
|
|
18
|
+
server: deps.server,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// Envelope first, accessor second — the two eras carry this differently.
|
|
22
|
+
// A modern request states the capabilities in its own `_meta` envelope; a
|
|
23
|
+
// legacy connection fixed them at `initialize` and has no envelope at all.
|
|
24
|
+
// The deprecated accessor still answers for both today (the SDK backfills it
|
|
25
|
+
// per request from the validated envelope on modern instances), but reading
|
|
26
|
+
// the envelope directly is the supported path, so it leads: when the accessor
|
|
27
|
+
// goes, legacy degrades to "cannot tell", which fails open to the behavior
|
|
28
|
+
// that predates this check.
|
|
29
|
+
//
|
|
30
|
+
// The cast is the SDK's shipped `RequestMetaEnvelope` declaration collapsing
|
|
31
|
+
// to `{}` — the reserved keys survive at runtime but not in the .d.ts, so the
|
|
32
|
+
// key constant cannot index the declared type.
|
|
33
|
+
const envelope = ctx.mcpReq.envelope;
|
|
34
|
+
const clientCapabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY] ??
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- the only source of client capabilities on a legacy connection, where no envelope exists.
|
|
36
|
+
deps.server.server.getClientCapabilities();
|
|
37
|
+
return {
|
|
38
|
+
signal: ctx.mcpReq.signal,
|
|
39
|
+
...(ctx.sessionId ? { sessionId: ctx.sessionId } : {}),
|
|
40
|
+
...(ctx.http?.authInfo ? { authInfo: ctx.http.authInfo } : {}),
|
|
41
|
+
...(ctx.mcpReq._meta ? { _meta: ctx.mcpReq._meta } : {}),
|
|
42
|
+
fs: new GuardedFileSystem(deps.pathGuard),
|
|
43
|
+
pageStore: deps.pageStore,
|
|
44
|
+
resourceStore: deps.resourceStore,
|
|
45
|
+
sendNotification: async (notification) => ctx.mcpReq.notify(notification),
|
|
46
|
+
inputResponses: ctx.mcpReq.inputResponses,
|
|
47
|
+
requestState: ctx.mcpReq.requestState,
|
|
48
|
+
...(clientCapabilities ? { clientCapabilities } : {}),
|
|
49
|
+
server: deps.server,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function resolveProgressCtx(def, args) {
|
|
53
|
+
if (!def.progress)
|
|
54
|
+
return { label: def.title };
|
|
55
|
+
try {
|
|
56
|
+
return def.progress(args);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
Logger.warn(`resolveProgressCtx: ${def.name}.progress threw: ${String(err)}`);
|
|
60
|
+
return { label: def.title };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function composeSignal(base, timeoutMs) {
|
|
64
|
+
if (!timeoutMs)
|
|
65
|
+
return base;
|
|
66
|
+
return AbortSignal.any([base, AbortSignal.timeout(timeoutMs)]);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A batch result where every path failed. The call produced no work at all, so
|
|
70
|
+
* it is a failed call — `isError` must say so. Partial failure is deliberately
|
|
71
|
+
* NOT an error: the per-path entries carry which paths failed and the succeeded
|
|
72
|
+
* ones really were done.
|
|
73
|
+
*
|
|
74
|
+
* Two result shapes reach here. Most batch tools carry a `summary`. `create` and
|
|
75
|
+
* `move` do not: they publish `{files|moves, failures, skipped}` (see
|
|
76
|
+
* create.ts's CreateOutputSchema and move.ts's MoveOutputSchema), so a
|
|
77
|
+
* summary-only check reported a fully-denied write as a successful call. Both
|
|
78
|
+
* shapes are handled here rather than by growing the two output schemas, which
|
|
79
|
+
* are the client-facing contract.
|
|
80
|
+
*/
|
|
81
|
+
function isTotalBatchFailure(structured) {
|
|
82
|
+
if (typeof structured !== 'object' || structured === null)
|
|
83
|
+
return false;
|
|
84
|
+
const result = structured;
|
|
85
|
+
const { summary } = result;
|
|
86
|
+
if (typeof summary?.total === 'number') {
|
|
87
|
+
return summary.total > 0 && summary.failed === summary.total;
|
|
88
|
+
}
|
|
89
|
+
const len = (value) => (Array.isArray(value) ? value.length : 0);
|
|
90
|
+
if (len(result.failures) === 0)
|
|
91
|
+
return false;
|
|
92
|
+
// A skipped entry is work the caller asked to skip, not work that failed —
|
|
93
|
+
// a self-move that was silently dropped means the call did something.
|
|
94
|
+
return len(result.files) + len(result.moves) + len(result.skipped) === 0;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The spec asks a structured result to *also* carry the JSON as a text block,
|
|
98
|
+
* for clients that read only `content`. Where a tool supplies its own `text`
|
|
99
|
+
* (list's ASCII tree, read's file) that serves such a client better, so it
|
|
100
|
+
* wins; tools with no `text` still fall back to the JSON.
|
|
101
|
+
*/
|
|
102
|
+
function buildSuccessResponse(result) {
|
|
103
|
+
const text = result.text ?? JSON.stringify(result.structured);
|
|
104
|
+
const content = [{ type: 'text', text }, ...(result.resources ?? [])];
|
|
105
|
+
return {
|
|
106
|
+
content,
|
|
107
|
+
structuredContent: result.structured,
|
|
108
|
+
...(isTotalBatchFailure(result.structured) ? { isError: true } : {}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
class ToolExecutor {
|
|
112
|
+
signal;
|
|
113
|
+
def;
|
|
114
|
+
parsedArgs;
|
|
115
|
+
toolCtx;
|
|
116
|
+
#progressClosed = false;
|
|
117
|
+
#progressCtx;
|
|
118
|
+
#mcpSink;
|
|
119
|
+
#progressSession;
|
|
120
|
+
constructor(toolName, ctx, def, parsedArgs) {
|
|
121
|
+
this.def = def;
|
|
122
|
+
this.parsedArgs = parsedArgs;
|
|
123
|
+
this.signal = composeSignal(ctx.signal, def.timeoutMs);
|
|
124
|
+
this.#progressCtx = resolveProgressCtx(def, parsedArgs);
|
|
125
|
+
const sinks = [];
|
|
126
|
+
if (ctx._meta?.progressToken !== undefined && ctx.sendNotification !== undefined) {
|
|
127
|
+
this.#mcpSink = new McpProgressSink(toolName, ctx._meta.progressToken, ctx.sendNotification);
|
|
128
|
+
sinks.push(this.#mcpSink);
|
|
129
|
+
}
|
|
130
|
+
const isTest = process.env['NODE_ENV'] === 'test' || process.execArgv.includes('--test');
|
|
131
|
+
this.#progressSession = new ProgressSession({
|
|
132
|
+
label: this.#progressCtx.label,
|
|
133
|
+
sinks,
|
|
134
|
+
...(isTest ? { rateLimitMs: 0 } : {}),
|
|
135
|
+
});
|
|
136
|
+
this.toolCtx = {
|
|
137
|
+
...ctx,
|
|
138
|
+
signal: this.signal,
|
|
139
|
+
log: (level, data, logger) => {
|
|
140
|
+
const msg = typeof data === 'string' ? data : String(data);
|
|
141
|
+
const prefix = logger ? `[${logger}] ` : '';
|
|
142
|
+
Logger.emit(level, `${prefix}${msg}`);
|
|
143
|
+
},
|
|
144
|
+
onProgress: (p) => {
|
|
145
|
+
this.#tick(p);
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
#tick(p) {
|
|
150
|
+
if (this.#progressClosed)
|
|
151
|
+
return;
|
|
152
|
+
const tickCtx = {
|
|
153
|
+
...this.#progressCtx,
|
|
154
|
+
current: p.current,
|
|
155
|
+
...(p.total !== undefined ? { total: p.total } : {}),
|
|
156
|
+
};
|
|
157
|
+
this.#progressSession.set({ ...p, message: plainMessage('tick', tickCtx) });
|
|
158
|
+
}
|
|
159
|
+
async #closeWithDone(message) {
|
|
160
|
+
this.#progressClosed = true;
|
|
161
|
+
this.#progressSession.complete(message);
|
|
162
|
+
if (this.#mcpSink)
|
|
163
|
+
await this.#mcpSink.flush();
|
|
164
|
+
}
|
|
165
|
+
async #closeWithFail(error, message) {
|
|
166
|
+
this.#progressClosed = true;
|
|
167
|
+
this.#progressSession.fail(error, message);
|
|
168
|
+
if (this.#mcpSink)
|
|
169
|
+
await this.#mcpSink.flush();
|
|
170
|
+
}
|
|
171
|
+
async #flushProgress() {
|
|
172
|
+
if (this.#mcpSink)
|
|
173
|
+
await this.#mcpSink.flush();
|
|
174
|
+
}
|
|
175
|
+
async completeProgress(result) {
|
|
176
|
+
const doneCtx = this.def.progressDone
|
|
177
|
+
? { ...this.#progressCtx, ...this.def.progressDone(this.parsedArgs, result) }
|
|
178
|
+
: this.#progressCtx;
|
|
179
|
+
await this.#closeWithDone(plainMessage('done', doneCtx));
|
|
180
|
+
}
|
|
181
|
+
async failProgress(error) {
|
|
182
|
+
const errMsg = formatUnknownErrorMessage(error);
|
|
183
|
+
const message = plainMessage('fail', { ...this.#progressCtx, error: errMsg });
|
|
184
|
+
await this.#closeWithFail(error, message);
|
|
185
|
+
const { text: errorText } = Problem.toText(error, this.def.defaultErrorCode ?? ErrorCode.UNKNOWN);
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: 'text', text: errorText }],
|
|
188
|
+
isError: true,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Access-grant pre-check (R7/R8/R9). Before `run` touches the filesystem,
|
|
193
|
+
* ask PathGuard which of the tool's declared paths are out-of-root and
|
|
194
|
+
* grantable. If any are and this round carries no verified `requestState`,
|
|
195
|
+
* return `input_required` (nothing is read or written — R7). On retry, the
|
|
196
|
+
* verified state must bind this grant set (R9); apply each accepted grant for
|
|
197
|
+
* the session (R8) and proceed. A declined or ungrantable path is left for
|
|
198
|
+
* `validateAccess` to fail closed during the operation. Returns `undefined`
|
|
199
|
+
* when no round-trip is needed and any applicable grants have been applied.
|
|
200
|
+
*/
|
|
201
|
+
async precheckGrant() {
|
|
202
|
+
const extract = this.def.accessPaths;
|
|
203
|
+
if (!extract)
|
|
204
|
+
return undefined;
|
|
205
|
+
const paths = extract(this.parsedArgs);
|
|
206
|
+
if (paths.length === 0)
|
|
207
|
+
return undefined;
|
|
208
|
+
const grantDirs = await this.toolCtx.fs.pathGuard.precheckAccess(paths);
|
|
209
|
+
if (grantDirs.length === 0)
|
|
210
|
+
return undefined;
|
|
211
|
+
// One grant dir: a single boolean confirm. Multiple: one multi-select
|
|
212
|
+
// elicitation so the client picks the subset to allow in one round-trip
|
|
213
|
+
// (N dirs → 1 round-trip, not N). `pendingRoundTrip` still binds
|
|
214
|
+
// `paths: grantDirs` sorted, so R9 path-binding is unaffected.
|
|
215
|
+
const multi = grantDirs.length > 1;
|
|
216
|
+
const round = await pendingRoundTrip({
|
|
217
|
+
op: 'grant',
|
|
218
|
+
pending: grantDirs,
|
|
219
|
+
requestState: this.toolCtx.requestState,
|
|
220
|
+
clientCapabilities: this.toolCtx.clientCapabilities,
|
|
221
|
+
buildInputs: (dirs) => multi
|
|
222
|
+
? [
|
|
223
|
+
multiSelectInput('grant', 'Grant filesystem access to these directories? Select the ones to allow.', dirs.map((d) => ({ value: d, title: d }))),
|
|
224
|
+
]
|
|
225
|
+
: dirs.map((dir, i) => confirmInput(`confirm_${i}`, `Grant filesystem access to "${dir}"?`)),
|
|
226
|
+
});
|
|
227
|
+
if (round !== undefined)
|
|
228
|
+
return round;
|
|
229
|
+
// Apply accepted grants for the session (R8). Declined/missing dirs are
|
|
230
|
+
// skipped here; their paths fail with ACCESS_DENIED during the operation.
|
|
231
|
+
//
|
|
232
|
+
// No list-changed notification follows: a grant widens the allowed roots,
|
|
233
|
+
// and no resource list reads them. The instructions resource has a fixed
|
|
234
|
+
// URI, the result template lists the ResourceStore, and the file template
|
|
235
|
+
// lists nothing at all (see resources.ts). Notifying here only bought the
|
|
236
|
+
// client a round trip to re-fetch a list that could not have changed.
|
|
237
|
+
//
|
|
238
|
+
// `readAcceptedMultiChoice` does NOT validate against the offered choices
|
|
239
|
+
// (attacker-controlled inputResponses on re-entry). Only grant dirs that
|
|
240
|
+
// were actually offered by precheckAccess — a value outside grantDirs is
|
|
241
|
+
// ignored and fails closed in validateAccess. The single-select path reads
|
|
242
|
+
// `confirm_${i}` for `grantDirs[i]`, so the isSamePath filter is a no-op
|
|
243
|
+
// there; it only bites for the multi-select array.
|
|
244
|
+
const accepted = multi
|
|
245
|
+
? (readAcceptedMultiChoice(this.toolCtx.inputResponses, 'grant') ?? [])
|
|
246
|
+
: grantDirs.filter((_dir, i) => readAcceptedConfirm(this.toolCtx.inputResponses, `confirm_${i}`));
|
|
247
|
+
for (const dir of accepted) {
|
|
248
|
+
if (!grantDirs.some((g) => isSamePath(g, dir)))
|
|
249
|
+
continue;
|
|
250
|
+
await this.toolCtx.fs.pathGuard.applyGrant(dir);
|
|
251
|
+
}
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
async execute() {
|
|
255
|
+
const runTool = async () => {
|
|
256
|
+
try {
|
|
257
|
+
// Access-grant pre-check before any filesystem touch (R7/R8/R9). A
|
|
258
|
+
// grant input_required short-circuits here (progress paused, not
|
|
259
|
+
// finished); an R9 mismatch throws, which this catch surfaces as an
|
|
260
|
+
// isError tool result like every other handler failure — not a raw
|
|
261
|
+
// JSON-RPC error (GRANT-1 impact #2).
|
|
262
|
+
const grantRequired = await this.precheckGrant();
|
|
263
|
+
if (grantRequired !== undefined)
|
|
264
|
+
return grantRequired;
|
|
265
|
+
const result = await this.def.run(this.parsedArgs, this.toolCtx);
|
|
266
|
+
// input_required is a return value, not a completed call: the client
|
|
267
|
+
// retries the same tools/call carrying inputResponses, and this
|
|
268
|
+
// handler re-enters from the top. Skip the progress "done" close and
|
|
269
|
+
// return it verbatim — progress is paused, not finished.
|
|
270
|
+
if (isInputRequiredResult(result)) {
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
await this.completeProgress(result.structured);
|
|
274
|
+
return buildSuccessResponse(result);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
return await this.failProgress(error);
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
await this.#flushProgress();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
return runTool();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function createServerToolHandler(def, deps) {
|
|
287
|
+
return async (args, ctx) => new ToolExecutor(def.name, toToolCtx(ctx, deps), def, args).execute();
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Validate with Zod while publishing the precomputed JSON Schema: clients get
|
|
291
|
+
* draft-2020-12, handlers get Zod's coercions, defaults and error messages.
|
|
292
|
+
*
|
|
293
|
+
* Each validator instance is built for exactly one schema and handed straight
|
|
294
|
+
* to `fromJsonSchema(thatSchema, ...)`, which calls `getValidator` once with
|
|
295
|
+
* the same schema — so the argument is redundant here and deliberately ignored.
|
|
296
|
+
* Reusing one instance across schemas would silently validate against `schema`.
|
|
297
|
+
*/
|
|
298
|
+
function zodJsonSchemaValidator(schema) {
|
|
299
|
+
return {
|
|
300
|
+
getValidator() {
|
|
301
|
+
return (input) => {
|
|
302
|
+
const result = schema.safeParse(input);
|
|
303
|
+
if (result.success) {
|
|
304
|
+
return {
|
|
305
|
+
valid: true,
|
|
306
|
+
data: result.data,
|
|
307
|
+
errorMessage: undefined,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
valid: false,
|
|
312
|
+
data: undefined,
|
|
313
|
+
errorMessage: z.prettifyError(result.error),
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Generate the wire copy of a schema for `tools/list`. The Zod schema keeps
|
|
321
|
+
* every constraint for runtime validation; the `override` pass only trims what
|
|
322
|
+
* the wire copy costs every client at session start:
|
|
323
|
+
*
|
|
324
|
+
* - `$schema` and `title` carry no information the host uses.
|
|
325
|
+
* - `maximum: Number.MAX_SAFE_INTEGER` is zod's int() artifact, not a bound.
|
|
326
|
+
* - `examples` is dropped in both directions: on output it describes a field the
|
|
327
|
+
* server itself fills in, and on input every description that carried one
|
|
328
|
+
* already spells the same example out inline, so the keyword paid twice.
|
|
329
|
+
*
|
|
330
|
+
* Output `description`s are NOT dropped. They were, and the result was a wire
|
|
331
|
+
* contract the model had to guess at: `delete` returns `path` XOR `paths`,
|
|
332
|
+
* `edit.diff` appears only under dryRun, `read.value` has no required field at
|
|
333
|
+
* all. None of that is inferable from types alone.
|
|
334
|
+
*
|
|
335
|
+
* No shared subschema carries a `.meta({ id })`, so zod inlines every one of
|
|
336
|
+
* them and the emitted document has no `$defs`/`$ref` to dereference.
|
|
337
|
+
*/
|
|
338
|
+
function toDraft202012(schema, io) {
|
|
339
|
+
const generated = z.toJSONSchema(schema, {
|
|
340
|
+
target: 'draft-2020-12',
|
|
341
|
+
io,
|
|
342
|
+
override: ({ jsonSchema }) => {
|
|
343
|
+
const node = jsonSchema;
|
|
344
|
+
delete node['title'];
|
|
345
|
+
if (node['maximum'] === Number.MAX_SAFE_INTEGER)
|
|
346
|
+
delete node['maximum'];
|
|
347
|
+
delete node['examples'];
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
delete generated['$schema'];
|
|
351
|
+
return generated;
|
|
352
|
+
}
|
|
353
|
+
export function defineTool(def) {
|
|
354
|
+
const inputJsonSchema = toDraft202012(def.input, 'input');
|
|
355
|
+
const outputJsonSchema = toDraft202012(def.output, 'output');
|
|
356
|
+
// Nothing here depends on `deps`, so it is built once per tool definition
|
|
357
|
+
// rather than once per `register` — the HTTP leg registers every tool afresh
|
|
358
|
+
// on each request.
|
|
359
|
+
// Published annotations are derived, not passed through. `readOnlyHint` is
|
|
360
|
+
// load-bearing (MUTATING_TOOL_NAMES derives the --read-only gate from it) and
|
|
361
|
+
// `openWorldHint: false` is a real claim for a filesystem server. The other
|
|
362
|
+
// two describe behavior a read-only tool cannot have, and restate the default
|
|
363
|
+
// for a mutating one — 29 tokens per tool for nothing a client acts on.
|
|
364
|
+
const publishedAnnotations = {
|
|
365
|
+
readOnlyHint: def.annotations.readOnlyHint,
|
|
366
|
+
openWorldHint: def.annotations.openWorldHint ?? false,
|
|
367
|
+
...(def.annotations.readOnlyHint
|
|
368
|
+
? {}
|
|
369
|
+
: { destructiveHint: def.annotations.destructiveHint ?? true }),
|
|
370
|
+
};
|
|
371
|
+
const toolDefShape = {
|
|
372
|
+
title: def.title,
|
|
373
|
+
description: def.description,
|
|
374
|
+
inputSchema: fromJsonSchema(inputJsonSchema, zodJsonSchemaValidator(def.input)),
|
|
375
|
+
// The validator is built either way — results are still checked against the
|
|
376
|
+
// Zod object — but the schema reaches the wire only where the shape is not
|
|
377
|
+
// inferable from the description plus the text content.
|
|
378
|
+
...(def.publishOutputSchema
|
|
379
|
+
? {
|
|
380
|
+
outputSchema: fromJsonSchema(outputJsonSchema, zodJsonSchemaValidator(def.output)),
|
|
381
|
+
}
|
|
382
|
+
: {}),
|
|
383
|
+
annotations: publishedAnnotations,
|
|
384
|
+
};
|
|
385
|
+
const tool = {
|
|
386
|
+
name: def.name,
|
|
387
|
+
annotations: def.annotations,
|
|
388
|
+
register(deps) {
|
|
389
|
+
return deps.server.registerTool(def.name, toolDefShape, createServerToolHandler(def, deps));
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
return tool;
|
|
393
|
+
}
|
|
394
|
+
//# sourceMappingURL=define.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/tools/define.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AuthInfo,\n CallToolResult,\n ClientCapabilities,\n ContentBlock,\n InputRequiredResult,\n JsonSchemaType,\n JsonSchemaValidator,\n jsonSchemaValidator,\n McpServer,\n Notification,\n RegisteredTool,\n RequestMeta,\n RequestStateAccessor,\n ServerContext,\n ToolAnnotations,\n} from '@modelcontextprotocol/server';\nimport {\n CLIENT_CAPABILITIES_META_KEY,\n fromJsonSchema,\n isInputRequiredResult,\n} from '@modelcontextprotocol/server';\n\nimport * as z from 'zod/v4';\n\nimport { ErrorCode, formatUnknownErrorMessage, Problem } from '../core/errors.js';\nimport type { ProgressCtx } from '../core/fmt.js';\nimport { plainMessage } from '../core/fmt.js';\nimport { GuardedFileSystem } from '../core/fs.js';\nimport {\n confirmInput,\n multiSelectInput,\n pendingRoundTrip,\n readAcceptedConfirm,\n readAcceptedMultiChoice,\n} from '../core/input-required.js';\nimport { Logger } from '../core/observability.js';\nimport type { LoggingLevel } from '../core/observability.js';\nimport type { PageSnapshotStore } from '../core/page-store.js';\nimport { isSamePath } from '../core/path-utils.js';\nimport type { PathGuard } from '../core/path.js';\nimport type { ResourceStore } from '../core/store.js';\nimport type { ProgressSink } from './progress.js';\nimport { McpProgressSink, ProgressSession } from './progress.js';\n\nexport interface ToolCtx {\n readonly signal: AbortSignal;\n readonly sessionId?: string;\n readonly authInfo?: AuthInfo;\n readonly _meta?: RequestMeta | undefined;\n readonly fs: GuardedFileSystem;\n readonly pageStore: PageSnapshotStore;\n readonly resourceStore: ResourceStore | undefined;\n /** Emits a log line to stderr via `Logger.emit`, gated by `LOG_LEVEL`. */\n readonly log?: (level: LoggingLevel, data: unknown, logger?: string) => void;\n readonly sendNotification?: (notification: Notification) => Promise<void>;\n readonly onProgress?: (params: { current: number; total?: number }) => void;\n /**\n * Multi-round-trip `inputResponses` carried by a retried `tools/call`\n * (protocol revision 2026-07-28); `undefined` on the first round. Bare,\n * unvalidated client values — handlers read them through\n * `readAcceptedConfirm`.\n */\n readonly inputResponses?: Record<string, unknown> | undefined;\n /**\n * Reads the verified multi-round-trip `requestState` for the current round:\n * the decoded `PendingState` the `requestStateCodec` verified, or `undefined`\n * when the round carried no state (the first round). `undefined` when no\n * live request context is available.\n */\n readonly requestState?: RequestStateAccessor | undefined;\n /**\n * What the connected client declared it can do, or `undefined` when this\n * connection cannot say. Read by the `input_required` flows: an embedded\n * elicitation sent to a client that never declared `elicitation` is rejected\n * by the SDK with `-32021` before it reaches the wire, so the handlers check\n * first and answer with an actionable tool error instead.\n *\n * Two sources, because the eras differ: a legacy connection negotiated them\n * at `initialize` (`Server.getClientCapabilities()`), a modern one carries\n * them per request in the `_meta` envelope and never runs `initialize` at\n * all. `undefined` means \"cannot tell\" — never \"no capabilities\".\n */\n readonly clientCapabilities?: ClientCapabilities | undefined;\n readonly server?: McpServer;\n}\n\ninterface ToolDeps {\n readonly server: McpServer;\n readonly pathGuard: PathGuard;\n readonly pageStore: PageSnapshotStore;\n readonly resourceStore: ResourceStore | undefined;\n}\n\ninterface RunResult<T> {\n readonly structured: T;\n readonly text?: string;\n readonly resources?: ContentBlock[];\n}\n\n/**\n * `ToolAnnotations` with `readOnlyHint` required. It is optional in the SDK, but\n * MUTATING_TOOL_NAMES derives the `--read-only` gate from it, so a tool that\n * forgets it must be a compile error rather than a silent reclassification.\n */\ntype DeclaredAnnotations = ToolAnnotations & { readonly readOnlyHint: boolean };\n\nexport interface ToolDef<I extends z.ZodType, O extends z.ZodType> {\n readonly name: string;\n readonly title: string;\n readonly description: string;\n readonly input: I;\n readonly output: O;\n readonly annotations: DeclaredAnnotations;\n readonly timeoutMs?: number;\n readonly progress?: (args: z.infer<I>) => ProgressCtx;\n readonly progressDone?: (args: z.infer<I>, result: z.infer<O>) => Partial<ProgressCtx>;\n readonly defaultErrorCode?: ErrorCode;\n /**\n * The filesystem paths this tool will operate on, extracted from its parsed\n * args. The executor runs an access-grant pre-check over these BEFORE `run`\n * (R7): any path outside the granted roots yields an `input_required`\n * round-trip requesting a grant, and nothing is touched until the client\n * retries with an accepted grant (R8). Omit for tools that do not operate on\n * caller-supplied filesystem paths.\n */\n readonly accessPaths?: (args: z.infer<I>) => readonly string[];\n /**\n * Publish `output` as the tool's wire `outputSchema`. Off by default: the\n * spec makes the field optional and it costs more than every other part of\n * a tool entry combined. Turn it on only where the shape is not inferable —\n * a discriminated per-path union, or a field that appears under one flag and\n * not another. The Zod object still validates every result either way.\n */\n readonly publishOutputSchema?: boolean;\n readonly run: (\n args: z.infer<I>,\n ctx: ToolCtx,\n ) => Promise<RunResult<z.infer<O>> | InputRequiredResult>;\n}\n\nexport interface DefinedTool {\n readonly name: string;\n readonly annotations: DeclaredAnnotations;\n\n register(deps: ToolDeps): RegisteredTool;\n}\n\nfunction toToolCtx(\n ctx: ServerContext | undefined,\n deps: Pick<ToolDeps, 'pathGuard' | 'pageStore' | 'resourceStore' | 'server'>,\n): ToolCtx {\n if (!ctx) {\n const signal = new AbortController().signal;\n return {\n signal,\n fs: new GuardedFileSystem(deps.pathGuard),\n pageStore: deps.pageStore,\n resourceStore: deps.resourceStore,\n server: deps.server,\n };\n }\n // Envelope first, accessor second — the two eras carry this differently.\n // A modern request states the capabilities in its own `_meta` envelope; a\n // legacy connection fixed them at `initialize` and has no envelope at all.\n // The deprecated accessor still answers for both today (the SDK backfills it\n // per request from the validated envelope on modern instances), but reading\n // the envelope directly is the supported path, so it leads: when the accessor\n // goes, legacy degrades to \"cannot tell\", which fails open to the behavior\n // that predates this check.\n //\n // The cast is the SDK's shipped `RequestMetaEnvelope` declaration collapsing\n // to `{}` — the reserved keys survive at runtime but not in the .d.ts, so the\n // key constant cannot index the declared type.\n const envelope = ctx.mcpReq.envelope as Record<string, unknown> | undefined;\n const clientCapabilities =\n (envelope?.[CLIENT_CAPABILITIES_META_KEY] as ClientCapabilities | undefined) ??\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- the only source of client capabilities on a legacy connection, where no envelope exists.\n deps.server.server.getClientCapabilities();\n\n return {\n signal: ctx.mcpReq.signal,\n ...(ctx.sessionId ? { sessionId: ctx.sessionId } : {}),\n ...(ctx.http?.authInfo ? { authInfo: ctx.http.authInfo } : {}),\n ...(ctx.mcpReq._meta ? { _meta: ctx.mcpReq._meta } : {}),\n fs: new GuardedFileSystem(deps.pathGuard),\n pageStore: deps.pageStore,\n resourceStore: deps.resourceStore,\n sendNotification: async (notification) => ctx.mcpReq.notify(notification),\n inputResponses: ctx.mcpReq.inputResponses,\n requestState: ctx.mcpReq.requestState,\n ...(clientCapabilities ? { clientCapabilities } : {}),\n server: deps.server,\n };\n}\n\nfunction resolveProgressCtx<I extends z.ZodType, O extends z.ZodType>(\n def: ToolDef<I, O>,\n args: z.infer<I>,\n): ProgressCtx {\n if (!def.progress) return { label: def.title };\n try {\n return def.progress(args);\n } catch (err: unknown) {\n Logger.warn(`resolveProgressCtx: ${def.name}.progress threw: ${String(err)}`);\n return { label: def.title };\n }\n}\n\nfunction composeSignal(base: AbortSignal, timeoutMs?: number): AbortSignal {\n if (!timeoutMs) return base;\n return AbortSignal.any([base, AbortSignal.timeout(timeoutMs)]);\n}\n\n/**\n * A batch result where every path failed. The call produced no work at all, so\n * it is a failed call — `isError` must say so. Partial failure is deliberately\n * NOT an error: the per-path entries carry which paths failed and the succeeded\n * ones really were done.\n *\n * Two result shapes reach here. Most batch tools carry a `summary`. `create` and\n * `move` do not: they publish `{files|moves, failures, skipped}` (see\n * create.ts's CreateOutputSchema and move.ts's MoveOutputSchema), so a\n * summary-only check reported a fully-denied write as a successful call. Both\n * shapes are handled here rather than by growing the two output schemas, which\n * are the client-facing contract.\n */\nfunction isTotalBatchFailure(structured: unknown): boolean {\n if (typeof structured !== 'object' || structured === null) return false;\n const result = structured as {\n summary?: { total?: number; failed?: number };\n failures?: unknown;\n files?: unknown;\n moves?: unknown;\n skipped?: unknown;\n };\n\n const { summary } = result;\n if (typeof summary?.total === 'number') {\n return summary.total > 0 && summary.failed === summary.total;\n }\n\n const len = (value: unknown): number => (Array.isArray(value) ? value.length : 0);\n if (len(result.failures) === 0) return false;\n // A skipped entry is work the caller asked to skip, not work that failed —\n // a self-move that was silently dropped means the call did something.\n return len(result.files) + len(result.moves) + len(result.skipped) === 0;\n}\n\n/**\n * The spec asks a structured result to *also* carry the JSON as a text block,\n * for clients that read only `content`. Where a tool supplies its own `text`\n * (list's ASCII tree, read's file) that serves such a client better, so it\n * wins; tools with no `text` still fall back to the JSON.\n */\nfunction buildSuccessResponse<O>(result: RunResult<O>): CallToolResult {\n const text = result.text ?? JSON.stringify(result.structured);\n const content: ContentBlock[] = [{ type: 'text' as const, text }, ...(result.resources ?? [])];\n return {\n content,\n structuredContent: result.structured,\n ...(isTotalBatchFailure(result.structured) ? { isError: true } : {}),\n };\n}\n\nclass ToolExecutor<I extends z.ZodType, O extends z.ZodType> {\n readonly signal: AbortSignal;\n private readonly def: ToolDef<I, O>;\n private readonly parsedArgs: z.infer<I>;\n private readonly toolCtx: ToolCtx;\n\n #progressClosed = false;\n readonly #progressCtx: ProgressCtx;\n readonly #mcpSink?: McpProgressSink;\n readonly #progressSession: ProgressSession;\n\n constructor(toolName: string, ctx: ToolCtx, def: ToolDef<I, O>, parsedArgs: z.infer<I>) {\n this.def = def;\n this.parsedArgs = parsedArgs;\n this.signal = composeSignal(ctx.signal, def.timeoutMs);\n this.#progressCtx = resolveProgressCtx(def, parsedArgs);\n const sinks: ProgressSink[] = [];\n if (ctx._meta?.progressToken !== undefined && ctx.sendNotification !== undefined) {\n this.#mcpSink = new McpProgressSink(toolName, ctx._meta.progressToken, ctx.sendNotification);\n sinks.push(this.#mcpSink);\n }\n const isTest = process.env['NODE_ENV'] === 'test' || process.execArgv.includes('--test');\n this.#progressSession = new ProgressSession({\n label: this.#progressCtx.label,\n sinks,\n ...(isTest ? { rateLimitMs: 0 } : {}),\n });\n this.toolCtx = {\n ...ctx,\n signal: this.signal,\n log: (level: LoggingLevel, data: unknown, logger?: string) => {\n const msg = typeof data === 'string' ? data : String(data);\n const prefix = logger ? `[${logger}] ` : '';\n Logger.emit(level, `${prefix}${msg}`);\n },\n onProgress: (p) => {\n this.#tick(p);\n },\n };\n }\n\n #tick(p: { current: number; total?: number }): void {\n if (this.#progressClosed) return;\n const tickCtx: ProgressCtx = {\n ...this.#progressCtx,\n current: p.current,\n ...(p.total !== undefined ? { total: p.total } : {}),\n };\n this.#progressSession.set({ ...p, message: plainMessage('tick', tickCtx) });\n }\n\n async #closeWithDone(message: string): Promise<void> {\n this.#progressClosed = true;\n this.#progressSession.complete(message);\n if (this.#mcpSink) await this.#mcpSink.flush();\n }\n\n async #closeWithFail(error: unknown, message: string): Promise<void> {\n this.#progressClosed = true;\n this.#progressSession.fail(error, message);\n if (this.#mcpSink) await this.#mcpSink.flush();\n }\n\n async #flushProgress(): Promise<void> {\n if (this.#mcpSink) await this.#mcpSink.flush();\n }\n\n private async completeProgress(result: z.infer<O>): Promise<void> {\n const doneCtx: ProgressCtx = this.def.progressDone\n ? { ...this.#progressCtx, ...this.def.progressDone(this.parsedArgs, result) }\n : this.#progressCtx;\n await this.#closeWithDone(plainMessage('done', doneCtx));\n }\n\n private async failProgress(error: unknown): Promise<{ isError: true; content: ContentBlock[] }> {\n const errMsg = formatUnknownErrorMessage(error);\n const message = plainMessage('fail', { ...this.#progressCtx, error: errMsg });\n await this.#closeWithFail(error, message);\n const { text: errorText } = Problem.toText(\n error,\n this.def.defaultErrorCode ?? ErrorCode.UNKNOWN,\n );\n return {\n content: [{ type: 'text' as const, text: errorText }],\n isError: true,\n };\n }\n\n /**\n * Access-grant pre-check (R7/R8/R9). Before `run` touches the filesystem,\n * ask PathGuard which of the tool's declared paths are out-of-root and\n * grantable. If any are and this round carries no verified `requestState`,\n * return `input_required` (nothing is read or written — R7). On retry, the\n * verified state must bind this grant set (R9); apply each accepted grant for\n * the session (R8) and proceed. A declined or ungrantable path is left for\n * `validateAccess` to fail closed during the operation. Returns `undefined`\n * when no round-trip is needed and any applicable grants have been applied.\n */\n private async precheckGrant(): Promise<InputRequiredResult | undefined> {\n const extract = this.def.accessPaths;\n if (!extract) return undefined;\n const paths = extract(this.parsedArgs);\n if (paths.length === 0) return undefined;\n const grantDirs = await this.toolCtx.fs.pathGuard.precheckAccess(paths);\n if (grantDirs.length === 0) return undefined;\n\n // One grant dir: a single boolean confirm. Multiple: one multi-select\n // elicitation so the client picks the subset to allow in one round-trip\n // (N dirs → 1 round-trip, not N). `pendingRoundTrip` still binds\n // `paths: grantDirs` sorted, so R9 path-binding is unaffected.\n const multi = grantDirs.length > 1;\n const round = await pendingRoundTrip({\n op: 'grant',\n pending: grantDirs,\n requestState: this.toolCtx.requestState,\n clientCapabilities: this.toolCtx.clientCapabilities,\n buildInputs: (dirs) =>\n multi\n ? [\n multiSelectInput(\n 'grant',\n 'Grant filesystem access to these directories? Select the ones to allow.',\n dirs.map((d) => ({ value: d, title: d })),\n ),\n ]\n : dirs.map((dir, i) =>\n confirmInput(`confirm_${i}`, `Grant filesystem access to \"${dir}\"?`),\n ),\n });\n if (round !== undefined) return round;\n // Apply accepted grants for the session (R8). Declined/missing dirs are\n // skipped here; their paths fail with ACCESS_DENIED during the operation.\n //\n // No list-changed notification follows: a grant widens the allowed roots,\n // and no resource list reads them. The instructions resource has a fixed\n // URI, the result template lists the ResourceStore, and the file template\n // lists nothing at all (see resources.ts). Notifying here only bought the\n // client a round trip to re-fetch a list that could not have changed.\n //\n // `readAcceptedMultiChoice` does NOT validate against the offered choices\n // (attacker-controlled inputResponses on re-entry). Only grant dirs that\n // were actually offered by precheckAccess — a value outside grantDirs is\n // ignored and fails closed in validateAccess. The single-select path reads\n // `confirm_${i}` for `grantDirs[i]`, so the isSamePath filter is a no-op\n // there; it only bites for the multi-select array.\n const accepted = multi\n ? (readAcceptedMultiChoice(this.toolCtx.inputResponses, 'grant') ?? [])\n : grantDirs.filter((_dir, i) =>\n readAcceptedConfirm(this.toolCtx.inputResponses, `confirm_${i}`),\n );\n for (const dir of accepted) {\n if (!grantDirs.some((g) => isSamePath(g, dir))) continue;\n await this.toolCtx.fs.pathGuard.applyGrant(dir);\n }\n return undefined;\n }\n\n async execute(): Promise<CallToolResult | InputRequiredResult> {\n const runTool = async (): Promise<CallToolResult | InputRequiredResult> => {\n try {\n // Access-grant pre-check before any filesystem touch (R7/R8/R9). A\n // grant input_required short-circuits here (progress paused, not\n // finished); an R9 mismatch throws, which this catch surfaces as an\n // isError tool result like every other handler failure — not a raw\n // JSON-RPC error (GRANT-1 impact #2).\n const grantRequired = await this.precheckGrant();\n if (grantRequired !== undefined) return grantRequired;\n const result = await this.def.run(this.parsedArgs, this.toolCtx);\n // input_required is a return value, not a completed call: the client\n // retries the same tools/call carrying inputResponses, and this\n // handler re-enters from the top. Skip the progress \"done\" close and\n // return it verbatim — progress is paused, not finished.\n if (isInputRequiredResult(result)) {\n return result;\n }\n await this.completeProgress(result.structured);\n return buildSuccessResponse(result);\n } catch (error) {\n return await this.failProgress(error);\n } finally {\n await this.#flushProgress();\n }\n };\n\n return runTool();\n }\n}\n\nfunction createServerToolHandler<I extends z.ZodType, O extends z.ZodType>(\n def: ToolDef<I, O>,\n deps: ToolDeps,\n): (args: z.infer<I>, ctx: ServerContext) => Promise<CallToolResult | InputRequiredResult> {\n return async (args, ctx) =>\n new ToolExecutor<I, O>(def.name, toToolCtx(ctx, deps), def, args).execute();\n}\n\n/**\n * Validate with Zod while publishing the precomputed JSON Schema: clients get\n * draft-2020-12, handlers get Zod's coercions, defaults and error messages.\n *\n * Each validator instance is built for exactly one schema and handed straight\n * to `fromJsonSchema(thatSchema, ...)`, which calls `getValidator` once with\n * the same schema — so the argument is redundant here and deliberately ignored.\n * Reusing one instance across schemas would silently validate against `schema`.\n */\nfunction zodJsonSchemaValidator(schema: z.ZodType): jsonSchemaValidator {\n return {\n getValidator<U>(): JsonSchemaValidator<U> {\n return (input: unknown) => {\n const result = schema.safeParse(input);\n if (result.success) {\n return {\n valid: true as const,\n data: result.data as U,\n errorMessage: undefined,\n };\n }\n return {\n valid: false as const,\n data: undefined,\n errorMessage: z.prettifyError(result.error),\n };\n };\n },\n };\n}\n\n/**\n * Generate the wire copy of a schema for `tools/list`. The Zod schema keeps\n * every constraint for runtime validation; the `override` pass only trims what\n * the wire copy costs every client at session start:\n *\n * - `$schema` and `title` carry no information the host uses.\n * - `maximum: Number.MAX_SAFE_INTEGER` is zod's int() artifact, not a bound.\n * - `examples` is dropped in both directions: on output it describes a field the\n * server itself fills in, and on input every description that carried one\n * already spells the same example out inline, so the keyword paid twice.\n *\n * Output `description`s are NOT dropped. They were, and the result was a wire\n * contract the model had to guess at: `delete` returns `path` XOR `paths`,\n * `edit.diff` appears only under dryRun, `read.value` has no required field at\n * all. None of that is inferable from types alone.\n *\n * No shared subschema carries a `.meta({ id })`, so zod inlines every one of\n * them and the emitted document has no `$defs`/`$ref` to dereference.\n */\nfunction toDraft202012(schema: z.ZodType, io: 'input' | 'output'): JsonSchemaType {\n const generated = z.toJSONSchema(schema, {\n target: 'draft-2020-12',\n io,\n override: ({ jsonSchema }) => {\n const node = jsonSchema as Record<string, unknown>;\n delete node['title'];\n if (node['maximum'] === Number.MAX_SAFE_INTEGER) delete node['maximum'];\n delete node['examples'];\n },\n }) as Record<string, unknown>;\n delete generated['$schema'];\n return generated;\n}\n\nexport function defineTool<I extends z.ZodType, O extends z.ZodType>(\n def: ToolDef<I, O>,\n): DefinedTool {\n const inputJsonSchema = toDraft202012(def.input, 'input');\n const outputJsonSchema = toDraft202012(def.output, 'output');\n\n // Nothing here depends on `deps`, so it is built once per tool definition\n // rather than once per `register` — the HTTP leg registers every tool afresh\n // on each request.\n // Published annotations are derived, not passed through. `readOnlyHint` is\n // load-bearing (MUTATING_TOOL_NAMES derives the --read-only gate from it) and\n // `openWorldHint: false` is a real claim for a filesystem server. The other\n // two describe behavior a read-only tool cannot have, and restate the default\n // for a mutating one — 29 tokens per tool for nothing a client acts on.\n const publishedAnnotations: ToolAnnotations = {\n readOnlyHint: def.annotations.readOnlyHint,\n openWorldHint: def.annotations.openWorldHint ?? false,\n ...(def.annotations.readOnlyHint\n ? {}\n : { destructiveHint: def.annotations.destructiveHint ?? true }),\n };\n\n const toolDefShape = {\n title: def.title,\n description: def.description,\n inputSchema: fromJsonSchema<z.infer<I>>(inputJsonSchema, zodJsonSchemaValidator(def.input)),\n // The validator is built either way — results are still checked against the\n // Zod object — but the schema reaches the wire only where the shape is not\n // inferable from the description plus the text content.\n ...(def.publishOutputSchema\n ? {\n outputSchema: fromJsonSchema<z.infer<O>>(\n outputJsonSchema,\n zodJsonSchemaValidator(def.output),\n ),\n }\n : {}),\n annotations: publishedAnnotations,\n };\n\n const tool: DefinedTool = {\n name: def.name,\n annotations: def.annotations,\n\n register(deps: ToolDeps): RegisteredTool {\n return deps.server.registerTool(def.name, toolDefShape, createServerToolHandler(def, deps));\n },\n };\n\n return tool;\n}\n"]}
|
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const DELETE_FILE_TOOL: ToolContract;
|
|
4
|
-
export declare function registerDeleteFileTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
1
|
+
export declare const DELETE_FILE: import("./define.js").DefinedTool;
|
|
2
|
+
//# sourceMappingURL=delete-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-file.d.ts","sourceRoot":"","sources":["../../src/tools/delete-file.ts"],"names":[],"mappings":"AAwZA,eAAO,MAAM,WAAW,mCAyDtB,CAAC"}
|