reasonix 0.11.1 → 0.11.3

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/dist/index.d.ts CHANGED
@@ -3134,6 +3134,68 @@ declare class SseTransport implements McpTransport {
3134
3134
  private markClosed;
3135
3135
  }
3136
3136
 
3137
+ /**
3138
+ * Streamable HTTP transport for MCP (spec version 2025-03-26).
3139
+ *
3140
+ * Wire shape (single endpoint, no separate POST URL handshake):
3141
+ *
3142
+ * 1. Client POSTs each outgoing JSON-RPC frame to the endpoint with
3143
+ * `Accept: application/json, text/event-stream`. The server picks
3144
+ * ONE of three responses:
3145
+ * a. `202 Accepted`, no body → notification or response
3146
+ * was accepted; nothing more to deliver.
3147
+ * b. `200 OK`, `Content-Type: application/json` → body is a
3148
+ * single JSON-RPC response (or batch). Connection closes.
3149
+ * c. `200 OK`, `Content-Type: text/event-stream` → an SSE
3150
+ * stream of `event: message` frames carrying responses,
3151
+ * server-initiated requests, and notifications. Stream may
3152
+ * close after the matching response or stay open longer.
3153
+ * 2. The server may include `Mcp-Session-Id: <opaque>` on the response
3154
+ * to `initialize`. Client echoes that header on every subsequent
3155
+ * request. A 404 on a request with a session id means the session
3156
+ * expired — caller must reinitialize.
3157
+ *
3158
+ * Compared to 2024-11-05 HTTP+SSE:
3159
+ * - No two-endpoint dance (no `event: endpoint` handshake).
3160
+ * - Replies arrive on the POST response, not on a separate GET stream.
3161
+ * - Session continuity is explicit (`Mcp-Session-Id`), not implicit.
3162
+ *
3163
+ * Not yet implemented in this transport (acceptable for v1):
3164
+ * - Long-lived GET stream for unsolicited server-initiated frames
3165
+ * (sampling requests, etc.). Most MCP servers we care about today
3166
+ * don't issue server-initiated requests, and POST-only handles
3167
+ * full request/response/notification traffic. Add when a real
3168
+ * server we're integrating against needs it.
3169
+ * - Resumability via `Last-Event-ID` on reconnect.
3170
+ */
3171
+
3172
+ interface StreamableHttpTransportOptions {
3173
+ /** Streamable HTTP endpoint URL, e.g. `https://mcp.example.com/mcp`. */
3174
+ url: string;
3175
+ /** Extra headers sent on every request (e.g. `Authorization`). */
3176
+ headers?: Record<string, string>;
3177
+ }
3178
+ declare class StreamableHttpTransport implements McpTransport {
3179
+ private readonly url;
3180
+ private readonly extraHeaders;
3181
+ private readonly queue;
3182
+ private readonly waiters;
3183
+ private readonly controller;
3184
+ /** Session id minted by server on (typically) the initialize response. */
3185
+ private sessionId;
3186
+ private closed;
3187
+ /** Background SSE read-loops kicked off by send(); awaited on close(). */
3188
+ private readonly streams;
3189
+ constructor(opts: StreamableHttpTransportOptions);
3190
+ send(message: JsonRpcMessage): Promise<void>;
3191
+ messages(): AsyncIterableIterator<JsonRpcMessage>;
3192
+ close(): Promise<void>;
3193
+ /** Visible for tests — confirm session header round-trip. */
3194
+ getSessionId(): string | null;
3195
+ private consumeStream;
3196
+ private pushMessage;
3197
+ }
3198
+
3137
3199
  /**
3138
3200
  * Bridge: register an MCP server's tools into a Reasonix ToolRegistry.
3139
3201
  *
@@ -3263,11 +3325,13 @@ declare function truncateForModelByTokens(s: string, maxTokens: number): string;
3263
3325
  * Parse the `--mcp` CLI argument into a transport-tagged spec.
3264
3326
  *
3265
3327
  * Accepted forms:
3266
- * "name=command args..." → stdio, namespaced (tools prefixed with `name_`)
3267
- * "command args..." → stdio, anonymous
3268
- * "name=https://host/sse" → SSE, namespaced
3269
- * "https://host/sse" → SSE, anonymous
3270
- * ("http://" is also honored useful for local dev servers.)
3328
+ * "name=command args..." → stdio, namespaced (tools prefixed with `name_`)
3329
+ * "command args..." → stdio, anonymous
3330
+ * "name=https://host/sse" HTTP+SSE (2024-11-05), namespaced
3331
+ * "https://host/sse" HTTP+SSE (2024-11-05), anonymous
3332
+ * "name=streamable+https://host/mcp" Streamable HTTP (2025-03-26), namespaced
3333
+ * "streamable+https://host/mcp" → Streamable HTTP (2025-03-26), anonymous
3334
+ * ("http://" / "streamable+http://" also honored — useful for local dev.)
3271
3335
  *
3272
3336
  * The identifier regex before `=` is deliberately narrow
3273
3337
  * (`[a-zA-Z_][a-zA-Z0-9_]*`) so Windows drive letters ("C:\\...") and
@@ -3276,9 +3340,15 @@ declare function truncateForModelByTokens(s: string, maxTokens: number): string;
3276
3340
  * with `foo=...` as a bare command, they can wrap it in quotes inside the
3277
3341
  * shell command string.
3278
3342
  *
3279
- * Transport is selected solely by whether the body begins with `http://`
3280
- * or `https://`. Anything else is stdio including ws:// (unsupported)
3281
- * which will surface later as a spawn error, keeping the rule local.
3343
+ * Transport selection:
3344
+ * - body starts with `streamable+http(s)://` Streamable HTTP. The
3345
+ * `streamable+` prefix is stripped from the URL we hand the transport.
3346
+ * - body starts with `http(s)://` → HTTP+SSE (2024-11-05).
3347
+ * Default for plain http URLs to preserve back-compat with users who
3348
+ * already have `--mcp https://...` config entries pointed at SSE
3349
+ * servers; opt into Streamable HTTP explicitly.
3350
+ * - anything else → stdio (including ws://,
3351
+ * which will surface later as a spawn error).
3282
3352
  */
