Skip to main content
Glama

blowsh-mcp

Model Context Protocol Server für JS-fähiges Terminal-Browsing mit Browsh


Was ist blowsh-mcp?

blowsh-mcp ist ein Model Context Protocol (MCP)-Server, der die Leistungsfähigkeit von Browsh – einem vollständig JavaScript-fähigen Terminal-Browser – für beliebige KI-Agenten, IDE-Agenten oder MCP-Clients zugänglich macht. Dieses Projekt ermöglicht es Ihrer KI, jede moderne Webseite abzurufen und zu rendern, auch solche, die JavaScript erfordern, und das Ergebnis als leicht zu parsenden Klartext, HTML oder Markdown zu erhalten.

Mnemotechnik: „blowsh“ = Browsh-betriebener MCP-Server.


Related MCP server: openmcp

Hauptfunktionen

  • fetch_web-Tool: Einheitliches Werkzeug zur Extraktion von lesbarem Klartext, HTML oder Markdown (nach vollständigem JS-Rendering). Unterstützt CSS-selector-Extraktion, max_chars-Ausgabebegrenzungen und wait_ms-Polling, bis die JS-Ausführung abgeschlossen ist.

  • search_web-Tool: Entdecken Sie Seiten über eine gerenderte Suchmaschine (DuckDuckGo HTML mit Bing-Fallback) – sortierte Ergebnisse mit URLs und Snippets.

  • extract_links-Tool: Listet Hyperlinks (Text + absolute URL) von jeder JS-gerenderten Seite auf, um der Navigation zu folgen.

  • fetch_web_batch-Tool: Ruft bis zu 10 URLs in einem Aufruf ab, mit Fehlerisolierung pro URL.

  • SSRF-Schutz: Lehnt Anfragen an Loopback-, private, Link-Local- oder reservierte Adressen ab (DNS-aufgelöst) und schützt so den serverseitigen Browser.

  • KI-optimierte Tool-Dokumentation: Eingaben, Ausgaben und illustrierte Anwendungsfälle, die für eine nahtlose Agenten-Automatisierung entwickelt wurden. Tools werfen strukturierte Fehler mit HTTP-Statuscodes (isError in MCP-Antworten).

  • Robustes Browsh-Management: Startet Browsh einmal, hält es am Laufen, verwendet ein RAM/CPU-schonendes Singleton und fährt beim Beenden sauber herunter.

  • In-Memory-Render-Cache mit TTL: Wiederholte Abrufe werden sofort bedient, ohne erneutes Rendern.

  • Entwickelt für PaaS, Cloud, lokale KI-Tools und IDE-Agenten.



So funktioniert es

  1. KI/Agent sendet eine MCP-Anfrage: fetch_web (einzelne URL), search_web (Suchanfrage), extract_links (URL) oder fetch_web_batch (bis zu 10 URLs).

  2. blowsh-mcp startet Browsh im HTTP-Server-Modus (bei der ersten Verwendung) und verwendet ihn für alle weiteren Aufrufe erneut.

  3. blowsh-mcp fordert die Rohausgabe von Browsh an, unter Verwendung von X-Browsh-Raw-Mode: PLAIN (für Text), DOM (für HTML), oder es ruft HTML ab und konvertiert es dann in Markdown.

  4. Die Seite (nach vollständiger JS-Ausführung) wird als Terminal-Klartext, umfangreiches HTML-DOM oder sauberes Markdown zurückgegeben – KI/Agenten wählen den Ausgabetyp passend zur nachgelagerten Verarbeitung.

  5. Ergebnisse werden im Speicher zwischengespeichert (TTL), sodass wiederholte Abrufe sofort erfolgen; jede Anfrage wird vor dem Erreichen des Browsers auf SSRF geprüft.


Schnellstart (Docker – vorgefertigtes Image)

Das Image wird in der GitHub Container Registry veröffentlicht und bei jedem main-Push über GitHub Actions automatisch neu erstellt – kein Firefox/Browsh/html2markdown auf dem Host erforderlich:

docker pull ghcr.io/mokhtarabadi/blowsh-mcp:latest
docker run --rm -i ghcr.io/mokhtarabadi/blowsh-mcp:latest

Das -i-Flag ist zwingend erforderlich: Der MCP-Server kommuniziert über stdin/stdout per JSON-RPC. Lassen Sie ihn interaktiv und leiten Sie Anfragen weiter, oder richten Sie Ihren MCP-Client darauf aus (siehe KI-Client-Konfiguration unten).


