// src/facade/mcp-core/client.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import {
  ListRootsRequestSchema,
  ElicitRequestSchema,
  CreateMessageRequestSchema,
  PingRequestSchema,
  ToolListChangedNotificationSchema,
  ResourceListChangedNotificationSchema,
  ResourceUpdatedNotificationSchema,
  PromptListChangedNotificationSchema,
  ProgressNotificationSchema,
  LoggingMessageNotificationSchema,
  CancelledNotificationSchema
} from "@modelcontextprotocol/sdk/types.js";

// src/facade/mcp-core/errors.ts
var MCPErrorCode = /* @__PURE__ */ ((MCPErrorCode2) => {
  MCPErrorCode2["CONNECTION_FAILED"] = "CONNECTION_FAILED";
  MCPErrorCode2["TIMEOUT"] = "TIMEOUT";
  MCPErrorCode2["TOOL_NOT_FOUND"] = "TOOL_NOT_FOUND";
  MCPErrorCode2["INVALID_PARAMETERS"] = "INVALID_PARAMETERS";
  MCPErrorCode2["SERVER_ERROR"] = "SERVER_ERROR";
  MCPErrorCode2["TRANSPORT_ERROR"] = "TRANSPORT_ERROR";
  MCPErrorCode2["NOT_CONNECTED"] = "NOT_CONNECTED";
  MCPErrorCode2["PROTOCOL_ERROR"] = "PROTOCOL_ERROR";
  MCPErrorCode2["CONFIG_ERROR"] = "CONFIG_ERROR";
  MCPErrorCode2["SCHEMA_CONVERSION_ERROR"] = "SCHEMA_CONVERSION_ERROR";
  MCPErrorCode2["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND";
  MCPErrorCode2["PROMPT_NOT_FOUND"] = "PROMPT_NOT_FOUND";
  MCPErrorCode2["SESSION_ERROR"] = "SESSION_ERROR";
  return MCPErrorCode2;
})(MCPErrorCode || {});
var MCPError = class extends Error {
  /** Error code */
  code;
  /** Additional error details */
  details;
  /** Server name where error occurred */
  serverName;
  /** Tool name if error occurred during tool execution */
  toolName;
  constructor(message, code, details, options) {
    super(message, options?.cause ? { cause: options.cause } : void 0);
    this.name = "MCPError";
    this.code = code;
    this.details = details;
    this.serverName = options?.serverName;
    this.toolName = options?.toolName;
  }
  /**
   * Convert error to JSON for logging/serialization.
   */
  toJSON() {
    return {
      name: this.name,
      message: this.message,
      code: this.code,
      details: this.details,
      serverName: this.serverName,
      toolName: this.toolName
    };
  }
  /**
   * Create a string representation of the error.
   */
  toString() {
    let str = `${this.name} [${this.code}]: ${this.message}`;
    if (this.serverName) str += ` (server: ${this.serverName})`;
    if (this.toolName) str += ` (tool: ${this.toolName})`;
    return str;
  }
};
function isMCPError(error) {
  return error instanceof MCPError;
}
function isSessionError(error) {
  if (!isMCPError(error)) return false;
  return error.code === "SESSION_ERROR" /* SESSION_ERROR */ || error.code === "NOT_CONNECTED" /* NOT_CONNECTED */;
}
function createConnectionError(serverName, cause) {
  return new MCPError(
    `Failed to connect to MCP server: ${serverName}`,
    "CONNECTION_FAILED" /* CONNECTION_FAILED */,
    { cause: cause?.message },
    { serverName, cause }
  );
}
function createTimeoutError(operation, serverName) {
  return new MCPError(`Operation timed out: ${operation}`, "TIMEOUT" /* TIMEOUT */, { operation }, { serverName });
}
function createToolNotFoundError(toolName, serverName) {
  return new MCPError(`Tool not found: ${toolName}`, "TOOL_NOT_FOUND" /* TOOL_NOT_FOUND */, { toolName }, { serverName, toolName });
}
function createInvalidParametersError(toolName, details, serverName) {
  return new MCPError(
    `Invalid parameters for tool: ${toolName}`,
    "INVALID_PARAMETERS" /* INVALID_PARAMETERS */,
    details,
    { serverName, toolName }
  );
}
function createServerError(message, serverName, details) {
  return new MCPError(message, "SERVER_ERROR" /* SERVER_ERROR */, details, { serverName });
}
function createNotConnectedError(serverName) {
  return new MCPError("Not connected to MCP server", "NOT_CONNECTED" /* NOT_CONNECTED */, void 0, { serverName });
}
function createConfigError(message, details) {
  return new MCPError(message, "CONFIG_ERROR" /* CONFIG_ERROR */, details);
}
function createSchemaConversionError(toolName, details, serverName) {
  return new MCPError(
    `Failed to convert schema for tool: ${toolName}`,
    "SCHEMA_CONVERSION_ERROR" /* SCHEMA_CONVERSION_ERROR */,
    details,
    { serverName, toolName }
  );
}

// src/facade/mcp-core/client.ts
var DEFAULT_REQUEST_TIMEOUT = 6e4;
var DEFAULT_CONNECT_TIMEOUT = 3e4;
var MCPClient = class {
  constructor(options) {
    this.options = options;
    this.serverName = options.name;
    this.config = options.config;
    this.timeout = options.timeout ?? DEFAULT_REQUEST_TIMEOUT;
    this.logHandler = options.logger;
    this.enableServerLogs = options.enableServerLogs ?? true;
    this.enableProgressTracking = options.enableProgressTracking ?? false;
    this._roots = options.roots ?? [];
  }
  options;
  client;
  transport;
  isConnected = false;
  connectionPromise = null;
  serverCapabilities;
  logHandler;
  enableServerLogs;
  enableProgressTracking;
  _roots;
  // --- Host-overridable handlers (Core of #13) --------------------------------
  // Server-initiated REQUESTS are answered by request handlers registered on the
  // SDK client; the handlers delegate to these host-supplied callbacks when set,
  // and fall back to sensible defaults otherwise.
  elicitationHandler;
  samplingHandler;
  // Server-initiated NOTIFICATIONS are dispatched to these (so the host can, for
  // example, re-list tools when a `tools/list_changed` arrives).
  resourceUpdatedHandler;
  resourceListChangedHandler;
  toolListChangedHandler;
  promptListChangedHandler;
  progressHandler;
  /** Server name */
  serverName;
  /** Server config */
  config;
  /** Request timeout */
  timeout;
  // ========================================================================
  // Connection Lifecycle
  // ========================================================================
  /**
   * Connect to the MCP server.
   * Safe to call multiple times - returns existing connection if already connected.
   */
  async connect() {
    if (this.isConnected) return;
    if (this.connectionPromise) {
      return this.connectionPromise;
    }
    this.connectionPromise = this.doConnect();
    try {
      await this.connectionPromise;
    } finally {
      this.connectionPromise = null;
    }
  }
  async doConnect() {
    const transport = this.createTransport(this.config);
    this.transport = transport;
    const client = new Client(
      { name: "indusagi-coding-agent", version: "0.13.0" },
      {
        capabilities: {
          roots: { listChanged: true },
          elicitation: {},
          sampling: {}
        }
      }
    );
    this.client = client;
    this.registerServerHandlers(client);
    const prevOnClose = transport.onclose;
    transport.onclose = () => {
      this.isConnected = false;
      this.log("debug", "Transport closed");
      prevOnClose?.();
    };
    await client.connect(transport, { timeout: DEFAULT_CONNECT_TIMEOUT });
    this.serverCapabilities = client.getServerCapabilities();
    this.isConnected = true;
    this.log("info", `Connected to MCP server`);
  }
  /**
   * Build the SDK transport for the configured server.
   *
   *  - stdio: `StdioClientTransport` (inherits the parent env, merges config.env).
   *  - http:  `StreamableHTTPClientTransport` (POST + server-push SSE channel,
   *           reconnect + session-expiry handled by the SDK), unless the config
   *           asks for the legacy `SSEClientTransport`.
   */
  createTransport(config) {
    if ("command" in config) {
      return new StdioClientTransport({
        command: config.command,
        args: config.args ?? [],
        env: { ...sanitizeEnv(process.env), ...config.env ?? {} },
        cwd: config.cwd,
        stderr: "pipe"
      });
    }
    if ("url" in config) {
      const requestInit = config.headers ? { headers: config.headers } : void 0;
      if (config.transport === "sse") {
        return new SSEClientTransport(config.url, { requestInit });
      }
      return new StreamableHTTPClientTransport(config.url, { requestInit });
    }
    throw new MCPError(
      "Server configuration must include either a command or a url",
      "CONFIG_ERROR" /* CONFIG_ERROR */,
      void 0,
      { serverName: this.serverName }
    );
  }
  /**
   * Register handlers for server-initiated REQUESTS and NOTIFICATIONS.
   *
   * REQUESTS (have both `.method` AND `.id`) must be ANSWERED with a JSON-RPC
   * RESPONSE carrying the matching id — the SDK does this automatically for the
   * value a request handler returns (or rejects). The previous hand-rolled
   * transport routed these into the notification path, so they were never
   * answered (bug #13).
   *
   * Defaults:
   *   - ping              → {}
   *   - roots/list        → the configured roots
   *   - elicitation/create→ decline ({action:"cancel"}) unless a host handler is set
   *   - sampling          → reject "Method not found" unless a host handler is set
   */
  registerServerHandlers(client) {
    client.setRequestHandler(PingRequestSchema, async () => ({}));
    client.setRequestHandler(ListRootsRequestSchema, async () => ({
      roots: this._roots.map((r) => ({ uri: r.uri, name: r.name }))
    }));
    client.setRequestHandler(ElicitRequestSchema, async (request) => {
      if (!this.elicitationHandler) {
        return { action: "cancel" };
      }
      const params = request.params;
      const elicitRequest = {
        message: params.message,
        requestedSchema: params.requestedSchema ?? {}
      };
      const result = await this.elicitationHandler(elicitRequest);
      return mapToSdkElicitResult(result);
    });
    client.setRequestHandler(CreateMessageRequestSchema, async (request) => {
      if (!this.samplingHandler) {
        throw new MCPError(
          "Method not found: sampling/createMessage",
          "SERVER_ERROR" /* SERVER_ERROR */,
          { method: "sampling/createMessage" },
          { serverName: this.serverName }
        );
      }
      return await this.samplingHandler(request.params);
    });
    client.setNotificationHandler(ToolListChangedNotificationSchema, () => {
      this.log("debug", "Tool list changed");
      this.toolListChangedHandler?.();
    });
    client.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
      this.log("debug", "Resource list changed");
      this.resourceListChangedHandler?.();
    });
    client.setNotificationHandler(ResourceUpdatedNotificationSchema, (n) => {
      const uri = n.params?.uri;
      this.log("debug", `Resource updated: ${uri}`);
      if (uri) this.resourceUpdatedHandler?.({ uri });
    });
    client.setNotificationHandler(PromptListChangedNotificationSchema, () => {
      this.log("debug", "Prompt list changed");
      this.promptListChangedHandler?.();
    });
    client.setNotificationHandler(ProgressNotificationSchema, (n) => {
      const params = n.params;
      this.log("debug", `Progress: ${JSON.stringify(params)}`);
      if (this.progressHandler && params) {
        const notification = {
          progressToken: params.progressToken,
          progress: params.progress,
          total: params.total,
          message: params.message
        };
        this.progressHandler(notification);
      }
    });
    client.setNotificationHandler(LoggingMessageNotificationSchema, (n) => {
      if (this.enableServerLogs && n.params) {
        const { level, ...rest } = n.params;
        this.log(level || "info", "[SERVER]", rest);
      }
    });
    client.setNotificationHandler(CancelledNotificationSchema, (n) => {
      this.log("debug", `Request cancelled: ${JSON.stringify(n.params)}`);
    });
  }
  /**
   * Disconnect from the MCP server.
   *
   * The SDK's `client.close()` closes the transport and rejects any pending
   * requests — a clean teardown for both stdio (graceful subprocess shutdown)
   * and HTTP (close the push channel / end the session).
   */
  async disconnect() {
    if (!this.client) {
      this.log("debug", "Disconnect called but not connected");
      this.isConnected = false;
      return;
    }
    this.log("debug", "Disconnecting from MCP server");
    try {
      await this.client.close();
    } catch (error) {
      this.log("error", `Error during disconnect: ${error}`);
    } finally {
      this.client = void 0;
      this.transport = void 0;
      this.isConnected = false;
      this.log("debug", "Successfully disconnected");
    }
  }
  /**
   * Whether the client is connected.
   */
  get connected() {
    return this.isConnected;
  }
  // ========================================================================
  // Tool Operations
  // ========================================================================
  /**
   * List all tools available from the server.
   */
  async listTools() {
    this.ensureConnected();
    const result = await this.client.listTools(void 0, {
      timeout: this.timeout
    });
    return result.tools;
  }
  /**
   * Call a tool on the server.
   */
  async callTool(name, args) {
    this.ensureConnected();
    this.log("debug", `Calling tool: ${name}`);
    try {
      const result = await this.client.callTool(
        { name, arguments: args },
        void 0,
        { timeout: this.timeout }
      );
      this.log("debug", `Tool ${name} executed successfully`);
      return result;
    } catch (error) {
      this.log("error", `Tool ${name} failed: ${error}`);
      throw this.wrapError(error);
    }
  }
  // ========================================================================
  // Resource Operations
  // ========================================================================
  /**
   * List all resources available from the server.
   */
  async listResources() {
    this.ensureConnected();
    if (!this.serverCapabilities?.resources) {
      return [];
    }
    const result = await this.client.listResources(void 0, {
      timeout: this.timeout
    });
    return result.resources;
  }
  /**
   * Read a resource from the server.
   */
  async readResource(uri) {
    this.ensureConnected();
    return await this.client.readResource({ uri }, { timeout: this.timeout });
  }
  /**
   * Subscribe to resource updates.
   */
  async subscribeResource(uri) {
    this.ensureConnected();
    await this.client.subscribeResource({ uri }, { timeout: this.timeout });
  }
  /**
   * Unsubscribe from resource updates.
   */
  async unsubscribeResource(uri) {
    this.ensureConnected();
    await this.client.unsubscribeResource({ uri }, { timeout: this.timeout });
  }
  // ========================================================================
  // Prompt Operations
  // ========================================================================
  /**
   * List all prompts available from the server.
   */
  async listPrompts() {
    this.ensureConnected();
    if (!this.serverCapabilities?.prompts) {
      return [];
    }
    const result = await this.client.listPrompts(void 0, {
      timeout: this.timeout
    });
    return result.prompts;
  }
  /**
   * Get a prompt from the server.
   */
  async getPrompt(name, args) {
    this.ensureConnected();
    return await this.client.getPrompt(
      { name, arguments: args },
      { timeout: this.timeout }
    );
  }
  // ========================================================================
  // Roots Operations
  // ========================================================================
  /**
   * Get the configured roots.
   */
  get roots() {
    return [...this._roots];
  }
  /**
   * Update the roots and notify the server.
   */
  async setRoots(roots) {
    this.log("debug", `Updating roots to ${roots.length} entries`);
    this._roots = [...roots];
    if (this.isConnected && this.client) {
      try {
        await this.client.sendRootsListChanged();
      } catch (error) {
        this.log("debug", `Failed to send roots/list_changed: ${error}`);
      }
    }
  }
  // ========================================================================
  // Handler Registration
  //
  // Setters STORE the host handler (and, for notifications, the SDK handler
  // registered in `registerServerHandlers` delegates to the stored callback).
  // This is the host-overridable layer at the core of bug #13.
  // ========================================================================
  /**
   * Set a handler for resource updated notifications.
   */
  setResourceUpdatedHandler(handler) {
    this.resourceUpdatedHandler = handler;
    this.log("debug", "Resource updated handler registered");
  }
  /**
   * Set a handler for resource list changed notifications.
   */
  setResourceListChangedHandler(handler) {
    this.resourceListChangedHandler = handler;
    this.log("debug", "Resource list changed handler registered");
  }
  /**
   * Set a handler for tool list changed notifications.
   */
  setToolListChangedHandler(handler) {
    this.toolListChangedHandler = handler;
    this.log("debug", "Tool list changed handler registered");
  }
  /**
   * Set a handler for prompt list changed notifications.
   */
  setPromptListChangedHandler(handler) {
    this.promptListChangedHandler = handler;
    this.log("debug", "Prompt list changed handler registered");
  }
  /**
   * Set a handler for elicitation requests. When set, the server's
   * `elicitation/create` REQUEST is answered with the host's decision; when
   * unset the request is declined ({action:"cancel"}).
   */
  setElicitationHandler(handler) {
    this.elicitationHandler = handler;
    this.log("debug", "Elicitation handler registered");
  }
  /**
   * Set a handler for sampling (`sampling/createMessage`) requests. When set,
   * the server request is answered with the host's result; when unset the
   * request is rejected with "Method not found".
   */
  setSamplingHandler(handler) {
    this.samplingHandler = handler;
    this.log("debug", "Sampling handler registered");
  }
  /**
   * Set a handler for progress notifications.
   */
  setProgressHandler(handler) {
    this.progressHandler = handler;
    this.log("debug", "Progress handler registered");
  }
  // ========================================================================
  // Private Methods
  // ========================================================================
  ensureConnected() {
    if (!this.isConnected || !this.client) {
      throw new MCPError(
        "Not connected to MCP server",
        "NOT_CONNECTED" /* NOT_CONNECTED */,
        void 0,
        { serverName: this.serverName }
      );
    }
  }
  /**
   * Normalize an SDK/transport error into an MCPError. Session-expiry style
   * failures (404 session / -32001) are surfaced as SESSION_ERROR so callers
   * (the pool) can decide to reconnect; the SDK's StreamableHTTP transport
   * already attempts a bounded reconnect re-using the session id before this.
   */
  wrapError(error) {
    if (error instanceof MCPError) return error;
    const message = error instanceof Error ? error.message : String(error);
    const code = error.code;
    if (code === -32001 || /\b404\b|session/i.test(message)) {
      return new MCPError(message, "SESSION_ERROR" /* SESSION_ERROR */, { code }, {
        serverName: this.serverName
      });
    }
    return new MCPError(message, "SERVER_ERROR" /* SERVER_ERROR */, { code }, {
      serverName: this.serverName
    });
  }
  log(level, message, details) {
    const msg = `[${this.serverName}] ${message}`;
    if (this.logHandler) {
      this.logHandler({
        level,
        message: msg,
        timestamp: /* @__PURE__ */ new Date(),
        serverName: this.serverName,
        details
      });
    } else {
      if (process.env.INDUSAGI_DEBUG || level === "error") {
        const prefix = `[MCP:${level.toUpperCase()}]`;
        if (details) {
          if (level === "error") {
            console.error(prefix, msg, details);
          } else {
            console.log(prefix, msg, details);
          }
        } else {
          if (level === "error") {
            console.error(prefix, msg);
          } else {
            console.log(prefix, msg);
          }
        }
      }
    }
  }
};
function mapToSdkElicitResult(result) {
  if (result.action === "accept") {
    return {
      action: "accept",
      content: result.content ?? {}
    };
  }
  return { action: result.action };
}
function sanitizeEnv(env) {
  const out = {};
  for (const [k, v] of Object.entries(env)) {
    if (typeof v === "string") out[k] = v;
  }
  return out;
}

