toolcraft 0.0.50 → 0.0.52
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/node_modules/@poe-code/agent-defs/README.md +35 -0
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/mock.js +9 -2
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript-script.js +3 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/request-approval.js +44 -2
- package/node_modules/@poe-code/agent-human-in-loop/package.json +0 -1
- package/node_modules/@poe-code/agent-mcp-config/README.md +54 -0
- package/node_modules/@poe-code/agent-mcp-config/package.json +0 -2
- package/node_modules/@poe-code/config-mutations/README.md +55 -0
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.d.ts +1 -0
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +65 -0
- package/node_modules/@poe-code/config-mutations/dist/execution/run-mutations.js +4 -11
- package/node_modules/@poe-code/config-mutations/package.json +0 -1
- package/node_modules/@poe-code/frontmatter/dist/fences.d.ts +17 -0
- package/node_modules/@poe-code/frontmatter/dist/fences.js +33 -5
- package/node_modules/@poe-code/frontmatter/dist/parse.js +86 -14
- package/node_modules/@poe-code/frontmatter/dist/stringify.js +13 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.js +14 -1
- package/node_modules/@poe-code/process-runner/dist/docker/build-context.d.ts +5 -0
- package/node_modules/@poe-code/process-runner/dist/docker/build-context.js +37 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +29 -29
- package/node_modules/@poe-code/process-runner/dist/index.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/index.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +49 -3
- package/node_modules/@poe-code/process-runner/package.json +8 -1
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +7 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +34 -7
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +75 -19
- package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/backends/utils.js +23 -2
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +16 -12
- package/node_modules/@poe-code/task-list/dist/state-machine.js +9 -0
- package/node_modules/@poe-code/task-list/package.json +0 -1
- package/node_modules/auth-store/dist/create-secret-store.js +4 -3
- package/node_modules/auth-store/dist/encrypted-file-store.js +49 -2
- package/node_modules/auth-store/dist/keychain-store.js +11 -4
- package/node_modules/auth-store/package.json +0 -1
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +69 -12
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +100 -68
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +19 -18
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +37 -31
- package/node_modules/mcp-oauth/package.json +0 -1
- package/node_modules/tiny-mcp-client/dist/internal.js +96 -10
- package/node_modules/tiny-mcp-client/package.json +0 -3
- package/node_modules/tiny-mcp-client/src/internal.ts +120 -18
- package/node_modules/tiny-mcp-client/src/transports.test.ts +231 -2
- package/node_modules/toolcraft-design/package.json +0 -1
- package/package.json +3 -2
|
@@ -71,7 +71,8 @@ export class McpClient {
|
|
|
71
71
|
await onToolsChanged();
|
|
72
72
|
});
|
|
73
73
|
messageLayer.onNotification("notifications/resources/list_changed", async () => {
|
|
74
|
-
if (onResourcesChanged === undefined
|
|
74
|
+
if (onResourcesChanged === undefined
|
|
75
|
+
|| this.currentServerCapabilities?.resources?.listChanged !== true) {
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
await onResourcesChanged();
|
|
@@ -90,7 +91,8 @@ export class McpClient {
|
|
|
90
91
|
await onResourceUpdated(uri);
|
|
91
92
|
});
|
|
92
93
|
messageLayer.onNotification("notifications/prompts/list_changed", async () => {
|
|
93
|
-
if (onPromptsChanged === undefined
|
|
94
|
+
if (onPromptsChanged === undefined
|
|
95
|
+
|| this.currentServerCapabilities?.prompts?.listChanged !== true) {
|
|
94
96
|
return;
|
|
95
97
|
}
|
|
96
98
|
await onPromptsChanged();
|
|
@@ -325,7 +327,11 @@ export class McpClient {
|
|
|
325
327
|
throw new Error("Server does not support resources");
|
|
326
328
|
}
|
|
327
329
|
const requestParams = params.cursor === undefined ? undefined : { cursor: params.cursor };
|
|
328
|
-
|
|
330
|
+
const result = await messageLayer.sendRequest("resources/list", requestParams);
|
|
331
|
+
if (!isResourcesListResult(result)) {
|
|
332
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid resources/list result");
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
329
335
|
}
|
|
330
336
|
async listResourceTemplates(params = {}) {
|
|
331
337
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -334,7 +340,11 @@ export class McpClient {
|
|
|
334
340
|
throw new Error("Server does not support resources");
|
|
335
341
|
}
|
|
336
342
|
const requestParams = params.cursor === undefined ? undefined : { cursor: params.cursor };
|
|
337
|
-
|
|
343
|
+
const result = await messageLayer.sendRequest("resources/templates/list", requestParams);
|
|
344
|
+
if (!isResourceTemplatesListResult(result)) {
|
|
345
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid resources/templates/list result");
|
|
346
|
+
}
|
|
347
|
+
return result;
|
|
338
348
|
}
|
|
339
349
|
async readResource(params) {
|
|
340
350
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -342,7 +352,11 @@ export class McpClient {
|
|
|
342
352
|
if (serverCapabilities.resources === undefined) {
|
|
343
353
|
throw new Error("Server does not support resources");
|
|
344
354
|
}
|
|
345
|
-
|
|
355
|
+
const result = await messageLayer.sendRequest("resources/read", params);
|
|
356
|
+
if (!isReadResourceResult(result)) {
|
|
357
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid resources/read result");
|
|
358
|
+
}
|
|
359
|
+
return result;
|
|
346
360
|
}
|
|
347
361
|
async subscribe(uri) {
|
|
348
362
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -377,7 +391,11 @@ export class McpClient {
|
|
|
377
391
|
if (serverCapabilities.prompts === undefined) {
|
|
378
392
|
throw new Error("Server does not support prompts");
|
|
379
393
|
}
|
|
380
|
-
|
|
394
|
+
const result = await messageLayer.sendRequest("prompts/get", params);
|
|
395
|
+
if (!isGetPromptResult(result)) {
|
|
396
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid prompts/get result");
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
381
399
|
}
|
|
382
400
|
async complete(params) {
|
|
383
401
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -385,7 +403,11 @@ export class McpClient {
|
|
|
385
403
|
if (serverCapabilities.completions === undefined) {
|
|
386
404
|
throw new Error("Server does not support completions");
|
|
387
405
|
}
|
|
388
|
-
|
|
406
|
+
const result = await messageLayer.sendRequest("completion/complete", params);
|
|
407
|
+
if (!isCompleteResult(result)) {
|
|
408
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid completion/complete result");
|
|
409
|
+
}
|
|
410
|
+
return result;
|
|
389
411
|
}
|
|
390
412
|
async setLogLevel(level) {
|
|
391
413
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -2434,6 +2456,72 @@ function isToolsListResult(value) {
|
|
|
2434
2456
|
&& Array.isArray(value.tools)
|
|
2435
2457
|
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
2436
2458
|
}
|
|
2459
|
+
function isResourcesListResult(value) {
|
|
2460
|
+
return isObjectRecord(value)
|
|
2461
|
+
&& Array.isArray(value.resources)
|
|
2462
|
+
&& value.resources.every(isResource)
|
|
2463
|
+
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
2464
|
+
}
|
|
2465
|
+
function isResourceTemplatesListResult(value) {
|
|
2466
|
+
return isObjectRecord(value)
|
|
2467
|
+
&& Array.isArray(value.resourceTemplates)
|
|
2468
|
+
&& value.resourceTemplates.every(isResourceTemplate)
|
|
2469
|
+
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
2470
|
+
}
|
|
2471
|
+
function isReadResourceResult(value) {
|
|
2472
|
+
return isObjectRecord(value)
|
|
2473
|
+
&& Array.isArray(value.contents)
|
|
2474
|
+
&& value.contents.every(isResourceContents);
|
|
2475
|
+
}
|
|
2476
|
+
function isGetPromptResult(value) {
|
|
2477
|
+
return isObjectRecord(value)
|
|
2478
|
+
&& (value.description === undefined || typeof value.description === "string")
|
|
2479
|
+
&& Array.isArray(value.messages)
|
|
2480
|
+
&& value.messages.every(isPromptMessage);
|
|
2481
|
+
}
|
|
2482
|
+
function isCompleteResult(value) {
|
|
2483
|
+
return isObjectRecord(value)
|
|
2484
|
+
&& isObjectRecord(value.completion)
|
|
2485
|
+
&& Array.isArray(value.completion.values)
|
|
2486
|
+
&& value.completion.values.every((candidate) => typeof candidate === "string")
|
|
2487
|
+
&& (value.completion.hasMore === undefined || typeof value.completion.hasMore === "boolean")
|
|
2488
|
+
&& (value.completion.total === undefined || typeof value.completion.total === "number");
|
|
2489
|
+
}
|
|
2490
|
+
function isResource(value) {
|
|
2491
|
+
return isObjectRecord(value)
|
|
2492
|
+
&& typeof value.uri === "string"
|
|
2493
|
+
&& typeof value.name === "string"
|
|
2494
|
+
&& (value.description === undefined || typeof value.description === "string")
|
|
2495
|
+
&& (value.mimeType === undefined || typeof value.mimeType === "string")
|
|
2496
|
+
&& (value.size === undefined || typeof value.size === "number");
|
|
2497
|
+
}
|
|
2498
|
+
function isResourceTemplate(value) {
|
|
2499
|
+
return isObjectRecord(value)
|
|
2500
|
+
&& typeof value.uriTemplate === "string"
|
|
2501
|
+
&& typeof value.name === "string"
|
|
2502
|
+
&& (value.description === undefined || typeof value.description === "string")
|
|
2503
|
+
&& (value.mimeType === undefined || typeof value.mimeType === "string");
|
|
2504
|
+
}
|
|
2505
|
+
function isResourceContents(value) {
|
|
2506
|
+
if (!isObjectRecord(value) || typeof value.uri !== "string") {
|
|
2507
|
+
return false;
|
|
2508
|
+
}
|
|
2509
|
+
if (value.mimeType !== undefined && typeof value.mimeType !== "string") {
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
const hasText = value.text !== undefined;
|
|
2513
|
+
const hasBlob = value.blob !== undefined;
|
|
2514
|
+
if (!hasText && !hasBlob) {
|
|
2515
|
+
return false;
|
|
2516
|
+
}
|
|
2517
|
+
return (!hasText || typeof value.text === "string")
|
|
2518
|
+
&& (!hasBlob || typeof value.blob === "string");
|
|
2519
|
+
}
|
|
2520
|
+
function isPromptMessage(value) {
|
|
2521
|
+
return isObjectRecord(value)
|
|
2522
|
+
&& (value.role === "user" || value.role === "assistant")
|
|
2523
|
+
&& isContentItem(value.content);
|
|
2524
|
+
}
|
|
2437
2525
|
function isContentItem(value) {
|
|
2438
2526
|
if (!isObjectRecord(value)) {
|
|
2439
2527
|
return false;
|
|
@@ -2447,9 +2535,7 @@ function isContentItem(value) {
|
|
|
2447
2535
|
if (value.type !== "resource" || !isObjectRecord(value.resource)) {
|
|
2448
2536
|
return false;
|
|
2449
2537
|
}
|
|
2450
|
-
return
|
|
2451
|
-
&& (value.resource.mimeType === undefined || typeof value.resource.mimeType === "string")
|
|
2452
|
-
&& (typeof value.resource.text === "string" || typeof value.resource.blob === "string");
|
|
2538
|
+
return isResourceContents(value.resource);
|
|
2453
2539
|
}
|
|
2454
2540
|
function hasOwn(value, property) {
|
|
2455
2541
|
return Object.prototype.hasOwnProperty.call(value, property);
|
|
@@ -213,7 +213,10 @@ export class McpClient {
|
|
|
213
213
|
await onToolsChanged();
|
|
214
214
|
});
|
|
215
215
|
messageLayer.onNotification("notifications/resources/list_changed", async () => {
|
|
216
|
-
if (
|
|
216
|
+
if (
|
|
217
|
+
onResourcesChanged === undefined
|
|
218
|
+
|| this.currentServerCapabilities?.resources?.listChanged !== true
|
|
219
|
+
) {
|
|
217
220
|
return;
|
|
218
221
|
}
|
|
219
222
|
|
|
@@ -236,7 +239,10 @@ export class McpClient {
|
|
|
236
239
|
await onResourceUpdated(uri);
|
|
237
240
|
});
|
|
238
241
|
messageLayer.onNotification("notifications/prompts/list_changed", async () => {
|
|
239
|
-
if (
|
|
242
|
+
if (
|
|
243
|
+
onPromptsChanged === undefined
|
|
244
|
+
|| this.currentServerCapabilities?.prompts?.listChanged !== true
|
|
245
|
+
) {
|
|
240
246
|
return;
|
|
241
247
|
}
|
|
242
248
|
|
|
@@ -521,10 +527,12 @@ export class McpClient {
|
|
|
521
527
|
}
|
|
522
528
|
|
|
523
529
|
const requestParams = params.cursor === undefined ? undefined : { cursor: params.cursor };
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
530
|
+
const result = await messageLayer.sendRequest("resources/list", requestParams);
|
|
531
|
+
if (!isResourcesListResult(result)) {
|
|
532
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid resources/list result");
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return result;
|
|
528
536
|
}
|
|
529
537
|
|
|
530
538
|
async listResourceTemplates(
|
|
@@ -537,10 +545,12 @@ export class McpClient {
|
|
|
537
545
|
}
|
|
538
546
|
|
|
539
547
|
const requestParams = params.cursor === undefined ? undefined : { cursor: params.cursor };
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
548
|
+
const result = await messageLayer.sendRequest("resources/templates/list", requestParams);
|
|
549
|
+
if (!isResourceTemplatesListResult(result)) {
|
|
550
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid resources/templates/list result");
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return result;
|
|
544
554
|
}
|
|
545
555
|
|
|
546
556
|
async readResource(params: ReadResourceParams): Promise<{ contents: ResourceContents[] }> {
|
|
@@ -550,9 +560,12 @@ export class McpClient {
|
|
|
550
560
|
throw new Error("Server does not support resources");
|
|
551
561
|
}
|
|
552
562
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
563
|
+
const result = await messageLayer.sendRequest("resources/read", params);
|
|
564
|
+
if (!isReadResourceResult(result)) {
|
|
565
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid resources/read result");
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return result;
|
|
556
569
|
}
|
|
557
570
|
|
|
558
571
|
async subscribe(uri: string): Promise<void> {
|
|
@@ -598,7 +611,12 @@ export class McpClient {
|
|
|
598
611
|
throw new Error("Server does not support prompts");
|
|
599
612
|
}
|
|
600
613
|
|
|
601
|
-
|
|
614
|
+
const result = await messageLayer.sendRequest("prompts/get", params);
|
|
615
|
+
if (!isGetPromptResult(result)) {
|
|
616
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid prompts/get result");
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return result;
|
|
602
620
|
}
|
|
603
621
|
|
|
604
622
|
async complete(params: CompleteParams): Promise<CompleteResult> {
|
|
@@ -608,7 +626,12 @@ export class McpClient {
|
|
|
608
626
|
throw new Error("Server does not support completions");
|
|
609
627
|
}
|
|
610
628
|
|
|
611
|
-
|
|
629
|
+
const result = await messageLayer.sendRequest("completion/complete", params);
|
|
630
|
+
if (!isCompleteResult(result)) {
|
|
631
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid completion/complete result");
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return result;
|
|
612
635
|
}
|
|
613
636
|
|
|
614
637
|
async setLogLevel(level: LogLevel): Promise<void> {
|
|
@@ -3597,6 +3620,87 @@ function isToolsListResult(value: unknown): value is { tools: Tool[]; nextCursor
|
|
|
3597
3620
|
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
3598
3621
|
}
|
|
3599
3622
|
|
|
3623
|
+
function isResourcesListResult(value: unknown): value is { resources: Resource[]; nextCursor?: string } {
|
|
3624
|
+
return isObjectRecord(value)
|
|
3625
|
+
&& Array.isArray(value.resources)
|
|
3626
|
+
&& value.resources.every(isResource)
|
|
3627
|
+
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
function isResourceTemplatesListResult(
|
|
3631
|
+
value: unknown
|
|
3632
|
+
): value is { resourceTemplates: ResourceTemplate[]; nextCursor?: string } {
|
|
3633
|
+
return isObjectRecord(value)
|
|
3634
|
+
&& Array.isArray(value.resourceTemplates)
|
|
3635
|
+
&& value.resourceTemplates.every(isResourceTemplate)
|
|
3636
|
+
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
function isReadResourceResult(value: unknown): value is { contents: ResourceContents[] } {
|
|
3640
|
+
return isObjectRecord(value)
|
|
3641
|
+
&& Array.isArray(value.contents)
|
|
3642
|
+
&& value.contents.every(isResourceContents);
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
function isGetPromptResult(value: unknown): value is GetPromptResult {
|
|
3646
|
+
return isObjectRecord(value)
|
|
3647
|
+
&& (value.description === undefined || typeof value.description === "string")
|
|
3648
|
+
&& Array.isArray(value.messages)
|
|
3649
|
+
&& value.messages.every(isPromptMessage);
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3652
|
+
function isCompleteResult(value: unknown): value is CompleteResult {
|
|
3653
|
+
return isObjectRecord(value)
|
|
3654
|
+
&& isObjectRecord(value.completion)
|
|
3655
|
+
&& Array.isArray(value.completion.values)
|
|
3656
|
+
&& value.completion.values.every((candidate) => typeof candidate === "string")
|
|
3657
|
+
&& (value.completion.hasMore === undefined || typeof value.completion.hasMore === "boolean")
|
|
3658
|
+
&& (value.completion.total === undefined || typeof value.completion.total === "number");
|
|
3659
|
+
}
|
|
3660
|
+
|
|
3661
|
+
function isResource(value: unknown): value is Resource {
|
|
3662
|
+
return isObjectRecord(value)
|
|
3663
|
+
&& typeof value.uri === "string"
|
|
3664
|
+
&& typeof value.name === "string"
|
|
3665
|
+
&& (value.description === undefined || typeof value.description === "string")
|
|
3666
|
+
&& (value.mimeType === undefined || typeof value.mimeType === "string")
|
|
3667
|
+
&& (value.size === undefined || typeof value.size === "number");
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
function isResourceTemplate(value: unknown): value is ResourceTemplate {
|
|
3671
|
+
return isObjectRecord(value)
|
|
3672
|
+
&& typeof value.uriTemplate === "string"
|
|
3673
|
+
&& typeof value.name === "string"
|
|
3674
|
+
&& (value.description === undefined || typeof value.description === "string")
|
|
3675
|
+
&& (value.mimeType === undefined || typeof value.mimeType === "string");
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
function isResourceContents(value: unknown): value is ResourceContents {
|
|
3679
|
+
if (!isObjectRecord(value) || typeof value.uri !== "string") {
|
|
3680
|
+
return false;
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3683
|
+
if (value.mimeType !== undefined && typeof value.mimeType !== "string") {
|
|
3684
|
+
return false;
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
const hasText = value.text !== undefined;
|
|
3688
|
+
const hasBlob = value.blob !== undefined;
|
|
3689
|
+
|
|
3690
|
+
if (!hasText && !hasBlob) {
|
|
3691
|
+
return false;
|
|
3692
|
+
}
|
|
3693
|
+
|
|
3694
|
+
return (!hasText || typeof value.text === "string")
|
|
3695
|
+
&& (!hasBlob || typeof value.blob === "string");
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3698
|
+
function isPromptMessage(value: unknown): value is PromptMessage {
|
|
3699
|
+
return isObjectRecord(value)
|
|
3700
|
+
&& (value.role === "user" || value.role === "assistant")
|
|
3701
|
+
&& isContentItem(value.content);
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3600
3704
|
function isContentItem(value: unknown): value is ContentItem {
|
|
3601
3705
|
if (!isObjectRecord(value)) {
|
|
3602
3706
|
return false;
|
|
@@ -3614,9 +3718,7 @@ function isContentItem(value: unknown): value is ContentItem {
|
|
|
3614
3718
|
return false;
|
|
3615
3719
|
}
|
|
3616
3720
|
|
|
3617
|
-
return
|
|
3618
|
-
&& (value.resource.mimeType === undefined || typeof value.resource.mimeType === "string")
|
|
3619
|
-
&& (typeof value.resource.text === "string" || typeof value.resource.blob === "string");
|
|
3721
|
+
return isResourceContents(value.resource);
|
|
3620
3722
|
}
|
|
3621
3723
|
|
|
3622
3724
|
function hasOwn(
|
|
@@ -3870,7 +3870,7 @@ describe("McpClient connect", () => {
|
|
|
3870
3870
|
id: initializeRequest.id,
|
|
3871
3871
|
result: {
|
|
3872
3872
|
protocolVersion: "2025-03-26",
|
|
3873
|
-
capabilities: {},
|
|
3873
|
+
capabilities: { resources: { listChanged: true } },
|
|
3874
3874
|
serverInfo: {
|
|
3875
3875
|
name: "server",
|
|
3876
3876
|
version: "1.0.0",
|
|
@@ -3900,6 +3900,40 @@ describe("McpClient connect", () => {
|
|
|
3900
3900
|
await client.close();
|
|
3901
3901
|
});
|
|
3902
3902
|
|
|
3903
|
+
it("ignores resources/list_changed when server did not advertise listChanged", async () => {
|
|
3904
|
+
const onResourcesChanged = vi.fn();
|
|
3905
|
+
const { client, readable, iterator, connectPromise } = await startClientHandshake(
|
|
3906
|
+
{
|
|
3907
|
+
protocolVersion: "2025-03-26",
|
|
3908
|
+
capabilities: { resources: {} },
|
|
3909
|
+
serverInfo: { name: "server", version: "1.0.0" },
|
|
3910
|
+
},
|
|
3911
|
+
{
|
|
3912
|
+
clientInfo: {
|
|
3913
|
+
name: "tiny-mcp-client",
|
|
3914
|
+
version: "0.1.0",
|
|
3915
|
+
},
|
|
3916
|
+
onResourcesChanged,
|
|
3917
|
+
}
|
|
3918
|
+
);
|
|
3919
|
+
await connectPromise;
|
|
3920
|
+
await iterator.next();
|
|
3921
|
+
|
|
3922
|
+
readable.write(
|
|
3923
|
+
`${JSON.stringify({
|
|
3924
|
+
jsonrpc: "2.0",
|
|
3925
|
+
method: "notifications/resources/list_changed",
|
|
3926
|
+
})}\n`
|
|
3927
|
+
);
|
|
3928
|
+
await new Promise<void>((resolve) => {
|
|
3929
|
+
setImmediate(resolve);
|
|
3930
|
+
});
|
|
3931
|
+
|
|
3932
|
+
expect(onResourcesChanged).toHaveBeenCalledTimes(0);
|
|
3933
|
+
|
|
3934
|
+
await client.close();
|
|
3935
|
+
});
|
|
3936
|
+
|
|
3903
3937
|
it("calls onPromptsChanged when server sends prompts/list_changed notification", async () => {
|
|
3904
3938
|
const readable = new PassThrough();
|
|
3905
3939
|
const writable = new PassThrough();
|
|
@@ -3942,7 +3976,7 @@ describe("McpClient connect", () => {
|
|
|
3942
3976
|
id: initializeRequest.id,
|
|
3943
3977
|
result: {
|
|
3944
3978
|
protocolVersion: "2025-03-26",
|
|
3945
|
-
capabilities: {},
|
|
3979
|
+
capabilities: { prompts: { listChanged: true } },
|
|
3946
3980
|
serverInfo: {
|
|
3947
3981
|
name: "server",
|
|
3948
3982
|
version: "1.0.0",
|
|
@@ -3972,6 +4006,40 @@ describe("McpClient connect", () => {
|
|
|
3972
4006
|
await client.close();
|
|
3973
4007
|
});
|
|
3974
4008
|
|
|
4009
|
+
it("ignores prompts/list_changed when server did not advertise listChanged", async () => {
|
|
4010
|
+
const onPromptsChanged = vi.fn();
|
|
4011
|
+
const { client, readable, iterator, connectPromise } = await startClientHandshake(
|
|
4012
|
+
{
|
|
4013
|
+
protocolVersion: "2025-03-26",
|
|
4014
|
+
capabilities: { prompts: {} },
|
|
4015
|
+
serverInfo: { name: "server", version: "1.0.0" },
|
|
4016
|
+
},
|
|
4017
|
+
{
|
|
4018
|
+
clientInfo: {
|
|
4019
|
+
name: "tiny-mcp-client",
|
|
4020
|
+
version: "0.1.0",
|
|
4021
|
+
},
|
|
4022
|
+
onPromptsChanged,
|
|
4023
|
+
}
|
|
4024
|
+
);
|
|
4025
|
+
await connectPromise;
|
|
4026
|
+
await iterator.next();
|
|
4027
|
+
|
|
4028
|
+
readable.write(
|
|
4029
|
+
`${JSON.stringify({
|
|
4030
|
+
jsonrpc: "2.0",
|
|
4031
|
+
method: "notifications/prompts/list_changed",
|
|
4032
|
+
})}\n`
|
|
4033
|
+
);
|
|
4034
|
+
await new Promise<void>((resolve) => {
|
|
4035
|
+
setImmediate(resolve);
|
|
4036
|
+
});
|
|
4037
|
+
|
|
4038
|
+
expect(onPromptsChanged).toHaveBeenCalledTimes(0);
|
|
4039
|
+
|
|
4040
|
+
await client.close();
|
|
4041
|
+
});
|
|
4042
|
+
|
|
3975
4043
|
it("calls onResourceUpdated with uri when server sends resources/updated notification", async () => {
|
|
3976
4044
|
const readable = new PassThrough();
|
|
3977
4045
|
const writable = new PassThrough();
|
|
@@ -5685,6 +5753,36 @@ describe("McpClient listResources", () => {
|
|
|
5685
5753
|
nextCursor: "4",
|
|
5686
5754
|
});
|
|
5687
5755
|
});
|
|
5756
|
+
|
|
5757
|
+
it("rejects invalid resource descriptors returned from resources/list", async () => {
|
|
5758
|
+
const { client, readable, iterator, connectPromise } = await startClientHandshake({
|
|
5759
|
+
protocolVersion: "2025-03-26",
|
|
5760
|
+
capabilities: { resources: {} },
|
|
5761
|
+
serverInfo: { name: "server", version: "1.0.0" },
|
|
5762
|
+
});
|
|
5763
|
+
await connectPromise;
|
|
5764
|
+
await iterator.next();
|
|
5765
|
+
|
|
5766
|
+
const requestPromise = client.listResources();
|
|
5767
|
+
const requestLine = await iterator.next();
|
|
5768
|
+
if (requestLine.done) {
|
|
5769
|
+
throw new Error("Expected resources/list request line to be written");
|
|
5770
|
+
}
|
|
5771
|
+
const request = JSON.parse(requestLine.value) as { id: number };
|
|
5772
|
+
readable.write(
|
|
5773
|
+
`${JSON.stringify({
|
|
5774
|
+
jsonrpc: "2.0",
|
|
5775
|
+
id: request.id,
|
|
5776
|
+
result: {
|
|
5777
|
+
resources: [{ uri: 42, name: null }],
|
|
5778
|
+
nextCursor: 7,
|
|
5779
|
+
},
|
|
5780
|
+
})}\n`
|
|
5781
|
+
);
|
|
5782
|
+
|
|
5783
|
+
await expect(requestPromise).rejects.toThrow("Invalid resources/list result");
|
|
5784
|
+
await client.close();
|
|
5785
|
+
});
|
|
5688
5786
|
});
|
|
5689
5787
|
|
|
5690
5788
|
describe("McpClient listResourceTemplates", () => {
|
|
@@ -5774,6 +5872,36 @@ describe("McpClient listResourceTemplates", () => {
|
|
|
5774
5872
|
resourceTemplates: expectedResourceTemplates,
|
|
5775
5873
|
});
|
|
5776
5874
|
});
|
|
5875
|
+
|
|
5876
|
+
it("rejects invalid resource templates returned from resources/templates/list", async () => {
|
|
5877
|
+
const { client, readable, iterator, connectPromise } = await startClientHandshake({
|
|
5878
|
+
protocolVersion: "2025-03-26",
|
|
5879
|
+
capabilities: { resources: {} },
|
|
5880
|
+
serverInfo: { name: "server", version: "1.0.0" },
|
|
5881
|
+
});
|
|
5882
|
+
await connectPromise;
|
|
5883
|
+
await iterator.next();
|
|
5884
|
+
|
|
5885
|
+
const requestPromise = client.listResourceTemplates();
|
|
5886
|
+
const requestLine = await iterator.next();
|
|
5887
|
+
if (requestLine.done) {
|
|
5888
|
+
throw new Error("Expected resources/templates/list request line to be written");
|
|
5889
|
+
}
|
|
5890
|
+
const request = JSON.parse(requestLine.value) as { id: number };
|
|
5891
|
+
readable.write(
|
|
5892
|
+
`${JSON.stringify({
|
|
5893
|
+
jsonrpc: "2.0",
|
|
5894
|
+
id: request.id,
|
|
5895
|
+
result: {
|
|
5896
|
+
resourceTemplates: [{ uriTemplate: 123, name: false, mimeType: 42 }],
|
|
5897
|
+
nextCursor: 7,
|
|
5898
|
+
},
|
|
5899
|
+
})}\n`
|
|
5900
|
+
);
|
|
5901
|
+
|
|
5902
|
+
await expect(requestPromise).rejects.toThrow("Invalid resources/templates/list result");
|
|
5903
|
+
await client.close();
|
|
5904
|
+
});
|
|
5777
5905
|
});
|
|
5778
5906
|
|
|
5779
5907
|
describe("McpClient listPrompts", () => {
|
|
@@ -6188,6 +6316,40 @@ describe("McpClient getPrompt", () => {
|
|
|
6188
6316
|
new Set(["user", "assistant"])
|
|
6189
6317
|
);
|
|
6190
6318
|
});
|
|
6319
|
+
|
|
6320
|
+
it("rejects invalid prompt messages returned from prompts/get", async () => {
|
|
6321
|
+
const { client, readable, iterator, connectPromise } = await startClientHandshake({
|
|
6322
|
+
protocolVersion: "2025-03-26",
|
|
6323
|
+
capabilities: { prompts: {} },
|
|
6324
|
+
serverInfo: { name: "server", version: "1.0.0" },
|
|
6325
|
+
});
|
|
6326
|
+
await connectPromise;
|
|
6327
|
+
await iterator.next();
|
|
6328
|
+
|
|
6329
|
+
const requestPromise = client.getPrompt({ name: "broken" });
|
|
6330
|
+
const requestLine = await iterator.next();
|
|
6331
|
+
if (requestLine.done) {
|
|
6332
|
+
throw new Error("Expected prompts/get request line to be written");
|
|
6333
|
+
}
|
|
6334
|
+
const request = JSON.parse(requestLine.value) as { id: number };
|
|
6335
|
+
readable.write(
|
|
6336
|
+
`${JSON.stringify({
|
|
6337
|
+
jsonrpc: "2.0",
|
|
6338
|
+
id: request.id,
|
|
6339
|
+
result: {
|
|
6340
|
+
messages: [
|
|
6341
|
+
{
|
|
6342
|
+
role: "system",
|
|
6343
|
+
content: { type: "text", text: 123 },
|
|
6344
|
+
},
|
|
6345
|
+
],
|
|
6346
|
+
},
|
|
6347
|
+
})}\n`
|
|
6348
|
+
);
|
|
6349
|
+
|
|
6350
|
+
await expect(requestPromise).rejects.toThrow("Invalid prompts/get result");
|
|
6351
|
+
await client.close();
|
|
6352
|
+
});
|
|
6191
6353
|
});
|
|
6192
6354
|
|
|
6193
6355
|
describe("McpClient complete", () => {
|
|
@@ -6482,6 +6644,44 @@ describe("McpClient complete", () => {
|
|
|
6482
6644
|
|
|
6483
6645
|
await expect(completePromise).resolves.toEqual(expectedResult);
|
|
6484
6646
|
});
|
|
6647
|
+
|
|
6648
|
+
it("rejects invalid completion values returned from completion/complete", async () => {
|
|
6649
|
+
const { client, readable, iterator, connectPromise } = await startClientHandshake({
|
|
6650
|
+
protocolVersion: "2025-03-26",
|
|
6651
|
+
capabilities: { completions: {} },
|
|
6652
|
+
serverInfo: { name: "server", version: "1.0.0" },
|
|
6653
|
+
});
|
|
6654
|
+
await connectPromise;
|
|
6655
|
+
await iterator.next();
|
|
6656
|
+
|
|
6657
|
+
const requestPromise = client.complete({
|
|
6658
|
+
ref: {
|
|
6659
|
+
type: "ref/prompt",
|
|
6660
|
+
name: "code_review",
|
|
6661
|
+
},
|
|
6662
|
+
argument: {
|
|
6663
|
+
name: "language",
|
|
6664
|
+
value: "p",
|
|
6665
|
+
},
|
|
6666
|
+
});
|
|
6667
|
+
const requestLine = await iterator.next();
|
|
6668
|
+
if (requestLine.done) {
|
|
6669
|
+
throw new Error("Expected completion/complete request line to be written");
|
|
6670
|
+
}
|
|
6671
|
+
const request = JSON.parse(requestLine.value) as { id: number };
|
|
6672
|
+
readable.write(
|
|
6673
|
+
`${JSON.stringify({
|
|
6674
|
+
jsonrpc: "2.0",
|
|
6675
|
+
id: request.id,
|
|
6676
|
+
result: {
|
|
6677
|
+
completion: { values: "not-an-array" },
|
|
6678
|
+
},
|
|
6679
|
+
})}\n`
|
|
6680
|
+
);
|
|
6681
|
+
|
|
6682
|
+
await expect(requestPromise).rejects.toThrow("Invalid completion/complete result");
|
|
6683
|
+
await client.close();
|
|
6684
|
+
});
|
|
6485
6685
|
});
|
|
6486
6686
|
|
|
6487
6687
|
describe("McpClient readResource", () => {
|
|
@@ -6575,6 +6775,35 @@ describe("McpClient readResource", () => {
|
|
|
6575
6775
|
});
|
|
6576
6776
|
});
|
|
6577
6777
|
|
|
6778
|
+
it("rejects invalid content returned from resources/read", async () => {
|
|
6779
|
+
const { client, readable, iterator, connectPromise } = await startClientHandshake({
|
|
6780
|
+
protocolVersion: "2025-03-26",
|
|
6781
|
+
capabilities: { resources: {} },
|
|
6782
|
+
serverInfo: { name: "server", version: "1.0.0" },
|
|
6783
|
+
});
|
|
6784
|
+
await connectPromise;
|
|
6785
|
+
await iterator.next();
|
|
6786
|
+
|
|
6787
|
+
const requestPromise = client.readResource({ uri: "memo://bad" });
|
|
6788
|
+
const requestLine = await iterator.next();
|
|
6789
|
+
if (requestLine.done) {
|
|
6790
|
+
throw new Error("Expected resources/read request line to be written");
|
|
6791
|
+
}
|
|
6792
|
+
const request = JSON.parse(requestLine.value) as { id: number };
|
|
6793
|
+
readable.write(
|
|
6794
|
+
`${JSON.stringify({
|
|
6795
|
+
jsonrpc: "2.0",
|
|
6796
|
+
id: request.id,
|
|
6797
|
+
result: {
|
|
6798
|
+
contents: [{ uri: "memo://bad", mimeType: 123, text: 456 }],
|
|
6799
|
+
},
|
|
6800
|
+
})}\n`
|
|
6801
|
+
);
|
|
6802
|
+
|
|
6803
|
+
await expect(requestPromise).rejects.toThrow("Invalid resources/read result");
|
|
6804
|
+
await client.close();
|
|
6805
|
+
});
|
|
6806
|
+
|
|
6578
6807
|
it("sends resources/read with uri and returns binary resource contents", async () => {
|
|
6579
6808
|
const readable = new PassThrough();
|
|
6580
6809
|
const writable = new PassThrough();
|