Skip to main content
Glama

Safari MCP Server

by lxman
safari-mcp-server.ts•14.5 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { SafariDriverManager } from './safari-driver.js'; import { LogLevel } from './types.js'; export class SafariMCPServer { private server: Server; private driverManager: SafariDriverManager; constructor() { this.server = new Server({ name: 'safari-devtools-mcp', version: '1.0.0' }, { capabilities: { tools: {} } }); this.driverManager = new SafariDriverManager(); this.setupHandlers(); } private setupHandlers(): void { // Tool call handler this.server.setRequestHandler( CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; return { content: await this.handleToolCall(name, args || {}) }; } ); // Tool list handler this.server.setRequestHandler( ListToolsRequestSchema, async () => ({ tools: [ { name: 'safari_start_session', description: 'Start a new Safari automation session with dev tools access', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Unique session identifier' }, options: { type: 'object', properties: { enableInspection: { type: 'boolean', description: 'Enable Web Inspector for debugging' }, enableProfiling: { type: 'boolean', description: 'Enable timeline profiling' }, usesTechnologyPreview: { type: 'boolean', description: 'Use Safari Technology Preview' } } } }, required: ['sessionId'] } }, { name: 'safari_navigate', description: 'Navigate to a URL in Safari', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' }, url: { type: 'string', description: 'URL to navigate to' } }, required: ['sessionId', 'url'] } }, { name: 'safari_get_console_logs', description: 'Get browser console logs for debugging', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' }, logLevel: { type: 'string', enum: ['ALL', 'DEBUG', 'INFO', 'WARNING', 'SEVERE'], description: 'Filter logs by level' } }, required: ['sessionId'] } }, { name: 'safari_get_network_logs', description: 'Get network activity logs for performance analysis', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' } }, required: ['sessionId'] } }, { name: 'safari_clear_console_logs', description: 'Clear captured console logs for a session', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' } }, required: ['sessionId'] } }, { name: 'safari_clear_network_logs', description: 'Clear captured network logs for a session', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' } }, required: ['sessionId'] } }, { name: 'safari_execute_script', description: 'Execute JavaScript in the browser context', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' }, script: { type: 'string', description: 'JavaScript code to execute' }, args: { type: 'array', description: 'Arguments to pass to the script' } }, required: ['sessionId', 'script'] } }, { name: 'safari_take_screenshot', description: 'Take a screenshot of the current page', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' } }, required: ['sessionId'] } }, { name: 'safari_inspect_element', description: 'Inspect a DOM element and get its properties', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' }, selector: { type: 'string', description: 'CSS selector for the element' } }, required: ['sessionId', 'selector'] } }, { name: 'safari_get_performance_metrics', description: 'Get page performance metrics', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' } }, required: ['sessionId'] } }, { name: 'safari_get_page_info', description: 'Get current page URL and title', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' } }, required: ['sessionId'] } }, { name: 'safari_list_sessions', description: 'List all active Safari sessions', inputSchema: { type: 'object', properties: {} } }, { name: 'safari_close_session', description: 'Close a Safari automation session', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' } }, required: ['sessionId'] } } ] }) ); } private async handleToolCall(name: string, args: Record<string, any>): Promise<Array<{ type: string; text?: string; data?: string; mimeType?: string }>> { try { switch (name) { case 'safari_start_session': return await this.startSession(args); case 'safari_navigate': return await this.navigate(args); case 'safari_get_console_logs': return await this.getConsoleLogs(args); case 'safari_get_network_logs': return await this.getNetworkLogs(args); case 'safari_clear_console_logs': return await this.clearConsoleLogs(args); case 'safari_clear_network_logs': return await this.clearNetworkLogs(args); case 'safari_execute_script': return await this.executeScript(args); case 'safari_take_screenshot': return await this.takeScreenshot(args); case 'safari_inspect_element': return await this.inspectElement(args); case 'safari_get_performance_metrics': return await this.getPerformanceMetrics(args); case 'safari_get_page_info': return await this.getPageInfo(args); case 'safari_list_sessions': return await this.listSessions(); case 'safari_close_session': return await this.closeSession(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return [ { type: 'text', text: `Error: ${errorMessage}` } ]; } } private async startSession(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId, options = {} } = args; await this.driverManager.createSession(sessionId, options); return [ { type: 'text', text: `Safari session '${sessionId}' started successfully with dev tools enabled.\nInspection: ${options.enableInspection !== false}\nProfiling: ${options.enableProfiling !== false}\nTechnology Preview: ${options.usesTechnologyPreview === true}` } ]; } private async navigate(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId, url } = args; await this.driverManager.navigateToUrl(sessionId, url); return [ { type: 'text', text: `Successfully navigated to: ${url}` } ]; } private async getConsoleLogs(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId, logLevel = 'ALL' } = args; const logs = await this.driverManager.getConsoleLogs(sessionId, logLevel as LogLevel); return [ { type: 'text', text: `Console Logs (${logs.length} entries):\n\n${JSON.stringify(logs, null, 2)}` } ]; } private async getNetworkLogs(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId } = args; const logs = await this.driverManager.getNetworkLogs(sessionId); return [ { type: 'text', text: `Network Logs (${logs.length} entries):\n\n${JSON.stringify(logs, null, 2)}` } ]; } private async executeScript(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId, script, args: scriptArgs = [] } = args; const result = await this.driverManager.executeScript(sessionId, script, scriptArgs); return [ { type: 'text', text: `Script execution result:\n${JSON.stringify(result, null, 2)}` } ]; } private async takeScreenshot(args: Record<string, any>): Promise<Array<{ type: string; text?: string; data?: string; mimeType?: string }>> { const { sessionId } = args; const screenshot = await this.driverManager.takeScreenshot(sessionId); return [ { type: 'text', text: `Screenshot captured successfully (${screenshot.length} bytes base64 data)` }, { type: 'image', data: screenshot, mimeType: 'image/png' } ]; } private async inspectElement(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId, selector } = args; const elementInfo = await this.driverManager.inspectElement(sessionId, selector); return [ { type: 'text', text: `Element inspection for selector '${selector}':\n\n${JSON.stringify(elementInfo, null, 2)}` } ]; } private async getPerformanceMetrics(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId } = args; const metrics = await this.driverManager.getPerformanceMetrics(sessionId); return [ { type: 'text', text: `Performance Metrics:\n\n${JSON.stringify(metrics, null, 2)}` } ]; } private async getPageInfo(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId } = args; const [url, title] = await Promise.all([ this.driverManager.getCurrentUrl(sessionId), this.driverManager.getPageTitle(sessionId) ]); return [ { type: 'text', text: `Page Info:\nURL: ${url}\nTitle: ${title}` } ]; } private async listSessions(): Promise<Array<{ type: string; text: string }>> { const sessions = this.driverManager.getAllSessions(); return [ { type: 'text', text: `Active Safari Sessions (${sessions.length}):\n${sessions.join('\n')}` } ]; } private async closeSession(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId } = args; await this.driverManager.closeSession(sessionId); return [ { type: 'text', text: `Safari session '${sessionId}' closed successfully` } ]; } private async clearConsoleLogs(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId } = args; await this.driverManager.clearConsoleLogs(sessionId); return [ { type: 'text', text: `Console logs cleared for session '${sessionId}'` } ]; } private async clearNetworkLogs(args: Record<string, any>): Promise<Array<{ type: string; text: string }>> { const { sessionId } = args; await this.driverManager.clearNetworkLogs(sessionId); return [ { type: 'text', text: `Network logs cleared for session '${sessionId}'` } ]; } async start(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Safari MCP Server running on stdio'); } async shutdown(): Promise<void> { try { await this.driverManager.closeAllSessions(); console.error('All Safari sessions closed'); } catch (error) { console.error('Error during shutdown:', 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/lxman/safari-mcp-server'

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