// src/facade/mcp-core/client-pool.ts
var MCPClientPool = class {
  constructor(options) {
    this.options = options;
  }
  options;
  clients = /* @__PURE__ */ new Map();
  isConnecting = false;
  /**
   * Connect to all configured servers.
   *
   * Errors for individual servers are logged but don't fail the entire operation.
   */
  async connectAll() {
    if (this.isConnecting) {
      throw new MCPError(
        "Already connecting to servers",
        "CONNECTION_FAILED" /* CONNECTION_FAILED */
      );
    }
    this.isConnecting = true;
    try {
      for (const serverConfig of this.options.servers) {
        try {
          const client = new MCPClient(serverConfig);
          await client.connect();
          this.clients.set(serverConfig.name, client);
          console.log(`[MCP] Connected to ${serverConfig.name}`);
        } catch (error) {
          console.error(
            `[MCP] Failed to connect to ${serverConfig.name}:`,
            error instanceof Error ? error.message : String(error)
          );
        }
      }
    } finally {
      this.isConnecting = false;
    }
  }
  /**
   * Disconnect from all servers.
   */
  async disconnectAll() {
    for (const [name, client] of this.clients) {
      try {
        await client.disconnect();
        console.log(`[MCP] Disconnected from ${name}`);
      } catch (error) {
        console.error(`[MCP] Error disconnecting from ${name}:`, error);
      }
    }
    this.clients.clear();
  }
  /**
   * Get a specific client by name.
   */
  getClient(name) {
    return this.clients.get(name);
  }
  /**
   * Get all connected clients.
   */
  getAllClients() {
    return Array.from(this.clients.values());
  }
  /**
   * Check if a client is connected.
   */
  isConnected(name) {
    return this.clients.get(name)?.connected ?? false;
  }
  /**
   * Get status of all servers.
   */
  async getStatus() {
    const statuses = [];
    for (const [name, client] of this.clients) {
      const status = {
        name,
        connected: client.connected
      };
      if (client.connected) {
        try {
          const tools = await client.listTools();
          status.toolCount = tools.length;
        } catch {
        }
        try {
          const resources = await client.listResources();
          status.resourceCount = resources.length;
        } catch {
        }
        try {
          const prompts = await client.listPrompts();
          status.promptCount = prompts.length;
        } catch {
        }
      }
      statuses.push(status);
    }
    return statuses;
  }
  /**
   * Get all tools from all connected servers.
   */
  async listAllTools() {
    const allTools = {};
    for (const [name, client] of this.clients) {
      if (client.connected) {
        try {
          allTools[name] = await client.listTools();
        } catch (error) {
          console.error(`[MCP] Failed to list tools from ${name}:`, error);
          allTools[name] = [];
        }
      }
    }
    return allTools;
  }
  /**
   * Get all resources from all connected servers.
   */
  async listAllResources() {
    const allResources = {};
    for (const [name, client] of this.clients) {
      if (client.connected) {
        try {
          allResources[name] = await client.listResources();
        } catch (error) {
          console.error(`[MCP] Failed to list resources from ${name}:`, error);
          allResources[name] = [];
        }
      }
    }
    return allResources;
  }
  /**
   * Get all prompts from all connected servers.
   */
  async listAllPrompts() {
    const allPrompts = {};
    for (const [name, client] of this.clients) {
      if (client.connected) {
        try {
          allPrompts[name] = await client.listPrompts();
        } catch (error) {
          console.error(`[MCP] Failed to list prompts from ${name}:`, error);
          allPrompts[name] = [];
        }
      }
    }
    return allPrompts;
  }
  /**
   * Reload all connections.
   */
  async reload() {
    await this.disconnectAll();
    await this.connectAll();
  }
  /**
   * Add a new server connection.
   */
  async addServer(config) {
    if (this.clients.has(config.name)) {
      console.warn(`[MCP] Server ${config.name} already exists`);
      return false;
    }
    try {
      const client = new MCPClient(config);
      await client.connect();
      this.clients.set(config.name, client);
      console.log(`[MCP] Connected to ${config.name}`);
      return true;
    } catch (error) {
      console.error(`[MCP] Failed to connect to ${config.name}:`, error);
      return false;
    }
  }
  /**
   * Remove a server connection.
   */
  async removeServer(name) {
    const client = this.clients.get(name);
    if (!client) {
      return false;
    }
    try {
      await client.disconnect();
      this.clients.delete(name);
      console.log(`[MCP] Disconnected from ${name}`);
      return true;
    } catch (error) {
      console.error(`[MCP] Error disconnecting from ${name}:`, error);
      return false;
    }
  }
};