Beispielverwendung

Von Claude, Cursor oder einem beliebigen MCP-fähigen Agenten:

{
  "tool": "search_web",
  "params": { "query": "bitcoin price today", "max_results": 5 }
}
// → Ranked results with URLs + snippets → feed top URL to fetch_web

{
  "tool": "fetch_web",
  "params": { "url": "https://coindesk.com/price/bitcoin/", "type": "plain" }
}
// → Returns readable plain text (live price as text table, etc)

{
  "tool": "fetch_web",
  "params": { "url": "https://coindesk.com/price/bitcoin/", "type": "markdown", "selector": "main", "wait_ms": 3000 }
}
// → Markdown of <main> only, after JS settles ("# Bitcoin Price\n\n| Time | Price | ...")

{
  "tool": "extract_links",
  "params": { "url": "https://example.com", "limit": 20 }
}
// → [{"text": "Learn more", "url": "https://iana.org/domains/example"}, ...]

{
  "tool": "fetch_web_batch",
  "params": { "urls": ["https://a.com", "https://b.com"], "type": "markdown" }
}
// → Per-URL results; a failing page never fails the batch

Die KI erhält:

  • Mit type: plain: reiner lesbarer Text (Tabellen, Listen, Hauptinhalt; ideal für NLP/Zusammenfassungen oder die Aufnahme in Terminal-Kontexte).

  • Mit type: html: das vollständige HTML-Markup nach dem gesamten JavaScript. Verwenden Sie es für Element-Parsing, Aufbau von Linkgraphen, komplexe Scrapes usw.

  • Mit type: markdown: eine saubere Markdown-Version – am besten für LLM-Kontextabschnitte, semantische Pipelines und KI-freundliche Nutzung/Workflows.

  • Fehler sind strukturiert: MCP-Antworten setzen isError: true mit einer FetchError-Meldung, die den HTTP-Status enthält, sofern verfügbar.


Projektstruktur

  • src/server.ts — MCP-Server, der Tools bereitstellt.

  • src/browshManager.ts — Startet, überwacht und beendet Browsh.

  • src/tools/fetchWeb.ts — Implementierung des fetchWeb-Tools (plain, html, markdown; selector/max_chars/wait_ms).

  • src/tools/searchWeb.ts — search_web (DuckDuckGo-HTML + Bing-Fallback-Parser).

  • src/tools/extractLinks.ts — extract_links (Hyperlinks aus gerendertem DOM).

  • src/tools/fetchWebBatch.ts — fetch_web_batch (Multi-URL, Fehlerisolierung pro URL).

  • src/tools/html2markdownManager.ts — Wrapper für die html2markdown-CLI.

  • src/ssrf.ts — SSRF-Schutz (blockiert private/loopback/reservierte Ziele).

  • src/cache.ts — In-Memory-TTL-Render-Cache.

  • src/extract.ts — Extraktion des Hauptinhalts, Selector-Helfer, Kürzung.

  • src/errors.ts — FetchError + Nachrichtenformatierung.

  • README.md — Diese Datei.

  • Dockerfile — Multi-Stage-Container (baut TS, bündelt Firefox, Browsh, html2markdown).

  • .github/workflows/docker-publish.yml — CI/CD: erstellt und veröffentlicht das Image auf ghcr.io bei main/v*.

  • .env — Konfigurationsüberschreibungen. Siehe .env.example für alle Optionen.


Installation

Voraussetzungen:

  • Node.js >= 20.18

  • Firefox installiert und im PATH

  • Browsh CLI installiert und im PATH

  • html2markdown CLI installiert und im PATH

    • Unter Debian/Ubuntu installieren mit:

      wget -O /tmp/html2markdown.deb "https://github.com/JohannesKaufmann/html-to-markdown/releases/download/v2.5.2/html2markdown_2.5.2_linux_amd64.deb"
      sudo apt-get install -y /tmp/html2markdown.deb
      rm /tmp/html2markdown.deb
    • Oder verwenden Sie das vorgefertigte Binärpaket für Ihr Betriebssystem von der Releases-Seite.

Bevorzugen Sie Docker? Überspringen Sie die Installation auf dem Host komplett – das Multi-Stage-Image bündelt Firefox, Browsh und html2markdown. Der schnellste Weg ist das veröffentlichte Image (ghcr.io/mokhtarabadi/blowsh-mcp:latest, siehe Schnellstart); um es selbst zu bauen:

