ollama-search-mcp
A zero-dependency MCP server that turns Ollama's web search and fetch REST APIs into two read-only tools for any MCP client.
web_search— search the live web with a natural-language query and get ranked results containing title, URL, and page text (default 5 results, 1–10; default 1500 chars per result).web_fetch— fetch one absolute http(s) URL and return the page title, readable text, and up to 40 outgoing links; supports HTML and PDFs (default 20000 chars).Both tools are read-only and auto-approvable (
readOnlyHint,openWorldHint), and can be used together in an agent loop: search to find a page, then fetch to read it fully.Works with any MCP client over stdio JSON-RPC (Claude Code, Codex CLI, Cursor, Cline, Zed, etc.), configured via
OLLAMA_API_KEYand optional env/config settings (base URL, timeout, retries).Limitations: no image/video/news verticals, no domain or date filters, no login-protected pages, and not a host-app search provider.
Integrates with Ollama's web search and web fetch APIs to provide live web search with ranked results and page text, plus URL fetching that returns clean text and outgoing links.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ollama-search-mcpsearch the web for today's top AI news and fetch the first article"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ollama-search-mcp
A dependency-free MCP server that gives any MCP client — Claude Code, Codex CLI, ZCode, Cursor, Cline, Zed — two tools:
web_search— search the live web, get ranked results with title, URL and page textweb_fetch— read one URL as clean text, plus its outgoing links
It is a thin wrapper around Ollama's web search and web fetch APIs. Ollama ships those as plain REST endpoints; this turns them into tools your agent can call.
your agent ──stdio/JSON-RPC──▶ ollama-search-mcp ──HTTPS──▶ ollama.com/api/web_{search,fetch}One file, zero dependencies. No
npm install, no build step, no transitive supply chain.Any MCP client. Plain stdio JSON-RPC; nothing client-specific.
Free. A free Ollama account includes the search API. No credit card, unlike most search APIs.
Search and read. Results come back with page text already attached, and
web_fetchreads any URL — the pair covers most grounding needs without a second vendor.
Scope: this is an independent community project. It is not affiliated with or endorsed by Ollama. It talks to Ollama's public API with your own key, and you are subject to their terms and quotas.
Requirements
Node.js 20+ (24+ recommended: it can read the OS trust store, which matters behind TLS-inspecting antivirus — see Troubleshooting)
A free Ollama API key: https://ollama.com/settings/keys
Related MCP server: searxng-mcp
Install
1. Get a key
Create one at https://ollama.com/settings/keys. It looks like a long random string.
2. Add the server to your client
Two ways in: run the published package through npx, or point your client at a local checkout.
Either way the key comes from OLLAMA_API_KEY or a config file (see Configuration).
From npm (recommended)
Claude Code — --scope user matters, since the default local scope would confine the server to
the directory you ran the command in:
claude mcp add --scope user ollama-search -e OLLAMA_API_KEY=your-key-here \
-- npx -y ollama-search-mcpCodex CLI:
codex mcp add ollama-search --env OLLAMA_API_KEY=your-key-here \
-- npx -y ollama-search-mcpAny client with a JSON config file:
{
"mcpServers": {
"ollama-search": {
"command": "npx",
"args": ["-y", "ollama-search-mcp"],
"env": { "OLLAMA_API_KEY": "your-key-here" }
}
}
}On Windows, if the client cannot find npx, write cmd /c npx -y ollama-search-mcp instead, or
use the local-checkout form below.
From a local checkout
The same three clients, pointed at server.mjs in a clone of this repo. On Windows, use the full
path to node.exe (C:\Program Files\nodejs\node.exe) and double the backslashes in JSON.
Claude Code
claude mcp add --scope user ollama-search -e OLLAMA_API_KEY=your-key-here \
-- node /absolute/path/to/server.mjsCodex CLI
codex mcp add ollama-search --env OLLAMA_API_KEY=your-key-here \
-- node /absolute/path/to/server.mjsAny client with a JSON config file
{
"mcpServers": {
"ollama-search": {
"command": "node",
"args": ["/absolute/path/to/server.mjs"],
"env": { "OLLAMA_API_KEY": "your-key-here" }
}
}
}On Windows, use the full path to node.exe (C:\Program Files\nodejs\node.exe) and double the
backslashes in JSON.
3. Verify
OLLAMA_API_KEY=your-key-here node test/live.test.js # or: npm run test:liveA healthy run prints four passing tests. If your client shows the server as failed, run server.mjs
by hand — it prints a plain-English diagnostic to stderr (stdout is reserved for JSON-RPC):
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' | node server.mjsConfiguration
Everything is optional; the defaults work with only an API key.
Setting | Default | Environment variable | Meaning |
|
|
| Upstream host. Point it at a proxy if you have one. |
| (none) |
| Your Ollama key. Required. |
|
|
| Per-request timeout. |
|
|
| Tries per request, for connection-level failures and 429/5xx. 1–5. |
Resolution order, lowest to highest precedence:
built-in defaults
config.jsonnext toserver.mjsconfig.jsonin your user config dir (%APPDATA%\ollama-search-mcp\, or~/.config/ollama-search-mcp/)the file named by
--config <path>orOLLAMA_MCP_CONFIGenvironment variables
Blank environment variables are ignored, so OLLAMA_API_KEY= (an unset-looking shell variable)
cannot silently wipe a key that is in the file.
Copy config.example.json to config.json to use a file. config.json
is gitignored — keep real keys out of version control.
Two extra switches: OLLAMA_MCP_TRUST_SYSTEM_CA=0 disables the OS trust store merge, and
--help / --version print usage without starting the protocol loop.
Tools
web_search
Argument | Type | Default | Notes |
| string | — | Required. Natural language. |
| integer 1–10 |
| Clamped, because upstream errors on out-of-range values instead of clamping. |
| integer ≥200 |
| Per-result text budget; longer bodies end with |
Returns a numbered list of title / url / text. Raised to 10 results with the default budget, a
response is around 15k characters — comfortable for any modern context window.
web_fetch
Argument | Type | Default | Notes |
| string | — | Required. Absolute |
| integer ≥500 |
| Total text budget for the page. |
Returns the page title, its text, and up to 40 outgoing links. Handles HTML pages and PDFs.
Both tools are read-only and annotated as such (readOnlyHint, openWorldHint), so clients can
auto-approve them without a permission prompt.
Typical agent loop
1. web_search("latest release notes for <project>") → finds the right page
2. web_fetch("<that url>") → reads it in full
3. answer, citing the URLSearch results already carry page text, so step 2 is only needed when the snippet is truncated.
Troubleshooting
Intermittent could not reach …: SELF_SIGNED_CERT_IN_CHAIN
This is the most likely failure you will hit, and it is not a bug in this server. If a TLS-inspecting antivirus or corporate proxy (Kaspersky, Zscaler, Netskope, …) is installed, it re-signs some outbound handshakes with its own root. Browsers and curl trust that root because they use the OS store; Node uses its own bundled list, so a fraction of requests fail while the rest succeed — typically 30–70%, which looks exactly like flaky networking.
Check it:
echo | openssl s_client -connect ollama.com:443 2>/dev/null | grep -E '^ *[0-9]+ s:'
# a chain mentioning Kaspersky / Zscaler / your AV = interceptedFixes, applied automatically where possible:
server.mjsmerges the OS trust store into the process at startup (tls.getCACertificates('system')→setDefaultCACertificates). Node 24+. Nothing to configure.Add
--use-system-cato the command in your client config (Node 22.9+). Belt and braces.Requests already retry
attemptstimes on connection-level errors.
Measured on an affected machine: 3/10 success before, 12/12 after. If you still see it, raise
attempts, and check that your client config still passes --use-system-ca after upgrades.
Do not "fix" this with NODE_TLS_REJECT_UNAUTHORIZED=0 — that disables certificate
verification entirely instead of trusting the right root.
no Ollama API key configured
The key is not reaching the process. Environment variables set in one shell do not propagate into
GUI-launched clients, so put the key in the config file, or in the env block of your client's
MCP config. Run with --config /path/to/config.json to be explicit.
Ollama rejected the credentials (HTTP 401)
Key revoked or wrong. Note that Ollama has separate keys for the cloud API and for local
ollama serve; you want one from https://ollama.com/settings/keys.
The tool never appears in the client
Check the client's own MCP status (claude mcp list, codex mcp get <name>, or the client's log).
Most clients bind their tool list when a session starts, so an already-open session will not see a
newly added server — start a new one. Claude Code's default local scope is per-directory; use
--scope user.
Server shows as failed on Windows
Use the absolute path to node.exe, and if you go through a .cmd shim, quote it. Paths with
spaces must be quoted in the client config.
Development
npm test # 30 unit tests, offline, ~5s (stub upstream, no network)
npm run test:live # 4 tests against the real API, needs OLLAMA_API_KEYThe unit suite spawns the real server as a child process and talks JSON-RPC to it over stdio, with a local HTTP stub standing in for ollama.com — so it covers the actual protocol surface, not just internal functions. It asserts the wire format, argument clamping, truncation, retry policy (non-retriable 4xx is called once; 5xx is retried), config precedence, and that no failure path kills the process.
Layout:
server.mjs the whole server
config.example.json copy to config.json (gitignored)
test/harness.mjs stdio client + HTTP stub
test/server.test.js offline suite
test/live.test.js network suiteReleasing
Publishing is automated: pushing a v* tag runs the offline suite, publishes to npm, and cuts a
GitHub Release. .github/workflows/release.yml does all three, and it refuses to publish when
the tag and the package.json version disagree.
npm version 1.0.1 --no-git-tag-version # or edit package.json by hand
git commit -am "chore: release 1.0.1"
git push
git tag v1.0.1 && git push origin v1.0.1Auth is npm Trusted Publishing, so there is no
NPM_TOKEN secret and no long-lived token; provenance is attached automatically. Configure it
once on npmjs.com under the package's Settings → Trusted Publisher → GitHub Actions:
Field | Value |
Organization or user |
|
Repository |
|
Workflow filename |
|
Allowed actions | must include |
That last row is the one that bites: the default only allows npm stage publish, so a release
fails until npm publish is ticked.
Notes and limitations
No search quota is documented. In testing, serial and parallel bursts (4 at once, 2×3 rounds) all returned 200 with identical bodies. Treat it as unmetered but unguaranteed; your account's limits, if any, apply.
Upstream shapes are used as-is.
web_searchexpects{results:[{title,url,content}]};web_fetchexpects{title,content,links}. If Ollama changes these, the tools return a clear error rather than silently degrading.No image/video/news verticals, no domain filters, no date ranges — Ollama's endpoints do not offer them.
web_searchresults are general web pages.web_fetchis a plain fetch. Pages behind a login or heavy bot protection will not render.Not an MCP "search provider" in the host-app sense: some clients (Claude Desktop, ChatGPT) only accept their own first-party search. This works in clients that let you add MCP servers.
License
MIT — see LICENSE.
中文说明见 README.zh-CN.md。
Available Tools
2 toolsweb_fetchWeb fetchARead-only
Fetch one URL and return its readable text plus outgoing links. Use it to read a page found by web_search.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Absolute http(s) URL to fetch. | |
| max_chars | No | Total text budget in characters (default 20000). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety and network-access profile is covered. The description adds that it returns readable text plus outgoing links, which annotations do not convey, but it says nothing about truncation, non-HTML content, or failure behavior for unreachable URLs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the purpose and the return value front-loaded before the usage pointer. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so at a high level (readable text plus outgoing links). Parameter edge cases are covered by the schema, but failure modes for unreachable or non-HTML URLs remain unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with url and max_chars each documented inline including the default and minimum, so the baseline is 3. The description adds no additional meaning about the parameters, which is acceptable given the schema does the work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (fetch), a specific resource (one URL), and the return payload (readable text plus outgoing links). The second sentence routes it against the sibling web_search by casting web_fetch as the read step for a page search discovered, so an agent can separate the two without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to reach for it ('read a page found by web_search') and effectively names the alternative tool. It stops short of stating exclusions, e.g. that it should not be used for discovery/searching or that non-HTML or blocked pages may fail.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
web_searchWeb searchARead-only
Search the live web and get ranked results with title, URL and page text. Use it for current facts, news, releases, pricing or anything past the model cutoff.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Natural-language search query. | |
| max_chars | No | Per-result text budget in characters (default 1500). | |
| max_results | No | How many results to return (default 5). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so safety and external-data traits are covered. The description adds the return composition (title, URL, page text) and the ranking behavior, which is genuinely useful given there is no output schema, but it says nothing about rate limits, result freshness/latency, or failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences: capability first, usage triggers second. No filler, no restatement of the name or title, and the most decision-relevant content is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter read-only tool with a fully documented schema, the description covers purpose, usage triggers and return shape, which compensates for the absent output schema. The main gap is not relating it to the sibling web_fetch, leaving the search-vs-fetch choice implicit.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so query, max_chars and max_results are all documented in the schema itself. The description adds no syntax, default, or formatting detail beyond that, which is the expected baseline when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (search) and resource (the live web), plus the shape of what comes back (ranked results with title, URL and page text). It is clearly distinguishable from web_fetch in spirit, but never names the sibling or explicitly contrasts searching vs. fetching a known URL.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use it for current facts, news, releases, pricing or anything past the model cutoff" gives concrete trigger conditions. It provides no when-not guidance and never points to web_fetch as the alternative for retrieving a specific page, so the routing decision is only half covered.
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.
2 tool updates
v1.0.0- First observed
web_fetch - First observed
web_search
TDQS
Scored across 2 tools
web_search and web_fetch have clearly distinct, complementary roles: one discovers ranked results, the other reads a single page. The descriptions explicitly frame fetch as the follow-up to search, so an agent can never confuse them.
Both tools follow the same snake_case verb_noun pattern with a shared 'web_' prefix (web_search, web_fetch). The convention is perfectly predictable.
Two tools is thin for a standalone server, though each earns its place as a search/read pair. It sits at the borderline of the rubric's 1-2 tool range rather than being clearly well-scoped.
For the stated purpose of live web access, the discover-then-read loop is fully covered with no dead ends. There is no obvious missing operation an agent would need for this domain.
Maintenance
Related MCP Connectors
Docs: https://docs.keenable.ai/mcp-server Keenable is a free, remote MCP server that gives agents access to the web index. Search the web with ranked results and date/site filters, then fetch any indexed page as clean markdown. Works out of the box with no account or API key.
Scrape, crawl and search the web for AI agents via MCP.
MCP server for Firecrawl — web search, scraping, and biomedical/arXiv paper search.
Free web search for AI agents. No API key required. Hosted MCP in active development.
Related MCP Servers
- AlicenseAqualityBmaintenanceA local-first, no-API-key MCP server that enables LLMs to search the web, fetch pages, and read documents using multiple engines and smart fallbacks.11138MIT
- AlicenseAqualityCmaintenanceMCP server for private web search via self-hosted SearXNG with local reranking, full-page content fetching via Firecrawl, and optional Ollama-powered query expansion and summaries.7222 npm25MIT
- AlicenseAqualityDmaintenanceWeb search, page fetching, and research from the terminal or any MCP client — no API key required.31MIT
- AlicenseAqualityCmaintenanceGives MCP-capable agents live web access: search the web, scrape pages into Markdown (including JavaScript-heavy and bot-protected sites), and extract named fields as JSON, with job polling, token-aware content offloading, and built-in research guidance. Ships as a self-hostable stdio or HTTP service with spend caps and per-request key support.7MIT