Skip to main content
Glama
kazuph

MCP Browser Tabs Server

by kazuph

get_tabs

Retrieve all open Google Chrome tabs with unique, persistent IDs for reliable identification and management. Output includes both display format and Tab ID for consistent operations via the MCP Browser Tabs Server.

Instructions

Get all open tabs from Google Chrome browser with unique tab IDs. Each tab has a stable, unique ID that persists across browser operations. Output shows both display format (1-1, 1-2) and Tab ID [Tab ID: 1234567890] for each tab. ALWAYS use the Tab ID number for reliable operations.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Handler for executing the 'get_tabs' tool: fetches Chrome tabs using getChromeTabsWithIds(), formats them with unique Tab IDs and safety instructions, returns formatted text response.
          if (name === "get_tabs") {
            const windows = await getChromeTabsWithIds();
            
            const formattedOutput = windows
              .map((window) => {
                const activeTab = window.tabs.find(tab => tab.isActive);
                const activeIndicator = activeTab ? ` (Active: Tab ID ${activeTab.id})` : "";
                
                return `Window ${window.windowIndex}${activeIndicator}:
    ${window.tabs
      .map((tab) => {
        const activeMarker = tab.isActive ? " ★" : "";
        return `  ${window.windowIndex}-${tab.tabIndex}. ${tab.title}${activeMarker}
         ${tab.url}
         [Tab ID: ${tab.id}]${activeMarker}`;
      })
      .join("\n")}`;
              })
              .join("\n\n");
    
            const totalTabs = windows.reduce((sum, window) => sum + window.tabs.length, 0);
            
            return {
              content: [
                {
                  type: "text",
                  text: `Found ${totalTabs} open tabs in Chrome:
    
    ${formattedOutput}
    
    🔥 CRITICAL INSTRUCTIONS FOR AI:
    1. ALWAYS use Tab ID (numbers in [Tab ID: xxxxx]) for tab operations
    2. NEVER use display numbers (1-1, 1-2) - these are just for human reference
    3. Use close_tab_by_id or activate_tab_by_id with the Tab ID number
    4. Example: If you see "[Tab ID: 1594670961]", use tabId: 1594670961
    
    ⚠️ DANGER: Window-Tab index changes when tabs are reordered/closed
    ✅ SAFE: Tab ID never changes and is unique across all tabs`,
                },
              ],
            };
          }
  • src/index.ts:235-239 (registration)
    Registration of the 'get_tabs' tool in the listTools response, including name, description, and empty input schema (no parameters required).
    {
      name: "get_tabs",
      description: "Get all open tabs from Google Chrome browser with unique tab IDs. Each tab has a stable, unique ID that persists across browser operations. Output shows both display format (1-1, 1-2) and Tab ID [Tab ID: 1234567890] for each tab. ALWAYS use the Tab ID number for reliable operations.",
      inputSchema: zodToJsonSchema(z.object({})),
    },
  • Input schema for get_tabs tool: empty object since no input parameters are needed.
      inputSchema: zodToJsonSchema(z.object({})),
    },
  • Core helper function that runs AppleScript to fetch all Chrome tabs with unique IDs, parses the pipe-delimited output, groups by window, and returns structured ChromeWindow[] data used by the get_tabs handler.
    async function getChromeTabsWithIds(): Promise<ChromeWindow[]> {
      const script = `
        tell application "Google Chrome"
          set windowList to windows
          set output to ""
          repeat with windowIndex from 1 to count of windowList
            set theWindow to item windowIndex of windowList
            set windowID to id of theWindow
            set activeTabIndex to active tab index of theWindow
            set tabList to tabs of theWindow
            repeat with tabIndexInWindow from 1 to count of tabList
              set theTab to item tabIndexInWindow of tabList
              set tabID to id of theTab
              set isActive to (tabIndexInWindow = activeTabIndex)
              set output to output & windowID & "|||" & windowIndex & "|||" & tabID & "|||" & tabIndexInWindow & "|||" & isActive & "|||" & (title of theTab) & "|||" & (URL of theTab) & "\\n"
            end repeat
          end repeat
          return output
        end tell
      `;
    
      try {
        const { stdout } = await execAsync(`osascript -e '${script}'`);
        const tabsData = stdout
          .trim()
          .split("\n")
          .filter((line) => line.length > 0)
          .map((line) => {
            const [windowId, windowIndex, tabId, tabIndex, isActive, title, url] = line.split("|||");
            return {
              windowId: Number.parseInt(windowId, 10),
              windowIndex: Number.parseInt(windowIndex, 10),
              tabId: Number.parseInt(tabId, 10),
              tabIndex: Number.parseInt(tabIndex, 10),
              isActive: isActive === "true",
              title: title || "",
              url: url || "",
            };
          });
    
        // Group by windows
        const windowMap = new Map<number, ChromeWindow>();
        
        for (const tabData of tabsData) {
          if (!windowMap.has(tabData.windowId)) {
            windowMap.set(tabData.windowId, {
              windowId: tabData.windowId,
              windowIndex: tabData.windowIndex,
              tabs: [],
            });
          }
          
          const window = windowMap.get(tabData.windowId)!;
          window.tabs.push({
            id: tabData.tabId,
            windowId: tabData.windowId,
            title: tabData.title,
            url: tabData.url,
            isActive: tabData.isActive,
            windowIndex: tabData.windowIndex,
            tabIndex: tabData.tabIndex,
          });
        }
    
        return Array.from(windowMap.values());
      } catch (error) {
        throw new Error(
          `Failed to get Chrome tabs: ${error instanceof Error ? error.message : String(error)}`
        );
      }
    }
  • Type interfaces for ChromeTab and ChromeWindow used throughout the get_tabs implementation.
    interface ChromeTab {
      id: number;           // Unique Chrome tab ID
      windowId: number;     // Window ID
      title: string;
      url: string;
      isActive: boolean;
      windowIndex: number;  // For backward compatibility
      tabIndex: number;     // For backward compatibility
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds useful context about the persistence of tab IDs and output format, but does not cover potential limitations such as browser state dependencies, error conditions, or performance implications. It adequately describes core behavior but lacks depth on operational constraints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, followed by essential details about tab IDs and output format. Every sentence earns its place by providing critical information without redundancy, making it efficiently structured and appropriately sized for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's low complexity (0 parameters, no output schema, no annotations), the description is largely complete. It covers purpose, output details, and usage guidance. However, it could slightly improve by mentioning any prerequisites (e.g., browser must be open) or error handling, which holds it back from a perfect score.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 0 parameters with 100% coverage, so no parameter documentation is needed. The description appropriately focuses on output semantics, explaining the structure of tab data (display format and Tab ID) and usage guidance for the IDs. This adds value beyond the empty schema, warranting a score above the baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verb ('Get') and resource ('all open tabs from Google Chrome browser'), and distinguishes it from siblings by focusing on retrieval rather than activation or closure. It specifies the scope ('all open tabs') and key output details (unique tab IDs, display format).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for usage by emphasizing that Tab IDs should be used for reliable operations, which implicitly guides when to use this tool (to obtain IDs for sibling tools like activate_tab_by_id). However, it does not explicitly state when not to use it or name alternatives, keeping it from a perfect score.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/kazuph/mcp-browser-tabs'

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