Skip to main content
Glama

@pokutuna/mcp-chrome-tabs

by pokutuna
arc.ts4.6 kB
import type { BrowserInterface, TabRef, Tab, TabContent } from "./browser.js"; import { escapeAppleScript, executeAppleScript, separator, } from "./osascript.js"; /* Arc browser implementation notes - Tab/Window IDs are UUIDs (unlike Chrome's numeric IDs) - The return value of "execute javascript" may be wrapped in "..." and escaped (e.g., \u003C), so decode it with JSON.parse - Directly telling the active tab (front window/active tab) can fail depending on the environment; even when unspecified, first resolve the active tab's windowId/tabId and execute via the ID-targeted path for stability */ async function getArcTabList(applicationName: string): Promise<Tab[]> { const sep = separator(); const appleScript = ` tell application "${applicationName}" set output to "" repeat with aWindow in (every window) set windowId to id of aWindow repeat with aTab in (every tab of aWindow) set tabId to id of aTab set tabTitle to title of aTab set tabURL to URL of aTab set output to output & windowId & "${sep}" & tabId & "${sep}" & tabTitle & "${sep}" & tabURL & "\\n" end repeat end repeat return output end tell `; const result = await executeAppleScript(appleScript); const lines = result.trim().split("\n"); const tabs: Tab[] = []; for (const line of lines) { const [wId, tId, title, url] = line.split(sep); if (!/^https?:\/\//.test(url)) continue; tabs.push({ windowId: wId, tabId: tId, title: title.trim(), url: url.trim(), }); } return tabs; } async function getActiveTabRef(applicationName: string): Promise<TabRef> { const sep = separator(); const appleScript = ` try tell application "${applicationName}" set wId to id of front window set tId to id of active tab of front window return wId & "${sep}" & tId end tell on error errMsg return "ERROR" & "${sep}" & errMsg end try `; const result = await executeAppleScript(appleScript); if (result.startsWith(`ERROR${sep}`)) { throw new Error(result.split(sep)[1]); } const [windowId, tabId] = result.split(sep); return { windowId: windowId.trim(), tabId: tabId.trim() }; } async function getPageContent( applicationName: string, tab?: TabRef | null ): Promise<TabContent> { const sep = separator(); const inner = ` set tabTitle to title set tabURL to URL set tabContent to execute javascript "document.documentElement.outerHTML" return tabTitle & "${sep}" & tabURL & "${sep}" & tabContent `; const targetTab: TabRef = tab ?? (await getActiveTabRef(applicationName)); const appleScript = ` try tell application "${applicationName}" tell window id "${targetTab.windowId}" tell tab id "${targetTab.tabId}" with timeout of 3 seconds ${inner} end timeout end tell end tell end tell on error errMsg return "ERROR" & "${sep}" & errMsg end try `; const scriptResult = await executeAppleScript(appleScript); if (scriptResult.startsWith(`ERROR${sep}`)) { throw new Error(scriptResult.split(sep)[1]); } const parts = scriptResult.split(sep).map((part) => part.trim()); if (parts.length < 3) { throw new Error("Failed to read the tab content"); } const [title, url, rawContent] = parts; // Arc's "execute javascript" return string may be wrapped in "..." and escaped like \u003C. // In such cases, decode with JSON.parse to restore the raw HTML. let content = rawContent; if (content.startsWith('"') && content.endsWith('"')) { try { content = JSON.parse(content); } catch { // If decoding fails, return the value as-is } } return { title, url, content, }; } async function openURL(applicationName: string, url: string): Promise<TabRef> { const escapedUrl = escapeAppleScript(url); const sep = separator(); const appleScript = ` tell application "${applicationName}" tell front window set newTab to (make new tab with properties {URL:"${escapedUrl}"}) set windowId to id set tabId to id of (active tab) -- cannot retrieve id of newTab return windowId & "${sep}" & tabId end tell end tell `; const result = await executeAppleScript(appleScript); const [windowId, tabId] = result.trim().split(sep); return { windowId, tabId }; } export const arcBrowser: BrowserInterface = { getTabList: getArcTabList, getPageContent, openURL, };

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/pokutuna/mcp-chrome-tabs'

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