browser-console-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ var BrowserConsoleMCP=function(){"use strict";const e=new class{constructor(e="ws://localhost:7898/browser"){this.ws=null,this.connected=!1,this.commandHistory=[],this.historyIndex=-1,this.serverUrl=e}connect(){try{this.loadHtml2Canvas().then((()=>{this.ws=new WebSocket(this.serverUrl),this.ws.onopen=()=>{this.connected=!0,console.info("%c[MCP Client] Connected to server","color: green"),this.registerConsoleCommands()},this.ws.onmessage=e=>{try{const t=JSON.parse(e.data);this.handleServerMessage(t)}catch(e){console.error("[MCP Client] Message parsing error:",e)}},this.ws.onclose=()=>{this.connected=!1,console.info("%c[MCP Client] Disconnected from server","color: orange")},this.ws.onerror=e=>{console.error("[MCP Client] WebSocket error:",e)}})).catch((e=>{console.error("[MCP Client] Failed to load html2canvas:",e)}))}catch(e){console.error("[MCP Client] Connection error:",e)}}loadHtml2Canvas(){return new Promise(((e,t)=>{if(void 0!==window.html2canvas)return console.info("[MCP Client] html2canvas already loaded"),void e(!0);console.info("[MCP Client] Loading html2canvas...");const n=document.createElement("script");n.src="https://html2canvas.hertzen.com/dist/html2canvas.min.js",n.onload=()=>{console.info("[MCP Client] html2canvas loaded from CDN"),e(!0)},n.onerror=()=>{console.info("[MCP Client] Trying local path for html2canvas...");const n=document.createElement("script");n.src="/html2canvas.min.js",n.onload=()=>{console.info("[MCP Client] html2canvas loaded from local path"),e(!0)},n.onerror=n=>{console.info("[MCP Client] Trying unpkg CDN for html2canvas...");const o=document.createElement("script");o.src="https://unpkg.com/html2canvas/dist/html2canvas.min.js",o.onload=()=>{console.info("[MCP Client] html2canvas loaded from unpkg CDN"),e(!0)},o.onerror=e=>{console.error("[MCP Client] Failed to load html2canvas:",e),t(new Error("Unable to load html2canvas library, please ensure your network connection is working"))},document.head.appendChild(o)},document.head.appendChild(n)},document.head.appendChild(n)}))}handleServerMessage(e){if("get_page_html"!==e.type)if("execute_js"!==e.type)if("get_page_title"!==e.type)if("get_elements"!==e.type)if("capture_screenshot"!==e.type)if("get_page_url"!==e.type)if("click_element"!==e.type)if("input_text"!==e.type)switch(e.type){case"command_result":console.info(`%c[MCP Server] ${e.payload?.result}`,"color: blue");break;case"error":console.error(`[MCP Server] Error: ${e.payload?.error}`);break;case"connection_status":console.info(`%c[MCP Server] ${e.message}`,"color: green");break;default:console.info(`[MCP Server] Received message type: ${e.type}`)}else{const t=e.requestId,n=e.selector,o=e.text;try{const e=document.querySelector(n);if(!e)return void this.sendError(t,`Input field not found: ${n}`);if("INPUT"!==e.tagName&&"TEXTAREA"!==e.tagName)return void this.sendError(t,`Selected element is not an input field: ${e.tagName}`);e.value=o,e.dispatchEvent(new Event("input",{bubbles:!0})),this.sendResponse(t,{success:!0,message:`Successfully input text to: ${n}`})}catch(e){this.sendError(t,`Error inputting text: ${e.message}`)}}else{const t=e.requestId,n=e.selector;try{const e=document.querySelector(n);if(!e)return void this.sendError(t,`Element not found: ${n}`);e.click(),this.sendResponse(t,{success:!0,message:`Successfully clicked element: ${n}`})}catch(e){this.sendError(t,`Error clicking element: ${e.message}`)}}else{const t=e.requestId;try{const e=window.location.href;this.sendResponse(t,{url:e})}catch(e){this.sendError(t,`Error getting URL: ${e.message}`)}}else{const t=e.requestId,n=e.selector||"body";(async()=>{try{await this.loadHtml2Canvas();const e=document.querySelector(n);if(!e)return void this.sendError(t,`Element not found: ${n}`);const o={allowTaint:!0,useCORS:!0,logging:!1,scale:window.devicePixelRatio||1,backgroundColor:null,removeContainer:!0,x:0,y:0,scrollX:0,scrollY:0,width:e.offsetWidth,height:e.offsetHeight},s=window.html2canvas;if(!s)return void this.sendError(t,"html2canvas library not properly loaded");let r=await s(e,o);if(!r)return void this.sendError(t,"Screenshot failed: unable to create canvas");const c=r.getContext("2d");if(c){const e=c.getImageData(0,0,r.width,r.height),t=this.getContentBounds(e);if(t){const e=document.createElement("canvas");e.width=t.width,e.height=t.height;const n=e.getContext("2d");n&&(n.drawImage(r,t.left,t.top,t.width,t.height,0,0,t.width,t.height),r=e)}}const i=r.toDataURL("image/png");this.sendResponse(t,{imageDataUrl:i})}catch(e){console.error("[MCP Client] Screenshot error:",e),this.sendError(t,`Screenshot error: ${e.message}`)}})()}else{const t=e.requestId,n=e.selector;try{const e=[...document.querySelectorAll(n)].map((e=>({tagName:e.tagName,id:e.id,className:e.className,textContent:e.textContent?.trim().substring(0,500)||"",attributes:[...e.attributes].reduce(((e,t)=>(e[t.name]=t.value,e)),{})})));this.sendResponse(t,{elements:e})}catch(e){this.sendError(t,`Error getting elements: ${e.message}`)}}else{const t=e.requestId;try{const e=document.title;this.sendResponse(t,{title:e})}catch(e){this.sendError(t,`Error getting title: ${e.message}`)}}else{const t=e.requestId,n=e.code;try{const e=new Function(n)();let o;try{o=JSON.stringify(e)}catch(t){o=String(e)}const s={requestId:t,result:o};this.ws?.send(JSON.stringify(s))}catch(e){console.error("[MCP Client] Error executing JavaScript:",e),this.sendError(t,`Error executing JavaScript: ${e.message}`)}}else{const t=e.requestId;try{const e=document.documentElement.outerHTML;this.sendResponse(t,{html:e})}catch(e){this.sendError(t,`Error getting HTML: ${e.message}`)}}}sendResponse(e,t){if(!this.connected||!this.ws)return void console.error("[MCP Client] Not connected to server, cannot send response");const n={requestId:e,...t};this.ws.send(JSON.stringify(n))}sendError(e,t){if(!this.connected||!this.ws)return void console.error("[MCP Client] Not connected to server, cannot send error response");const n={requestId:e,error:t};this.ws.send(JSON.stringify(n))}sendCommand(e){if(!this.connected||!this.ws)return void console.error("[MCP Client] Not connected to server");const t={type:"command",payload:{command:e}};this.ws.send(JSON.stringify(t)),this.commandHistory.push(e),this.historyIndex=this.commandHistory.length}registerConsoleCommands(){window.mcp={exec:e=>(this.sendCommand(e),"Command sent"),disconnect:()=>(this.ws&&(this.ws.close(),this.ws=null),"Disconnected"),reconnect:()=>(this.connect(),"Reconnecting..."),help:()=>"\nMCP Client Commands:\n mcp.exec(command) - Execute a command\n mcp.disconnect() - Disconnect from server\n mcp.reconnect() - Reconnect to server\n mcp.help() - Show help information\n "},console.info("%c[MCP Client] Console commands registered, use mcp.help() to see available commands","color: green")}getContentBounds(e){const{width:t,height:n,data:o}=e;let s=t,r=n,c=0,i=0,a=!1;for(let e=0;e<n;e++)for(let n=0;n<t;n++){o[4*(e*t+n)+3]>10&&(a=!0,s=Math.min(s,n),r=Math.min(r,e),c=Math.max(c,n),i=Math.max(i,e))}if(!a)return null;return s=Math.max(0,s-10),r=Math.max(0,r-10),c=Math.min(t-1,c+10),i=Math.min(n-1,i+10),{left:s,top:r,width:c-s+1,height:i-r+1}}};return e.connect(),e}();
2
+ //# sourceMappingURL=browser-console-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-console-mcp.js","sources":["../../src/client/index.ts"],"sourcesContent":["/**\n * Browser Console MCP Client\n *\n * This client runs in the browser console and communicates with the MCP server\n */\n\ninterface MCPMessage {\n\ttype: string;\n\tpayload?: Record<string, unknown>;\n\trequestId?: string;\n\tcode?: string;\n\tmessage?: string;\n\tselector?: string;\n\ttext?: string;\n}\n\ninterface MCPConsole {\n\texec: (command: string) => string;\n\tdisconnect: () => string;\n\treconnect: () => string;\n\thelp: () => string;\n}\n\n// html2canvas options type\ninterface Html2CanvasOptions {\n\tallowTaint?: boolean;\n\tuseCORS?: boolean;\n\tlogging?: boolean;\n\tscale?: number;\n\tbackgroundColor?: string | null;\n\tremoveContainer?: boolean;\n\tscrollX?: number;\n\tscrollY?: number;\n\twindowWidth?: number;\n\twindowHeight?: number;\n\tx?: number;\n\ty?: number;\n\twidth?: number;\n\theight?: number;\n\t[key: string]: boolean | number | string | undefined | null;\n}\n\n// Extend global Window interface\ndeclare global {\n\tinterface Window {\n\t\tmcp: MCPConsole;\n\t}\n\n\tinterface WindowWithHtml2Canvas extends Window {\n\t\thtml2canvas?: (\n\t\t\telement: HTMLElement,\n\t\t\toptions?: Html2CanvasOptions,\n\t\t) => Promise<HTMLCanvasElement>;\n\t}\n}\n\nclass BrowserConsoleMCP {\n\tprivate ws: WebSocket | null = null;\n\tprivate serverUrl: string;\n\tprivate connected = false;\n\tprivate commandHistory: string[] = [];\n\tprivate historyIndex = -1;\n\n\tconstructor(serverUrl = \"ws://localhost:7898/browser\") {\n\t\tthis.serverUrl = serverUrl;\n\t}\n\n\t/**\n\t * Connect to MCP server\n\t */\n\tconnect(): void {\n\t\ttry {\n\t\t\t// First load html2canvas\n\t\t\tthis.loadHtml2Canvas()\n\t\t\t\t.then(() => {\n\t\t\t\t\tthis.ws = new WebSocket(this.serverUrl);\n\n\t\t\t\t\tthis.ws.onopen = () => {\n\t\t\t\t\t\tthis.connected = true;\n\t\t\t\t\t\tconsole.info(\"%c[MCP Client] Connected to server\", \"color: green\");\n\t\t\t\t\t\tthis.registerConsoleCommands();\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.ws.onmessage = (event) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst message: MCPMessage = JSON.parse(event.data);\n\t\t\t\t\t\t\tthis.handleServerMessage(message);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error(\"[MCP Client] Message parsing error:\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.ws.onclose = () => {\n\t\t\t\t\t\tthis.connected = false;\n\t\t\t\t\t\tconsole.info(\n\t\t\t\t\t\t\t\"%c[MCP Client] Disconnected from server\",\n\t\t\t\t\t\t\t\"color: orange\",\n\t\t\t\t\t\t);\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.ws.onerror = (error) => {\n\t\t\t\t\t\tconsole.error(\"[MCP Client] WebSocket error:\", error);\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tconsole.error(\"[MCP Client] Failed to load html2canvas:\", error);\n\t\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[MCP Client] Connection error:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Load html2canvas library\n\t */\n\tprivate loadHtml2Canvas(): Promise<boolean> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tif (\n\t\t\t\ttypeof (window as WindowWithHtml2Canvas).html2canvas !== \"undefined\"\n\t\t\t) {\n\t\t\t\tconsole.info(\"[MCP Client] html2canvas already loaded\");\n\t\t\t\tresolve(true);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconsole.info(\"[MCP Client] Loading html2canvas...\");\n\n\t\t\t// Prioritize loading html2canvas from CDN\n\t\t\tconst script = document.createElement(\"script\");\n\t\t\tscript.src = \"https://html2canvas.hertzen.com/dist/html2canvas.min.js\";\n\t\t\tscript.onload = () => {\n\t\t\t\tconsole.info(\"[MCP Client] html2canvas loaded from CDN\");\n\t\t\t\tresolve(true);\n\t\t\t};\n\t\t\tscript.onerror = () => {\n\t\t\t\t// If CDN fails, try local path\n\t\t\t\tconsole.info(\"[MCP Client] Trying local path for html2canvas...\");\n\t\t\t\tconst localScript = document.createElement(\"script\");\n\t\t\t\tlocalScript.src = \"/html2canvas.min.js\";\n\t\t\t\tlocalScript.onload = () => {\n\t\t\t\t\tconsole.info(\"[MCP Client] html2canvas loaded from local path\");\n\t\t\t\t\tresolve(true);\n\t\t\t\t};\n\t\t\t\tlocalScript.onerror = (err) => {\n\t\t\t\t\t// Try using unpkg CDN\n\t\t\t\t\tconsole.info(\"[MCP Client] Trying unpkg CDN for html2canvas...\");\n\t\t\t\t\tconst unpkgScript = document.createElement(\"script\");\n\t\t\t\t\tunpkgScript.src =\n\t\t\t\t\t\t\"https://unpkg.com/html2canvas/dist/html2canvas.min.js\";\n\t\t\t\t\tunpkgScript.onload = () => {\n\t\t\t\t\t\tconsole.info(\"[MCP Client] html2canvas loaded from unpkg CDN\");\n\t\t\t\t\t\tresolve(true);\n\t\t\t\t\t};\n\t\t\t\t\tunpkgScript.onerror = (err) => {\n\t\t\t\t\t\tconsole.error(\"[MCP Client] Failed to load html2canvas:\", err);\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\"Unable to load html2canvas library, please ensure your network connection is working\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t};\n\t\t\t\t\tdocument.head.appendChild(unpkgScript);\n\t\t\t\t};\n\t\t\t\tdocument.head.appendChild(localScript);\n\t\t\t};\n\t\t\tdocument.head.appendChild(script);\n\t\t});\n\t}\n\n\t/**\n\t * Handle messages from server\n\t */\n\tprivate handleServerMessage(message: MCPMessage): void {\n\t\t// Handle special request types\n\t\tif (message.type === \"get_page_html\") {\n\t\t\t// Handle request to get page HTML\n\t\t\tconst requestId = message.requestId as string;\n\t\t\ttry {\n\t\t\t\tconst html = document.documentElement.outerHTML;\n\t\t\t\tthis.sendResponse(requestId, { html });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting HTML: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"execute_js\") {\n\t\t\t// Handle request to execute JavaScript\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst code = message.code as string;\n\t\t\ttry {\n\t\t\t\t// Use Function constructor to create function, then execute\n\t\t\t\tconst result = new Function(code)();\n\t\t\t\t// Ensure result can be JSON serialized\n\t\t\t\tlet serializedResult: string;\n\t\t\t\ttry {\n\t\t\t\t\tserializedResult = JSON.stringify(result);\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// If result cannot be serialized, return string representation\n\t\t\t\t\tserializedResult = String(result);\n\t\t\t\t}\n\n\t\t\t\t// Send response\n\t\t\t\tconst response = {\n\t\t\t\t\trequestId,\n\t\t\t\t\tresult: serializedResult,\n\t\t\t\t};\n\n\t\t\t\tthis.ws?.send(JSON.stringify(response));\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[MCP Client] Error executing JavaScript:\", error);\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error executing JavaScript: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"get_page_title\") {\n\t\t\t// Handle request to get page title\n\t\t\tconst requestId = message.requestId as string;\n\t\t\ttry {\n\t\t\t\tconst title = document.title;\n\t\t\t\tthis.sendResponse(requestId, { title });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting title: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"get_elements\") {\n\t\t\t// Handle request to get elements\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = message.selector as string;\n\t\t\ttry {\n\t\t\t\tconst elements = [...document.querySelectorAll(selector)];\n\t\t\t\tconst result = elements.map((el) => ({\n\t\t\t\t\ttagName: el.tagName,\n\t\t\t\t\tid: el.id,\n\t\t\t\t\tclassName: el.className,\n\t\t\t\t\ttextContent: el.textContent?.trim().substring(0, 500) || \"\",\n\t\t\t\t\tattributes: [...el.attributes].reduce(\n\t\t\t\t\t\t(attrs: Record<string, string>, attr) => {\n\t\t\t\t\t\t\tattrs[attr.name] = attr.value;\n\t\t\t\t\t\t\treturn attrs;\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{},\n\t\t\t\t\t),\n\t\t\t\t}));\n\t\t\t\tthis.sendResponse(requestId, { elements: result });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting elements: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"capture_screenshot\") {\n\t\t\t// Handle screenshot request\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = (message.selector as string) || \"body\";\n\n\t\t\t// Process screenshot asynchronously\n\t\t\t(async () => {\n\t\t\t\ttry {\n\t\t\t\t\t// Ensure html2canvas is loaded\n\t\t\t\t\tawait this.loadHtml2Canvas();\n\n\t\t\t\t\tconst element = document.querySelector(selector);\n\t\t\t\t\tif (!element) {\n\t\t\t\t\t\tthis.sendError(requestId, `Element not found: ${selector}`);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Use html2canvas for screenshot, add config for better compatibility\n\t\t\t\t\tconst html2canvasOptions: Html2CanvasOptions = {\n\t\t\t\t\t\tallowTaint: true,\n\t\t\t\t\t\tuseCORS: true,\n\t\t\t\t\t\tlogging: false,\n\t\t\t\t\t\tscale: window.devicePixelRatio || 1,\n\t\t\t\t\t\tbackgroundColor: null, // Transparent background\n\t\t\t\t\t\tremoveContainer: true, // Remove temporary container\n\t\t\t\t\t\t// Calculate actual element size and position\n\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\tscrollX: 0,\n\t\t\t\t\t\tscrollY: 0,\n\t\t\t\t\t\t// Get actual element width and height\n\t\t\t\t\t\twidth: (element as HTMLElement).offsetWidth,\n\t\t\t\t\t\theight: (element as HTMLElement).offsetHeight,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst html2canvas = (window as WindowWithHtml2Canvas).html2canvas;\n\n\t\t\t\t\tif (!html2canvas) {\n\t\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\t\"html2canvas library not properly loaded\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet canvas = await html2canvas(\n\t\t\t\t\t\telement as HTMLElement,\n\t\t\t\t\t\thtml2canvasOptions,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (!canvas) {\n\t\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\t\"Screenshot failed: unable to create canvas\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Crop canvas, remove excess white space\n\t\t\t\t\tconst context = canvas.getContext(\"2d\");\n\t\t\t\t\tif (context) {\n\t\t\t\t\t\t// Try to detect content area, remove whitespace\n\t\t\t\t\t\tconst imageData = context.getImageData(\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tcanvas.width,\n\t\t\t\t\t\t\tcanvas.height,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst bounds = this.getContentBounds(imageData);\n\n\t\t\t\t\t\tif (bounds) {\n\t\t\t\t\t\t\t// If content boundaries found, create a new cropped canvas\n\t\t\t\t\t\t\tconst croppedCanvas = document.createElement(\"canvas\");\n\t\t\t\t\t\t\tcroppedCanvas.width = bounds.width;\n\t\t\t\t\t\t\tcroppedCanvas.height = bounds.height;\n\n\t\t\t\t\t\t\tconst croppedContext = croppedCanvas.getContext(\"2d\");\n\t\t\t\t\t\t\tif (croppedContext) {\n\t\t\t\t\t\t\t\tcroppedContext.drawImage(\n\t\t\t\t\t\t\t\t\tcanvas,\n\t\t\t\t\t\t\t\t\tbounds.left,\n\t\t\t\t\t\t\t\t\tbounds.top,\n\t\t\t\t\t\t\t\t\tbounds.width,\n\t\t\t\t\t\t\t\t\tbounds.height,\n\t\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t\tbounds.width,\n\t\t\t\t\t\t\t\t\tbounds.height,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Use cropped canvas\n\t\t\t\t\t\t\t\tcanvas = croppedCanvas;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Compress image quality to reduce data size, use PNG format to maintain transparency\n\t\t\t\t\tconst dataUrl = canvas.toDataURL(\"image/png\");\n\t\t\t\t\tthis.sendResponse(requestId, { imageDataUrl: dataUrl });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"[MCP Client] Screenshot error:\", error);\n\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t`Screenshot error: ${(error as Error).message}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t})();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"get_page_url\") {\n\t\t\t// Handle request to get page URL\n\t\t\tconst requestId = message.requestId as string;\n\t\t\ttry {\n\t\t\t\tconst url = window.location.href;\n\t\t\t\tthis.sendResponse(requestId, { url });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting URL: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"click_element\") {\n\t\t\t// Handle request to click element\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = message.selector as string;\n\t\t\ttry {\n\t\t\t\tconst element = document.querySelector(selector);\n\t\t\t\tif (!element) {\n\t\t\t\t\tthis.sendError(requestId, `Element not found: ${selector}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t(element as HTMLElement).click();\n\t\t\t\tthis.sendResponse(requestId, {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Successfully clicked element: ${selector}`,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error clicking element: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"input_text\") {\n\t\t\t// Handle request to input text\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = message.selector as string;\n\t\t\tconst text = message.text as string;\n\n\t\t\ttry {\n\t\t\t\tconst input = document.querySelector(selector);\n\t\t\t\tif (!input) {\n\t\t\t\t\tthis.sendError(requestId, `Input field not found: ${selector}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (input.tagName !== \"INPUT\" && input.tagName !== \"TEXTAREA\") {\n\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t`Selected element is not an input field: ${input.tagName}`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t(input as HTMLInputElement).value = text;\n\t\t\t\tinput.dispatchEvent(new Event(\"input\", { bubbles: true }));\n\n\t\t\t\tthis.sendResponse(requestId, {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Successfully input text to: ${selector}`,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error inputting text: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle other message types\n\t\tswitch (message.type) {\n\t\t\tcase \"command_result\":\n\t\t\t\tconsole.info(\n\t\t\t\t\t`%c[MCP Server] ${message.payload?.result}`,\n\t\t\t\t\t\"color: blue\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"error\":\n\t\t\t\tconsole.error(`[MCP Server] Error: ${message.payload?.error}`);\n\t\t\t\tbreak;\n\t\t\tcase \"connection_status\":\n\t\t\t\tconsole.info(`%c[MCP Server] ${message.message}`, \"color: green\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// Only log message type, not full message content\n\t\t\t\tconsole.info(`[MCP Server] Received message type: ${message.type}`);\n\t\t}\n\t}\n\n\t/**\n\t * Send response\n\t */\n\tprivate sendResponse(requestId: string, data: Record<string, unknown>): void {\n\t\tif (!this.connected || !this.ws) {\n\t\t\tconsole.error(\n\t\t\t\t\"[MCP Client] Not connected to server, cannot send response\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst response = {\n\t\t\trequestId,\n\t\t\t...data,\n\t\t};\n\n\t\tthis.ws.send(JSON.stringify(response));\n\t}\n\n\t/**\n\t * Send error response\n\t */\n\tprivate sendError(requestId: string, errorMessage: string): void {\n\t\tif (!this.connected || !this.ws) {\n\t\t\tconsole.error(\n\t\t\t\t\"[MCP Client] Not connected to server, cannot send error response\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst response = {\n\t\t\trequestId,\n\t\t\terror: errorMessage,\n\t\t};\n\n\t\tthis.ws.send(JSON.stringify(response));\n\t}\n\n\t/**\n\t * Send command to server\n\t */\n\tsendCommand(command: string): void {\n\t\tif (!this.connected || !this.ws) {\n\t\t\tconsole.error(\"[MCP Client] Not connected to server\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst message: MCPMessage = {\n\t\t\ttype: \"command\",\n\t\t\tpayload: { command },\n\t\t};\n\n\t\tthis.ws.send(JSON.stringify(message));\n\t\tthis.commandHistory.push(command);\n\t\tthis.historyIndex = this.commandHistory.length;\n\t}\n\n\t/**\n\t * Register console commands\n\t */\n\tprivate registerConsoleCommands(): void {\n\t\t// Define global commands\n\t\twindow.mcp = {\n\t\t\texec: (command: string) => {\n\t\t\t\tthis.sendCommand(command);\n\t\t\t\treturn \"Command sent\";\n\t\t\t},\n\t\t\tdisconnect: () => {\n\t\t\t\tif (this.ws) {\n\t\t\t\t\tthis.ws.close();\n\t\t\t\t\tthis.ws = null;\n\t\t\t\t}\n\t\t\t\treturn \"Disconnected\";\n\t\t\t},\n\t\t\treconnect: () => {\n\t\t\t\tthis.connect();\n\t\t\t\treturn \"Reconnecting...\";\n\t\t\t},\n\t\t\thelp: () => {\n\t\t\t\treturn `\nMCP Client Commands:\n mcp.exec(command) - Execute a command\n mcp.disconnect() - Disconnect from server\n mcp.reconnect() - Reconnect to server\n mcp.help() - Show help information\n `;\n\t\t\t},\n\t\t};\n\n\t\tconsole.info(\n\t\t\t\"%c[MCP Client] Console commands registered, use mcp.help() to see available commands\",\n\t\t\t\"color: green\",\n\t\t);\n\t}\n\n\t/**\n\t * Get image content boundaries, remove excess whitespace\n\t */\n\tprivate getContentBounds(\n\t\timageData: ImageData,\n\t): { left: number; top: number; width: number; height: number } | null {\n\t\tconst { width, height, data } = imageData;\n\t\tlet minX = width;\n\t\tlet minY = height;\n\t\tlet maxX = 0;\n\t\tlet maxY = 0;\n\t\tlet hasContent = false;\n\n\t\t// Iterate through pixel data, find boundaries of non-transparent pixels\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst alpha = data[(y * width + x) * 4 + 3]; // Alpha channel\n\t\t\t\tif (alpha > 10) {\n\t\t\t\t\t// Non-transparent pixel (allowing some slight transparency)\n\t\t\t\t\thasContent = true;\n\t\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!hasContent) {\n\t\t\treturn null; // No content found\n\t\t}\n\n\t\t// Add some padding\n\t\tconst padding = 10;\n\t\tminX = Math.max(0, minX - padding);\n\t\tminY = Math.max(0, minY - padding);\n\t\tmaxX = Math.min(width - 1, maxX + padding);\n\t\tmaxY = Math.min(height - 1, maxY + padding);\n\n\t\treturn {\n\t\t\tleft: minX,\n\t\t\ttop: minY,\n\t\t\twidth: maxX - minX + 1,\n\t\t\theight: maxY - minY + 1,\n\t\t};\n\t}\n}\n\n// Create and connect client instance\nconst client = new BrowserConsoleMCP();\nclient.connect();\n\n// Export client instance\nexport default client;\n"],"names":["client","constructor","serverUrl","this","ws","connected","commandHistory","historyIndex","connect","loadHtml2Canvas","then","WebSocket","onopen","console","info","registerConsoleCommands","onmessage","event","message","JSON","parse","data","handleServerMessage","error","onclose","onerror","catch","Promise","resolve","reject","window","html2canvas","script","document","createElement","src","onload","localScript","err","unpkgScript","Error","head","appendChild","type","payload","result","requestId","selector","text","input","querySelector","sendError","tagName","value","dispatchEvent","Event","bubbles","sendResponse","success","element","click","url","location","href","html2canvasOptions","allowTaint","useCORS","logging","scale","devicePixelRatio","backgroundColor","removeContainer","x","y","scrollX","scrollY","width","offsetWidth","height","offsetHeight","canvas","context","getContext","imageData","getImageData","bounds","getContentBounds","croppedCanvas","croppedContext","drawImage","left","top","dataUrl","toDataURL","imageDataUrl","querySelectorAll","map","el","id","className","textContent","trim","substring","attributes","reduce","attrs","attr","name","elements","title","code","Function","serializedResult","stringify","String","response","send","html","documentElement","outerHTML","errorMessage","sendCommand","command","push","length","mcp","exec","disconnect","close","reconnect","help","minX","minY","maxX","maxY","hasContent","Math","min","max"],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Browser Console MCP Client
3
+ *
4
+ * This client runs in the browser console and communicates with the MCP server
5
+ */
6
+ interface MCPConsole {
7
+ exec: (command: string) => string;
8
+ disconnect: () => string;
9
+ reconnect: () => string;
10
+ help: () => string;
11
+ }
12
+ interface Html2CanvasOptions {
13
+ allowTaint?: boolean;
14
+ useCORS?: boolean;
15
+ logging?: boolean;
16
+ scale?: number;
17
+ backgroundColor?: string | null;
18
+ removeContainer?: boolean;
19
+ scrollX?: number;
20
+ scrollY?: number;
21
+ windowWidth?: number;
22
+ windowHeight?: number;
23
+ x?: number;
24
+ y?: number;
25
+ width?: number;
26
+ height?: number;
27
+ [key: string]: boolean | number | string | undefined | null;
28
+ }
29
+ declare global {
30
+ interface Window {
31
+ mcp: MCPConsole;
32
+ }
33
+ interface WindowWithHtml2Canvas extends Window {
34
+ html2canvas?: (element: HTMLElement, options?: Html2CanvasOptions) => Promise<HTMLCanvasElement>;
35
+ }
36
+ }
37
+ declare class BrowserConsoleMCP {
38
+ private ws;
39
+ private serverUrl;
40
+ private connected;
41
+ private commandHistory;
42
+ private historyIndex;
43
+ constructor(serverUrl?: string);
44
+ /**
45
+ * Connect to MCP server
46
+ */
47
+ connect(): void;
48
+ /**
49
+ * Load html2canvas library
50
+ */
51
+ private loadHtml2Canvas;
52
+ /**
53
+ * Handle messages from server
54
+ */
55
+ private handleServerMessage;
56
+ /**
57
+ * Send response
58
+ */
59
+ private sendResponse;
60
+ /**
61
+ * Send error response
62
+ */
63
+ private sendError;
64
+ /**
65
+ * Send command to server
66
+ */
67
+ sendCommand(command: string): void;
68
+ /**
69
+ * Register console commands
70
+ */
71
+ private registerConsoleCommands;
72
+ /**
73
+ * Get image content boundaries, remove excess whitespace
74
+ */
75
+ private getContentBounds;
76
+ }
77
+ declare const client: BrowserConsoleMCP;
78
+ export default client;
@@ -0,0 +1,451 @@
1
+ /**
2
+ * Browser Console MCP Client
3
+ *
4
+ * This client runs in the browser console and communicates with the MCP server
5
+ */
6
+ class BrowserConsoleMCP {
7
+ constructor(serverUrl = "ws://localhost:7898/browser") {
8
+ this.ws = null;
9
+ this.connected = false;
10
+ this.commandHistory = [];
11
+ this.historyIndex = -1;
12
+ this.serverUrl = serverUrl;
13
+ }
14
+ /**
15
+ * Connect to MCP server
16
+ */
17
+ connect() {
18
+ try {
19
+ // First load html2canvas
20
+ this.loadHtml2Canvas()
21
+ .then(() => {
22
+ this.ws = new WebSocket(this.serverUrl);
23
+ this.ws.onopen = () => {
24
+ this.connected = true;
25
+ console.info("%c[MCP Client] Connected to server", "color: green");
26
+ this.registerConsoleCommands();
27
+ };
28
+ this.ws.onmessage = (event) => {
29
+ try {
30
+ const message = JSON.parse(event.data);
31
+ this.handleServerMessage(message);
32
+ }
33
+ catch (error) {
34
+ console.error("[MCP Client] Message parsing error:", error);
35
+ }
36
+ };
37
+ this.ws.onclose = () => {
38
+ this.connected = false;
39
+ console.info("%c[MCP Client] Disconnected from server", "color: orange");
40
+ };
41
+ this.ws.onerror = (error) => {
42
+ console.error("[MCP Client] WebSocket error:", error);
43
+ };
44
+ })
45
+ .catch((error) => {
46
+ console.error("[MCP Client] Failed to load html2canvas:", error);
47
+ });
48
+ }
49
+ catch (error) {
50
+ console.error("[MCP Client] Connection error:", error);
51
+ }
52
+ }
53
+ /**
54
+ * Load html2canvas library
55
+ */
56
+ loadHtml2Canvas() {
57
+ return new Promise((resolve, reject) => {
58
+ if (typeof window.html2canvas !== "undefined") {
59
+ console.info("[MCP Client] html2canvas already loaded");
60
+ resolve(true);
61
+ return;
62
+ }
63
+ console.info("[MCP Client] Loading html2canvas...");
64
+ // Prioritize loading html2canvas from CDN
65
+ const script = document.createElement("script");
66
+ script.src = "https://html2canvas.hertzen.com/dist/html2canvas.min.js";
67
+ script.onload = () => {
68
+ console.info("[MCP Client] html2canvas loaded from CDN");
69
+ resolve(true);
70
+ };
71
+ script.onerror = () => {
72
+ // If CDN fails, try local path
73
+ console.info("[MCP Client] Trying local path for html2canvas...");
74
+ const localScript = document.createElement("script");
75
+ localScript.src = "/html2canvas.min.js";
76
+ localScript.onload = () => {
77
+ console.info("[MCP Client] html2canvas loaded from local path");
78
+ resolve(true);
79
+ };
80
+ localScript.onerror = (err) => {
81
+ // Try using unpkg CDN
82
+ console.info("[MCP Client] Trying unpkg CDN for html2canvas...");
83
+ const unpkgScript = document.createElement("script");
84
+ unpkgScript.src =
85
+ "https://unpkg.com/html2canvas/dist/html2canvas.min.js";
86
+ unpkgScript.onload = () => {
87
+ console.info("[MCP Client] html2canvas loaded from unpkg CDN");
88
+ resolve(true);
89
+ };
90
+ unpkgScript.onerror = (err) => {
91
+ console.error("[MCP Client] Failed to load html2canvas:", err);
92
+ reject(new Error("Unable to load html2canvas library, please ensure your network connection is working"));
93
+ };
94
+ document.head.appendChild(unpkgScript);
95
+ };
96
+ document.head.appendChild(localScript);
97
+ };
98
+ document.head.appendChild(script);
99
+ });
100
+ }
101
+ /**
102
+ * Handle messages from server
103
+ */
104
+ handleServerMessage(message) {
105
+ // Handle special request types
106
+ if (message.type === "get_page_html") {
107
+ // Handle request to get page HTML
108
+ const requestId = message.requestId;
109
+ try {
110
+ const html = document.documentElement.outerHTML;
111
+ this.sendResponse(requestId, { html });
112
+ }
113
+ catch (error) {
114
+ this.sendError(requestId, `Error getting HTML: ${error.message}`);
115
+ }
116
+ return;
117
+ }
118
+ if (message.type === "execute_js") {
119
+ // Handle request to execute JavaScript
120
+ const requestId = message.requestId;
121
+ const code = message.code;
122
+ try {
123
+ // Use Function constructor to create function, then execute
124
+ const result = new Function(code)();
125
+ // Ensure result can be JSON serialized
126
+ let serializedResult;
127
+ try {
128
+ serializedResult = JSON.stringify(result);
129
+ }
130
+ catch (err) {
131
+ // If result cannot be serialized, return string representation
132
+ serializedResult = String(result);
133
+ }
134
+ // Send response
135
+ const response = {
136
+ requestId,
137
+ result: serializedResult,
138
+ };
139
+ this.ws?.send(JSON.stringify(response));
140
+ }
141
+ catch (error) {
142
+ console.error("[MCP Client] Error executing JavaScript:", error);
143
+ this.sendError(requestId, `Error executing JavaScript: ${error.message}`);
144
+ }
145
+ return;
146
+ }
147
+ if (message.type === "get_page_title") {
148
+ // Handle request to get page title
149
+ const requestId = message.requestId;
150
+ try {
151
+ const title = document.title;
152
+ this.sendResponse(requestId, { title });
153
+ }
154
+ catch (error) {
155
+ this.sendError(requestId, `Error getting title: ${error.message}`);
156
+ }
157
+ return;
158
+ }
159
+ if (message.type === "get_elements") {
160
+ // Handle request to get elements
161
+ const requestId = message.requestId;
162
+ const selector = message.selector;
163
+ try {
164
+ const elements = [...document.querySelectorAll(selector)];
165
+ const result = elements.map((el) => ({
166
+ tagName: el.tagName,
167
+ id: el.id,
168
+ className: el.className,
169
+ textContent: el.textContent?.trim().substring(0, 500) || "",
170
+ attributes: [...el.attributes].reduce((attrs, attr) => {
171
+ attrs[attr.name] = attr.value;
172
+ return attrs;
173
+ }, {}),
174
+ }));
175
+ this.sendResponse(requestId, { elements: result });
176
+ }
177
+ catch (error) {
178
+ this.sendError(requestId, `Error getting elements: ${error.message}`);
179
+ }
180
+ return;
181
+ }
182
+ if (message.type === "capture_screenshot") {
183
+ // Handle screenshot request
184
+ const requestId = message.requestId;
185
+ const selector = message.selector || "body";
186
+ // Process screenshot asynchronously
187
+ (async () => {
188
+ try {
189
+ // Ensure html2canvas is loaded
190
+ await this.loadHtml2Canvas();
191
+ const element = document.querySelector(selector);
192
+ if (!element) {
193
+ this.sendError(requestId, `Element not found: ${selector}`);
194
+ return;
195
+ }
196
+ // Use html2canvas for screenshot, add config for better compatibility
197
+ const html2canvasOptions = {
198
+ allowTaint: true,
199
+ useCORS: true,
200
+ logging: false,
201
+ scale: window.devicePixelRatio || 1,
202
+ backgroundColor: null, // Transparent background
203
+ removeContainer: true, // Remove temporary container
204
+ // Calculate actual element size and position
205
+ x: 0,
206
+ y: 0,
207
+ scrollX: 0,
208
+ scrollY: 0,
209
+ // Get actual element width and height
210
+ width: element.offsetWidth,
211
+ height: element.offsetHeight,
212
+ };
213
+ const html2canvas = window.html2canvas;
214
+ if (!html2canvas) {
215
+ this.sendError(requestId, "html2canvas library not properly loaded");
216
+ return;
217
+ }
218
+ let canvas = await html2canvas(element, html2canvasOptions);
219
+ if (!canvas) {
220
+ this.sendError(requestId, "Screenshot failed: unable to create canvas");
221
+ return;
222
+ }
223
+ // Crop canvas, remove excess white space
224
+ const context = canvas.getContext("2d");
225
+ if (context) {
226
+ // Try to detect content area, remove whitespace
227
+ const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
228
+ const bounds = this.getContentBounds(imageData);
229
+ if (bounds) {
230
+ // If content boundaries found, create a new cropped canvas
231
+ const croppedCanvas = document.createElement("canvas");
232
+ croppedCanvas.width = bounds.width;
233
+ croppedCanvas.height = bounds.height;
234
+ const croppedContext = croppedCanvas.getContext("2d");
235
+ if (croppedContext) {
236
+ croppedContext.drawImage(canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height);
237
+ // Use cropped canvas
238
+ canvas = croppedCanvas;
239
+ }
240
+ }
241
+ }
242
+ // Compress image quality to reduce data size, use PNG format to maintain transparency
243
+ const dataUrl = canvas.toDataURL("image/png");
244
+ this.sendResponse(requestId, { imageDataUrl: dataUrl });
245
+ }
246
+ catch (error) {
247
+ console.error("[MCP Client] Screenshot error:", error);
248
+ this.sendError(requestId, `Screenshot error: ${error.message}`);
249
+ }
250
+ })();
251
+ return;
252
+ }
253
+ if (message.type === "get_page_url") {
254
+ // Handle request to get page URL
255
+ const requestId = message.requestId;
256
+ try {
257
+ const url = window.location.href;
258
+ this.sendResponse(requestId, { url });
259
+ }
260
+ catch (error) {
261
+ this.sendError(requestId, `Error getting URL: ${error.message}`);
262
+ }
263
+ return;
264
+ }
265
+ if (message.type === "click_element") {
266
+ // Handle request to click element
267
+ const requestId = message.requestId;
268
+ const selector = message.selector;
269
+ try {
270
+ const element = document.querySelector(selector);
271
+ if (!element) {
272
+ this.sendError(requestId, `Element not found: ${selector}`);
273
+ return;
274
+ }
275
+ element.click();
276
+ this.sendResponse(requestId, {
277
+ success: true,
278
+ message: `Successfully clicked element: ${selector}`,
279
+ });
280
+ }
281
+ catch (error) {
282
+ this.sendError(requestId, `Error clicking element: ${error.message}`);
283
+ }
284
+ return;
285
+ }
286
+ if (message.type === "input_text") {
287
+ // Handle request to input text
288
+ const requestId = message.requestId;
289
+ const selector = message.selector;
290
+ const text = message.text;
291
+ try {
292
+ const input = document.querySelector(selector);
293
+ if (!input) {
294
+ this.sendError(requestId, `Input field not found: ${selector}`);
295
+ return;
296
+ }
297
+ if (input.tagName !== "INPUT" && input.tagName !== "TEXTAREA") {
298
+ this.sendError(requestId, `Selected element is not an input field: ${input.tagName}`);
299
+ return;
300
+ }
301
+ input.value = text;
302
+ input.dispatchEvent(new Event("input", { bubbles: true }));
303
+ this.sendResponse(requestId, {
304
+ success: true,
305
+ message: `Successfully input text to: ${selector}`,
306
+ });
307
+ }
308
+ catch (error) {
309
+ this.sendError(requestId, `Error inputting text: ${error.message}`);
310
+ }
311
+ return;
312
+ }
313
+ // Handle other message types
314
+ switch (message.type) {
315
+ case "command_result":
316
+ console.info(`%c[MCP Server] ${message.payload?.result}`, "color: blue");
317
+ break;
318
+ case "error":
319
+ console.error(`[MCP Server] Error: ${message.payload?.error}`);
320
+ break;
321
+ case "connection_status":
322
+ console.info(`%c[MCP Server] ${message.message}`, "color: green");
323
+ break;
324
+ default:
325
+ // Only log message type, not full message content
326
+ console.info(`[MCP Server] Received message type: ${message.type}`);
327
+ }
328
+ }
329
+ /**
330
+ * Send response
331
+ */
332
+ sendResponse(requestId, data) {
333
+ if (!this.connected || !this.ws) {
334
+ console.error("[MCP Client] Not connected to server, cannot send response");
335
+ return;
336
+ }
337
+ const response = {
338
+ requestId,
339
+ ...data,
340
+ };
341
+ this.ws.send(JSON.stringify(response));
342
+ }
343
+ /**
344
+ * Send error response
345
+ */
346
+ sendError(requestId, errorMessage) {
347
+ if (!this.connected || !this.ws) {
348
+ console.error("[MCP Client] Not connected to server, cannot send error response");
349
+ return;
350
+ }
351
+ const response = {
352
+ requestId,
353
+ error: errorMessage,
354
+ };
355
+ this.ws.send(JSON.stringify(response));
356
+ }
357
+ /**
358
+ * Send command to server
359
+ */
360
+ sendCommand(command) {
361
+ if (!this.connected || !this.ws) {
362
+ console.error("[MCP Client] Not connected to server");
363
+ return;
364
+ }
365
+ const message = {
366
+ type: "command",
367
+ payload: { command },
368
+ };
369
+ this.ws.send(JSON.stringify(message));
370
+ this.commandHistory.push(command);
371
+ this.historyIndex = this.commandHistory.length;
372
+ }
373
+ /**
374
+ * Register console commands
375
+ */
376
+ registerConsoleCommands() {
377
+ // Define global commands
378
+ window.mcp = {
379
+ exec: (command) => {
380
+ this.sendCommand(command);
381
+ return "Command sent";
382
+ },
383
+ disconnect: () => {
384
+ if (this.ws) {
385
+ this.ws.close();
386
+ this.ws = null;
387
+ }
388
+ return "Disconnected";
389
+ },
390
+ reconnect: () => {
391
+ this.connect();
392
+ return "Reconnecting...";
393
+ },
394
+ help: () => {
395
+ return `
396
+ MCP Client Commands:
397
+ mcp.exec(command) - Execute a command
398
+ mcp.disconnect() - Disconnect from server
399
+ mcp.reconnect() - Reconnect to server
400
+ mcp.help() - Show help information
401
+ `;
402
+ },
403
+ };
404
+ console.info("%c[MCP Client] Console commands registered, use mcp.help() to see available commands", "color: green");
405
+ }
406
+ /**
407
+ * Get image content boundaries, remove excess whitespace
408
+ */
409
+ getContentBounds(imageData) {
410
+ const { width, height, data } = imageData;
411
+ let minX = width;
412
+ let minY = height;
413
+ let maxX = 0;
414
+ let maxY = 0;
415
+ let hasContent = false;
416
+ // Iterate through pixel data, find boundaries of non-transparent pixels
417
+ for (let y = 0; y < height; y++) {
418
+ for (let x = 0; x < width; x++) {
419
+ const alpha = data[(y * width + x) * 4 + 3]; // Alpha channel
420
+ if (alpha > 10) {
421
+ // Non-transparent pixel (allowing some slight transparency)
422
+ hasContent = true;
423
+ minX = Math.min(minX, x);
424
+ minY = Math.min(minY, y);
425
+ maxX = Math.max(maxX, x);
426
+ maxY = Math.max(maxY, y);
427
+ }
428
+ }
429
+ }
430
+ if (!hasContent) {
431
+ return null; // No content found
432
+ }
433
+ // Add some padding
434
+ const padding = 10;
435
+ minX = Math.max(0, minX - padding);
436
+ minY = Math.max(0, minY - padding);
437
+ maxX = Math.min(width - 1, maxX + padding);
438
+ maxY = Math.min(height - 1, maxY + padding);
439
+ return {
440
+ left: minX,
441
+ top: minY,
442
+ width: maxX - minX + 1,
443
+ height: maxY - minY + 1,
444
+ };
445
+ }
446
+ }
447
+ // Create and connect client instance
448
+ const client = new BrowserConsoleMCP();
449
+ client.connect();
450
+ // Export client instance
451
+ export default client;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Browser Console MCP Server
3
+ *
4
+ * This server runs in Cursor and handles commands from the browser console
5
+ */
6
+ import "node:process";
7
+ declare class BrowserConsoleMCPServer {
8
+ private wss;
9
+ private clients;
10
+ private port;
11
+ private staticServer;
12
+ private httpServer;
13
+ constructor(port?: number, staticPort?: number);
14
+ /**
15
+ * Start server
16
+ */
17
+ start(): void;
18
+ /**
19
+ * Setup WebSocket server
20
+ */
21
+ private setupWebSocketServer;
22
+ /**
23
+ * Handle client messages
24
+ */
25
+ private handleClientMessage;
26
+ /**
27
+ * Execute command
28
+ */
29
+ private executeCommand;
30
+ /**
31
+ * Send result to client
32
+ */
33
+ private sendResultToClient;
34
+ /**
35
+ * Send error to client
36
+ */
37
+ private sendErrorToClient;
38
+ /**
39
+ * Generate client ID
40
+ */
41
+ private generateClientId;
42
+ /**
43
+ * Stop server
44
+ */
45
+ stop(): void;
46
+ }
47
+ declare const server: BrowserConsoleMCPServer;
48
+ export default server;