// src/facade/mcp-core/config.ts
import { existsSync, readFileSync, mkdirSync, statSync, writeFileSync } from "fs";
import { join, dirname } from "path";
import { homedir } from "os";
function loadMCPConfig(configPathOrCwd = process.cwd()) {
  const configs = [];
  if (existsSync(configPathOrCwd)) {
    try {
      const stats = statSync(configPathOrCwd);
      if (stats.isFile()) {
        const fileConfigs = parseConfigFile(configPathOrCwd);
        configs.push(...fileConfigs);
        return configs;
      }
    } catch {
    }
  }
  const cwd = configPathOrCwd;
  const projectConfig = join(cwd, ".indusvx", "mcp.json");
  if (existsSync(projectConfig)) {
    try {
      const projectConfigs = parseConfigFile(projectConfig);
      configs.push(...projectConfigs);
      console.log(`[MCP] Loaded ${projectConfigs.length} servers from project config`);
    } catch (error) {
      console.error(`[MCP] Error loading project config:`, error);
    }
  }
  const xdgConfigHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
  const userConfig = join(xdgConfigHome, "indusvx", "mcp.json");
  if (existsSync(userConfig)) {
    try {
      const userConfigs = parseConfigFile(userConfig);
      configs.push(...userConfigs);
      console.log(`[MCP] Loaded ${userConfigs.length} servers from user config`);
    } catch (error) {
      console.error(`[MCP] Error loading user config:`, error);
    }
  }
  const legacyConfig = join(homedir(), ".indusvx", "agent", "mcp.json");
  if (existsSync(legacyConfig)) {
    try {
      const legacyConfigs = parseConfigFile(legacyConfig);
      configs.push(...legacyConfigs);
      console.log(`[MCP] Loaded ${legacyConfigs.length} servers from legacy config`);
    } catch (error) {
      console.error(`[MCP] Error loading legacy config:`, error);
    }
  }
  const legacyServersConfig = join(homedir(), ".indusvx", "agent", "mcp-servers.json");
  if (existsSync(legacyServersConfig)) {
    try {
      const legacyConfigs = parseConfigFile(legacyServersConfig);
      configs.push(...legacyConfigs);
      console.log(`[MCP] Loaded ${legacyConfigs.length} servers from mcp-servers.json`);
    } catch (error) {
      console.error(`[MCP] Error loading mcp-servers.json:`, error);
    }
  }
  return configs;
}
function parseConfigFile(path) {
  const content = readFileSync(path, "utf-8");
  const config = JSON.parse(content);
  if (!config.servers) {
    console.warn(`[MCP] Invalid config file ${path}: missing "servers" field`);
    return [];
  }
  let serversArray;
  if (Array.isArray(config.servers)) {
    serversArray = config.servers;
  } else if (typeof config.servers === "object") {
    serversArray = Object.entries(config.servers).map(([name, serverConfig]) => ({
      name,
      ...serverConfig
    }));
  } else {
    console.warn(`[MCP] Invalid config file ${path}: "servers" must be array or object`);
    return [];
  }
  return serversArray.filter((server) => server.enabled !== false).map((server) => {
    if (server.url) {
      return {
        name: server.name,
        config: {
          url: new URL(server.url),
          headers: server.headers,
          ...server.transport ? { transport: server.transport } : {}
        },
        timeout: server.timeout
      };
    } else if (server.command) {
      return {
        name: server.name,
        config: {
          command: server.command,
          args: server.args,
          env: server.env
        },
        timeout: server.timeout
      };
    }
    throw new MCPError(
      `Invalid server config: ${server.name}`,
      "CONFIG_ERROR" /* CONFIG_ERROR */,
      { server }
    );
  });
}
function getUserConfigPath() {
  const xdgConfigHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
  return join(xdgConfigHome, "indusvx", "mcp.json");
}
function getProjectConfigPath(cwd = process.cwd()) {
  return join(cwd, ".indusvx", "mcp.json");
}
function ensureUserConfigDir() {
  const xdgConfigHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
  const configDir = join(xdgConfigHome, "indusvx");
  if (!existsSync(configDir)) {
    mkdirSync(configDir, { recursive: true });
  }
  return configDir;
}
function ensureProjectConfigDir(cwd = process.cwd()) {
  const configDir = join(cwd, ".indusvx");
  if (!existsSync(configDir)) {
    mkdirSync(configDir, { recursive: true });
  }
  return configDir;
}
function saveConfig(path, config) {
  const dir = dirname(path);
  if (!existsSync(dir)) {
    mkdirSync(dir, { recursive: true });
  }
  const content = JSON.stringify(config, null, 2);
  writeFileSync(path, content, "utf-8");
}
function saveUserConfig(config) {
  saveConfig(getUserConfigPath(), config);
}
function saveProjectConfig(config, cwd = process.cwd()) {
  saveConfig(getProjectConfigPath(cwd), config);
}
function createDefaultConfig() {
  return {
    servers: []
  };
}
var EXAMPLE_CONFIG = {
  servers: [
    {
      name: "filesystem",
      command: "npx",
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"],
      enabled: true
    },
    {
      name: "github",
      command: "npx",
      args: ["-y", "@modelcontextprotocol/server-github"],
      env: {
        GITHUB_TOKEN: "your-github-token"
      },
      enabled: true
    },
    {
      name: "remote-server",
      url: "http://localhost:8080/mcp",
      headers: {
        Authorization: "Bearer your-token"
      },
      enabled: false
    }
  ]
};

