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
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { PassThrough } from "node:stream";
|
|
3
|
-
import { createOAuthClientProvider, OAuthError, } from "
|
|
3
|
+
import { createOAuthClientProvider, OAuthError, } from "mcp-oauth";
|
|
4
4
|
import { OAuthMetadataDiscovery, parseBearerWwwAuthenticateHeader, } from "./oauth-discovery.js";
|
|
5
5
|
export { OAuthMetadataDiscovery, discoverOAuthMetadata, parseBearerWwwAuthenticateHeader, resolveAuthorizationServerMetadataUrl, resolveProtectedResourceMetadataUrl, } from "./oauth-discovery.js";
|
|
6
|
-
export { createAuthStoreSessionStore, createDefaultOAuthClientProvider, } from "
|
|
6
|
+
export { createAuthStoreSessionStore, createDefaultOAuthClientProvider, } from "mcp-oauth";
|
|
7
7
|
const MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
8
8
|
export class McpClient {
|
|
9
9
|
currentState = "disconnected";
|
|
10
10
|
currentServerCapabilities = null;
|
|
11
|
+
currentClientCapabilities = null;
|
|
11
12
|
currentServerInfo = null;
|
|
12
13
|
currentInstructions;
|
|
14
|
+
subscribedResourceUris = new Set();
|
|
15
|
+
activeProgressTokens = new Map();
|
|
13
16
|
options;
|
|
14
17
|
transport = null;
|
|
15
18
|
messageLayer = null;
|
|
@@ -20,7 +23,9 @@ export class McpClient {
|
|
|
20
23
|
return this.currentState;
|
|
21
24
|
}
|
|
22
25
|
get serverCapabilities() {
|
|
23
|
-
return this.currentServerCapabilities
|
|
26
|
+
return this.currentServerCapabilities === null
|
|
27
|
+
? null
|
|
28
|
+
: structuredClone(this.currentServerCapabilities);
|
|
24
29
|
}
|
|
25
30
|
get serverInfo() {
|
|
26
31
|
return this.currentServerInfo;
|
|
@@ -44,22 +49,23 @@ export class McpClient {
|
|
|
44
49
|
if (this.currentState !== "disconnected" && this.currentState !== "closed") {
|
|
45
50
|
throw new Error("MCP client is already connected");
|
|
46
51
|
}
|
|
52
|
+
this.currentServerCapabilities = null;
|
|
53
|
+
this.currentClientCapabilities = null;
|
|
54
|
+
this.currentServerInfo = null;
|
|
55
|
+
this.currentInstructions = undefined;
|
|
56
|
+
this.subscribedResourceUris.clear();
|
|
57
|
+
this.activeProgressTokens.clear();
|
|
47
58
|
const transportClosedReason = transport.closed
|
|
48
59
|
.then((closedEvent) => closedEvent.reason)
|
|
49
60
|
.catch((error) => error instanceof Error ? error : new Error(String(error)));
|
|
50
|
-
const messageLayer = new JsonRpcMessageLayer(transport.readable, transport.writable,
|
|
61
|
+
const messageLayer = new JsonRpcMessageLayer(transport.readable, transport.writable, this.options.requestTimeoutMs, transportClosedReason);
|
|
51
62
|
const { onSamplingRequest, onRootsList, onToolsChanged, onResourcesChanged, onResourceUpdated, onPromptsChanged, onLog, onProgress, } = this.options;
|
|
52
63
|
messageLayer.onRequest("ping", () => ({}));
|
|
53
64
|
if (onSamplingRequest !== undefined) {
|
|
54
65
|
messageLayer.onRequest("sampling/createMessage", (params) => onSamplingRequest(params));
|
|
55
66
|
}
|
|
56
|
-
if (onRootsList !== undefined) {
|
|
57
|
-
messageLayer.onRequest("roots/list", async () => ({
|
|
58
|
-
roots: await onRootsList(),
|
|
59
|
-
}));
|
|
60
|
-
}
|
|
61
67
|
messageLayer.onNotification("notifications/tools/list_changed", async () => {
|
|
62
|
-
if (onToolsChanged === undefined) {
|
|
68
|
+
if (onToolsChanged === undefined || this.currentServerCapabilities?.tools?.listChanged !== true) {
|
|
63
69
|
return;
|
|
64
70
|
}
|
|
65
71
|
await onToolsChanged();
|
|
@@ -78,7 +84,7 @@ export class McpClient {
|
|
|
78
84
|
return;
|
|
79
85
|
}
|
|
80
86
|
const { uri } = params;
|
|
81
|
-
if (typeof uri !== "string") {
|
|
87
|
+
if (typeof uri !== "string" || !this.subscribedResourceUris.has(uri)) {
|
|
82
88
|
return;
|
|
83
89
|
}
|
|
84
90
|
await onResourceUpdated(uri);
|
|
@@ -113,7 +119,9 @@ export class McpClient {
|
|
|
113
119
|
return;
|
|
114
120
|
}
|
|
115
121
|
const { progressToken, progress } = params;
|
|
116
|
-
if (!isRequestId(progressToken) ||
|
|
122
|
+
if (!isRequestId(progressToken) ||
|
|
123
|
+
typeof progress !== "number" ||
|
|
124
|
+
!this.activeProgressTokens.has(progressToken)) {
|
|
117
125
|
return;
|
|
118
126
|
}
|
|
119
127
|
const progressParams = {
|
|
@@ -138,6 +146,8 @@ export class McpClient {
|
|
|
138
146
|
this.transport = transport;
|
|
139
147
|
this.messageLayer = messageLayer;
|
|
140
148
|
this.currentState = "initializing";
|
|
149
|
+
this.subscribedResourceUris.clear();
|
|
150
|
+
this.activeProgressTokens.clear();
|
|
141
151
|
transport.closed
|
|
142
152
|
.then((closedEvent) => {
|
|
143
153
|
if (this.transport !== transport) {
|
|
@@ -169,20 +179,43 @@ export class McpClient {
|
|
|
169
179
|
...(capabilities.roots ?? {}),
|
|
170
180
|
};
|
|
171
181
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
this.currentClientCapabilities = structuredClone(capabilities);
|
|
183
|
+
try {
|
|
184
|
+
const initializeResultValue = await messageLayer.sendRequest("initialize", {
|
|
185
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
186
|
+
clientInfo: this.options.clientInfo,
|
|
187
|
+
capabilities,
|
|
188
|
+
});
|
|
189
|
+
if (!isInitializeResult(initializeResultValue)) {
|
|
190
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid initialize result");
|
|
191
|
+
}
|
|
192
|
+
const initializeResult = initializeResultValue;
|
|
193
|
+
if (initializeResult.protocolVersion !== MCP_PROTOCOL_VERSION) {
|
|
194
|
+
throw new McpError(ERROR_INVALID_REQUEST, `Unsupported protocol version: ${initializeResult.protocolVersion}`);
|
|
195
|
+
}
|
|
196
|
+
this.currentServerCapabilities = structuredClone(initializeResult.capabilities);
|
|
197
|
+
this.currentServerInfo = { ...initializeResult.serverInfo };
|
|
198
|
+
this.currentInstructions = initializeResult.instructions;
|
|
199
|
+
if (onRootsList !== undefined) {
|
|
200
|
+
messageLayer.onRequest("roots/list", async () => ({
|
|
201
|
+
roots: await onRootsList(),
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
messageLayer.sendNotification("notifications/initialized");
|
|
205
|
+
this.currentState = "ready";
|
|
206
|
+
return initializeResult;
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
if (this.transport === transport) {
|
|
210
|
+
const reason = error instanceof Error ? error : new Error(String(error));
|
|
211
|
+
messageLayer.dispose(reason);
|
|
212
|
+
transport.dispose(reason);
|
|
213
|
+
this.messageLayer = null;
|
|
214
|
+
this.transport = null;
|
|
215
|
+
this.currentState = "disconnected";
|
|
216
|
+
}
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
186
219
|
}
|
|
187
220
|
getServerCapabilitiesOrThrow() {
|
|
188
221
|
if (this.currentServerCapabilities === null) {
|
|
@@ -197,7 +230,11 @@ export class McpClient {
|
|
|
197
230
|
throw new Error("Server does not support tools");
|
|
198
231
|
}
|
|
199
232
|
const requestParams = params.cursor === undefined ? undefined : { cursor: params.cursor };
|
|
200
|
-
|
|
233
|
+
const result = await messageLayer.sendRequest("tools/list", requestParams);
|
|
234
|
+
if (!isToolsListResult(result)) {
|
|
235
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid tools/list result");
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
201
238
|
}
|
|
202
239
|
async callTool(params, options = {}) {
|
|
203
240
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -205,6 +242,9 @@ export class McpClient {
|
|
|
205
242
|
if (serverCapabilities.tools === undefined) {
|
|
206
243
|
throw new Error("Server does not support tools");
|
|
207
244
|
}
|
|
245
|
+
if (options.signal?.aborted) {
|
|
246
|
+
throw options.signal.reason;
|
|
247
|
+
}
|
|
208
248
|
const requestParams = options.progressToken === undefined
|
|
209
249
|
? params
|
|
210
250
|
: {
|
|
@@ -213,37 +253,58 @@ export class McpClient {
|
|
|
213
253
|
progressToken: options.progressToken,
|
|
214
254
|
},
|
|
215
255
|
};
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
256
|
+
if (options.progressToken !== undefined) {
|
|
257
|
+
this.activeProgressTokens.set(options.progressToken, (this.activeProgressTokens.get(options.progressToken) ?? 0) + 1);
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
let requestId;
|
|
261
|
+
const requestPromise = messageLayer.sendRequest("tools/call", requestParams, {
|
|
262
|
+
onRequestId: (nextRequestId) => {
|
|
263
|
+
requestId = nextRequestId;
|
|
264
|
+
},
|
|
265
|
+
}).then((result) => {
|
|
266
|
+
if (!isCallToolResult(result)) {
|
|
267
|
+
throw new McpError(ERROR_INVALID_REQUEST, "Invalid tool result");
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
});
|
|
271
|
+
if (options.signal === undefined) {
|
|
272
|
+
return await requestPromise;
|
|
273
|
+
}
|
|
274
|
+
const signal = options.signal;
|
|
275
|
+
let abortListener;
|
|
276
|
+
const abortPromise = new Promise((_, reject) => {
|
|
277
|
+
const rejectWithAbortReason = () => {
|
|
278
|
+
if (requestId !== undefined) {
|
|
279
|
+
messageLayer.sendNotification("notifications/cancelled", { requestId });
|
|
280
|
+
}
|
|
281
|
+
reject(signal.reason);
|
|
282
|
+
};
|
|
283
|
+
abortListener = rejectWithAbortReason;
|
|
284
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
285
|
+
if (signal.aborted) {
|
|
286
|
+
signal.removeEventListener("abort", abortListener);
|
|
287
|
+
rejectWithAbortReason();
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
try {
|
|
291
|
+
return (await Promise.race([requestPromise, abortPromise]));
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
if (abortListener !== undefined) {
|
|
295
|
+
signal.removeEventListener("abort", abortListener);
|
|
231
296
|
}
|
|
232
|
-
reject(signal.reason);
|
|
233
|
-
};
|
|
234
|
-
abortListener = rejectWithAbortReason;
|
|
235
|
-
signal.addEventListener("abort", abortListener, { once: true });
|
|
236
|
-
if (signal.aborted) {
|
|
237
|
-
signal.removeEventListener("abort", abortListener);
|
|
238
|
-
rejectWithAbortReason();
|
|
239
297
|
}
|
|
240
|
-
});
|
|
241
|
-
try {
|
|
242
|
-
return (await Promise.race([requestPromise, abortPromise]));
|
|
243
298
|
}
|
|
244
299
|
finally {
|
|
245
|
-
if (
|
|
246
|
-
|
|
300
|
+
if (options.progressToken !== undefined) {
|
|
301
|
+
const activeCount = this.activeProgressTokens.get(options.progressToken);
|
|
302
|
+
if (activeCount === 1) {
|
|
303
|
+
this.activeProgressTokens.delete(options.progressToken);
|
|
304
|
+
}
|
|
305
|
+
else if (activeCount !== undefined) {
|
|
306
|
+
this.activeProgressTokens.set(options.progressToken, activeCount - 1);
|
|
307
|
+
}
|
|
247
308
|
}
|
|
248
309
|
}
|
|
249
310
|
}
|
|
@@ -280,6 +341,7 @@ export class McpClient {
|
|
|
280
341
|
throw new Error("Server does not support resource subscriptions");
|
|
281
342
|
}
|
|
282
343
|
await messageLayer.sendRequest("resources/subscribe", { uri });
|
|
344
|
+
this.subscribedResourceUris.add(uri);
|
|
283
345
|
}
|
|
284
346
|
async unsubscribe(uri) {
|
|
285
347
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -288,6 +350,7 @@ export class McpClient {
|
|
|
288
350
|
throw new Error("Server does not support resource subscriptions");
|
|
289
351
|
}
|
|
290
352
|
await messageLayer.sendRequest("resources/unsubscribe", { uri });
|
|
353
|
+
this.subscribedResourceUris.delete(uri);
|
|
291
354
|
}
|
|
292
355
|
async listPrompts(params = {}) {
|
|
293
356
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
@@ -332,6 +395,9 @@ export class McpClient {
|
|
|
332
395
|
}
|
|
333
396
|
async sendRootsChanged() {
|
|
334
397
|
const messageLayer = this.getMessageLayerOrThrow();
|
|
398
|
+
if (this.currentClientCapabilities?.roots?.listChanged !== true) {
|
|
399
|
+
throw new Error("Client did not advertise roots list changes");
|
|
400
|
+
}
|
|
335
401
|
messageLayer.sendNotification("notifications/roots/list_changed");
|
|
336
402
|
}
|
|
337
403
|
async ping() {
|
|
@@ -347,6 +413,12 @@ export class McpClient {
|
|
|
347
413
|
this.transport?.dispose(closeError);
|
|
348
414
|
this.messageLayer = null;
|
|
349
415
|
this.transport = null;
|
|
416
|
+
this.currentServerCapabilities = null;
|
|
417
|
+
this.currentClientCapabilities = null;
|
|
418
|
+
this.currentServerInfo = null;
|
|
419
|
+
this.currentInstructions = undefined;
|
|
420
|
+
this.subscribedResourceUris.clear();
|
|
421
|
+
this.activeProgressTokens.clear();
|
|
350
422
|
this.currentState = "closed";
|
|
351
423
|
}
|
|
352
424
|
}
|
|
@@ -1543,16 +1615,29 @@ export class HttpTransport {
|
|
|
1543
1615
|
this.disposed = true;
|
|
1544
1616
|
this.abortInFlightFetches();
|
|
1545
1617
|
this.cancelOpenSseReaders();
|
|
1546
|
-
this.terminateSession();
|
|
1547
1618
|
if (!this.writeStream.destroyed && !this.writeStream.writableEnded) {
|
|
1548
1619
|
this.writeStream.end();
|
|
1549
1620
|
}
|
|
1550
1621
|
if (!this.readStream.destroyed && !this.readStream.writableEnded) {
|
|
1551
1622
|
this.readStream.end();
|
|
1552
1623
|
}
|
|
1624
|
+
void this.closeWithSessionTermination(reason);
|
|
1625
|
+
}
|
|
1626
|
+
async closeWithSessionTermination(reason) {
|
|
1627
|
+
let closeReason = reason;
|
|
1628
|
+
if (this.sessionId !== undefined) {
|
|
1629
|
+
const sessionId = this.sessionId;
|
|
1630
|
+
this.sessionId = undefined;
|
|
1631
|
+
try {
|
|
1632
|
+
await this.sendSessionTerminationRequest(sessionId);
|
|
1633
|
+
}
|
|
1634
|
+
catch (error) {
|
|
1635
|
+
closeReason = error instanceof Error ? error : new Error(String(error));
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1553
1638
|
const resolveClosed = this.resolveClosed;
|
|
1554
1639
|
this.resolveClosed = undefined;
|
|
1555
|
-
resolveClosed?.({ reason });
|
|
1640
|
+
resolveClosed?.({ reason: closeReason });
|
|
1556
1641
|
}
|
|
1557
1642
|
abortInFlightFetches() {
|
|
1558
1643
|
for (const abortController of this.inFlightFetchAbortControllers) {
|
|
@@ -1598,7 +1683,9 @@ export class HttpTransport {
|
|
|
1598
1683
|
await this.throwForPostHttpError(response);
|
|
1599
1684
|
this.captureSessionId(response);
|
|
1600
1685
|
this.maybeOpenGetSseStream();
|
|
1601
|
-
|
|
1686
|
+
void this.forwardResponseMessages(response).catch((error) => {
|
|
1687
|
+
this.dispose(error instanceof Error ? error : new Error(String(error)));
|
|
1688
|
+
});
|
|
1602
1689
|
}
|
|
1603
1690
|
}
|
|
1604
1691
|
async createPostHeaders() {
|
|
@@ -1607,6 +1694,7 @@ export class HttpTransport {
|
|
|
1607
1694
|
headers.set("Content-Type", "application/json");
|
|
1608
1695
|
if (this.sessionId !== undefined) {
|
|
1609
1696
|
headers.set("Mcp-Session-Id", this.sessionId);
|
|
1697
|
+
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
1610
1698
|
}
|
|
1611
1699
|
return this.authorizeRequestHeaders(headers);
|
|
1612
1700
|
}
|
|
@@ -1615,6 +1703,7 @@ export class HttpTransport {
|
|
|
1615
1703
|
headers.set("Accept", "text/event-stream");
|
|
1616
1704
|
if (this.sessionId !== undefined) {
|
|
1617
1705
|
headers.set("Mcp-Session-Id", this.sessionId);
|
|
1706
|
+
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
1618
1707
|
}
|
|
1619
1708
|
if (this.lastEventId !== undefined) {
|
|
1620
1709
|
headers.set("Last-Event-ID", this.lastEventId);
|
|
@@ -1624,6 +1713,7 @@ export class HttpTransport {
|
|
|
1624
1713
|
async createDeleteHeaders(sessionId) {
|
|
1625
1714
|
const headers = new Headers(this.headers);
|
|
1626
1715
|
headers.set("Mcp-Session-Id", sessionId);
|
|
1716
|
+
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
1627
1717
|
return this.authorizeRequestHeaders(headers);
|
|
1628
1718
|
}
|
|
1629
1719
|
async authorizeRequestHeaders(headers) {
|
|
@@ -1639,6 +1729,9 @@ export class HttpTransport {
|
|
|
1639
1729
|
if (sessionId === null || sessionId.length === 0) {
|
|
1640
1730
|
return;
|
|
1641
1731
|
}
|
|
1732
|
+
if (this.sessionId !== undefined && this.sessionId !== sessionId) {
|
|
1733
|
+
throw new Error("HTTP transport response changed active session ID");
|
|
1734
|
+
}
|
|
1642
1735
|
this.sessionId = sessionId;
|
|
1643
1736
|
}
|
|
1644
1737
|
maybeOpenGetSseStream() {
|
|
@@ -1653,22 +1746,20 @@ export class HttpTransport {
|
|
|
1653
1746
|
this.dispose(error instanceof Error ? error : new Error(String(error)));
|
|
1654
1747
|
});
|
|
1655
1748
|
}
|
|
1656
|
-
terminateSession() {
|
|
1657
|
-
if (this.sessionId === undefined) {
|
|
1658
|
-
return;
|
|
1659
|
-
}
|
|
1660
|
-
const sessionId = this.sessionId;
|
|
1661
|
-
this.sessionId = undefined;
|
|
1662
|
-
this.sendSessionTerminationRequest(sessionId).catch(() => undefined);
|
|
1663
|
-
}
|
|
1664
1749
|
async sendSessionTerminationRequest(sessionId) {
|
|
1665
1750
|
const response = await this.fetchImpl(this.url, {
|
|
1666
1751
|
method: "DELETE",
|
|
1667
1752
|
headers: await this.createDeleteHeaders(sessionId),
|
|
1668
1753
|
});
|
|
1669
|
-
if (response.status === 405) {
|
|
1754
|
+
if (response.status === 405 || response.ok) {
|
|
1670
1755
|
return;
|
|
1671
1756
|
}
|
|
1757
|
+
const responseBody = (await response.text()).trim();
|
|
1758
|
+
const statusDescriptor = `${response.status} ${response.statusText}`.trim();
|
|
1759
|
+
const message = responseBody.length === 0
|
|
1760
|
+
? `HTTP transport DELETE failed (${statusDescriptor})`
|
|
1761
|
+
: `HTTP transport DELETE failed (${statusDescriptor}): ${responseBody}`;
|
|
1762
|
+
throw new Error(message);
|
|
1672
1763
|
}
|
|
1673
1764
|
async consumeGetSseStream() {
|
|
1674
1765
|
const response = await this.fetchWithOAuthRetry({
|
|
@@ -1678,6 +1769,18 @@ export class HttpTransport {
|
|
|
1678
1769
|
if (response.status === 405) {
|
|
1679
1770
|
throw new HttpTransportGetSseNotSupportedError();
|
|
1680
1771
|
}
|
|
1772
|
+
if (response.status === 404) {
|
|
1773
|
+
this.sessionId = undefined;
|
|
1774
|
+
throw new Error("HTTP transport session expired (GET 404 response)");
|
|
1775
|
+
}
|
|
1776
|
+
if (!response.ok) {
|
|
1777
|
+
const responseBody = (await response.text()).trim();
|
|
1778
|
+
const statusDescriptor = `${response.status} ${response.statusText}`.trim();
|
|
1779
|
+
const message = responseBody.length === 0
|
|
1780
|
+
? `HTTP transport GET failed (${statusDescriptor})`
|
|
1781
|
+
: `HTTP transport GET failed (${statusDescriptor}): ${responseBody}`;
|
|
1782
|
+
throw new Error(message);
|
|
1783
|
+
}
|
|
1681
1784
|
const contentType = response.headers.get("Content-Type");
|
|
1682
1785
|
if (contentType === null) {
|
|
1683
1786
|
return;
|
|
@@ -1688,7 +1791,9 @@ export class HttpTransport {
|
|
|
1688
1791
|
if (!this.disposed && this.sessionId !== undefined && this.lastEventId !== undefined) {
|
|
1689
1792
|
this.maybeOpenGetSseStream();
|
|
1690
1793
|
}
|
|
1794
|
+
return;
|
|
1691
1795
|
}
|
|
1796
|
+
return;
|
|
1692
1797
|
}
|
|
1693
1798
|
async throwForPostHttpError(response) {
|
|
1694
1799
|
if (response.status < 400) {
|
|
@@ -1744,7 +1849,9 @@ export class HttpTransport {
|
|
|
1744
1849
|
}
|
|
1745
1850
|
if (normalizedContentType.includes("application/json")) {
|
|
1746
1851
|
await this.forwardJsonResponseMessage(response);
|
|
1852
|
+
return;
|
|
1747
1853
|
}
|
|
1854
|
+
throw new Error("HTTP transport POST returned an unsupported response content type");
|
|
1748
1855
|
}
|
|
1749
1856
|
async forwardSseResponseMessages(response) {
|
|
1750
1857
|
if (response.body === null) {
|
|
@@ -1866,8 +1973,12 @@ function normalizeLine(line) {
|
|
|
1866
1973
|
}
|
|
1867
1974
|
export async function* readLines(stream) {
|
|
1868
1975
|
let buffer = "";
|
|
1976
|
+
const decoder = new TextDecoder();
|
|
1869
1977
|
for await (const chunk of stream) {
|
|
1870
|
-
buffer +=
|
|
1978
|
+
buffer +=
|
|
1979
|
+
chunk instanceof Uint8Array
|
|
1980
|
+
? decoder.decode(chunk, { stream: true })
|
|
1981
|
+
: decoder.decode() + String(chunk);
|
|
1871
1982
|
while (true) {
|
|
1872
1983
|
const newlineIndex = buffer.indexOf("\n");
|
|
1873
1984
|
if (newlineIndex === -1) {
|
|
@@ -1878,6 +1989,7 @@ export async function* readLines(stream) {
|
|
|
1878
1989
|
yield normalizeLine(line);
|
|
1879
1990
|
}
|
|
1880
1991
|
}
|
|
1992
|
+
buffer += decoder.decode();
|
|
1881
1993
|
if (buffer.length > 0) {
|
|
1882
1994
|
yield normalizeLine(buffer);
|
|
1883
1995
|
}
|
|
@@ -2253,6 +2365,64 @@ export class JsonRpcMessageLayer {
|
|
|
2253
2365
|
function isObjectRecord(value) {
|
|
2254
2366
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2255
2367
|
}
|
|
2368
|
+
function isInitializeResult(value) {
|
|
2369
|
+
if (!isObjectRecord(value) || typeof value.protocolVersion !== "string") {
|
|
2370
|
+
return false;
|
|
2371
|
+
}
|
|
2372
|
+
if (!isServerCapabilities(value.capabilities)) {
|
|
2373
|
+
return false;
|
|
2374
|
+
}
|
|
2375
|
+
if (!isObjectRecord(value.serverInfo)
|
|
2376
|
+
|| typeof value.serverInfo.name !== "string"
|
|
2377
|
+
|| value.serverInfo.name.length === 0
|
|
2378
|
+
|| typeof value.serverInfo.version !== "string"
|
|
2379
|
+
|| value.serverInfo.version.length === 0) {
|
|
2380
|
+
return false;
|
|
2381
|
+
}
|
|
2382
|
+
return value.instructions === undefined || typeof value.instructions === "string";
|
|
2383
|
+
}
|
|
2384
|
+
function isServerCapabilities(value) {
|
|
2385
|
+
if (!isObjectRecord(value)) {
|
|
2386
|
+
return false;
|
|
2387
|
+
}
|
|
2388
|
+
for (const capability of ["prompts", "resources", "tools", "logging", "completions", "experimental"]) {
|
|
2389
|
+
if (value[capability] !== undefined && !isObjectRecord(value[capability])) {
|
|
2390
|
+
return false;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
return true;
|
|
2394
|
+
}
|
|
2395
|
+
function isCallToolResult(value) {
|
|
2396
|
+
if (!isObjectRecord(value) || !Array.isArray(value.content)) {
|
|
2397
|
+
return false;
|
|
2398
|
+
}
|
|
2399
|
+
if (value.isError !== undefined && typeof value.isError !== "boolean") {
|
|
2400
|
+
return false;
|
|
2401
|
+
}
|
|
2402
|
+
return value.content.every(isContentItem);
|
|
2403
|
+
}
|
|
2404
|
+
function isToolsListResult(value) {
|
|
2405
|
+
return isObjectRecord(value)
|
|
2406
|
+
&& Array.isArray(value.tools)
|
|
2407
|
+
&& (value.nextCursor === undefined || typeof value.nextCursor === "string");
|
|
2408
|
+
}
|
|
2409
|
+
function isContentItem(value) {
|
|
2410
|
+
if (!isObjectRecord(value)) {
|
|
2411
|
+
return false;
|
|
2412
|
+
}
|
|
2413
|
+
if (value.type === "text") {
|
|
2414
|
+
return typeof value.text === "string";
|
|
2415
|
+
}
|
|
2416
|
+
if (value.type === "image" || value.type === "audio") {
|
|
2417
|
+
return typeof value.data === "string" && typeof value.mimeType === "string";
|
|
2418
|
+
}
|
|
2419
|
+
if (value.type !== "resource" || !isObjectRecord(value.resource)) {
|
|
2420
|
+
return false;
|
|
2421
|
+
}
|
|
2422
|
+
return typeof value.resource.uri === "string"
|
|
2423
|
+
&& (value.resource.mimeType === undefined || typeof value.resource.mimeType === "string")
|
|
2424
|
+
&& (typeof value.resource.text === "string" || typeof value.resource.blob === "string");
|
|
2425
|
+
}
|
|
2256
2426
|
function hasOwn(value, property) {
|
|
2257
2427
|
return Object.prototype.hasOwnProperty.call(value, property);
|
|
2258
2428
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OAuthAuthorizationServerMetadata, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthUnauthorizedChallenge } from "
|
|
1
|
+
import type { OAuthAuthorizationServerMetadata, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthUnauthorizedChallenge } from "mcp-oauth";
|
|
2
2
|
export type { OAuthAuthorizationServerMetadata, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthUnauthorizedChallenge, };
|
|
3
3
|
export interface OAuthDiscoveryCache {
|
|
4
4
|
get(resourceUrl: string): OAuthDiscoveryResult | null | undefined | Promise<OAuthDiscoveryResult | null | undefined>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canonicalizeResourceIndicator } from "
|
|
1
|
+
import { canonicalizeResourceIndicator } from "mcp-oauth";
|
|
2
2
|
function defaultOAuthMetadataFetch(input, init) {
|
|
3
3
|
return fetch(input, init);
|
|
4
4
|
}
|
|
@@ -129,16 +129,13 @@ export class OAuthMetadataDiscovery {
|
|
|
129
129
|
const cacheKey = canonicalizeResourceIndicator(resourceUrl);
|
|
130
130
|
const resourceMetadataLocation = resolveProtectedResourceMetadataUrl(cacheKey, resourceMetadataUrl);
|
|
131
131
|
const memoryCachedResult = this.memoryCache.get(cacheKey);
|
|
132
|
-
if (memoryCachedResult !== undefined &&
|
|
133
|
-
(resourceMetadataUrl === undefined
|
|
134
|
-
|| memoryCachedResult.resourceMetadataUrl === resourceMetadataLocation)) {
|
|
132
|
+
if (memoryCachedResult !== undefined && resourceMetadataUrl === undefined) {
|
|
135
133
|
return memoryCachedResult;
|
|
136
134
|
}
|
|
137
135
|
const sharedCachedResult = await this.cache?.get(cacheKey);
|
|
138
136
|
if (sharedCachedResult !== null &&
|
|
139
137
|
sharedCachedResult !== undefined &&
|
|
140
|
-
|
|
141
|
-
|| sharedCachedResult.resourceMetadataUrl === resourceMetadataLocation)) {
|
|
138
|
+
resourceMetadataUrl === undefined) {
|
|
142
139
|
this.memoryCache.set(cacheKey, sharedCachedResult);
|
|
143
140
|
return sharedCachedResult;
|
|
144
141
|
}
|
|
@@ -340,7 +337,7 @@ export function parseBearerWwwAuthenticateHeader(headerValue) {
|
|
|
340
337
|
break;
|
|
341
338
|
}
|
|
342
339
|
index = skipOptionalWhitespace(headerValue, scheme.nextIndex);
|
|
343
|
-
const params =
|
|
340
|
+
const params = Object.create(null);
|
|
344
341
|
if (index < headerValue.length && headerValue[index] !== ",") {
|
|
345
342
|
const token68 = readToken68(headerValue, index);
|
|
346
343
|
if (token68 !== null) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-mcp-client",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
7
8
|
"scripts": {
|
|
8
|
-
"build": "tsc"
|
|
9
|
+
"build": "node ../../scripts/guard-package-dist.mjs && tsc"
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
@@ -427,6 +427,45 @@ describe("discoverOAuthMetadata", () => {
|
|
|
427
427
|
hintedAuthorizationServerMetadataUrl,
|
|
428
428
|
]);
|
|
429
429
|
});
|
|
430
|
+
|
|
431
|
+
it("refetches an explicitly repeated resource_metadata hint", async () => {
|
|
432
|
+
const resourceUrl = "https://resource.example.com/tenant/mcp";
|
|
433
|
+
const resourceMetadataUrl =
|
|
434
|
+
"https://resource.example.com/.well-known/oauth-protected-resource/tenant/mcp";
|
|
435
|
+
const firstIssuer = "https://auth.example.com/issuer-a";
|
|
436
|
+
const secondIssuer = "https://auth.example.com/issuer-b";
|
|
437
|
+
let rotated = false;
|
|
438
|
+
const fetchMock = vi.fn(async (input: string | URL): Promise<Response> => {
|
|
439
|
+
const url = input.toString();
|
|
440
|
+
if (url === resourceMetadataUrl) {
|
|
441
|
+
return jsonResponse({
|
|
442
|
+
resource: resourceUrl,
|
|
443
|
+
authorization_servers: [rotated ? secondIssuer : firstIssuer],
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
const issuer = url.includes("issuer-a") ? firstIssuer : secondIssuer;
|
|
447
|
+
return jsonResponse({
|
|
448
|
+
issuer,
|
|
449
|
+
authorization_endpoint: `${issuer}/authorize`,
|
|
450
|
+
token_endpoint: `${issuer}/token`,
|
|
451
|
+
response_types_supported: ["code"],
|
|
452
|
+
code_challenge_methods_supported: ["S256"],
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
const discoveryClient = new OAuthMetadataDiscovery({ fetch: fetchMock });
|
|
456
|
+
|
|
457
|
+
expect((await discoveryClient.discover(resourceUrl)).authorizationServer).toBe(firstIssuer);
|
|
458
|
+
rotated = true;
|
|
459
|
+
expect(
|
|
460
|
+
(await discoveryClient.discover(resourceUrl, { resourceMetadataUrl })).authorizationServer
|
|
461
|
+
).toBe(secondIssuer);
|
|
462
|
+
expect(fetchMock.mock.calls.map(([input]) => input.toString())).toEqual([
|
|
463
|
+
resourceMetadataUrl,
|
|
464
|
+
"https://auth.example.com/.well-known/oauth-authorization-server/issuer-a",
|
|
465
|
+
resourceMetadataUrl,
|
|
466
|
+
"https://auth.example.com/.well-known/oauth-authorization-server/issuer-b",
|
|
467
|
+
]);
|
|
468
|
+
});
|
|
430
469
|
});
|
|
431
470
|
|
|
432
471
|
describe("parseBearerWwwAuthenticateHeader", () => {
|
|
@@ -464,6 +503,13 @@ describe("parseBearerWwwAuthenticateHeader", () => {
|
|
|
464
503
|
'Digest abc123==, Bearer abc123==, Bearer realm="mcp", resource_metadata="https://resource.example.com/.well-known/oauth-protected-resource/mcp"',
|
|
465
504
|
});
|
|
466
505
|
});
|
|
506
|
+
|
|
507
|
+
it("preserves a __proto__ auth parameter as parsed data", () => {
|
|
508
|
+
const challenge = parseBearerWwwAuthenticateHeader('Bearer __proto__="visible"');
|
|
509
|
+
|
|
510
|
+
expect(Object.hasOwn(challenge!.params, "__proto__")).toBe(true);
|
|
511
|
+
expect(challenge?.params.__proto__).toBe("visible");
|
|
512
|
+
});
|
|
467
513
|
});
|
|
468
514
|
|
|
469
515
|
describe("resolveAuthorizationServerMetadataUrl", () => {
|