Stealth Web Search
  • MCP server
  • One Docker container
  • LM Studio ready
  • Apache-2.0

A stealth web browser and research agents for your AI

Stealth Web Search gives any MCP client a real, JavaScript-rendering browser with built-in anti-fingerprinting, plus sub-agents that browse, automate and cross-check the web on their own. It runs in one Docker container, logs every step, and lets you watch it all live.

The Stealth Web Search dashboard: the live view of a sub-agent's browser on a book catalogue, the activity feed of tool calls, and the Agents tab with a finder result and its sources.
The live dashboard while a sub-agent reads a catalogue and a finder cross-checks its answer.

Quick start

Running in three commands

You need Docker. The first build downloads the checksum-verified Obscura browser and compiles the server; it takes about a minute.

  • MCP endpoint at http://127.0.0.1:8931/mcp (Streamable HTTP)
  • Live dashboard at http://127.0.0.1:8931/
  • Published on 127.0.0.1 only, with optional bearer-token auth
  • JSON logs in ./logs, one transcript per sub-agent run

Read the full setup guide

bash
$ git clone https://github.com/tanishmeh/stealth-web-search.git
$ cd stealth-web-search
$ docker compose up -d --build
$ curl -s http://127.0.0.1:8931/healthz
{"ok":true,"name":"stealth-web-search","version":"0.1.0",...}

Features

Everything an agent needs to use the web, and everything you need to trust it

A production-quality MCP server around a fast, stealthy browser engine, with the observability to see exactly what your agents did.

A real browser, not a fetcher

41 browser_* tools: navigate, read pages as text, Markdown, links or structured data, click, type, fill forms, use tabs and cookies, run JavaScript, take screenshots and PDFs. Pages run their scripts in V8 first, so single-page apps work.

Stealth by default

Obscura's stealth build presents a consistent Chrome fingerprint, impersonates its TLS fingerprint, reports navigator.webdriver as false and blocks trackers. Element references stay on the server, so nothing is written into the page.

Sub-agents for whole jobs

Hand over a task and get back only the result. Each run gets its own isolated browser and uses any OpenAI-compatible model: vLLM, LM Studio, Ollama, llama.cpp or a hosted API.

Watch it live

The dashboard streams the agent's tab with markers where it clicks and types, lists every tool call with its arguments and result, and shows the console, network, logs and each sub-agent's reasoning as it streams.

Logs everything

Every MCP message, tool call, browser command, console message and network request goes to rotating JSON log files. Passwords, cookie values and credential headers are redacted, and each sub-agent run leaves a transcript.

LM Studio and any MCP client

One click adds it to LM Studio. Claude Code, Cursor, VS Code and other clients connect over Streamable HTTP; stdio-only clients use the bundled bridge.

Reusable automation scripts

The automation agent turns a task into a verified script with named parameters. Replay it later with script_run in a fresh browser, with no model, in a QuickJS sandbox with memory and time limits.

A hardened container

Non-root, read-only file system, every Linux capability dropped. The browser's control port never leaves the container, private networks are blocked by default, and sub-agents use a separate browser engine.

Tested end to end

Unit and integration suites run against the real browser engine, locally and in the container. Sub-agents are tested with a scripted model and live with a real one, and LM Studio scenarios run against a local model.

51MCP tools
3kinds of sub-agent
1Docker container
0cloud accounts needed

Sub-agents

Give a TASK, get back the OUTPUT

Sub-agents run inside the container next to the browser. Your agent stays in charge and keeps its context small; the sub-agent does the clicking and reading, and returns only what you asked for.

agent_run

Agentic

Completes a task in its own browser and returns the output in the shape you describe, as text or validated JSON.

TaskOn books.toscrape.com, open the Poetry category and read the first 3 books.
Output[{"title": "A Light in the Attic", "price": "£51.77"}, {"title": "The Black Maria", "price": "£52.15"}, …]
agent_automate

Automation

Does the task once, writes a script that repeats it for any inputs, verifies it in a fresh browser, and stores it.

TaskOn quotes.toscrape.com, get the first 3 quotes for a tag.
OutputScript "quotes-by-tag" v1, verification PASSED script_run {"name": "quotes-by-tag", "params": {"tag": "love", "count": 3}}
agent_find

Finder

Searches the web, reads the pages, cross-checks several independent sources and returns the answer with cited, verified quotes.

ObjectiveWhat is the default TCP port a PostgreSQL server listens on?
Output5432 (confidence: high) [1] postgresql.org: "The TCP port the server listens on; 5432 by default." [2] dbvis.com

How sub-agents work, their limits and the script API

See every step

Nothing happens out of sight

The dashboard at http://127.0.0.1:8931/ shows what each agent sees and does, while it does it.

Integrations

First-class LM Studio support, and standard MCP for everything else

Use the tools from an LM Studio chat, run the sub-agents on an LM Studio model, or connect any MCP client.

LM Studio

LM Studio connects to MCP servers listed in its mcp.json. Add this one in one click, or with the setup script.

  1. Start the server: docker compose up -d
  2. Click Add to LM Studio, or run npm run lmstudio:setup
  3. Load a tool-use model with a context of 32k or more
  4. In a chat, turn on mcp/stealth-web-search under Integrations