// src/facade/mcp-core/schema-converter.ts
import { Type } from "@sinclair/typebox";
function jsonSchemaToTypeBox(schema) {
  if (schema.type === "null") {
    return Type.Null({ description: schema.description });
  }
  if (schema.enum) {
    if (schema.enum.length === 1) {
      return Type.Literal(schema.enum[0], {
        description: schema.description
      });
    }
    const literals = schema.enum.map((e) => {
      if (e === null) return Type.Null();
      return Type.Literal(e);
    });
    return Type.Union(literals, { description: schema.description });
  }
  if (schema.const !== void 0) {
    if (schema.const === null) {
      return Type.Null({ description: schema.description });
    }
    return Type.Literal(schema.const, {
      description: schema.description
    });
  }
  if (schema.oneOf || schema.anyOf) {
    const schemas = (schema.oneOf || schema.anyOf).map((s) => jsonSchemaToTypeBox(s));
    return Type.Union(schemas, { description: schema.description });
  }
  if (schema.allOf) {
    const schemas = schema.allOf.map((s) => jsonSchemaToTypeBox(s));
    return Type.Intersect(schemas, { description: schema.description });
  }
  if (schema.type === "array" || Array.isArray(schema.type) && schema.type.includes("array")) {
    return convertArraySchema(schema);
  }
  if (schema.type === "object" || Array.isArray(schema.type) && schema.type.includes("object")) {
    return convertObjectSchema(schema);
  }
  switch (schema.type) {
    case "string":
      return convertStringSchema(schema);
    case "number":
    case "integer":
      return convertNumberSchema(schema);
    case "boolean":
      return Type.Boolean({ description: schema.description });
    default:
      if (schema.properties) {
        return convertObjectSchema(schema);
      }
      if (schema.items) {
        return convertArraySchema(schema);
      }
      return Type.Any({ description: schema.description });
  }
}
function convertStringSchema(schema) {
  return Type.String({
    description: schema.description,
    minLength: schema.minLength,
    maxLength: schema.maxLength,
    pattern: schema.pattern,
    format: schema.format
  });
}
function convertNumberSchema(schema) {
  const options = {
    description: schema.description
  };
  if (schema.minimum !== void 0) options.minimum = schema.minimum;
  if (schema.maximum !== void 0) options.maximum = schema.maximum;
  if (typeof schema.exclusiveMinimum === "number") options.exclusiveMinimum = schema.exclusiveMinimum;
  if (typeof schema.exclusiveMaximum === "number") options.exclusiveMaximum = schema.exclusiveMaximum;
  if (schema.multipleOf !== void 0) options.multipleOf = schema.multipleOf;
  return Type.Number(options);
}
function convertArraySchema(schema) {
  let itemsSchema;
  if (schema.items) {
    if (Array.isArray(schema.items)) {
      const items = schema.items.map((item) => jsonSchemaToTypeBox(item));
      itemsSchema = Type.Tuple(items);
    } else {
      itemsSchema = jsonSchemaToTypeBox(schema.items);
    }
  } else {
    itemsSchema = Type.Any();
  }
  return Type.Array(itemsSchema, {
    description: schema.description,
    minItems: schema.minItems,
    maxItems: schema.maxItems
  });
}
function convertObjectSchema(schema) {
  const properties = {};
  const required = new Set(schema.required ?? []);
  for (const [key, value] of Object.entries(schema.properties ?? {})) {
    let propSchema = jsonSchemaToTypeBox(value);
    if (!required.has(key)) {
      propSchema = Type.Optional(propSchema);
    }
    properties[key] = propSchema;
  }
  let additionalProperties = void 0;
  if (schema.additionalProperties === true) {
    additionalProperties = true;
  } else if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
    additionalProperties = jsonSchemaToTypeBox(schema.additionalProperties);
  } else if (schema.additionalProperties === false) {
    additionalProperties = false;
  }
  return Type.Object(properties, {
    description: schema.description,
    additionalProperties
  });
}
function applyPassthrough(schema) {
  const kind = schema[/* @__PURE__ */ Symbol.for("TypeBox.Kind")];
  if (kind === "Object") {
    const shape = { ...schema.properties };
    for (const key of Object.keys(shape)) {
      shape[key] = applyPassthrough(shape[key]);
    }
    return Type.Object(shape, {
      description: schema.description,
      additionalProperties: true
    });
  }
  if (kind === "Array") {
    return Type.Array(applyPassthrough(schema.items), {
      description: schema.description,
      minItems: schema.minItems,
      maxItems: schema.maxItems
    });
  }
  if (kind === "Union") {
    return Type.Union(
      schema.anyOf.map((s) => applyPassthrough(s)),
      { description: schema.description }
    );
  }
  if (kind === "Intersect") {
    return Type.Intersect(
      schema.allOf.map((s) => applyPassthrough(s)),
      { description: schema.description }
    );
  }
  if (kind === "Optional") {
    return Type.Optional(applyPassthrough(schema.schema ?? schema.type));
  }
  return schema;
}
function convertMCPInputSchema(inputSchema) {
  const typeboxSchema = jsonSchemaToTypeBox(inputSchema);
  return applyPassthrough(typeboxSchema);
}
function convertMCPOutputSchema(outputSchema) {
  if (!outputSchema) return void 0;
  const typeboxSchema = jsonSchemaToTypeBox(outputSchema);
  return applyPassthrough(typeboxSchema);
}