docker build -t blowsh-mcp:latest .
docker run --rm -i blowsh-mcp:latest
git clone https://github.com/mokhtarabadi/blowsh-mcp.git
cd blowsh-mcp
npm install
npm run build

MCP-Server ausführen

Nach dem Erstellen starten Sie den Server mit:

node dist/server.js

Ersetzen Sie dist/server.js durch den korrekten Pfad, falls sich Ihre Build-Ausgabe unterscheidet.

Erstellen Sie bei Bedarf eine .env-Datei für die Konfiguration. Zum Beispiel:

MCP_TRANSPORT=stdio
BROWSH_FIREFOX_PATH=/usr/bin/firefox-esr
HTML2MARKDOWN_PATH=html2markdown
CACHE_TTL_MS=300000
BROWSH_REQUEST_TIMEOUT_MS=30000
ALLOW_PRIVATE_URLS=false
NODE_ENV=production
  • BROWSH_FIREFOX_PATH ermöglicht es Ihnen, die von Browsh während des Headless/HTTP-Betriebs verwendete Firefox-Datei anzupassen.

  • HTML2MARKDOWN_PATH ermöglicht es Ihnen, einen benutzerdefinierten Pfad zur html2markdown-Binärdatei anzugeben (Standard: html2markdown im PATH).

  • CACHE_TTL_MS, BROWSH_REQUEST_TIMEOUT_MS und ALLOW_PRIVATE_URLS optimieren den Render-Cache, das Timeout pro Anfrage bzw. den SSRF-Schutz.

  • Browshs HTTP-Port/-Host sind NICHT konfigurierbar.


Projektdokumentation

Datei

Zielgruppe

Zweck

AGENTS.md

Agenten

Betriebsregeln, Leitplanken, Aufgabenlebenszyklus

DESIGN.md

Alle

Designsprache für MCP-Antworten/-Ausgaben

docs/architecture.md

Entwickler

Systemübersicht, Komponentenverdrahtung

docs/data_model.md

Entwickler

Tool-Eingabe-/Ausgabeschemata und Fehlermodell

docs/conventions.md

Entwickler

DateTime-Standard, SOLID-Richtlinien

CHANGELOG.md

Alle

Versionshistorie (Keep a Changelog)

tasks/

Team

Kanban-Aufgabendateien (Backlog → Archiv)

Diese README ist der benutzerorientierte Einstiegspunkt; agentenorientierte Regeln stehen in AGENTS.md und sind vor jeder Implementierung Pflichtlektüre.


Tool-API

Name

Parameter

KI-Anwendungsfall/Beschreibung

fetch_web

{ url, type: "plain"|"html"|"markdown"|"pdf", selector?, max_chars?, wait_ms? }

Ruft eine Seite nach dem JS-Rendering als Text/HTML/Markdown ab. selector (CSS) extrahiert nur das übereinstimmende Element; max_chars begrenzt die Ausgabe; wait_ms pollt, bis JavaScript zur Ruhe kommt. type: pdf lädt das PDF direkt herunter (max. 20 MB) und extrahiert Text über pdftotext – selector/wait_ms/max_chars finden keine Anwendung.

search_web

{ query: string, max_results?: number, page?: number, enrich?: boolean }

Durchsucht das Web (DuckDuckGo-HTML + Bing parallel gerendert) und gibt [{title, url, snippet, fetched_at}] zurück. page 1–10 für die Paginierung; enrich: true ersetzt die Top-3-Snippets durch abgerufene Markdown-Inhalte (45-s-Budget). fetched_at ist die UTC-Epochenzeit in ms zur Bestimmung der Veraltung. Füttern Sie die Ergebnis-URLs an fetch_web/extract_links.

extract_links

{ url: string, limit?: number }

Gibt alle Hyperlinks ({text, url}, absolut) auf einer JS-gerenderten Seite zurück, um der Navigation zu folgen, ohne vollständige DOM-Auszüge zu benötigen.

fetch_web_batch

{ urls: string[], type: "plain"|"html"|"markdown", selector?, max_chars?, wait_ms? }

Ruft bis zu 10 URLs in einem Aufruf ab (cache-bewusst). Gibt pro URL {url, ok, content|error} zurück – ein einzelner Fehler beendet nie den gesamten Stapel.

