import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  McpError,
  ErrorCode
} from "@modelcontextprotocol/sdk/types.js";
import puppeteer from "puppeteer";
import { logger } from "../../core/monitoring/logger.js";
class BrowserMCPIntegration {
  // 30 minutes
  constructor(config = {}) {
    this.config = config;
    this.startCleanupInterval();
  }
  sessions = /* @__PURE__ */ new Map();
  server;
  maxSessions = 5;
  sessionTimeout = 30 * 60 * 1e3;
  /**
   * Initialize the Browser MCP server
   */
  async initialize(mcpServer) {
    this.server = mcpServer || new Server(
      {
        name: "stackmemory-browser",
        version: "1.0.0"
      },
      {
        capabilities: {
          tools: {}
        }
      }
    );
    this.setupHandlers();
    logger.info("Browser MCP integration initialized");
  }
  /**
   * Set up MCP request handlers
   */
  setupHandlers() {
    if (!this.server) return;
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: "browser_navigate",
          description: "Navigate to a URL in the browser",
          inputSchema: {
            type: "object",
            properties: {
              url: { type: "string", description: "URL to navigate to" },
              sessionId: { type: "string", description: "Optional session ID" }
            },
            required: ["url"]
          }
        },
        {
          name: "browser_screenshot",
          description: "Take a screenshot of the current page",
          inputSchema: {
            type: "object",
            properties: {
              sessionId: { type: "string", description: "Session ID" },
              fullPage: { type: "boolean", description: "Capture full page" },
              selector: {
                type: "string",
                description: "CSS selector to screenshot"
              }
            },
            required: ["sessionId"]
          }
        },
        {
          name: "browser_click",
          description: "Click an element on the page",
          inputSchema: {
            type: "object",
            properties: {
              sessionId: { type: "string", description: "Session ID" },
              selector: {
                type: "string",
                description: "CSS selector to click"
              }
            },
            required: ["sessionId", "selector"]
          }
        },
        {
          name: "browser_type",
          description: "Type text into an input field",
          inputSchema: {
            type: "object",
            properties: {
              sessionId: { type: "string", description: "Session ID" },
              selector: {
                type: "string",
                description: "CSS selector of input"
              },
              text: { type: "string", description: "Text to type" }
            },
            required: ["sessionId", "selector", "text"]
          }
        },
        {
          name: "browser_evaluate",
          description: "Execute JavaScript in the browser context",
          inputSchema: {
            type: "object",
            properties: {
              sessionId: { type: "string", description: "Session ID" },
              script: {
                type: "string",
                description: "JavaScript code to execute"
              }
            },
            required: ["sessionId", "script"]
          }
        },
        {
          name: "browser_wait",
          description: "Wait for an element or condition",
          inputSchema: {
            type: "object",
            properties: {
              sessionId: { type: "string", description: "Session ID" },
              selector: {
                type: "string",
                description: "CSS selector to wait for"
              },
              timeout: {
                type: "number",
                description: "Timeout in milliseconds"
              }
            },
            required: ["sessionId"]
          }
        },
        {
          name: "browser_get_content",
          description: "Get the text content of the page or element",
          inputSchema: {
            type: "object",
            properties: {
              sessionId: { type: "string", description: "Session ID" },
              selector: {
                type: "string",
                description: "CSS selector (optional)"
              }
            },
            required: ["sessionId"]
          }
        },
        {
          name: "browser_close",
          description: "Close a browser session",
          inputSchema: {
            type: "object",
            properties: {
              sessionId: { type: "string", description: "Session ID to close" }
            },
            required: ["sessionId"]
          }
        }
      ]
    }));
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;
      if (!args) {
        throw new McpError(ErrorCode.InvalidParams, "Missing arguments");
      }
      try {
        switch (name) {
          case "browser_navigate":
            return await this.navigate(
              String(args.url),
              args.sessionId
            );
          case "browser_screenshot":
            return await this.screenshot(
              String(args.sessionId),
              args.fullPage,
              args.selector
            );
          case "browser_click":
            return await this.click(
              String(args.sessionId),
              String(args.selector)
            );
          case "browser_type":
            return await this.type(
              String(args.sessionId),
              String(args.selector),
              String(args.text)
            );
          case "browser_evaluate":
            return await this.evaluate(
              String(args.sessionId),
              String(args.script)
            );
          case "browser_wait":
            return await this.waitFor(
              String(args.sessionId),
              args.selector,
              args.timeout
            );
          case "browser_get_content":
            return await this.getContent(
              String(args.sessionId),
              args.selector
            );
          case "browser_close":
            return await this.closeSession(String(args.sessionId));
          default:
            throw new McpError(
              ErrorCode.MethodNotFound,
              `Unknown tool: ${name}`
            );
        }
      } catch (error) {
        logger.error("Browser MCP tool error", error);
        throw new McpError(ErrorCode.InternalError, error.message);
      }
    });
  }
  /**
   * Navigate to a URL
   */
  async navigate(url, sessionId) {
    const session = await this.getOrCreateSession(sessionId);
    await session.page.goto(url, { waitUntil: "networkidle2" });
    session.url = url;
    session.lastActivity = /* @__PURE__ */ new Date();
    logger.info(`Browser navigated to ${url}`, { sessionId: session.id });
    return {
      content: [
        {
          type: "text",
          text: `Navigated to ${url}`
        }
      ],
      sessionId: session.id,
      url
    };
  }
  /**
   * Take a screenshot
   */
  async screenshot(sessionId, fullPage = false, selector) {
    const session = this.getSession(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }
    let screenshot;
    if (selector) {
      const element = await session.page.$(selector);
      if (!element) {
        throw new Error(`Element ${selector} not found`);
      }
      screenshot = Buffer.from(await element.screenshot());
    } else {
      screenshot = Buffer.from(await session.page.screenshot({ fullPage }));
    }
    session.lastActivity = /* @__PURE__ */ new Date();
    return {
      content: [
        {
          type: "image",
          data: screenshot.toString("base64")
        }
      ],
      sessionId: session.id
    };
  }
  /**
   * Click an element
   */
  async click(sessionId, selector) {
    const session = this.getSession(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }
    await session.page.click(selector);
    session.lastActivity = /* @__PURE__ */ new Date();
    return {
      content: [
        {
          type: "text",
          text: `Clicked element: ${selector}`
        }
      ],
      sessionId: session.id
    };
  }
  /**
   * Type text into an input
   */
  async type(sessionId, selector, text) {
    const session = this.getSession(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }
    await session.page.type(selector, text);
    session.lastActivity = /* @__PURE__ */ new Date();
    return {
      content: [
        {
          type: "text",
          text: `Typed "${text}" into ${selector}`
        }
      ],
      sessionId: session.id
    };
  }
  /**
   * Execute JavaScript in page context
   */
  async evaluate(sessionId, script) {
    const session = this.getSession(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }
    const result = await session.page.evaluate(script);
    session.lastActivity = /* @__PURE__ */ new Date();
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(result, null, 2)
        }
      ],
      sessionId: session.id,
      result
    };
  }
  /**
   * Wait for element or timeout
   */
  async waitFor(sessionId, selector, timeout = 5e3) {
    const session = this.getSession(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }
    if (selector) {
      await session.page.waitForSelector(selector, { timeout });
    } else {
      await new Promise((resolve) => setTimeout(resolve, timeout));
    }
    session.lastActivity = /* @__PURE__ */ new Date();
    return {
      content: [
        {
          type: "text",
          text: selector ? `Element ${selector} found` : `Waited ${timeout}ms`
        }
      ],
      sessionId: session.id
    };
  }
  /**
   * Get page content
   */
  async getContent(sessionId, selector) {
    const session = this.getSession(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }
    let content;
    if (selector) {
      content = await session.page.$eval(
        selector,
        (el) => el.textContent || ""
      );
    } else {
      content = await session.page.content();
    }
    session.lastActivity = /* @__PURE__ */ new Date();
    return {
      content: [
        {
          type: "text",
          text: content
        }
      ],
      sessionId: session.id
    };
  }
  /**
   * Get or create a browser session
   */
  async getOrCreateSession(sessionId) {
    if (sessionId) {
      const existing = this.sessions.get(sessionId);
      if (existing) {
        existing.lastActivity = /* @__PURE__ */ new Date();
        return existing;
      }
    }
    if (this.sessions.size >= this.maxSessions) {
      const oldest = Array.from(this.sessions.values()).sort(
        (a, b) => a.lastActivity.getTime() - b.lastActivity.getTime()
      )[0];
      await this.closeSession(oldest.id);
    }
    const browser = await puppeteer.launch({
      headless: this.config.headless ?? true,
      defaultViewport: this.config.defaultViewport || {
        width: 1280,
        height: 720
      },
      userDataDir: this.config.userDataDir,
      executablePath: this.config.executablePath,
      args: ["--no-sandbox", "--disable-setuid-sandbox"]
      // For Railway/Docker
    });
    const page = await browser.newPage();
    const id = sessionId || `session-${Date.now()}`;
    const session = {
      id,
      browser,
      page,
      createdAt: /* @__PURE__ */ new Date(),
      lastActivity: /* @__PURE__ */ new Date()
    };
    this.sessions.set(id, session);
    logger.info(`Created browser session ${id}`);
    return session;
  }
  /**
   * Get existing session
   */
  getSession(sessionId) {
    return this.sessions.get(sessionId);
  }
  /**
   * Close a browser session
   */
  async closeSession(sessionId) {
    const session = this.sessions.get(sessionId);
    if (!session) {
      return {
        content: [
          {
            type: "text",
            text: `Session ${sessionId} not found`
          }
        ]
      };
    }
    await session.browser.close();
    this.sessions.delete(sessionId);
    logger.info(`Closed browser session ${sessionId}`);
    return {
      content: [
        {
          type: "text",
          text: `Session ${sessionId} closed`
        }
      ]
    };
  }
  /**
   * Clean up inactive sessions
   */
  startCleanupInterval() {
    setInterval(async () => {
      const now = Date.now();
      for (const [id, session] of this.sessions.entries()) {
        const inactiveTime = now - session.lastActivity.getTime();
        if (inactiveTime > this.sessionTimeout) {
          logger.info(`Cleaning up inactive session ${id}`);
          await this.closeSession(id);
        }
      }
    }, 6e4);
  }
  /**
   * Close all sessions
   */
  async cleanup() {
    for (const sessionId of this.sessions.keys()) {
      await this.closeSession(sessionId);
    }
  }
}
export {
  BrowserMCPIntegration
};
//# sourceMappingURL=browser-mcp.js.map
