Skip to main content
Glama
yashpreetbathla

MCP Accessibility Bridge

Get Accessibility Tree

get_accessibility_tree

Capture a page's accessibility tree to analyze ARIA roles, names, and properties for testing and audits.

Instructions

Capture a snapshot of the current page's accessibility tree. Returns a hierarchical tree of ARIA roles, names, and properties. Use interestingOnly=false for the complete raw tree. Use useFullTree=true for the CDP-level complete tree (slower but more accurate). Use maxDepth to control how deep the tree goes.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
interestingOnlyNoIf true, prunes nodes that are not interesting (hidden, presentational). Set false to get the raw full tree. Default: true
maxDepthNoMaximum tree depth to return. Default: 10
useFullTreeNoIf true, uses CDP Accessibility.getFullAXTree (more complete but slower). If false, uses page.accessibility.snapshot(). Default: false

Implementation Reference

  • Main handler function getAccessibilityTreeHandler that executes the tool logic. It captures the accessibility tree from the current page using either CDP's getFullAXTree or Puppeteer's accessibility.snapshot, filters based on interestingOnly, prunes to maxDepth, and returns the tree with metadata (url, title, nodeCount).
    export async function getAccessibilityTreeHandler(args: {
      interestingOnly?: boolean;
      maxDepth?: number;
      useFullTree?: boolean;
    }): Promise<ReturnType<typeof toolSuccess | typeof toolError>> {
      try {
        const { page, cdpSession } = browserManager.requireConnection();
    
        const interestingOnly = args.interestingOnly ?? true;
        const maxDepth = args.maxDepth ?? 10;
        const useFullTree = args.useFullTree ?? false;
    
        let tree: unknown;
        let nodeCount = 0;
    
        if (useFullTree) {
          // Use CDP Accessibility.getFullAXTree for the raw complete tree
          const result = await cdpSession.send(
            'Accessibility.getFullAXTree',
            {}
          ) as GetFullAXTreeResponse;
    
          const nodes: CdpAXNode[] = result.nodes;
          const filtered = interestingOnly
            ? nodes.filter((n) => !n.ignored)
            : nodes;
    
          // Find root node (no parentId or parentId not in set)
          const nodeIds = new Set(filtered.map((n) => n.nodeId));
          const root = filtered.find((n) => !n.parentId || !nodeIds.has(n.parentId));
    
          if (!root) {
            return toolSuccess({
              tree: null,
              nodeCount: 0,
              message: 'No root node found in accessibility tree.',
            });
          }
    
          const index = buildTreeIndex(filtered);
          const assembled = assembleTree(root, index, 0, maxDepth);
          tree = pruneToDepth(assembled, maxDepth);
          nodeCount = countNodes(assembled);
        } else {
          // Use Puppeteer's higher-level accessibility snapshot
          const snapshot = await page.accessibility.snapshot({
            interestingOnly,
          });
    
          if (!snapshot) {
            return toolSuccess({
              tree: null,
              nodeCount: 0,
              message: 'Accessibility snapshot returned null. The page may not have loaded.',
            });
          }
    
          tree = snapshot;
          // Count nodes in the snapshot
          nodeCount = countSnapshotNodes(snapshot as unknown as Record<string, unknown>);
        }
    
        const title = await page.title().catch(() => '');
        const url = page.url();
    
        return toolSuccess({
          url,
          title,
          nodeCount,
          maxDepth,
          interestingOnly,
          tree,
        });
      } catch (error) {
        return toolError(error);
      }
    }
  • Zod schema definition getAccessibilityTreeSchema that defines input parameters: interestingOnly (boolean, default true) to filter uninteresting nodes, maxDepth (number, default 10) for tree depth control, and useFullTree (boolean, default false) to choose between CDP's getFullAXTree or Puppeteer's accessibility.snapshot.
    export const getAccessibilityTreeSchema = {
      interestingOnly: z
        .boolean()
        .optional()
        .default(true)
        .describe(
          'If true, prunes nodes that are not interesting (hidden, presentational). ' +
          'Set false to get the raw full tree. Default: true'
        ),
      maxDepth: z
        .number()
        .int()
        .positive()
        .optional()
        .default(10)
        .describe('Maximum tree depth to return. Default: 10'),
      useFullTree: z
        .boolean()
        .optional()
        .default(false)
        .describe(
          'If true, uses CDP Accessibility.getFullAXTree (more complete but slower). ' +
          'If false, uses page.accessibility.snapshot(). Default: false'
        ),
    };
  • src/index.ts:60-74 (registration)
    Tool registration in src/index.ts where get_accessibility_tree is registered with the MCP server, including title, description, inputSchema, and the handler function reference.
    // ── get_accessibility_tree ───────────────────────────────────────────────────
    server.registerTool(
      'get_accessibility_tree',
      {
        title: 'Get Accessibility Tree',
        description:
          'Capture a snapshot of the current page\'s accessibility tree. ' +
          'Returns a hierarchical tree of ARIA roles, names, and properties. ' +
          'Use interestingOnly=false for the complete raw tree. ' +
          'Use useFullTree=true for the CDP-level complete tree (slower but more accurate). ' +
          'Use maxDepth to control how deep the tree goes.',
        inputSchema: getAccessibilityTreeSchema,
      },
      getAccessibilityTreeHandler
    );
  • Helper function assembleTree that recursively builds an AXNodeSummary tree from a flat list of CDP AX nodes, attaching children up to maxDepth and filtering out ignored nodes.
    export function assembleTree(
      node: CdpAXNode,
      index: Map<string, CdpAXNode>,
      depth: number,
      maxDepth: number,
    ): AXNodeSummary {
      const summary = cdpAXNodeToSummary(node);
    
      if (depth >= maxDepth || !node.childIds || node.childIds.length === 0) {
        return summary;
      }
    
      const children: AXNodeSummary[] = [];
      for (const childId of node.childIds) {
        const child = index.get(childId);
        if (child && !child.ignored) {
          children.push(assembleTree(child, index, depth + 1, maxDepth));
        }
      }
    
      if (children.length > 0) {
        summary.children = children;
      }
    
      return summary;
    }
  • Helper function cdpAXNodeToSummary that converts raw CDP AX node data into a cleaner AXNodeSummary object, extracting role, name, description, value, and various ARIA properties.
    export function cdpAXNodeToSummary(node: CdpAXNode): AXNodeSummary {
      const role = (node.role?.value as string) ?? 'unknown';
      const name = (node.name?.value as string) ?? '';
      const description = node.description?.value as string | undefined;
      const value = node.value?.value as string | undefined;
    
      const props = node.properties ?? [];
      const focused = getPropertyValue(props, 'focused') as boolean | undefined;
      const disabled = getPropertyValue(props, 'disabled') as boolean | undefined;
      const checked = getPropertyValue(props, 'checked') as boolean | 'mixed' | undefined;
      const expanded = getPropertyValue(props, 'expanded') as boolean | undefined;
      const required = getPropertyValue(props, 'required') as boolean | undefined;
      const level = getPropertyValue(props, 'level') as number | undefined;
    
      return {
        role,
        name,
        ...(description !== undefined && { description }),
        ...(value !== undefined && { value }),
        ...(focused !== undefined && { focused }),
        ...(disabled !== undefined && { disabled }),
        ...(checked !== undefined && { checked }),
        ...(expanded !== undefined && { expanded }),
        ...(required !== undefined && { required }),
        ...(level !== undefined && { level }),
        ...(node.backendDOMNodeId !== undefined && { backendDOMNodeId: node.backendDOMNodeId }),
        nodeId: node.nodeId,
      };
    }
