Skip to main content
Glama

haden-browser

haden-browser는 로컬 Model Context Protocol 서버로 노출되는 빠르고 AI 우선(AI-first) 웹 브라우저입니다. 매 턴마다 스크린샷과 원시 페이지 소스를 보내는 대신, AI에게 현재 목표에 가장 유용한 최소한의 페이지 표현을 제공합니다.

벤치마크: AI 브라우저 컨텍스트 33% 감소

주요 결과: Haden은 핵심 브라우징 세션에서 7,822 참조 토큰을 사용했습니다—Playwright MCP보다 32.7% 적고 Chrome DevTools MCP 전체 모드보다 32.5% 적습니다. 시맨틱 액션 refs와 이미지 alt 커버리지를 반환한 테스트된 브라우저 MCP 중에서 Haden이 가장 낮은 토큰 사용량을 기록했습니다.

토큰 사용량, 지연 시간, 원본 전송량을 보여주는 브라우저 MCP 벤치마크 개요

5회 실행 중앙값

Haden

Playwright MCP

Chrome Slim†

Chrome Full

핵심 컨텍스트 토큰

7,822

11,627

4,638

11,596

도구 스키마 토큰

727

3,999

221

4,974

문서 워크플로 토큰

2,732

4,640

2,497

4,388

첫 유용한 문서

1,044 ms

2,714 ms

3,005 ms

3,133 ms

문서 원본 전송량

16.7 KB / 1 request

95.0 KB / 10

95.0 KB / 10

95.0 KB / 10

시맨틱 액션 refs

15

172

0

143

이미지 alt 커버리지

8/8

8/8

0/8

8/8

† Chrome Slim은 절대적인 최소 토큰이지만, 저수준 JavaScript 제어 기준선입니다. 모델이 페이지 스크립트를 직접 작성해야 하며, 테스트된 경로는 시맨틱 액션 refs나 이미지 alt 텍스트를 반환하지 않았습니다. 따라서 투명하게 표시되지만 시맨틱 브라우저와 동등한 것으로 취급되지는 않습니다.

핵심 컨텍스트에는 도구 스키마 전체 로드 1회와 문서 읽기, 폼 제출, 계산된 스타일 검사에 대한 직렬화된 MCP 도구 호출 및 결과가 포함됩니다. 카운트는 o200k_base 토크나이저를 사용합니다. 이미지 픽셀, 사용자/시스템 프롬프트, 모델 추론 또는 산문은 제외되므로 실제 청구 사용량은 MCP 호스트와 모델에 따라 달라집니다.

전체 벤치마크 보고서, 실행별 원시 데이터, 재현 방법을 참조하세요.

Related MCP server: Charlotte

왜 다른가

  • 목표 기반 뷰 — 연구에는 간결한 시맨틱 텍스트, 구조에는 정화된 DOM, 시각적 작업에는 제한된 계산된 스타일을 사용합니다.

  • 이미지는 지연 로드 유지 — 이미지는 alt 텍스트 또는 [image img1]로 표현됩니다. 픽셀 데이터는 AI가 명시적으로 haden_image를 호출할 때만 반환됩니다.

  • 낮은 왕복 제어 — 모든 상호작용이 새로운 스냅샷을 반환하므로 대부분의 클릭/입력 흐름은 단계당 도구 호출 한 번만 필요합니다.

  • 안정적인 액션 참조 — 표시되는 컨트롤은 e1, e2 등이 되며, 모델이 CSS 선택자를 직접 만들도록 요청하는 것보다 저렴하고 안정적입니다.

  • 기본적으로 빠름 — 일반 브라우징 중에는 이미지, 미디어, 글꼴 요청이 차단됩니다. CSS는 visual 뷰에서 계속 사용할 수 있습니다.

  • 더 안전한 로컬 실행 — 사용자가 명시적으로 옵트인하지 않는 한 비HTTP 스킴, 자격 증명이 포함된 URL, 사설 네트워크 대상이 차단됩니다.

flowchart LR
    AI[AI / MCP host] -->|stdio tools| MCP[haden-browser]
    MCP --> PW[Playwright Chromium]
    PW --> PAGE[Web page]
    PAGE -->|semantic text / DOM / computed CSS| MCP
    MCP -.->|only haden_image imgN| PIXELS[Image bytes]
    PIXELS -.-> AI

뷰

뷰

적합한 작업

반환 데이터

text

검색, 읽기, 폼 흐름

제목, 표시 텍스트, e refs가 있는 링크와 컨트롤, 이미지 플레이스홀더

dom

HTML 구조 및 속성

정화된 HTML; 스크립트와 임베디드 콘텐츠 제거됨; <img>는 <haden-image>로 대체됨

visual

레이아웃 및 CSS 추론

