Files
cokaremote/test/all-tools.integration.test.ts
T

902 lines
28 KiB
TypeScript

import { createHash, randomUUID } from "node:crypto";
import { mkdtemp, rm } from "node:fs/promises";
import type { AddressInfo } from "node:net";
import os from "node:os";
import path from "node:path";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { loadConfig } from "../src/config.js";
import { startHttpServer, type RunningHttpServer } from "../src/http-server.js";
import { createServices } from "../src/mcp-server.js";
const ALL_TOOLS = [
"apply_patch",
"cancel_managed_task",
"chmod_path",
"copy_path",
"download_file",
"exec_command",
"hash_file",
"list_directory",
"list_managed_tasks",
"list_processes",
"make_directory",
"move_path",
"read_file",
"read_managed_task",
"read_process",
"remove_path",
"replace_in_file",
"run_script",
"start_managed_task",
"stat_path",
"terminate_process",
"upload_file",
"write_file",
"write_stdin",
] as const;
type ToolName = (typeof ALL_TOOLS)[number];
type ToolResult = Awaited<ReturnType<Client["callTool"]>>;
const EXPECTED_ANNOTATIONS = {
apply_patch: [false, true, false, false],
cancel_managed_task: [false, true, false, false],
chmod_path: [false, true, true, false],
copy_path: [false, true, true, false],
download_file: [true, false, true, false],
exec_command: [false, true, false, true],
hash_file: [true, false, true, false],
list_directory: [true, false, true, false],
list_managed_tasks: [true, false, true, false],
list_processes: [true, false, true, false],
make_directory: [false, false, true, false],
move_path: [false, true, true, false],
read_file: [true, false, true, false],
read_managed_task: [true, false, true, false],
read_process: [true, false, true, false],
remove_path: [false, true, true, false],
replace_in_file: [false, true, false, false],
run_script: [false, true, false, true],
start_managed_task: [false, true, false, true],
stat_path: [true, false, true, false],
terminate_process: [false, true, false, false],
upload_file: [false, true, true, false],
write_file: [false, true, false, false],
write_stdin: [false, true, false, true],
} as const satisfies Record<ToolName, readonly [boolean, boolean, boolean, boolean]>;
function structured(result: ToolResult): Record<string, unknown> {
return (result.structuredContent ?? {}) as Record<string, unknown>;
}
function errorText(result: ToolResult): string {
return String(structured(result).error ?? "");
}
function externalRoot(): string | undefined {
const value = process.env.MCP_E2E_ROOT?.trim();
if (!value) {
return undefined;
}
if (!/^\/tmp\/cokacremote-tools-e2e-[A-Za-z0-9._-]+$/.test(value)) {
throw new Error(
"MCP_E2E_ROOT must be an isolated /tmp/cokacremote-tools-e2e-* path",
);
}
return value;
}
describe.sequential("all registered MCP tools", () => {
let localDirectory: string | undefined;
let testRoot: string;
let running: RunningHttpServer | undefined;
let client: Client;
let transport: StreamableHTTPClientTransport;
const exercised = new Set<ToolName>();
const call = async (
name: ToolName,
arguments_: Record<string, unknown> = {},
): Promise<ToolResult> => {
exercised.add(name);
return client.callTool({ name, arguments: arguments_ });
};
const callOk = async (
name: ToolName,
arguments_: Record<string, unknown> = {},
): Promise<Record<string, unknown>> => {
const result = await call(name, arguments_);
expect(result.isError, `${name} unexpectedly returned an error: ${errorText(result)}`).not.toBe(
true,
);
return structured(result);
};
const callError = async (
name: ToolName,
arguments_: Record<string, unknown> = {},
): Promise<string> => {
const result = await call(name, arguments_);
expect(result.isError, `${name} unexpectedly succeeded`).toBe(true);
expect(errorText(result)).not.toBe("");
return errorText(result);
};
beforeAll(async () => {
const externalUrl = process.env.MCP_E2E_URL?.trim();
let endpoint: URL;
let authToken: string;
if (externalUrl) {
authToken = process.env.MCP_E2E_TOKEN?.trim() ?? "";
if (!authToken) {
throw new Error("MCP_E2E_TOKEN is required with MCP_E2E_URL");
}
testRoot = externalRoot() ?? `/tmp/cokacremote-tools-e2e-${randomUUID()}`;
endpoint = new URL(externalUrl);
} else {
localDirectory = await mkdtemp(path.join(os.tmpdir(), "cokacremote-all-tools-"));
testRoot = path.join(localDirectory, "tool-root");
authToken = "all-tools-test-secret";
const config = loadConfig(
{
MCP_AUTH_TOKEN: authToken,
MCP_HOST: "127.0.0.1",
MCP_DEFAULT_CWD: localDirectory,
MCP_MAX_FILE_CHUNK_BYTES: "65536",
},
localDirectory,
);
config.port = 0;
running = await startHttpServer(config, createServices(config));
const address = running.httpServer.address() as AddressInfo;
endpoint = new URL(`http://127.0.0.1:${address.port}${config.endpoint}`);
}
client = new Client({ name: "all-tools-e2e", version: "1.0.0" });
transport = new StreamableHTTPClientTransport(endpoint, {
requestInit: { headers: { authorization: `Bearer ${authToken}` } },
});
await client.connect(transport);
await callOk("make_directory", {
path: testRoot,
recursive: true,
mode: "0700",
});
}, 20_000);
afterAll(async () => {
if (client && testRoot) {
await client
.callTool({
name: "remove_path",
arguments: { path: testRoot, recursive: true, force: true },
})
.catch(() => undefined);
await client.close().catch(() => undefined);
}
await running?.close();
if (localDirectory) {
await rm(localDirectory, { recursive: true, force: true });
}
});
it("publishes the exact tool inventory and annotations", async () => {
const listed = await client.listTools();
expect(listed.tools.map((tool) => tool.name).sort()).toEqual([...ALL_TOOLS]);
for (const tool of listed.tools) {
expect(tool.inputSchema.type).toBe("object");
const [readOnlyHint, destructiveHint, idempotentHint, openWorldHint] =
EXPECTED_ANNOTATIONS[tool.name as ToolName];
expect(tool.annotations, `${tool.name} annotations`).toEqual({
readOnlyHint,
destructiveHint,
idempotentHint,
openWorldHint,
});
}
});
it("executes, polls, writes to, times out, lists, and terminates processes", async () => {
const completed = await callOk("exec_command", {
cmd: "printf '%s\\n' \"$E2E_VALUE\"; pwd; printf 'stderr-ok' >&2; exit 7",
workdir: testRoot,
env: { E2E_VALUE: "env-ok" },
yieldTimeMs: 3000,
});
expect(completed).toMatchObject({ completed: true, exitCode: 7, stderr: "stderr-ok" });
expect(String(completed.stdout)).toContain("env-ok");
expect(String(completed.stdout)).toContain(testRoot);
expect(await callOk("exec_command", {
cmd: "printf shell-ok",
workdir: testRoot,
shell: "/bin/sh",
login: false,
yieldTimeMs: 3000,
})).toMatchObject({ completed: true, exitCode: 0, stdout: "shell-ok" });
const bounded = await callOk("exec_command", {
cmd: "node -e \"process.stdout.write('x'.repeat(20000))\"",
workdir: testRoot,
yieldTimeMs: 3000,
maxOutputBytes: 16384,
});
expect(bounded).toMatchObject({ completed: true, exitCode: 0, hasMore: true });
let boundedOutput = String(bounded.stdout);
let boundedCursor = Number(bounded.nextSeq);
for (let page = 0; page < 5 && bounded.hasMore === true; page += 1) {
const next = await callOk("read_process", {
sessionId: bounded.sessionId,
afterSeq: boundedCursor,
maxOutputBytes: 16384,
});
boundedOutput += String(next.stdout);
boundedCursor = Number(next.nextSeq);
bounded.hasMore = next.hasMore;
}
expect(boundedOutput).toBe("x".repeat(20000));
const timedOut = await callOk("exec_command", {
cmd: "sleep 10",
workdir: testRoot,
timeoutMs: 100,
yieldTimeMs: 3000,
});
expect(timedOut).toMatchObject({ completed: true, timedOut: true });
expect(String(timedOut.error)).toContain("timeout");
const interactive = await callOk("exec_command", {
cmd: "node -e \"process.stdin.once('data', d => { process.stdout.write(d); process.exit(0); })\"",
workdir: testRoot,
yieldTimeMs: 0,
});
expect(interactive).toMatchObject({ running: true, completed: false });
const interactiveSession = String(interactive.sessionId);
const written = await callOk("write_stdin", {
sessionId: interactiveSession,
chars: "interactive-ok\\n",
closeStdin: true,
yieldTimeMs: 3000,
});
expect(String(written.stdout)).toContain("interactive-ok");
const read = await callOk("read_process", {
sessionId: interactiveSession,
afterSeq: written.nextSeq,
waitMs: 1000,
});
expect(read).toMatchObject({ running: false, completed: true, exitCode: 0 });
const script = await callOk("run_script", {
runtime: "node",
script:
"process.stdin.once('data', d => { console.log(JSON.stringify({ arg: process.argv[2], env: process.env.E2E_SCRIPT, stdin: d.toString() })); process.exit(0); });",
workdir: testRoot,
args: ["argument-ok"],
env: { E2E_SCRIPT: "script-env-ok" },
stdin: "script-stdin-ok",
yieldTimeMs: 3000,
keepScript: true,
});
expect(script).toMatchObject({ completed: true, exitCode: 0 });
expect(JSON.parse(String(script.stdout).trim())).toEqual({
arg: "argument-ok",
env: "script-env-ok",
stdin: "script-stdin-ok",
});
expect(String(script.scriptPath)).toMatch(/^\/tmp\/remote-dev-mcp-script-/);
const keptScript = await callOk("stat_path", { path: script.scriptPath });
expect(keptScript).toMatchObject({ type: "file", mode: "0700" });
await callOk("remove_path", {
path: path.dirname(String(script.scriptPath)),
recursive: true,
force: true,
});
for (const request of [
{ script: "printf default-bash-ok", expected: "default-bash-ok" },
{ runtime: "bash", script: "printf bash-ok", expected: "bash-ok" },
{ runtime: "sh", script: "printf sh-ok", expected: "sh-ok" },
{ runtime: "python", script: "print('python-ok')", expected: "python-ok\n" },
{
runtime: "custom",
interpreter: "/bin/sh",
script: "printf custom-ok",
expected: "custom-ok",
},
]) {
const runtimeResult = await callOk("run_script", {
...request,
workdir: testRoot,
yieldTimeMs: 3000,
});
expect(runtimeResult).toMatchObject({
completed: true,
exitCode: 0,
stdout: request.expected,
});
}
expect(await callError("run_script", { runtime: "custom", script: "exit 0" })).toContain(
"interpreter is required",
);
const longRunning = await callOk("exec_command", {
cmd: "node -e \"setInterval(() => {}, 1000)\"",
workdir: testRoot,
yieldTimeMs: 0,
});
const longSession = String(longRunning.sessionId);
const processes = await callOk("list_processes");
expect(processes.processes).toEqual(
expect.arrayContaining([
expect.objectContaining({ sessionId: longSession, running: true }),
]),
);
await callOk("terminate_process", {
sessionId: longSession,
signal: "SIGTERM",
graceMs: 1000,
});
const terminated = await callOk("read_process", {
sessionId: longSession,
waitMs: 2000,
});
expect(terminated).toMatchObject({ running: false, completed: true, signal: "SIGTERM" });
const managed = await callOk("start_managed_task", {
taskKey: "e2e-managed-task",
cmd: "printf 'managed-start\n'; sleep 0.1; printf 'BUILD SUCCESSFUL\n'",
workdir: testRoot,
initialWaitMs: 0,
});
expect(managed).toMatchObject({
taskKey: "e2e-managed-task",
running: true,
reused: false,
});
const reusedManaged = await callOk("start_managed_task", {
taskKey: "e2e-managed-task",
cmd: "printf 'managed-start\n'; sleep 0.1; printf 'BUILD SUCCESSFUL\n'",
workdir: testRoot,
initialWaitMs: 0,
});
expect(reusedManaged).toMatchObject({
sessionId: managed.sessionId,
reused: true,
});
const listedManaged = await callOk("list_managed_tasks");
expect(listedManaged.tasks).toEqual(
expect.arrayContaining([
expect.objectContaining({ taskKey: "e2e-managed-task" }),
]),
);
const finishedManaged = await callOk("read_managed_task", {
taskKey: "e2e-managed-task",
waitMs: 3000,
});
expect(finishedManaged).toMatchObject({
completed: true,
status: "succeeded",
exitCode: 0,
});
expect(String(finishedManaged.tail)).toContain("BUILD SUCCESSFUL");
await callOk("start_managed_task", {
taskKey: "e2e-managed-cancel",
cmd: "sleep 10",
workdir: testRoot,
initialWaitMs: 0,
});
await callOk("cancel_managed_task", {
taskKey: "e2e-managed-cancel",
signal: "SIGTERM",
graceMs: 1000,
});
}, 30_000);
it("handles text, metadata, listings, permissions, and unified patches", async () => {
await callOk("make_directory", {
path: "text/nested",
cwd: testRoot,
recursive: true,
mode: "0750",
});
expect(await callError("make_directory", {
path: "missing-parent/child",
cwd: testRoot,
recursive: false,
})).toMatch(/ENOENT|no such file/i);
const unicodeText = "A😀한글B";
await callOk("write_file", {
path: "text/nested/unicode.txt",
cwd: testRoot,
content: unicodeText,
fileMode: "0640",
});
expect(await callOk("stat_path", {
path: "text/nested/unicode.txt",
cwd: testRoot,
})).toMatchObject({ type: "file", mode: "0640" });
await callOk("chmod_path", {
path: "text/nested/unicode.txt",
cwd: testRoot,
mode: "0600",
});
await callOk("write_file", {
path: "text/nested/unicode.txt",
cwd: testRoot,
content: unicodeText,
mode: "overwrite",
fileMode: "0644",
});
expect(await callOk("stat_path", {
path: "text/nested/unicode.txt",
cwd: testRoot,
})).toMatchObject({ mode: "0644" });
let offset = 0;
let reconstructed = "";
for (let part = 0; part < 20; part += 1) {
const chunk = await callOk("read_file", {
path: "text/nested/unicode.txt",
cwd: testRoot,
offset,
maxBytes: 3,
encoding: "utf8",
});
reconstructed += String(chunk.content);
const nextOffset = Number(chunk.nextOffset);
expect(nextOffset).toBeGreaterThan(offset);
offset = nextOffset;
if (chunk.eof === true) {
break;
}
}
expect(reconstructed).toBe(unicodeText);
const encoded = await callOk("read_file", {
path: "text/nested/unicode.txt",
cwd: testRoot,
maxBytes: 65536,
encoding: "base64",
});
expect(Buffer.from(String(encoded.content), "base64").toString("utf8")).toBe(unicodeText);
expect(await callError("read_file", {
path: "text",
cwd: testRoot,
})).toContain("not a regular file");
expect(await callError("write_file", {
path: "text/invalid-base64.bin",
cwd: testRoot,
content: "%%%not-base64%%%",
encoding: "base64",
})).toMatch(/base64/i);
expect(await callError("write_file", {
path: "text/no-parent/value.txt",
cwd: testRoot,
content: "must fail",
createParents: false,
})).toMatch(/ENOENT|no such file/i);
await callOk("write_file", {
path: "text/append.txt",
cwd: testRoot,
content: "first",
});
await callOk("write_file", {
path: "text/append.txt",
cwd: testRoot,
content: "-second",
mode: "append",
});
expect(await callOk("read_file", {
path: "text/append.txt",
cwd: testRoot,
})).toMatchObject({ content: "first-second", eof: true });
await callOk("write_file", {
path: "text/empty.txt",
cwd: testRoot,
content: "",
});
expect(await callOk("read_file", {
path: "text/empty.txt",
cwd: testRoot,
maxBytes: 1,
})).toMatchObject({ content: "", bytesRead: 0, eof: true });
await callOk("write_file", {
path: "text/replace.txt",
cwd: testRoot,
content: "one two one\n",
});
expect(await callError("replace_in_file", {
path: "text/replace.txt",
cwd: testRoot,
oldText: "one",
newText: "ONE",
})).toContain("found 2");
await callOk("replace_in_file", {
path: "text/replace.txt",
cwd: testRoot,
oldText: "one",
newText: "ONE",
replaceAll: true,
expectedOccurrences: 2,
});
await callOk("replace_in_file", {
path: "text/replace.txt",
cwd: testRoot,
oldText: "two",
newText: "three",
expectedOccurrences: 1,
});
expect(await callOk("read_file", {
path: "text/replace.txt",
cwd: testRoot,
})).toMatchObject({ content: "ONE three ONE\n", eof: true });
await callOk("write_file", {
path: "text/.hidden",
cwd: testRoot,
content: "hidden",
});
const visible = await callOk("list_directory", {
path: "text",
cwd: testRoot,
recursive: true,
maxDepth: 8,
includeHidden: false,
includeMetadata: true,
});
expect(visible.entries).toEqual(
expect.arrayContaining([
expect.objectContaining({ relativePath: "nested/unicode.txt", type: "file" }),
]),
);
expect(visible.entries).not.toEqual(
expect.arrayContaining([expect.objectContaining({ name: ".hidden" })]),
);
expect(await callOk("list_directory", {
path: "text",
cwd: testRoot,
recursive: true,
maxEntries: 2,
})).toMatchObject({ count: 2, truncated: true });
await callOk("exec_command", {
cmd: "ln -s nested/unicode.txt text/unicode-link",
workdir: testRoot,
yieldTimeMs: 3000,
});
expect(await callOk("stat_path", {
path: "text/unicode-link",
cwd: testRoot,
})).toMatchObject({ type: "symlink", symlinkTarget: "nested/unicode.txt" });
await callOk("write_file", {
path: "patch-target.txt",
cwd: testRoot,
content: "old\n",
});
const patch = [
"diff --git a/patch-target.txt b/patch-target.txt",
"--- a/patch-target.txt",
"+++ b/patch-target.txt",
"@@ -1 +1 @@",
"-old",
"+new",
"",
].join("\n");
expect(await callOk("apply_patch", {
patch,
cwd: testRoot,
checkOnly: true,
})).toMatchObject({ applied: false, checkOnly: true });
expect(await callOk("read_file", {
path: "patch-target.txt",
cwd: testRoot,
})).toMatchObject({ content: "old\n" });
expect(await callOk("apply_patch", { patch, cwd: testRoot })).toMatchObject({
applied: true,
checkOnly: false,
});
expect(await callOk("apply_patch", {
patch,
cwd: testRoot,
reverse: true,
})).toMatchObject({ applied: true });
expect(await callOk("read_file", {
path: "patch-target.txt",
cwd: testRoot,
})).toMatchObject({ content: "old\n" });
expect(await callError("apply_patch", {
patch: "this is not a unified patch",
cwd: testRoot,
})).not.toBe("");
await callOk("make_directory", {
path: "three-way",
cwd: testRoot,
recursive: true,
});
await callOk("write_file", {
path: "three-way/value.txt",
cwd: testRoot,
content: "base\n",
});
await callOk("exec_command", {
cmd: "git init -q && git config user.email e2e@example.invalid && git config user.name E2E && git add value.txt && git commit -qm base",
workdir: path.join(testRoot, "three-way"),
yieldTimeMs: 3000,
});
await callOk("write_file", {
path: "value.txt",
cwd: path.join(testRoot, "three-way"),
content: "three-way-result\n",
});
const generatedPatch = await callOk("exec_command", {
cmd: "git diff --binary -- value.txt",
workdir: path.join(testRoot, "three-way"),
yieldTimeMs: 3000,
});
await callOk("exec_command", {
cmd: "git checkout -- value.txt",
workdir: path.join(testRoot, "three-way"),
yieldTimeMs: 3000,
});
expect(await callOk("apply_patch", {
patch: generatedPatch.stdout,
cwd: path.join(testRoot, "three-way"),
threeWay: true,
})).toMatchObject({ applied: true });
expect(await callOk("read_file", {
path: "value.txt",
cwd: path.join(testRoot, "three-way"),
})).toMatchObject({ content: "three-way-result\n" });
}, 30_000);
it("transfers, hashes, copies, moves, and removes isolated paths", async () => {
await callOk("make_directory", {
path: "transfer",
cwd: testRoot,
recursive: true,
});
expect(await callError("upload_file", {
path: "transfer/invalid.bin",
cwd: testRoot,
dataBase64: "not@base64",
truncate: true,
})).toMatch(/base64/i);
const binary = Buffer.from(Array.from({ length: 1024 }, (_, index) => index % 256));
const first = binary.subarray(0, 333);
const second = binary.subarray(333);
await callOk("write_file", {
path: "transfer/artifact.bin",
cwd: testRoot,
content: "stale content that must be truncated",
});
const firstUpload = await callOk("upload_file", {
path: "transfer/artifact.bin",
cwd: testRoot,
dataBase64: first.toString("base64"),
offset: 0,
truncate: true,
});
expect(firstUpload).toMatchObject({
bytesWritten: first.length,
nextOffset: first.length,
chunkSha256: createHash("sha256").update(first).digest("hex"),
});
await callOk("upload_file", {
path: "transfer/artifact.bin",
cwd: testRoot,
dataBase64: second.toString("base64"),
offset: first.length,
truncate: false,
});
let downloaded = Buffer.alloc(0);
let offset = 0;
for (let part = 0; part < 20; part += 1) {
const chunk = await callOk("download_file", {
path: "transfer/artifact.bin",
cwd: testRoot,
offset,
maxBytes: 113,
});
downloaded = Buffer.concat([
downloaded,
Buffer.from(String(chunk.dataBase64), "base64"),
]);
offset = Number(chunk.nextOffset);
if (chunk.eof === true) {
break;
}
}
expect(downloaded).toEqual(binary);
expect(await callOk("download_file", {
path: "transfer/artifact.bin",
cwd: testRoot,
offset: binary.length,
maxBytes: 1,
})).toMatchObject({ bytesRead: 0, eof: true, nextOffset: binary.length });
for (const algorithm of ["sha256", "sha512", "md5"] as const) {
expect(await callOk("hash_file", {
path: "transfer/artifact.bin",
cwd: testRoot,
algorithm,
})).toMatchObject({
algorithm,
digest: createHash(algorithm).update(binary).digest("hex"),
});
}
await callOk("write_file", {
path: "transfer/base64-copy.bin",
cwd: testRoot,
content: binary.toString("base64"),
encoding: "base64",
});
expect(await callOk("hash_file", {
path: "transfer/base64-copy.bin",
cwd: testRoot,
})).toMatchObject({ digest: createHash("sha256").update(binary).digest("hex") });
await callOk("write_file", {
path: "transfer/existing.bin",
cwd: testRoot,
content: "existing",
});
expect(await callError("copy_path", {
sourcePath: "transfer/artifact.bin",
destinationPath: "transfer/existing.bin",
cwd: testRoot,
force: false,
})).toContain("already exists");
expect(await callError("copy_path", {
sourcePath: "transfer/artifact.bin",
destinationPath: "transfer/artifact.bin",
cwd: testRoot,
})).toContain("must be different");
await callOk("copy_path", {
sourcePath: "transfer/artifact.bin",
destinationPath: "transfer/copied.bin",
cwd: testRoot,
force: true,
});
expect(await callOk("hash_file", {
path: "transfer/copied.bin",
cwd: testRoot,
})).toMatchObject({ digest: createHash("sha256").update(binary).digest("hex") });
await callOk("make_directory", {
path: "transfer/source-dir",
cwd: testRoot,
recursive: true,
});
await callOk("write_file", {
path: "transfer/source-dir/value.txt",
cwd: testRoot,
content: "source",
});
await callOk("make_directory", {
path: "transfer/destination-dir",
cwd: testRoot,
recursive: true,
});
await callOk("write_file", {
path: "transfer/destination-dir/value.txt",
cwd: testRoot,
content: "destination",
});
expect(await callError("copy_path", {
sourcePath: "transfer/source-dir",
destinationPath: "transfer/destination-dir",
cwd: testRoot,
recursive: false,
})).toContain("recursive=true");
expect(await callError("copy_path", {
sourcePath: "transfer/source-dir",
destinationPath: "transfer/destination-dir",
cwd: testRoot,
recursive: true,
force: false,
})).toContain("already exists");
await callOk("copy_path", {
sourcePath: "transfer/source-dir",
destinationPath: "transfer/destination-dir",
cwd: testRoot,
recursive: true,
force: true,
});
expect(await callOk("read_file", {
path: "transfer/destination-dir/value.txt",
cwd: testRoot,
})).toMatchObject({ content: "source" });
await callOk("write_file", {
path: "transfer/move-source.txt",
cwd: testRoot,
content: "move-source",
});
await callOk("write_file", {
path: "transfer/move-destination.txt",
cwd: testRoot,
content: "move-destination",
});
expect(await callError("move_path", {
sourcePath: "transfer/move-source.txt",
destinationPath: "transfer/move-destination.txt",
cwd: testRoot,
overwrite: false,
})).toContain("already exists");
expect(await callOk("move_path", {
sourcePath: "transfer/move-source.txt",
destinationPath: "transfer/move-destination.txt",
cwd: testRoot,
overwrite: true,
})).toMatchObject({ moved: true });
expect(await callOk("read_file", {
path: "transfer/move-destination.txt",
cwd: testRoot,
})).toMatchObject({ content: "move-source" });
expect(await callError("move_path", {
sourcePath: "transfer/move-source.txt",
destinationPath: "transfer/move-destination.txt",
cwd: testRoot,
overwrite: true,
})).toMatch(/ENOENT|no such file/i);
expect(await callOk("read_file", {
path: "transfer/move-destination.txt",
cwd: testRoot,
})).toMatchObject({ content: "move-source" });
expect(await callOk("move_path", {
sourcePath: "transfer/move-destination.txt",
destinationPath: "transfer/move-destination.txt",
cwd: testRoot,
})).toMatchObject({ moved: false, samePath: true });
await callOk("make_directory", {
path: "transfer/remove-dir/nested",
cwd: testRoot,
recursive: true,
});
await callOk("write_file", {
path: "transfer/remove-dir/nested/value.txt",
cwd: testRoot,
content: "remove",
});
expect(await callError("remove_path", {
path: "transfer/remove-dir",
cwd: testRoot,
recursive: false,
force: false,
})).toMatch(/directory|EISDIR|recursive/i);
await callOk("remove_path", {
path: "transfer/remove-dir",
cwd: testRoot,
recursive: true,
force: false,
});
expect(await callError("stat_path", {
path: "transfer/remove-dir",
cwd: testRoot,
})).toMatch(/ENOENT|no such file/i);
expect(await callOk("remove_path", {
path: "transfer/does-not-exist",
cwd: testRoot,
force: true,
})).toMatchObject({ removed: true });
}, 30_000);
it("exercises every published tool through MCP", () => {
expect([...exercised].sort()).toEqual([...ALL_TOOLS]);
});
});