Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,12 @@ If Google keeps showing CAPTCHAs:
- Add 10-30 second delays between searches
- The server automatically restarts after 3 consecutive CAPTCHAs

**Browser won't launch:**
**Browser won't launch or persistent issues:**

Clear the browser profile:
The easiest way to fix browser-level issues or log out of Google is to use the built-in tool:
- Tell your agent: "Clear my browser profile" or run the `clear_browser_profile` tool.

Manual fallback:
```bash
# Linux/macOS
rm -rf ~/.local/share/google-ai-mode-mcp/chrome_profile
Expand All @@ -250,7 +253,7 @@ rmdir /s "%LOCALAPPDATA%\google-ai-mode-mcp\chrome_profile"

**Wrong language results:**

The server forces English results. If you still get wrong languages, clear the profile (see above).
The server forces English results. If you still get wrong languages, use the `clear_browser_profile` tool.

**Missing citations:**

Expand Down
8 changes: 6 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
} from "@modelcontextprotocol/sdk/types.js";

import { ToolHandler } from "./tools/tool-handler.js";
import { SEARCH_AI_TOOL } from "./tools/search-tool.js";
import { SEARCH_AI_TOOL, CLEAR_PROFILE_TOOL } from "./tools/search-tool.js";
import {
GOOGLE_AI_SEARCH_PROMPT,
GOOGLE_AI_SEARCH_PROMPT_TEMPLATE,
Expand Down Expand Up @@ -85,7 +85,7 @@ class GoogleAiSearchMCPServer {
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
log.info("πŸ“‹ [MCP] list_tools request received");
return {
tools: [SEARCH_AI_TOOL],
tools: [SEARCH_AI_TOOL, CLEAR_PROFILE_TOOL],
};
});

Expand Down Expand Up @@ -130,6 +130,10 @@ class GoogleAiSearchMCPServer {
);
break;

case "clear_browser_profile":
result = await this.toolHandler.handleClearProfile(sendProgress);
break;

default:
log.error(`❌ [MCP] Unknown tool: ${name}`);
return {
Expand Down
2 changes: 1 addition & 1 deletion src/search/search-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class SearchHandler {
// Navigate to Google AI Search
log.info("🌐 Navigating to Google AI Search...");
await page.goto(searchUrl, {
waitUntil: "domcontentloaded",
waitUntil: "networkidle", // Wait for network to be idle to captures redirects/AJAX correctly
timeout: CONFIG.browserTimeout,
});

Expand Down
10 changes: 10 additions & 0 deletions src/tools/search-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,13 @@ Note: If CAPTCHA is detected, you will be prompted to solve it in a visible brow
required: ["query"],
},
};

export const CLEAR_PROFILE_TOOL: Tool = {
name: "clear_browser_profile",
description: "Clears the browser profile (cookies, session, cache). Useful for resetting Google login or fixing persistent issues.",
inputSchema: {
type: "object",
properties: {},
},
};

69 changes: 65 additions & 4 deletions src/tools/tool-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,12 @@ export class ToolHandler {
// Generate filename
let filename: string;
if (customFilename) {
// Use custom filename, ensure .md extension
filename = customFilename.endsWith(".md")
? customFilename
: `${customFilename}.md`;
// Sanitize filename to prevent Path Traversal
const sanitized = path.basename(customFilename);
// Use sanitized filename, ensure .md extension
filename = sanitized.endsWith(".md")
? sanitized
: `${sanitized}.md`;
} else {
// Auto-generate from query and timestamp
const timestamp = new Date()
Expand All @@ -149,6 +151,65 @@ export class ToolHandler {
return filePath;
}

/**
* Handle clear_browser_profile tool call
*/
async handleClearProfile(sendProgress: ProgressCallback): Promise<any> {
try {
log.info("🧹 Tool call: clear_browser_profile");

// 1. Close search handler (which closes the current browser context)
await sendProgress("Terminating active browser sessions...");
await this.searchHandler.cleanup();

// 2. Cooldown to allow OS to release file locks
await new Promise(resolve => setTimeout(resolve, 2000));

// 3. Wipe browser profile data
await sendProgress("Wiping browser profile data...");

const profileDir = CONFIG.browserProfileDir;
if (fs.existsSync(profileDir)) {
try {
// Recursive deletion of profile directory
// Using force:true to ignore non-existent files and handle read-only files
fs.rmSync(profileDir, { recursive: true, force: true });

// Triple-check: if still exists, it might be a permission/lock issue
if (fs.existsSync(profileDir)) {
log.warning(`⚠️ Profile directory still exists at: ${profileDir} - attempting second pass`);
await new Promise(resolve => setTimeout(resolve, 1000));
fs.rmSync(profileDir, { recursive: true, force: true });
}

log.success(`βœ… Profile cleared at: ${profileDir}`);
} catch (rmError) {
log.error(`Deletion failed: ${rmError}`);
// On Windows, if process is still locked, we might need to inform the user
if (process.platform === 'win32') {
throw new Error(`Could not delete profile directory. It may be locked by another process. Please close all Chrome/Chromium windows and try again.`);
}
throw rmError;
}
} else {
log.info("Profile directory already empty or not found.");
}

await sendProgress("Profile cleared successfully!", 100, 100);

return {
success: true,
message: "Browser profile cleared successfully. Local history, sessions, and cookies have been removed.",
};
} catch (error) {
log.error(`Clear profile error: ${error}`);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}

/**
* Cleanup handler
*/
Expand Down