"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/mcp/index.ts
var mcp_exports = {};
__export(mcp_exports, {
  MCPClient: () => MCPClient,
  MCPConnectionError: () => MCPConnectionError,
  MCPNotConnectedError: () => MCPNotConnectedError,
  MCPResourceError: () => MCPResourceError,
  MCPToolCallError: () => MCPToolCallError,
  ToolRegistryBridge: () => ToolRegistryBridge,
  dispatchMcpToolUse: () => dispatchMcpToolUse
});
module.exports = __toCommonJS(mcp_exports);

// src/mcp/client.ts
var import_client = require("@modelcontextprotocol/sdk/client/index.js");

// src/mcp/errors.ts
var MCPNotConnectedError = class extends Error {
  clientName;
  constructor(clientName) {
    super(
      `MCPClient '${clientName}' is not connected. Call connect() first.`
    );
    this.name = "MCPNotConnectedError";
    this.clientName = clientName;
  }
};
var MCPConnectionError = class extends Error {
  clientName;
  constructor(clientName, message, options) {
    super(`MCPClient '${clientName}' failed to connect: ${message}`);
    this.name = "MCPConnectionError";
    this.clientName = clientName;
    if (options?.cause !== void 0) {
      this.cause = options.cause;
    }
  }
};
var MCPToolCallError = class extends Error {
  toolName;
  clientName;
  constructor(toolName, clientName, message, options) {
    super(
      `MCPClient '${clientName}' failed to call tool '${toolName}': ${message}`
    );
    this.name = "MCPToolCallError";
    this.toolName = toolName;
    this.clientName = clientName;
    if (options?.cause !== void 0) {
      this.cause = options.cause;
    }
  }
};
var MCPResourceError = class extends Error {
  uri;
  clientName;
  constructor(uri, clientName, message, options) {
    super(
      `MCPClient '${clientName}' failed to read resource '${uri}': ${message}`
    );
    this.name = "MCPResourceError";
    this.uri = uri;
    this.clientName = clientName;
    if (options?.cause !== void 0) {
      this.cause = options.cause;
    }
  }
};

// src/mcp/transport-http.ts
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
function createHttpTransport(config) {
  return new import_sse.SSEClientTransport(new URL(config.url), {
    requestInit: config.headers ? { headers: config.headers } : void 0
  });
}

// src/mcp/transport-stdio.ts
var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
function createStdioTransport(config) {
  return new import_stdio.StdioClientTransport({
    command: config.command,
    args: config.args ?? [],
    env: config.env
  });
}

// src/mcp/client.ts
var MCPClient = class {
  config;
  sdkClient = null;
  _connected = false;
  constructor(config) {
    this.config = config;
  }
  /** `true` after a successful `connect()` and before `close()`. */
  get connected() {
    return this._connected;
  }
  /** The client name advertised during the handshake. */
  get name() {
    return this.config.name;
  }
  /**
   * Establishes the connection to the MCP server and performs the
   * initialization handshake (capability negotiation).
   *
   * Must be called exactly once before any other method. Calling `connect()`
   * on an already-connected client is a no-op.
   *
   * @throws {MCPConnectionError} if the transport cannot be established or
   *   the server rejects the client's hello.
   */
  async connect() {
    if (this._connected) return;
    const sdkClient = new import_client.Client(
      { name: this.config.name, version: this.config.version },
      { capabilities: {} }
    );
    const transport = this.config.transport.type === "stdio" ? createStdioTransport(this.config.transport) : createHttpTransport(this.config.transport);
    try {
      await sdkClient.connect(transport);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      throw new MCPConnectionError(this.config.name, msg, { cause: err });
    }
    this.sdkClient = sdkClient;
    this._connected = true;
  }
  /**
   * Discovers the tools available on the connected MCP server.
   *
   * @throws {MCPNotConnectedError} if called before `connect()`.
   */
  async listTools() {
    this.assertConnected();
    const { tools } = await this.sdkClient.listTools();
    return (tools ?? []).map((t) => ({
      name: t.name,
      description: t.description,
      inputSchema: t.inputSchema ?? {}
    }));
  }
  /**
   * Invokes a tool on the connected MCP server.
   *
   * Returns the tool's result including its content blocks. If the tool
   * reports a failure (result.isError === true), that is returned normally —
   * the tool did run, it just indicated an error in its output.
   *
   * @throws {MCPNotConnectedError} if called before `connect()`.
   * @throws {MCPToolCallError} if the transport or RPC layer fails.
   */
  async callTool(name, args) {
    this.assertConnected();
    try {
      const result = await this.sdkClient.callTool({
        name,
        arguments: args
      });
      const r = result;
      return {
        content: r.content ?? [],
        isError: r.isError === true ? true : void 0
      };
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      throw new MCPToolCallError(name, this.config.name, msg, { cause: err });
    }
  }
  /**
   * Discovers the resources available on the connected MCP server.
   *
   * @throws {MCPNotConnectedError} if called before `connect()`.
   */
  async listResources() {
    this.assertConnected();
    const { resources } = await this.sdkClient.listResources();
    return (resources ?? []).map((r) => ({
      uri: r.uri,
      name: r.name,
      description: r.description,
      mimeType: r.mimeType
    }));
  }
  /**
   * Reads the contents of a resource by its URI.
   *
   * @throws {MCPNotConnectedError} if called before `connect()`.
   * @throws {MCPResourceError} if the transport or RPC layer fails.
   */
  async readResource(uri) {
    this.assertConnected();
    try {
      const result = await this.sdkClient.readResource({ uri });
      const contents = (result.contents ?? []).map(
        (c) => ({
          uri: c.uri,
          mimeType: c.mimeType,
          text: "text" in c ? c.text : void 0,
          blob: "blob" in c ? c.blob : void 0
        })
      );
      return { contents };
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      throw new MCPResourceError(uri, this.config.name, msg, { cause: err });
    }
  }
  /**
   * Discovers the prompt templates available on the connected MCP server.
   *
   * @throws {MCPNotConnectedError} if called before `connect()`.
   */
  async listPrompts() {
    this.assertConnected();
    const { prompts } = await this.sdkClient.listPrompts();
    return (prompts ?? []).map((p) => ({
      name: p.name,
      description: p.description,
      arguments: p.arguments?.map((a) => ({
        name: a.name,
        description: a.description,
        required: a.required
      }))
    }));
  }
  /**
   * Closes the connection to the MCP server.
   *
   * Safe to call multiple times — subsequent calls are no-ops.
   */
  async close() {
    if (!this._connected || !this.sdkClient) return;
    await this.sdkClient.close();
    this.sdkClient = null;
    this._connected = false;
  }
  // ─── private ───────────────────────────────────────────────────────────────
  assertConnected() {
    if (!this._connected) {
      throw new MCPNotConnectedError(this.config.name);
    }
  }
};