// src/facade/mcp-core/tool-factory.ts
function createMCPAgentToolFactory(mcpTool, client) {
  return () => {
    const namespacedName = `${client.serverName}_${mcpTool.name}`;
    const parameters = convertMCPInputSchema(mcpTool.inputSchema);
    return {
      name: namespacedName,
      label: mcpTool.name,
      description: `[${client.serverName}] ${mcpTool.description || ""}`,
      category: "mcp",
      parameters,
      execute: async (toolCallId, params, signal, onUpdate) => {
        const typedParams = params;
        try {
          if (!client.connected) {
            throw new MCPError(
              `MCP server ${client.serverName} not connected`,
              "NOT_CONNECTED" /* NOT_CONNECTED */,
              void 0,
              { serverName: client.serverName, toolName: mcpTool.name }
            );
          }
          if (signal?.aborted) {
            throw new MCPError(
              "Tool execution aborted",
              "TIMEOUT" /* TIMEOUT */,
              void 0,
              { serverName: client.serverName, toolName: mcpTool.name }
            );
          }
          const result = await client.callTool(mcpTool.name, typedParams);
          return convertMCPResultToAgentResult(result, client.serverName, mcpTool.name);
        } catch (error) {
          if (error instanceof MCPError) {
            return {
              content: [
                {
                  type: "text",
                  text: `MCP Error: ${error.message}`
                }
              ],
              details: {
                error: error.message,
                code: error.code,
                serverName: error.serverName,
                toolName: error.toolName
              },
              isError: true
            };
          }
          if (error instanceof Error && error.name === "AbortError") {
            return {
              content: [
                {
                  type: "text",
                  text: `Tool execution aborted`
                }
              ],
              details: {
                error: "Aborted"
              },
              isError: true
            };
          }
          return {
            content: [
              {
                type: "text",
                text: `Error: ${error instanceof Error ? error.message : String(error)}`
              }
            ],
            details: {
              error: String(error)
            },
            isError: true
          };
        }
      }
    };
  };
}
function truncateTextForDisplay(text, maxLines = 4, maxChars = 500) {
  const lines = text.split("\n");
  if (lines.length <= maxLines && text.length <= maxChars) {
    return text;
  }
  let truncated = lines.slice(0, maxLines).join("\n");
  if (truncated.length > maxChars) {
    truncated = truncated.slice(0, maxChars);
  }
  const remaining = lines.length - maxLines;
  if (remaining > 0) {
    truncated += `
... (${remaining} more lines)`;
  } else if (text.length > maxChars) {
    truncated += "...";
  }
  return truncated;
}
function convertMCPResultToAgentResult(result, serverName, toolName) {
  const content = [];
  if (result.content && Array.isArray(result.content)) {
    for (const item of result.content) {
      if (item.type === "text") {
        const truncatedText = truncateTextForDisplay(item.text);
        content.push({ type: "text", text: truncatedText });
      } else if (item.type === "image") {
        content.push({
          type: "image",
          data: item.data,
          mimeType: item.mimeType
        });
      }
    }
  } else if (typeof result === "string") {
    const truncatedText = truncateTextForDisplay(result);
    content.push({ type: "text", text: truncatedText });
  } else {
    const jsonStr = JSON.stringify(result, null, 2);
    const truncatedText = truncateTextForDisplay(jsonStr);
    content.push({ type: "text", text: truncatedText });
  }
  return {
    content,
    details: result,
    isError: result.isError || false
  };
}
async function registerMCPToolsInRegistry(registry, client, tools) {
  let registeredCount = 0;
  for (const tool of tools) {
    try {
      const factory = createMCPAgentToolFactory(tool, client);
      registry.register(
        {
          name: `${client.serverName}_${tool.name}`,
          label: tool.name,
          category: "mcp",
          description: `[${client.serverName}] ${tool.description || ""}`
        },
        factory
      );
      registeredCount++;
    } catch (error) {
      console.error(`Failed to register MCP tool ${tool.name}:`, error);
    }
  }
  return registeredCount;
}
function createMCPToolsMap(tools, client) {
  const toolMap = /* @__PURE__ */ new Map();
  for (const tool of tools) {
    const factory = createMCPAgentToolFactory(tool, client);
    const namespacedName = `${client.serverName}_${tool.name}`;
    toolMap.set(namespacedName, factory());
  }
  return toolMap;
}
function createMCPToolsRecord(tools, client) {
  const toolRecord = {};
  for (const tool of tools) {
    const factory = createMCPAgentToolFactory(tool, client);
    const namespacedName = `${client.serverName}_${tool.name}`;
    toolRecord[namespacedName] = factory();
  }
  return toolRecord;
}

