chrome-mcp
Version:
Model Context Protocol (MCP) server for controlling Google Chrome browser on macOS. Enables AI assistants like Claude to automate Chrome via DevTools Protocol.
321 lines • 10.2 kB
JavaScript
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 { ChromeController } from './chrome-controller.js';
// Create Chrome controller instance
const chromeController = new ChromeController();
// Define available tools
const TOOLS = [
{
name: 'chrome_navigate',
description: 'Navigate to a specific URL in Chrome',
inputSchema: {
type: 'object',
properties: {
url: {
type: 'string',
description: 'The URL to navigate to',
},
},
required: ['url'],
},
},
{
name: 'chrome_get_current_url',
description: 'Get the current URL of the active tab',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_get_title',
description: 'Get the title of the current page',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_get_content',
description: 'Get the HTML content of the current page',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_get_visible_text',
description: 'Get the visible text content of the current page',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_execute_script',
description: 'Execute JavaScript in the current page and return the result',
inputSchema: {
type: 'object',
properties: {
script: {
type: 'string',
description: 'JavaScript code to execute',
},
},
required: ['script'],
},
},
{
name: 'chrome_click',
description: 'Click on an element using a CSS selector',
inputSchema: {
type: 'object',
properties: {
selector: {
type: 'string',
description: 'CSS selector for the element to click',
},
},
required: ['selector'],
},
},
{
name: 'chrome_type',
description: 'Type text into an input field using a CSS selector',
inputSchema: {
type: 'object',
properties: {
selector: {
type: 'string',
description: 'CSS selector for the input element',
},
text: {
type: 'string',
description: 'Text to type into the input field',
},
},
required: ['selector', 'text'],
},
},
{
name: 'chrome_screenshot',
description: 'Take a screenshot of the current page (returns base64 encoded PNG)',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_open_new_tab',
description: 'Open a new tab in Chrome, optionally with a URL',
inputSchema: {
type: 'object',
properties: {
url: {
type: 'string',
description: 'Optional URL to open in the new tab',
},
},
},
},
{
name: 'chrome_close_tab',
description: 'Close the current tab',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_list_tabs',
description: 'List all open tabs with their titles and URLs',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_reload',
description: 'Reload the current page',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_go_back',
description: 'Go back in browser history',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'chrome_go_forward',
description: 'Go forward in browser history',
inputSchema: {
type: 'object',
properties: {},
},
},
];
// Create MCP server
const server = new Server({
name: 'chrome-mcp',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
// Handle tool listing
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: TOOLS,
};
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'chrome_navigate': {
const url = args?.url;
if (!url) {
throw new Error('URL is required');
}
const result = await chromeController.navigate(url);
return {
content: [{ type: 'text', text: result }],
};
}
case 'chrome_get_current_url': {
const url = await chromeController.getUrl();
return {
content: [{ type: 'text', text: url }],
};
}
case 'chrome_get_title': {
const title = await chromeController.getTitle();
return {
content: [{ type: 'text', text: title }],
};
}
case 'chrome_get_content': {
const content = await chromeController.getContent();
return {
content: [{ type: 'text', text: content }],
};
}
case 'chrome_get_visible_text': {
const text = await chromeController.getVisibleText();
return {
content: [{ type: 'text', text: text }],
};
}
case 'chrome_execute_script': {
const script = args?.script;
if (!script) {
throw new Error('Script is required');
}
const result = await chromeController.executeScript(script);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
case 'chrome_click': {
const selector = args?.selector;
if (!selector) {
throw new Error('Selector is required');
}
const result = await chromeController.click(selector);
return {
content: [{ type: 'text', text: result }],
};
}
case 'chrome_type': {
const selector = args?.selector;
const text = args?.text;
if (!selector || !text) {
throw new Error('Selector and text are required');
}
const result = await chromeController.type(selector, text);
return {
content: [{ type: 'text', text: result }],
};
}
case 'chrome_screenshot': {
const screenshot = await chromeController.screenshot();
return {
content: [
{
type: 'image',
data: screenshot,
mimeType: 'image/png',
},
],
};
}
case 'chrome_open_new_tab': {
const url = args?.url;
const result = await chromeController.openNewTab(url);
return {
content: [{ type: 'text', text: result }],
};
}
case 'chrome_close_tab': {
const result = await chromeController.closeTab();
return {
content: [{ type: 'text', text: result }],
};
}
case 'chrome_list_tabs': {
const tabs = await chromeController.listTabs();
return {
content: [{ type: 'text', text: JSON.stringify(tabs, null, 2) }],
};
}
case 'chrome_reload': {
const result = await chromeController.reload();
return {
content: [{ type: 'text', text: result }],
};
}
case 'chrome_go_back': {
const result = await chromeController.goBack();
return {
content: [{ type: 'text', text: result }],
};
}
case 'chrome_go_forward': {
const result = await chromeController.goForward();
return {
content: [{ type: 'text', text: result }],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{ type: 'text', text: `Error: ${errorMessage}` }],
isError: true,
};
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Chrome MCP Server running on stdio');
console.error('Available tools:', TOOLS.map(t => t.name).join(', '));
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map