toolcraft 0.0.22 → 0.0.24
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 +2 -2
- package/dist/cli.compile-check.js +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +57 -14
- package/dist/error-report.js +32 -3
- package/dist/human-in-loop/approval-tasks.d.ts +1 -0
- package/dist/human-in-loop/approval-tasks.js +7 -5
- package/dist/human-in-loop/approvals-commands.js +51 -8
- package/dist/human-in-loop/runner.js +24 -19
- package/dist/human-in-loop/state-machine.d.ts +3 -3
- package/dist/human-in-loop/state-machine.js +13 -5
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -1
- package/dist/mcp-proxy.js +85 -19
- package/dist/mcp.compile-check.js +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +50 -8
- package/dist/renderer.js +119 -13
- package/dist/sdk.compile-check.js +1 -0
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +56 -11
- package/node_modules/@poe-code/agent-defs/dist/registry.d.ts +1 -1
- package/node_modules/@poe-code/agent-defs/dist/registry.js +22 -11
- package/node_modules/@poe-code/agent-defs/package.json +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript-script.js +5 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript.js +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/package.json +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.d.ts +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +41 -92
- package/node_modules/@poe-code/agent-mcp-config/dist/configs.js +4 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.d.ts +14 -2
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.js +11 -4
- package/node_modules/@poe-code/agent-mcp-config/package.json +1 -1
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +200 -22
- package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +7 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/index.js +1 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/json.js +11 -7
- package/node_modules/@poe-code/config-mutations/dist/formats/object.d.ts +4 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/object.js +27 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/toml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/formats/yaml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.d.ts +11 -1
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.js +10 -1
- package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +25 -1
- package/node_modules/@poe-code/config-mutations/dist/types.d.ts +12 -2
- package/node_modules/@poe-code/config-mutations/package.json +1 -1
- package/node_modules/@poe-code/design-system/dist/acp/components.js +3 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.js +6 -1
- package/node_modules/@poe-code/design-system/dist/components/color.js +9 -8
- package/node_modules/@poe-code/design-system/dist/components/command-errors.js +3 -2
- package/node_modules/@poe-code/design-system/dist/components/detail-card.d.ts +22 -0
- package/node_modules/@poe-code/design-system/dist/components/detail-card.js +69 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +88 -11
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/table.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/components/table.js +82 -5
- package/node_modules/@poe-code/design-system/dist/components/template.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/template.js +198 -32
- package/node_modules/@poe-code/design-system/dist/components/text.js +29 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +2 -2
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +77 -32
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +28 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +45 -28
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.js +71 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +32 -10
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +3 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +57 -6
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +12 -15
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/design-system/dist/index.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +8 -5
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +1 -1
- package/node_modules/@poe-code/design-system/dist/static/menu.js +8 -2
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +10 -4
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +9 -2
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +19 -2
- package/node_modules/@poe-code/design-system/package.json +2 -1
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +244 -110
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +16 -4
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +3 -2
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +16 -1
- 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/testing/mock-runner.js +30 -8
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +57 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +484 -0
- package/node_modules/@poe-code/process-runner/package.json +1 -1
- package/node_modules/@poe-code/task-list/README.md +0 -2
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +3 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +89 -59
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +9 -3
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +460 -99
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +156 -154
- 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 +79 -0
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +120 -132
- package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/task-list/dist/index.js +2 -0
- package/node_modules/@poe-code/task-list/dist/move.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/move.js +215 -0
- package/node_modules/@poe-code/task-list/dist/open.js +3 -4
- package/node_modules/@poe-code/task-list/dist/state-machine.js +3 -1
- package/node_modules/@poe-code/task-list/dist/state.js +9 -0
- package/node_modules/@poe-code/task-list/dist/types.d.ts +48 -13
- package/node_modules/@poe-code/task-list/package.json +1 -2
- package/node_modules/auth-store/dist/create-secret-store.js +4 -1
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +7 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +69 -7
- package/node_modules/auth-store/dist/index.d.ts +1 -1
- package/node_modules/auth-store/dist/keychain-store.d.ts +4 -1
- package/node_modules/auth-store/dist/keychain-store.js +18 -16
- package/node_modules/auth-store/dist/provider-store.d.ts +5 -1
- package/node_modules/auth-store/dist/provider-store.js +55 -7
- package/node_modules/auth-store/dist/types.d.ts +3 -1
- package/node_modules/auth-store/package.json +2 -1
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +46 -15
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +49 -12
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +6 -1
- package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +1 -1
- package/node_modules/mcp-oauth/package.json +1 -0
- package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +1 -1
- package/node_modules/tiny-mcp-client/dist/internal.d.ts +8 -4
- package/node_modules/tiny-mcp-client/dist/internal.js +237 -67
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +1 -1
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +4 -7
- package/node_modules/tiny-mcp-client/package.json +2 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.integration.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +46 -0
- package/node_modules/tiny-mcp-client/src/internal.ts +279 -77
- package/node_modules/tiny-mcp-client/src/mcp-client-tiny-stdio-test-server-tools.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/oauth-discovery.ts +5 -10
- package/node_modules/tiny-mcp-client/src/transports.test.ts +588 -6
- package/package.json +10 -12
- package/node_modules/@poe-code/file-lock/README.md +0 -52
- package/node_modules/@poe-code/file-lock/dist/index.d.ts +0 -1
- package/node_modules/@poe-code/file-lock/dist/index.js +0 -1
- package/node_modules/@poe-code/file-lock/dist/lock.d.ts +0 -27
- package/node_modules/@poe-code/file-lock/dist/lock.js +0 -203
- package/node_modules/@poe-code/file-lock/package.json +0 -23
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
OAuthError,
|
|
9
9
|
type OAuthClientProvider,
|
|
10
10
|
type OAuthClientProviderOptions,
|
|
11
|
-
} from "
|
|
11
|
+
} from "mcp-oauth";
|
|
12
12
|
import type { Server as TinyStdioMcpServer } from "tiny-stdio-mcp-server";
|
|
13
13
|
import {
|
|
14
14
|
OAuthMetadataDiscovery,
|
|
@@ -28,7 +28,7 @@ export {
|
|
|
28
28
|
export {
|
|
29
29
|
createAuthStoreSessionStore,
|
|
30
30
|
createDefaultOAuthClientProvider,
|
|
31
|
-
} from "
|
|
31
|
+
} from "mcp-oauth";
|
|
32
32
|
export type {
|
|
33
33
|
OAuthDiscoveryCache,
|
|
34
34
|
} from "./oauth-discovery.js";
|
|
@@ -39,7 +39,7 @@ export type {
|
|
|
39
39
|
OAuthClientProviderOptions,
|
|
40
40
|
OAuthSessionStore,
|
|
41
41
|
StoredOAuthSession,
|
|
42
|
-
} from "
|
|
42
|
+
} from "mcp-oauth";
|
|
43
43
|
|
|
44
44
|
export type RequestId = number | string;
|
|
45
45
|
|
|
@@ -97,6 +97,7 @@ export interface InitializeResult {
|
|
|
97
97
|
|
|
98
98
|
export interface McpClientOptions {
|
|
99
99
|
clientInfo: Implementation;
|
|
100
|
+
requestTimeoutMs?: number;
|
|
100
101
|
capabilities?: ClientCapabilities;
|
|
101
102
|
onToolsChanged?: () => void | Promise<void>;
|
|
102
103
|
onResourcesChanged?: () => void | Promise<void>;
|
|
@@ -115,8 +116,11 @@ const MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
|
115
116
|
export class McpClient {
|
|
116
117
|
private currentState: "disconnected" | "initializing" | "ready" | "closed" = "disconnected";
|
|
117
118
|
private currentServerCapabilities: ServerCapabilities | null = null;
|
|
119
|
+
private currentClientCapabilities: ClientCapabilities | null = null;
|
|
118
120
|
private currentServerInfo: Implementation | null = null;
|
|
119
121
|
private currentInstructions: string | undefined;
|
|
122
|
+
private readonly subscribedResourceUris = new Set<string>();
|
|
123
|
+
private readonly activeProgressTokens = new Map<ProgressToken, number>();
|
|
120
124
|
private readonly options: McpClientOptions;
|
|
121
125
|
private transport: McpTransport | null = null;
|
|
122
126
|
private messageLayer: JsonRpcMessageLayer | null = null;
|
|
@@ -130,7 +134,9 @@ export class McpClient {
|
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
get serverCapabilities(): ServerCapabilities | null {
|
|
133
|
-
return this.currentServerCapabilities
|
|
137
|
+
return this.currentServerCapabilities === null
|
|
138
|
+
? null
|
|
139
|
+
: structuredClone(this.currentServerCapabilities);
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
get serverInfo(): Implementation | null {
|
|
@@ -162,6 +168,13 @@ export class McpClient {
|
|
|
162
168
|
throw new Error("MCP client is already connected");
|
|
163
169
|
}
|
|
164
170
|
|
|
171
|
+
this.currentServerCapabilities = null;
|
|
172
|
+
this.currentClientCapabilities = null;
|
|
173
|
+
this.currentServerInfo = null;
|
|
174
|
+
this.currentInstructions = undefined;
|
|
175
|
+
this.subscribedResourceUris.clear();
|
|
176
|
+
this.activeProgressTokens.clear();
|
|
177
|
+
|
|
165
178
|
const transportClosedReason = transport.closed
|
|
166
179
|
.then((closedEvent) => closedEvent.reason)
|
|
167
180
|
.catch((error: unknown) =>
|
|
@@ -170,7 +183,7 @@ export class McpClient {
|
|
|
170
183
|
const messageLayer = new JsonRpcMessageLayer(
|
|
171
184
|
transport.readable,
|
|
172
185
|
transport.writable,
|
|
173
|
-
|
|
186
|
+
this.options.requestTimeoutMs,
|
|
174
187
|
transportClosedReason
|
|
175
188
|
);
|
|
176
189
|
const {
|
|
@@ -192,14 +205,8 @@ export class McpClient {
|
|
|
192
205
|
);
|
|
193
206
|
}
|
|
194
207
|
|
|
195
|
-
if (onRootsList !== undefined) {
|
|
196
|
-
messageLayer.onRequest("roots/list", async () => ({
|
|
197
|
-
roots: await onRootsList(),
|
|
198
|
-
}));
|
|
199
|
-
}
|
|
200
|
-
|
|
201
208
|
messageLayer.onNotification("notifications/tools/list_changed", async () => {
|
|
202
|
-
if (onToolsChanged === undefined) {
|
|
209
|
+
if (onToolsChanged === undefined || this.currentServerCapabilities?.tools?.listChanged !== true) {
|
|
203
210
|
return;
|
|
204
211
|
}
|
|
205
212
|
|
|
@@ -222,7 +229,7 @@ export class McpClient {
|
|
|
222
229
|
}
|
|
223
230
|
|
|
224
231
|
const { uri } = params as { uri?: unknown };
|
|
225
|
-
if (typeof uri !== "string") {
|
|
232
|
+
if (typeof uri !== "string" || !this.subscribedResourceUris.has(uri)) {
|
|
226
233
|
return;
|
|
227
234
|
}
|
|
228
235
|
|
|
@@ -265,7 +272,11 @@ export class McpClient {
|
|
|
265
272
|
}
|
|
266
273
|
|
|
267
274
|
const { progressToken, progress } = params;
|
|
268
|
-
if (
|
|
275
|
+
if (
|
|
276
|
+
!isRequestId(progressToken) ||
|
|
277
|
+
typeof progress !== "number" ||
|
|
278
|
+
!this.activeProgressTokens.has(progressToken)
|
|
279
|
+
) {
|
|
269
280
|
return;
|
|
270
281
|
}
|
|
271
282
|
|
|
@@ -297,6 +308,8 @@ export class McpClient {
|
|
|
297
308
|
this.transport = transport;
|
|
298
309
|
this.messageLayer = messageLayer;
|
|
299
310
|
this.currentState = "initializing";
|
|
311
|
+
this.subscribedResourceUris.clear();
|
|
312
|
+
this.activeProgressTokens.clear();
|
|
300
313
|
transport.closed
|
|
301
314
|
.then((closedEvent) => {
|
|
302
315
|
if (this.transport !== transport) {
|
|
@@ -334,26 +347,52 @@ export class McpClient {
|
|
|
334
347
|
};
|
|
335
348
|
}
|
|
336
349
|
|
|
337
|
-
|
|
338
|
-
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
339
|
-
clientInfo: this.options.clientInfo,
|
|
340
|
-
capabilities,
|
|
341
|
-
})) as InitializeResult;
|
|
350
|
+
this.currentClientCapabilities = structuredClone(capabilities);
|
|
342
351
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
352
|
+
try {
|
|
353
|
+
const initializeResultValue = await messageLayer.sendRequest("initialize", {
|
|
354
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
355
|
+
clientInfo: this.options.clientInfo,
|
|
356
|
+
capabilities,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
if (!isInitializeResult(initializeResultValue)) {
|
|
360
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid initialize result");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const initializeResult = initializeResultValue;
|
|
364
|
+
|
|
365
|
+
if (initializeResult.protocolVersion !== MCP_PROTOCOL_VERSION) {
|
|
366
|
+
throw new McpError(
|
|
367
|
+
ERROR_INVALID_REQUEST,
|
|
368
|
+
`Unsupported protocol version: ${initializeResult.protocolVersion}`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
349
371
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
372
|
+
this.currentServerCapabilities = structuredClone(initializeResult.capabilities);
|
|
373
|
+
this.currentServerInfo = { ...initializeResult.serverInfo };
|
|
374
|
+
this.currentInstructions = initializeResult.instructions;
|
|
375
|
+
if (onRootsList !== undefined) {
|
|
376
|
+
messageLayer.onRequest("roots/list", async () => ({
|
|
377
|
+
roots: await onRootsList(),
|
|
378
|
+
}));
|
|
379
|
+
}
|
|
380
|
+
messageLayer.sendNotification("notifications/initialized");
|
|
381
|
+
this.currentState = "ready";
|
|
382
|
+
|
|
383
|
+
return initializeResult;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (this.transport === transport) {
|
|
386
|
+
const reason = error instanceof Error ? error : new Error(String(error));
|
|
387
|
+
messageLayer.dispose(reason);
|
|
388
|
+
transport.dispose(reason);
|
|
389
|
+
this.messageLayer = null;
|
|
390
|
+
this.transport = null;
|
|
391
|
+
this.currentState = "disconnected";
|
|
392
|
+
}
|
|
355
393
|
|
|
356
|
-
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
357
396
|
}
|
|
358
397
|
|
|
359
398
|
private getServerCapabilitiesOrThrow(): ServerCapabilities {
|
|
@@ -372,10 +411,12 @@ export class McpClient {
|
|
|
372
411
|
}
|
|
373
412
|
|
|
374
413
|
const requestParams = params.cursor === undefined ? undefined : { cursor: params.cursor };
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
414
|
+
const result = await messageLayer.sendRequest("tools/list", requestParams);
|
|
415
|
+
if (!isToolsListResult(result)) {
|
|
416
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid tools/list result");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return result;
|
|
379
420
|
}
|
|
380
421
|
|
|
381
422
|
async callTool(params: CallToolParams, options: CallToolOptions = {}): Promise<CallToolResult> {
|
|
@@ -385,6 +426,10 @@ export class McpClient {
|
|
|
385
426
|
throw new Error("Server does not support tools");
|
|
386
427
|
}
|
|
387
428
|
|
|
429
|
+
if (options.signal?.aborted) {
|
|
430
|
+
throw options.signal.reason;
|
|
431
|
+
}
|
|
432
|
+
|
|
388
433
|
const requestParams =
|
|
389
434
|
options.progressToken === undefined
|
|
390
435
|
? params
|
|
@@ -395,39 +440,63 @@ export class McpClient {
|
|
|
395
440
|
},
|
|
396
441
|
};
|
|
397
442
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}) as Promise<CallToolResult>;
|
|
404
|
-
if (options.signal === undefined) {
|
|
405
|
-
return await requestPromise;
|
|
443
|
+
if (options.progressToken !== undefined) {
|
|
444
|
+
this.activeProgressTokens.set(
|
|
445
|
+
options.progressToken,
|
|
446
|
+
(this.activeProgressTokens.get(options.progressToken) ?? 0) + 1
|
|
447
|
+
);
|
|
406
448
|
}
|
|
407
|
-
const signal = options.signal;
|
|
408
449
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
450
|
+
try {
|
|
451
|
+
let requestId: RequestId | undefined;
|
|
452
|
+
const requestPromise = messageLayer.sendRequest("tools/call", requestParams, {
|
|
453
|
+
onRequestId: (nextRequestId) => {
|
|
454
|
+
requestId = nextRequestId;
|
|
455
|
+
},
|
|
456
|
+
}).then((result) => {
|
|
457
|
+
if (!isCallToolResult(result)) {
|
|
458
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid tool result");
|
|
414
459
|
}
|
|
415
|
-
reject(signal.reason);
|
|
416
|
-
};
|
|
417
460
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if (signal
|
|
421
|
-
|
|
422
|
-
rejectWithAbortReason();
|
|
461
|
+
return result;
|
|
462
|
+
});
|
|
463
|
+
if (options.signal === undefined) {
|
|
464
|
+
return await requestPromise;
|
|
423
465
|
}
|
|
424
|
-
|
|
466
|
+
const signal = options.signal;
|
|
425
467
|
|
|
426
|
-
|
|
427
|
-
|
|
468
|
+
let abortListener: (() => void) | undefined;
|
|
469
|
+
const abortPromise = new Promise<CallToolResult>((_, reject) => {
|
|
470
|
+
const rejectWithAbortReason = () => {
|
|
471
|
+
if (requestId !== undefined) {
|
|
472
|
+
messageLayer.sendNotification("notifications/cancelled", { requestId });
|
|
473
|
+
}
|
|
474
|
+
reject(signal.reason);
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
abortListener = rejectWithAbortReason;
|
|
478
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
479
|
+
if (signal.aborted) {
|
|
480
|
+
signal.removeEventListener("abort", abortListener);
|
|
481
|
+
rejectWithAbortReason();
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
return (await Promise.race([requestPromise, abortPromise])) as CallToolResult;
|
|
487
|
+
} finally {
|
|
488
|
+
if (abortListener !== undefined) {
|
|
489
|
+
signal.removeEventListener("abort", abortListener);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
428
492
|
} finally {
|
|
429
|
-
if (
|
|
430
|
-
|
|
493
|
+
if (options.progressToken !== undefined) {
|
|
494
|
+
const activeCount = this.activeProgressTokens.get(options.progressToken);
|
|
495
|
+
if (activeCount === 1) {
|
|
496
|
+
this.activeProgressTokens.delete(options.progressToken);
|
|
497
|
+
} else if (activeCount !== undefined) {
|
|
498
|
+
this.activeProgressTokens.set(options.progressToken, activeCount - 1);
|
|
499
|
+
}
|
|
431
500
|
}
|
|
432
501
|
}
|
|
433
502
|
}
|
|
@@ -484,6 +553,7 @@ export class McpClient {
|
|
|
484
553
|
}
|
|
485
554
|
|
|
486
555
|
await messageLayer.sendRequest("resources/subscribe", { uri });
|
|
556
|
+
this.subscribedResourceUris.add(uri);
|
|
487
557
|
}
|
|
488
558
|
|
|
489
559
|
async unsubscribe(uri: string): Promise<void> {
|
|
@@ -494,6 +564,7 @@ export class McpClient {
|
|
|
494
564
|
}
|
|
495
565
|
|
|
496
566
|
await messageLayer.sendRequest("resources/unsubscribe", { uri });
|
|
567
|
+
this.subscribedResourceUris.delete(uri);
|
|
497
568
|
}
|
|
498
569
|
|
|
499
570
|
async listPrompts(params: PaginatedParams = {}): Promise<{ prompts: Prompt[]; nextCursor?: string }> {
|
|
@@ -553,6 +624,10 @@ export class McpClient {
|
|
|
553
624
|
|
|
554
625
|
async sendRootsChanged(): Promise<void> {
|
|
555
626
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
627
|
+
if (this.currentClientCapabilities?.roots?.listChanged !== true) {
|
|
628
|
+
throw new Error("Client did not advertise roots list changes");
|
|
629
|
+
}
|
|
630
|
+
|
|
556
631
|
messageLayer.sendNotification("notifications/roots/list_changed");
|
|
557
632
|
}
|
|
558
633
|
|
|
@@ -571,6 +646,12 @@ export class McpClient {
|
|
|
571
646
|
this.transport?.dispose(closeError);
|
|
572
647
|
this.messageLayer = null;
|
|
573
648
|
this.transport = null;
|
|
649
|
+
this.currentServerCapabilities = null;
|
|
650
|
+
this.currentClientCapabilities = null;
|
|
651
|
+
this.currentServerInfo = null;
|
|
652
|
+
this.currentInstructions = undefined;
|
|
653
|
+
this.subscribedResourceUris.clear();
|
|
654
|
+
this.activeProgressTokens.clear();
|
|
574
655
|
this.currentState = "closed";
|
|
575
656
|
}
|
|
576
657
|
}
|
|
@@ -2402,7 +2483,6 @@ export class HttpTransport implements McpTransport {
|
|
|
2402
2483
|
this.disposed = true;
|
|
2403
2484
|
this.abortInFlightFetches();
|
|
2404
2485
|
this.cancelOpenSseReaders();
|
|
2405
|
-
this.terminateSession();
|
|
2406
2486
|
|
|
2407
2487
|
if (!this.writeStream.destroyed && !this.writeStream.writableEnded) {
|
|
2408
2488
|
this.writeStream.end();
|
|
@@ -2412,9 +2492,24 @@ export class HttpTransport implements McpTransport {
|
|
|
2412
2492
|
this.readStream.end();
|
|
2413
2493
|
}
|
|
2414
2494
|
|
|
2495
|
+
void this.closeWithSessionTermination(reason);
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
private async closeWithSessionTermination(reason: Error): Promise<void> {
|
|
2499
|
+
let closeReason = reason;
|
|
2500
|
+
if (this.sessionId !== undefined) {
|
|
2501
|
+
const sessionId = this.sessionId;
|
|
2502
|
+
this.sessionId = undefined;
|
|
2503
|
+
try {
|
|
2504
|
+
await this.sendSessionTerminationRequest(sessionId);
|
|
2505
|
+
} catch (error) {
|
|
2506
|
+
closeReason = error instanceof Error ? error : new Error(String(error));
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2415
2510
|
const resolveClosed = this.resolveClosed;
|
|
2416
2511
|
this.resolveClosed = undefined;
|
|
2417
|
-
resolveClosed?.({ reason });
|
|
2512
|
+
resolveClosed?.({ reason: closeReason });
|
|
2418
2513
|
}
|
|
2419
2514
|
|
|
2420
2515
|
private abortInFlightFetches(): void {
|
|
@@ -2469,7 +2564,9 @@ export class HttpTransport implements McpTransport {
|
|
|
2469
2564
|
await this.throwForPostHttpError(response);
|
|
2470
2565
|
this.captureSessionId(response);
|
|
2471
2566
|
this.maybeOpenGetSseStream();
|
|
2472
|
-
|
|
2567
|
+
void this.forwardResponseMessages(response).catch((error) => {
|
|
2568
|
+
this.dispose(error instanceof Error ? error : new Error(String(error)));
|
|
2569
|
+
});
|
|
2473
2570
|
}
|
|
2474
2571
|
}
|
|
2475
2572
|
|
|
@@ -2479,6 +2576,7 @@ export class HttpTransport implements McpTransport {
|
|
|
2479
2576
|
headers.set("Content-Type", "application/json");
|
|
2480
2577
|
if (this.sessionId !== undefined) {
|
|
2481
2578
|
headers.set("Mcp-Session-Id", this.sessionId);
|
|
2579
|
+
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
2482
2580
|
}
|
|
2483
2581
|
return this.authorizeRequestHeaders(headers);
|
|
2484
2582
|
}
|
|
@@ -2488,6 +2586,7 @@ export class HttpTransport implements McpTransport {
|
|
|
2488
2586
|
headers.set("Accept", "text/event-stream");
|
|
2489
2587
|
if (this.sessionId !== undefined) {
|
|
2490
2588
|
headers.set("Mcp-Session-Id", this.sessionId);
|
|
2589
|
+
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
2491
2590
|
}
|
|
2492
2591
|
if (this.lastEventId !== undefined) {
|
|
2493
2592
|
headers.set("Last-Event-ID", this.lastEventId);
|
|
@@ -2498,6 +2597,7 @@ export class HttpTransport implements McpTransport {
|
|
|
2498
2597
|
private async createDeleteHeaders(sessionId: string): Promise<Headers> {
|
|
2499
2598
|
const headers = new Headers(this.headers);
|
|
2500
2599
|
headers.set("Mcp-Session-Id", sessionId);
|
|
2600
|
+
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
2501
2601
|
return this.authorizeRequestHeaders(headers);
|
|
2502
2602
|
}
|
|
2503
2603
|
|
|
@@ -2516,6 +2616,10 @@ export class HttpTransport implements McpTransport {
|
|
|
2516
2616
|
return;
|
|
2517
2617
|
}
|
|
2518
2618
|
|
|
2619
|
+
if (this.sessionId !== undefined && this.sessionId !== sessionId) {
|
|
2620
|
+
throw new Error("HTTP transport response changed active session ID");
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2519
2623
|
this.sessionId = sessionId;
|
|
2520
2624
|
}
|
|
2521
2625
|
|
|
@@ -2534,26 +2638,22 @@ export class HttpTransport implements McpTransport {
|
|
|
2534
2638
|
});
|
|
2535
2639
|
}
|
|
2536
2640
|
|
|
2537
|
-
private terminateSession(): void {
|
|
2538
|
-
if (this.sessionId === undefined) {
|
|
2539
|
-
return;
|
|
2540
|
-
}
|
|
2541
|
-
|
|
2542
|
-
const sessionId = this.sessionId;
|
|
2543
|
-
this.sessionId = undefined;
|
|
2544
|
-
|
|
2545
|
-
this.sendSessionTerminationRequest(sessionId).catch(() => undefined);
|
|
2546
|
-
}
|
|
2547
|
-
|
|
2548
2641
|
private async sendSessionTerminationRequest(sessionId: string): Promise<void> {
|
|
2549
2642
|
const response = await this.fetchImpl(this.url, {
|
|
2550
2643
|
method: "DELETE",
|
|
2551
2644
|
headers: await this.createDeleteHeaders(sessionId),
|
|
2552
2645
|
});
|
|
2553
2646
|
|
|
2554
|
-
if (response.status === 405) {
|
|
2647
|
+
if (response.status === 405 || response.ok) {
|
|
2555
2648
|
return;
|
|
2556
2649
|
}
|
|
2650
|
+
|
|
2651
|
+
const responseBody = (await response.text()).trim();
|
|
2652
|
+
const statusDescriptor = `${response.status} ${response.statusText}`.trim();
|
|
2653
|
+
const message = responseBody.length === 0
|
|
2654
|
+
? `HTTP transport DELETE failed (${statusDescriptor})`
|
|
2655
|
+
: `HTTP transport DELETE failed (${statusDescriptor}): ${responseBody}`;
|
|
2656
|
+
throw new Error(message);
|
|
2557
2657
|
}
|
|
2558
2658
|
|
|
2559
2659
|
private async consumeGetSseStream(): Promise<void> {
|
|
@@ -2566,6 +2666,20 @@ export class HttpTransport implements McpTransport {
|
|
|
2566
2666
|
throw new HttpTransportGetSseNotSupportedError();
|
|
2567
2667
|
}
|
|
2568
2668
|
|
|
2669
|
+
if (response.status === 404) {
|
|
2670
|
+
this.sessionId = undefined;
|
|
2671
|
+
throw new Error("HTTP transport session expired (GET 404 response)");
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
if (!response.ok) {
|
|
2675
|
+
const responseBody = (await response.text()).trim();
|
|
2676
|
+
const statusDescriptor = `${response.status} ${response.statusText}`.trim();
|
|
2677
|
+
const message = responseBody.length === 0
|
|
2678
|
+
? `HTTP transport GET failed (${statusDescriptor})`
|
|
2679
|
+
: `HTTP transport GET failed (${statusDescriptor}): ${responseBody}`;
|
|
2680
|
+
throw new Error(message);
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2569
2683
|
const contentType = response.headers.get("Content-Type");
|
|
2570
2684
|
if (contentType === null) {
|
|
2571
2685
|
return;
|
|
@@ -2577,7 +2691,10 @@ export class HttpTransport implements McpTransport {
|
|
|
2577
2691
|
if (!this.disposed && this.sessionId !== undefined && this.lastEventId !== undefined) {
|
|
2578
2692
|
this.maybeOpenGetSseStream();
|
|
2579
2693
|
}
|
|
2694
|
+
return;
|
|
2580
2695
|
}
|
|
2696
|
+
|
|
2697
|
+
return;
|
|
2581
2698
|
}
|
|
2582
2699
|
|
|
2583
2700
|
private async throwForPostHttpError(response: Response): Promise<void> {
|
|
@@ -2645,7 +2762,10 @@ export class HttpTransport implements McpTransport {
|
|
|
2645
2762
|
|
|
2646
2763
|
if (normalizedContentType.includes("application/json")) {
|
|
2647
2764
|
await this.forwardJsonResponseMessage(response);
|
|
2765
|
+
return;
|
|
2648
2766
|
}
|
|
2767
|
+
|
|
2768
|
+
throw new Error("HTTP transport POST returned an unsupported response content type");
|
|
2649
2769
|
}
|
|
2650
2770
|
|
|
2651
2771
|
private async forwardSseResponseMessages(response: Response): Promise<void> {
|
|
@@ -2856,9 +2976,13 @@ function normalizeLine(line: string): string {
|
|
|
2856
2976
|
|
|
2857
2977
|
export async function* readLines(stream: Readable): AsyncGenerator<string> {
|
|
2858
2978
|
let buffer = "";
|
|
2979
|
+
const decoder = new TextDecoder();
|
|
2859
2980
|
|
|
2860
2981
|
for await (const chunk of stream as AsyncIterable<unknown>) {
|
|
2861
|
-
buffer +=
|
|
2982
|
+
buffer +=
|
|
2983
|
+
chunk instanceof Uint8Array
|
|
2984
|
+
? decoder.decode(chunk, { stream: true })
|
|
2985
|
+
: decoder.decode() + String(chunk);
|
|
2862
2986
|
|
|
2863
2987
|
while (true) {
|
|
2864
2988
|
const newlineIndex = buffer.indexOf("\n");
|
|
@@ -2872,6 +2996,8 @@ export async function* readLines(stream: Readable): AsyncGenerator<string> {
|
|
|
2872
2996
|
}
|
|
2873
2997
|
}
|
|
2874
2998
|
|
|
2999
|
+
buffer += decoder.decode();
|
|
3000
|
+
|
|
2875
3001
|
if (buffer.length > 0) {
|
|
2876
3002
|
yield normalizeLine(buffer);
|
|
2877
3003
|
}
|
|
@@ -3383,6 +3509,82 @@ function isObjectRecord(value: unknown): value is Record<string, unknown> {
|
|
|
3383
3509
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3384
3510
|
}
|
|
3385
3511
|
|
|
3512
|
+
function isInitializeResult(value: unknown): value is InitializeResult {
|
|
3513
|
+
if (!isObjectRecord(value) || typeof value.protocolVersion !== "string") {
|
|
3514
|
+
return false;
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
if (!isServerCapabilities(value.capabilities)) {
|
|
3518
|
+
return false;
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
if (
|
|
3522
|
+
!isObjectRecord(value.serverInfo)
|
|
3523
|
+
|| typeof value.serverInfo.name !== "string"
|
|
3524
|
+
|| value.serverInfo.name.length === 0
|
|
3525
|
+
|| typeof value.serverInfo.version !== "string"
|
|
3526
|
+
|| value.serverInfo.version.length === 0
|
|
3527
|
+
) {
|
|
3528
|
+
return false;
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
return value.instructions === undefined || typeof value.instructions === "string";
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
function isServerCapabilities(value: unknown): value is ServerCapabilities {
|
|
3535
|
+
if (!isObjectRecord(value)) {
|
|
3536
|
+
return false;
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
for (const capability of ["prompts", "resources", "tools", "logging", "completions", "experimental"] as const) {
|
|
3540
|
+
if (value[capability] !== undefined && !isObjectRecord(value[capability])) {
|
|
3541
|
+
return false;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
|
|
3545
|
+
return true;
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
function isCallToolResult(value: unknown): value is CallToolResult {
|
|
3549
|
+
if (!isObjectRecord(value) || !Array.isArray(value.content)) {
|
|
3550
|
+
return false;
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
if (value.isError !== undefined && typeof value.isError !== "boolean") {
|
|
3554
|
+
return false;
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
return value.content.every(isContentItem);
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3560
|
+
function isToolsListResult(value: unknown): value is { tools: Tool[]; nextCursor?: string } {
|
|
3561
|
+
return isObjectRecord(value)
|
|
3562
|
+
&& Array.isArray(value.tools)
|
|
3563
|
+
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
function isContentItem(value: unknown): value is ContentItem {
|
|
3567
|
+
if (!isObjectRecord(value)) {
|
|
3568
|
+
return false;
|
|
3569
|
+
}
|
|
3570
|
+
|
|
3571
|
+
if (value.type === "text") {
|
|
3572
|
+
return typeof value.text === "string";
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
if (value.type === "image" || value.type === "audio") {
|
|
3576
|
+
return typeof value.data === "string" && typeof value.mimeType === "string";
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3579
|
+
if (value.type !== "resource" || !isObjectRecord(value.resource)) {
|
|
3580
|
+
return false;
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
return typeof value.resource.uri === "string"
|
|
3584
|
+
&& (value.resource.mimeType === undefined || typeof value.resource.mimeType === "string")
|
|
3585
|
+
&& (typeof value.resource.text === "string" || typeof value.resource.blob === "string");
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3386
3588
|
function hasOwn(
|
|
3387
3589
|
value: Record<string, unknown>,
|
|
3388
3590
|
property: string
|
|
@@ -17,7 +17,7 @@ describe("McpClient integration tools with tiny-stdio-mcp-test-server", () => {
|
|
|
17
17
|
try {
|
|
18
18
|
expect(client.serverInfo).toEqual({
|
|
19
19
|
name: "tiny-stdio-mcp-test-server",
|
|
20
|
-
version: "0.0
|
|
20
|
+
version: "0.1.0",
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
const { tools } = await client.listTools();
|
|
@@ -4,8 +4,8 @@ import type {
|
|
|
4
4
|
OAuthMetadataFetch,
|
|
5
5
|
OAuthProtectedResourceMetadata,
|
|
6
6
|
OAuthUnauthorizedChallenge,
|
|
7
|
-
} from "
|
|
8
|
-
import { canonicalizeResourceIndicator } from "
|
|
7
|
+
} from "mcp-oauth";
|
|
8
|
+
import { canonicalizeResourceIndicator } from "mcp-oauth";
|
|
9
9
|
|
|
10
10
|
export type {
|
|
11
11
|
OAuthAuthorizationServerMetadata,
|
|
@@ -237,11 +237,7 @@ export class OAuthMetadataDiscovery {
|
|
|
237
237
|
resourceMetadataUrl
|
|
238
238
|
);
|
|
239
239
|
const memoryCachedResult = this.memoryCache.get(cacheKey);
|
|
240
|
-
if (
|
|
241
|
-
memoryCachedResult !== undefined &&
|
|
242
|
-
(resourceMetadataUrl === undefined
|
|
243
|
-
|| memoryCachedResult.resourceMetadataUrl === resourceMetadataLocation)
|
|
244
|
-
) {
|
|
240
|
+
if (memoryCachedResult !== undefined && resourceMetadataUrl === undefined) {
|
|
245
241
|
return memoryCachedResult;
|
|
246
242
|
}
|
|
247
243
|
|
|
@@ -249,8 +245,7 @@ export class OAuthMetadataDiscovery {
|
|
|
249
245
|
if (
|
|
250
246
|
sharedCachedResult !== null &&
|
|
251
247
|
sharedCachedResult !== undefined &&
|
|
252
|
-
|
|
253
|
-
|| sharedCachedResult.resourceMetadataUrl === resourceMetadataLocation)
|
|
248
|
+
resourceMetadataUrl === undefined
|
|
254
249
|
) {
|
|
255
250
|
this.memoryCache.set(cacheKey, sharedCachedResult);
|
|
256
251
|
return sharedCachedResult;
|
|
@@ -530,7 +525,7 @@ export function parseBearerWwwAuthenticateHeader(
|
|
|
530
525
|
}
|
|
531
526
|
|
|
532
527
|
index = skipOptionalWhitespace(headerValue, scheme.nextIndex);
|
|
533
|
-
const params: Record<string, string> =
|
|
528
|
+
const params: Record<string, string> = Object.create(null) as Record<string, string>;
|
|
534
529
|
|
|
535
530
|
if (index < headerValue.length && headerValue[index] !== ",") {
|
|
536
531
|
const token68 = readToken68(headerValue, index);
|