argsbarg 5.0.1 → 5.0.2
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/CHANGELOG.md +8 -1
- package/docs/distribution-homebrew.md +2 -0
- package/examples/full-example/scripts/formula-shared.ts +1 -0
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +23 -0
- package/src/builtins/config.test.ts +8 -0
- package/src/cli-tool/cli-smoke.test.ts +8 -0
- package/src/cli-tool/create.test.ts +13 -0
- package/src/cli-tool/full-example-capabilities.test.ts +8 -0
- package/src/cli-tool/main.ts +0 -0
- package/src/config/context.test.ts +8 -0
- package/src/config/file.test.ts +12 -0
- package/src/config/resolve.test.ts +18 -0
- package/src/config/validate.test.ts +11 -0
- package/src/config.integration.test.ts +5 -0
- package/src/configure/configure.test.ts +22 -0
- package/src/configure/index.ts +14 -0
- package/src/configure/prompt.ts +7 -0
- package/src/docs/api-guide.test.ts +10 -0
- package/src/docs/docs.test.ts +27 -0
- package/src/docs/mcp-resources.test.ts +10 -0
- package/src/formats.test.ts +9 -0
- package/src/headless.test.ts +10 -0
- package/src/hidden-mcpb.test.ts +22 -0
- package/src/install/binary-placement.test.ts +14 -0
- package/src/install/gh-release-update.test.ts +9 -0
- package/src/install/install-validate.test.ts +10 -0
- package/src/install/mcp-codex.test.ts +7 -0
- package/src/install/mcp-openclaw.test.ts +6 -0
- package/src/install/mcp-opencode.test.ts +10 -0
- package/src/install/status.test.ts +9 -0
- package/src/install/targets.test.ts +19 -0
- package/src/invoke.test.ts +14 -0
- package/src/mcp/claude.test.ts +10 -0
- package/src/mcp/env.test.ts +12 -0
- package/src/mcp/zip.test.ts +5 -0
- package/src/mcp.integration.test.ts +39 -0
- package/src/parse.test.ts +71 -0
package/src/mcp/env.test.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for mcp/env module behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
6
|
import { chmodSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
3
7
|
import { tmpdir } from "node:os";
|
|
@@ -6,7 +10,9 @@ import { applyShellEnv, bootstrapMcpEnv } from "./env.ts";
|
|
|
6
10
|
|
|
7
11
|
const TEST_VAR = "ARGS_BARG_SHELL_ENV_TEST";
|
|
8
12
|
|
|
13
|
+
/** Tests for mcp/env. */
|
|
9
14
|
describe("mcp/env", () => {
|
|
15
|
+
/** ApplyShellEnv merges PATH and fills missing vars. */
|
|
10
16
|
test("applyShellEnv merges PATH and fills missing vars", () => {
|
|
11
17
|
const prevPath = process.env.PATH;
|
|
12
18
|
const prevTest = process.env[TEST_VAR];
|
|
@@ -27,6 +33,7 @@ describe("mcp/env", () => {
|
|
|
27
33
|
}
|
|
28
34
|
});
|
|
29
35
|
|
|
36
|
+
/** ApplyShellEnv does not overwrite host vars except PATH merge. */
|
|
30
37
|
test("applyShellEnv does not overwrite host vars except PATH merge", () => {
|
|
31
38
|
const prev = process.env.HOME;
|
|
32
39
|
process.env.HOME = "/host/home";
|
|
@@ -40,6 +47,7 @@ describe("mcp/env", () => {
|
|
|
40
47
|
});
|
|
41
48
|
});
|
|
42
49
|
|
|
50
|
+
/** Tests for bootstrapMcpEnv. */
|
|
43
51
|
describe("bootstrapMcpEnv", () => {
|
|
44
52
|
let fakeShell: string;
|
|
45
53
|
let prevShell: string | undefined;
|
|
@@ -70,21 +78,25 @@ fi
|
|
|
70
78
|
else process.env[TEST_VAR] = prevTest;
|
|
71
79
|
});
|
|
72
80
|
|
|
81
|
+
/** Defaults on when shellEnv is undefined. */
|
|
73
82
|
test("defaults on when shellEnv is undefined", () => {
|
|
74
83
|
bootstrapMcpEnv({});
|
|
75
84
|
expect(process.env[TEST_VAR]).toBe("from_fake_shell");
|
|
76
85
|
});
|
|
77
86
|
|
|
87
|
+
/** Runs when shellEnv is true. */
|
|
78
88
|
test("runs when shellEnv is true", () => {
|
|
79
89
|
bootstrapMcpEnv({ shellEnv: true });
|
|
80
90
|
expect(process.env[TEST_VAR]).toBe("from_fake_shell");
|
|
81
91
|
});
|
|
82
92
|
|
|
93
|
+
/** Tests that uses explicit shell path when shellEnv is a string. */
|
|
83
94
|
test("uses explicit shell path when shellEnv is a string", () => {
|
|
84
95
|
bootstrapMcpEnv({ shellEnv: fakeShell });
|
|
85
96
|
expect(process.env[TEST_VAR]).toBe("from_fake_shell");
|
|
86
97
|
});
|
|
87
98
|
|
|
99
|
+
/** Tests that skips capture when shellEnv is false. */
|
|
88
100
|
test("skips capture when shellEnv is false", () => {
|
|
89
101
|
bootstrapMcpEnv({ shellEnv: false });
|
|
90
102
|
expect(process.env[TEST_VAR]).toBeUndefined();
|
package/src/mcp/zip.test.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for mcp/zip module behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import { expect, test } from "bun:test";
|
|
2
6
|
import { execSync } from "node:child_process";
|
|
3
7
|
import { mkdtempSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -5,6 +9,7 @@ import { tmpdir } from "node:os";
|
|
|
5
9
|
import { join } from "node:path";
|
|
6
10
|
import { zipStore } from "./zip.ts";
|
|
7
11
|
|
|
12
|
+
/** ZipStore preserves unix executable mode on extract. */
|
|
8
13
|
test("zipStore preserves unix executable mode on extract", () => {
|
|
9
14
|
const work = mkdtempSync(join(tmpdir(), "zip-exec-"));
|
|
10
15
|
const data = Buffer.from("#!/bin/sh\necho hi\n");
|
|
@@ -17,10 +17,12 @@ import { cliSchemaExport } from "./schema.ts";
|
|
|
17
17
|
import { mcpRequest, nestedMcpFixture, testProgram } from "./test-fixtures.ts";
|
|
18
18
|
import { cliValidateProgram } from "./validate.ts";
|
|
19
19
|
|
|
20
|
+
/** Tests that sanitizeToolSegment normalizes dotted app keys. */
|
|
20
21
|
test("sanitizeToolSegment normalizes dotted app keys", () => {
|
|
21
22
|
expect(sanitizeToolSegment("minimal.ts")).toBe("minimal_ts");
|
|
22
23
|
});
|
|
23
24
|
|
|
25
|
+
/** McpToolDescription formats CLI path and root-leaf prefix. */
|
|
24
26
|
test("mcpToolDescription formats CLI path and root-leaf prefix", () => {
|
|
25
27
|
expect(mcpToolDescription(["stat", "owner", "lookup"], "nested.ts", "Resolve owner info.")).toBe(
|
|
26
28
|
"stat owner lookup — Resolve owner info.",
|
|
@@ -29,6 +31,7 @@ test("mcpToolDescription formats CLI path and root-leaf prefix", () => {
|
|
|
29
31
|
expect(mcpToolDescription([], "helloapp", "Tiny demo.")).toBe("helloapp — Tiny demo.");
|
|
30
32
|
});
|
|
31
33
|
|
|
34
|
+
/** Tests that collectMcpTools lists user leaf commands only. */
|
|
32
35
|
test("collectMcpTools lists user leaf commands only", () => {
|
|
33
36
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
34
37
|
const names = tools.map((t) => t.name);
|
|
@@ -42,6 +45,7 @@ test("collectMcpTools lists user leaf commands only", () => {
|
|
|
42
45
|
expect(lookup.description).toBe("stat owner lookup — Resolve owner info.");
|
|
43
46
|
});
|
|
44
47
|
|
|
48
|
+
/** Tests that collectMcpTools appends leaf notes to MCP tool description. */
|
|
45
49
|
test("collectMcpTools appends leaf notes to MCP tool description", () => {
|
|
46
50
|
const root = testProgram({
|
|
47
51
|
key: "app",
|
|
@@ -61,6 +65,7 @@ test("collectMcpTools appends leaf notes to MCP tool description", () => {
|
|
|
61
65
|
expect(tools[0]?.description).toBe("run — Run.\n\nUse `--json` for structured output.");
|
|
62
66
|
});
|
|
63
67
|
|
|
68
|
+
/** Tests that collectMcpTools appends notes after mcpTool.description override. */
|
|
64
69
|
test("collectMcpTools appends notes after mcpTool.description override", () => {
|
|
65
70
|
const root = testProgram({
|
|
66
71
|
key: "app",
|
|
@@ -81,6 +86,7 @@ test("collectMcpTools appends notes after mcpTool.description override", () => {
|
|
|
81
86
|
expect(tools[0]?.description).toBe("Custom MCP text.\n\nOperational hint.");
|
|
82
87
|
});
|
|
83
88
|
|
|
89
|
+
/** Tests that collectMcpTools resolves {argsbarg:program} in appended notes. */
|
|
84
90
|
test("collectMcpTools resolves {argsbarg:program} in appended notes", () => {
|
|
85
91
|
const root = testProgram({
|
|
86
92
|
key: "myapp",
|
|
@@ -100,6 +106,7 @@ test("collectMcpTools resolves {argsbarg:program} in appended notes", () => {
|
|
|
100
106
|
expect(tools[0]?.description).toContain("See `myapp docs api`.");
|
|
101
107
|
});
|
|
102
108
|
|
|
109
|
+
/** CliSchemaExport includes leaf outputSchema. */
|
|
103
110
|
test("cliSchemaExport includes leaf outputSchema", () => {
|
|
104
111
|
const root = testProgram({
|
|
105
112
|
key: "app",
|
|
@@ -125,6 +132,7 @@ test("cliSchemaExport includes leaf outputSchema", () => {
|
|
|
125
132
|
});
|
|
126
133
|
});
|
|
127
134
|
|
|
135
|
+
/** CliSchemaExport accepts legacy mcpTool.outputSchema. */
|
|
128
136
|
test("cliSchemaExport accepts legacy mcpTool.outputSchema", () => {
|
|
129
137
|
const root = testProgram({
|
|
130
138
|
key: "app",
|
|
@@ -147,6 +155,7 @@ test("cliSchemaExport accepts legacy mcpTool.outputSchema", () => {
|
|
|
147
155
|
});
|
|
148
156
|
});
|
|
149
157
|
|
|
158
|
+
/** Tests that outputSchema must be a JSON Schema object. */
|
|
150
159
|
test("outputSchema must be a JSON Schema object", () => {
|
|
151
160
|
const root = testProgram({
|
|
152
161
|
key: "app",
|
|
@@ -164,6 +173,7 @@ test("outputSchema must be a JSON Schema object", () => {
|
|
|
164
173
|
expect(() => cliValidateProgram(root)).toThrow(/outputSchema must be a JSON Schema object/);
|
|
165
174
|
});
|
|
166
175
|
|
|
176
|
+
/** Tests that outputSchema cannot be set on both leaf and mcpTool. */
|
|
167
177
|
test("outputSchema cannot be set on both leaf and mcpTool", () => {
|
|
168
178
|
const root = testProgram({
|
|
169
179
|
key: "app",
|
|
@@ -182,6 +192,7 @@ test("outputSchema cannot be set on both leaf and mcpTool", () => {
|
|
|
182
192
|
expect(() => cliValidateProgram(root)).toThrow(/Set outputSchema on the leaf only/);
|
|
183
193
|
});
|
|
184
194
|
|
|
195
|
+
/** Tests that collectMcpTools merges parent options into inputSchema. */
|
|
185
196
|
test("collectMcpTools merges parent options into inputSchema", () => {
|
|
186
197
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
187
198
|
const lookup = tools.find((t) => t.name === "stat_owner_lookup")!;
|
|
@@ -190,6 +201,7 @@ test("collectMcpTools merges parent options into inputSchema", () => {
|
|
|
190
201
|
expect(schema.required).toContain("path");
|
|
191
202
|
});
|
|
192
203
|
|
|
204
|
+
/** Tests that collectMcpTools includes outputSchema when set on leaf. */
|
|
193
205
|
test("collectMcpTools includes outputSchema when set on leaf", () => {
|
|
194
206
|
const root = testProgram({
|
|
195
207
|
key: "app",
|
|
@@ -218,12 +230,14 @@ test("collectMcpTools includes outputSchema when set on leaf", () => {
|
|
|
218
230
|
});
|
|
219
231
|
});
|
|
220
232
|
|
|
233
|
+
/** Tests that collectMcpTools omits outputSchema when leaf has none. */
|
|
221
234
|
test("collectMcpTools omits outputSchema when leaf has none", () => {
|
|
222
235
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
223
236
|
const lookup = tools.find((t) => t.name === "stat_owner_lookup")!;
|
|
224
237
|
expect(lookup.outputSchema).toBeUndefined();
|
|
225
238
|
});
|
|
226
239
|
|
|
240
|
+
/** McpToolCallToArgv builds nested lookup argv. */
|
|
227
241
|
test("mcpToolCallToArgv builds nested lookup argv", () => {
|
|
228
242
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
229
243
|
const lookup = tools.find((t) => t.name === "stat_owner_lookup")!;
|
|
@@ -235,6 +249,7 @@ test("mcpToolCallToArgv builds nested lookup argv", () => {
|
|
|
235
249
|
expect(argv).toEqual(["stat", "owner", "lookup", "--json", "--user-name", "alice", "./x"]);
|
|
236
250
|
});
|
|
237
251
|
|
|
252
|
+
/** McpToolCallToArgv expands varargs positionals. */
|
|
238
253
|
test("mcpToolCallToArgv expands varargs positionals", () => {
|
|
239
254
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
240
255
|
const read = tools.find((t) => t.name === "read")!;
|
|
@@ -242,6 +257,7 @@ test("mcpToolCallToArgv expands varargs positionals", () => {
|
|
|
242
257
|
expect(argv).toEqual(["read", "a", "b"]);
|
|
243
258
|
});
|
|
244
259
|
|
|
260
|
+
/** Tests that reserved command name configure is rejected. */
|
|
245
261
|
test("reserved command name configure is rejected", () => {
|
|
246
262
|
const root = testProgram({
|
|
247
263
|
key: "app",
|
|
@@ -257,6 +273,7 @@ test("reserved command name configure is rejected", () => {
|
|
|
257
273
|
expect(() => cliValidateProgram(root)).toThrow(/Reserved command name: configure/);
|
|
258
274
|
});
|
|
259
275
|
|
|
276
|
+
/** Tests that top-level command name mcp is allowed without mcpServer. */
|
|
260
277
|
test("top-level command name mcp is allowed without mcpServer", () => {
|
|
261
278
|
const root = testProgram({
|
|
262
279
|
key: "app",
|
|
@@ -272,6 +289,7 @@ test("top-level command name mcp is allowed without mcpServer", () => {
|
|
|
272
289
|
expect(() => cliValidateProgram(root)).not.toThrow();
|
|
273
290
|
});
|
|
274
291
|
|
|
292
|
+
/** Tests that top-level command name mcp is rejected when mcpServer is enabled. */
|
|
275
293
|
test("top-level command name mcp is rejected when mcpServer is enabled", () => {
|
|
276
294
|
const root = testProgram({
|
|
277
295
|
key: "app",
|
|
@@ -288,6 +306,7 @@ test("top-level command name mcp is rejected when mcpServer is enabled", () => {
|
|
|
288
306
|
expect(() => cliValidateProgram(root)).toThrow(/Reserved command name: mcp/);
|
|
289
307
|
});
|
|
290
308
|
|
|
309
|
+
/** McpServer on non-root node is rejected. */
|
|
291
310
|
test("mcpServer on non-root node is rejected", () => {
|
|
292
311
|
const root = {
|
|
293
312
|
key: "app",
|
|
@@ -305,6 +324,7 @@ test("mcpServer on non-root node is rejected", () => {
|
|
|
305
324
|
expect(() => cliValidateProgram(root)).toThrow(/mcpServer is only supported on the program root/);
|
|
306
325
|
});
|
|
307
326
|
|
|
327
|
+
/** McpTool on root is rejected. */
|
|
308
328
|
test("mcpTool on root is rejected", () => {
|
|
309
329
|
const root = testProgram({
|
|
310
330
|
key: "app",
|
|
@@ -315,6 +335,7 @@ test("mcpTool on root is rejected", () => {
|
|
|
315
335
|
expect(() => cliValidateProgram(root)).toThrow(/mcpTool is only supported on leaf commands/);
|
|
316
336
|
});
|
|
317
337
|
|
|
338
|
+
/** McpTool on routing node is rejected. */
|
|
318
339
|
test("mcpTool on routing node is rejected", () => {
|
|
319
340
|
const root = testProgram({
|
|
320
341
|
key: "app",
|
|
@@ -337,6 +358,7 @@ test("mcpTool on routing node is rejected", () => {
|
|
|
337
358
|
expect(() => cliValidateProgram(root)).toThrow(/mcpTool is only supported on leaf commands/);
|
|
338
359
|
});
|
|
339
360
|
|
|
361
|
+
/** BuildToolCallSuccess returns stdout only. */
|
|
340
362
|
test("buildToolCallSuccess returns stdout only", () => {
|
|
341
363
|
const result = buildToolCallSuccess("hello\n", "");
|
|
342
364
|
expect(result.isError).toBe(false);
|
|
@@ -344,6 +366,7 @@ test("buildToolCallSuccess returns stdout only", () => {
|
|
|
344
366
|
expect(result.structuredContent).toBeUndefined();
|
|
345
367
|
});
|
|
346
368
|
|
|
369
|
+
/** BuildToolCallSuccess adds stderr as second content block. */
|
|
347
370
|
test("buildToolCallSuccess adds stderr as second content block", () => {
|
|
348
371
|
const result = buildToolCallSuccess("out\n", "warn\n");
|
|
349
372
|
expect(result.content).toEqual([
|
|
@@ -353,6 +376,7 @@ test("buildToolCallSuccess adds stderr as second content block", () => {
|
|
|
353
376
|
expect(result.structuredContent).toBeUndefined();
|
|
354
377
|
});
|
|
355
378
|
|
|
379
|
+
/** BuildToolCallSuccess stderr-only still includes stdout slot. */
|
|
356
380
|
test("buildToolCallSuccess stderr-only still includes stdout slot", () => {
|
|
357
381
|
const result = buildToolCallSuccess("", "warn\n");
|
|
358
382
|
expect(result.content).toEqual([
|
|
@@ -361,22 +385,26 @@ test("buildToolCallSuccess stderr-only still includes stdout slot", () => {
|
|
|
361
385
|
]);
|
|
362
386
|
});
|
|
363
387
|
|
|
388
|
+
/** BuildToolCallSuccess parses JSON structuredContent. */
|
|
364
389
|
test("buildToolCallSuccess parses JSON structuredContent", () => {
|
|
365
390
|
const result = buildToolCallSuccess('{"a":1}\n', "");
|
|
366
391
|
expect(result.structuredContent).toEqual({ a: 1 });
|
|
367
392
|
expect(result.content[0]?.text).toBe('{"a":1}\n');
|
|
368
393
|
});
|
|
369
394
|
|
|
395
|
+
/** BuildToolCallSuccess skips structuredContent for plain text. */
|
|
370
396
|
test("buildToolCallSuccess skips structuredContent for plain text", () => {
|
|
371
397
|
const result = buildToolCallSuccess("lookup user=x\n", "");
|
|
372
398
|
expect(result.structuredContent).toBeUndefined();
|
|
373
399
|
});
|
|
374
400
|
|
|
401
|
+
/** BuildToolCallSuccess parses JSON primitives. */
|
|
375
402
|
test("buildToolCallSuccess parses JSON primitives", () => {
|
|
376
403
|
const result = buildToolCallSuccess("true\n", "");
|
|
377
404
|
expect(result.structuredContent).toBe(true);
|
|
378
405
|
});
|
|
379
406
|
|
|
407
|
+
/** MCP initialize returns tools and resources capabilities. */
|
|
380
408
|
test("MCP initialize returns tools and resources capabilities", async () => {
|
|
381
409
|
const responses = await mcpRequest([{ jsonrpc: "2.0", id: 1, method: "initialize", params: {} }]);
|
|
382
410
|
const res = responses.get(1) as { result: { capabilities: Record<string, unknown> } };
|
|
@@ -384,6 +412,7 @@ test("MCP initialize returns tools and resources capabilities", async () => {
|
|
|
384
412
|
expect(res.result.capabilities.resources).toBeDefined();
|
|
385
413
|
});
|
|
386
414
|
|
|
415
|
+
/** MCP tools/list includes stat_owner_lookup. */
|
|
387
416
|
test("MCP tools/list includes stat_owner_lookup", async () => {
|
|
388
417
|
const responses = await mcpRequest([{ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }]);
|
|
389
418
|
const res = responses.get(2) as {
|
|
@@ -394,6 +423,7 @@ test("MCP tools/list includes stat_owner_lookup", async () => {
|
|
|
394
423
|
expect(lookup?.inputSchema.required).toContain("path");
|
|
395
424
|
});
|
|
396
425
|
|
|
426
|
+
/** MCP resources/read returns schema JSON. */
|
|
397
427
|
test("MCP resources/read returns schema JSON", async () => {
|
|
398
428
|
const responses = await mcpRequest([
|
|
399
429
|
{ jsonrpc: "2.0", id: 3, method: "resources/read", params: { uri: "nested_ts://schema" } },
|
|
@@ -403,6 +433,7 @@ test("MCP resources/read returns schema JSON", async () => {
|
|
|
403
433
|
expect(schema.key).toBe("nested.ts");
|
|
404
434
|
});
|
|
405
435
|
|
|
436
|
+
/** MCP tools/call runs stat_owner_lookup. */
|
|
406
437
|
test("MCP tools/call runs stat_owner_lookup", async () => {
|
|
407
438
|
const readme = join(import.meta.dir, "..", "README.md");
|
|
408
439
|
const responses = await mcpRequest([
|
|
@@ -421,6 +452,7 @@ test("MCP tools/call runs stat_owner_lookup", async () => {
|
|
|
421
452
|
expect(res.result.content[0]?.text).toContain("lookup user=test");
|
|
422
453
|
});
|
|
423
454
|
|
|
455
|
+
/** MCP tools/call returns structuredContent for JSON stdout. */
|
|
424
456
|
test("MCP tools/call returns structuredContent for JSON stdout", async () => {
|
|
425
457
|
const readme = join(import.meta.dir, "..", "README.md");
|
|
426
458
|
const responses = await mcpRequest([
|
|
@@ -446,6 +478,7 @@ test("MCP tools/call returns structuredContent for JSON stdout", async () => {
|
|
|
446
478
|
expect(JSON.parse(res.result.content[0]?.text.trim())).toEqual({ user: "test", path: readme });
|
|
447
479
|
});
|
|
448
480
|
|
|
481
|
+
/** MCP tools/call errors on missing required positional. */
|
|
449
482
|
test("MCP tools/call errors on missing required positional", async () => {
|
|
450
483
|
const responses = await mcpRequest([
|
|
451
484
|
{
|
|
@@ -460,18 +493,21 @@ test("MCP tools/call errors on missing required positional", async () => {
|
|
|
460
493
|
expect(res.result.content[0]?.text).toContain("Missing argument: path");
|
|
461
494
|
});
|
|
462
495
|
|
|
496
|
+
/** MCP ping returns empty result. */
|
|
463
497
|
test("MCP ping returns empty result", async () => {
|
|
464
498
|
const responses = await mcpRequest([{ jsonrpc: "2.0", id: 99, method: "ping", params: {} }]);
|
|
465
499
|
const res = responses.get(99) as { result: Record<string, never> };
|
|
466
500
|
expect(res.result).toEqual({});
|
|
467
501
|
});
|
|
468
502
|
|
|
503
|
+
/** Tests that minimal.ts mcp without opt-in fails. */
|
|
469
504
|
test("minimal.ts mcp without opt-in fails", async () => {
|
|
470
505
|
const { stderr, exitCode } = await $`bun run examples/minimal.ts mcp`.nothrow().quiet();
|
|
471
506
|
expect(exitCode).toBe(1);
|
|
472
507
|
expect(stderr.toString()).toContain("MCP is not available");
|
|
473
508
|
});
|
|
474
509
|
|
|
510
|
+
/** MCP resources/list includes custom resource. */
|
|
475
511
|
test("MCP resources/list includes custom resource", async () => {
|
|
476
512
|
const responses = await mcpRequest(
|
|
477
513
|
[{ jsonrpc: "2.0", id: 10, method: "resources/list", params: {} }],
|
|
@@ -484,6 +520,7 @@ test("MCP resources/list includes custom resource", async () => {
|
|
|
484
520
|
expect(uris).toContain("test://hello");
|
|
485
521
|
});
|
|
486
522
|
|
|
523
|
+
/** MCP resources/read returns docs topic resource body. */
|
|
487
524
|
test("MCP resources/read returns docs topic resource body", async () => {
|
|
488
525
|
const responses = await mcpRequest(
|
|
489
526
|
[
|
|
@@ -500,6 +537,7 @@ test("MCP resources/read returns docs topic resource body", async () => {
|
|
|
500
537
|
expect(res.result.contents[0]?.text).toBe("# MCP test readme\n");
|
|
501
538
|
});
|
|
502
539
|
|
|
540
|
+
/** MCP resources/read returns custom resource body. */
|
|
503
541
|
test("MCP resources/read returns custom resource body", async () => {
|
|
504
542
|
const responses = await mcpRequest(
|
|
505
543
|
[{ jsonrpc: "2.0", id: 11, method: "resources/read", params: { uri: "test://hello" } }],
|
|
@@ -509,6 +547,7 @@ test("MCP resources/read returns custom resource body", async () => {
|
|
|
509
547
|
expect(res.result.contents[0]?.text).toBe("hello resource");
|
|
510
548
|
});
|
|
511
549
|
|
|
550
|
+
/** MCP resources/read unknown URI returns error. */
|
|
512
551
|
test("MCP resources/read unknown URI returns error", async () => {
|
|
513
552
|
const responses = await mcpRequest(
|
|
514
553
|
[{ jsonrpc: "2.0", id: 12, method: "resources/read", params: { uri: "missing://nope" } }],
|