Listing on LM Studio Hub:
webbrain/web-tools(ownerwebbrain, nameweb-tools, public) ā https://lmstudio.ai/webbrain/web-toolsOne-click install while LM Studio is open: click the Run in LM Studio button on the Hub page, or open this URL on your machine:
lmstudio://plugin?owner=webbrain&name=web-toolsThat triggers LM Studio to download the plugin from the Hub into your local plugins directory. You can also install from inside LM Studio (integrations icon next to the chat input ā toggle webbrain/web-tools on) or via CLI with
lms clone webbrain/web-tools.
Give any LM Studio model the ability to read the live web ā and, if you have the browser extension, to act inside your own signed-in session.
Works with no browser, nothing installed:
fetch_url ā raw HTTP fetch with content-type smarts. JSON gets
pretty-printed, HTML is stripped to readable text + <title>,
plain text comes back verbatim unless it is long enough to compact,
binaries are summarised instead of inlined.research_url ā same fetcher, biased toward "give me the
article body, not the navigation chrome." Extracts <main> /
<article>, drops header/nav/footer/aside before stripping tags.
Best for news, blog posts, READMEs, Wikipedia, docs.Adds real browser access when the WebBrain extension is installed on a Chromium browser (Chrome, Edge, Brave, Opera, Vivaldi ā not Firefox):
The browser tools are always offered. Without an extension they return an actionable message instead of failing opaquely, so the model can tell you how to switch the capability on rather than retrying blindly.
Long text results are context-friendly by default. Instead of dumping
the first N characters and losing everything after the cutoff, the tools
run an LLM-free compaction pass that keeps the beginning, a few high-signal
middle passages, and the ending. Results include compacted:true plus a
compaction object when this happens. Pass compact:false for legacy
head-only truncation, or maxChars to ask for a smaller/larger return
budget (fetch_url: 8k text / 16k JSON by default, hard-capped at 50k;
research_url: 16k by default, hard-capped at 60k).
Pure Node ā no Puppeteer, no Playwright, no headless Chromium.
Same fetching logic the WebBrain browser
extension ships, ported off chrome.* onto Node fetch.