3283
3353
  interface StdioMcpSpec {
3284
3354
  transport: "stdio";
@@ -3295,7 +3365,13 @@ interface SseMcpSpec {
3295
3365
  /** Fully qualified SSE endpoint URL. */
3296
3366
  url: string;
3297
3367
  }
3298
- type McpSpec = StdioMcpSpec | SseMcpSpec;
3368
+ interface StreamableHttpMcpSpec {
3369
+ transport: "streamable-http";
3370
+ name: string | null;
3371
+ /** Fully qualified Streamable HTTP endpoint URL (no `streamable+` prefix). */
3372
+ url: string;
3373
+ }
3374
+ type McpSpec = StdioMcpSpec | SseMcpSpec | StreamableHttpMcpSpec;
3299
3375
  declare function parseMcpSpec(input: string): McpSpec;
3300
3376
 
3301
3377
  /**
@@ -3795,4 +3871,4 @@ declare function aggregateUsage(records: UsageRecord[], opts?: AggregateOptions)
3795
3871
  /** File-size helper for the stats header — "1.2 MB" etc. Returns "" if missing. */
3796
3872
  declare function formatLogSize(path?: string): string;
3797
3873
 
3798
- export { AT_MENTION_PATTERN, AT_PICKER_PREFIX, type AggregateOptions, AppendOnlyLog, type AppendUsageInput, type ApplyResult, type ApplyStatus, type AtMentionExpansion, type AtMentionOptions, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, type ChoiceOption, ChoiceRequestedError, type ChoiceToolOptions, DEFAULT_AT_MENTION_MAX_BYTES, DEFAULT_MAX_RESULT_CHARS, DEFAULT_MAX_RESULT_TOKENS, DEFAULT_PICKER_IGNORE_DIRS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EditBlock, type EditSnapshot, type EventRole, type FileWithStats, type FilesystemToolsOptions, type FlattenDecision, type FlattenOptions, type GetLatestVersionOptions, type GetPromptResult, HOOK_EVENTS, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME, type HarvestOptions, type HookConfig, type HookEvent, type HookOutcome, type HookPayload, type HookReport, type HookScope, type HookSettings, type HookSpawnInput, type HookSpawnResult, type HookSpawner, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, LATEST_CACHE_TTL_MS, LATEST_FETCH_TIMEOUT_MS, type ListFilesOptions, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoadHookSettingsOptions, type LoopEvent, MCP_PROTOCOL_VERSION, MEMORY_INDEX_FILE, MEMORY_INDEX_MAX_CHARS, McpClient, type McpClientOptions, type McpContentBlock, type McpProgressHandler, type McpProgressInfo, type McpPrompt, type McpPromptArgument, type McpPromptMessage, type McpPromptResourceBlock, type McpResource, type McpResourceContents, type McpResourceContentsBlob, type McpResourceContentsText, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type MemoryEntry, type MemoryScope, MemoryStore, type MemoryStoreOptions, type MemoryToolsOptions, type MemoryType, type WriteInput as MemoryWriteInput, NeedsConfirmationError, PROJECT_MEMORY_FILE, PROJECT_MEMORY_MAX_CHARS, type PageContent, type PickerCandidate, PlanCheckpointError, PlanProposedError, PlanRevisionProposedError, type PlanStep, type PlanStepRisk, type PlanToolOptions, type ProgressNotificationParams, type ProjectMemory, type RankPickerOptions, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type ResolvedHook, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, type RunHooksOptions, type ScavengeOptions, type ScavengeResult, type SearchResult, type SectionResult, type SessionInfo, SessionStats, type SessionSummary, type ShellToolsOptions, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, type StepCompletion, StormBreaker, type StreamChunk, type SubagentEvent, type SubagentSink, type SubagentToolOptions, type ToolCall, type ToolCallContext, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, USER_MEMORY_DIR, Usage, type UsageAggregate, type UsageBucket, type UsageRecord, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, aggregateUsage, analyzeSchema, appendSessionMessage, appendUsage, applyEditBlock, applyEditBlocks, applyMemoryStack, applyProjectMemory, applyUserMemory, bridgeMcpTools, bucketCacheHitRatio, bucketSavingsFraction, claudeEquivalentCost, codeSystemPrompt, compareVersions, computeReplayStats, costUsd, decideOutcome, defaultConfigPath, defaultSelector, defaultUsageLogPath, deleteSession, detectAtPicker, detectShellOperator, diffTranscripts, emptyPlanState, expandAtMentions, fetchWithRetry, fixToolCallPairing, flattenMcpResult, flattenSchema, forkRegistryExcluding, formatCommandResult, formatHookOutcomeMessage, formatLogSize, formatLoopError, formatSearchResults, getLatestVersion, globalSettingsPath, harvest, healLoadedMessages, healLoadedMessagesByTokens, htmlToText, injectPowerShellUtf8, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isNpxInstall, isPlanStateEmpty, isPlausibleKey, listFilesSync, listFilesWithStatsSync, listSessions, loadApiKey, loadDotenv, loadHooks, loadSessionMessages, matchesTool, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, prepareSpawn, projectHash, projectSettingsPath, quoteForCmdExe, rankPickerCandidates, readConfig, readProjectMemory, readTranscript, readUsageLog, recordFromLoopEvent, redactKey, registerChoiceTool, registerFilesystemTools, registerMemoryTools, registerPlanTool, registerShellTools, registerSubagentTool, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, resolveExecutable, restoreSnapshots, runBranches, runCommand, runHooks, sanitizeMemoryName, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, truncateForModelByTokens, webFetch, webSearch, withUtf8Codepage, writeConfig, writeMeta, writeRecord };
3874
+ export { AT_MENTION_PATTERN, AT_PICKER_PREFIX, type AggregateOptions, AppendOnlyLog, type AppendUsageInput, type ApplyResult, type ApplyStatus, type AtMentionExpansion, type AtMentionOptions, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, type ChoiceOption, ChoiceRequestedError, type ChoiceToolOptions, DEFAULT_AT_MENTION_MAX_BYTES, DEFAULT_MAX_RESULT_CHARS, DEFAULT_MAX_RESULT_TOKENS, DEFAULT_PICKER_IGNORE_DIRS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EditBlock, type EditSnapshot, type EventRole, type FileWithStats, type FilesystemToolsOptions, type FlattenDecision, type FlattenOptions, type GetLatestVersionOptions, type GetPromptResult, HOOK_EVENTS, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME, type HarvestOptions, type HookConfig, type HookEvent, type HookOutcome, type HookPayload, type HookReport, type HookScope, type HookSettings, type HookSpawnInput, type HookSpawnResult, type HookSpawner, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, LATEST_CACHE_TTL_MS, LATEST_FETCH_TIMEOUT_MS, type ListFilesOptions, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoadHookSettingsOptions, type LoopEvent, MCP_PROTOCOL_VERSION, MEMORY_INDEX_FILE, MEMORY_INDEX_MAX_CHARS, McpClient, type McpClientOptions, type McpContentBlock, type McpProgressHandler, type McpProgressInfo, type McpPrompt, type McpPromptArgument, type McpPromptMessage, type McpPromptResourceBlock, type McpResource, type McpResourceContents, type McpResourceContentsBlob, type McpResourceContentsText, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type MemoryEntry, type MemoryScope, MemoryStore, type MemoryStoreOptions, type MemoryToolsOptions, type MemoryType, type WriteInput as MemoryWriteInput, NeedsConfirmationError, PROJECT_MEMORY_FILE, PROJECT_MEMORY_MAX_CHARS, type PageContent, type PickerCandidate, PlanCheckpointError, PlanProposedError, PlanRevisionProposedError, type PlanStep, type PlanStepRisk, type PlanToolOptions, type ProgressNotificationParams, type ProjectMemory, type RankPickerOptions, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type ResolvedHook, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, type RunHooksOptions, type ScavengeOptions, type ScavengeResult, type SearchResult, type SectionResult, type SessionInfo, SessionStats, type SessionSummary, type ShellToolsOptions, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, type StepCompletion, StormBreaker, type StreamChunk, type StreamableHttpMcpSpec, StreamableHttpTransport, type StreamableHttpTransportOptions, type SubagentEvent, type SubagentSink, type SubagentToolOptions, type ToolCall, type ToolCallContext, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, USER_MEMORY_DIR, Usage, type UsageAggregate, type UsageBucket, type UsageRecord, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, aggregateUsage, analyzeSchema, appendSessionMessage, appendUsage, applyEditBlock, applyEditBlocks, applyMemoryStack, applyProjectMemory, applyUserMemory, bridgeMcpTools, bucketCacheHitRatio, bucketSavingsFraction, claudeEquivalentCost, codeSystemPrompt, compareVersions, computeReplayStats, costUsd, decideOutcome, defaultConfigPath, defaultSelector, defaultUsageLogPath, deleteSession, detectAtPicker, detectShellOperator, diffTranscripts, emptyPlanState, expandAtMentions, fetchWithRetry, fixToolCallPairing, flattenMcpResult, flattenSchema, forkRegistryExcluding, formatCommandResult, formatHookOutcomeMessage, formatLogSize, formatLoopError, formatSearchResults, getLatestVersion, globalSettingsPath, harvest, healLoadedMessages, healLoadedMessagesByTokens, htmlToText, injectPowerShellUtf8, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isNpxInstall, isPlanStateEmpty, isPlausibleKey, listFilesSync, listFilesWithStatsSync, listSessions, loadApiKey, loadDotenv, loadHooks, loadSessionMessages, matchesTool, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, prepareSpawn, projectHash, projectSettingsPath, quoteForCmdExe, rankPickerCandidates, readConfig, readProjectMemory, readTranscript, readUsageLog, recordFromLoopEvent, redactKey, registerChoiceTool, registerFilesystemTools, registerMemoryTools, registerPlanTool, registerShellTools, registerSubagentTool, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, resolveExecutable, restoreSnapshots, runBranches, runCommand, runHooks, sanitizeMemoryName, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, truncateForModelByTokens, webFetch, webSearch, withUtf8Codepage, writeConfig, writeMeta, writeRecord };
package/dist/index.js CHANGED
@@ -7325,6 +7325,159 @@ var SseTransport = class {
7325
7325
  }
7326
7326
  };
