Skip to main content
Glama
ananddtyagi

Webpage Screenshot MCP Server

login-and-wait

Opens a webpage for manual login, waits for user authentication, and saves cookies for session continuity. Ideal for automating login processes in web applications.

Instructions

Opens a webpage in a visible browser window for manual login, waits for user to complete login, then saves cookies

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
successIndicatorNoOptional CSS selector or URL pattern that indicates successful login
urlYesThe URL of the login page
useDefaultBrowserNoWhether to use the system's default browser instead of Puppeteer's bundled Chromium
waitMinutesNoMaximum minutes to wait for login (default: 3)

Implementation Reference

  • Main execution logic for the 'login-and-wait' tool: initializes visible browser, loads existing cookies, applies anti-detection, navigates to login URL, waits for completion via indicator/ navigation/ signal file/ timeout, saves cookies, keeps page open.
    async ({ url, waitMinutes, successIndicator, useDefaultBrowser }) => {
        let page: Page | null = null;
        
        try {
            // Initialize browser in non-headless mode with default browser option
            const browserInstance = await initBrowser(false, useDefaultBrowser);
            
            // Create or reuse persistent page
            if (!persistentPage || persistentPage.isClosed()) {
                persistentPage = await browserInstance.newPage();
            }
            page = persistentPage;
            
            // Load existing cookies if available
            const existingCookies = await loadCookies(url);
            if (existingCookies.length > 0) {
                await page.setCookie(...existingCookies);
            }
            
            // Set user agent and anti-detection measures for login
            await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
            
            // Additional anti-detection measures for Google login
            await page.evaluateOnNewDocument(() => {
                // Remove webdriver property
                delete (window.navigator as any).webdriver;
                
                // Override the plugins property to add fake plugins
                Object.defineProperty(window.navigator, 'plugins', {
                    get: () => [1, 2, 3, 4, 5]
                });
                
                // Override the languages property
                Object.defineProperty(window.navigator, 'languages', {
                    get: () => ['en-US', 'en']
                });
                
                // Override permissions
                Object.defineProperty(window.navigator, 'permissions', {
                    get: () => ({
                        query: () => Promise.resolve({ state: 'granted' })
                    })
                });
            });
            
            // Navigate to the URL
            await page.goto(url, {
                waitUntil: 'networkidle2',
                timeout: 30000
            });
            
            const startTime = Date.now();
            const maxWaitTime = waitMinutes * 60 * 1000;
            
            // Wait for login
            console.error(`Waiting for manual login... (up to ${waitMinutes} minutes)`);
            console.error(`Please complete the login in the ${useDefaultBrowser ? 'default' : 'Puppeteer'} browser window.`);
            console.error(`To continue immediately after login, use the 'signal-login-complete' tool or navigate away from the login page.`);
            
            if (successIndicator) {
                try {
                    // If it's a URL pattern
                    if (successIndicator.startsWith('http') || successIndicator.includes('/')) {
                        await page.waitForFunction(
                            (pattern) => window.location.href.includes(pattern),
                            { timeout: maxWaitTime },
                            successIndicator
                        );
                    } else {
                        // Otherwise treat as CSS selector
                        await page.waitForSelector(successIndicator, { timeout: maxWaitTime });
                    }
                } catch (timeoutError) {
                    // Continue even if indicator not found
                    console.error('Success indicator not found, but continuing...');
                }
            } else {
                // Wait for user confirmation via multiple methods
                await new Promise((resolve) => {
                    const checkInterval = setInterval(() => {
                        if (Date.now() - startTime > maxWaitTime) {
                            clearInterval(checkInterval);
                            resolve(null);
                        }
                    }, 1000);
                    
                    // Method 1: Page navigation detection
                    page?.on('framenavigated', () => {
                        const currentUrl = page?.url() || '';
                        // Check if we've navigated away from login pages
                        if (!currentUrl.includes('accounts.google.com') && 
                            !currentUrl.includes('login') && 
                            !currentUrl.includes('signin') &&
                            !currentUrl.includes('auth')) {
                            setTimeout(() => {
                                clearInterval(checkInterval);
                                resolve(null);
                            }, 2000);
                        }
                    });
                    
                    // Method 2: Check for a completion marker file
                    const completionFile = path.join(os.tmpdir(), 'mcp-login-complete.txt');
                    const fileCheckInterval = setInterval(async () => {
                        try {
                            if (fs.existsSync(completionFile)) {
                                await fsPromises.unlink(completionFile).catch(() => {});
                                clearInterval(checkInterval);
                                clearInterval(fileCheckInterval);
                                resolve(null);
                            }
                        } catch (e) {
                            // Ignore file check errors
                        }
                    }, 1000);
                    
                    // Clean up file checker when main interval ends
                    setTimeout(() => {
                        clearInterval(fileCheckInterval);
                    }, maxWaitTime);
                });
            }
            
            // Save cookies after login
            const cookies = await page.cookies();
            await saveCookies(url, cookies);
            
            const finalUrl = page.url();
            const browserType = useDefaultBrowser ? 'default browser' : 'Puppeteer browser';
            
            return {
                content: [
                    {
                        type: "text",
                        text: `Login session established and cookies saved!\n\nBrowser: ${browserType}\nInitial URL: ${url}\nFinal URL: ${finalUrl}\nCookies saved: ${cookies.length}\n\nLogin completed via: ${successIndicator ? 'success indicator detected' : 'automatic navigation detection or manual signal'}\n\nThe browser window will remain open for future screenshots.`
                    }
                ],
            };
        } catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            return {
                isError: true,
                content: [
                    {
                        type: "text",
                        text: `Error during login process: ${errorMessage}`,
                    },
                ],
            };
        }
        // Don't close the page - keep it for future use
    }
  • Zod schema defining input parameters for the login-and-wait tool.
    {
        url: z.string().url().describe("The URL of the login page"),
        waitMinutes: z.number().optional().default(3).describe("Maximum minutes to wait for login (default: 3)"),
        successIndicator: z.string().optional().describe("Optional CSS selector or URL pattern that indicates successful login"),
        useDefaultBrowser: z.boolean().optional().default(true).describe("Whether to use the system's default browser instead of Puppeteer's bundled Chromium")
    },
  • src/index.ts:332-495 (registration)
    Registration of the 'login-and-wait' tool using server.tool() with name, description, schema, and handler.
    // Register the login-and-wait tool
    server.tool(
        "login-and-wait",
        "Opens a webpage in a visible browser window for manual login, waits for user to complete login, then saves cookies",
        {
            url: z.string().url().describe("The URL of the login page"),
            waitMinutes: z.number().optional().default(3).describe("Maximum minutes to wait for login (default: 3)"),
            successIndicator: z.string().optional().describe("Optional CSS selector or URL pattern that indicates successful login"),
            useDefaultBrowser: z.boolean().optional().default(true).describe("Whether to use the system's default browser instead of Puppeteer's bundled Chromium")
        },
        async ({ url, waitMinutes, successIndicator, useDefaultBrowser }) => {
            let page: Page | null = null;
            
            try {
                // Initialize browser in non-headless mode with default browser option
                const browserInstance = await initBrowser(false, useDefaultBrowser);
                
                // Create or reuse persistent page
                if (!persistentPage || persistentPage.isClosed()) {
                    persistentPage = await browserInstance.newPage();
                }
                page = persistentPage;
                
                // Load existing cookies if available
                const existingCookies = await loadCookies(url);
                if (existingCookies.length > 0) {
                    await page.setCookie(...existingCookies);
                }
                
                // Set user agent and anti-detection measures for login
                await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
                
                // Additional anti-detection measures for Google login
                await page.evaluateOnNewDocument(() => {
                    // Remove webdriver property
                    delete (window.navigator as any).webdriver;
                    
                    // Override the plugins property to add fake plugins
                    Object.defineProperty(window.navigator, 'plugins', {
                        get: () => [1, 2, 3, 4, 5]
                    });
                    
                    // Override the languages property
                    Object.defineProperty(window.navigator, 'languages', {
                        get: () => ['en-US', 'en']
                    });
                    
                    // Override permissions
                    Object.defineProperty(window.navigator, 'permissions', {
                        get: () => ({
                            query: () => Promise.resolve({ state: 'granted' })
                        })
                    });
                });
                
                // Navigate to the URL
                await page.goto(url, {
                    waitUntil: 'networkidle2',
                    timeout: 30000
                });
                
                const startTime = Date.now();
                const maxWaitTime = waitMinutes * 60 * 1000;
                
                // Wait for login
                console.error(`Waiting for manual login... (up to ${waitMinutes} minutes)`);
                console.error(`Please complete the login in the ${useDefaultBrowser ? 'default' : 'Puppeteer'} browser window.`);
                console.error(`To continue immediately after login, use the 'signal-login-complete' tool or navigate away from the login page.`);
                
                if (successIndicator) {
                    try {
                        // If it's a URL pattern
                        if (successIndicator.startsWith('http') || successIndicator.includes('/')) {
                            await page.waitForFunction(
                                (pattern) => window.location.href.includes(pattern),
                                { timeout: maxWaitTime },
                                successIndicator
                            );
                        } else {
                            // Otherwise treat as CSS selector
                            await page.waitForSelector(successIndicator, { timeout: maxWaitTime });
                        }
                    } catch (timeoutError) {
                        // Continue even if indicator not found
                        console.error('Success indicator not found, but continuing...');
                    }
                } else {
                    // Wait for user confirmation via multiple methods
                    await new Promise((resolve) => {
                        const checkInterval = setInterval(() => {
                            if (Date.now() - startTime > maxWaitTime) {
                                clearInterval(checkInterval);
                                resolve(null);
                            }
                        }, 1000);
                        
                        // Method 1: Page navigation detection
                        page?.on('framenavigated', () => {
                            const currentUrl = page?.url() || '';
                            // Check if we've navigated away from login pages
                            if (!currentUrl.includes('accounts.google.com') && 
                                !currentUrl.includes('login') && 
                                !currentUrl.includes('signin') &&
                                !currentUrl.includes('auth')) {
                                setTimeout(() => {
                                    clearInterval(checkInterval);
                                    resolve(null);
                                }, 2000);
                            }
                        });
                        
                        // Method 2: Check for a completion marker file
                        const completionFile = path.join(os.tmpdir(), 'mcp-login-complete.txt');
                        const fileCheckInterval = setInterval(async () => {
                            try {
                                if (fs.existsSync(completionFile)) {
                                    await fsPromises.unlink(completionFile).catch(() => {});
                                    clearInterval(checkInterval);
                                    clearInterval(fileCheckInterval);
                                    resolve(null);
                                }
                            } catch (e) {
                                // Ignore file check errors
                            }
                        }, 1000);
                        
                        // Clean up file checker when main interval ends
                        setTimeout(() => {
                            clearInterval(fileCheckInterval);
                        }, maxWaitTime);
                    });
                }
                
                // Save cookies after login
                const cookies = await page.cookies();
                await saveCookies(url, cookies);
                
                const finalUrl = page.url();
                const browserType = useDefaultBrowser ? 'default browser' : 'Puppeteer browser';
                
                return {
                    content: [
                        {
                            type: "text",
                            text: `Login session established and cookies saved!\n\nBrowser: ${browserType}\nInitial URL: ${url}\nFinal URL: ${finalUrl}\nCookies saved: ${cookies.length}\n\nLogin completed via: ${successIndicator ? 'success indicator detected' : 'automatic navigation detection or manual signal'}\n\nThe browser window will remain open for future screenshots.`
                        }
                    ],
                };
            } catch (error) {
                const errorMessage = error instanceof Error ? error.message : String(error);
                return {
                    isError: true,
                    content: [
                        {
                            type: "text",
                            text: `Error during login process: ${errorMessage}`,
                        },
                    ],
                };
            }
            // Don't close the page - keep it for future use
        }
    );
  • 'signal-login-complete' helper tool that creates a temp file to signal the login-and-wait tool to continue after manual login.
    server.tool(
        "signal-login-complete",
        "Signals that manual login is complete and the login-and-wait tool should continue",
        {},
        async () => {
            try {
                const completionFile = path.join(os.tmpdir(), 'mcp-login-complete.txt');
                await fsPromises.writeFile(completionFile, 'complete');
                
                return {
                    content: [
                        {
                            type: "text",
                            text: "Login completion signal sent! The login-and-wait tool should continue shortly."
                        }
                    ],
                };
            } catch (error) {
                const errorMessage = error instanceof Error ? error.message : String(error);
                return {
                    isError: true,
                    content: [
                        {
                            type: "text",
                            text: `Error signaling login completion: ${errorMessage}`,
                        },
                    ],
                };
            }
        }
    );
  • loadCookies helper function used by login-and-wait to load existing cookies for the domain.
    async function loadCookies(url: string): Promise<Cookie[]> {
        try {
            const domain = getDomainFromUrl(url);
            const cookiesPath = path.join(cookiesDir, `${domain}.json`);
            const cookiesData = await fsPromises.readFile(cookiesPath, 'utf-8');
            return JSON.parse(cookiesData);
        } catch {
            return [];
        }
Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ananddtyagi/webpage-screenshot-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server