diff --git a/index.d.ts b/index.d.ts index 01e49c0..f1cb56d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -15,33 +15,46 @@ * limitations under the License. */ -import type { LaunchOptions } from 'playwright'; import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; type ToolCapability = 'core' | 'tabs' | 'pdf' | 'history' | 'wait' | 'files' | 'install'; type Options = { - /** - * Path to the user data directory. - */ - userDataDir?: string; - - /** - * Launch options for the browser. - */ - launchOptions?: LaunchOptions; - - /** - * Use screenshots instead of snapshots. Less accurate, reliable and overall - * slower, but contains visual representation of the page. - * @default false - */ - vision?: boolean; - - /** - * Capabilities to enable. - */ - capabilities?: ToolCapability[]; + /** + * The browser to use (e.g., 'chrome', 'chromium', 'firefox', 'webkit', 'msedge'). + */ + browser?: string; + /** + * Path to a user data directory for browser profile persistence. + */ + userDataDir?: string; + /** + * Whether to run the browser in headless mode (default: true). + */ + headless?: boolean; + /** + * Path to a custom browser executable. + */ + executablePath?: string; + /** + * Chrome DevTools Protocol endpoint to connect to an existing browser instance. + */ + cdpEndpoint?: string; + /** + * Enable vision capabilities (e.g., visual automation or OCR). + */ + vision?: boolean; + /** + * List of enabled tool capabilities. Possible values: + * - 'core': Core browser automation features. + * - 'tabs': Tab management features. + * - 'pdf': PDF generation and manipulation. + * - 'history': Browser history access. + * - 'wait': Wait and timing utilities. + * - 'files': File upload/download support. + * - 'install': Browser installation utilities. + */ + capabilities?: ToolCapability[]; }; - -export function createServer(options?: Options): Server; +export declare function createServer(options?: Options): Promise; +export {}; diff --git a/src/index.ts b/src/index.ts index 0900fb9..d052d8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,10 @@ * limitations under the License. */ +import path from 'path'; +import os from 'os'; +import fs from 'fs'; + import { createServerWithTools } from './server'; import common from './tools/common'; import files from './tools/files'; @@ -58,9 +62,10 @@ const resources: Resource[] = [ ]; type Options = { - browserName?: 'chromium' | 'firefox' | 'webkit'; + browser?: string; userDataDir?: string; - launchOptions?: LaunchOptions; + headless?: boolean; + executablePath?: string; cdpEndpoint?: string; vision?: boolean; capabilities?: ToolCapability[]; @@ -68,7 +73,42 @@ type Options = { const packageJSON = require('../package.json'); -export function createServer(options?: Options): Server { +export async function createServer(options?: Options): Promise { + let browserName: 'chromium' | 'firefox' | 'webkit'; + let channel: string | undefined; + switch (options?.browser) { + case 'chrome': + case 'chrome-beta': + case 'chrome-canary': + case 'chrome-dev': + case 'msedge': + case 'msedge-beta': + case 'msedge-canary': + case 'msedge-dev': + browserName = 'chromium'; + channel = options.browser; + break; + case 'chromium': + browserName = 'chromium'; + break; + case 'firefox': + browserName = 'firefox'; + break; + case 'webkit': + browserName = 'webkit'; + break; + default: + browserName = 'chromium'; + channel = 'chrome'; + } + const userDataDir = options?.userDataDir ?? await createUserDataDir(browserName); + + const launchOptions: LaunchOptions = { + headless: !!(options?.headless ?? (os.platform() === 'linux' && !process.env.DISPLAY)), + channel, + executablePath: options?.executablePath, + }; + const allTools = options?.vision ? screenshotTools : snapshotTools; const tools = allTools.filter(tool => !options?.capabilities || tool.capability === 'core' || options.capabilities.includes(tool.capability)); return createServerWithTools({ @@ -76,9 +116,24 @@ export function createServer(options?: Options): Server { version: packageJSON.version, tools, resources, - browserName: options?.browserName, - userDataDir: options?.userDataDir ?? '', - launchOptions: options?.launchOptions, + browserName, + userDataDir, + launchOptions, cdpEndpoint: options?.cdpEndpoint, }); } + +async function createUserDataDir(browserName: 'chromium' | 'firefox' | 'webkit') { + let cacheDirectory: string; + if (process.platform === 'linux') + cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'); + else if (process.platform === 'darwin') + cacheDirectory = path.join(os.homedir(), 'Library', 'Caches'); + else if (process.platform === 'win32') + cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'); + else + throw new Error('Unsupported platform: ' + process.platform); + const result = path.join(cacheDirectory, 'ms-playwright', `mcp-${browserName}-profile`); + await fs.promises.mkdir(result, { recursive: true }); + return result; +} diff --git a/src/program.ts b/src/program.ts index 52419f9..f8a3870 100644 --- a/src/program.ts +++ b/src/program.ts @@ -15,9 +15,6 @@ */ import http from 'http'; -import fs from 'fs'; -import os from 'os'; -import path from 'path'; import { program } from 'commander'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; @@ -27,7 +24,6 @@ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { createServer } from './index'; import { ServerList } from './server'; -import type { LaunchOptions } from 'playwright'; import assert from 'assert'; import { ToolCapability } from './tools/tool'; @@ -45,46 +41,11 @@ program .option('--user-data-dir ', 'Path to the user data directory') .option('--vision', 'Run server that uses screenshots (Aria snapshots are used by default)') .action(async options => { - let browserName: 'chromium' | 'firefox' | 'webkit'; - let channel: string | undefined; - switch (options.browser) { - case 'chrome': - case 'chrome-beta': - case 'chrome-canary': - case 'chrome-dev': - case 'msedge': - case 'msedge-beta': - case 'msedge-canary': - case 'msedge-dev': - browserName = 'chromium'; - channel = options.browser; - break; - case 'chromium': - browserName = 'chromium'; - break; - case 'firefox': - browserName = 'firefox'; - break; - case 'webkit': - browserName = 'webkit'; - break; - default: - browserName = 'chromium'; - channel = 'chrome'; - } - - const launchOptions: LaunchOptions = { - headless: !!(options.headless ?? (os.platform() === 'linux' && !process.env.DISPLAY)), - channel, - executablePath: options.executablePath, - }; - - const userDataDir = options.userDataDir ?? await createUserDataDir(browserName); - const serverList = new ServerList(() => createServer({ - browserName, - userDataDir, - launchOptions, + browser: options.browser, + userDataDir: options.userDataDir, + headless: options.headless, + executablePath: options.executablePath, vision: !!options.vision, cdpEndpoint: options.cdpEndpoint, capabilities: options.caps?.split(',').map((c: string) => c.trim() as ToolCapability), @@ -113,21 +74,6 @@ function setupExitWatchdog(serverList: ServerList) { program.parse(process.argv); -async function createUserDataDir(browserName: 'chromium' | 'firefox' | 'webkit') { - let cacheDirectory: string; - if (process.platform === 'linux') - cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'); - else if (process.platform === 'darwin') - cacheDirectory = path.join(os.homedir(), 'Library', 'Caches'); - else if (process.platform === 'win32') - cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'); - else - throw new Error('Unsupported platform: ' + process.platform); - const result = path.join(cacheDirectory, 'ms-playwright', `mcp-${browserName}-profile`); - await fs.promises.mkdir(result, { recursive: true }); - return result; -} - async function startSSEServer(port: number, serverList: ServerList) { const sessions = new Map(); const httpServer = http.createServer(async (req, res) => { diff --git a/src/server.ts b/src/server.ts index 2b20f98..8659102 100644 --- a/src/server.ts +++ b/src/server.ts @@ -89,14 +89,14 @@ export function createServerWithTools(options: Options): Server { export class ServerList { private _servers: Server[] = []; - private _serverFactory: () => Server; + private _serverFactory: () => Promise; - constructor(serverFactory: () => Server) { + constructor(serverFactory: () => Promise) { this._serverFactory = serverFactory; } async create() { - const server = this._serverFactory(); + const server = await this._serverFactory(); this._servers.push(server); return server; }