json
{
  "mcpServers": {
    "stealth-web-search": {
      "url": "http://127.0.0.1:8931/mcp",
      "timeout": 180000
    }
  }
}

The sub-agents can run on LM Studio too: see the LM Studio provider in config/models.example.json.

Any MCP client

The endpoint is http://127.0.0.1:8931/mcp (Streamable HTTP). Clients that only speak stdio use the bridge that ships in the image.

bash
claude mcp add --transport http stealth-web-search http://127.0.0.1:8931/mcp
  • Streamable HTTP for the 2025 and 2026 protocol revisions
  • A stdio bridge in the image for stdio-only clients
  • Optional bearer token with AUTH_TOKEN
  • Every client shares one browser; the dashboard lists the sessions

All clients, tokens and remote access

Model configuration

Point the sub-agents at your model with one JSON file

Copy config/models.example.json to config/models.json, set your endpoint, model and key, and restart. The format is the provider list editors use for custom endpoints, so you can reuse the one you already have.

  • Any OpenAI-compatible endpointvLLM, LM Studio, Ollama, llama.cpp or a hosted API, with streaming, tool calling and reasoning effort.
  • Validated at startupClear errors for invalid values, warnings for unknown fields and keys that appear twice. Comments and trailing commas are fine.
  • Checked in one commanddocker compose run --rm --no-deps stealth-web-search node dist/check-config.js --ping shows the settings the agents will use and whether the model answers.
models.json reference
config/models.json
[
  {
    "name": "Local vLLM",
    "vendor": "customendpoint",
    "apiKey": "your-api-key",
    "apiType": "chat-completions",
    "models": [
      {
        "id": "qwen3.8-27b",
        "name": "Qwen3.8 27B (vLLM)",
        "url": "http://192.168.1.50:8000/v1/chat/completions",
        "toolCalling": true,
        "vision": false,
        "streaming": true,
        "contextWindow": 262144,
        "maxOutputTokens": 32768,
        "thinking": true,
        "supportsReasoningEffort": ["low", "medium", "xhigh"],
        "reasoningEffortFormat": "chat-completions",
        "modelOptions": {
          "temperature": 0.4,
          "top_p": 0.95
        }
      }
    ],
    "settings": {
      "qwen3.8-27b": {
        "reasoningEffort": "medium"
      }
    }
  },
  {
    "name": "LM Studio",
    "vendor": "lmstudio",
    "apiKey": "lm-studio",
    "apiType": "chat-completions",
    "models": [
      {
        "id": "qwen/qwen3.8-27b",
        "name": "Qwen3.8 27B (LM Studio)",
        "url": "http://host.docker.internal:1234/v1/chat/completions",
        "toolCalling": true,
        "vision": true,
        "streaming": true,
        "contextWindow": 32768,
        "maxOutputTokens": 8192,
        "thinking": true,
        "supportsReasoningEffort": ["low", "medium", "high"],
        "reasoningEffortFormat": "chat-completions",
        "modelOptions": {
          "temperature": 0.4,
          "top_p": 0.95
        }
      }
    ],
    "settings": {
      "qwen/qwen3.8-27b": {
        "reasoningEffort": "low"
      }
    }
  }
]

How it works

One container, two browser engines, your model

All MCP clients share one browser and their tool calls run in order. Sub-agents and scripts get private browsers on a second engine, so they never disturb yours.

Architecture An AI client talks MCP to the server in the Docker container. The server drives the main Obscura browser over CDP and runs sub-agents, which use a second, isolated Obscura engine and call your model endpoint. Both engines load websites. You watch through the dashboard and the logs. DOCKER CONTAINER AI client LM Studio · Claude Code Cursor · VS Code · … You dashboard :8931 ./logs (JSON) MCP server (Node.js) 51 tools · ordered queue logs · live view · auth Sub-agents agent_run · automate · find scripts (QuickJS sandbox) Obscura shared browser V8 · stealth Obscura isolated engine a browser per run Websites rendered with JavaScript Your model vLLM · LM Studio · Ollama OpenAI-compatible API MCP CDP CDP chat completions (tool calling) live view

Details, including the tool queue, isolation and crash recovery, are in the architecture notes.

Security

Treat it like a remote-control port. It is built like one.

Whoever reaches the MCP endpoint drives a browser on your machine, so the defaults are strict.

  • Loopback only. The port is published on 127.0.0.1; AUTH_TOKEN adds bearer-token auth for the MCP endpoint and the dashboard.
  • DNS-rebinding protection. Host and Origin headers are validated, so a web page cannot reach the server through a rebound domain.
  • URL guards. file: and javascript: URLs are refused, and private networks are blocked unless you allow them.
  • Locked-down container. Non-root user, read-only file system, all capabilities dropped, no-new-privileges.
  • Isolation. Sub-agents and scripts use a separate engine and start with empty browsers; scripts run in a WebAssembly sandbox.
  • Redacted logs. Passwords, cookie values and credential headers are masked in the logs and on the dashboard.

Pages are untrusted input: text on a page can try to steer a model. Keep a human in the loop for sensitive accounts. Security policy

Give your agent the web, safely

Clone, build, connect. The setup guide takes you from nothing to a local model browsing in LM Studio in a few minutes.