Rückgabewerte

  • type: plain: terminalartiger, JS-ausgeführter lesbarer Text (oder Fehlerzeichenfolge).

  • type: html: HTML-Markup-Zeichenfolge nach JS (oder Fehlerzeichenfolge). Mit selector nur das HTML des übereinstimmenden Elements.

  • type: markdown: Markdown-Konvertierung des Hauptinhalts oder des ausgewählten Elements (oder Fehlerzeichenfolge). Links, Überschriften, Listen und Seitenstruktur bleiben für KI-freundliche Kontexte erhalten.

  • type: pdf: extrahierter Klartext aus dem PDF-Dokument (über pdftotext, 20-MB-Grenze).

  • Fehler sind strukturiert: eine MCP-Antwort mit isError: true und einer FetchError-Meldung, die den HTTP-Status enthält, sofern dieser bekannt ist (niemals eine stille leere Zeichenfolge).


Umgebungsvariablen

Legen Sie diese über .env (automatisch geladen) oder die Umgebung fest:

Variable

Default

Description

BROWSH_FIREFOX_PATH

firefox

Firefox-Binärdatei, die von Browsh verwendet wird (z. B. /usr/bin/firefox-esr).

HTML2MARKDOWN_PATH

html2markdown

Pfad zur html2markdown-Binärdatei.

BROWSH_REQUEST_TIMEOUT_MS

30000

Zeitüberschreitung pro Render-Anfrage (ms).

PDF_MAX_BYTES

20971520

Maximale PDF-Dateigröße in Bytes für fetch_web type: pdf.

BROWSH_RECYCLE_REQUESTS

100

Anzahl der Anfragen, nach denen der Browserprozess recycelt wird.

BROWSH_IDLE_TIMEOUT_MS

600000

Leerlaufzeit in ms, bevor der Browserprozess beendet wird (10 Min.).

CACHE_TTL_MS

300000

TTL des Render-Caches im Speicher (ms).

ALLOW_PRIVATE_URLS

false

Setzen Sie true, um den SSRF-Schutz für Loopback-/private Ziele zu deaktivieren.

MCP_TRANSPORT

stdio

Transporttyp (nur stdio implementiert).

NODE_ENV

production

Node-Umgebung.


KI-gestützte Tool-Auswahl

  • Beginnen Sie mit search_web: Um Seiten zu entdecken, führen Sie eine Abfrage aus und wählen Sie die besten Ergebnis-URLs; rufen Sie diese dann ab.

  • Verwenden Sie fetch_web für einzelne Seiten: plain, wenn Sie eine schnelle, lesbare Ausgabe für Zusammenfassung/Klassifizierung benötigen; html, um Elemente, Links oder Tabellen zu parsen; markdown für LLM-freundliche Kontextblöcke. Fügen Sie selector/max_chars/wait_ms hinzu, um token-effizient zu bleiben und abgeschlossene, relevante Inhalte zu erhalten.

  • Verwenden Sie extract_links vor tiefen Crawls: Folgen Sie der Navigation kostengünstig, anstatt vollständige DOMs abzurufen.

  • Verwenden Sie fetch_web_batch für mehrere Quellen: Ein Aufruf statt N Round-Trips; Fehler werden pro URL isoliert.

Fehlerbehandlung: Tools werfen FetchError und MCP gibt isError: true mit einer umsetzbaren Meldung zurück — ungültige Protokolle, SSRF-Blöcke, nicht übereinstimmende Selektoren, HTTP-Statuscodes und Rendering-Fehler werden nie verschwiegen.


MCP-Protokoll: KI-Client-Konfiguration

Bevor Sie Ihren KI-Client (Claude, Cursor usw.) konfigurieren, müssen Sie

  1. Abhängigkeiten installieren:    npm install

  2. Das Projekt erstellen:    npm run build

  3. Den MCP-Server aus dem kompilierten Output starten:    node dist/server.js

Beispielkonfiguration für Claude Desktop oder Cursor:

{
  "mcpServers": {
    "blowsh": {
      "command": "node",
      "args": ["dist/server.js"],
      "env": {}
    }
  }
}

Beispielkonfiguration für opencode (Projekt opencode.json):

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "blowsh": {
      "type": "local",
      "command": ["docker", "run", "--rm", "-i", "ghcr.io/mokhtarabadi/blowsh-mcp:latest"],
      "enabled": true,
      "timeout": 120000
    }
  },
  "permission": { "blowsh_*": "allow" }
}

