source dump of claude code
at main 106 lines 4.1 kB view raw
1import { 2 buildComputerUseTools, 3 createComputerUseMcpServer, 4} from '@ant/computer-use-mcp' 5import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' 6import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js' 7import { homedir } from 'os' 8 9import { shutdownDatadog } from '../../services/analytics/datadog.js' 10import { shutdown1PEventLogging } from '../../services/analytics/firstPartyEventLogger.js' 11import { initializeAnalyticsSink } from '../../services/analytics/sink.js' 12import { enableConfigs } from '../config.js' 13import { logForDebugging } from '../debug.js' 14import { filterAppsForDescription } from './appNames.js' 15import { getChicagoCoordinateMode } from './gates.js' 16import { getComputerUseHostAdapter } from './hostAdapter.js' 17 18const APP_ENUM_TIMEOUT_MS = 1000 19 20/** 21 * Enumerate installed apps, timed. Fails soft — if Spotlight is slow or 22 * claude-swift throws, the tool description just omits the list. Resolution 23 * happens at call time regardless; the model just doesn't get hints. 24 */ 25async function tryGetInstalledAppNames(): Promise<string[] | undefined> { 26 const adapter = getComputerUseHostAdapter() 27 const enumP = adapter.executor.listInstalledApps() 28 let timer: ReturnType<typeof setTimeout> | undefined 29 const timeoutP = new Promise<undefined>(resolve => { 30 timer = setTimeout(resolve, APP_ENUM_TIMEOUT_MS, undefined) 31 }) 32 const installed = await Promise.race([enumP, timeoutP]) 33 .catch(() => undefined) 34 .finally(() => clearTimeout(timer)) 35 if (!installed) { 36 // The enumeration continues in the background — swallow late rejections. 37 void enumP.catch(() => {}) 38 logForDebugging( 39 `[Computer Use MCP] app enumeration exceeded ${APP_ENUM_TIMEOUT_MS}ms or failed; tool description omits list`, 40 ) 41 return undefined 42 } 43 return filterAppsForDescription(installed, homedir()) 44} 45 46/** 47 * Construct the in-process server. Delegates to the package's 48 * `createComputerUseMcpServer` for the Server object + stub CallTool handler, 49 * then REPLACES the ListTools handler with one that includes installed-app 50 * names in the `request_access` description (the package's factory doesn't 51 * take `installedAppNames`, and Cowork builds its own tool array in 52 * serverDef.ts for the same reason). 53 * 54 * Async so the 1s app-enumeration timeout doesn't block startup — called from 55 * an `await import()` in `client.ts` on first CU connection, not `main.tsx`. 56 * 57 * Real dispatch still goes through `wrapper.tsx`'s `.call()` override; this 58 * server exists only to answer ListTools. 59 */ 60export async function createComputerUseMcpServerForCli(): Promise< 61 ReturnType<typeof createComputerUseMcpServer> 62> { 63 const adapter = getComputerUseHostAdapter() 64 const coordinateMode = getChicagoCoordinateMode() 65 const server = createComputerUseMcpServer(adapter, coordinateMode) 66 67 const installedAppNames = await tryGetInstalledAppNames() 68 const tools = buildComputerUseTools( 69 adapter.executor.capabilities, 70 coordinateMode, 71 installedAppNames, 72 ) 73 server.setRequestHandler(ListToolsRequestSchema, async () => 74 adapter.isDisabled() ? { tools: [] } : { tools }, 75 ) 76 77 return server 78} 79 80/** 81 * Subprocess entrypoint for `--computer-use-mcp`. Mirror of 82 * `runClaudeInChromeMcpServer` — stdio transport, exit on stdin close, 83 * flush analytics before exit. 84 */ 85export async function runComputerUseMcpServer(): Promise<void> { 86 enableConfigs() 87 initializeAnalyticsSink() 88 89 const server = await createComputerUseMcpServerForCli() 90 const transport = new StdioServerTransport() 91 92 let exiting = false 93 const shutdownAndExit = async (): Promise<void> => { 94 if (exiting) return 95 exiting = true 96 await Promise.all([shutdown1PEventLogging(), shutdownDatadog()]) 97 // eslint-disable-next-line custom-rules/no-process-exit 98 process.exit(0) 99 } 100 process.stdin.on('end', () => void shutdownAndExit()) 101 process.stdin.on('error', () => void shutdownAndExit()) 102 103 logForDebugging('[Computer Use MCP] Starting MCP server') 104 await server.connect(transport) 105 logForDebugging('[Computer Use MCP] MCP server started') 106}