Behavior4/5

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

With no annotations provided, the description carries the full burden and does well by disclosing key behavioral traits: it returns a hierarchical tree structure, mentions performance implications ('slower but more accurate' for useFullTree), and describes the effect of parameters on output. It doesn't cover error conditions or permissions, but provides substantial operational context.

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 efficiently structured with three focused sentences: first states the core purpose, second describes the return format, and third provides parameter usage tips. Every sentence adds value with zero waste, and it's front-loaded with the main action.

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 3 parameters with full schema coverage and no output schema, the description provides good context about what the tool returns and how parameters affect behavior. It could be more complete by mentioning typical use cases or limitations, but covers the essential operational aspects adequately for this complexity level.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already fully documents all three parameters. The description adds minimal value by restating parameter purposes in a more conversational tone (e.g., 'Use interestingOnly=false for the complete raw tree'), but doesn't provide additional semantics beyond what's in the schema. Baseline 3 is appropriate.

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 specific action ('Capture a snapshot') and resource ('current page's accessibility tree'), and distinguishes it from sibling 'query_accessibility_tree' by focusing on snapshot capture rather than querying. It explicitly mentions the hierarchical structure and key attributes returned.

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 on when to use specific parameter settings (e.g., 'use interestingOnly=false for the complete raw tree'), but does not explicitly state when to choose this tool over alternatives like 'query_accessibility_tree' or other sibling tools. It offers practical guidance without naming exclusions.

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

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/yashpreetbathla/mcp-accessibility-bridge'

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