Die Docker-Form benötigt keine binären Dateien auf dem Host; das Image enthält Firefox, Browsh und html2markdown. Starten Sie opencode nach dem Speichern neu (die Konfiguration wird beim Start einmalig geladen).


Sauberes Herunterfahren

blowsh-mcp fängt SIGINT/SIGTERM ab und stellt sicher, dass Browsh sauber beendet wird – keine verwaisten Browser.


Sicherheit und Überlegungen

  • Der Server führt Browsh lokal aus und ruft über HTTP localhost ab.

  • SSRF-Schutz: Standardmäßig lehnen fetch_web/search_web/extract_links/fetch_web_batch URLs ab, die auf Loopback-, private, link-lokale oder reservierte IP-Bereiche aufgelöst werden (über DNS geprüft). Setzen Sie ALLOW_PRIVATE_URLS=true, um dies zu deaktivieren – nicht empfohlen.

  • Keine öffentliche Bereitstellung, es sei denn, der MCP HTTP/streamable Server ist explizit konfiguriert.

  • Setzen Sie Ports niemals ohne Firewall dem offenen Web aus.

  • Verwenden Sie Umgebungsvariablen für Geheimnisse/Konfiguration.


Erweitern

Fügen Sie neue Tools in src/tools/ hinzu, exportieren Sie sie in src/server.ts, und dokumentieren.
KI-Clients entdecken Docstrings automatisch.


Fehlerbehebung

  • Wenn fetchPlain 404 zurückgibt oder JS nicht rendern kann: Überprüfen Sie, ob Firefox und Browsh installiert und im PATH sind.

  • Wenn Firefox nicht gefunden wird oder nicht startet, setzen Sie BROWSH_FIREFOX_PATH in .env, um den vollständigen Pfad zu Ihrer Firefox-Installation anzugeben.

  • Browsh-Port/-Host sind festgelegt – es gibt keine Umgebungs- oder CLI-Einstellung, um sie zu ändern.

  • Führen Sie es für maximale Sicherheit in einem Container aus.


Lizenz

MIT


Autor: Mohammad Reza Mokhtarabadi mmokhtarabadi@gmail.com

Available Tools

1 tool
fetch_webFetch Web (plain, html, markdown)A

Fetch a web page and return its content as plain text, HTML, or Markdown. Uses a JS-capable browser for dynamic sites.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesThe HTTP/HTTPS web URL to fetch
typeYesThe output type: plain, html, or markdown

TDQS

A4/5.0
Behavior4/5

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

With no annotations, the description carries full burden for behavioral traits. It discloses the use of a JS-capable browser, which is critical for understanding behavior with dynamic sites. It does not mention rate limits or error handling, but the core behavioral trait is well covered.

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, front-loading the main purpose and adding the browser capability as a key differentiator. Every sentence earns its place without 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 2 simple parameters, no output schema, and no annotations, the description is sufficient. It covers the purpose, output types, and a notable behavior (JS browser). Minor missing details like response size limits or timeout are not critical for a basic fetch tool.

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 coverage is 100% with both parameters well described. The description adds 'plain text, HTML, or Markdown' but that is a restatement of the enum values. No additional nuance is provided beyond the schema.

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 action (fetch a web page) and the resource (web page content), and specifies three output types (plain, HTML, Markdown). It distinguishes the tool by mentioning JS-capable browser for dynamic sites, which sets it apart from simple fetchers.

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?

No explicit guidance on when to use this tool vs alternatives or when not to use it. Given no sibling tools are listed, it is minimally adequate but lacks context like 'use for public pages only' or 'prefer for dynamic content'.

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. 1 tool updatev1.0.0
    • First observedfetch_web

TDQS

A4.1/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of ambiguity between tools. The tool's purpose is clear and distinct.

Naming Consistency5/5

The single tool name 'fetch_web' follows a clear verb_noun pattern. With only one tool, there is no inconsistency to evaluate.

Tool Count3/5

A single tool is borderline for a server. While it serves a specific purpose, it feels thin compared to typical MCP servers that offer multiple related operations.

Completeness4/5

The tool provides core web fetching functionality with output format options. A minor gap might be the lack of custom headers or request methods, but agents can work around this for most use cases.

Maintenance

ActivityActive
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    F
    maintenance
    A Model Context Protocol server that enables AI agents to fetch live web content with JavaScript rendering, proxy rotation, and anti-bot evasion.
    9
    79 npm
    58
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to automate web tasks such as browsing, clicking, typing, and taking screenshots via the Model Context Protocol.
    1
    MIT