Skip to main content
Glama

Firefox MCP Server

by JediLuke
index-multi.js23.9 kB
// #!/usr/bin/env node // import { Server } from '@modelcontextprotocol/sdk/server/index.js'; // import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; // import { // CallToolRequestSchema, // ErrorCode, // ListToolsRequestSchema, // McpError, // } from '@modelcontextprotocol/sdk/types.js'; // import { chromium, firefox } from 'playwright'; // class MultiTabFirefoxMCPServer { // constructor() { // this.server = new Server( // { // name: 'firefox-multi-mcp-server', // version: '2.0.0', // }, // { // capabilities: { // tools: {}, // }, // } // ); // this.browser = null; // this.contexts = new Map(); // Map of contextId -> BrowserContext // this.pages = new Map(); // Map of tabId -> Page // this.activeTabId = null; // this.setupToolHandlers(); // } // setupToolHandlers() { // this.server.setRequestHandler(ListToolsRequestSchema, async () => { // return { // tools: [ // { // name: 'launch_firefox_multi', // description: 'Launch Firefox browser with multi-tab support', // inputSchema: { // type: 'object', // properties: { // headless: { // type: 'boolean', // description: 'Run browser in headless mode', // default: false // } // } // } // }, // { // name: 'create_tab', // description: 'Create a new tab with isolated session', // inputSchema: { // type: 'object', // properties: { // tabId: { // type: 'string', // description: 'Unique identifier for this tab' // }, // url: { // type: 'string', // description: 'Initial URL to navigate to', // default: 'about:blank' // }, // contextId: { // type: 'string', // description: 'Context ID for session isolation (optional, auto-generated if not provided)' // } // }, // required: ['tabId'] // } // }, // { // name: 'list_tabs', // description: 'List all active tabs', // inputSchema: { // type: 'object', // properties: {} // } // }, // { // name: 'close_tab', // description: 'Close a specific tab', // inputSchema: { // type: 'object', // properties: { // tabId: { // type: 'string', // description: 'Tab ID to close' // } // }, // required: ['tabId'] // } // }, // { // name: 'navigate', // description: 'Navigate to a URL', // inputSchema: { // type: 'object', // properties: { // url: { // type: 'string', // description: 'URL to navigate to' // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // }, // required: ['url'] // } // }, // { // name: 'click', // description: 'Click on an element', // inputSchema: { // type: 'object', // properties: { // selector: { // type: 'string', // description: 'CSS selector or text content to click' // }, // coordinates: { // type: 'object', // properties: { // x: { type: 'number' }, // y: { type: 'number' } // }, // description: 'Click at specific coordinates (alternative to selector)' // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'type_text', // description: 'Type text into an input field', // inputSchema: { // type: 'object', // properties: { // selector: { // type: 'string', // description: 'CSS selector of the input field' // }, // text: { // type: 'string', // description: 'Text to type' // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // }, // required: ['selector', 'text'] // } // }, // { // name: 'get_page_content', // description: 'Get the HTML content of the current page', // inputSchema: { // type: 'object', // properties: { // selector: { // type: 'string', // description: 'CSS selector to get specific element content (optional)' // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'get_page_text', // description: 'Get the visible text content of the current page', // inputSchema: { // type: 'object', // properties: { // selector: { // type: 'string', // description: 'CSS selector to get specific element text (optional)' // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'screenshot', // description: 'Take a screenshot of the current page', // inputSchema: { // type: 'object', // properties: { // path: { // type: 'string', // description: 'File path to save screenshot', // default: 'screenshot.png' // }, // fullPage: { // type: 'boolean', // description: 'Capture full page', // default: false // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'wait_for_element', // description: 'Wait for an element to appear on the page', // inputSchema: { // type: 'object', // properties: { // selector: { // type: 'string', // description: 'CSS selector to wait for' // }, // timeout: { // type: 'number', // description: 'Timeout in milliseconds', // default: 30000 // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // }, // required: ['selector'] // } // }, // { // name: 'execute_script', // description: 'Execute JavaScript in the browser', // inputSchema: { // type: 'object', // properties: { // script: { // type: 'string', // description: 'JavaScript code to execute' // }, // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // }, // required: ['script'] // } // }, // { // name: 'close_browser', // description: 'Close the Firefox browser and all tabs', // inputSchema: { // type: 'object', // properties: {} // } // }, // { // name: 'get_current_url', // description: 'Get the current page URL', // inputSchema: { // type: 'object', // properties: { // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'back', // description: 'Navigate back in browser history', // inputSchema: { // type: 'object', // properties: { // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'forward', // description: 'Navigate forward in browser history', // inputSchema: { // type: 'object', // properties: { // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'reload', // description: 'Reload the current page', // inputSchema: { // type: 'object', // properties: { // tabId: { // type: 'string', // description: 'Tab ID (uses active tab if not provided)' // } // } // } // }, // { // name: 'set_active_tab', // description: 'Set the active tab for operations that don\'t specify a tabId', // inputSchema: { // type: 'object', // properties: { // tabId: { // type: 'string', // description: 'Tab ID to set as active' // } // }, // required: ['tabId'] // } // } // ], // }; // }); // this.server.setRequestHandler(CallToolRequestSchema, async (request) => { // const { name, arguments: args } = request.params; // try { // switch (name) { // case 'launch_firefox_multi': // return await this.launchFirefoxMulti(args); // case 'create_tab': // return await this.createTab(args); // case 'list_tabs': // return await this.listTabs(); // case 'close_tab': // return await this.closeTab(args); // case 'set_active_tab': // return await this.setActiveTab(args); // case 'navigate': // return await this.navigate(args); // case 'click': // return await this.click(args); // case 'type_text': // return await this.typeText(args); // case 'get_page_content': // return await this.getPageContent(args); // case 'get_page_text': // return await this.getPageText(args); // case 'screenshot': // return await this.screenshot(args); // case 'wait_for_element': // return await this.waitForElement(args); // case 'execute_script': // return await this.executeScript(args); // case 'close_browser': // return await this.closeBrowser(); // case 'get_current_url': // return await this.getCurrentUrl(args); // case 'back': // return await this.back(args); // case 'forward': // return await this.forward(args); // case 'reload': // return await this.reload(args); // default: // throw new McpError( // ErrorCode.MethodNotFound, // `Unknown tool: ${name}` // ); // } // } catch (error) { // throw new McpError( // ErrorCode.InternalError, // `Error executing ${name}: ${error.message}` // ); // } // }); // } // async launchFirefoxMulti(args = {}) { // const { headless = false } = args; // try { // this.browser = await firefox.launch({ // headless, // firefoxUserPrefs: { // 'dom.webnotifications.enabled': false, // 'media.navigator.permission.disabled': true // } // }); // return { // content: [ // { // type: 'text', // text: `Firefox launched successfully with multi-tab support. Use create_tab to create new tabs.` // } // ] // }; // } catch (error) { // throw new Error(`Failed to launch Firefox: ${error.message}`); // } // } // async createTab(args) { // this.ensureBrowserRunning(); // const { tabId, url = 'about:blank', contextId } = args; // if (this.pages.has(tabId)) { // throw new Error(`Tab with ID '${tabId}' already exists`); // } // // Create or reuse context // const effectiveContextId = contextId || `context-${tabId}`; // let context; // if (this.contexts.has(effectiveContextId)) { // context = this.contexts.get(effectiveContextId); // } else { // context = await this.browser.newContext({ // // Each context has isolated storage, cookies, etc. // storageState: undefined // Start with clean state // }); // this.contexts.set(effectiveContextId, context); // } // // Create new page in the context // const page = await context.newPage(); // await page.goto(url); // this.pages.set(tabId, page); // // Set as active tab if no active tab exists // if (!this.activeTabId) { // this.activeTabId = tabId; // } // return { // content: [ // { // type: 'text', // text: `Tab '${tabId}' created successfully. Navigated to: ${url}. Context: ${effectiveContextId}` // } // ] // }; // } // async listTabs() { // const tabs = []; // for (const [tabId, page] of this.pages) { // const url = page.url(); // const isActive = tabId === this.activeTabId; // tabs.push({ // tabId, // url, // active: isActive // }); // } // return { // content: [ // { // type: 'text', // text: `Active tabs (${tabs.length}):\n` + // tabs.map(tab => `- ${tab.tabId}: ${tab.url}${tab.active ? ' (active)' : ''}`).join('\n') // } // ] // }; // } // async closeTab(args) { // const { tabId } = args; // if (!this.pages.has(tabId)) { // throw new Error(`Tab '${tabId}' not found`); // } // const page = this.pages.get(tabId); // await page.close(); // this.pages.delete(tabId); // // If this was the active tab, clear active tab // if (this.activeTabId === tabId) { // this.activeTabId = this.pages.size > 0 ? Array.from(this.pages.keys())[0] : null; // } // return { // content: [ // { // type: 'text', // text: `Tab '${tabId}' closed successfully.${this.activeTabId ? ` Active tab is now '${this.activeTabId}'` : ''}` // } // ] // }; // } // async setActiveTab(args) { // const { tabId } = args; // if (!this.pages.has(tabId)) { // throw new Error(`Tab '${tabId}' not found`); // } // this.activeTabId = tabId; // return { // content: [ // { // type: 'text', // text: `Active tab set to '${tabId}'` // } // ] // }; // } // getPage(tabId) { // if (tabId) { // if (!this.pages.has(tabId)) { // throw new Error(`Tab '${tabId}' not found`); // } // return this.pages.get(tabId); // } else { // if (!this.activeTabId || !this.pages.has(this.activeTabId)) { // throw new Error('No active tab. Use create_tab or set_active_tab first.'); // } // return this.pages.get(this.activeTabId); // } // } // async navigate(args) { // this.ensureBrowserRunning(); // const { url, tabId } = args; // const page = this.getPage(tabId); // await page.goto(url); // return { // content: [ // { // type: 'text', // text: `Tab '${tabId || this.activeTabId}' navigated to: ${url}` // } // ] // }; // } // async click(args) { // this.ensureBrowserRunning(); // const { selector, coordinates, tabId } = args; // const page = this.getPage(tabId); // if (coordinates) { // await page.click(`body`, { position: coordinates }); // return { // content: [ // { // type: 'text', // text: `Clicked at coordinates (${coordinates.x}, ${coordinates.y}) in tab '${tabId || this.activeTabId}'` // } // ] // }; // } else if (selector) { // await page.click(selector); // return { // content: [ // { // type: 'text', // text: `Clicked element '${selector}' in tab '${tabId || this.activeTabId}'` // } // ] // }; // } else { // throw new Error('Either selector or coordinates must be provided'); // } // } // async typeText(args) { // this.ensureBrowserRunning(); // const { selector, text, tabId } = args; // const page = this.getPage(tabId); // await page.fill(selector, text); // return { // content: [ // { // type: 'text', // text: `Typed "${text}" into '${selector}' in tab '${tabId || this.activeTabId}'` // } // ] // }; // } // async getPageContent(args = {}) { // this.ensureBrowserRunning(); // const { selector, tabId } = args; // const page = this.getPage(tabId); // let content; // if (selector) { // content = await page.innerHTML(selector); // } else { // content = await page.content(); // } // return { // content: [ // { // type: 'text', // text: content // } // ] // }; // } // async getPageText(args = {}) { // this.ensureBrowserRunning(); // const { selector, tabId } = args; // const page = this.getPage(tabId); // let text; // if (selector) { // text = await page.textContent(selector); // } else { // text = await page.textContent('body'); // } // return { // content: [ // { // type: 'text', // text: text || '' // } // ] // }; // } // async screenshot(args = {}) { // this.ensureBrowserRunning(); // const { path = 'screenshot.png', fullPage = false, tabId } = args; // const page = this.getPage(tabId); // // Add tab ID to path if not specified // const effectiveTabId = tabId || this.activeTabId; // const effectivePath = path.includes(effectiveTabId) ? path : // path.replace(/\.([^.]+)$/, `_${effectiveTabId}.$1`); // await page.screenshot({ // path: effectivePath, // fullPage // }); // return { // content: [ // { // type: 'text', // text: `Screenshot of tab '${effectiveTabId}' saved to: ${effectivePath}` // } // ] // }; // } // async waitForElement(args) { // this.ensureBrowserRunning(); // const { selector, timeout = 30000, tabId } = args; // const page = this.getPage(tabId); // await page.waitForSelector(selector, { timeout }); // return { // content: [ // { // type: 'text', // text: `Element '${selector}' found in tab '${tabId || this.activeTabId}'` // } // ] // }; // } // async executeScript(args) { // this.ensureBrowserRunning(); // const { script, tabId } = args; // const page = this.getPage(tabId); // const result = await page.evaluate(script); // return { // content: [ // { // type: 'text', // text: `Script executed in tab '${tabId || this.activeTabId}'. Result: ${JSON.stringify(result)}` // } // ] // }; // } // async closeBrowser() { // if (this.browser) { // await this.browser.close(); // this.browser = null; // this.contexts.clear(); // this.pages.clear(); // this.activeTabId = null; // } // return { // content: [ // { // type: 'text', // text: 'Firefox browser closed, all tabs and contexts cleared' // } // ] // }; // } // async getCurrentUrl(args = {}) { // this.ensureBrowserRunning(); // const { tabId } = args; // const page = this.getPage(tabId); // const url = page.url(); // return { // content: [ // { // type: 'text', // text: `Current URL in tab '${tabId || this.activeTabId}': ${url}` // } // ] // }; // } // async back(args = {}) { // this.ensureBrowserRunning(); // const { tabId } = args; // const page = this.getPage(tabId); // await page.goBack(); // return { // content: [ // { // type: 'text', // text: `Navigated back in tab '${tabId || this.activeTabId}'` // } // ] // }; // } // async forward(args = {}) { // this.ensureBrowserRunning(); // const { tabId } = args; // const page = this.getPage(tabId); // await page.goForward(); // return { // content: [ // { // type: 'text', // text: `Navigated forward in tab '${tabId || this.activeTabId}'` // } // ] // }; // } // async reload(args = {}) { // this.ensureBrowserRunning(); // const { tabId } = args; // const page = this.getPage(tabId); // await page.reload(); // return { // content: [ // { // type: 'text', // text: `Page reloaded in tab '${tabId || this.activeTabId}'` // } // ] // }; // } // ensureBrowserRunning() { // if (!this.browser) { // throw new Error('Firefox browser is not running. Please launch it first using the launch_firefox_multi tool.'); // } // } // async run() { // const transport = new StdioServerTransport(); // await this.server.connect(transport); // console.error('Multi-tab Firefox MCP server running on stdio'); // } // } // const server = new MultiTabFirefoxMCPServer(); // server.run().catch(console.error);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JediLuke/firefox-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server