// src/facade/mcp-core/server.ts
var MCPServer = class {
  constructor(options) {
    this.options = options;
    this.name = options.name;
    this.version = options.version || "1.0.0";
    for (const tool of options.tools) {
      const converted = this.convertTool(tool);
      this.tools.set(converted.name, converted);
    }
    console.error(`[MCP Server] Initialized "${this.name}" v${this.version} with ${this.tools.size} tools`);
  }
  options;
  tools = /* @__PURE__ */ new Map();
  buffer = "";
  isConnected = false;
  /** Server name */
  name;
  /** Server version */
  version;
  // ========================================================================
  // Lifecycle
  // ========================================================================
  /**
   * Start the server using stdio transport.
   *
   * This is typically used when running the server as a subprocess
   * that MCP clients (like Claude Desktop) spawn.
   */
  async startStdio() {
    this.isConnected = true;
    console.error(`[MCP Server] Starting stdio transport`);
    process.stdin.setEncoding("utf8");
    process.stdin.on("data", (chunk) => {
      this.buffer += chunk;
      this.processBuffer();
    });
    process.stdin.on("end", () => {
      console.error(`[MCP Server] Stdin closed, shutting down`);
      this.isConnected = false;
      process.exit(0);
    });
    return new Promise(() => {
    });
  }
  /**
   * Stop the server.
   */
  async stop() {
    this.isConnected = false;
    console.error(`[MCP Server] Stopped`);
  }
  // ========================================================================
  // Tool Management
  // ========================================================================
  /**
   * Add a tool to the server.
   */
  addTool(tool) {
    const converted = this.convertTool(tool);
    this.tools.set(converted.name, converted);
    console.error(`[MCP Server] Added tool: ${converted.name}`);
  }
  /**
   * Remove a tool from the server.
   */
  removeTool(name) {
    const result = this.tools.delete(name);
    if (result) {
      console.error(`[MCP Server] Removed tool: ${name}`);
    }
    return result;
  }
  /**
   * List all available tools.
   */
  listTools() {
    return Array.from(this.tools.values());
  }
  // ========================================================================
  // Private Methods
  // ========================================================================
  convertTool(tool) {
    const inputSchema = this.typeBoxToJsonSchema(tool.parameters);
    return {
      name: tool.name,
      description: tool.description || tool.label,
      inputSchema,
      execute: async (args) => {
        const result = await tool.execute(
          `mcp_${Date.now()}`,
          args,
          void 0,
          void 0
        );
        return result;
      }
    };
  }
  typeBoxToJsonSchema(schema) {
    if (!schema) {
      return { type: "object", properties: {} };
    }
    if (schema.type || schema.properties || schema.$schema) {
      return schema;
    }
    const jsonSchema = schema.toJSONSchema?.() || schema;
    return {
      type: "object",
      properties: jsonSchema.properties || {},
      required: jsonSchema.required || []
    };
  }
  processBuffer() {
    const lines = this.buffer.split("\n");
    this.buffer = lines.pop() || "";
    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed) continue;
      try {
        const message = JSON.parse(trimmed);
        this.handleMessage(message);
      } catch (error) {
        console.error(`[MCP Server] Failed to parse message: ${trimmed}`);
      }
    }
  }
  async handleMessage(message) {
    console.error(`[MCP Server] Received: ${message.method || `response to ${message.id}`}`);
    try {
      if (message.method) {
        const response = await this.handleRequest(message);
        this.sendResponse(message.id, response);
      }
    } catch (error) {
      console.error(`[MCP Server] Error handling message:`, error);
      if (message.id !== void 0) {
        this.sendError(message.id, error);
      }
    }
  }
  async handleRequest(message) {
    switch (message.method) {
      case "initialize":
        return this.handleInitialize(message.params);
      case "tools/list":
        return this.handleToolsList();
      case "tools/call":
        return this.handleToolsCall(message.params);
      case "resources/list":
        return { resources: [] };
      case "prompts/list":
        return { prompts: [] };
      default:
        throw new MCPError(
          `Unknown method: ${message.method}`,
          "PROTOCOL_ERROR" /* PROTOCOL_ERROR */
        );
    }
  }
  handleInitialize(params) {
    console.error(`[MCP Server] Initialize from client: ${params?.clientInfo?.name || "unknown"}`);
    return {
      protocolVersion: "2024-11-05",
      serverInfo: {
        name: this.name,
        version: this.version
      },
      capabilities: {
        tools: {}
      }
    };
  }
  handleToolsList() {
    const tools = Array.from(this.tools.values()).map((tool) => ({
      name: tool.name,
      description: tool.description,
      inputSchema: tool.inputSchema
    }));
    return { tools };
  }
  async handleToolsCall(params) {
    const { name, arguments: args } = params;
    const tool = this.tools.get(name);
    if (!tool) {
      return {
        content: [{ type: "text", text: `Unknown tool: ${name}` }],
        isError: true
      };
    }
    try {
      console.error(`[MCP Server] Executing tool: ${name}`);
      const result = await tool.execute(args || {});
      if (result && typeof result === "object" && "content" in result) {
        const agentResult = result;
        const content = [];
        for (const item of agentResult.content || []) {
          if (item.type === "text") {
            content.push({ type: "text", text: item.text });
          } else if (item.type === "image") {
            content.push({
              type: "text",
              text: `[Image: ${item.mimeType}, ${item.data.length} bytes]`
            });
          }
        }
        return {
          content,
          isError: agentResult.isError || false
        };
      }
      return {
        content: [
          {
            type: "text",
            text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
          }
        ]
      };
    } catch (error) {
      console.error(`[MCP Server] Tool execution failed:`, error);
      return {
        content: [
          {
            type: "text",
            text: `Error: ${error instanceof Error ? error.message : String(error)}`
          }
        ],
        isError: true
      };
    }
  }
  sendResponse(id, result) {
    const response = {
      jsonrpc: "2.0",
      id,
      result
    };
    process.stdout.write(JSON.stringify(response) + "\n");
  }
  sendError(id, error) {
    const response = {
      jsonrpc: "2.0",
      id,
      error: {
        code: -32e3,
        message: error instanceof Error ? error.message : String(error)
      }
    };
    process.stdout.write(JSON.stringify(response) + "\n");
  }
};
function createMCPServer(options) {
  return new MCPServer(options);
}