박스와 제한된 계산된 CSS 속성 집합을 포함한 표시되는 의미 있는 요소

text가 기본값이며, 작업에 구조나 표현 세부 정보가 정말로 필요하지 않은 한 text를 우선해야 합니다.

MCP 도구

도구

용도

haden_open

HTTP(S) URL을 열고 첫 스냅샷을 반환합니다

haden_read

현재 페이지를 읽습니다. 선택적으로 CSS 선택자 범위 내에서 읽습니다

haden_interact

클릭, 입력, 선택, 키 입력, 스크롤, 기록 탐색, 새로고침 또는 대기를 수행한 후 업데이트된 스냅샷을 반환합니다

haden_image

최신 스냅샷의 img ref 하나에 대한 실제 이미지 픽셀을 반환합니다

haden_status

Chromium을 시작하지 않고 활성 페이지를 보고합니다

텍스트 출력 예시:

# Example Domain
URL: https://example.com/
View: text
Interactive elements: 1
Images: 1 (use haden_image with an img ref only when visual inspection is needed)

# Example Domain
This domain is for use in illustrative examples.
[e1] link "More information" -> https://iana.org/domains/example
[image img1: "Example diagram"]

참조는 최신 스냅샷으로 범위가 제한됩니다. 페이지가 크게 변경된 후에는 이전 e 또는 img ref를 재사용하기 전에 다시 읽으세요.

요구 사항

  • Node.js 20 이상

  • Playwright를 통해 설치된 Chromium

이 저장소에서 설치

git clone https://github.com/Haden-Min/haden-browser.git
cd haden-browser
npm ci
npm run setup-browser
npm run build

그런 다음 MCP 호스트가 빌드된 stdio 서버를 실행하도록 구성하세요. 실제 구성에서는 절대 경로를 사용하세요:

{
  "mcpServers": {
    "haden-browser": {
      "command": "node",
      "args": ["/absolute/path/to/haden-browser/dist/index.js"]
    }
  }
}

개발 중에는 호스트가 npx tsx /absolute/path/to/haden-browser/src/index.ts를 대신 실행할 수 있습니다.

패키지가 npm에 게시된 후 의도된 구성은 다음과 같습니다:

{
  "mcpServers": {
    "haden-browser": {
      "command": "npx",
      "args": ["-y", "haden-browser"]
    }
  }
}

해당 머신에서 첫 브라우저 호출 전에 npx playwright install chromium을 한 번 실행하세요.

구성

환경 변수

기본값

의미

HADEN_HEADLESS

true

보이는 창 없이 Chromium 실행

HADEN_BLOCK_HEAVY_ASSETS

true

일반 브라우징 중 이미지, 미디어, 글꼴 차단

HADEN_ALLOW_PRIVATE_HOSTS

false

localhost 및 사설 네트워크 대상 허용

HADEN_NAVIGATION_TIMEOUT_MS

20000

탐색 및 이미지 가져오기 제한 시간

HADEN_ACTION_TIMEOUT_MS

8000

클릭, 입력, 로케이터 제한 시간

HADEN_MAX_CHARS

30000

기본 스냅샷 문자 수 제한

HADEN_MAX_IMAGE_BYTES

5242880

haden_image가 반환하는 최대 바이트 수

불리언 변수는 true/false, 1/0, yes/no, on/off를 허용합니다.

로컬 개발 페이지

SSRF 위험을 줄이기 위해 사설 호스트는 기본적으로 차단됩니다. localhost에서 애플리케이션을 탐색하려면 해당 MCP 프로세스에 대해서만 옵트인하세요:

{
  "mcpServers": {
    "haden-browser-local": {
      "command": "node",
      "args": ["/absolute/path/to/haden-browser/dist/index.js"],
      "env": { "HADEN_ALLOW_PRIVATE_HOSTS": "true" }
    }
  }
}

개발

npm install
npm run setup-browser
npm run check
npm run build

테스트 스위트는 네트워크 정책, 텍스트 셰이핑, 실제 Chromium 추출, ref 기반 상호작용, 지연 이미지 검색, 인메모리 MCP 클라이언트/서버 연결을 다룹니다.

현재 제한 사항

  • stdio MCP 연결당 하나의 활성 브라우저 컨텍스트가 유지됩니다.

  • 이미지 refs는 현재 표시되는 HTML <img> 요소만 다루며, CSS 배경 이미지, 캔버스, 비디오 프레임, iframe 콘텐츠는 다루지 않습니다.

  • visual 뷰는 계산된 속성을 보고하며, 모든 스타일시트 규칙을 덤프하지는 않습니다.

  • 다운로드, 파일 업로드, 브라우저 확장 프로그램, 영구 프로필, CAPTCHA 해결은 MVP 범위 밖입니다.

  • 사설 호스트 필터링은 SSRF 노출을 줄이지만, 적대적인 멀티테넌트 환경에서 OS/컨테이너 수준 네트워크 격리를 대체하지는 않습니다.

