@j0hanz/filesystem-mcp 2.2.0 → 2.4.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 +38 -32
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +21 -40
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -46
- package/dist/cli.js.map +1 -1
- package/dist/core/concurrency.d.ts +5 -17
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +32 -40
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/config.d.ts +2 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/cursor.d.ts +1 -12
- package/dist/core/cursor.d.ts.map +1 -1
- package/dist/core/cursor.js +2 -12
- package/dist/core/cursor.js.map +1 -1
- package/dist/core/errors.d.ts +6 -11
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +62 -117
- package/dist/core/errors.js.map +1 -1
- package/dist/core/file-uri.d.ts +7 -3
- package/dist/core/file-uri.d.ts.map +1 -1
- package/dist/core/file-uri.js +8 -4
- package/dist/core/file-uri.js.map +1 -1
- package/dist/core/fmt.d.ts +0 -11
- package/dist/core/fmt.d.ts.map +1 -1
- package/dist/core/fmt.js +1 -19
- package/dist/core/fmt.js.map +1 -1
- package/dist/core/fs.d.ts +34 -4
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +151 -12
- package/dist/core/fs.js.map +1 -1
- package/dist/core/glob.d.ts +1 -2
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +12 -28
- package/dist/core/glob.js.map +1 -1
- package/dist/core/input-required.d.ts +19 -29
- package/dist/core/input-required.d.ts.map +1 -1
- package/dist/core/input-required.js +58 -73
- package/dist/core/input-required.js.map +1 -1
- package/dist/core/observability.d.ts +9 -0
- package/dist/core/observability.d.ts.map +1 -1
- package/dist/core/observability.js +13 -2
- package/dist/core/observability.js.map +1 -1
- package/dist/core/path-completer.d.ts +6 -9
- package/dist/core/path-completer.d.ts.map +1 -1
- package/dist/core/path-completer.js +28 -55
- package/dist/core/path-completer.js.map +1 -1
- package/dist/core/path-discovery.d.ts.map +1 -1
- package/dist/core/path-discovery.js +3 -4
- package/dist/core/path-discovery.js.map +1 -1
- package/dist/core/path-utils.d.ts +28 -0
- package/dist/core/path-utils.d.ts.map +1 -1
- package/dist/core/path-utils.js +57 -2
- package/dist/core/path-utils.js.map +1 -1
- package/dist/core/path.d.ts +3 -8
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +8 -9
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts +3 -22
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +46 -96
- package/dist/core/read.js.map +1 -1
- package/dist/core/schema.d.ts +8 -9
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +11 -14
- package/dist/core/schema.js.map +1 -1
- package/dist/core/search.d.ts +6 -1
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +21 -23
- package/dist/core/search.js.map +1 -1
- package/dist/core/sensitive.d.ts +4 -2
- package/dist/core/sensitive.d.ts.map +1 -1
- package/dist/core/sensitive.js +262 -45
- package/dist/core/sensitive.js.map +1 -1
- package/dist/core/store.d.ts +41 -16
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +147 -82
- package/dist/core/store.js.map +1 -1
- package/dist/core/util.d.ts +3 -2
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +5 -6
- package/dist/core/util.js.map +1 -1
- package/dist/core/watcher-registry.d.ts +1 -1
- package/dist/core/watcher-registry.d.ts.map +1 -1
- package/dist/core/watcher-registry.js +9 -5
- package/dist/core/watcher-registry.js.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/instructions.js +2 -2
- package/dist/instructions.js.map +1 -1
- package/dist/prompts.js +1 -1
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts +11 -19
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +51 -62
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts +4 -5
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +15 -11
- package/dist/server.js.map +1 -1
- package/dist/tools/batch.d.ts +2 -2
- package/dist/tools/batch.js +3 -3
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/create.d.ts +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +168 -17
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +15 -9
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +48 -94
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete.d.ts +1 -1
- package/dist/tools/delete.d.ts.map +1 -1
- package/dist/tools/delete.js +23 -23
- package/dist/tools/delete.js.map +1 -1
- package/dist/tools/diff.d.ts +1 -1
- package/dist/tools/diff.d.ts.map +1 -1
- package/dist/tools/diff.js +7 -37
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +101 -92
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/find-files.d.ts +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +18 -37
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/index.d.ts +10 -10
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +14 -13
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-roots.d.ts +1 -1
- package/dist/tools/list-roots.js +3 -3
- package/dist/tools/list-roots.js.map +1 -1
- package/dist/tools/list.d.ts +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +23 -29
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +31 -45
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts +1 -1
- package/dist/tools/patch.js +7 -7
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/progress.d.ts +17 -28
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +42 -94
- package/dist/tools/progress.js.map +1 -1
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +10 -12
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +32 -49
- package/dist/tools/replace-text.js.map +1 -1
- package/dist/tools/search-text.d.ts +1 -1
- package/dist/tools/search-text.d.ts.map +1 -1
- package/dist/tools/search-text.js +82 -45
- package/dist/tools/search-text.js.map +1 -1
- package/dist/tools/stat.d.ts +1 -1
- package/dist/tools/stat.d.ts.map +1 -1
- package/dist/tools/stat.js +9 -29
- package/dist/tools/stat.js.map +1 -1
- package/dist/transport/http-policy.js +4 -4
- package/dist/transport/http-policy.js.map +1 -1
- package/dist/transport/http.d.ts +2 -2
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +21 -18
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/shared.d.ts +2 -2
- package/dist/transport/shared.js +2 -2
- package/dist/transport/shared.js.map +1 -1
- package/dist/transport/stdio.d.ts +4 -3
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/transport/stdio.js +7 -6
- package/dist/transport/stdio.js.map +1 -1
- package/dist/transport.d.ts +2 -2
- package/dist/transport.js +2 -2
- package/dist/transport.js.map +1 -1
- package/package.json +6 -13
- package/dist/core/page-store.d.ts +0 -41
- package/dist/core/page-store.d.ts.map +0 -1
- package/dist/core/page-store.js +0 -74
- package/dist/core/page-store.js.map +0 -1
- package/dist/core/primitives.d.ts +0 -34
- package/dist/core/primitives.d.ts.map +0 -1
- package/dist/core/primitives.js +0 -63
- package/dist/core/primitives.js.map +0 -1
package/dist/tools/move.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"move.js","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InputRequiredResult } from '@modelcontextprotocol/server';\nimport { isInputRequiredResult } from '@modelcontextprotocol/server';\n\nimport { basename, dirname, resolve } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.js';\nimport {\n ErrorCode,\n FsError,\n isFsError,\n isNodeError,\n Problem,\n rethrowIfAborted,\n} from '../core/errors.js';\nimport { joinRoster, pathLabel } from '../core/fmt.js';\nimport { destExists } from '../core/fs.js';\nimport type { GuardedFileSystem } from '../core/fs.js';\nimport {\n choiceInput,\n confirmKey,\n pendingRoundTrip,\n readAcceptedChoice,\n} from '../core/input-required.js';\nimport {\n isPathInsideDirectory,\n isSamePath,\n normalizeCaseForComparison,\n} from '../core/path-utils.js';\nimport { defaultFalseBoolean, PerFileErrorSchema, RequiredPath } from '../core/schema.js';\nimport { PARALLEL_CONCURRENCY } from '../core/util.js';\nimport type { ToolCtx } from './define.js';\nimport { defineTool } from './define.js';\n\nconst MoveItemSchema = z.strictObject({\n source: RequiredPath.describe('Path of the file or directory to move or copy'),\n destination: RequiredPath.describe('Destination path'),\n});\n\nconst MoveItemResultSchema = z.strictObject({\n from: z.string().describe('Resolved absolute source path'),\n to: z.string().describe('Resolved absolute destination path'),\n});\n\nconst MoveInputSchema = z.strictObject({\n moves: z.array(MoveItemSchema).min(1).max(100).describe('Operations to perform (max 100)'),\n copy: defaultFalseBoolean('Copy instead of move; sources are left in place'),\n overwrite: defaultFalseBoolean(\n 'Copy mode only: overwrite existing destinations without confirmation',\n ),\n});\n\nconst MoveFailureItemSchema = z.strictObject({\n source: z.string().describe('Source path that could not be moved'),\n destination: z.string().describe('Intended destination path for the failed move'),\n error: PerFileErrorSchema,\n});\n\ntype MoveFailureItem = z.infer<typeof MoveFailureItemSchema>;\n\nconst MoveOutputSchema = z.strictObject({\n moves: z.array(MoveItemResultSchema).describe('Successfully completed operations'),\n failures: z\n .array(MoveFailureItemSchema)\n .optional()\n .describe('Operations that failed with per-item error details'),\n skipped: z\n .array(z.string())\n .optional()\n .describe('Destinations skipped because the user chose Skip'),\n});\n\ntype MoveItemResult = z.infer<typeof MoveItemResultSchema>;\n\ntype PairOp = 'move' | 'copy';\n\nconst VERB: Readonly<Record<PairOp, string>> = { move: 'Move', copy: 'Copy' };\n\n/** A move or copy pre-checked up to the point a confirmation decision is needed. */\ninterface TransferPlan {\n pair: { source: string; destination: string };\n /**\n * The path the mutation runs on: the link itself for a move, the resolved\n * target for a copy. Every collision check uses the resolved source instead\n * — see {@link validateTransferSource}.\n */\n opSource: string;\n validDest: string;\n /**\n * A rename that only changes the destination's case. Move must perform it —\n * it is real work — so it is exempt from the destination-exists checks that\n * would otherwise read the source itself as an existing destination. Never\n * true for a copy, which treats a case-only target as a self-copy and skips.\n */\n isCaseOnlyRename: boolean;\n /** Whether the destination existed when planned (drives the TOCTOU guard). */\n destExistedOriginally: boolean;\n /** Destination exists and is not a self/case-only target → needs overwrite confirmation. */\n pending: boolean;\n}\n\n/**\n * Phase 1 (no mutation): validate source and destination, reject self-targets\n * and transfers into a subdirectory of the source, and stat the destination to\n * decide whether this pair needs an overwrite confirmation. No `mkdir` happens\n * here — the old flow created the destination's parent before asking, which\n * mutated the filesystem before a confirmation; that now waits for phase 2 (R14).\n *\n * `overwrite` is copy-only — move has no confirmation bypass — so a move always\n * passes false.\n */\nasync function planTransfer(\n op: PairOp,\n pair: { source: string; destination: string },\n fs: ToolCtx['fs'],\n overwrite: boolean,\n): Promise<TransferPlanResult> {\n let realSource: string;\n let opSource: string;\n let validDest: string;\n try {\n ({ realSource, opSource } = await validateTransferSource(op, pair.source, fs));\n validDest = await fs.pathGuard.validatePathForWrite(pair.destination);\n } catch (error) {\n return { status: 'fail', failure: pairFailure(pair, error) };\n }\n\n // Comparisons run on the resolved source; only the fs call in phase 2 uses\n // opSource. validatePathForWrite resolves the destination through a symlink\n // too, so both sides of every check must be resolved to match.\n const resolvedSource = resolve(realSource);\n const resolvedDest = resolve(validDest);\n\n const isSelf = resolvedSource === resolvedDest;\n const isCaseOnlyRename = !isSelf && isSamePath(resolvedSource, resolvedDest);\n\n // A copy onto a case-only variant of its own source is the same file, so it\n // is a no-op; the same rename is real work for move and proceeds.\n if (isSelf || (isCaseOnlyRename && op === 'copy')) {\n return { status: 'noop' };\n }\n\n if (!isCaseOnlyRename && isPathInsideDirectory(resolvedSource, resolvedDest)) {\n return {\n status: 'fail',\n failure: pairFailure(\n pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `Cannot ${op} a directory into its own subdirectory`,\n pair.source,\n ),\n ),\n };\n }\n\n const destExistedOriginally = !isCaseOnlyRename && (await destExists(fs, validDest, op));\n const pending = destExistedOriginally && !overwrite;\n\n return {\n status: 'plan',\n plan: { pair, opSource, validDest, isCaseOnlyRename, destExistedOriginally, pending },\n };\n}\n\n/**\n * Phase 2 (mutation): confirm an overwrite if needed, TOCTOU-check the\n * destination, create its parent, then rename (with cross-device fallback) or\n * copy. A declined/missing confirmation throws `CANCELLED`, collected as a\n * per-pair failure by the caller. A destination that appeared between plan and\n * mutation (created during the confirmation gap) also fails closed.\n */\nasync function executeTransfer(\n op: PairOp,\n plan: TransferPlan,\n ctx: Pick<ToolCtx, 'fs' | 'signal' | 'inputResponses' | 'log'>,\n pendingSorted: readonly string[],\n): Promise<TransferExecResult> {\n if (plan.pending) {\n const key = confirmKey(pendingSorted.indexOf(plan.validDest));\n const choice = readAcceptedChoice(ctx.inputResponses, key);\n if (choice === 'skip') {\n return { skipped: plan.pair.destination };\n }\n if (choice !== 'overwrite') {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: overwrite of \"${plan.pair.destination}\" was declined or missing`,\n plan.pair.destination,\n );\n }\n }\n\n // TOCTOU check before any mutation: a destination that did not exist when\n // planned but exists now was created during the confirmation gap.\n if (\n !plan.isCaseOnlyRename &&\n !plan.destExistedOriginally &&\n (await destExists(ctx.fs, plan.validDest, op))\n ) {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: destination \"${plan.pair.destination}\" was created during confirmation.`,\n plan.pair.destination,\n );\n }\n\n await ctx.fs.mkdir(dirname(plan.validDest), { recursive: true });\n\n if (op === 'move') {\n await performRenameWithFallback(plan.opSource, plan.validDest, ctx.fs, plan.pair.source);\n ctx.log?.('info', `move: ${plan.pair.source} -> ${plan.pair.destination}`, 'move');\n } else {\n await ctx.fs.cp(plan.opSource, plan.validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n force: true,\n });\n }\n return { value: { from: plan.opSource, to: plan.validDest } };\n}\n\ntype TransferPlanResult =\n | { status: 'fail'; failure: MoveFailureItem }\n | { status: 'noop' }\n | { status: 'plan'; plan: TransferPlan };\n\n/** A discriminated union, so the fold below narrows without a generic cast. */\ntype TransferExecResult = { readonly skipped: string } | { readonly value: MoveItemResult };\n\nfunction pairFailure(\n pair: { source: string; destination: string },\n error: unknown,\n): MoveFailureItem {\n return {\n source: pair.source,\n destination: pair.destination,\n error: Problem.fromUnknown(error, ErrorCode.UNKNOWN, pair.source),\n };\n}\n\n/**\n * The source→destination sibling of `runOverPaths`: plan every pair, fail closed\n * on a duplicated destination, round-trip the overwrite confirmations as one\n * set, then execute what survived in parallel. `copy` and `move` differ only in\n * their `plan` and `execute` callbacks.\n */\nasync function runTransfers(\n items: readonly z.infer<typeof MoveItemSchema>[],\n ctx: ToolCtx,\n op: PairOp,\n overwrite: boolean,\n): Promise<\n | { results: MoveItemResult[]; skipped: string[]; failures: MoveFailureItem[] }\n | InputRequiredResult\n> {\n const { results: planned, errors: planErrors } = await processInParallel(\n items,\n (pair) => planTransfer(op, pair, ctx.fs, overwrite),\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n // plan callbacks are fail-closed (they return { status: 'fail' }, never\n // throw), so planErrors should always be empty. Surface the first rather\n // than silently dropping the item if a future plan callback violates that.\n const firstPlanError = planErrors[0];\n if (firstPlanError) throw firstPlanError.error;\n\n const failures: MoveFailureItem[] = [];\n const candidates: TransferPlan[] = [];\n for (const { value } of planned) {\n if (value.status === 'fail') failures.push(value.failure);\n else if (value.status === 'plan') candidates.push(value.plan);\n // 'noop' (self-copy / self-move) is silently skipped.\n }\n\n // Two sources targeting the same destination in one batch would otherwise\n // collapse to a single shared overwrite confirmation and let the second\n // one silently clobber the first's freshly-written content. Fail closed:\n // only the first plan per destination proceeds; later ones targeting the\n // same destination are reported as a per-item failure.\n const seenDest = new Set<string>();\n const ready: TransferPlan[] = [];\n for (const candidate of candidates) {\n const destKey = normalizeCaseForComparison(candidate.validDest);\n if (seenDest.has(destKey)) {\n failures.push(\n pairFailure(\n candidate.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${VERB[op]} cancelled: another entry in this batch already targets destination \"${candidate.pair.destination}\"`,\n candidate.pair.destination,\n ),\n ),\n );\n continue;\n }\n seenDest.add(destKey);\n ready.push(candidate);\n }\n\n const pendingSorted = ready\n .filter((p) => p.pending)\n .map((p) => p.validDest)\n .sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip({\n op,\n pending: pendingSorted,\n requestState: ctx.requestState,\n clientCapabilities: ctx.clientCapabilities,\n buildInputs: (dests) =>\n dests.map((dest, i) =>\n choiceInput(\n confirmKey(i),\n op === 'move'\n ? `\"${dest}\" already exists. Overwrite it?`\n : `Destination \"${dest}\" already exists. Overwrite it?`,\n [\n { value: 'overwrite', title: 'Overwrite' },\n { value: 'skip', title: 'Skip' },\n ],\n ),\n ),\n });\n if (round !== undefined) return round;\n }\n\n const total = ready.length;\n let completed = 0;\n const tick = (): void => {\n completed += 1;\n ctx.onProgress?.({ current: completed, total });\n };\n const { results: execResults, errors: execErrors } = await processInParallel(\n ready,\n async (readyPlan) => {\n try {\n return await executeTransfer(op, readyPlan, ctx, pendingSorted);\n } finally {\n tick();\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n for (const { error, index } of execErrors) {\n rethrowIfAborted(error);\n const failed = ready[index];\n if (failed) failures.push(pairFailure(failed.pair, error));\n }\n\n const results: MoveItemResult[] = [];\n const skipped: string[] = [];\n for (const { value } of execResults) {\n if ('skipped' in value) skipped.push(value.skipped);\n else results.push(value.value);\n }\n\n return { results, skipped, failures };\n}\n\n/**\n * Two-phase move: pre-check every move (no mutation) to build the overwrite\n * pending set; if any move is pending and the round carries no verified\n * `requestState`, return `input_required` moving nothing (R14). On retry, R9\n * checks the state binds this move set, then each pending move proceeds only on\n * an accepted overwrite. Non-pending moves proceed alongside them.\n */\nasync function handleMove(\n args: z.infer<typeof MoveInputSchema>,\n ctx: ToolCtx,\n): Promise<z.infer<typeof MoveOutputSchema> | InputRequiredResult> {\n const op: PairOp = args.copy ? 'copy' : 'move';\n // `overwrite` is copy-only; move has no confirmation bypass.\n const overwrite = args.copy && args.overwrite;\n const outcome = await runTransfers(args.moves, ctx, op, overwrite);\n if (isInputRequiredResult(outcome)) return outcome;\n\n const { results, skipped, failures } = outcome;\n\n return {\n moves: results,\n ...(failures.length > 0 ? { failures } : {}),\n ...(skipped.length > 0 ? { skipped } : {}),\n };\n}\n\nfunction buildSummary(\n verb: 'move' | 'copy',\n results: readonly MoveItemResult[],\n failures: readonly MoveFailureItem[],\n skipped: readonly string[],\n): string {\n const successCount = results.length;\n const failCount = failures.length;\n if (failCount === 0 && skipped.length === 0 && successCount === 1) {\n const result = results[0];\n if (result) {\n return `${verb}: ${pathLabel(result.from)} → ${pathLabel(result.to)}`;\n }\n }\n // Name each pair. \"move: 2 items\" left the caller to open structuredContent\n // to learn which two, and a partial failure was unreadable without it.\n // Skipped destinations are an outcome the user chose, not a failure — name\n // them, or an all-skipped call reads as \"copy: nothing\".\n const tokens = [\n ...results.map((r) => `${pathLabel(r.from)} → ${pathLabel(r.to)}`),\n ...skipped.map((dest) => `${pathLabel(dest)} SKIPPED`),\n ];\n const parts = [`${verb}: ${joinRoster(tokens) || 'nothing'}`];\n if (failCount > 0) parts.push(`${String(failCount)} failed`);\n return parts.join(' · ');\n}\n\ninterface TransferSource {\n /** Symlink resolved — the identity used for every same-target comparison. */\n realSource: string;\n /** The path the mutation runs on: symlink preserved for move, resolved for copy. */\n opSource: string;\n}\n\n/**\n * A move needs two views of its source. Renaming must operate on the link\n * itself, or moving a symlink would rename the file it points at and leave the\n * link dangling. Every collision check must instead compare the resolved\n * target, or a link moved onto itself (or onto its own target) reads as a real\n * move and renames the link over that target, destroying it. A copy reads\n * through the link, so both views are the resolved one.\n */\nasync function validateTransferSource(\n op: PairOp,\n source: string,\n fs: ToolCtx['fs'],\n): Promise<TransferSource> {\n try {\n const realSource = await fs.pathGuard.validateExistingPath(source);\n const opSource = op === 'move' ? await fs.pathGuard.validatePathForDelete(source) : realSource;\n return { realSource, opSource };\n } catch (error) {\n if (isFsError(error)) throw error;\n throw new FsError(ErrorCode.ACCESS_DENIED, `${VERB[op]} failed for ${source}`, source);\n }\n}\n\nasync function performRenameWithFallback(\n validSource: string,\n validDest: string,\n fsOps: Pick<GuardedFileSystem, 'rename' | 'cp' | 'rm'>,\n originalSource: string,\n): Promise<void> {\n try {\n await fsOps.rename(validSource, validDest);\n } catch (error: unknown) {\n rethrowIfAborted(error);\n\n if (!isNodeError(error) || error.code !== 'EXDEV') {\n throw error;\n }\n\n // EXDEV: cross-device rename. Copy then remove, preserving symlinks and\n // timestamps so link semantics survive the move.\n let copied = false;\n try {\n await fsOps.cp(validSource, validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n });\n copied = true;\n await fsOps.rm(validSource, { recursive: true, force: true });\n } catch (copyOrRemoveError) {\n rethrowIfAborted(copyOrRemoveError);\n if (copied) {\n // cp succeeded but rm failed: the destination already holds a complete\n // copy and the source remains. Surface the rm error as the cause so the\n // caller can recover (clean up the duplicate) instead of a silent generic\n // failure that hides the partial completion.\n const baseProblem = Problem.fromUnknown(\n copyOrRemoveError,\n ErrorCode.UNKNOWN,\n originalSource,\n );\n throw new FsError(\n baseProblem.code,\n `Cross-device move of ${originalSource}: copy succeeded but source removal failed (destination holds a copy): ${baseProblem.message}`,\n originalSource,\n copyOrRemoveError,\n );\n }\n throw copyOrRemoveError;\n }\n }\n}\n\nexport const MOVE = defineTool({\n name: 'move',\n title: 'Move or Copy Files',\n description:\n 'Move, rename, or copy files and directories to explicit destination paths (max 100 operations per call). ' +\n 'Pass moves: [{ source, destination }] — there is no single-pair form. ' +\n 'Parent directories are created automatically. Set copy=true to copy instead of move (sources are kept). ' +\n 'An existing destination prompts the user to confirm the overwrite, so the call returns without moving ' +\n 'anything until that confirmation comes back; copy=true with overwrite=true skips the prompt, move has no ' +\n 'such bypass. Self-moves are silently skipped.',\n input: MoveInputSchema,\n output: MoveOutputSchema,\n annotations: {\n readOnlyHint: false,\n idempotentHint: false,\n destructiveHint: true,\n openWorldHint: false,\n },\n progress: (args) => {\n const label = args.copy ? 'Copy' : 'Move';\n if (args.moves.length === 1) {\n const move = args.moves[0];\n return {\n label,\n subject: `${basename(move?.source ?? '')} → ${basename(move?.destination ?? '')}`,\n };\n }\n return { label, subject: `${String(args.moves.length)} files` };\n },\n defaultErrorCode: ErrorCode.UNKNOWN,\n accessPaths: (args) => args.moves.flatMap((m) => [m.source, m.destination]),\n run: async (args, ctx) => {\n const output = await handleMove(args, ctx);\n // input_required is a return value, not a completed call: surface it\n // verbatim so the executor short-circuits before building a CallToolResult.\n if (isInputRequiredResult(output)) return output;\n const failures = output.failures ?? [];\n const skipped = output.skipped ?? [];\n return {\n structured: output,\n text: buildSummary(args.copy ? 'copy' : 'move', output.moves, failures, skipped),\n // Every requested pair failed: no move, no copy, no user-chosen skip - the\n // call did nothing. A skip is work the caller asked for, so it counts.\n isError: failures.length > 0 && output.moves.length + skipped.length === 0,\n };\n },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"move.js","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InputRequiredResult } from '@modelcontextprotocol/server';\nimport { isInputRequiredResult } from '@modelcontextprotocol/server';\n\nimport { basename, dirname, resolve } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.ts';\nimport {\n ErrorCode,\n FsError,\n isFsError,\n isNodeError,\n Problem,\n rethrowIfAborted,\n} from '../core/errors.ts';\nimport { joinRoster, pathLabel } from '../core/fmt.ts';\nimport { destExists } from '../core/fs.ts';\nimport type { GuardedFileSystem } from '../core/fs.ts';\nimport {\n confirmKey,\n describeRefusal,\n pendingRoundTrip,\n readAcceptedChoice,\n} from '../core/input-required.ts';\nimport {\n isPathInsideDirectory,\n isSamePath,\n normalizeCaseForComparison,\n} from '../core/path-utils.ts';\nimport { defaultFalseBoolean, PerFileErrorSchema, RequiredPath } from '../core/schema.ts';\nimport { PARALLEL_CONCURRENCY } from '../core/util.ts';\nimport type { ToolCtx } from './define.ts';\nimport { defineTool } from './define.ts';\n\nconst MoveItemSchema = z.strictObject({\n source: RequiredPath.describe('Path of the file or directory to move or copy'),\n destination: RequiredPath.describe('Destination path'),\n});\n\nconst MoveItemResultSchema = z.strictObject({\n from: z.string().describe('Resolved absolute source path'),\n to: z.string().describe('Resolved absolute destination path'),\n});\n\nconst MoveInputSchema = z.strictObject({\n moves: z.array(MoveItemSchema).min(1).max(100).describe('Operations to perform (max 100)'),\n copy: defaultFalseBoolean('Copy instead of move; sources are left in place'),\n overwrite: defaultFalseBoolean(\n 'Copy mode only: overwrite existing destinations without confirmation',\n ),\n});\n\nconst MoveFailureItemSchema = z.strictObject({\n source: z.string().describe('Source path that could not be moved'),\n destination: z.string().describe('Intended destination path for the failed move'),\n error: PerFileErrorSchema,\n});\n\ntype MoveFailureItem = z.infer<typeof MoveFailureItemSchema>;\n\nconst MoveOutputSchema = z.strictObject({\n moves: z.array(MoveItemResultSchema).describe('Successfully completed operations'),\n failures: z\n .array(MoveFailureItemSchema)\n .optional()\n .describe('Operations that failed with per-item error details'),\n skipped: z\n .array(z.string())\n .optional()\n .describe('Destinations skipped because the user chose Skip'),\n});\n\ntype MoveItemResult = z.infer<typeof MoveItemResultSchema>;\n\ntype PairOp = 'move' | 'copy';\n\nconst VERB: Readonly<Record<PairOp, string>> = { move: 'Move', copy: 'Copy' };\n\n/** A move or copy pre-checked up to the point a confirmation decision is needed. */\ninterface TransferPlan {\n pair: { source: string; destination: string };\n /**\n * The path the mutation runs on: the link itself for a move, the resolved\n * target for a copy. Every collision check uses the resolved source instead\n * — see {@link validateTransferSource}.\n */\n opSource: string;\n validDest: string;\n /**\n * A rename that only changes the destination's case. Move must perform it —\n * it is real work — so it is exempt from the destination-exists checks that\n * would otherwise read the source itself as an existing destination. Never\n * true for a copy, which treats a case-only target as a self-copy and skips.\n */\n isCaseOnlyRename: boolean;\n /** Whether the destination existed when planned (drives the TOCTOU guard). */\n destExistedOriginally: boolean;\n /** Destination exists and is not a self/case-only target → needs overwrite confirmation. */\n pending: boolean;\n}\n\n/**\n * Phase 1 (no mutation): validate source and destination, reject self-targets\n * and transfers into a subdirectory of the source, and stat the destination to\n * decide whether this pair needs an overwrite confirmation. No `mkdir` happens\n * here — the old flow created the destination's parent before asking, which\n * mutated the filesystem before a confirmation; that now waits for phase 2 (R14).\n *\n * `overwrite` is copy-only — move has no confirmation bypass — so a move always\n * passes false.\n */\nasync function planTransfer(\n op: PairOp,\n pair: { source: string; destination: string },\n fs: ToolCtx['fs'],\n overwrite: boolean,\n): Promise<TransferPlanResult> {\n let realSource: string;\n let opSource: string;\n let validDest: string;\n try {\n ({ realSource, opSource } = await validateTransferSource(op, pair.source, fs));\n validDest = await fs.pathGuard.validatePathForWrite(pair.destination);\n } catch (error) {\n return { status: 'fail', failure: pairFailure(pair, error) };\n }\n\n // Comparisons run on the resolved source; only the fs call in phase 2 uses\n // opSource. validatePathForWrite resolves the destination through a symlink\n // too, so both sides of every check must be resolved to match.\n const resolvedSource = resolve(realSource);\n const resolvedDest = resolve(validDest);\n\n const isSelf = resolvedSource === resolvedDest;\n const isCaseOnlyRename = !isSelf && isSamePath(resolvedSource, resolvedDest);\n\n // A copy onto a case-only variant of its own source is the same file, so it\n // is a no-op; the same rename is real work for move and proceeds.\n if (isSelf || (isCaseOnlyRename && op === 'copy')) {\n return { status: 'noop' };\n }\n\n if (!isCaseOnlyRename && isPathInsideDirectory(resolvedSource, resolvedDest)) {\n return {\n status: 'fail',\n failure: pairFailure(\n pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `Cannot ${op} a directory into its own subdirectory`,\n pair.source,\n ),\n ),\n };\n }\n\n const destExistedOriginally = !isCaseOnlyRename && (await destExists(fs, validDest, op));\n const pending = destExistedOriginally && !overwrite;\n\n return {\n status: 'plan',\n plan: { pair, opSource, validDest, isCaseOnlyRename, destExistedOriginally, pending },\n };\n}\n\n/**\n * Phase 2 (mutation): confirm an overwrite if needed, TOCTOU-check the\n * destination, create its parent, then rename (with cross-device fallback) or\n * copy. A declined/missing confirmation throws `CANCELLED`, collected as a\n * per-pair failure by the caller. A destination that appeared between plan and\n * mutation (created during the confirmation gap) also fails closed.\n */\nasync function executeTransfer(\n op: PairOp,\n plan: TransferPlan,\n ctx: Pick<ToolCtx, 'fs' | 'signal' | 'inputResponses' | 'log'>,\n pendingSorted: readonly string[],\n): Promise<TransferExecResult> {\n if (plan.pending) {\n const key = confirmKey(pendingSorted.indexOf(plan.validDest));\n const choice = readAcceptedChoice(ctx.inputResponses, key);\n if (choice === 'skip') {\n return { skipped: plan.pair.destination };\n }\n if (choice !== 'overwrite') {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: overwrite of \"${plan.pair.destination}\" was ${describeRefusal(ctx.inputResponses, key)}`,\n plan.pair.destination,\n );\n }\n }\n\n // TOCTOU check before any mutation: a destination that did not exist when\n // planned but exists now was created during the confirmation gap.\n if (\n !plan.isCaseOnlyRename &&\n !plan.destExistedOriginally &&\n (await destExists(ctx.fs, plan.validDest, op))\n ) {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: destination \"${plan.pair.destination}\" was created during confirmation.`,\n plan.pair.destination,\n );\n }\n\n await ctx.fs.mkdir(dirname(plan.validDest), { recursive: true });\n\n if (op === 'move') {\n await performRenameWithFallback(plan.opSource, plan.validDest, ctx.fs, plan.pair.source);\n ctx.log?.('info', `move: ${plan.pair.source} -> ${plan.pair.destination}`, 'move');\n } else {\n await ctx.fs.cp(plan.opSource, plan.validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n force: true,\n });\n }\n return { value: { from: plan.opSource, to: plan.validDest } };\n}\n\ntype TransferPlanResult =\n | { status: 'fail'; failure: MoveFailureItem }\n | { status: 'noop' }\n | { status: 'plan'; plan: TransferPlan };\n\n/** A discriminated union, so the fold below narrows without a generic cast. */\ntype TransferExecResult = { readonly skipped: string } | { readonly value: MoveItemResult };\n\nfunction pairFailure(\n pair: { source: string; destination: string },\n error: unknown,\n): MoveFailureItem {\n return {\n source: pair.source,\n destination: pair.destination,\n error: Problem.fromUnknown(error, ErrorCode.UNKNOWN, pair.source),\n };\n}\n\n/**\n * The source→destination sibling of `runOverPaths`: plan every pair, fail closed\n * on a duplicated destination, round-trip the overwrite confirmations as one\n * set, then execute what survived in parallel. `copy` and `move` differ only in\n * their `plan` and `execute` callbacks.\n */\nasync function runTransfers(\n items: readonly z.infer<typeof MoveItemSchema>[],\n ctx: ToolCtx,\n op: PairOp,\n overwrite: boolean,\n): Promise<\n | { results: MoveItemResult[]; skipped: string[]; failures: MoveFailureItem[] }\n | InputRequiredResult\n> {\n const { results: planned, errors: planErrors } = await processInParallel(\n items,\n (pair) => planTransfer(op, pair, ctx.fs, overwrite),\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n // plan callbacks are fail-closed (they return { status: 'fail' }, never\n // throw), so planErrors should always be empty. Surface the first rather\n // than silently dropping the item if a future plan callback violates that.\n const firstPlanError = planErrors[0];\n if (firstPlanError) throw firstPlanError.error;\n\n const failures: MoveFailureItem[] = [];\n const candidates: TransferPlan[] = [];\n for (const { value } of planned) {\n if (value.status === 'fail') failures.push(value.failure);\n else if (value.status === 'plan') candidates.push(value.plan);\n // 'noop' (self-copy / self-move) is silently skipped.\n }\n\n // Two sources targeting the same destination in one batch would otherwise\n // collapse to a single shared overwrite confirmation and let the second\n // one silently clobber the first's freshly-written content. Fail closed:\n // only the first plan per destination proceeds; later ones targeting the\n // same destination are reported as a per-item failure.\n const seenDest = new Set<string>();\n const ready: TransferPlan[] = [];\n for (const candidate of candidates) {\n const destKey = normalizeCaseForComparison(candidate.validDest);\n if (seenDest.has(destKey)) {\n failures.push(\n pairFailure(\n candidate.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${VERB[op]} cancelled: another entry in this batch already targets destination \"${candidate.pair.destination}\"`,\n candidate.pair.destination,\n ),\n ),\n );\n continue;\n }\n seenDest.add(destKey);\n ready.push(candidate);\n }\n\n const pendingSorted = ready\n .filter((p) => p.pending)\n .map((p) => p.validDest)\n .sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip({\n op,\n pending: pendingSorted,\n requestState: ctx.requestState,\n clientCapabilities: ctx.clientCapabilities,\n serverCtx: ctx.serverCtx,\n buildInputs: (dests) =>\n dests.map((dest, i) => ({\n key: confirmKey(i),\n message:\n op === 'move'\n ? `\"${dest}\" already exists. Overwrite it?`\n : `Destination \"${dest}\" already exists. Overwrite it?`,\n choices: ['overwrite', 'skip'],\n })),\n });\n if (round !== undefined) return round;\n }\n\n const total = ready.length;\n let completed = 0;\n const tick = (): void => {\n completed += 1;\n ctx.onProgress?.({ current: completed, total });\n };\n const { results: execResults, errors: execErrors } = await processInParallel(\n ready,\n async (readyPlan) => {\n try {\n return await executeTransfer(op, readyPlan, ctx, pendingSorted);\n } finally {\n tick();\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n for (const { error, index } of execErrors) {\n rethrowIfAborted(error);\n const failed = ready[index];\n if (failed) failures.push(pairFailure(failed.pair, error));\n }\n\n const results: MoveItemResult[] = [];\n const skipped: string[] = [];\n for (const { value } of execResults) {\n if ('skipped' in value) skipped.push(value.skipped);\n else results.push(value.value);\n }\n\n return { results, skipped, failures };\n}\n\nfunction buildSummary(\n verb: 'move' | 'copy',\n results: readonly MoveItemResult[],\n failures: readonly MoveFailureItem[],\n skipped: readonly string[],\n): string {\n const successCount = results.length;\n const failCount = failures.length;\n if (failCount === 0 && skipped.length === 0 && successCount === 1) {\n const result = results[0];\n if (result) {\n return `${verb}: ${pathLabel(result.from)} → ${pathLabel(result.to)}`;\n }\n }\n // Name each pair. \"move: 2 items\" left the caller to open structuredContent\n // to learn which two, and a partial failure was unreadable without it.\n // Skipped destinations are an outcome the user chose, not a failure — name\n // them, or an all-skipped call reads as \"copy: nothing\".\n const tokens = [\n ...results.map((r) => `${pathLabel(r.from)} → ${pathLabel(r.to)}`),\n ...skipped.map((dest) => `${pathLabel(dest)} SKIPPED`),\n ];\n const parts = [`${verb}: ${joinRoster(tokens) || 'nothing'}`];\n if (failCount > 0) parts.push(`${String(failCount)} failed`);\n return parts.join(' · ');\n}\n\ninterface TransferSource {\n /** Symlink resolved — the identity used for every same-target comparison. */\n realSource: string;\n /** The path the mutation runs on: symlink preserved for move, resolved for copy. */\n opSource: string;\n}\n\n/**\n * A move needs two views of its source. Renaming must operate on the link\n * itself, or moving a symlink would rename the file it points at and leave the\n * link dangling. Every collision check must instead compare the resolved\n * target, or a link moved onto itself (or onto its own target) reads as a real\n * move and renames the link over that target, destroying it. A copy reads\n * through the link, so both views are the resolved one.\n */\nasync function validateTransferSource(\n op: PairOp,\n source: string,\n fs: ToolCtx['fs'],\n): Promise<TransferSource> {\n try {\n const realSource = await fs.pathGuard.validateExistingPath(source);\n const opSource = op === 'move' ? await fs.pathGuard.validatePathForDelete(source) : realSource;\n return { realSource, opSource };\n } catch (error) {\n if (isFsError(error)) throw error;\n throw new FsError(ErrorCode.ACCESS_DENIED, `${VERB[op]} failed for ${source}`, source);\n }\n}\n\nasync function performRenameWithFallback(\n validSource: string,\n validDest: string,\n fsOps: Pick<GuardedFileSystem, 'rename' | 'cp' | 'rm'>,\n originalSource: string,\n): Promise<void> {\n try {\n await fsOps.rename(validSource, validDest);\n } catch (error: unknown) {\n rethrowIfAborted(error);\n\n if (!isNodeError(error) || error.code !== 'EXDEV') {\n throw error;\n }\n\n // EXDEV: cross-device rename. Copy then remove, preserving symlinks and\n // timestamps so link semantics survive the move.\n let copied = false;\n try {\n await fsOps.cp(validSource, validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n });\n copied = true;\n await fsOps.rm(validSource, { recursive: true, force: true });\n } catch (copyOrRemoveError) {\n rethrowIfAborted(copyOrRemoveError);\n if (copied) {\n // cp succeeded but rm failed: the destination already holds a complete\n // copy and the source remains. Surface the rm error as the cause so the\n // caller can recover (clean up the duplicate) instead of a silent generic\n // failure that hides the partial completion.\n const baseProblem = Problem.fromUnknown(\n copyOrRemoveError,\n ErrorCode.UNKNOWN,\n originalSource,\n );\n throw new FsError(\n baseProblem.code,\n `Cross-device move of ${originalSource}: copy succeeded but source removal failed (destination holds a copy): ${baseProblem.message}`,\n originalSource,\n copyOrRemoveError,\n );\n }\n throw copyOrRemoveError;\n }\n }\n}\n\nexport const MOVE = defineTool({\n name: 'move',\n title: 'Move or Copy Files',\n description:\n 'Move, rename, or copy files and directories to explicit destination paths (max 100 operations per call). ' +\n 'Pass moves: [{ source, destination }] — there is no single-pair form. ' +\n 'Parent directories are created automatically. Set copy=true to copy instead of move (sources are kept). ' +\n 'An existing destination prompts the user to confirm the overwrite, so the call returns without moving ' +\n 'anything until that confirmation comes back; copy=true with overwrite=true skips the prompt, move has no ' +\n 'such bypass. Self-moves are silently skipped.',\n input: MoveInputSchema,\n output: MoveOutputSchema,\n annotations: {\n readOnlyHint: false,\n idempotentHint: false,\n destructiveHint: true,\n openWorldHint: false,\n },\n progress: (args) => {\n const label = args.copy ? 'Copy' : 'Move';\n if (args.moves.length === 1) {\n const move = args.moves[0];\n return {\n label,\n subject: `${basename(move?.source ?? '')} → ${basename(move?.destination ?? '')}`,\n };\n }\n return { label, subject: `${String(args.moves.length)} files` };\n },\n defaultErrorCode: ErrorCode.UNKNOWN,\n accessPaths: (args) => args.moves.flatMap((m) => [m.source, m.destination]),\n run: async (args, ctx) => {\n const op: PairOp = args.copy ? 'copy' : 'move';\n // `overwrite` is copy-only; move has no confirmation bypass.\n const outcome = await runTransfers(args.moves, ctx, op, args.copy && args.overwrite);\n // input_required is a return value, not a completed call: surface it\n // verbatim so the executor short-circuits before building a CallToolResult.\n if (isInputRequiredResult(outcome)) return outcome;\n const { results, skipped, failures } = outcome;\n return {\n structured: {\n moves: results,\n ...(failures.length > 0 ? { failures } : {}),\n ...(skipped.length > 0 ? { skipped } : {}),\n },\n text: buildSummary(op, results, failures, skipped),\n // Every requested pair failed: no move, no copy, no user-chosen skip - the\n // call did nothing. A skip is work the caller asked for, so it counts.\n isError: failures.length > 0 && results.length + skipped.length === 0,\n };\n },\n});\n"]}
|
package/dist/tools/patch.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PATCH: import("./define.
|
|
1
|
+
export declare const PATCH: import("./define.ts").DefinedTool;
|
|
2
2
|
//# sourceMappingURL=patch.d.ts.map
|
package/dist/tools/patch.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
2
|
import * as z from 'zod/v4';
|
|
3
3
|
import { applyPatch, parsePatch } from 'diff';
|
|
4
|
-
import { ErrorCode, FsError } from
|
|
5
|
-
import { buildWrittenFileMeta } from
|
|
6
|
-
import { defaultFalseBoolean, FileKind, IsoDateTime, NonNegInt, RequiredPath, } from
|
|
7
|
-
import { defineTool } from
|
|
4
|
+
import { ErrorCode, FsError } from "../core/errors.js";
|
|
5
|
+
import { buildWrittenFileMeta } from "../core/file-uri.js";
|
|
6
|
+
import { defaultFalseBoolean, FileKind, IsoDateTime, NonNegInt, RequiredPath, } from "../core/schema.js";
|
|
7
|
+
import { defineTool } from "./define.js";
|
|
8
8
|
const PatchInputSchema = z.strictObject({
|
|
9
9
|
path: RequiredPath.describe('File to apply the diff to'),
|
|
10
10
|
diff: z
|
|
@@ -19,7 +19,7 @@ const PatchOutputSchema = z.strictObject({
|
|
|
19
19
|
lineCount: NonNegInt.describe('Number of lines in the file after patching'),
|
|
20
20
|
mimeType: z.string().describe('Detected MIME type of the file'),
|
|
21
21
|
kind: FileKind.describe('Broad file kind: text, binary, image, audio, or pdf'),
|
|
22
|
-
resourceUri: z.string().describe('Resource URI pointing to the patched file content'),
|
|
22
|
+
resourceUri: z.string().optional().describe('Resource URI pointing to the patched file content'),
|
|
23
23
|
modified: IsoDateTime.describe('Last modification timestamp after patching (ISO 8601 UTC)'),
|
|
24
24
|
linesAdded: NonNegInt.describe('Number of lines added by the patch'),
|
|
25
25
|
linesRemoved: NonNegInt.describe('Number of lines removed by the patch'),
|
|
@@ -77,10 +77,10 @@ async function handlePatch(args, ctx) {
|
|
|
77
77
|
lineCount: meta.lineCount,
|
|
78
78
|
mimeType: meta.mimeType,
|
|
79
79
|
kind: meta.kind,
|
|
80
|
-
resourceUri: meta.resourceUri,
|
|
81
80
|
modified: stats.mtime.toISOString(),
|
|
82
81
|
linesAdded,
|
|
83
82
|
linesRemoved,
|
|
83
|
+
...(meta.resourceUri !== undefined ? { resourceUri: meta.resourceUri } : {}),
|
|
84
84
|
diff: args.diff,
|
|
85
85
|
},
|
|
86
86
|
// A dry run exists to be previewed, so it keeps returning the patched
|
|
@@ -103,10 +103,10 @@ async function handlePatch(args, ctx) {
|
|
|
103
103
|
lineCount: meta.lineCount,
|
|
104
104
|
mimeType: meta.mimeType,
|
|
105
105
|
kind: meta.kind,
|
|
106
|
-
resourceUri: meta.resourceUri,
|
|
107
106
|
modified: fileStats.mtime.toISOString(),
|
|
108
107
|
linesAdded,
|
|
109
108
|
linesRemoved,
|
|
109
|
+
...(meta.resourceUri !== undefined ? { resourceUri: meta.resourceUri } : {}),
|
|
110
110
|
},
|
|
111
111
|
text: summaryText,
|
|
112
112
|
...(meta.resourceLink !== undefined ? { resources: [meta.resourceLink] } : {}),
|
package/dist/tools/patch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patch.js","sourceRoot":"","sources":["../../src/tools/patch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\nimport { applyPatch, parsePatch } from 'diff';\n\nimport { ErrorCode, FsError } from '../core/errors.js';\nimport { buildWrittenFileMeta } from '../core/file-uri.js';\nimport {\n defaultFalseBoolean,\n FileKind,\n IsoDateTime,\n NonNegInt,\n RequiredPath,\n} from '../core/schema.js';\nimport { defineTool, type ToolCtx } from './define.js';\n\nconst PatchInputSchema = z.strictObject({\n path: RequiredPath.describe('File to apply the diff to'),\n diff: z\n .string()\n .min(1)\n .describe('Single-file unified diff to apply (as produced by the diff tool or edit dry-run)'),\n dryRun: defaultFalseBoolean('Preview the result without writing (default: false)'),\n});\n\nconst PatchOutputSchema = z.strictObject({\n path: z.string().describe('Resolved absolute path of the patched file'),\n size: NonNegInt.describe('File size in bytes after patching'),\n lineCount: NonNegInt.describe('Number of lines in the file after patching'),\n mimeType: z.string().describe('Detected MIME type of the file'),\n kind: FileKind.describe('Broad file kind: text, binary, image, audio, or pdf'),\n resourceUri: z.string().describe('Resource URI pointing to the patched file content'),\n modified: IsoDateTime.describe('Last modification timestamp after patching (ISO 8601 UTC)'),\n linesAdded: NonNegInt.describe('Number of lines added by the patch'),\n linesRemoved: NonNegInt.describe('Number of lines removed by the patch'),\n diff: z.string().optional().describe('Unified diff preview (present only in dryRun mode)'),\n});\n\n// Hunk lines are the raw unified lines with their +/-/ leading prefix (no\n// +++/--- file headers inside hunks), so the first char is a reliable marker.\nfunction countAddedRemoved(parsedPatch: ReturnType<typeof parsePatch>[number]): {\n linesAdded: number;\n linesRemoved: number;\n} {\n let linesAdded = 0;\n let linesRemoved = 0;\n for (const hunk of parsedPatch.hunks) {\n for (const line of hunk.lines) {\n if (line.startsWith('+')) linesAdded += 1;\n else if (line.startsWith('-')) linesRemoved += 1;\n }\n }\n return { linesAdded, linesRemoved };\n}\n\nasync function handlePatch(\n args: z.infer<typeof PatchInputSchema>,\n ctx: ToolCtx,\n): Promise<{\n structured: z.infer<typeof PatchOutputSchema>;\n text: string;\n resources?: ContentBlock[];\n}> {\n const { validPath, content, stats } = await ctx.fs.readEditableText(args.path, {\n signal: ctx.signal,\n tool: 'patch',\n });\n\n // patch is single-file only: a multi-file unified diff (parsePatch length > 1)\n // is rejected. Multi-file application needs per-file PathGuard-resolved loaders\n // and is deferred (out of scope).\n const parsed = parsePatch(args.diff);\n if (parsed.length !== 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `patch accepts a single-file unified diff; received ${parsed.length} file(s)`,\n args.path,\n );\n }\n const parsedPatch = parsed[0];\n if (!parsedPatch || parsedPatch.hunks.length === 0) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch accepts a single-file unified diff with at least one hunk; received an empty patch',\n args.path,\n );\n }\n // applyPatch returns false (strict) on hunk mismatch — not a falsy empty string,\n // so `=== false` distinguishes a no-op patch ('') from a failed one.\n const patched = applyPatch(content, parsedPatch);\n if (patched === false) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch did not apply cleanly: hunk context does not match file content',\n args.path,\n );\n }\n\n const { linesAdded, linesRemoved } = countAddedRemoved(parsedPatch);\n // The patched file used to come back whole in the text block, so a one-line\n // hunk against a 5000-line file returned 5000 lines. Every other write tool\n // answers with a summary; the result is reachable via `resourceUri` or a\n // follow-up `read` when the caller actually wants the bytes.\n const summaryText = `patch: ${basename(args.path)} +${String(linesAdded)} -${String(linesRemoved)}`;\n\n if (args.dryRun) {\n const meta = buildWrittenFileMeta(validPath, patched, ctx.resourceStore);\n return {\n structured: {\n path: validPath,\n size: meta.size,\n lineCount: meta.lineCount,\n mimeType: meta.mimeType,\n kind: meta.kind,\n resourceUri: meta.resourceUri,\n modified: stats.mtime.toISOString(),\n linesAdded,\n linesRemoved,\n diff: args.diff,\n },\n // A dry run exists to be previewed, so it keeps returning the patched\n // text; only the write path summarizes, where the bytes are on disk.\n text: patched,\n ...(meta.resourceLink !== undefined ? { resources: [meta.resourceLink] } : {}),\n };\n }\n\n await ctx.fs.writeFile(args.path, patched, { encoding: 'utf-8', signal: ctx.signal });\n ctx.log?.('info', `patch: ${args.path} (+${linesAdded}/-${linesRemoved})`, 'patch');\n\n // `modified` is read from a post-write stat and is advisory: under a concurrent\n // writer it may reflect that writer's mtime while `size`/content come from this\n // patch's atomic write. The file content itself is always consistent.\n const { stats: fileStats } = await ctx.fs.stat(args.path, { signal: ctx.signal });\n const meta = buildWrittenFileMeta(validPath, patched, ctx.resourceStore);\n return {\n structured: {\n path: validPath,\n size: meta.size,\n lineCount: meta.lineCount,\n mimeType: meta.mimeType,\n kind: meta.kind,\n resourceUri: meta.resourceUri,\n modified: fileStats.mtime.toISOString(),\n linesAdded,\n linesRemoved,\n },\n text: summaryText,\n ...(meta.resourceLink !== undefined ? { resources: [meta.resourceLink] } : {}),\n };\n}\n\nexport const PATCH = defineTool({\n name: 'patch',\n title: 'Patch',\n description:\n 'Apply a single-file unified diff to one file and write the result. Pass { path, diff }. ' +\n 'Use after inspecting a diff tool dry-run: pass the diff blob directly instead of re-expressing it as line edits. ' +\n 'Rejects multi-file diffs and diffs whose hunk context does not match the file. ' +\n 'Set dryRun=true to preview the result without writing.',\n input: PatchInputSchema,\n output: PatchOutputSchema,\n annotations: {\n readOnlyHint: false,\n idempotentHint: false,\n destructiveHint: true,\n openWorldHint: false,\n },\n progress: (args) => ({\n label: args.dryRun ? 'Patch [dry run]' : 'Patch',\n subject: basename(args.path),\n }),\n accessPaths: (args) => [args.path],\n run: (args, ctx) => handlePatch(args, ctx),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"patch.js","sourceRoot":"","sources":["../../src/tools/patch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\nimport { applyPatch, parsePatch } from 'diff';\n\nimport { ErrorCode, FsError } from '../core/errors.ts';\nimport { buildWrittenFileMeta } from '../core/file-uri.ts';\nimport {\n defaultFalseBoolean,\n FileKind,\n IsoDateTime,\n NonNegInt,\n RequiredPath,\n} from '../core/schema.ts';\nimport { defineTool, type ToolCtx } from './define.ts';\n\nconst PatchInputSchema = z.strictObject({\n path: RequiredPath.describe('File to apply the diff to'),\n diff: z\n .string()\n .min(1)\n .describe('Single-file unified diff to apply (as produced by the diff tool or edit dry-run)'),\n dryRun: defaultFalseBoolean('Preview the result without writing (default: false)'),\n});\n\nconst PatchOutputSchema = z.strictObject({\n path: z.string().describe('Resolved absolute path of the patched file'),\n size: NonNegInt.describe('File size in bytes after patching'),\n lineCount: NonNegInt.describe('Number of lines in the file after patching'),\n mimeType: z.string().describe('Detected MIME type of the file'),\n kind: FileKind.describe('Broad file kind: text, binary, image, audio, or pdf'),\n resourceUri: z.string().optional().describe('Resource URI pointing to the patched file content'),\n modified: IsoDateTime.describe('Last modification timestamp after patching (ISO 8601 UTC)'),\n linesAdded: NonNegInt.describe('Number of lines added by the patch'),\n linesRemoved: NonNegInt.describe('Number of lines removed by the patch'),\n diff: z.string().optional().describe('Unified diff preview (present only in dryRun mode)'),\n});\n\n// Hunk lines are the raw unified lines with their +/-/ leading prefix (no\n// +++/--- file headers inside hunks), so the first char is a reliable marker.\nfunction countAddedRemoved(parsedPatch: ReturnType<typeof parsePatch>[number]): {\n linesAdded: number;\n linesRemoved: number;\n} {\n let linesAdded = 0;\n let linesRemoved = 0;\n for (const hunk of parsedPatch.hunks) {\n for (const line of hunk.lines) {\n if (line.startsWith('+')) linesAdded += 1;\n else if (line.startsWith('-')) linesRemoved += 1;\n }\n }\n return { linesAdded, linesRemoved };\n}\n\nasync function handlePatch(\n args: z.infer<typeof PatchInputSchema>,\n ctx: ToolCtx,\n): Promise<{\n structured: z.infer<typeof PatchOutputSchema>;\n text: string;\n resources?: ContentBlock[];\n}> {\n const { validPath, content, stats } = await ctx.fs.readEditableText(args.path, {\n signal: ctx.signal,\n tool: 'patch',\n });\n\n // patch is single-file only: a multi-file unified diff (parsePatch length > 1)\n // is rejected. Multi-file application needs per-file PathGuard-resolved loaders\n // and is deferred (out of scope).\n const parsed = parsePatch(args.diff);\n if (parsed.length !== 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `patch accepts a single-file unified diff; received ${parsed.length} file(s)`,\n args.path,\n );\n }\n const parsedPatch = parsed[0];\n if (!parsedPatch || parsedPatch.hunks.length === 0) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch accepts a single-file unified diff with at least one hunk; received an empty patch',\n args.path,\n );\n }\n // applyPatch returns false (strict) on hunk mismatch — not a falsy empty string,\n // so `=== false` distinguishes a no-op patch ('') from a failed one.\n const patched = applyPatch(content, parsedPatch);\n if (patched === false) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch did not apply cleanly: hunk context does not match file content',\n args.path,\n );\n }\n\n const { linesAdded, linesRemoved } = countAddedRemoved(parsedPatch);\n // The patched file used to come back whole in the text block, so a one-line\n // hunk against a 5000-line file returned 5000 lines. Every other write tool\n // answers with a summary; the result is reachable via `resourceUri` or a\n // follow-up `read` when the caller actually wants the bytes.\n const summaryText = `patch: ${basename(args.path)} +${String(linesAdded)} -${String(linesRemoved)}`;\n\n if (args.dryRun) {\n const meta = buildWrittenFileMeta(validPath, patched, ctx.resourceStore);\n return {\n structured: {\n path: validPath,\n size: meta.size,\n lineCount: meta.lineCount,\n mimeType: meta.mimeType,\n kind: meta.kind,\n modified: stats.mtime.toISOString(),\n linesAdded,\n linesRemoved,\n ...(meta.resourceUri !== undefined ? { resourceUri: meta.resourceUri } : {}),\n diff: args.diff,\n },\n // A dry run exists to be previewed, so it keeps returning the patched\n // text; only the write path summarizes, where the bytes are on disk.\n text: patched,\n ...(meta.resourceLink !== undefined ? { resources: [meta.resourceLink] } : {}),\n };\n }\n\n await ctx.fs.writeFile(args.path, patched, { encoding: 'utf-8', signal: ctx.signal });\n ctx.log?.('info', `patch: ${args.path} (+${linesAdded}/-${linesRemoved})`, 'patch');\n\n // `modified` is read from a post-write stat and is advisory: under a concurrent\n // writer it may reflect that writer's mtime while `size`/content come from this\n // patch's atomic write. The file content itself is always consistent.\n const { stats: fileStats } = await ctx.fs.stat(args.path, { signal: ctx.signal });\n const meta = buildWrittenFileMeta(validPath, patched, ctx.resourceStore);\n return {\n structured: {\n path: validPath,\n size: meta.size,\n lineCount: meta.lineCount,\n mimeType: meta.mimeType,\n kind: meta.kind,\n modified: fileStats.mtime.toISOString(),\n linesAdded,\n linesRemoved,\n ...(meta.resourceUri !== undefined ? { resourceUri: meta.resourceUri } : {}),\n },\n text: summaryText,\n ...(meta.resourceLink !== undefined ? { resources: [meta.resourceLink] } : {}),\n };\n}\n\nexport const PATCH = defineTool({\n name: 'patch',\n title: 'Patch',\n description:\n 'Apply a single-file unified diff to one file and write the result. Pass { path, diff }. ' +\n 'Use after inspecting a diff tool dry-run: pass the diff blob directly instead of re-expressing it as line edits. ' +\n 'Rejects multi-file diffs and diffs whose hunk context does not match the file. ' +\n 'Set dryRun=true to preview the result without writing.',\n input: PatchInputSchema,\n output: PatchOutputSchema,\n annotations: {\n readOnlyHint: false,\n idempotentHint: false,\n destructiveHint: true,\n openWorldHint: false,\n },\n progress: (args) => ({\n label: args.dryRun ? 'Patch [dry run]' : 'Patch',\n subject: basename(args.path),\n }),\n accessPaths: (args) => [args.path],\n run: (args, ctx) => handlePatch(args, ctx),\n});\n"]}
|
package/dist/tools/progress.d.ts
CHANGED
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
import type { Notification } from '@modelcontextprotocol/server';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
kind: 'complete';
|
|
9
|
-
current: number;
|
|
10
|
-
total?: number;
|
|
11
|
-
message: string;
|
|
12
|
-
} | {
|
|
13
|
-
kind: 'fail';
|
|
14
|
-
current: number;
|
|
15
|
-
total?: number;
|
|
16
|
-
message: string;
|
|
17
|
-
error: unknown;
|
|
18
|
-
};
|
|
2
|
+
/** Where progress frames go: the `notifications/progress` sink for one `progressToken`. */
|
|
3
|
+
export interface ProgressSink {
|
|
4
|
+
toolName: string;
|
|
5
|
+
token: string | number;
|
|
6
|
+
notify: (n: Notification) => Promise<void>;
|
|
7
|
+
}
|
|
19
8
|
interface ProgressSessionOptions {
|
|
20
9
|
label: string;
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
/** Absent when the client sent no `progressToken`; the session then only tracks state. */
|
|
11
|
+
sink?: ProgressSink;
|
|
12
|
+
/** Override the tick rate-limit window. Default: 50ms. */
|
|
23
13
|
rateLimitMs?: number;
|
|
24
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* One tool call's progress stream: a monotonic cursor, a tick rate limit, and
|
|
17
|
+
* the wire guard the spec requires (`progress` strictly increases per token).
|
|
18
|
+
* Terminal frames (`complete`, `fail`) always reach the wire — they carry the
|
|
19
|
+
* outcome message — so they advance past the last value instead of dropping.
|
|
20
|
+
*/
|
|
25
21
|
export declare class ProgressSession {
|
|
26
22
|
#private;
|
|
27
23
|
constructor(opts: ProgressSessionOptions);
|
|
@@ -31,15 +27,8 @@ export declare class ProgressSession {
|
|
|
31
27
|
message?: string;
|
|
32
28
|
}): void;
|
|
33
29
|
complete(message: string): void;
|
|
34
|
-
fail(
|
|
35
|
-
|
|
36
|
-
export declare class McpProgressSink {
|
|
37
|
-
#private;
|
|
38
|
-
private readonly toolName;
|
|
39
|
-
private readonly token;
|
|
40
|
-
private readonly notify;
|
|
41
|
-
constructor(toolName: string, token: string | number, notify: (n: Notification) => Promise<void>);
|
|
42
|
-
emit(event: ProgressEvent): void;
|
|
30
|
+
fail(message?: string): void;
|
|
31
|
+
/** Await every notification in flight; a failed send is logged, never thrown. */
|
|
43
32
|
flush(): Promise<void>;
|
|
44
33
|
}
|
|
45
34
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,8BAA8B,CAAC;AAK7F,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,MAAM,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED,UAAU,sBAAsB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;;;GAKG;AACH,qBAAa,eAAe;;gBAYd,IAAI,EAAE,sBAAsB;IAUxC,GAAG,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAQvE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM/B,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAM5B,iFAAiF;IAC3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAoD7B"}
|
package/dist/tools/progress.js
CHANGED
|
@@ -1,120 +1,74 @@
|
|
|
1
|
-
import { formatUnknownErrorMessage } from
|
|
2
|
-
import { Logger } from
|
|
3
|
-
function reportDetachedError(toolName, context, error) {
|
|
4
|
-
const message = formatUnknownErrorMessage(error);
|
|
5
|
-
Logger.emit('warning', `${toolName}: ${context} failed: ${message}`);
|
|
6
|
-
}
|
|
1
|
+
import { formatUnknownErrorMessage } from "../core/errors.js";
|
|
2
|
+
import { Logger } from "../core/observability.js";
|
|
7
3
|
const DEFAULT_RATE_LIMIT_MS = 50;
|
|
4
|
+
/**
|
|
5
|
+
* One tool call's progress stream: a monotonic cursor, a tick rate limit, and
|
|
6
|
+
* the wire guard the spec requires (`progress` strictly increases per token).
|
|
7
|
+
* Terminal frames (`complete`, `fail`) always reach the wire — they carry the
|
|
8
|
+
* outcome message — so they advance past the last value instead of dropping.
|
|
9
|
+
*/
|
|
8
10
|
export class ProgressSession {
|
|
9
11
|
#label;
|
|
10
12
|
#sink;
|
|
11
13
|
#rateLimitMs;
|
|
12
14
|
#startTime;
|
|
15
|
+
#pending = new Set();
|
|
13
16
|
#cursor = 0;
|
|
14
17
|
#lastSentMs = 0;
|
|
18
|
+
#lastProgress = -1;
|
|
15
19
|
#done = false;
|
|
16
20
|
constructor(opts) {
|
|
17
21
|
this.#label = opts.label;
|
|
18
22
|
this.#sink = opts.sink;
|
|
19
23
|
this.#rateLimitMs = opts.rateLimitMs ?? DEFAULT_RATE_LIMIT_MS;
|
|
20
|
-
|
|
21
|
-
this.#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.#dispatch({
|
|
25
|
-
kind: 'tick',
|
|
26
|
-
current: 0,
|
|
27
|
-
message: this.#label,
|
|
28
|
-
});
|
|
24
|
+
this.#startTime = Date.now();
|
|
25
|
+
this.#lastSentMs = this.#startTime - this.#rateLimitMs;
|
|
26
|
+
// Synthetic start tick: the wire sees 0 at session creation.
|
|
27
|
+
this.#emit('tick', 0, undefined, this.#label);
|
|
29
28
|
}
|
|
30
29
|
set(input) {
|
|
31
30
|
if (this.#done)
|
|
32
31
|
return;
|
|
33
|
-
//
|
|
34
|
-
// constructor already dispatched current: 0. A repeated or backward tick
|
|
35
|
-
// would put a duplicate value on the wire — drop it.
|
|
32
|
+
// A repeated or backward tick would put a duplicate value on the wire — drop it.
|
|
36
33
|
if (input.current <= this.#cursor)
|
|
37
34
|
return;
|
|
38
35
|
this.#cursor = input.current;
|
|
39
|
-
this.#
|
|
40
|
-
kind: 'tick',
|
|
41
|
-
current: this.#cursor,
|
|
42
|
-
...(input.total !== undefined ? { total: input.total } : {}),
|
|
43
|
-
message: input.message ?? this.#label,
|
|
44
|
-
});
|
|
36
|
+
this.#emit('tick', this.#cursor, input.total, input.message ?? this.#label);
|
|
45
37
|
}
|
|
46
38
|
complete(message) {
|
|
47
39
|
if (this.#done)
|
|
48
40
|
return;
|
|
49
41
|
this.#done = true;
|
|
50
|
-
this.#
|
|
51
|
-
kind: 'complete',
|
|
52
|
-
current: this.#cursor,
|
|
53
|
-
message,
|
|
54
|
-
});
|
|
42
|
+
this.#emit('complete', this.#cursor, undefined, message);
|
|
55
43
|
}
|
|
56
|
-
fail(
|
|
44
|
+
fail(message) {
|
|
57
45
|
if (this.#done)
|
|
58
46
|
return;
|
|
59
47
|
this.#done = true;
|
|
60
|
-
this.#
|
|
61
|
-
kind: 'fail',
|
|
62
|
-
current: this.#cursor,
|
|
63
|
-
message: message ?? this.#label,
|
|
64
|
-
error,
|
|
65
|
-
});
|
|
48
|
+
this.#emit('fail', this.#cursor, undefined, message ?? this.#label);
|
|
66
49
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
50
|
+
/** Await every notification in flight; a failed send is logged, never thrown. */
|
|
51
|
+
async flush() {
|
|
52
|
+
if (this.#pending.size > 0)
|
|
53
|
+
await Promise.allSettled([...this.#pending]);
|
|
54
|
+
}
|
|
55
|
+
#emit(kind, current, total, message) {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
if (kind === 'tick') {
|
|
58
|
+
// Widen the window after 5s of execution; a rateLimitMs of 0 (tests)
|
|
59
|
+
// stays 0 only inside the first 5s, which every test run fits in.
|
|
60
|
+
const window = now - this.#startTime > 5000 ? Math.max(this.#rateLimitMs, 250) : this.#rateLimitMs;
|
|
61
|
+
if (now - this.#lastSentMs < window)
|
|
62
|
+
return;
|
|
70
63
|
}
|
|
71
|
-
this.#lastSentMs =
|
|
64
|
+
this.#lastSentMs = now;
|
|
72
65
|
if (!this.#sink)
|
|
73
66
|
return;
|
|
74
|
-
|
|
75
|
-
this.#sink.emit(event);
|
|
76
|
-
}
|
|
77
|
-
catch (err) {
|
|
78
|
-
// A sink failure must never fail the tool call it is reporting on.
|
|
79
|
-
Logger.warn('progress sink emit failed', { eventKind: event.kind, err });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
#shouldRateLimit(event) {
|
|
83
|
-
if (event.kind !== 'tick') {
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
// Widen the window after 5s of execution; a rateLimitMs of 0 (tests) stays 0
|
|
87
|
-
// only inside the first 5s, which every test run fits in.
|
|
88
|
-
const now = Date.now();
|
|
89
|
-
const effectiveRateLimit = now - this.#startTime > 5000 ? Math.max(this.#rateLimitMs, 250) : this.#rateLimitMs;
|
|
90
|
-
const elapsed = now - this.#lastSentMs;
|
|
91
|
-
return elapsed < effectiveRateLimit;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
export class McpProgressSink {
|
|
95
|
-
toolName;
|
|
96
|
-
token;
|
|
97
|
-
notify;
|
|
98
|
-
#pending = new Set();
|
|
99
|
-
#lastProgress = -1;
|
|
100
|
-
constructor(toolName, token, notify) {
|
|
101
|
-
this.toolName = toolName;
|
|
102
|
-
this.token = token;
|
|
103
|
-
this.notify = notify;
|
|
104
|
-
}
|
|
105
|
-
emit(event) {
|
|
106
|
-
let current = event.current;
|
|
107
|
-
let total = event.total;
|
|
108
|
-
if (event.kind === 'complete') {
|
|
67
|
+
if (kind === 'complete') {
|
|
109
68
|
current = total ?? current;
|
|
110
69
|
total = current;
|
|
111
70
|
}
|
|
112
|
-
if (
|
|
113
|
-
// A terminal frame carries the outcome message, so it must reach the
|
|
114
|
-
// wire. It repeats the cursor whenever the session has no `total` (there
|
|
115
|
-
// is nothing higher to report), and the spec requires progress to
|
|
116
|
-
// increase per token — so advance past the last value instead of dropping
|
|
117
|
-
// the frame.
|
|
71
|
+
if (kind !== 'tick') {
|
|
118
72
|
current = Math.max(current, this.#lastProgress + 1);
|
|
119
73
|
if (total !== undefined && total < current)
|
|
120
74
|
total = current;
|
|
@@ -123,28 +77,22 @@ export class McpProgressSink {
|
|
|
123
77
|
if (current <= this.#lastProgress)
|
|
124
78
|
return;
|
|
125
79
|
this.#lastProgress = current;
|
|
126
|
-
const
|
|
127
|
-
|
|
80
|
+
const { toolName, token, notify } = this.#sink;
|
|
81
|
+
const params = {
|
|
82
|
+
progressToken: token,
|
|
128
83
|
progress: current,
|
|
129
84
|
...(total !== undefined ? { total } : {}),
|
|
130
|
-
message
|
|
85
|
+
message,
|
|
131
86
|
};
|
|
132
|
-
const promise =
|
|
133
|
-
method: 'notifications/progress',
|
|
134
|
-
params: notificationParams,
|
|
135
|
-
})
|
|
87
|
+
const promise = notify({ method: 'notifications/progress', params })
|
|
136
88
|
.catch((error) => {
|
|
137
|
-
|
|
89
|
+
// A sink failure must never fail the tool call it is reporting on.
|
|
90
|
+
Logger.warn(`${toolName}: progressNotification failed: ${formatUnknownErrorMessage(error)}`);
|
|
138
91
|
})
|
|
139
92
|
.finally(() => {
|
|
140
93
|
this.#pending.delete(promise);
|
|
141
94
|
});
|
|
142
95
|
this.#pending.add(promise);
|
|
143
96
|
}
|
|
144
|
-
async flush() {
|
|
145
|
-
if (this.#pending.size > 0) {
|
|
146
|
-
await Promise.allSettled([...this.#pending]);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
97
|
}
|
|
150
98
|
//# sourceMappingURL=progress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Notification, ProgressNotificationParams } from '@modelcontextprotocol/server';\n\nimport { formatUnknownErrorMessage } from '../core/errors.js';\nimport { Logger } from '../core/observability.js';\n\nfunction reportDetachedError(toolName: string, context: string, error: unknown): void {\n const message = formatUnknownErrorMessage(error);\n Logger.emit('warning', `${toolName}: ${context} failed: ${message}`);\n}\n\ntype ProgressEvent =\n | { kind: 'tick'; current: number; total?: number; message: string }\n | { kind: 'complete'; current: number; total?: number; message: string }\n | {\n kind: 'fail';\n current: number;\n total?: number;\n message: string;\n error: unknown;\n };\n\ninterface ProgressSessionOptions {\n label: string;\n sink?: McpProgressSink;\n /** Override the rate limit window. Default: 50ms. */\n rateLimitMs?: number;\n}\n\nconst DEFAULT_RATE_LIMIT_MS = 50;\n\nexport class ProgressSession {\n readonly #label: string;\n readonly #sink: McpProgressSink | undefined;\n readonly #rateLimitMs: number;\n readonly #startTime: number;\n\n #cursor = 0;\n #lastSentMs = 0;\n #done = false;\n\n constructor(opts: ProgressSessionOptions) {\n this.#label = opts.label;\n this.#sink = opts.sink;\n this.#rateLimitMs = opts.rateLimitMs ?? DEFAULT_RATE_LIMIT_MS;\n\n const now = Date.now();\n this.#startTime = now;\n this.#lastSentMs = now - this.#rateLimitMs;\n\n // Synthetic start tick — preserves today's \"fire 0 at session creation\" wire behavior.\n this.#dispatch({\n kind: 'tick',\n current: 0,\n message: this.#label,\n });\n }\n\n set(input: { current: number; total?: number; message?: string }): void {\n if (this.#done) return;\n // The spec requires `progress` to increase on every notification, and the\n // constructor already dispatched current: 0. A repeated or backward tick\n // would put a duplicate value on the wire — drop it.\n if (input.current <= this.#cursor) return;\n this.#cursor = input.current;\n this.#dispatch({\n kind: 'tick',\n current: this.#cursor,\n ...(input.total !== undefined ? { total: input.total } : {}),\n message: input.message ?? this.#label,\n });\n }\n\n complete(message: string): void {\n if (this.#done) return;\n this.#done = true;\n this.#dispatch({\n kind: 'complete',\n current: this.#cursor,\n message,\n });\n }\n\n fail(error: unknown, message?: string): void {\n if (this.#done) return;\n this.#done = true;\n this.#dispatch({\n kind: 'fail',\n current: this.#cursor,\n message: message ?? this.#label,\n error,\n });\n }\n\n #dispatch(event: ProgressEvent): void {\n if (this.#shouldRateLimit(event)) {\n return;\n }\n\n this.#lastSentMs = Date.now();\n\n if (!this.#sink) return;\n try {\n this.#sink.emit(event);\n } catch (err) {\n // A sink failure must never fail the tool call it is reporting on.\n Logger.warn('progress sink emit failed', { eventKind: event.kind, err });\n }\n }\n\n #shouldRateLimit(event: ProgressEvent): boolean {\n if (event.kind !== 'tick') {\n return false;\n }\n\n // Widen the window after 5s of execution; a rateLimitMs of 0 (tests) stays 0\n // only inside the first 5s, which every test run fits in.\n const now = Date.now();\n const effectiveRateLimit =\n now - this.#startTime > 5000 ? Math.max(this.#rateLimitMs, 250) : this.#rateLimitMs;\n\n const elapsed = now - this.#lastSentMs;\n return elapsed < effectiveRateLimit;\n }\n}\n\nexport class McpProgressSink {\n private readonly toolName: string;\n private readonly token: string | number;\n private readonly notify: (n: Notification) => Promise<void>;\n readonly #pending = new Set<Promise<void>>();\n #lastProgress = -1;\n\n constructor(\n toolName: string,\n token: string | number,\n notify: (n: Notification) => Promise<void>,\n ) {\n this.toolName = toolName;\n this.token = token;\n this.notify = notify;\n }\n\n emit(event: ProgressEvent): void {\n let current = event.current;\n let total = event.total;\n if (event.kind === 'complete') {\n current = total ?? current;\n total = current;\n }\n if (event.kind !== 'tick') {\n // A terminal frame carries the outcome message, so it must reach the\n // wire. It repeats the cursor whenever the session has no `total` (there\n // is nothing higher to report), and the spec requires progress to\n // increase per token — so advance past the last value instead of dropping\n // the frame.\n current = Math.max(current, this.#lastProgress + 1);\n if (total !== undefined && total < current) total = current;\n }\n // Spec: progress must increase on every notification for the same token.\n if (current <= this.#lastProgress) return;\n this.#lastProgress = current;\n const notificationParams: ProgressNotificationParams = {\n progressToken: this.token,\n progress: current,\n ...(total !== undefined ? { total } : {}),\n message: event.message,\n };\n const promise = this.notify({\n method: 'notifications/progress',\n params: notificationParams,\n })\n .catch((error: unknown) => {\n reportDetachedError(this.toolName, 'progressNotification', error);\n })\n .finally(() => {\n this.#pending.delete(promise);\n });\n this.#pending.add(promise);\n }\n\n async flush(): Promise<void> {\n if (this.#pending.size > 0) {\n await Promise.allSettled([...this.#pending]);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Notification, ProgressNotificationParams } from '@modelcontextprotocol/server';\n\nimport { formatUnknownErrorMessage } from '../core/errors.ts';\nimport { Logger } from '../core/observability.ts';\n\n/** Where progress frames go: the `notifications/progress` sink for one `progressToken`. */\nexport interface ProgressSink {\n toolName: string;\n token: string | number;\n notify: (n: Notification) => Promise<void>;\n}\n\ninterface ProgressSessionOptions {\n label: string;\n /** Absent when the client sent no `progressToken`; the session then only tracks state. */\n sink?: ProgressSink;\n /** Override the tick rate-limit window. Default: 50ms. */\n rateLimitMs?: number;\n}\n\nconst DEFAULT_RATE_LIMIT_MS = 50;\n\n/**\n * One tool call's progress stream: a monotonic cursor, a tick rate limit, and\n * the wire guard the spec requires (`progress` strictly increases per token).\n * Terminal frames (`complete`, `fail`) always reach the wire — they carry the\n * outcome message — so they advance past the last value instead of dropping.\n */\nexport class ProgressSession {\n readonly #label: string;\n readonly #sink: ProgressSink | undefined;\n readonly #rateLimitMs: number;\n readonly #startTime: number;\n readonly #pending = new Set<Promise<void>>();\n\n #cursor = 0;\n #lastSentMs = 0;\n #lastProgress = -1;\n #done = false;\n\n constructor(opts: ProgressSessionOptions) {\n this.#label = opts.label;\n this.#sink = opts.sink;\n this.#rateLimitMs = opts.rateLimitMs ?? DEFAULT_RATE_LIMIT_MS;\n this.#startTime = Date.now();\n this.#lastSentMs = this.#startTime - this.#rateLimitMs;\n // Synthetic start tick: the wire sees 0 at session creation.\n this.#emit('tick', 0, undefined, this.#label);\n }\n\n set(input: { current: number; total?: number; message?: string }): void {\n if (this.#done) return;\n // A repeated or backward tick would put a duplicate value on the wire — drop it.\n if (input.current <= this.#cursor) return;\n this.#cursor = input.current;\n this.#emit('tick', this.#cursor, input.total, input.message ?? this.#label);\n }\n\n complete(message: string): void {\n if (this.#done) return;\n this.#done = true;\n this.#emit('complete', this.#cursor, undefined, message);\n }\n\n fail(message?: string): void {\n if (this.#done) return;\n this.#done = true;\n this.#emit('fail', this.#cursor, undefined, message ?? this.#label);\n }\n\n /** Await every notification in flight; a failed send is logged, never thrown. */\n async flush(): Promise<void> {\n if (this.#pending.size > 0) await Promise.allSettled([...this.#pending]);\n }\n\n #emit(\n kind: 'tick' | 'complete' | 'fail',\n current: number,\n total: number | undefined,\n message: string,\n ): void {\n const now = Date.now();\n if (kind === 'tick') {\n // Widen the window after 5s of execution; a rateLimitMs of 0 (tests)\n // stays 0 only inside the first 5s, which every test run fits in.\n const window =\n now - this.#startTime > 5000 ? Math.max(this.#rateLimitMs, 250) : this.#rateLimitMs;\n if (now - this.#lastSentMs < window) return;\n }\n this.#lastSentMs = now;\n if (!this.#sink) return;\n\n if (kind === 'complete') {\n current = total ?? current;\n total = current;\n }\n if (kind !== 'tick') {\n current = Math.max(current, this.#lastProgress + 1);\n if (total !== undefined && total < current) total = current;\n }\n // Spec: progress must increase on every notification for the same token.\n if (current <= this.#lastProgress) return;\n this.#lastProgress = current;\n\n const { toolName, token, notify } = this.#sink;\n const params: ProgressNotificationParams = {\n progressToken: token,\n progress: current,\n ...(total !== undefined ? { total } : {}),\n message,\n };\n const promise = notify({ method: 'notifications/progress', params })\n .catch((error: unknown) => {\n // A sink failure must never fail the tool call it is reporting on.\n Logger.warn(\n `${toolName}: progressNotification failed: ${formatUnknownErrorMessage(error)}`,\n );\n })\n .finally(() => {\n this.#pending.delete(promise);\n });\n this.#pending.add(promise);\n }\n}\n"]}
|
package/dist/tools/read.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const READ: import("./define.
|
|
1
|
+
export declare const READ: import("./define.ts").DefinedTool;
|
|
2
2
|
//# sourceMappingURL=read.d.ts.map
|
package/dist/tools/read.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAkYA,eAAO,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAkYA,eAAO,MAAM,IAAI,mCAmKf,CAAC"}
|