import { diffSchemas } from 'json-schema-diff';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
import { Client as Client$1 } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
import { ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

// src/core/binder.ts
var HTTPClientTransport = class extends StreamableHTTPClientTransport {
  constructor(url, opts) {
    super(url, opts);
  }
  send(message, options) {
    const mockAction = getMockActionFor(message);
    if (mockAction?.type === "emit") {
      this.onmessage?.(mockAction.message);
      return Promise.resolve();
    }
    if (mockAction?.type === "suppress") {
      return Promise.resolve();
    }
    return super.send(message, options);
  }
};
function getMockActionFor(message) {
  const m = message;
  if (!m || typeof m !== "object" || !("method" in m)) return null;
  switch (m.method) {
    case "initialize": {
      const protocolVersion = m?.params?.protocolVersion;
      if (!protocolVersion) return null;
      return {
        type: "emit",
        message: {
          result: {
            protocolVersion,
            capabilities: { tools: {} },
            serverInfo: { name: "deco-chat-server", version: "1.0.0" }
          },
          jsonrpc: m.jsonrpc ?? "2.0",
          // @ts-expect-error - id is not typed
          id: m.id
        }
      };
    }
    case "notifications/roots/list_changed":
    case "notifications/initialized":
    case "notifications/cancelled":
    case "notifications/progress": {
      return { type: "suppress" };
    }
    default:
      return null;
  }
}

// src/core/client/mcp-client.ts
var Client = class extends Client$1 {
  constructor(_clientInfo, options) {
    super(_clientInfo, options);
  }
  async listTools(params, options) {
    const result = await this.request(
      { method: "tools/list", params },
      ListToolsResultSchema,
      options
    );
    return result;
  }
};
var createServerClient = async (mcpServer, signal, extraHeaders) => {
  const transport = createTransport(mcpServer.connection, signal, extraHeaders);
  if (!transport) {
    throw new Error("Unknown MCP connection type");
  }
  const client = new Client({
    name: mcpServer?.name ?? "MCP Client",
    version: "1.0.0"
  });
  await client.connect(transport);
  return {
    client,
    callStreamableTool: (tool, args) => {
      if (mcpServer.connection.type !== "HTTP") {
        throw new Error("HTTP connection required");
      }
      return fetch(mcpServer.connection.url + `/call-tool/${tool}`, {
        method: "POST",
        redirect: "manual",
        body: JSON.stringify(args),
        headers: {
          ...extraHeaders,
          Authorization: `Bearer ${mcpServer.connection.token}`
        }
      });
    }
  };
};
var createTransport = (connection, signal, extraHeaders) => {
  if (connection.type === "Websocket") {
    return new WebSocketClientTransport(new URL(connection.url));
  }
  if (connection.type !== "SSE" && connection.type !== "HTTP") {
    return null;
  }
  const authHeaders = connection.token ? { authorization: `Bearer ${connection.token}` } : {};
  const headers = {
    ...authHeaders,
    ...extraHeaders ?? {},
    ..."headers" in connection ? connection.headers || {} : {}
  };
  if (connection.type === "SSE") {
    const config = {
      requestInit: { headers, signal }
    };
    if (connection.token) {
      config.eventSourceInit = {
        fetch: (req, init) => {
          return fetch(req, {
            ...init,
            headers: {
              ...headers,
              Accept: "text/event-stream"
            },
            signal
          });
        }
      };
    }
    return new SSEClientTransport(new URL(connection.url), config);
  }
  return new HTTPClientTransport(new URL(connection.url), {
    requestInit: {
      headers,
      signal,
      // @ts-ignore - this is a valid option for fetch
      credentials: "include"
    }
  });
};

// src/core/client/proxy.ts
var safeParse = (content) => {
  try {
    return JSON.parse(content);
  } catch {
    return content;
  }
};
var toolsMap = /* @__PURE__ */ new Map();
function createMCPClientProxy(options) {
  return new Proxy({}, {
    get(_, name) {
      if (name === "toJSON") {
        return null;
      }
      if (typeof name !== "string") {
        throw new Error("Name must be a string");
      }
      async function callToolFn(args) {
        const debugId = options?.debugId?.();
        const extraHeaders = debugId ? { "x-trace-debug-id": debugId } : void 0;
        const { client, callStreamableTool } = await createServerClient(
          { connection: options.connection },
          void 0,
          extraHeaders
        );
        if (options?.streamable?.[String(name)]) {
          return callStreamableTool(String(name), args);
        }
        const { structuredContent, isError, content } = await client.callTool(
          {
            name: String(name),
            arguments: args
          },
          void 0,
          {
            timeout: 3e6
          }
        );
        if (isError) {
          const maybeErrorMessage = content?.[0]?.text;
          const error = typeof maybeErrorMessage === "string" ? safeParse(maybeErrorMessage) : null;
          const throwableError = error?.code && typeof options?.getErrorByStatusCode === "function" ? options.getErrorByStatusCode(
            error.code,
            error.message,
            error.traceId
          ) : null;
          if (throwableError) {
            throw throwableError;
          }
          throw new Error(
            `Tool ${String(name)} returned an error: ${JSON.stringify(
              structuredContent ?? content
            )}`
          );
        }
        return structuredContent;
      }
      const listToolsFn = async () => {
        const { client } = await createServerClient({
          connection: options.connection
        });
        const { tools } = await client.listTools();
        return tools;
      };
      async function listToolsOnce() {
        const conn = options.connection;
        const key = JSON.stringify(conn);
        try {
          if (!toolsMap.has(key)) {
            toolsMap.set(key, listToolsFn());
          }
          return await toolsMap.get(key);
        } catch (error) {
          console.error("Failed to list tools", error);
          toolsMap.delete(key);
          return;
        }
      }
      callToolFn.asTool = async () => {
        const tools = await listToolsOnce() ?? [];
        const tool = tools.find((t) => t.name === name);
        if (!tool) {
          throw new Error(`Tool ${name} not found`);
        }
        return {
          ...tool,
          id: tool.name,
          inputSchema: tool.inputSchema ? convertJsonSchemaToZod(tool.inputSchema) : void 0,
          outputSchema: tool.outputSchema ? convertJsonSchemaToZod(tool.outputSchema) : void 0,
          execute: (input) => {
            return callToolFn(input.context);
          }
        };
      };
      return callToolFn;
    }
  });
}

// src/core/client/mcp.ts
new Proxy(
  {},
  {
    get(_, name) {
      if (name === "toJSON") {
        return null;
      }
      if (name === "forConnection") {
        return (connection) => createMCPFetchStub({
          connection
        });
      }
      return global[name];
    }
  }
);
function createMCPFetchStub(options) {
  return createMCPClientProxy(options);
}

// src/core/binder.ts
function normalizeSchema(schema) {
  if (!schema) return void 0;
  if (schema._def) {
    const jsonSchema2 = zodToJsonSchema(schema, {
      // Don't add additionalProperties: false to allow structural compatibility
      $refStrategy: "none"
    });
    if (jsonSchema2.type === "object") {
      delete jsonSchema2.additionalProperties;
    }
    return jsonSchema2;
  }
  const jsonSchema = schema;
  if (jsonSchema.type === "object" && "additionalProperties" in jsonSchema) {
    const copy = { ...jsonSchema };
    delete copy.additionalProperties;
    return copy;
  }
  return jsonSchema;
}
function createBindingChecker(binderTools) {
  return {
    isImplementedBy: async (tools) => {
      for (const binderTool of binderTools) {
        const pattern = typeof binderTool.name === "string" ? new RegExp(`^${binderTool.name}$`) : binderTool.name;
        const matchedTool = tools.find((t) => pattern.test(t.name));
        if (!matchedTool && binderTool.opt) {
          continue;
        }
        if (!matchedTool) {
          return false;
        }
        const binderInputSchema = normalizeSchema(binderTool.inputSchema);
        const toolInputSchema = normalizeSchema(matchedTool.inputSchema);
        if (binderInputSchema && toolInputSchema) {
          try {
            const inputDiff = await diffSchemas({
              sourceSchema: binderInputSchema,
              destinationSchema: toolInputSchema
            });
            if (inputDiff.removalsFound) {
              return false;
            }
          } catch (error) {
            console.error("Schema diff failed", error);
            return false;
          }
        } else if (binderInputSchema && !toolInputSchema) {
          return false;
        }
        const binderOutputSchema = normalizeSchema(binderTool.outputSchema);
        const toolOutputSchema = normalizeSchema(matchedTool.outputSchema);
        if (binderOutputSchema && toolOutputSchema) {
          try {
            const outputDiff = await diffSchemas({
              sourceSchema: binderOutputSchema,
              destinationSchema: toolOutputSchema
            });
            if (outputDiff.removalsFound) {
              return false;
            }
          } catch (error) {
            console.error("Schema diff failed", error);
            return false;
          }
        } else if (binderOutputSchema && !toolOutputSchema) {
          return false;
        }
      }
      return true;
    }
  };
}

export { createBindingChecker };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map