README.md
Give any LM Studio model the ability to read the live web. Two tools, no browser required:
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, binaries are summarised instead of
inlined.research_url ā same fetcher, biased toward "give me the
article body, not the navigation chrome." Extracts /
, drops header/nav/footer/aside before stripping tags.
Best for news, blog posts, READMEs, Wikipedia, docs.<main><article>Pure Node ā no Puppeteer, no Playwright, no headless Chromium.
Same fetching logic the WebBrain browser
extension ships, ported off chrome.* onto Node fetch.

lms clone webbrain/web-tools
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.
Listed up front so it doesn't bite you mid-session:
spaSuspected: true so the model can
give up gracefully instead of looping.If you need any of the above, install the WebBrain browser extension instead (or alongside) ā same model server, full agent loop with browser interaction.
If you want to hack on the plugin, clone this repo (rather than
lms clone the published artifact) and:
npm install lms dev
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.
. āāā 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 āāā util/ āāā htmlToText.ts ā regex HTML stripper āāā safeFetch.ts ā redirect-revalidating wrapper + streaming cap āāā urlGuard.ts ā private-IP / file:// blocker
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).
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.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.Location runs through both checks before we
follow it ā a public URL cannot 302 to http://169.254.169.254/....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.fetch_url, 6 MB for research_url). Past the cap, the
stream is cancelled and the result is marked truncated: true.credentials: 'include'. Unlike the browser extension,
this plugin makes anonymous requests.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.