@@ -8,27 +8,11 @@ import './polyfill.js';
88
99import process from 'node:process' ;
1010
11- import type { Channel } from './browser.js' ;
12- import { ensureBrowserConnected , ensureBrowserLaunched } from './browser.js' ;
1311import { cliOptions , parseArguments } from './cli.js' ;
14- import { loadIssueDescriptions } from './issue-descriptions.js' ;
1512import { logger , saveLogsToFile } from './logger.js' ;
16- import { McpContext } from './McpContext.js' ;
17- import { McpResponse } from './McpResponse.js' ;
18- import { Mutex } from './Mutex.js' ;
19- import { SlimMcpResponse } from './SlimMcpResponse.js' ;
20- import { ClearcutLogger } from './telemetry/ClearcutLogger.js' ;
13+ import { createMcpServer } from './server.js' ;
2114import { computeFlagUsage } from './telemetry/flagUtils.js' ;
22- import { bucketizeLatency } from './telemetry/metricUtils.js' ;
23- import {
24- McpServer ,
25- StdioServerTransport ,
26- type CallToolResult ,
27- SetLevelRequestSchema ,
28- } from './third_party/index.js' ;
29- import { ToolCategory } from './tools/categories.js' ;
30- import type { ToolDefinition } from './tools/ToolDefinition.js' ;
31- import { createTools } from './tools/tools.js' ;
15+ import { StdioServerTransport } from './third_party/index.js' ;
3216import { VERSION } from './version.js' ;
3317
3418export const args = parseArguments ( VERSION ) ;
4428 args . usageStatistics = false ;
4529}
4630
47- let clearcutLogger : ClearcutLogger | undefined ;
48- if ( args . usageStatistics ) {
49- clearcutLogger = new ClearcutLogger ( {
50- logFile : args . logFile ,
51- appVersion : VERSION ,
52- clearcutEndpoint : args . clearcutEndpoint ,
53- clearcutForceFlushIntervalMs : args . clearcutForceFlushIntervalMs ,
54- clearcutIncludePidHeader : args . clearcutIncludePidHeader ,
55- } ) ;
56- }
57-
5831if ( process . env [ 'CHROME_DEVTOOLS_MCP_CRASH_ON_UNCAUGHT' ] !== 'true' ) {
5932 process . on ( 'unhandledRejection' , ( reason , promise ) => {
6033 logger ( 'Unhandled promise rejection' , promise , reason ) ;
6134 } ) ;
6235}
6336
6437logger ( `Starting Chrome DevTools MCP Server v${ VERSION } ` ) ;
65- const server = new McpServer (
66- {
67- name : 'chrome_devtools' ,
68- title : 'Chrome DevTools MCP server' ,
69- version : VERSION ,
70- } ,
71- { capabilities : { logging : { } } } ,
72- ) ;
73- server . server . setRequestHandler ( SetLevelRequestSchema , ( ) => {
74- return { } ;
75- } ) ;
76-
77- let context : McpContext ;
78- async function getContext ( ) : Promise < McpContext > {
79- const chromeArgs : string [ ] = ( args . chromeArg ?? [ ] ) . map ( String ) ;
80- const ignoreDefaultChromeArgs : string [ ] = (
81- args . ignoreDefaultChromeArg ?? [ ]
82- ) . map ( String ) ;
83- if ( args . proxyServer ) {
84- chromeArgs . push ( `--proxy-server=${ args . proxyServer } ` ) ;
85- }
86- const devtools = args . experimentalDevtools ?? false ;
87- const browser =
88- args . browserUrl || args . wsEndpoint || args . autoConnect
89- ? await ensureBrowserConnected ( {
90- browserURL : args . browserUrl ,
91- wsEndpoint : args . wsEndpoint ,
92- wsHeaders : args . wsHeaders ,
93- // Important: only pass channel, if autoConnect is true.
94- channel : args . autoConnect ? ( args . channel as Channel ) : undefined ,
95- userDataDir : args . userDataDir ,
96- devtools,
97- } )
98- : await ensureBrowserLaunched ( {
99- headless : args . headless ,
100- executablePath : args . executablePath ,
101- channel : args . channel as Channel ,
102- isolated : args . isolated ?? false ,
103- userDataDir : args . userDataDir ,
104- logFile,
105- viewport : args . viewport ,
106- chromeArgs,
107- ignoreDefaultChromeArgs,
108- acceptInsecureCerts : args . acceptInsecureCerts ,
109- devtools,
110- enableExtensions : args . categoryExtensions ,
111- } ) ;
112-
113- if ( context ?. browser !== browser ) {
114- context = await McpContext . from ( browser , logger , {
115- experimentalDevToolsDebugging : devtools ,
116- experimentalIncludeAllPages : args . experimentalIncludeAllPages ,
117- performanceCrux : args . performanceCrux ,
118- } ) ;
119- }
120- return context ;
121- }
12238
12339const logDisclaimers = ( ) => {
12440 console . error (
@@ -142,128 +58,9 @@ For more details, visit: https://github.com/ChromeDevTools/chrome-devtools-mcp#u
14258 }
14359} ;
14460
145- const toolMutex = new Mutex ( ) ;
146-
147- function registerTool ( tool : ToolDefinition ) : void {
148- if (
149- tool . annotations . category === ToolCategory . EMULATION &&
150- args . categoryEmulation === false
151- ) {
152- return ;
153- }
154- if (
155- tool . annotations . category === ToolCategory . PERFORMANCE &&
156- args . categoryPerformance === false
157- ) {
158- return ;
159- }
160- if (
161- tool . annotations . category === ToolCategory . NETWORK &&
162- args . categoryNetwork === false
163- ) {
164- return ;
165- }
166- if (
167- tool . annotations . category === ToolCategory . EXTENSIONS &&
168- args . categoryExtensions === false
169- ) {
170- return ;
171- }
172- if (
173- tool . annotations . conditions ?. includes ( 'computerVision' ) &&
174- ! args . experimentalVision
175- ) {
176- return ;
177- }
178- if (
179- tool . annotations . conditions ?. includes ( 'experimentalInteropTools' ) &&
180- ! args . experimentalInteropTools
181- ) {
182- return ;
183- }
184- if (
185- tool . annotations . conditions ?. includes ( 'screencast' ) &&
186- ! args . experimentalScreencast
187- ) {
188- return ;
189- }
190- server . registerTool (
191- tool . name ,
192- {
193- description : tool . description ,
194- inputSchema : tool . schema ,
195- annotations : tool . annotations ,
196- } ,
197- async ( params ) : Promise < CallToolResult > => {
198- const guard = await toolMutex . acquire ( ) ;
199- const startTime = Date . now ( ) ;
200- let success = false ;
201- try {
202- logger ( `${ tool . name } request: ${ JSON . stringify ( params , null , ' ' ) } ` ) ;
203- const context = await getContext ( ) ;
204- logger ( `${ tool . name } context: resolved` ) ;
205- await context . detectOpenDevToolsWindows ( ) ;
206- const response = args . slim
207- ? new SlimMcpResponse ( args )
208- : new McpResponse ( args ) ;
209-
210- await tool . handler (
211- {
212- params,
213- } ,
214- response ,
215- context ,
216- ) ;
217- const { content, structuredContent} = await response . handle (
218- tool . name ,
219- context ,
220- ) ;
221- const result : CallToolResult & {
222- structuredContent ?: Record < string , unknown > ;
223- } = {
224- content,
225- } ;
226- success = true ;
227- if ( args . experimentalStructuredContent ) {
228- result . structuredContent = structuredContent as Record <
229- string ,
230- unknown
231- > ;
232- }
233- return result ;
234- } catch ( err ) {
235- logger ( `${ tool . name } error:` , err , err ?. stack ) ;
236- let errorText = err && 'message' in err ? err . message : String ( err ) ;
237- if ( 'cause' in err && err . cause ) {
238- errorText += `\nCause: ${ err . cause . message } ` ;
239- }
240- return {
241- content : [
242- {
243- type : 'text' ,
244- text : errorText ,
245- } ,
246- ] ,
247- isError : true ,
248- } ;
249- } finally {
250- void clearcutLogger ?. logToolInvocation ( {
251- toolName : tool . name ,
252- success,
253- latencyMs : bucketizeLatency ( Date . now ( ) - startTime ) ,
254- } ) ;
255- guard . dispose ( ) ;
256- }
257- } ,
258- ) ;
259- }
260-
261- const tools = createTools ( args ) ;
262- for ( const tool of tools ) {
263- registerTool ( tool ) ;
264- }
265-
266- await loadIssueDescriptions ( ) ;
61+ const { server, clearcutLogger} = await createMcpServer ( args , {
62+ logFile,
63+ } ) ;
26764const transport = new StdioServerTransport ( ) ;
26865await server . connect ( transport ) ;
26966logger ( 'Chrome DevTools MCP Server connected' ) ;
0 commit comments