// src/facade/mcp-core/index.ts
async function initializeMCP(registry, cwd = process.cwd()) {
  const configs = loadMCPConfig(cwd);
  if (configs.length === 0) {
    console.log("[MCP] No servers configured");
    return { pool: new MCPClientPool({ servers: [] }), toolCount: 0 };
  }
  const pool = new MCPClientPool({ servers: configs });
  await pool.connectAll();
  let totalTools = 0;
  for (const client of pool.getAllClients()) {
    try {
      const tools = await client.listTools();
      const count = await registerMCPToolsInRegistry(registry, client, tools);
      totalTools += count;
      console.log(`[MCP] Registered ${count} tools from ${client.serverName}`);
    } catch (error) {
      console.error(`[MCP] Failed to register tools from ${client.serverName}:`, error);
    }
  }
  return { pool, toolCount: totalTools };
}
export {
  EXAMPLE_CONFIG,
  MCPClient,
  MCPClientPool,
  MCPError,
  MCPErrorCode,
  MCPServer,
  applyPassthrough,
  convertMCPInputSchema,
  convertMCPOutputSchema,
  createConfigError,
  createConnectionError,
  createDefaultConfig,
  createInvalidParametersError,
  createMCPAgentToolFactory,
  createMCPServer,
  createMCPToolsMap,
  createMCPToolsRecord,
  createNotConnectedError,
  createSchemaConversionError,
  createServerError,
  createTimeoutError,
  createToolNotFoundError,
  ensureProjectConfigDir,
  ensureUserConfigDir,
  getProjectConfigPath,
  getUserConfigPath,
  initializeMCP,
  isMCPError,
  isSessionError,
  jsonSchemaToTypeBox,
  loadMCPConfig,
  registerMCPToolsInRegistry,
  saveConfig,
  saveProjectConfig,
  saveUserConfig
};
