{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n  Middleware,\n  EventType,\n  type AbstractAgent,\n  type BaseEvent,\n  type Message,\n  type RunAgentInput,\n  type Tool,\n  type ToolCall,\n  type ToolCallResultEvent,\n} from \"@ag-ui/client\";\nimport { Observable, type Subscription } from \"rxjs\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n// Type-only: erased at compile time, so it never enters the runtime graph.\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\n/**\n * MCP Client configuration for HTTP (streamable) transport.\n */\nexport interface MCPClientConfigHTTP {\n  type: \"http\";\n  url: string;\n  headers?: Record<string, string>;\n  serverId?: string;\n}\n\n/**\n * MCP Client configuration for SSE transport.\n */\nexport interface MCPClientConfigSSE {\n  type: \"sse\";\n  url: string;\n  headers?: Record<string, string>;\n  serverId?: string;\n}\n\n/**\n * MCP Client configuration — one of the supported transports.\n */\nexport type MCPClientConfig = MCPClientConfigHTTP | MCPClientConfigSSE;\n\n/**\n * Maximum length of a tool name. Bounded by the strictest mainstream LLM\n * provider constraint (OpenAI function names: `^[a-zA-Z0-9_-]{1,64}$`),\n * which is also why `__` — not `:` or `/` — is used as the delimiter.\n */\nexport const MAX_TOOL_NAME_LENGTH = 64;\n\n/**\n * The namespace prefix applied to every MCP-sourced tool. Mirrors the\n * Claude Agent SDK convention: `mcp__{server}__{tool}`.\n */\nexport const MCP_TOOL_NAME_PREFIX = \"mcp\";\n\n/**\n * Default cap on the number of MCP tool-execution rounds in a single\n * `run()`. Prevents a runaway loop (and unbounded cost) if the model keeps\n * calling MCP tools forever.\n */\nexport const DEFAULT_MAX_ITERATIONS = 32;\n\n/**\n * Options for {@link MCPMiddleware}.\n */\nexport interface MCPMiddlewareOptions {\n  /**\n   * Maximum number of MCP tool-execution rounds before the middleware stops\n   * looping and lets the run finish. Defaults to {@link DEFAULT_MAX_ITERATIONS}.\n   */\n  maxIterations?: number;\n}\n\n/**\n * A tool resolved from an MCP server, carrying the metadata needed to map\n * the exposed (prefixed) name back to its origin. The mapping is kept as a\n * descriptor — never reconstructed by string-splitting the exposed name —\n * so server ids or tool names containing `__` can't corrupt the round-trip.\n */\nexport interface ResolvedMCPTool {\n  /** The (prefixed, possibly truncated/deduped) tool exposed to the agent. */\n  tool: Tool;\n  /** The original tool name as reported by the MCP server. */\n  originalName: string;\n  /** The server this tool came from. */\n  serverConfig: MCPClientConfig;\n}\n\n/**\n * Restrict a name segment to characters valid across LLM providers.\n */\nfunction sanitizeSegment(segment: string): string {\n  return segment.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\n/**\n * Build a unique, length-bounded, namespaced tool name.\n *\n * Shape: `mcp__{serverId}__{toolName}` (sanitized), truncated to\n * {@link MAX_TOOL_NAME_LENGTH}. If the truncated name collides with one\n * already in `used`, a `_N` suffix is appended (and the base re-truncated to\n * make room) until unique.\n */\nfunction makeUniqueToolName(\n  serverId: string,\n  toolName: string,\n  used: Set<string>,\n): string {\n  const base = `${MCP_TOOL_NAME_PREFIX}__${sanitizeSegment(serverId)}__${sanitizeSegment(toolName)}`;\n  let candidate = base.slice(0, MAX_TOOL_NAME_LENGTH);\n  if (!used.has(candidate)) {\n    return candidate;\n  }\n  for (let i = 1; ; i++) {\n    const suffix = `_${i}`;\n    candidate = base.slice(0, MAX_TOOL_NAME_LENGTH - suffix.length) + suffix;\n    if (!used.has(candidate)) {\n      return candidate;\n    }\n  }\n}\n\n/**\n * Collect assistant tool calls that have no corresponding `role: \"tool\"`\n * result message — i.e. the still-open tool calls.\n */\nfunction getOpenToolCalls(messages: Message[]): ToolCall[] {\n  const allToolCalls: ToolCall[] = [];\n  for (const message of messages) {\n    if (message.role === \"assistant\" && \"toolCalls\" in message && message.toolCalls) {\n      allToolCalls.push(...message.toolCalls);\n    }\n  }\n  const resolvedIds = new Set<string>();\n  for (const message of messages) {\n    if (message.role === \"tool\" && \"toolCallId\" in message) {\n      resolvedIds.add(message.toolCallId);\n    }\n  }\n  return allToolCalls.filter((tc) => !resolvedIds.has(tc.id));\n}\n\n/**\n * Close an MCP client without letting a `close()` failure escape — a throw\n * here would otherwise clobber the value being returned from the enclosing\n * `try`/`catch` (or abort the listing loop). Best-effort: log and move on.\n */\nasync function safeClose(client: Client | undefined): Promise<void> {\n  if (!client) return;\n  try {\n    await client.close();\n  } catch (error) {\n    console.error(\"[MCPMiddleware] Failed to close MCP client:\", error);\n  }\n}\n\n/**\n * Extract text content from an MCP `callTool` result, falling back to a JSON\n * stringification of the content when it isn't plain text.\n */\nfunction extractTextContent(mcpResult: unknown): string {\n  const result = mcpResult as { content?: unknown };\n  if (Array.isArray(result.content)) {\n    const text = result.content\n      .filter(\n        (c): c is { type: \"text\"; text: string } =>\n          !!c &&\n          typeof c === \"object\" &&\n          (c as { type?: unknown }).type === \"text\" &&\n          typeof (c as { text?: unknown }).text === \"string\",\n      )\n      .map((c) => c.text)\n      .join(\"\\n\");\n    return text || JSON.stringify(result.content);\n  }\n  return JSON.stringify(result.content ?? result);\n}\n\n/**\n * One MCP tool as returned by `listTools`, paired with the server it came\n * from. Cached on the middleware instance so we only hit the network once.\n */\ninterface ListedTool {\n  mcpTool: {\n    name: string;\n    description?: string;\n    inputSchema?: Record<string, unknown>;\n  };\n  serverConfig: MCPClientConfig;\n  serverId: string;\n}\n\n/**\n * AG-UI middleware that lists tools from one or more MCP servers, injects\n * them into the agent run (namespaced as `mcp__{server}__{tool}`), and\n * executes the resulting MCP tool calls server-side.\n *\n * Loop, on each agent `RUN_FINISHED`:\n *   - Find open tool calls (assistant calls without a result message).\n *   - Of those, execute the ones that target our injected MCP tools and emit\n *     a `TOOL_CALL_RESULT` for each.\n *   - If no open tool calls remain afterwards, start another run with the new\n *     result messages appended (same threadId, fresh runId).\n *   - If open tool calls still remain (e.g. frontend tools), stop and let the\n *     frontend resolve them.\n *\n * If a run produces no open tool calls targeting our MCP tools, the\n * middleware does not interfere at all — every event is forwarded verbatim.\n */\nexport class MCPMiddleware extends Middleware {\n  private readonly mcpServers: MCPClientConfig[];\n  private readonly maxIterations: number;\n  /**\n   * Lazily-populated cache of the full `listTools` result across every\n   * configured server. Populated on the first `run()` and reused for the\n   * lifetime of the instance — so listing happens exactly once per\n   * middleware instance, no matter how many runs come through.\n   */\n  private listingPromise: Promise<ListedTool[]> | null = null;\n\n  constructor(\n    mcpServers: MCPClientConfig[] = [],\n    options: MCPMiddlewareOptions = {},\n  ) {\n    super();\n    this.mcpServers = mcpServers;\n    // Clamp to a positive integer — a 0/negative/NaN cap would otherwise\n    // trip the runaway guard on the first round and silently disable tool\n    // execution entirely.\n    const requested = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n    this.maxIterations = Number.isFinite(requested)\n      ? Math.max(1, Math.floor(requested))\n      : DEFAULT_MAX_ITERATIONS;\n  }\n\n  run(input: RunAgentInput, next: AbstractAgent): Observable<BaseEvent> {\n    if (this.mcpServers.length === 0) {\n      return this.runNext(input, next);\n    }\n\n    return new Observable<BaseEvent>((subscriber) => {\n      let cancelled = false;\n      let activeSub: Subscription | undefined;\n      // Number of MCP tool-execution rounds performed so far in this run.\n      let toolRounds = 0;\n\n      // Run the agent once; on completion decide whether to execute MCP tool\n      // calls and loop. `toolMap` (exposed name -> origin) is built once and\n      // reused across iterations.\n      //\n      // Run-lifecycle policy: from the consumer's perspective, the entire\n      // tool-execution loop is presented as a SINGLE run. We forward the\n      // first run's `RUN_STARTED` and suppress every subsequent\n      // `RUN_STARTED`. We buffer *every* run's `RUN_FINISHED` (each one\n      // replacing the prior) and flush only the final one when the loop\n      // actually stops. This keeps any downstream consumer (or persistence\n      // layer) that treats `RUN_FINISHED` as \"the assistant turn is over\"\n      // from prematurely closing things between iterations.\n      //\n      // Why we sync `next.messages`: `runNextWithState` uses\n      // `defaultApplyEvents`, which seeds its `messages` from\n      // `agent.messages` (the downstream agent's persistent state) — NOT\n      // from `input.messages`. So passing tool results only via\n      // `runInput.messages` makes them visible to the LLM call but\n      // INVISIBLE to the next iteration's apply chain, which then sees the\n      // assistant tool call as still-open and the model re-emits it. The\n      // chained-agent proxy exposes `.messages` as a getter returning the\n      // underlying array reference, so mutating it via `.push` is the way\n      // to keep both the model and the apply chain in sync.\n      const runOnce = (\n        runInput: RunAgentInput,\n        toolMap: Map<string, ResolvedMCPTool>,\n        isContinuation: boolean,\n      ): void => {\n        let latestMessages: Message[] = runInput.messages;\n        let errored = false;\n        let bufferedRunFinished: BaseEvent | null = null;\n\n        activeSub = this.runNextWithState(runInput, next).subscribe({\n          next: ({ event, messages }) => {\n            latestMessages = messages;\n            if (event.type === EventType.RUN_ERROR) {\n              errored = true;\n              subscriber.next(event);\n              return;\n            }\n            if (event.type === EventType.RUN_FINISHED) {\n              // Always buffer; only flushed when the loop truly stops.\n              bufferedRunFinished = event;\n              return;\n            }\n            if (event.type === EventType.RUN_STARTED && isContinuation) {\n              // Hide continuation run boundary — consumer sees one run.\n              return;\n            }\n            subscriber.next(event);\n          },\n          error: (err) => subscriber.error(err),\n          complete: () => {\n            // Route any rejection from the async continuation back onto the\n            // stream — otherwise it becomes an unhandled rejection and the\n            // observable silently never completes.\n            onRunComplete(\n              runInput,\n              latestMessages,\n              toolMap,\n              errored,\n              bufferedRunFinished,\n            ).catch((err) => subscriber.error(err));\n          },\n        });\n      };\n\n      const onRunComplete = async (\n        runInput: RunAgentInput,\n        messages: Message[],\n        toolMap: Map<string, ResolvedMCPTool>,\n        errored: boolean,\n        bufferedRunFinished: BaseEvent | null,\n      ): Promise<void> => {\n        if (cancelled) return;\n\n        // The run errored — do not execute tools or loop; the RUN_ERROR has\n        // already been forwarded. There's no RUN_FINISHED to flush.\n        if (errored) {\n          subscriber.complete();\n          return;\n        }\n\n        const openCalls = getOpenToolCalls(messages);\n        const ourCalls = openCalls.filter((tc) => toolMap.has(tc.function.name));\n\n        // Nothing for us — flush the buffered RUN_FINISHED untouched and stop.\n        if (ourCalls.length === 0) {\n          if (bufferedRunFinished) subscriber.next(bufferedRunFinished);\n          subscriber.complete();\n          return;\n        }\n\n        // Runaway guard: flush RUN_FINISHED and stop without executing more.\n        if (toolRounds >= this.maxIterations) {\n          console.warn(\n            `[MCPMiddleware] Reached maxIterations (${this.maxIterations}); ` +\n              `leaving ${ourCalls.length} MCP tool call(s) unexecuted.`,\n          );\n          if (bufferedRunFinished) subscriber.next(bufferedRunFinished);\n          subscriber.complete();\n          return;\n        }\n        toolRounds++;\n\n        // Execute our MCP tool calls (in parallel), then emit results in\n        // their original order — *before* flushing the held RUN_FINISHED —\n        // so the stream stays valid under AG-UI verify.\n        const executed = await Promise.all(\n          ourCalls.map(async (tc) => {\n            const resolved = toolMap.get(tc.function.name)!;\n            const content = await this.executeToolCall(resolved, tc);\n            return { tc, content };\n          }),\n        );\n        if (cancelled) return;\n\n        const resultMessages: Message[] = [];\n        for (const { tc, content } of executed) {\n          const messageId = crypto.randomUUID();\n          const resultEvent: ToolCallResultEvent = {\n            type: EventType.TOOL_CALL_RESULT,\n            messageId,\n            toolCallId: tc.id,\n            content,\n            role: \"tool\",\n          };\n          subscriber.next(resultEvent);\n          resultMessages.push({\n            id: messageId,\n            role: \"tool\",\n            content,\n            toolCallId: tc.id,\n          });\n        }\n\n        const updatedMessages = [...messages, ...resultMessages];\n        const stillOpen = getOpenToolCalls(updatedMessages);\n\n        // Scenario 2: other (e.g. frontend) tool calls are still open — we\n        // don't trigger another run. Flush the buffered RUN_FINISHED and\n        // hand off to the frontend.\n        if (stillOpen.length > 0) {\n          if (bufferedRunFinished) subscriber.next(bufferedRunFinished);\n          subscriber.complete();\n          return;\n        }\n\n        // Sync our tool results into the downstream agent's persistent\n        // message state so the next iteration's `defaultApplyEvents` (which\n        // seeds from `agent.messages`, not `input.messages`) sees the tool\n        // calls as resolved instead of re-emitting them.\n        next.messages.push(...resultMessages);\n\n        // Scenario 1: everything is resolved — start a continuation run\n        // WITHOUT flushing RUN_FINISHED. The continuation's own RUN_STARTED\n        // will be suppressed by `runOnce`, and its RUN_FINISHED will be\n        // buffered (and only flushed when the loop truly stops). The\n        // consumer sees one seamless run.\n        runOnce(\n          { ...runInput, runId: crypto.randomUUID(), messages: updatedMessages },\n          toolMap,\n          true,\n        );\n      };\n\n      // Bootstrap: list tools once, inject, run.\n      void (async () => {\n        try {\n          const resolved = await this.resolveTools(\n            new Set(input.tools.map((t) => t.name)),\n          );\n          if (cancelled) return;\n          const toolMap = new Map<string, ResolvedMCPTool>(\n            resolved.map((r) => [r.tool.name, r]),\n          );\n          runOnce(\n            { ...input, tools: [...input.tools, ...resolved.map((r) => r.tool)] },\n            toolMap,\n            false,\n          );\n        } catch (err) {\n          subscriber.error(err);\n        }\n      })();\n\n      return () => {\n        cancelled = true;\n        activeSub?.unsubscribe();\n      };\n    });\n  }\n\n  /**\n   * Resolve injectable tool descriptors for this run. Listing is cached\n   * per-instance (see {@link listingPromise}); only the name resolution\n   * (prefix / truncate / dedupe) is recomputed per run, since dedupe needs\n   * the current `input.tools` as its seed.\n   */\n  private async resolveTools(\n    existingNames: Set<string>,\n  ): Promise<ResolvedMCPTool[]> {\n    const listed = await this.listAllTools();\n    const used = new Set(existingNames);\n    return listed.map((entry) => {\n      const name = makeUniqueToolName(entry.serverId, entry.mcpTool.name, used);\n      used.add(name);\n      return {\n        tool: {\n          name,\n          description: entry.mcpTool.description ?? \"\",\n          parameters: entry.mcpTool.inputSchema ?? {\n            type: \"object\",\n            properties: {},\n          },\n        },\n        originalName: entry.mcpTool.name,\n        serverConfig: entry.serverConfig,\n      };\n    });\n  }\n\n  /**\n   * List tools from every configured server, exactly once per instance. A\n   * server that fails to connect or list is logged and skipped — one bad\n   * server never blocks the other servers' tools. The failure is part of\n   * the cached result, so we don't keep retrying broken servers.\n   */\n  private listAllTools(): Promise<ListedTool[]> {\n    if (this.listingPromise === null) {\n      this.listingPromise = this.doListAllTools();\n    }\n    return this.listingPromise;\n  }\n\n  private async doListAllTools(): Promise<ListedTool[]> {\n    const listed: ListedTool[] = [];\n    let index = 0;\n    for (const serverConfig of this.mcpServers) {\n      const serverId = serverConfig.serverId ?? `server${index}`;\n      index++;\n\n      let client: Client | undefined;\n      try {\n        client = await this.connect(serverConfig);\n        const { tools } = await client.listTools();\n        for (const mcpTool of tools) {\n          listed.push({ mcpTool, serverConfig, serverId });\n        }\n      } catch (error) {\n        console.error(\n          `[MCPMiddleware] Failed to list tools from MCP server ${serverConfig.url}:`,\n          error,\n        );\n      } finally {\n        await safeClose(client);\n      }\n    }\n    return listed;\n  }\n\n  /**\n   * Execute a single MCP tool call against its origin server and return the\n   * result as text. Errors are caught and returned as the result content so\n   * the agentic loop can react rather than crash.\n   */\n  private async executeToolCall(\n    resolved: ResolvedMCPTool,\n    toolCall: ToolCall,\n  ): Promise<string> {\n    let args: Record<string, unknown> = {};\n    try {\n      args = toolCall.function.arguments\n        ? (JSON.parse(toolCall.function.arguments) as Record<string, unknown>)\n        : {};\n    } catch {\n      // Leave args empty if the model emitted malformed JSON, but surface it\n      // — running a tool with no arguments is rarely what the model intended.\n      console.warn(\n        `[MCPMiddleware] Malformed JSON arguments for ${resolved.originalName}; ` +\n          `executing with empty arguments.`,\n      );\n    }\n\n    let client: Client | undefined;\n    try {\n      client = await this.connect(resolved.serverConfig);\n      const result = await client.callTool({\n        name: resolved.originalName,\n        arguments: args,\n      });\n      return extractTextContent(result);\n    } catch (error) {\n      // The error is returned as the tool result so the agentic loop can\n      // react; also log it server-side so an operator has observability\n      // (the model-facing string is the only other trace of the failure).\n      console.error(\n        `[MCPMiddleware] Tool execution failed for ${resolved.originalName}:`,\n        error,\n      );\n      return `Error executing tool ${resolved.originalName}: ${String(error)}`;\n    } finally {\n      await safeClose(client);\n    }\n  }\n\n  /**\n   * Open a connected MCP client for a server config. If `headers` is set on\n   * the config, they're stamped on every outbound request via the\n   * transport's `requestInit`. This is the seam the runtime uses to forward\n   * per-request auth (e.g. `Authorization: Bearer …`, `X-Cpki-User-Id: …`):\n   * the middleware is constructed per request, so static headers in the\n   * config are effectively per-request.\n   *\n   * Caveat: for the SSE transport, `requestInit.headers` only applies to\n   * the POST channel — the SSE event stream uses `eventSourceInit`. For\n   * streamable HTTP (the typical case) it covers all traffic.\n   *\n   * The SSE transport is imported lazily so that `eventsource` — which it\n   * pulls in transitively, and which only some consumers ever need — stays out\n   * of the module graph unless an SSE server is actually configured. Under Bun\n   * a static import of it breaks at load time: `eventsource`'s `bun` export\n   * condition resolves to its ESM build, so the SDK's CJS `require` gets an\n   * async module back and throws.\n   */\n  private async connect(serverConfig: MCPClientConfig): Promise<Client> {\n    const opts = serverConfig.headers\n      ? { requestInit: { headers: serverConfig.headers } }\n      : undefined;\n    let transport: Transport;\n    if (serverConfig.type === \"sse\") {\n      const { SSEClientTransport } = await import(\n        \"@modelcontextprotocol/sdk/client/sse.js\"\n      );\n      transport = new SSEClientTransport(new URL(serverConfig.url), opts);\n    } else {\n      transport = new StreamableHTTPClientTransport(\n        new URL(serverConfig.url),\n        opts,\n      );\n    }\n    const client = new Client({\n      name: \"ag-ui-mcp-middleware\",\n      version: \"0.0.1\",\n    });\n    await client.connect(transport);\n    return client;\n  }\n}\n"],"mappings":"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"}