@pentoshi/clai 4.13.1 → 4.14.1
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/CONTRIBUTING.md +9 -4
- package/MCP.md +182 -0
- package/PROVIDERS.md +69 -0
- package/README.md +19 -6
- package/dist/agent/runner.js +1 -1
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/stream-recovery.d.ts +1 -1
- package/dist/agent/stream-recovery.js +10 -4
- package/dist/agent/stream-recovery.js.map +1 -1
- package/dist/agent/turn/mcp-agent-tools.d.ts +4 -2
- package/dist/agent/turn/mcp-agent-tools.js +15 -6
- package/dist/agent/turn/mcp-agent-tools.js.map +1 -1
- package/dist/agent/turn/tool-call-preparation.d.ts +2 -1
- package/dist/agent/turn/tool-call-preparation.js +20 -4
- package/dist/agent/turn/tool-call-preparation.js.map +1 -1
- package/dist/agent/turn/tool-execution/single-tool.js +1 -1
- package/dist/agent/turn/tool-execution/single-tool.js.map +1 -1
- package/dist/app/commands/catalog.js +1 -0
- package/dist/app/commands/catalog.js.map +1 -1
- package/dist/classic/app/action-handlers.js +2 -1
- package/dist/classic/app/action-handlers.js.map +1 -1
- package/dist/classic/app/app-wiring.js +2 -0
- package/dist/classic/app/app-wiring.js.map +1 -1
- package/dist/classic/bootstrap/runtime-view.d.ts +6 -0
- package/dist/classic/bootstrap/runtime-view.js +46 -0
- package/dist/classic/bootstrap/runtime-view.js.map +1 -0
- package/dist/classic/bootstrap/terminal-session.d.ts +3 -0
- package/dist/classic/bootstrap/terminal-session.js +3 -0
- package/dist/classic/bootstrap/terminal-session.js.map +1 -1
- package/dist/classic/chrome/composer-controller.d.ts +2 -1
- package/dist/classic/chrome/composer-controller.js +36 -19
- package/dist/classic/chrome/composer-controller.js.map +1 -1
- package/dist/classic/chrome/composer-frame.js +1 -1
- package/dist/classic/chrome/composer-frame.js.map +1 -1
- package/dist/classic/input/input-router.d.ts +2 -1
- package/dist/classic/input/input-router.js +9 -0
- package/dist/classic/input/input-router.js.map +1 -1
- package/dist/classic/input/key-event.d.ts +3 -0
- package/dist/classic/input/key-event.js.map +1 -1
- package/dist/classic/input/paste-burst.js +5 -0
- package/dist/classic/input/paste-burst.js.map +1 -1
- package/dist/classic/input/raw-decoder.d.ts +1 -0
- package/dist/classic/input/raw-decoder.js +15 -10
- package/dist/classic/input/raw-decoder.js.map +1 -1
- package/dist/classic/input/terminal-sequences.d.ts +0 -1
- package/dist/classic/input/terminal-sequences.js +0 -1
- package/dist/classic/input/terminal-sequences.js.map +1 -1
- package/dist/commands/providers.js +2 -0
- package/dist/commands/providers.js.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/adapters/mistral-messages.d.ts +1 -0
- package/dist/llm/adapters/mistral-messages.js +56 -0
- package/dist/llm/adapters/mistral-messages.js.map +1 -0
- package/dist/llm/capabilities.js +63 -1
- package/dist/llm/capabilities.js.map +1 -1
- package/dist/llm/capability/state.d.ts +1 -1
- package/dist/llm/capability/tool-dialect.js +8 -1
- package/dist/llm/capability/tool-dialect.js.map +1 -1
- package/dist/llm/capability/vision-patterns.js +6 -0
- package/dist/llm/capability/vision-patterns.js.map +1 -1
- package/dist/llm/capability/vision-registry.js +11 -1
- package/dist/llm/capability/vision-registry.js.map +1 -1
- package/dist/llm/catalog-context.d.ts +6 -0
- package/dist/llm/catalog-context.js +15 -0
- package/dist/llm/catalog-context.js.map +1 -0
- package/dist/llm/catalog-facts.d.ts +2 -0
- package/dist/llm/catalog-facts.js.map +1 -1
- package/dist/llm/cline-model-catalog.d.ts +4 -0
- package/dist/llm/cline-model-catalog.js +197 -0
- package/dist/llm/cline-model-catalog.js.map +1 -0
- package/dist/llm/cline.d.ts +1 -0
- package/dist/llm/cline.js +69 -17
- package/dist/llm/cline.js.map +1 -1
- package/dist/llm/free-model-limits.d.ts +1 -0
- package/dist/llm/free-model-limits.js +101 -0
- package/dist/llm/free-model-limits.js.map +1 -0
- package/dist/llm/free.js +136 -103
- package/dist/llm/free.js.map +1 -1
- package/dist/llm/http.d.ts +2 -1
- package/dist/llm/http.js +3 -1
- package/dist/llm/http.js.map +1 -1
- package/dist/llm/mistral-models.d.ts +2 -0
- package/dist/llm/mistral-models.js +23 -0
- package/dist/llm/mistral-models.js.map +1 -0
- package/dist/llm/mistral.d.ts +2 -0
- package/dist/llm/mistral.js +129 -0
- package/dist/llm/mistral.js.map +1 -0
- package/dist/llm/profile/layer-merge.js +3 -3
- package/dist/llm/profile/layer-merge.js.map +1 -1
- package/dist/llm/provider-identity.js +5 -0
- package/dist/llm/provider-identity.js.map +1 -1
- package/dist/llm/provider-info-text.js +26 -0
- package/dist/llm/provider-info-text.js.map +1 -1
- package/dist/llm/provider-model-layers.js +45 -0
- package/dist/llm/provider-model-layers.js.map +1 -1
- package/dist/llm/provider-profile-layers.js +25 -0
- package/dist/llm/provider-profile-layers.js.map +1 -1
- package/dist/llm/provider-profiles.js +28 -4
- package/dist/llm/provider-profiles.js.map +1 -1
- package/dist/llm/qoder/qoder-catalog.d.ts +15 -0
- package/dist/llm/qoder/qoder-catalog.js +56 -0
- package/dist/llm/qoder/qoder-catalog.js.map +1 -0
- package/dist/llm/qoder/qoder-http.d.ts +13 -0
- package/dist/llm/qoder/qoder-http.js +55 -0
- package/dist/llm/qoder/qoder-http.js.map +1 -0
- package/dist/llm/qoder/qoder-queue.d.ts +15 -0
- package/dist/llm/qoder/qoder-queue.js +169 -0
- package/dist/llm/qoder/qoder-queue.js.map +1 -0
- package/dist/llm/qoder/qoder-response.d.ts +19 -0
- package/dist/llm/qoder/qoder-response.js +98 -0
- package/dist/llm/qoder/qoder-response.js.map +1 -0
- package/dist/llm/qoder/qoder-stream.d.ts +23 -0
- package/dist/llm/qoder/qoder-stream.js +75 -0
- package/dist/llm/qoder/qoder-stream.js.map +1 -0
- package/dist/llm/qoder/qoder.d.ts +4 -4
- package/dist/llm/qoder/qoder.js +71 -189
- package/dist/llm/qoder/qoder.js.map +1 -1
- package/dist/llm/quota-signals.d.ts +1 -0
- package/dist/llm/quota-signals.js +8 -0
- package/dist/llm/quota-signals.js.map +1 -1
- package/dist/llm/reasoning-capability.js +1 -0
- package/dist/llm/reasoning-capability.js.map +1 -1
- package/dist/llm/request-plan.js +15 -6
- package/dist/llm/request-plan.js.map +1 -1
- package/dist/llm/router.js +1 -1
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/routing/attempt-complete.js +4 -3
- package/dist/llm/routing/attempt-complete.js.map +1 -1
- package/dist/llm/routing/attempt-request.d.ts +5 -1
- package/dist/llm/routing/attempt-request.js +22 -2
- package/dist/llm/routing/attempt-request.js.map +1 -1
- package/dist/llm/routing/attempt-stream.js +4 -3
- package/dist/llm/routing/attempt-stream.js.map +1 -1
- package/dist/llm/routing/error-classification.js +2 -0
- package/dist/llm/routing/error-classification.js.map +1 -1
- package/dist/llm/routing/failure-report.d.ts +1 -1
- package/dist/llm/routing/failure-report.js +11 -5
- package/dist/llm/routing/failure-report.js.map +1 -1
- package/dist/llm/routing/provider-selection.js +3 -0
- package/dist/llm/routing/provider-selection.js.map +1 -1
- package/dist/llm/tool-protocol.d.ts +1 -1
- package/dist/llm/tool-protocol.js +4 -1
- package/dist/llm/tool-protocol.js.map +1 -1
- package/dist/llm/wire/chat-body.js +51 -28
- package/dist/llm/wire/chat-body.js.map +1 -1
- package/dist/llm/wire/effort-preflight.js +4 -1
- package/dist/llm/wire/effort-preflight.js.map +1 -1
- package/dist/llm/wire/mistral-content.d.ts +19 -0
- package/dist/llm/wire/mistral-content.js +106 -0
- package/dist/llm/wire/mistral-content.js.map +1 -0
- package/dist/llm/wire/mistral-model-catalog.d.ts +2 -0
- package/dist/llm/wire/mistral-model-catalog.js +73 -0
- package/dist/llm/wire/mistral-model-catalog.js.map +1 -0
- package/dist/llm/wire/openai-complete.js +10 -4
- package/dist/llm/wire/openai-complete.js.map +1 -1
- package/dist/llm/wire/openai-stream.js +38 -16
- package/dist/llm/wire/openai-stream.js.map +1 -1
- package/dist/llm/wire/response-errors.d.ts +1 -0
- package/dist/llm/wire/response-errors.js +1 -1
- package/dist/llm/wire/response-errors.js.map +1 -1
- package/dist/mcp/auth/device.d.ts +7 -3
- package/dist/mcp/auth/device.js +55 -32
- package/dist/mcp/auth/device.js.map +1 -1
- package/dist/mcp/auth/http.d.ts +12 -0
- package/dist/mcp/auth/http.js +62 -0
- package/dist/mcp/auth/http.js.map +1 -0
- package/dist/mcp/auth/loopback.d.ts +9 -1
- package/dist/mcp/auth/loopback.js +80 -8
- package/dist/mcp/auth/loopback.js.map +1 -1
- package/dist/mcp/auth/metadata.d.ts +2 -3
- package/dist/mcp/auth/metadata.js +25 -25
- package/dist/mcp/auth/metadata.js.map +1 -1
- package/dist/mcp/auth/provider.d.ts +1 -0
- package/dist/mcp/auth/provider.js +215 -42
- package/dist/mcp/auth/provider.js.map +1 -1
- package/dist/mcp/auth/registration.d.ts +2 -3
- package/dist/mcp/auth/registration.js +10 -14
- package/dist/mcp/auth/registration.js.map +1 -1
- package/dist/mcp/auth/token-exchange.d.ts +5 -3
- package/dist/mcp/auth/token-exchange.js +15 -15
- package/dist/mcp/auth/token-exchange.js.map +1 -1
- package/dist/mcp/auth/token-store.d.ts +1 -1
- package/dist/mcp/auth/token-store.js +105 -54
- package/dist/mcp/auth/token-store.js.map +1 -1
- package/dist/mcp/auth/types.d.ts +11 -2
- package/dist/mcp/catalog.d.ts +11 -0
- package/dist/mcp/catalog.js +58 -0
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/mcp/client.d.ts +10 -1
- package/dist/mcp/client.js +93 -29
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/coerce.js +4 -4
- package/dist/mcp/coerce.js.map +1 -1
- package/dist/mcp/config-file.js +53 -10
- package/dist/mcp/config-file.js.map +1 -1
- package/dist/mcp/discovery.js +2 -0
- package/dist/mcp/discovery.js.map +1 -1
- package/dist/mcp/jsonrpc.js +28 -18
- package/dist/mcp/jsonrpc.js.map +1 -1
- package/dist/mcp/manager.d.ts +17 -0
- package/dist/mcp/manager.js +211 -29
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/results.js +25 -10
- package/dist/mcp/results.js.map +1 -1
- package/dist/mcp/runtime.d.ts +6 -6
- package/dist/mcp/runtime.js +168 -60
- package/dist/mcp/runtime.js.map +1 -1
- package/dist/mcp/schema-compact.js +3 -29
- package/dist/mcp/schema-compact.js.map +1 -1
- package/dist/mcp/storage-lock.d.ts +1 -0
- package/dist/mcp/storage-lock.js +80 -0
- package/dist/mcp/storage-lock.js.map +1 -0
- package/dist/mcp/transport-http.d.ts +13 -2
- package/dist/mcp/transport-http.js +245 -77
- package/dist/mcp/transport-http.js.map +1 -1
- package/dist/mcp/transport-stdio.d.ts +5 -2
- package/dist/mcp/transport-stdio.js +96 -52
- package/dist/mcp/transport-stdio.js.map +1 -1
- package/dist/mcp/transport.d.ts +9 -1
- package/dist/mcp/transport.js +38 -0
- package/dist/mcp/transport.js.map +1 -1
- package/dist/mcp/types.d.ts +8 -2
- package/dist/mcp/types.js +1 -5
- package/dist/mcp/types.js.map +1 -1
- package/dist/mcp/validation.js +81 -12
- package/dist/mcp/validation.js.map +1 -1
- package/dist/noninteractive/stream-renderer.d.ts +1 -0
- package/dist/noninteractive/stream-renderer.js +13 -0
- package/dist/noninteractive/stream-renderer.js.map +1 -1
- package/dist/session-runtime/child-bridge.d.ts +10 -4
- package/dist/session-runtime/child-bridge.js +34 -6
- package/dist/session-runtime/child-bridge.js.map +1 -1
- package/dist/session-runtime/client.d.ts +3 -2
- package/dist/session-runtime/client.js +22 -9
- package/dist/session-runtime/client.js.map +1 -1
- package/dist/session-runtime/host.d.ts +4 -0
- package/dist/session-runtime/host.js +108 -16
- package/dist/session-runtime/host.js.map +1 -1
- package/dist/session-runtime/launch.d.ts +1 -0
- package/dist/session-runtime/launch.js +3 -1
- package/dist/session-runtime/launch.js.map +1 -1
- package/dist/session-runtime/start-runtime.d.ts +12 -0
- package/dist/session-runtime/start-runtime.js +69 -0
- package/dist/session-runtime/start-runtime.js.map +1 -0
- package/dist/session-runtime/store.js +1 -0
- package/dist/session-runtime/store.js.map +1 -1
- package/dist/session-runtime/terminal-options.d.ts +4 -0
- package/dist/session-runtime/terminal-options.js +49 -0
- package/dist/session-runtime/terminal-options.js.map +1 -0
- package/dist/session-runtime/types.d.ts +38 -1
- package/dist/session-runtime/view-manager.d.ts +38 -0
- package/dist/session-runtime/view-manager.js +189 -0
- package/dist/session-runtime/view-manager.js.map +1 -0
- package/dist/session-runtime/view-services.d.ts +14 -0
- package/dist/session-runtime/view-services.js +83 -0
- package/dist/session-runtime/view-services.js.map +1 -0
- package/dist/session-runtime/view-terminal.d.ts +29 -0
- package/dist/session-runtime/view-terminal.js +51 -0
- package/dist/session-runtime/view-terminal.js.map +1 -0
- package/dist/store/config/endpoints.js +1 -0
- package/dist/store/config/endpoints.js.map +1 -1
- package/dist/store/config.js +2 -0
- package/dist/store/config.js.map +1 -1
- package/dist/store/rtk-usage.d.ts +1 -0
- package/dist/store/rtk-usage.js +8 -3
- package/dist/store/rtk-usage.js.map +1 -1
- package/dist/tools/definitions/aggregate.js +4 -1
- package/dist/tools/definitions/aggregate.js.map +1 -1
- package/dist/tools/rtk/binary.d.ts +2 -2
- package/dist/tools/rtk/binary.js +12 -4
- package/dist/tools/rtk/binary.js.map +1 -1
- package/dist/tools/rtk/rewrite.js +13 -3
- package/dist/tools/rtk/rewrite.js.map +1 -1
- package/dist/tui-v2/bootstrap/runtime-view.d.ts +6 -0
- package/dist/tui-v2/bootstrap/runtime-view.js +54 -0
- package/dist/tui-v2/bootstrap/runtime-view.js.map +1 -0
- package/dist/tui-v2/components/composer/composer-input-box.d.ts +6 -1
- package/dist/tui-v2/components/composer/composer-input-box.js +27 -18
- package/dist/tui-v2/components/composer/composer-input-box.js.map +1 -1
- package/dist/tui-v2/components/composer/paste-chip.d.ts +3 -2
- package/dist/tui-v2/components/composer/paste-chip.js +17 -48
- package/dist/tui-v2/components/composer/paste-chip.js.map +1 -1
- package/dist/tui-v2/composer/composer-cursor.d.ts +3 -0
- package/dist/tui-v2/composer/composer-cursor.js +21 -0
- package/dist/tui-v2/composer/composer-cursor.js.map +1 -0
- package/dist/tui-v2/composer/composer-editor.js +91 -100
- package/dist/tui-v2/composer/composer-editor.js.map +1 -1
- package/dist/tui-v2/composer/composer-highlight.d.ts +6 -1
- package/dist/tui-v2/composer/composer-highlight.js +9 -2
- package/dist/tui-v2/composer/composer-highlight.js.map +1 -1
- package/dist/tui-v2/composer/composer-wheel.js +2 -1
- package/dist/tui-v2/composer/composer-wheel.js.map +1 -1
- package/dist/tui-v2/composer/expand-paste.d.ts +4 -0
- package/dist/tui-v2/composer/expand-paste.js +34 -0
- package/dist/tui-v2/composer/expand-paste.js.map +1 -0
- package/dist/tui-v2/composer/paste-hit-test.d.ts +28 -0
- package/dist/tui-v2/composer/paste-hit-test.js +25 -0
- package/dist/tui-v2/composer/paste-hit-test.js.map +1 -0
- package/dist/tui-v2/composer/use-draft-actions.js +3 -1
- package/dist/tui-v2/composer/use-draft-actions.js.map +1 -1
- package/dist/tui-v2/input/paste-burst-guard.js +3 -0
- package/dist/tui-v2/input/paste-burst-guard.js.map +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/mentions.js +12 -1
- package/dist/ui/mentions.js.map +1 -1
- package/dist/ui-core/actions/action-id.d.ts +1 -1
- package/dist/ui-core/actions/action-id.js +1 -0
- package/dist/ui-core/actions/action-id.js.map +1 -1
- package/dist/ui-core/actions/format-shortcuts.js +3 -2
- package/dist/ui-core/actions/format-shortcuts.js.map +1 -1
- package/dist/ui-core/actions/keymap.js +2 -0
- package/dist/ui-core/actions/keymap.js.map +1 -1
- package/dist/ui-core/bootstrap/composition-root.d.ts +4 -1
- package/dist/ui-core/bootstrap/composition-root.js +33 -14
- package/dist/ui-core/bootstrap/composition-root.js.map +1 -1
- package/dist/ui-core/commands/mcp-commands.js +3 -3
- package/dist/ui-core/commands/mcp-commands.js.map +1 -1
- package/dist/ui-core/commands/pickers/search-reasoning.js +21 -6
- package/dist/ui-core/commands/pickers/search-reasoning.js.map +1 -1
- package/dist/ui-core/commands/rtk-commands.js +3 -3
- package/dist/ui-core/commands/rtk-commands.js.map +1 -1
- package/dist/ui-core/commands/session-commands.js +1 -1
- package/dist/ui-core/commands/session-commands.js.map +1 -1
- package/dist/ui-core/composer/completion.d.ts +1 -0
- package/dist/ui-core/composer/completion.js +31 -3
- package/dist/ui-core/composer/completion.js.map +1 -1
- package/dist/ui-core/composer/composer-height.d.ts +1 -1
- package/dist/ui-core/composer/composer-height.js +26 -3
- package/dist/ui-core/composer/composer-height.js.map +1 -1
- package/dist/ui-core/composer/paste-placeholder.d.ts +10 -0
- package/dist/ui-core/composer/paste-placeholder.js +60 -12
- package/dist/ui-core/composer/paste-placeholder.js.map +1 -1
- package/dist/ui-core/input/paste-burst.d.ts +2 -1
- package/dist/ui-core/input/paste-burst.js +20 -0
- package/dist/ui-core/input/paste-burst.js.map +1 -1
- package/dist/ui-core/state/transcript-store.d.ts +7 -0
- package/dist/ui-core/state/transcript-store.js +53 -0
- package/dist/ui-core/state/transcript-store.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/package.json +3 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isJsonRpcResponse, parseMessage, SseDecoder
|
|
2
|
-
import { McpTransportError, withTimeout, } from "./transport.js";
|
|
1
|
+
import { createNotification, isJsonRpcResponse, parseMessage, SseDecoder } from "./jsonrpc.js";
|
|
2
|
+
import { McpTransportError, awaitMcpOperation, dispatchServerMessage, withTimeout, } from "./transport.js";
|
|
3
3
|
import { parseWwwAuthenticate } from "./auth/www-authenticate.js";
|
|
4
4
|
import { MCP_PROTOCOL_VERSION, } from "./types.js";
|
|
5
5
|
const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
@@ -36,14 +36,10 @@ async function rejectRedirect(response) {
|
|
|
36
36
|
throw error;
|
|
37
37
|
}
|
|
38
38
|
function sseStreamError(server, status) {
|
|
39
|
-
return new McpTransportError("network", status === 401
|
|
40
|
-
? unauthorizedHint(server)
|
|
41
|
-
: `MCP SSE stream returned ${status}.`, status);
|
|
39
|
+
return new McpTransportError("network", status === 401 ? unauthorizedHint(server) : `MCP SSE stream returned ${status}.`, status);
|
|
42
40
|
}
|
|
43
41
|
function ssePostError(server, status) {
|
|
44
|
-
return new McpTransportError("network", status === 401
|
|
45
|
-
? unauthorizedHint(server)
|
|
46
|
-
: `MCP SSE POST returned ${status}.`, status);
|
|
42
|
+
return new McpTransportError("network", status === 401 ? unauthorizedHint(server) : `MCP SSE POST returned ${status}.`, status);
|
|
47
43
|
}
|
|
48
44
|
async function readBoundedText(response, maxBytes) {
|
|
49
45
|
const body = response.body;
|
|
@@ -80,14 +76,13 @@ function baseHeaders(sessionId, protocolVersion, accept) {
|
|
|
80
76
|
headers[PROTOCOL_HEADER] = protocolVersion;
|
|
81
77
|
return headers;
|
|
82
78
|
}
|
|
83
|
-
async function collectSseResponse(response, targetId, maxBytes) {
|
|
79
|
+
async function collectSseResponse(response, targetId, maxBytes, onMessage, onEvent) {
|
|
84
80
|
const body = response.body;
|
|
85
81
|
if (!body) {
|
|
86
82
|
throw new McpTransportError("protocol", "MCP SSE response had no body.");
|
|
87
83
|
}
|
|
88
84
|
const reader = body.getReader();
|
|
89
|
-
const sse = new SseDecoder();
|
|
90
|
-
let total = 0;
|
|
85
|
+
const sse = new SseDecoder(maxBytes);
|
|
91
86
|
try {
|
|
92
87
|
while (true) {
|
|
93
88
|
const { done, value } = await reader.read();
|
|
@@ -95,22 +90,21 @@ async function collectSseResponse(response, targetId, maxBytes) {
|
|
|
95
90
|
break;
|
|
96
91
|
if (!value)
|
|
97
92
|
continue;
|
|
98
|
-
total += value.byteLength;
|
|
99
|
-
if (total > maxBytes) {
|
|
100
|
-
throw new McpTransportError("too-large", "MCP SSE stream exceeded the size limit.");
|
|
101
|
-
}
|
|
102
93
|
for (const event of sse.push(value)) {
|
|
94
|
+
onEvent(event.id);
|
|
103
95
|
const message = parseMessage(event.data);
|
|
104
96
|
if (message && isJsonRpcResponse(message) && String(message.id) === String(targetId)) {
|
|
105
97
|
return message;
|
|
106
98
|
}
|
|
99
|
+
if (message)
|
|
100
|
+
await onMessage(message);
|
|
107
101
|
}
|
|
108
102
|
}
|
|
109
103
|
}
|
|
110
104
|
finally {
|
|
111
105
|
await reader.cancel().catch(() => undefined);
|
|
112
106
|
}
|
|
113
|
-
throw new McpTransportError("
|
|
107
|
+
throw new McpTransportError("closed", "MCP SSE stream closed before a matching response arrived.");
|
|
114
108
|
}
|
|
115
109
|
function unauthorizedHint(server) {
|
|
116
110
|
return (`MCP server ${server} returned 401 Unauthorized. ` +
|
|
@@ -124,6 +118,10 @@ export class StreamableHttpTransport {
|
|
|
124
118
|
protocolVersion = MCP_PROTOCOL_VERSION;
|
|
125
119
|
nextId = 1;
|
|
126
120
|
closed = false;
|
|
121
|
+
handlers;
|
|
122
|
+
lifetime = new AbortController();
|
|
123
|
+
listening = false;
|
|
124
|
+
sentTokens = new WeakMap();
|
|
127
125
|
constructor(config, options = {}) {
|
|
128
126
|
this.config = config;
|
|
129
127
|
this.options = options;
|
|
@@ -140,35 +138,49 @@ export class StreamableHttpTransport {
|
|
|
140
138
|
setProtocolVersion(version) {
|
|
141
139
|
this.protocolVersion = version;
|
|
142
140
|
}
|
|
141
|
+
setHandlers(handlers) {
|
|
142
|
+
this.handlers = handlers;
|
|
143
|
+
}
|
|
143
144
|
start() {
|
|
144
145
|
return Promise.resolve();
|
|
145
146
|
}
|
|
146
147
|
async resolveHeaders(accept) {
|
|
147
148
|
const base = baseHeaders(this.session, this.protocolVersion, accept);
|
|
148
|
-
const authHeaders = this.options.authProvider
|
|
149
|
-
? await this.options.authProvider.headers()
|
|
150
|
-
: {};
|
|
149
|
+
const authHeaders = this.options.authProvider ? await this.options.authProvider.headers() : {};
|
|
151
150
|
return {
|
|
152
151
|
...base,
|
|
153
152
|
...authHeaders,
|
|
154
153
|
...this.config.headers,
|
|
155
154
|
};
|
|
156
155
|
}
|
|
157
|
-
async sendOnce(accept, method, body, signal) {
|
|
158
|
-
const headers = await this.resolveHeaders(accept);
|
|
159
|
-
const init = {
|
|
156
|
+
async sendOnce(accept, method, body, signal, extraHeaders) {
|
|
157
|
+
const headers = await awaitMcpOperation(this.resolveHeaders(accept), signal);
|
|
158
|
+
const init = {
|
|
159
|
+
method,
|
|
160
|
+
headers: { ...headers, ...extraHeaders },
|
|
161
|
+
redirect: "manual",
|
|
162
|
+
signal,
|
|
163
|
+
};
|
|
160
164
|
if (body !== undefined)
|
|
161
165
|
init.body = body;
|
|
162
|
-
|
|
166
|
+
const response = await this.fetchImpl(this.config.url, init);
|
|
167
|
+
const authorization = new Headers(headers).get("authorization");
|
|
168
|
+
if (authorization)
|
|
169
|
+
this.sentTokens.set(response, authorization.replace(/^Bearer\s+/i, ""));
|
|
170
|
+
return response;
|
|
163
171
|
}
|
|
164
|
-
async retryOnUnauthorized(response, resend) {
|
|
172
|
+
async retryOnUnauthorized(response, resend, signal) {
|
|
165
173
|
const provider = this.options.authProvider;
|
|
166
174
|
if (!provider)
|
|
167
175
|
return response;
|
|
168
176
|
const challenge = parseWwwAuthenticate(response.headers.get("www-authenticate"));
|
|
169
177
|
if (response.body)
|
|
170
178
|
await response.body.cancel().catch(() => undefined);
|
|
171
|
-
const refreshed = await provider.onUnauthorized(challenge
|
|
179
|
+
const refreshed = await provider.onUnauthorized(challenge, {
|
|
180
|
+
interactive: false,
|
|
181
|
+
signal,
|
|
182
|
+
rejectedToken: this.sentTokens.get(response),
|
|
183
|
+
});
|
|
172
184
|
if (!refreshed)
|
|
173
185
|
return response;
|
|
174
186
|
return resend();
|
|
@@ -187,38 +199,70 @@ export class StreamableHttpTransport {
|
|
|
187
199
|
const id = this.nextId++;
|
|
188
200
|
const framed = { ...message, id };
|
|
189
201
|
const timeoutMs = options.timeoutMs ?? this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
190
|
-
const
|
|
202
|
+
const parent = options.signal
|
|
203
|
+
? AbortSignal.any([options.signal, this.lifetime.signal])
|
|
204
|
+
: this.lifetime.signal;
|
|
205
|
+
const { signal, dispose } = withTimeout(parent, timeoutMs);
|
|
191
206
|
const accept = "application/json, text/event-stream";
|
|
192
207
|
const body = JSON.stringify(framed);
|
|
193
208
|
const resend = () => this.sendOnce(accept, "POST", body, signal);
|
|
194
209
|
try {
|
|
195
210
|
let response = await resend();
|
|
196
|
-
|
|
197
|
-
const redirect = redirectError(response);
|
|
198
|
-
if (redirect)
|
|
199
|
-
throw redirect;
|
|
200
|
-
if (response.status === 401) {
|
|
201
|
-
response = await this.retryOnUnauthorized(response, resend);
|
|
211
|
+
if (framed.method === "initialize" && response.ok)
|
|
202
212
|
this.captureSession(response);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
await rejectRedirect(response);
|
|
214
|
+
if (response.status === 401) {
|
|
215
|
+
response = await this.retryOnUnauthorized(response, resend, signal);
|
|
216
|
+
if (framed.method === "initialize" && response.ok)
|
|
217
|
+
this.captureSession(response);
|
|
218
|
+
await rejectRedirect(response);
|
|
206
219
|
}
|
|
207
220
|
if (!response.ok)
|
|
208
221
|
await this.failForStatus(response);
|
|
209
222
|
const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
|
|
210
223
|
if (contentType.includes("text/event-stream")) {
|
|
211
|
-
|
|
224
|
+
let lastEventId;
|
|
225
|
+
for (let attempt = 0;; attempt++) {
|
|
226
|
+
try {
|
|
227
|
+
return await collectSseResponse(response, framed.id, this.maxBytes, (message) => dispatchServerMessage(message, this.handlers, (reply) => this.postResponse(reply, signal)), (eventId) => {
|
|
228
|
+
if (eventId !== undefined)
|
|
229
|
+
lastEventId = eventId;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
if (signal.aborted ||
|
|
234
|
+
!lastEventId ||
|
|
235
|
+
attempt >= 3 ||
|
|
236
|
+
(error instanceof McpTransportError &&
|
|
237
|
+
error.kind !== "closed" &&
|
|
238
|
+
error.kind !== "network"))
|
|
239
|
+
throw error;
|
|
240
|
+
response = await this.sendOnce("text/event-stream", "GET", undefined, signal, {
|
|
241
|
+
"last-event-id": lastEventId,
|
|
242
|
+
});
|
|
243
|
+
await rejectRedirect(response);
|
|
244
|
+
if (!response.ok)
|
|
245
|
+
await this.failForStatus(response);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
212
248
|
}
|
|
213
249
|
const text = await readBoundedText(response, this.maxBytes);
|
|
214
250
|
const parsed = parseMessage(text);
|
|
215
251
|
if (!parsed || !isJsonRpcResponse(parsed)) {
|
|
216
252
|
throw new McpTransportError("protocol", "MCP HTTP response was not a JSON-RPC response.");
|
|
217
253
|
}
|
|
254
|
+
if (String(parsed.id) !== String(framed.id))
|
|
255
|
+
throw new McpTransportError("protocol", "MCP HTTP response id did not match the request.");
|
|
218
256
|
return parsed;
|
|
219
257
|
}
|
|
220
258
|
catch (error) {
|
|
221
|
-
|
|
259
|
+
if (signal.aborted && message.method !== "initialize" && !this.closed) {
|
|
260
|
+
void this.notify(createNotification("notifications/cancelled", {
|
|
261
|
+
requestId: id,
|
|
262
|
+
reason: "Client stopped waiting for this request.",
|
|
263
|
+
}), { timeoutMs: 1_000 }).catch(() => undefined);
|
|
264
|
+
}
|
|
265
|
+
throw mapFetchError(error, signal);
|
|
222
266
|
}
|
|
223
267
|
finally {
|
|
224
268
|
dispose();
|
|
@@ -228,18 +272,24 @@ export class StreamableHttpTransport {
|
|
|
228
272
|
if (this.closed)
|
|
229
273
|
throw new McpTransportError("closed", "MCP HTTP transport closed.");
|
|
230
274
|
const timeoutMs = options.timeoutMs ?? this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
231
|
-
const
|
|
275
|
+
const parent = options.signal
|
|
276
|
+
? AbortSignal.any([options.signal, this.lifetime.signal])
|
|
277
|
+
: this.lifetime.signal;
|
|
278
|
+
const { signal, dispose } = withTimeout(parent, timeoutMs);
|
|
232
279
|
try {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
|
|
280
|
+
const resend = () => this.sendOnce("application/json, text/event-stream", "POST", JSON.stringify(message), signal);
|
|
281
|
+
let response = await resend();
|
|
282
|
+
await rejectRedirect(response);
|
|
283
|
+
if (response.status === 401)
|
|
284
|
+
response = await this.retryOnUnauthorized(response, resend, signal);
|
|
285
|
+
await rejectRedirect(response);
|
|
286
|
+
if (!response.ok)
|
|
287
|
+
await this.failForStatus(response);
|
|
238
288
|
if (response.body)
|
|
239
289
|
await response.body.cancel().catch(() => undefined);
|
|
240
290
|
}
|
|
241
291
|
catch (error) {
|
|
242
|
-
throw mapFetchError(error);
|
|
292
|
+
throw mapFetchError(error, signal);
|
|
243
293
|
}
|
|
244
294
|
finally {
|
|
245
295
|
dispose();
|
|
@@ -247,17 +297,74 @@ export class StreamableHttpTransport {
|
|
|
247
297
|
}
|
|
248
298
|
captureSession(response) {
|
|
249
299
|
const id = response.headers.get(SESSION_HEADER);
|
|
250
|
-
if (id && id
|
|
300
|
+
if (id && !/^[\x21-\x7e]+$/.test(id))
|
|
301
|
+
throw new McpTransportError("protocol", "MCP server returned an invalid session id.");
|
|
302
|
+
if (id)
|
|
251
303
|
this.session = id;
|
|
252
304
|
}
|
|
305
|
+
async postResponse(message, signal) {
|
|
306
|
+
const response = await this.sendOnce("application/json, text/event-stream", "POST", JSON.stringify(message), signal);
|
|
307
|
+
await rejectRedirect(response);
|
|
308
|
+
if (!response.ok)
|
|
309
|
+
await this.failForStatus(response);
|
|
310
|
+
await response.body?.cancel().catch(() => undefined);
|
|
311
|
+
}
|
|
312
|
+
listen() {
|
|
313
|
+
if (this.listening || this.closed)
|
|
314
|
+
return;
|
|
315
|
+
this.listening = true;
|
|
316
|
+
void this.pumpNotifications().finally(() => {
|
|
317
|
+
this.listening = false;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
async pumpNotifications() {
|
|
321
|
+
let lastEventId;
|
|
322
|
+
for (let attempt = 0; !this.closed && attempt < 4; attempt++) {
|
|
323
|
+
try {
|
|
324
|
+
const response = await this.sendOnce("text/event-stream", "GET", undefined, this.lifetime.signal, lastEventId ? { "last-event-id": lastEventId } : undefined);
|
|
325
|
+
await rejectRedirect(response);
|
|
326
|
+
if (!response.ok || !response.body) {
|
|
327
|
+
await response.body?.cancel().catch(() => undefined);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const decoder = new SseDecoder(this.maxBytes);
|
|
331
|
+
const reader = response.body.getReader();
|
|
332
|
+
try {
|
|
333
|
+
for (;;) {
|
|
334
|
+
const { value, done } = await reader.read();
|
|
335
|
+
if (done)
|
|
336
|
+
break;
|
|
337
|
+
if (!value)
|
|
338
|
+
continue;
|
|
339
|
+
for (const event of decoder.push(value)) {
|
|
340
|
+
if (event.id !== undefined)
|
|
341
|
+
lastEventId = event.id;
|
|
342
|
+
const message = parseMessage(event.data);
|
|
343
|
+
if (message)
|
|
344
|
+
await dispatchServerMessage(message, this.handlers, (reply) => this.postResponse(reply, this.lifetime.signal));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
finally {
|
|
349
|
+
await reader.cancel().catch(() => undefined);
|
|
350
|
+
}
|
|
351
|
+
if (!lastEventId)
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
253
359
|
async close() {
|
|
254
360
|
if (this.closed)
|
|
255
361
|
return;
|
|
256
362
|
this.closed = true;
|
|
363
|
+
this.lifetime.abort(new McpTransportError("closed", "MCP HTTP transport closed."));
|
|
257
364
|
if (!this.session)
|
|
258
365
|
return;
|
|
259
366
|
try {
|
|
260
|
-
const { signal, dispose } = withTimeout(undefined,
|
|
367
|
+
const { signal, dispose } = withTimeout(undefined, 2_000);
|
|
261
368
|
try {
|
|
262
369
|
const response = await this.sendOnce("application/json", "DELETE", undefined, signal);
|
|
263
370
|
if (response.body)
|
|
@@ -284,6 +391,8 @@ export class LegacySseTransport {
|
|
|
284
391
|
pending = new Map();
|
|
285
392
|
readyPromise;
|
|
286
393
|
pumpError;
|
|
394
|
+
handlers;
|
|
395
|
+
sentTokens = new WeakMap();
|
|
287
396
|
constructor(config, options = {}) {
|
|
288
397
|
this.config = config;
|
|
289
398
|
this.options = options;
|
|
@@ -300,23 +409,46 @@ export class LegacySseTransport {
|
|
|
300
409
|
setProtocolVersion(version) {
|
|
301
410
|
this.protocolVersion = version;
|
|
302
411
|
}
|
|
303
|
-
|
|
412
|
+
setHandlers(handlers) {
|
|
413
|
+
this.handlers = handlers;
|
|
414
|
+
}
|
|
415
|
+
start(options = {}) {
|
|
416
|
+
if (this.closed)
|
|
417
|
+
return Promise.reject(new McpTransportError("closed", "MCP SSE transport closed."));
|
|
304
418
|
if (this.readyPromise)
|
|
305
419
|
return this.readyPromise;
|
|
306
420
|
this.readyPromise = new Promise((resolve, reject) => {
|
|
421
|
+
const { signal, dispose } = withTimeout(options.signal, options.timeoutMs ?? this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
307
422
|
let settled = false;
|
|
308
423
|
const onReady = () => {
|
|
309
424
|
if (settled)
|
|
310
425
|
return;
|
|
311
426
|
settled = true;
|
|
427
|
+
dispose();
|
|
428
|
+
signal.removeEventListener("abort", onAbort);
|
|
312
429
|
resolve();
|
|
313
430
|
};
|
|
314
431
|
const onFail = (error) => {
|
|
315
432
|
if (settled)
|
|
316
433
|
return;
|
|
317
434
|
settled = true;
|
|
435
|
+
dispose();
|
|
436
|
+
signal.removeEventListener("abort", onAbort);
|
|
318
437
|
reject(error);
|
|
319
438
|
};
|
|
439
|
+
const onAbort = () => {
|
|
440
|
+
const error = signal.reason instanceof McpTransportError
|
|
441
|
+
? signal.reason
|
|
442
|
+
: new McpTransportError("cancelled", "MCP SSE connection cancelled.");
|
|
443
|
+
this.pumpError = error;
|
|
444
|
+
this.streamController.abort(error);
|
|
445
|
+
onFail(error);
|
|
446
|
+
};
|
|
447
|
+
if (signal.aborted) {
|
|
448
|
+
onAbort();
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
320
452
|
this.pump(onReady, onFail).catch((error) => onFail(mapFetchError(error)));
|
|
321
453
|
});
|
|
322
454
|
return this.readyPromise;
|
|
@@ -325,19 +457,21 @@ export class LegacySseTransport {
|
|
|
325
457
|
const base = { accept };
|
|
326
458
|
if (this.protocolVersion)
|
|
327
459
|
base[PROTOCOL_HEADER] = this.protocolVersion;
|
|
328
|
-
const authHeaders = this.options.authProvider
|
|
329
|
-
? await this.options.authProvider.headers()
|
|
330
|
-
: {};
|
|
460
|
+
const authHeaders = this.options.authProvider ? await this.options.authProvider.headers() : {};
|
|
331
461
|
return { ...base, ...authHeaders, ...this.config.headers };
|
|
332
462
|
}
|
|
333
463
|
async openStream() {
|
|
334
|
-
const headers = await this.streamHeaders("text/event-stream");
|
|
335
|
-
|
|
464
|
+
const headers = await awaitMcpOperation(this.streamHeaders("text/event-stream"), this.streamController.signal);
|
|
465
|
+
const response = await this.fetchImpl(this.config.url, {
|
|
336
466
|
method: "GET",
|
|
337
467
|
headers,
|
|
338
468
|
redirect: "manual",
|
|
339
469
|
signal: this.streamController.signal,
|
|
340
470
|
});
|
|
471
|
+
const authorization = new Headers(headers).get("authorization");
|
|
472
|
+
if (authorization)
|
|
473
|
+
this.sentTokens.set(response, authorization.replace(/^Bearer\s+/i, ""));
|
|
474
|
+
return response;
|
|
341
475
|
}
|
|
342
476
|
async openAuthorizedStream() {
|
|
343
477
|
let response = await this.openStream();
|
|
@@ -348,7 +482,11 @@ export class LegacySseTransport {
|
|
|
348
482
|
const challenge = parseWwwAuthenticate(response.headers.get("www-authenticate"));
|
|
349
483
|
if (response.body)
|
|
350
484
|
await response.body.cancel().catch(() => undefined);
|
|
351
|
-
const refreshed = await provider.onUnauthorized(challenge
|
|
485
|
+
const refreshed = await provider.onUnauthorized(challenge, {
|
|
486
|
+
interactive: false,
|
|
487
|
+
signal: this.streamController.signal,
|
|
488
|
+
rejectedToken: this.sentTokens.get(response),
|
|
489
|
+
});
|
|
352
490
|
if (!refreshed)
|
|
353
491
|
return response;
|
|
354
492
|
response = await this.openStream();
|
|
@@ -369,8 +507,7 @@ export class LegacySseTransport {
|
|
|
369
507
|
return;
|
|
370
508
|
}
|
|
371
509
|
const reader = response.body.getReader();
|
|
372
|
-
const sse = new SseDecoder();
|
|
373
|
-
let total = 0;
|
|
510
|
+
const sse = new SseDecoder(this.maxBytes);
|
|
374
511
|
try {
|
|
375
512
|
while (true) {
|
|
376
513
|
const { done, value } = await reader.read();
|
|
@@ -378,15 +515,14 @@ export class LegacySseTransport {
|
|
|
378
515
|
break;
|
|
379
516
|
if (!value)
|
|
380
517
|
continue;
|
|
381
|
-
total += value.byteLength;
|
|
382
|
-
if (total > this.maxBytes) {
|
|
383
|
-
throw new McpTransportError("too-large", "MCP SSE stream exceeded the size limit.");
|
|
384
|
-
}
|
|
385
518
|
for (const event of sse.push(value)) {
|
|
386
519
|
this.handleEvent(event.event, event.data, onReady);
|
|
387
520
|
}
|
|
388
521
|
}
|
|
389
|
-
|
|
522
|
+
const closed = new McpTransportError("closed", "MCP SSE stream closed.");
|
|
523
|
+
this.pumpError = closed;
|
|
524
|
+
onFail(closed);
|
|
525
|
+
this.failAll(closed);
|
|
390
526
|
}
|
|
391
527
|
catch (error) {
|
|
392
528
|
const mapped = mapFetchError(error);
|
|
@@ -401,17 +537,29 @@ export class LegacySseTransport {
|
|
|
401
537
|
handleEvent(event, data, onReady) {
|
|
402
538
|
if (event === "endpoint") {
|
|
403
539
|
try {
|
|
404
|
-
|
|
540
|
+
const endpoint = new URL(data, this.config.url);
|
|
541
|
+
if (endpoint.origin !== new URL(this.config.url).origin ||
|
|
542
|
+
endpoint.username ||
|
|
543
|
+
endpoint.password) {
|
|
544
|
+
throw new McpTransportError("protocol", "MCP SSE announced an endpoint on another origin; credentials cannot be forwarded there.");
|
|
545
|
+
}
|
|
546
|
+
this.endpointUrl = endpoint.toString();
|
|
405
547
|
onReady();
|
|
406
548
|
}
|
|
407
|
-
catch {
|
|
408
|
-
|
|
549
|
+
catch (error) {
|
|
550
|
+
throw error instanceof McpTransportError
|
|
551
|
+
? error
|
|
552
|
+
: new McpTransportError("protocol", "MCP SSE announced an invalid endpoint.");
|
|
409
553
|
}
|
|
410
554
|
return;
|
|
411
555
|
}
|
|
412
556
|
const message = parseMessage(data);
|
|
413
|
-
if (!message
|
|
557
|
+
if (!message)
|
|
414
558
|
return;
|
|
559
|
+
if (!isJsonRpcResponse(message)) {
|
|
560
|
+
void dispatchServerMessage(message, this.handlers, (reply) => this.postMessage(JSON.stringify(reply), this.streamController.signal)).catch(() => undefined);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
415
563
|
const numericId = typeof message.id === "number" ? message.id : Number(message.id);
|
|
416
564
|
if (!Number.isFinite(numericId))
|
|
417
565
|
return;
|
|
@@ -433,29 +581,35 @@ export class LegacySseTransport {
|
|
|
433
581
|
const base = { "content-type": "application/json" };
|
|
434
582
|
if (this.protocolVersion)
|
|
435
583
|
base[PROTOCOL_HEADER] = this.protocolVersion;
|
|
436
|
-
const authHeaders = this.options.authProvider
|
|
437
|
-
? await this.options.authProvider.headers()
|
|
438
|
-
: {};
|
|
584
|
+
const authHeaders = this.options.authProvider ? await this.options.authProvider.headers() : {};
|
|
439
585
|
return { ...base, ...authHeaders, ...this.config.headers };
|
|
440
586
|
}
|
|
441
587
|
async sendPost(endpoint, body, signal) {
|
|
442
|
-
const headers = await this.postHeaders();
|
|
443
|
-
|
|
588
|
+
const headers = await awaitMcpOperation(this.postHeaders(), signal);
|
|
589
|
+
const response = await this.fetchImpl(endpoint, {
|
|
444
590
|
method: "POST",
|
|
445
591
|
headers,
|
|
446
592
|
body,
|
|
447
593
|
signal,
|
|
448
594
|
redirect: "manual",
|
|
449
595
|
});
|
|
596
|
+
const authorization = new Headers(headers).get("authorization");
|
|
597
|
+
if (authorization)
|
|
598
|
+
this.sentTokens.set(response, authorization.replace(/^Bearer\s+/i, ""));
|
|
599
|
+
return response;
|
|
450
600
|
}
|
|
451
|
-
async retryPostUnauthorized(response, resend) {
|
|
601
|
+
async retryPostUnauthorized(response, resend, signal) {
|
|
452
602
|
const provider = this.options.authProvider;
|
|
453
603
|
if (response.status !== 401 || !provider)
|
|
454
604
|
return response;
|
|
455
605
|
const challenge = parseWwwAuthenticate(response.headers.get("www-authenticate"));
|
|
456
606
|
if (response.body)
|
|
457
607
|
await response.body.cancel().catch(() => undefined);
|
|
458
|
-
const refreshed = await provider.onUnauthorized(challenge
|
|
608
|
+
const refreshed = await provider.onUnauthorized(challenge, {
|
|
609
|
+
interactive: false,
|
|
610
|
+
signal,
|
|
611
|
+
rejectedToken: this.sentTokens.get(response),
|
|
612
|
+
});
|
|
459
613
|
if (!refreshed)
|
|
460
614
|
return response;
|
|
461
615
|
const retried = await resend();
|
|
@@ -470,7 +624,7 @@ export class LegacySseTransport {
|
|
|
470
624
|
const resend = () => this.sendPost(endpoint, body, signal);
|
|
471
625
|
let response = await resend();
|
|
472
626
|
await rejectRedirect(response);
|
|
473
|
-
response = await this.retryPostUnauthorized(response, resend);
|
|
627
|
+
response = await this.retryPostUnauthorized(response, resend, signal);
|
|
474
628
|
if (response.body)
|
|
475
629
|
await response.body.cancel().catch(() => undefined);
|
|
476
630
|
if (!response.ok)
|
|
@@ -479,19 +633,28 @@ export class LegacySseTransport {
|
|
|
479
633
|
async request(message, options = {}) {
|
|
480
634
|
if (this.closed)
|
|
481
635
|
throw new McpTransportError("closed", "MCP SSE transport closed.");
|
|
482
|
-
await this.start();
|
|
636
|
+
await this.start(options);
|
|
483
637
|
if (this.pumpError)
|
|
484
638
|
throw this.pumpError;
|
|
485
639
|
const id = this.nextId++;
|
|
486
640
|
const framed = { ...message, id };
|
|
487
641
|
const timeoutMs = options.timeoutMs ?? this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
488
|
-
const
|
|
642
|
+
const parent = options.signal
|
|
643
|
+
? AbortSignal.any([options.signal, this.streamController.signal])
|
|
644
|
+
: this.streamController.signal;
|
|
645
|
+
const { signal, dispose } = withTimeout(parent, timeoutMs);
|
|
489
646
|
return await new Promise((resolve, reject) => {
|
|
490
647
|
const onAbort = () => {
|
|
491
648
|
const entry = this.pending.get(id);
|
|
492
649
|
if (entry) {
|
|
493
650
|
this.pending.delete(id);
|
|
494
651
|
entry.dispose();
|
|
652
|
+
if (message.method !== "initialize") {
|
|
653
|
+
void this.notify(createNotification("notifications/cancelled", {
|
|
654
|
+
requestId: id,
|
|
655
|
+
reason: "Client stopped waiting for this request.",
|
|
656
|
+
}), { timeoutMs: 1_000 }).catch(() => undefined);
|
|
657
|
+
}
|
|
495
658
|
}
|
|
496
659
|
const reason = signal.reason;
|
|
497
660
|
reject(reason instanceof McpTransportError
|
|
@@ -515,16 +678,21 @@ export class LegacySseTransport {
|
|
|
515
678
|
this.pending.delete(id);
|
|
516
679
|
entry.dispose();
|
|
517
680
|
}
|
|
518
|
-
reject(mapFetchError(error));
|
|
681
|
+
reject(mapFetchError(error, signal));
|
|
519
682
|
});
|
|
520
683
|
});
|
|
521
684
|
}
|
|
522
685
|
async notify(message, options = {}) {
|
|
523
686
|
if (this.closed)
|
|
524
687
|
throw new McpTransportError("closed", "MCP SSE transport closed.");
|
|
525
|
-
await this.start();
|
|
688
|
+
await this.start(options);
|
|
689
|
+
if (this.pumpError)
|
|
690
|
+
throw this.pumpError;
|
|
526
691
|
const timeoutMs = options.timeoutMs ?? this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
527
|
-
const
|
|
692
|
+
const parent = options.signal
|
|
693
|
+
? AbortSignal.any([options.signal, this.streamController.signal])
|
|
694
|
+
: this.streamController.signal;
|
|
695
|
+
const { signal, dispose } = withTimeout(parent, timeoutMs);
|
|
528
696
|
try {
|
|
529
697
|
await this.postMessage(JSON.stringify(message), signal);
|
|
530
698
|
}
|