Skip to main content
Glama

runAccessibilityAudit

Audit webpage accessibility to identify and address compliance issues for improved user experience.

Instructions

Run an accessibility audit on the current page

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Core handler function that runs Lighthouse accessibility audit and extracts AI-optimized data
    export async function runAccessibilityAudit(
      url: string
    ): Promise<AIOptimizedAccessibilityReport> {
      try {
        const lhr = await runLighthouseAudit(url, [AuditCategory.ACCESSIBILITY]);
        return extractAIOptimizedData(lhr, url);
      } catch (error) {
        throw new Error(
          `Accessibility audit failed: ${
            error instanceof Error ? error.message : String(error)
          }`
        );
      }
    }
  • Supporting handler function that processes raw Lighthouse results into structured AI-optimized accessibility report with issues, scores, and recommendations
    const extractAIOptimizedData = (
      lhr: LighthouseResult,
      url: string
    ): AIOptimizedAccessibilityReport => {
      const categoryData = lhr.categories[AuditCategory.ACCESSIBILITY];
      const audits = lhr.audits || {};
    
      // Add metadata
      const metadata = {
        url,
        timestamp: lhr.fetchTime || new Date().toISOString(),
        device: "desktop", // This could be made configurable
        lighthouseVersion: lhr.lighthouseVersion,
      };
    
      // Initialize variables
      const issues: AIAccessibilityIssue[] = [];
      const criticalElements: AIAccessibilityElement[] = [];
      const categories: {
        [category: string]: { score: number; issues_count: number };
      } = {};
    
      // Count audits by type
      let failedCount = 0;
      let passedCount = 0;
      let manualCount = 0;
      let informativeCount = 0;
      let notApplicableCount = 0;
    
      // Process audit refs
      const auditRefs = categoryData?.auditRefs || [];
    
      // First pass: count audits by type and initialize categories
      auditRefs.forEach((ref) => {
        const audit = audits[ref.id];
        if (!audit) return;
    
        // Count by scoreDisplayMode
        if (audit.scoreDisplayMode === "manual") {
          manualCount++;
        } else if (audit.scoreDisplayMode === "informative") {
          informativeCount++;
        } else if (audit.scoreDisplayMode === "notApplicable") {
          notApplicableCount++;
        } else if (audit.score !== null) {
          // Binary pass/fail
          if (audit.score >= 0.9) {
            passedCount++;
          } else {
            failedCount++;
          }
        }
    
        // Process categories
        if (ref.group) {
          // Initialize category if not exists
          if (!categories[ref.group]) {
            categories[ref.group] = { score: 0, issues_count: 0 };
          }
    
          // Update category score and issues count
          if (audit.score !== null && audit.score < 0.9) {
            categories[ref.group].issues_count++;
          }
        }
      });
    
      // Second pass: process failed audits into AI-friendly format
      auditRefs
        .filter((ref) => {
          const audit = audits[ref.id];
          return audit && audit.score !== null && audit.score < 0.9;
        })
        .sort((a, b) => (b.weight || 0) - (a.weight || 0))
        // No limit on number of failed audits - we'll show them all
        .forEach((ref) => {
          const audit = audits[ref.id];
    
          // Determine impact level based on score and weight
          let impact: "critical" | "serious" | "moderate" | "minor" = "moderate";
          if (audit.score === 0) {
            impact = "critical";
          } else if (audit.score !== null && audit.score <= 0.5) {
            impact = "serious";
          } else if (audit.score !== null && audit.score > 0.7) {
            impact = "minor";
          }
    
          // Create elements array
          const elements: AIAccessibilityElement[] = [];
    
          if (audit.details) {
            const details = audit.details as any;
            if (details.items && Array.isArray(details.items)) {
              const items = details.items;
              // Apply limits based on impact level
              const itemLimit = DETAIL_LIMITS[impact];
              items.slice(0, itemLimit).forEach((item: any) => {
                if (item.node) {
                  const element: AIAccessibilityElement = {
                    selector: item.node.selector,
                    snippet: item.node.snippet,
                    label: item.node.nodeLabel,
                    issue_description: item.node.explanation || item.explanation,
                  };
    
                  if (item.value !== undefined) {
                    element.value = item.value;
                  }
    
                  elements.push(element);
    
                  // Add to critical elements if impact is critical or serious
                  if (impact === "critical" || impact === "serious") {
                    criticalElements.push(element);
                  }
                }
              });
            }
          }
    
          // Create the issue
          const issue: AIAccessibilityIssue = {
            id: ref.id,
            title: audit.title,
            impact,
            category: ref.group || "other",
            elements: elements.length > 0 ? elements : undefined,
            score: audit.score,
          };
    
          issues.push(issue);
        });
    
      // Calculate overall score
      const score = Math.round((categoryData?.score || 0) * 100);
    
      // Generate prioritized recommendations
      const prioritized_recommendations: string[] = [];
    
      // Add category-specific recommendations
      Object.entries(categories)
        .filter(([_, data]) => data.issues_count > 0)
        .sort(([_, a], [__, b]) => b.issues_count - a.issues_count)
        .forEach(([category, data]) => {
          let recommendation = "";
    
          switch (category) {
            case "a11y-color-contrast":
              recommendation = "Improve color contrast for better readability";
              break;
            case "a11y-names-labels":
              recommendation = "Add proper labels to all interactive elements";
              break;
            case "a11y-aria":
              recommendation = "Fix ARIA attributes and roles";
              break;
            case "a11y-navigation":
              recommendation = "Improve keyboard navigation and focus management";
              break;
            case "a11y-language":
              recommendation = "Add proper language attributes to HTML";
              break;
            case "a11y-tables-lists":
              recommendation = "Fix table and list structures for screen readers";
              break;
            default:
              recommendation = `Fix ${data.issues_count} issues in ${category}`;
          }
    
          prioritized_recommendations.push(recommendation);
        });
    
      // Add specific high-impact recommendations
      if (issues.some((issue) => issue.id === "color-contrast")) {
        prioritized_recommendations.push(
          "Fix low contrast text for better readability"
        );
      }
    
      if (issues.some((issue) => issue.id === "document-title")) {
        prioritized_recommendations.push("Add a descriptive page title");
      }
    
      if (issues.some((issue) => issue.id === "image-alt")) {
        prioritized_recommendations.push("Add alt text to all images");
      }
    
      // Create the report content
      const reportContent: AccessibilityReportContent = {
        score,
        audit_counts: {
          failed: failedCount,
          passed: passedCount,
          manual: manualCount,
          informative: informativeCount,
          not_applicable: notApplicableCount,
        },
        issues,
        categories,
        critical_elements: criticalElements,
        prioritized_recommendations:
          prioritized_recommendations.length > 0
            ? prioritized_recommendations
            : undefined,
      };
    
      // Return the full report following the LighthouseReport interface
      return {
        metadata,
        report: reportContent,
      };
    };
  • MCP tool registration and proxy handler that forwards to browser-tools-server /accessibility-audit endpoint
    server.tool(
      "runAccessibilityAudit",
      "Run an accessibility audit on the current page",
      {},
      async () => {
        return await withServerConnection(async () => {
          try {
            // Simplified approach - let the browser connector handle the current tab and URL
            console.log(
              `Sending POST request to http://${discoveredHost}:${discoveredPort}/accessibility-audit`
            );
            const response = await fetch(
              `http://${discoveredHost}:${discoveredPort}/accessibility-audit`,
              {
                method: "POST",
                headers: {
                  "Content-Type": "application/json",
                  Accept: "application/json",
                },
                body: JSON.stringify({
                  category: AuditCategory.ACCESSIBILITY,
                  source: "mcp_tool",
                  timestamp: Date.now(),
                }),
              }
            );
    
            // Log the response status
            console.log(`Accessibility audit response status: ${response.status}`);
    
            if (!response.ok) {
              const errorText = await response.text();
              console.error(`Accessibility audit error: ${errorText}`);
              throw new Error(`Server returned ${response.status}: ${errorText}`);
            }
    
            const json = await response.json();
    
            // flatten it by merging metadata with the report contents
            if (json.report) {
              const { metadata, report } = json;
              const flattened = {
                ...metadata,
                ...report,
              };
    
              return {
                content: [
                  {
                    type: "text",
                    text: JSON.stringify(flattened, null, 2),
                  },
                ],
              };
            } else {
              // Return as-is if it's not in the new format
              return {
                content: [
                  {
                    type: "text",
                    text: JSON.stringify(json, null, 2),
                  },
                ],
              };
            }
          } catch (error) {
            const errorMessage =
              error instanceof Error ? error.message : String(error);
            console.error("Error in accessibility audit:", errorMessage);
            return {
              content: [
                {
                  type: "text",
                  text: `Failed to run accessibility audit: ${errorMessage}`,
                },
              ],
            };
          }
        });
      }
    );
  • HTTP endpoint registration for /accessibility-audit that invokes the lighthouse runAccessibilityAudit function
    private setupAccessibilityAudit() {
      this.setupAuditEndpoint(
        AuditCategory.ACCESSIBILITY,
        "/accessibility-audit",
        runAccessibilityAudit
      );
  • Input/output schema and type definitions for accessibility reports and issues
    export interface AccessibilityReportContent {
      score: number; // Overall score (0-100)
      audit_counts: {
        // Counts of different audit types
        failed: number;
        passed: number;
        manual: number;
        informative: number;
        not_applicable: number;
      };
      issues: AIAccessibilityIssue[];
      categories: {
        [category: string]: {
          score: number;
          issues_count: number;
        };
      };
      critical_elements: AIAccessibilityElement[];
      prioritized_recommendations?: string[]; // Ordered list of recommendations
    }
    
    /**
     * Full accessibility report implementing the base LighthouseReport interface
     */
    export type AIOptimizedAccessibilityReport =
      LighthouseReport<AccessibilityReportContent>;
    
    /**
     * AI-optimized accessibility issue
     */
    interface AIAccessibilityIssue {
      id: string; // e.g., "color-contrast"
      title: string; // e.g., "Color contrast is sufficient"
      impact: "critical" | "serious" | "moderate" | "minor";
      category: string; // e.g., "contrast", "aria", "forms", "keyboard"
      elements?: AIAccessibilityElement[]; // Elements with issues
      score: number | null; // 0-1 or null
    }
    
    /**
     * Accessibility element with issues
     */
    interface AIAccessibilityElement {
      selector: string; // CSS selector
      snippet?: string; // HTML snippet
      label?: string; // Element label
      issue_description?: string; // Description of the issue
      value?: string | number; // Current value (e.g., contrast ratio)
    }
Behavior2/5

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

No annotations are provided, so the description carries full burden. While 'Run an accessibility audit' implies analysis/read operations, it doesn't disclose what the audit actually does - whether it modifies the page, requires specific page states, has side effects, or what kind of output to expect. The description is too minimal to understand the tool's behavior beyond its basic purpose.

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 extremely concise - a single sentence that directly states what the tool does. There's zero waste or unnecessary elaboration. It's front-loaded with the core action and target, making it immediately understandable at minimal cognitive cost.

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

Completeness2/5

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

For a tool with no annotations, no output schema, and multiple sibling audit tools, the description is insufficiently complete. It doesn't explain what an 'accessibility audit' entails, what results to expect, how it differs from other audit tools, or any behavioral characteristics. The agent would need to guess about the tool's output format and appropriate usage context.

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?

With 0 parameters and 100% schema description coverage, the baseline is 4. The description appropriately doesn't discuss parameters since there are none, and the schema already fully documents the empty parameter set. No additional parameter information is needed or provided.

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

Purpose4/5

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

The description clearly states the action ('Run an accessibility audit') and target ('on the current page'), providing a specific verb+resource combination. However, it doesn't distinguish this tool from sibling audit tools like 'runBestPracticesAudit' or 'runSEOAudit' - it only specifies the audit type but not how it differs from other audit tools.

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

Usage Guidelines2/5

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

The description provides no guidance about when to use this tool versus alternatives. With multiple audit tools available (accessibility, best practices, SEO, performance, NextJS), there's no indication of what makes accessibility audits distinct or when they're appropriate versus other audit types. No prerequisites, exclusions, or alternative recommendations are mentioned.

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/Sugatraj/Cursor-Browser-Tools-MCP'

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