Skip to main content
@mcp-b/smart-dom-reader extracts DOM structure optimized for AI/LLM consumption. It provides stable CSS selectors, interactive element maps, and semantic page structure while minimizing token usage. Zero runtime dependencies.
npm: @mcp-b/smart-dom-reader
license: MIT
dependencies: none
optional peer: @modelcontextprotocol/sdk ^1.26.0 (for MCP server)

Installation

npm install @mcp-b/smart-dom-reader

Minimal example

Two extraction approaches

Full extraction (SmartDOMReader)

Single-pass extraction of all elements. Use when you need everything upfront.
import { SmartDOMReader } from '@mcp-b/smart-dom-reader';

// Interactive mode: UI elements only
const interactive = SmartDOMReader.extractInteractive(document);

// Full mode: interactive + semantic elements
const full = SmartDOMReader.extractFull(document);

// From a specific element
const region = SmartDOMReader.extractFromElement(element, 'interactive');
Instance-based usage with options:
const reader = new SmartDOMReader({
  mode: 'interactive',
  mainContentOnly: true,
  viewportOnly: false,
});
const result = reader.extract(document);

Progressive extraction (ProgressiveExtractor)

Step-by-step extraction for token-sensitive AI workflows.
import { ProgressiveExtractor } from '@mcp-b/smart-dom-reader';

// Step 1: Page structure overview (minimal tokens)
const structure = ProgressiveExtractor.extractStructure(document);

// Step 2: Extract a specific region
const region = ProgressiveExtractor.extractRegion(
  structure.summary.mainContentSelector,
  document,
  { mode: 'interactive' }
);

// Step 3: Extract readable content
const content = ProgressiveExtractor.extractContent(
  'article.main-article',
  document,
  { includeHeadings: true, includeLists: true }
);
extractStructure accepts a Document or an Element to scope the extraction.

Extraction modes

ModeIncludes
interactiveButtons, links, form inputs, clickable elements, form structures
fullEverything in interactive plus headings, images, tables, lists, articles, metadata

Options

OptionTypeDefaultDescription
mode'interactive' | 'full''interactive'Extraction mode
maxDepthnumber5Maximum DOM traversal depth
includeHiddenbooleanfalseInclude hidden elements
includeShadowDOMbooleantrueTraverse shadow DOM roots
includeIframesbooleanfalseTraverse iframes
viewportOnlybooleanfalseOnly extract elements in the visible viewport
mainContentOnlybooleanfalseFocus on the detected main content area
customSelectorsstring[][]Additional CSS selectors to extract

Output structure

SmartDOMResult

interface SmartDOMResult {
  mode: 'interactive' | 'full';
  timestamp: number;

  page: {
    url: string;
    title: string;
    hasErrors: boolean;
    isLoading: boolean;
    hasModals: boolean;
    hasFocus?: string;
  };

  landmarks: {
    navigation: string[];
    main: string[];
    forms: string[];
    headers: string[];
    footers: string[];
    articles: string[];
    sections: string[];
  };

  interactive: {
    buttons: ExtractedElement[];
    links: ExtractedElement[];
    inputs: ExtractedElement[];
    forms: FormInfo[];
    clickable: ExtractedElement[];
  };

  semantic?: {           // full mode only
    headings: ExtractedElement[];
    images: ExtractedElement[];
    tables: ExtractedElement[];
    lists: ExtractedElement[];
    articles: ExtractedElement[];
  };

  metadata?: {           // full mode only
    totalElements: number;
    extractedElements: number;
    mainContent?: string;
    language?: string;
  };
}

ExtractedElement

interface ExtractedElement {
  tag: string;
  text: string;

  selector: {
    css: string;
    xpath: string;
    textBased?: string;
    dataTestId?: string;
    ariaLabel?: string;
    candidates?: Array<{
      type: 'id' | 'data-testid' | 'role-aria' | 'name' | 'class-path' | 'css-path' | 'xpath' | 'text';
      value: string;
      score: number;
    }>;
  };

  attributes: Record<string, string>;

  context: {
    nearestForm?: string;
    nearestSection?: string;
    nearestMain?: string;
    nearestNav?: string;
    parentChain: string[];
  };

  interaction: {
    click?: boolean;
    change?: boolean;
    submit?: boolean;
    nav?: boolean;
    disabled?: boolean;
    hidden?: boolean;
    role?: string;
    form?: string;
  };
}
Interaction flags are only present when true, saving tokens.

Selector ranking

Selectors are ranked by stability (higher score = more reliable):
StrategyScoreExample
ID100#unique-id
data-testid90[data-testid="submit"]
ARIA (role + label)80[role="button"][aria-label="Submit"]
Name/ID attributes70input[name="email"]
Class paths50.form-container .submit-btn

Exported modules

ExportKindDescription
SmartDOMReaderClassFull single-pass extraction
ProgressiveExtractorClassStep-by-step extraction
SelectorGeneratorClassCSS/XPath selector generation with ranking
ContentDetectionClassLandmark detection and main content identification
MarkdownFormatterClassFormat extraction results as Markdown
MarkdownFormatOptionsTypeOptions for Markdown formatting
SmartDOMResultTypeFull extraction result shape
ExtractedElementTypeSingle extracted element
ExtractionOptionsTypeOptions accepted by the reader
ExtractionModeType'interactive' | 'full'
FormInfoTypeForm metadata (selector, inputs, buttons, action, method)
PageStateTypeCurrent page state (URL, title, loading, errors)
PageLandmarksTypeDetected page landmarks

Bundle string export

A self-contained IIFE bundle is available for injection into pages (e.g. via chrome.userScripts.execute):
import { SMART_DOM_READER_BUNDLE } from '@mcp-b/smart-dom-reader/bundle-string';

const code = `(() => {
  ${SMART_DOM_READER_BUNDLE}
  return SmartDOMReaderBundle.executeExtraction('extractStructure', {
    selector: undefined,
    formatOptions: { detail: 'summary' }
  });
})()`;
The bundle contains guarded fallbacks for non-browser environments and has no runtime imports.

MCP server

An optional MCP server returns XML-wrapped Markdown. Output format:
<page title="..." url="...">
  <outline><![CDATA[ ...markdown... ]]></outline>
</page>
Server tools:
ToolParameters
browser_connectheadless?, executablePath?
browser_navigateurl
dom_extract_structureselector?, detail?, maxTextLength?, maxElements?
dom_extract_regionselector, options?
dom_extract_contentselector, options?
dom_extract_interactiveselector?, options?
browser_screenshotpath?, fullPage?
browser_close(none)
Golden path sequence:
  1. dom_extract_structure to get the page outline
  2. dom_extract_region to get selectors for a target area
  3. Write a script using those selectors
  4. Optionally dom_extract_content for readable text