개발 지침은 CONTRIBUTING.md를, 취약점 신고는 SECURITY.md를 참조하세요.

라이선스

MIT

Available Tools

5 tools
haden_imageInspect imageA
Read-only

Return the actual pixels for one img ref from the latest snapshot. Use only after alt text/placeholders are insufficient for the goal.

NameRequiredDescriptionDefault
refYes

TDQS

A4/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, openWorldHint=true, and destructiveHint=false, covering safety. The description adds context about the data source ('latest snapshot') and output nature ('actual pixels'), but doesn't detail return format or potential edge cases, which is acceptable given annotation coverage.

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?

Two concise sentences, front-loaded with the primary action and followed by a usage condition. Every word is purposeful, with no filler or repetition.

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 simplicity (one param, no nested objects) and rich annotations (readOnly, openWorld, destructive), the description sufficiently covers what the tool does and when to use it. The absence of an output schema reduces the need to document return values.

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?

With one parameter (ref) and schema description coverage of 0%, the description does not explicitly explain the parameter format. However, the schema pattern (^img\d+$) and the phrase 'one img ref' provide some meaning. The description adds minimal semantic value beyond what the schema already implies.

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 function with a specific verb and resource: 'Return the actual pixels for one img ref from the latest snapshot.' This distinguishes it from sibling tools like haden_read or haden_open by focusing on image pixel retrieval.

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?

It provides explicit guidance on when to use the tool: 'Use only after alt text/placeholders are insufficient for the goal.' This implies alternatives (alt text/placeholders) and sets a clear condition, though it does not name specific sibling tools or list when-not-to-use scenarios.

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

haden_interactInteract with pageA
Destructive

Perform one browser action and immediately return the updated snapshot, avoiding an extra read call. click/fill/select need a current e ref; press can target a ref or the page.

NameRequiredDescriptionDefault
keyNo
refNo
modeNotext
valueNo
actionYes
amountNo
waitMsNo
maxCharsNo
directionNo

TDQS

A4/5.0
Behavior4/5

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

Describes the immediate return of the updated snapshot, adding value beyond the destructiveHint annotation. Does not contradict annotations.

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?

Two sentences, no fluff, well-structured and easy to parse.

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

Completeness3/5

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

Explains core behavior and return, but lacks detail on parameter usage and edge cases, which is somewhat expected given the 9 parameters and 3 enums.

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

Parameters2/5

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

Only partially explains parameters (action, ref), leaving many (key, value, amount, waitMs, maxChars) undocumented. Schema enums help but description does not compensate sufficiently.

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?

Clearly states it performs a browser action and returns an updated snapshot, distinguishing it from read-only siblings like read and status.

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?

Provides guidance on when to use (to avoid extra read calls) and which actions require a current ref, but does not explicitly contrast with alternatives.

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

haden_openOpen web pageA
Read-only

Open an HTTP(S) URL and return an AI-optimized snapshot. text is fastest; dom returns sanitized HTML; visual adds computed layout/CSS.

NameRequiredDescriptionDefault
urlYes
modeNotext
maxCharsNo

TDQS

A3.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint and destructiveHint, so the safety profile is clear. The description adds valuable behavioral details: returning an AI-optimized snapshot and specifying sanitized HTML for dom mode. It does not contradict annotations and offers meaningful context beyond them.

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 a single sentence that front-loads the core action and then efficiently outlines mode differences. Every clause adds value; there is no fluff or redundancy. It is concise and well-structured.

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

Completeness3/5

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

For a tool with 3 parameters, no output schema, and moderate complexity, the description covers purpose and mode meanings but omits maxChars explanation and does not clarify what 'snapshot' encompasses beyond the modes. It is adequate but leaves room for improvement, especially since schema coverage is zero.

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

Parameters2/5

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

Schema descriptions cover 0% of parameters, so the description must compensate. It effectively explains the mode parameter (text/dom/visual semantics), but fails to describe maxChars, leaving its purpose and constraints solely to the schema. Url is self-evident from its format but still lacks narrative. Given the low coverage, this is insufficient.

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 states the primary action (open an HTTP(S) URL) and the result (AI-optimized snapshot), making the purpose clear. It doesn't explicitly compare with sibling tools, but the resource and verb are specific enough to differentiate from likely 'read' or 'interact' functions.

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?

Provides guidance on choosing output modes (text, dom, visual) with performance/reliability trade-offs, but lacks explicit direction on when to use this tool versus sibling tools like haden_read or haden_interact. The description implies usage for fetching web pages but does not state alternatives or exclusions.

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

haden_readRead current pageA
Read-only

