Skip to main content
Glama
yashpreetbathla

MCP Accessibility Bridge

Get Interactive Elements

get_interactive_elements

Identify interactive page elements like buttons and inputs, providing accessibility details and test selectors for automated testing and screen reader compatibility.

Instructions

Find all interactive elements on the page (buttons, inputs, links, etc.) and return their accessibility info plus multi-framework test selectors. Covers 20 interactive ARIA roles. Use roles[] to filter to specific roles.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
rolesNoSpecific ARIA roles to include. Defaults to all 20 interactive roles: button, link, textbox, searchbox, combobox, listbox, option, checkbox, radio, switch, slider, spinbutton, menuitem, tab, treeitem, gridcell, rowheader, columnheader, progressbar, scrollbar
includeDisabledNoInclude disabled elements. Default: false
maxElementsNoMaximum number of elements to return. Default: 100

Implementation Reference

  • Main handler function getInteractiveElementsHandler that retrieves interactive elements from the page. It fetches the full accessibility tree, filters by interactive roles (button, link, textbox, etc.), optionally includes disabled elements, resolves DOM info in parallel, and returns element details with accessibility properties and suggested selectors.
    export async function getInteractiveElementsHandler(args: {
      roles?: string[];
      includeDisabled?: boolean;
      maxElements?: number;
    }): Promise<ReturnType<typeof toolSuccess | typeof toolError>> {
      try {
        const { cdpSession } = browserManager.requireConnection();
    
        const targetRoles = args.roles
          ? new Set(args.roles.map((r) => r.toLowerCase()))
          : INTERACTIVE_ROLES;
    
        const includeDisabled = args.includeDisabled ?? false;
        const maxElements = args.maxElements ?? 100;
    
        // Get full AX tree
        const result = await cdpSession.send(
          'Accessibility.getFullAXTree',
          {}
        ) as GetFullAXTreeResponse;
    
        const allNodes: CdpAXNode[] = result.nodes;
    
        // Filter to interactive roles
        const interactiveNodes = allNodes.filter((node) => {
          if (node.ignored) return false;
    
          const role = (node.role?.value as string ?? '').toLowerCase();
          if (!targetRoles.has(role)) return false;
    
          // Skip disabled unless requested
          if (!includeDisabled) {
            const props = node.properties ?? [];
            const disabledProp = props.find((p) => p.name === 'disabled');
            if (disabledProp?.value?.value === true) return false;
          }
    
          return true;
        });
    
        const limited = interactiveNodes.slice(0, maxElements);
    
        // Resolve DOM info for each node in parallel
        const elementResults = await Promise.all(
          limited.map(async (node): Promise<ElementResult> => {
            const axSummary = cdpAXNodeToSummary(node);
            const role = (node.role?.value as string) ?? '';
            const name = (node.name?.value as string) ?? '';
    
            const base: ElementResult = {
              role,
              name,
              nodeId: node.nodeId,
              backendDOMNodeId: node.backendDOMNodeId,
              axProperties: axSummary,
            };
    
            if (!node.backendDOMNodeId) {
              return {
                ...base,
                suggestedSelectors: buildSelectorFromRawNode(name, role, 'unknown', undefined),
              };
            }
    
            try {
              const describeResult = await cdpSession.send('DOM.describeNode', {
                backendNodeId: node.backendDOMNodeId,
                depth: 0,
              }) as DomDescribeNodeResponse;
    
              const domNode = describeResult.node;
              const tagName = domNode.localName;
              const rawAttrs = domNode.attributes;
              const domAttributes = parseAttributes(rawAttrs);
    
              return {
                ...base,
                tagName,
                domAttributes,
                suggestedSelectors: buildSelectorFromRawNode(name, role, tagName, rawAttrs),
              };
            } catch {
              // DOM.describeNode can fail for some nodes (e.g. detached)
              return {
                ...base,
                suggestedSelectors: buildSelectorFromRawNode(name, role, 'unknown', undefined),
              };
            }
          })
        );
    
        return toolSuccess({
          totalFound: interactiveNodes.length,
          returned: elementResults.length,
          maxElements,
          roles: [...targetRoles],
          elements: elementResults,
        });
      } catch (error) {
        return toolError(error);
      }
    }
  • Zod schema definition getInteractiveElementsSchema validating input parameters: roles (array of ARIA roles), includeDisabled (boolean), and maxElements (positive integer). Defaults to all 20 interactive roles, excludes disabled elements, and max 100 elements.
    export const getInteractiveElementsSchema = {
      roles: z
        .array(z.string())
        .optional()
        .describe(
          'Specific ARIA roles to include. Defaults to all 20 interactive roles: ' +
          'button, link, textbox, searchbox, combobox, listbox, option, checkbox, radio, ' +
          'switch, slider, spinbutton, menuitem, tab, treeitem, gridcell, rowheader, ' +
          'columnheader, progressbar, scrollbar'
        ),
      includeDisabled: z
        .boolean()
        .optional()
        .default(false)
        .describe('Include disabled elements. Default: false'),
      maxElements: z
        .number()
        .int()
        .positive()
        .optional()
        .default(100)
        .describe('Maximum number of elements to return. Default: 100'),
    };
  • src/index.ts:104-116 (registration)
    Tool registration of 'get_interactive_elements' with title, description explaining it finds interactive elements and returns accessibility info plus multi-framework test selectors, and references the schema and handler.
    // ── get_interactive_elements ─────────────────────────────────────────────────
    server.registerTool(
      'get_interactive_elements',
      {
        title: 'Get Interactive Elements',
        description:
          'Find all interactive elements on the page (buttons, inputs, links, etc.) ' +
          'and return their accessibility info plus multi-framework test selectors. ' +
          'Covers 20 interactive ARIA roles. Use roles[] to filter to specific roles.',
        inputSchema: getInteractiveElementsSchema,
      },
      getInteractiveElementsHandler
    );
  • INTERACTIVE_ROLES constant containing Set of 20 ARIA role strings (button, link, textbox, searchbox, combobox, listbox, option, checkbox, radio, switch, slider, spinbutton, menuitem, tab, treeitem, gridcell, rowheader, columnheader, progressbar, scrollbar) used for filtering nodes.
    const INTERACTIVE_ROLES = new Set([
      'button',
      'link',
      'textbox',
      'searchbox',
      'combobox',
      'listbox',
      'option',
      'checkbox',
      'radio',
      'switch',
      'slider',
      'spinbutton',
      'menuitem',
      'tab',
      'treeitem',
      'gridcell',
      'rowheader',
      'columnheader',
      'progressbar',
      'scrollbar',
    ]);
  • parseAttributes helper function that converts DOM attributes from alternating string array format (['attr1', 'value1', 'attr2', 'value2']) into a key-value Record object for easier access.
    function parseAttributes(attrs: string[] | undefined): Record<string, string> {
      const map: Record<string, string> = {};
      if (!attrs) return map;
      for (let i = 0; i + 1 < attrs.length; i += 2) {
        map[attrs[i]] = attrs[i + 1];
      }
      return map;
    }
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 of behavioral disclosure. It effectively describes what the tool does (finds interactive elements and returns accessibility info with test selectors), covers 20 ARIA roles, and mentions filtering capability. However, it doesn't disclose performance characteristics, rate limits, or error handling, which would be helpful for a tool that might return up to 100 elements.

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 two sentences that are front-loaded with the core purpose and efficiently convey key details (coverage of 20 roles, filtering capability). Every sentence earns its place with no wasted words or redundancy.

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 moderate complexity (3 parameters, no output schema, no annotations), the description is fairly complete. It explains what the tool does, what it returns, and how to filter results. However, without an output schema, it could benefit from more detail on the return format (e.g., structure of accessibility info and test selectors) to fully guide the agent.

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 documents all three parameters thoroughly. The description adds value by mentioning 'Use roles[] to filter to specific roles', which clarifies the purpose of the roles parameter beyond the schema's list of default roles. However, it doesn't provide additional context for includeDisabled or maxElements beyond what the schema states.

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 verb 'find' and resource 'interactive elements on the page', specifying the types (buttons, inputs, links, etc.) and what information is returned (accessibility info plus multi-framework test selectors). It distinguishes from sibling tools like get_accessibility_tree by focusing specifically on interactive elements rather than the full accessibility tree.

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

Usage Guidelines3/5

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

The description implies usage by mentioning 'Covers 20 interactive ARIA roles' and 'Use roles[] to filter to specific roles', which suggests when to use this tool for interactive element analysis. However, it doesn't explicitly state when to choose this over alternatives like get_accessibility_tree or query_accessibility_tree, nor does it provide exclusion criteria or prerequisites.

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