// src/mcp/tool-registry-bridge.ts
var ToolRegistryBridge = class {
  _registry;
  mcpTools = /* @__PURE__ */ new Map();
  constructor(registry) {
    this._registry = registry;
  }
  /** Access the underlying `ToolRegistry` for native tool operations. */
  get registry() {
    return this._registry;
  }
  /**
   * Discovers all tools on the connected MCP server and registers them with
   * the bridge. Existing registrations for the same tool name are overwritten
   * (last-registered wins), which matches the behaviour of hot-reloading an
   * MCP server after it has updated its tool list.
   *
   * @returns The number of tools registered from this server.
   *
   * @throws if `client.listTools()` fails (the client must be connected).
   */
  async registerMcpServer(client) {
    const tools = await client.listTools();
    for (const tool of tools) {
      this.mcpTools.set(tool.name, { tool, client });
    }
    return tools.length;
  }
  /**
   * Returns `true` if a tool with the given name was registered from an MCP
   * server. Use this to route `tool_use` blocks from Claude to `callMcpTool`
   * vs the native tool dispatcher.
   */
  hasMcpTool(toolName) {
    return this.mcpTools.has(toolName);
  }
  /**
   * Returns all registered MCP tool names.
   */
  mcpToolNames() {
    return [...this.mcpTools.keys()];
  }
  /**
   * Invokes a named MCP tool.
   *
   * @throws {MCPNotConnectedError} if the backing client is not connected.
   * @throws {MCPToolCallError} if the transport or RPC layer fails.
   */
  async callMcpTool(toolName, args) {
    const entry = this.mcpTools.get(toolName);
    if (!entry) {
      throw new Error(
        `No MCP tool named '${toolName}' is registered on this bridge.`
      );
    }
    return entry.client.callTool(toolName, args);
  }
  /**
   * Returns all tools (native registry tools + MCP server tools) in the shape
   * the Anthropic SDK's `messages.create({ tools })` parameter expects.
   *
   * Native tools use their Zod schema (converted to JSON Schema by the
   * registry). MCP tools use their server-side JSON Schema directly — so
   * Claude sees accurate parameter descriptions without any Zod round-trip.
   */
  toAnthropicTools() {
    const nativeTools = this._registry.toAnthropicTools();
    const mcpToolDefs = [
      ...this.mcpTools.values()
    ].map(({ tool }) => ({
      name: tool.name,
      description: tool.description ?? "",
      input_schema: tool.inputSchema
    }));
    return [...nativeTools, ...mcpToolDefs];
  }
};
async function dispatchMcpToolUse(bridge, block) {
  try {
    const result = await bridge.callMcpTool(
      block.name,
      block.input ?? {}
    );
    const textContent = result.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
    const serialized = textContent || JSON.stringify(result.content.length > 0 ? result.content : "");
    return {
      type: "tool_result",
      tool_use_id: block.id,
      content: serialized,
      is_error: result.isError ? true : void 0
    };
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    return {
      type: "tool_result",
      tool_use_id: block.id,
      content: msg,
      is_error: true
    };
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  MCPClient,
  MCPConnectionError,
  MCPNotConnectedError,
  MCPResourceError,
  MCPToolCallError,
  ToolRegistryBridge,
  dispatchMcpToolUse
});
//# sourceMappingURL=index.js.map