Read the current page again, optionally limited to a CSS selector. Returns stable e refs for actions and img refs for optional image retrieval.

NameRequiredDescriptionDefault
modeNotext
maxCharsNo
selectorNo

TDQS

A3.9/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, so the description adds value by disclosing that refs are stable and that image retrieval is optional. It does not contradict annotations, and the additional behavioral context (stable refs) goes beyond the schema.

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 a single concise sentence followed by a clarifying clause. It front-loads the core purpose and adds only high-value return info. No fluff, every word contributes.

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?

With no output schema and 3 parameters lacking schema descriptions, the description is incomplete. It covers the selector's purpose but ignores mode and maxChars, which are essential for understanding output types and limits. The refs info helps but does not bridge the gap.

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

Parameters2/5

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

Schema coverage is 0%, so the description must compensate. It explains 'selector' via 'optionally limited to a CSS selector', but gives no meaning for 'mode' (text/dom/visual) or 'maxChars'. This is a significant gap for effective tool invocation, as agents cannot infer the purpose of these parameters.

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 reads the current page, optionally limited by a CSS selector, and mentions it returns stable refs for actions and images. This specific verb+resource+scope distinguishes it from siblings like haden_interact or haden_open.

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 implies when to use it ('Read the current page again') and mentions optional selector, which suggests targeted reading. However, it does not explicitly state when NOT to use it or compare with alternatives like haden_image for visual capture, though the context is reasonably clear.

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

haden_statusBrowser statusA
Read-only

Report whether the browser is running and which page is active.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior3/5

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

The description aligns with the annotations (readOnlyHint=true, destructiveHint=false) and adds a small behavioral detail: it reports the active page, not just a running flag. However, it does not disclose what happens if the browser is not running, what 'active page' means in practice, or whether the tool returns a simple boolean, a URL, or both. With annotations already signaling safety, the description adds only modest 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 a single, front-loaded sentence with no filler. Every word earns its place: it names the action, the target resource, and the exact two pieces of information reported.

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

Completeness5/5

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

For a zero-parameter read-only status tool with no output schema, the description is sufficiently complete. It clearly states what the tool reports, and no additional input or prerequisite detail is needed. The absence of an output schema is compensated by specifying that the tool reports both browser-running state and active page.

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 tool has zero parameters and the schema contains no properties, so the description does not need to explain parameter meaning. The baseline for a parameterless tool is 4, and the description correctly focuses on behavior rather than inputs.

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 uses a specific verb ('Report') and a clear resource ('whether the browser is running and which page is active'), making the tool's purpose immediately obvious. It is also clearly differentiated from sibling tools like haden_open, haden_read, haden_interact, and haden_image, which describe actions rather than state inspection.

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?

There is no explicit guidance about when to use this tool versus alternatives, nor any when-not-to-use conditions. The context is implied by the name and siblings, but the description does not state that this should be used before acting on the browser or how it relates to the other browser tools.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observedhaden_image
    • First observedhaden_interact
    • First observedhaden_open
    • First observedhaden_read
    • First observedhaden_status

TDQS

A4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a unique, non-overlapping purpose: open, read, interact, image, and status. No ambiguity between them.

Naming Consistency5/5

All tools follow the consistent 'haden_' prefix with clear action verbs (open, read, interact, image, status). The pattern is uniform.

Tool Count5/5

Five tools provide a well-scoped set for browser automation—covering navigation, reading, interaction, visual capture, and state checking—without excess or deficiency.

Completeness4/5

The set covers core browser operations, but lacks explicit navigation controls (e.g., back, forward, refresh) and waiting mechanisms. These can be worked around via interact/open, but are minor gaps.

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to browse and interact with web pages through compact, structured representations — page summaries, targeted element queries, and full detail on demand — instead of paying for a full accessibility-tree dump on every call. This cuts the token cost of reading content-heavy pages by up to ~140x while still supporting navigation, forms, clicks, screenshots, and session management.
    4 npm
    -
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to browse and interact with web pages through compact, structured page representations instead of full accessibility-tree dumps, returning a lightweight orientation on arrival and targeted element queries on demand. Agents can navigate, search, fill forms, and control sessions while choosing detail levels to keep token usage low.
    4 npm
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    Gives AI agents structured, token-efficient access to the web by returning compact page summaries and targeted queries instead of full accessibility-tree dumps, with tools for navigation, interaction, monitoring, and session management. Agents can drill into detail only when needed, cutting context costs dramatically on content-heavy pages.
    4 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to browse and interact with web pages through a compact, structured representation, returning a small orientation on arrival plus targeted queries for specific elements instead of a full accessibility-tree dump. Supports navigation, element interaction, form filling, screenshots, network monitoring, and session management while cutting token cost dramatically on content-heavy pages.
    4 npm
    MIT