diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index b1f5fe0..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/browser-tools-mcp/README.md b/browser-tools-mcp/README.md index 059ec32..683ae0d 100644 --- a/browser-tools-mcp/README.md +++ b/browser-tools-mcp/README.md @@ -63,6 +63,7 @@ The server provides the following MCP functions: - `mcp_getNetworkSuccess` - Get successful network requests - `mcp_getNetworkLogs` - Get all network logs - `mcp_getSelectedElement` - Get the currently selected DOM element +- `mcp_refreshBrowser` - Refresh the currently active browser tab that's connected to the MCP server - `mcp_runAccessibilityAudit` - Run a WCAG-compliant accessibility audit - `mcp_runPerformanceAudit` - Run a performance audit - `mcp_runSEOAudit` - Run an SEO audit diff --git a/browser-tools-mcp/mcp-server.ts b/browser-tools-mcp/mcp-server.ts index a7a1272..6776554 100644 --- a/browser-tools-mcp/mcp-server.ts +++ b/browser-tools-mcp/mcp-server.ts @@ -339,6 +339,57 @@ server.tool("wipeLogs", "Wipe all browser logs from memory", async () => { }); }); +server.tool("refreshBrowser", "Refresh the currently active browser tab that is connected to the MCP server", async () => { + return await withServerConnection(async () => { + try { + const response = await fetch( + `http://${discoveredHost}:${discoveredPort}/refresh-browser`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok) { + return { + content: [ + { + type: "text", + text: result.message || "Browser refreshed successfully", + }, + ], + }; + } else { + return { + content: [ + { + type: "text", + text: `Error refreshing browser: ${result.error || "Unknown error"}`, + }, + ], + isError: true, + }; + } + } catch (error: any) { + const errorMessage = + error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text", + text: `Failed to refresh browser: ${errorMessage}`, + }, + ], + isError: true, + }; + } + }); +}); + // Define audit categories as enum to match the server's AuditCategory enum enum AuditCategory { ACCESSIBILITY = "accessibility", diff --git a/browser-tools-mcp/package-lock.json b/browser-tools-mcp/package-lock.json index 0784e8f..be0a7a1 100644 --- a/browser-tools-mcp/package-lock.json +++ b/browser-tools-mcp/package-lock.json @@ -1,12 +1,12 @@ { "name": "@agentdeskai/browser-tools-mcp", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@agentdeskai/browser-tools-mcp", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.4.1", diff --git a/browser-tools-server/browser-connector.ts b/browser-tools-server/browser-connector.ts index a4cc03c..d57683e 100644 --- a/browser-tools-server/browser-connector.ts +++ b/browser-tools-server/browser-connector.ts @@ -540,6 +540,8 @@ app.post("/wipelogs", (req, res) => { res.json({ status: "ok", message: "All logs cleared successfully" }); }); + + // Add endpoint for the extension to report the current URL app.post("/current-url", (req, res) => { console.log( @@ -936,6 +938,37 @@ export class BrowserConnector { return this.activeConnection !== null; } + public async sendRefreshCommand(): Promise { + console.log("Browser Connector: sendRefreshCommand called"); + console.log(`Browser Connector: Current tab ID being targeted: ${currentTabId}`); + console.log(`Browser Connector: Current URL being targeted: ${currentUrl}`); + + if (!this.activeConnection) { + throw new Error("No active WebSocket connection to Chrome extension"); + } + + return new Promise((resolve, reject) => { + try { + const message = JSON.stringify({ + type: "refresh-browser", + timestamp: Date.now(), + targetTabId: currentTabId, // Include the tab ID for reference + }); + + console.log("Browser Connector: Sending refresh command to extension:", message); + this.activeConnection!.send(message); + + // For refresh, we assume success if we can send the message + // The actual refresh will happen asynchronously in the browser + console.log(`Browser Connector: Refresh command sent for tab ${currentTabId}`); + resolve(); + } catch (error) { + console.error("Browser Connector: Error sending refresh command:", error); + reject(error); + } + }); + } + // Add new endpoint for programmatic screenshot capture async captureScreenshot(req: express.Request, res: express.Response) { console.log("Browser Connector: Starting captureScreenshot method"); @@ -1473,6 +1506,50 @@ export class BrowserConnector { // Initialize the browser connector with the existing app AND server const browserConnector = new BrowserConnector(app, server); + // Add refresh browser endpoint after browserConnector is created + app.post("/refresh-browser", (req, res) => { + console.log("Browser Connector: Received request to refresh browser"); + + if (!browserConnector.hasActiveConnection()) { + console.log("Browser Connector: No active WebSocket connection"); + res.status(503).json({ + status: "error", + error: "Chrome extension not connected" + }); + return; + } + + try { + // Send refresh command to the Chrome extension + browserConnector.sendRefreshCommand() + .then(() => { + console.log("Browser Connector: Refresh command sent successfully"); + const message = currentTabId && currentUrl + ? `Browser refresh command sent to tab ${currentTabId} (${currentUrl})` + : "Browser refresh command sent to active tab"; + res.json({ + status: "ok", + message: message, + tabId: currentTabId, + url: currentUrl + }); + }) + .catch((error: any) => { + console.error("Browser Connector: Error sending refresh command:", error); + res.status(500).json({ + status: "error", + error: error.message || "Failed to send refresh command" + }); + }); + } catch (error: any) { + console.error("Browser Connector: Error in refresh endpoint:", error); + res.status(500).json({ + status: "error", + error: error.message || "Internal server error" + }); + } + }); + // Handle shutdown gracefully with improved error handling process.on("SIGINT", async () => { console.log("\nReceived SIGINT signal. Starting graceful shutdown..."); diff --git a/browser-tools-server/package-lock.json b/browser-tools-server/package-lock.json index c38a600..966f355 100644 --- a/browser-tools-server/package-lock.json +++ b/browser-tools-server/package-lock.json @@ -1,12 +1,12 @@ { "name": "@agentdeskai/browser-tools-server", - "version": "1.1.1", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@agentdeskai/browser-tools-server", - "version": "1.1.1", + "version": "1.2.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.4.1", diff --git a/chrome-extension/background.js b/chrome-extension/background.js index 45bc4d7..e334534 100644 --- a/chrome-extension/background.js +++ b/chrome-extension/background.js @@ -11,6 +11,21 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { return true; // Required to use sendResponse asynchronously } + if (message.type === "REFRESH_TAB" && message.tabId) { + console.log(`Background: Received request to refresh tab ${message.tabId}`); + + chrome.tabs.reload(message.tabId, {}, () => { + if (chrome.runtime.lastError) { + console.error("Background: Error refreshing tab:", chrome.runtime.lastError); + sendResponse({ success: false, error: chrome.runtime.lastError.message }); + } else { + console.log(`Background: Successfully refreshed tab ${message.tabId}`); + sendResponse({ success: true }); + } + }); + return true; // Required to use sendResponse asynchronously + } + // Handle explicit request to update the server with the URL if (message.type === "UPDATE_SERVER_URL" && message.tabId && message.url) { console.log( diff --git a/chrome-extension/devtools.js b/chrome-extension/devtools.js index 6197f2f..93591d2 100644 --- a/chrome-extension/devtools.js +++ b/chrome-extension/devtools.js @@ -1002,6 +1002,28 @@ async function setupWebSocket() { ws.send(JSON.stringify(response)); }); + } else if (message.type === "refresh-browser") { + console.log("Chrome Extension: Received refresh browser command"); + + // Use chrome.tabs.reload to refresh the current tab + const tabId = chrome.devtools.inspectedWindow.tabId; + + chrome.runtime.sendMessage( + { + type: "REFRESH_TAB", + tabId: tabId, + }, + (response) => { + if (chrome.runtime.lastError) { + console.error( + "Chrome Extension: Error refreshing tab:", + chrome.runtime.lastError + ); + } else { + console.log("Chrome Extension: Tab refresh command sent successfully"); + } + } + ); } else if (message.type === "get-current-url") { console.log("Chrome Extension: Received request for current URL");