import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  ListToolsRequestSchema,
  CallToolRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
import { zodToJsonSchema } from "zod-to-json-schema";
import { getToolRegistry } from "./tool-registry.js";
import { executeTool } from "./tool-executor.js";
import { loadAllModuleTools, indexToolsForSearch } from "./tool-loader.js";
import { authenticateMcpRequest, hasRequiredFeatures } from "./auth.js";
async function createMcpServer(options) {
  const { config, container, context, apiKeySecret } = options;
  let tenantId = null;
  let organizationId = null;
  let userId = null;
  let userFeatures = [];
  let isSuperAdmin = false;
  if (apiKeySecret) {
    const authResult = await authenticateMcpRequest(apiKeySecret, container);
    if (!authResult.success) {
      throw new Error(`API key authentication failed: ${authResult.error}`);
    }
    tenantId = authResult.tenantId;
    organizationId = authResult.organizationId;
    userId = authResult.userId;
    userFeatures = authResult.features;
    isSuperAdmin = authResult.isSuperAdmin;
    console.error(`[MCP Server] Authenticated via API key: ${authResult.keyName}`);
  } else if (context) {
    tenantId = context.tenantId;
    organizationId = context.organizationId;
    userId = context.userId;
    if (userId) {
      try {
        const rbacService = container.resolve("rbacService");
        const acl = await rbacService.loadAcl(userId, {
          tenantId,
          organizationId
        });
        userFeatures = acl.features;
        isSuperAdmin = acl.isSuperAdmin;
      } catch (error) {
        console.error("[MCP Server] Failed to load user ACL:", error);
      }
    } else {
      isSuperAdmin = true;
      console.error("[MCP Server] No user specified, running with superadmin access");
    }
  } else {
    isSuperAdmin = true;
    console.error("[MCP Server] No auth context, running with superadmin access");
  }
  const toolContext = {
    tenantId,
    organizationId,
    userId,
    container,
    userFeatures,
    isSuperAdmin,
    apiKeySecret
  };
  const server = new Server(
    { name: config.name, version: config.version },
    { capabilities: { tools: {} } }
  );
  server.setRequestHandler(ListToolsRequestSchema, async () => {
    const registry = getToolRegistry();
    const tools = Array.from(registry.getTools().values());
    const rbacService = container.resolve("rbacService");
    const accessibleTools = tools.filter(
      (tool) => hasRequiredFeatures(tool.requiredFeatures, userFeatures, isSuperAdmin, rbacService)
    );
    if (config.debug) {
      console.error(
        `[MCP Server] Listing ${accessibleTools.length}/${tools.length} tools (filtered by ACL)`
      );
    }
    return {
      tools: accessibleTools.map((tool) => ({
        name: tool.name,
        description: tool.description,
        // Cast to any for Zod v4 compatibility with zod-to-json-schema
        inputSchema: zodToJsonSchema(tool.inputSchema)
      }))
    };
  });
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    if (config.debug) {
      console.error(`[MCP Server] Calling tool: ${name}`, JSON.stringify(args));
    }
    const result = await executeTool(name, args ?? {}, toolContext);
    if (!result.success) {
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify({ error: result.error, code: result.errorCode })
          }
        ],
        isError: true
      };
    }
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(result.result, null, 2)
        }
      ]
    };
  });
  return server;
}
async function runMcpServer(options) {
  try {
    const { extractEntityGraph, cacheEntityGraph } = await import("./entity-graph.js");
    const { getOrm } = await import("@open-mercato/shared/lib/db/mikro");
    const orm = await getOrm();
    const graph = await extractEntityGraph(orm);
    cacheEntityGraph(graph);
    console.error(`[MCP Server] Entity graph: ${graph.nodes.length} entities`);
  } catch (error) {
    console.error("[MCP Server] Entity graph skipped:", error instanceof Error ? error.message : error);
  }
  try {
    const { getRawOpenApiSpec } = await import("./api-endpoint-index.js");
    await getRawOpenApiSpec();
    console.error("[MCP Server] Raw OpenAPI spec cached for Code Mode");
  } catch (error) {
    console.error("[MCP Server] Raw OpenAPI spec caching skipped:", error instanceof Error ? error.message : error);
  }
  await loadAllModuleTools();
  try {
    const searchService = options.container.resolve("searchService");
    await indexToolsForSearch(searchService);
  } catch (error) {
    console.error("[MCP Server] Search indexing skipped (search service not available)");
  }
  const server = await createMcpServer(options);
  const transport = new StdioServerTransport();
  const toolCount = getToolRegistry().listToolNames().length;
  console.error(`[MCP Server] Starting ${options.config.name} v${options.config.version}`);
  if (options.apiKeySecret) {
    console.error(`[MCP Server] Authentication: API key`);
  } else if (options.context) {
    console.error(`[MCP Server] Tenant: ${options.context.tenantId ?? "(none)"}`);
    console.error(`[MCP Server] Organization: ${options.context.organizationId ?? "(none)"}`);
    console.error(`[MCP Server] User: ${options.context.userId ?? "(superadmin)"}`);
  } else {
    console.error(`[MCP Server] Authentication: none (superadmin mode)`);
  }
  console.error(`[MCP Server] Tools registered: ${toolCount}`);
  await server.connect(transport);
  console.error("[MCP Server] Connected and ready for requests");
  const shutdown = async () => {
    console.error("[MCP Server] Shutting down...");
    await server.close();
    process.exit(0);
  };
  process.on("SIGINT", shutdown);
  process.on("SIGTERM", shutdown);
}
export {
  createMcpServer,
  runMcpServer
};
//# sourceMappingURL=mcp-server.js.map