That pulls the plugin from LM Studio Hub into your local plugins
directory. Open LM Studio, click the integrations icon next to the
chat input, and toggle webbrain/web-tools on (see screenshot
above). Any tool-capable model in the same chat can now call
fetch_url and research_url.
Try it:
What's on the front page of news.ycombinator.com right now?
The model should call research_url and come back with live
headlines.
browser_task needs the WebBrain extension on a
Chromium browser ā Chrome, Edge, Brave, Opera or Vivaldi. The extension
dials out to this plugin; a Manifest V3 extension cannot listen on a socket,
so the plugin hosts the listener.
The bridge runs from the extension's offscreen document, and the Firefox
build has none ā see src/firefox/ARCHITECTURE.md.
cloud-bridge.js and cloud-runs.js exist only under src/chrome/. On
Firefox, browser_task will always return the not-connected response.
fetch_url and research_url are unaffected and work everywhere.
ws://127.0.0.1:17375/extension and enable it.browser_status to confirm.One bridge at a time. The extension holds exactly one outbound bridge socket. Pointing it here means it is not pointed at WebBrain Cloud (
17373) or the MCP server (17374). Override the port withWEBBRAIN_BRIDGE_PORT.
Try it:
Check my GitHub notifications and summarise anything that mentions me.
Listed up front so it doesn't bite you mid-session. All three limits apply
to fetch_url and research_url only ā browser_task is the answer to
every one of them:
spaSuspected: true so the model can
give up gracefully instead of looping.If you want to hack on the plugin, clone this repo (rather than
lms clone the published artifact) and:
lms dev bundles src/ and registers the plugin live with your
running LM Studio instance ā edits to TypeScript files hot-reload
without a restart. The dist/ build via npm run build is only
needed if you want to import the tool implementations from another
Node project.
bridgeClient.ts is a deliberate standalone copy of the same protocol in
mcp-server/src/bridge.ts. The two ship to
different registries ā LM Studio Hub and npm ā and sharing a package would
drag the monorepo into both installs. Change the protocol in one, change
it in the other.
tools/ and util/ are pure functions with no SDK dependency ā you
can import them from any Node project that wants the same
web-fetching primitives.
The plugin runs in your local Node process, so anything reachable from that process is reachable from the LLM. The defenses below are layered ā each closes a class of bypass the previous one missed ā but DNS rebinding is a known residual gap (see end of this section).
The browser bridge is a separate trust surface from the HTTP tools.
Everything below concerns fetch_url / research_url. For browser_task:
Set allowPrivate: true on a per-call basis to opt out of the URL
guard + DNS check ā useful when you actually want to talk to
localhost services.
An attacker controlling a domain with TTL=0 can return a public IP
when our guard runs dns.lookup and a private IP a moment later when
fetch connects. Closing this fully requires pinning the resolved
IP via a custom undici Agent's connect.lookup hook so the
connection uses the exact address we validated. Tracked as a
follow-up. If you're running this on a cloud VM with sensitive
instance-metadata endpoints, use a network policy that blocks
169.254.169.254 outbound rather than relying solely on this
plugin's checks.
MIT, same as the rest of WebBrain. See ../LICENSE.
browser_task ā hand a goal to WebBrain running in the browser
you are already logged into. Reaches the authenticated dashboards,
webmail and client-rendered apps that plain HTTP cannot see.
mode='ask' is read-only; mode='act' can click and type, gated by
in-browser approval prompts.browser_status ā report whether the extension is attached, and
what to do if it isn't. Pass a runId to poll a task that outlived its
initial timeout and retrieve the eventual result.browser_respond ā forward the user's answer when a task pauses with
needs_user_input. Use the runId and clarifyId returned by the task;
never guess the answer on the user's behalf.browser_abort ā stop a continuing run by runId. Browser actions that
already completed are not rolled back.The bridge listener binds 127.0.0.1 only. Anything that can reach that
port can drive your signed-in browser ā never expose it to a network or a
container bridge.
Connections must send the extension's hello frame with
client: "webbrain-extension"; anything else is closed. This is not
authentication ā the shipping extension sends no shared secret, so a local
process could impersonate it. Treat the port as trusted-local.
browser_task delegates a goal, never individual clicks. WebBrain's
capability Ć origin permission gate runs inside its own agent loop, so
every approval prompt a human would see still fires. That is why this
plugin does not expose the low-level browser primitives.
A timeout does not cancel the run. A task that already submitted a
form should not be silently killed; the browser keeps going and the result
stays visible in the WebBrain side panel.
URL guard, structural (sync). Requests to RFC1918 (10.*,
172.16-31.*, 192.168.*), loopback (127.*, ::1), link-local
(169.254.*, fe80::/10), unique-local IPv6 (fc00::/7),
cloud-metadata IPs, and *.local / *.internal / *.lan /
*.home / *.corp / *.intranet are blocked. Non-http(s)
protocols (file://, ftp://, gopher://, javascript:) too.
DNS resolution check. Before each hop, the hostname is
resolved via dns.lookup({all: true}) and every returned A/AAAA
address is checked against the same private ranges. Closes the
bypass where attacker.example A 127.0.0.1 would otherwise pass
the syntactic check.
Per-redirect re-validation. Redirects are followed manually
(5 hops max). Each Location runs through both checks before we
follow it ā a public URL cannot 302 to http://169.254.169.254/....
Cross-origin header stripping. When a redirect crosses origins,
Authorization, Cookie, Proxy-Authorization, and
Proxy-Authenticate headers are dropped. Stops credentials passed
via the per-call headers arg from leaking through an open-redirect
chain.
Streaming response cap. Bodies read with a hard byte ceiling
(4 MB for fetch_url, 6 MB for research_url). Past the cap, the
stream is cancelled and the result is marked truncated: true.
No credentials: 'include'. Unlike the browser extension,
this plugin makes anonymous requests.
Timeouts. Default 30 s, hard cap 120 s.
lms clone webbrain/web-tools
npm install
lms dev
.
āāā manifest.json ā LM Studio plugin metadata
āāā package.json ā npm deps + build scripts
āāā tsconfig.json ā strict-mode TS, ES2022
āāā src/
āāā index.ts ā plugin registration glue
āāā tools/
ā āāā fetchUrl.ts ā fetch_url implementation
ā āāā researchUrl.ts ā research_url implementation
ā āāā browserTask.ts ā browser task + status/respond/abort recovery tools
āāā util/
āāā htmlToText.ts ā regex HTML stripper
āāā safeFetch.ts ā redirect-revalidating wrapper + streaming cap
āāā urlGuard.ts ā private-IP / file:// blocker
āāā bridgeClient.ts ā WebSocket listener the extension dials into