Skip to main content
Glama
kevin-weitgenant

LinkedIn-Posts-Hunter-MCP-Server

browser.ts4.97 kB
import { chromium, Browser, BrowserContext, Page } from "playwright"; import { saveAuthData } from './storage.js'; export interface AuthBrowserSession { browser: Browser; context: BrowserContext; page: Page; } /** * Launch browser for LinkedIn authentication */ export const launchAuthBrowser = async (): Promise<AuthBrowserSession> => { const browser = await chromium.launch({ headless: false }); const context = await browser.newContext(); const page = await context.newPage(); // Set generous timeouts for auth flow page.setDefaultNavigationTimeout(0); page.setDefaultTimeout(0); return { browser, context, page }; }; /** * Navigate to LinkedIn login page */ export const navigateToLogin = async (page: Page): Promise<void> => { await page.goto("https://www.linkedin.com/login", { waitUntil: "domcontentloaded", }); }; /** * Check if URL looks like LinkedIn feed */ export const looksLikeFeed = (url: string): boolean => { return /linkedin\.com\/feed/.test(url); }; /** * Check if context has LinkedIn authentication cookie */ export const hasLinkedInCookie = async (context: BrowserContext): Promise<boolean> => { try { const cookies = await context.cookies(["https://www.linkedin.com"]); return cookies.some((c) => c.name === "li_at"); } catch { return false; } }; /** * Set up authentication detection and auto-save */ export const setupAuthDetection = ( page: Page, context: BrowserContext, onAuthDetected: (reason: string) => Promise<void> ): { cleanup: () => void } => { let saved = false; let interval: NodeJS.Timeout; const saveOnce = async (reason: string): Promise<void> => { if (saved) return; try { const storageState = await context.storageState(); await saveAuthData(storageState); saved = true; await onAuthDetected(reason); } catch (e) { // ignore save errors during auth flow } }; // Event listeners for navigation-based detection const handleFrameNavigation = async (frame: any) => { if (frame === page.mainFrame()) { const url = frame.url(); if (looksLikeFeed(url)) { await saveOnce("navigated-to-feed"); } } }; const handleDOMContentLoaded = async () => { if (looksLikeFeed(page.url())) { await saveOnce("domcontentloaded-on-feed"); } }; const handlePageClose = () => { cleanup(); }; // Set up event listeners page.on("framenavigated", handleFrameNavigation); page.on("domcontentloaded", handleDOMContentLoaded); page.on("close", handlePageClose); // Periodic cookie checking interval = setInterval(async () => { if (saved) return; try { const hasCookie = await hasLinkedInCookie(context); const onFeed = looksLikeFeed(page.url()); if (hasCookie || onFeed) { await saveOnce(hasCookie ? "li_at-cookie-detected" : "feed-url-detected"); } } catch { // ignore errors during periodic checks } }, 1500); // Immediate check for already-authenticated scenarios if (looksLikeFeed(page.url())) { saveOnce("immediate-on-feed").catch(() => {}); } const cleanup = () => { if (interval) clearInterval(interval); page.off("framenavigated", handleFrameNavigation); page.off("domcontentloaded", handleDOMContentLoaded); page.off("close", handlePageClose); }; return { cleanup }; }; /** * Wait for user to complete authentication */ export const waitForAuthentication = async (page: Page): Promise<void> => { try { await page.waitForEvent("close"); } catch { // Page might be already closed } }; /** * Clean up browser session */ export const cleanupBrowser = async (session: AuthBrowserSession): Promise<void> => { const { browser, context } = session; try { await context.close(); } catch { // ignore cleanup errors } try { await browser.close(); } catch { // ignore cleanup errors } }; /** * Complete authentication flow */ export const performAuthentication = async (): Promise<{ success: boolean; reason?: string; error?: string; }> => { let session: AuthBrowserSession | null = null; let authReason: string | undefined; try { session = await launchAuthBrowser(); const { page, context } = session; await navigateToLogin(page); // Browser window will guide the user through login // Authentication will be detected automatically const { cleanup } = setupAuthDetection(page, context, async (reason) => { authReason = reason; // Authentication successful }); await waitForAuthentication(page); cleanup(); return { success: !!authReason, reason: authReason }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' }; } finally { if (session) { await cleanupBrowser(session); } } };

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/kevin-weitgenant/LinkedIn-Posts-Hunter-MCP-Server'

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