7327
7327
 
7328
+ // src/mcp/streamable-http.ts
7329
+ import { createParser as createParser3 } from "eventsource-parser";
7330
+ var SESSION_HEADER = "mcp-session-id";
7331
+ var StreamableHttpTransport = class {
7332
+ url;
7333
+ extraHeaders;
7334
+ queue = [];
7335
+ waiters = [];
7336
+ controller = new AbortController();
7337
+ /** Session id minted by server on (typically) the initialize response. */
7338
+ sessionId = null;
7339
+ closed = false;
7340
+ /** Background SSE read-loops kicked off by send(); awaited on close(). */
7341
+ streams = /* @__PURE__ */ new Set();
7342
+ constructor(opts) {
7343
+ this.url = opts.url;
7344
+ this.extraHeaders = opts.headers ?? {};
7345
+ }
7346
+ async send(message) {
7347
+ if (this.closed) throw new Error("MCP Streamable HTTP transport is closed");
7348
+ const headers = {
7349
+ "content-type": "application/json",
7350
+ // Both accepted — server picks. application/json first signals a
7351
+ // mild preference for the simpler shape when the response is a
7352
+ // single message.
7353
+ accept: "application/json, text/event-stream",
7354
+ ...this.extraHeaders
7355
+ };
7356
+ if (this.sessionId !== null) headers["mcp-session-id"] = this.sessionId;
7357
+ let res;
7358
+ try {
7359
+ res = await fetch(this.url, {
7360
+ method: "POST",
7361
+ headers,
7362
+ body: JSON.stringify(message),
7363
+ signal: this.controller.signal
7364
+ });
7365
+ } catch (err) {
7366
+ throw new Error(`MCP Streamable HTTP POST ${this.url} failed: ${err.message}`);
7367
+ }
7368
+ const serverSessionId = res.headers.get(SESSION_HEADER);
7369
+ if (serverSessionId && this.sessionId === null) {
7370
+ this.sessionId = serverSessionId;
7371
+ }
7372
+ if (res.status === 404 && this.sessionId !== null) {
7373
+ await res.body?.cancel().catch(() => void 0);
7374
+ throw new Error(
7375
+ `MCP Streamable HTTP session expired (server returned 404 with Mcp-Session-Id "${this.sessionId}"). Reinitialize the client.`
7376
+ );
7377
+ }
7378
+ if (!res.ok) {
7379
+ const body = await res.text().catch(() => "");
7380
+ throw new Error(
7381
+ `MCP Streamable HTTP POST ${this.url} \u2192 ${res.status} ${res.statusText}${body ? `: ${body}` : ""}`
7382
+ );
7383
+ }
7384
+ if (res.status === 202) {
7385
+ await res.body?.cancel().catch(() => void 0);
7386
+ return;
7387
+ }
7388
+ const ct = (res.headers.get("content-type") ?? "").toLowerCase();
7389
+ if (ct.includes("application/json")) {
7390
+ let parsed;
7391
+ try {
7392
+ parsed = await res.json();
7393
+ } catch (err) {
7394
+ throw new Error(`MCP Streamable HTTP body wasn't valid JSON: ${err.message}`);
7395
+ }
7396
+ if (Array.isArray(parsed)) {
7397
+ for (const item of parsed) this.pushMessage(item);
7398
+ } else {
7399
+ this.pushMessage(parsed);
7400
+ }
7401
+ return;
7402
+ }
7403
+ if (ct.includes("text/event-stream")) {
7404
+ if (!res.body) {
7405
+ throw new Error("MCP Streamable HTTP SSE response had no body");
7406
+ }
7407
+ const stream = this.consumeStream(res.body);
7408
+ this.streams.add(stream);
7409
+ stream.finally(() => this.streams.delete(stream));
7410
+ return;
7411
+ }
7412
+ await res.body?.cancel().catch(() => void 0);
7413
+ }
7414
+ async *messages() {
7415
+ while (true) {
7416
+ if (this.queue.length > 0) {
7417
+ yield this.queue.shift();
7418
+ continue;
7419
+ }
7420
+ if (this.closed) return;
7421
+ const next = await new Promise((resolve9) => {
7422
+ this.waiters.push(resolve9);
7423
+ });
7424
+ if (next === null) return;
7425
+ yield next;
7426
+ }
7427
+ }
7428
+ async close() {
7429
+ if (this.closed) return;
7430
+ this.closed = true;
7431
+ while (this.waiters.length > 0) this.waiters.shift()(null);
7432
+ try {
7433
+ this.controller.abort();
7434
+ } catch {
7435
+ }
7436
+ await Promise.allSettled(Array.from(this.streams));
7437
+ }
7438
+ /** Visible for tests — confirm session header round-trip. */
7439
+ getSessionId() {
7440
+ return this.sessionId;
7441
+ }
7442
+ // ---------- internals ----------
7443
+ async consumeStream(body) {
7444
+ const parser = createParser3({
7445
+ onEvent: (ev) => {
7446
+ const type = ev.event ?? "message";
7447
+ if (type !== "message") return;
7448
+ try {
7449
+ const parsed = JSON.parse(ev.data);
7450
+ this.pushMessage(parsed);
7451
+ } catch {
7452
+ }
7453
+ }
7454
+ });
7455
+ const decoder = new TextDecoder();
7456
+ try {
7457
+ for await (const chunk of body) {
7458
+ if (this.closed) break;
7459
+ parser.feed(decoder.decode(chunk, { stream: true }));
7460
+ }
7461
+ } catch (err) {
7462
+ if (!this.closed) {
7463
+ this.pushMessage({
7464
+ jsonrpc: "2.0",
7465
+ id: null,
7466
+ error: {
7467
+ code: -32e3,
7468
+ message: `Streamable HTTP stream error: ${err.message}`
7469
+ }
7470
+ });
7471
+ }
7472
+ }
7473
+ }
7474
+ pushMessage(msg) {
7475
+ const waiter = this.waiters.shift();
7476
+ if (waiter) waiter(msg);
7477
+ else this.queue.push(msg);
7478
+ }
7479
+ };
7480
+
7328
7481
  // src/mcp/shell-split.ts
7329
7482
  function shellSplit(input) {
7330
7483
  const tokens = [];
@@ -7377,6 +7530,7 @@ function shellSplit(input) {
7377
7530
  // src/mcp/spec.ts
7378
7531
  var NAME_PREFIX = /^([a-zA-Z_][a-zA-Z0-9_]*)=(.*)$/;
7379
7532
  var HTTP_URL = /^https?:\/\//i;
7533
+ var STREAMABLE_PREFIX = /^streamable\+(https?:\/\/.+)$/i;
7380
7534
  function parseMcpSpec(input) {
7381
7535
  const trimmed = input.trim();
7382
7536
  if (!trimmed) {
@@ -7388,6 +7542,10 @@ function parseMcpSpec(input) {
7388
7542
  if (!body) {
7389
7543
  throw new Error(`MCP spec has name but no command: ${input}`);
7390
7544
  }
7545
+ const streamMatch = STREAMABLE_PREFIX.exec(body);
7546
+ if (streamMatch) {
7547
+ return { transport: "streamable-http", name, url: streamMatch[1] };
7548
+ }
7391
7549
  if (HTTP_URL.test(body)) {
7392
7550
  return { transport: "sse", name, url: body };
7393
7551
  }
@@ -8112,6 +8270,7 @@ export {
8112
8270
  SseTransport,
8113
8271
  StdioTransport,
8114
8272
  StormBreaker,
8273
+ StreamableHttpTransport,
8115
8274
  ToolCallRepair,
8116
8275
  ToolRegistry,
8117
8276
  USER_MEMORY_DIR,