A lightweight Rust MCP server for Grok / OpenAI‑compatible web search, plus Tavily fetch/map and Firecrawl fallback.
grok-search-rs is an MCP stdio server — your client (Claude Code, Codex, Cursor, VS Code, …) launches it; you do not run it directly. It exposes one set of tools (web_search, get_sources, web_fetch, web_map, doctor) and supports two upstream transports so you can plug into either xAI's official API or any OpenAI‑compatible relay.
- 🔎 Live web search with cited sources, cached for follow‑up
get_sourcescalls. Opt‑ininclude_contentenriches the top sources with full extracted text in one call. - 🧩 Structured
web_fetch— GitHub issues/PRs, StackExchange/MathOverflow, arXiv, and Wikipedia URLs are parsed by specialist extractors into clean Markdown (title, state/labels, accepted‑answer ordering, abstracts, vote‑sorted answers). Anything else falls back to the generic Tavily → Firecrawl chain. Output carriessource_typeand afallback_reasonwhen a specialist was skipped. - 🔀 Two transports — native xAI Responses (
/v1/responses) or any OpenAI‑compatible chat‑completions gateway (/v1/chat/completions). Pick by env vars; no flag. - 🔐 Optional Grok OAuth mode —
login/status/logoutcommands store a local xAI OAuth token for Responses auth, so the MCP server can run withoutGROK_SEARCH_API_KEY. - 📥 Tavily fetch / map for full‑text extraction and link discovery, with Firecrawl as automatic fallback.
- 🐦 Optional X/Twitter search via
x_search(Responses transport only). - 🩺
doctor— connectivity probe + redacted config in one tool call. - 🗂 Single global config file so multiple MCP clients share one set of keys.
npm install -g grok-search-rsThe npm package ships a native Rust binary; the grok-search-rs command is what your MCP client launches.
-
After
npm install -g grok-search-rs, add this MCP server entry to your client config:{ "grok-search-rs": { "command": "grok-search-rs", "args": [], "env": { "GROK_SEARCH_API_KEY": "", "GROK_SEARCH_URL": "", "GROK_SEARCH_MODEL": "grok-4.20-fast", "TAVILY_API_KEY": "", "TAVILY_API_URL": "https://api.tavily.com", "FIRECRAWL_API_KEY": "" } } }For Codex TOML config:
[mcp_servers.grok-search-rs] type = "stdio" command = "grok-search-rs" [mcp_servers.grok-search-rs.env] FIRECRAWL_API_KEY = "" GROK_SEARCH_API_KEY = "" GROK_SEARCH_MODEL = "grok-4.20-fast" GROK_SEARCH_URL = "" TAVILY_API_KEY = "" TAVILY_API_URL = "https://api.tavily.com"
Put your real keys in the empty values. If your client expects a top-level
mcpServers/mcp_serversobject, place thegrok-search-rsentry under that section. -
Optional: scaffold a shared global config file instead of duplicating env blocks in every MCP client:
grok-search-rs --init $EDITOR ~/.config/grok-search-rs/config.toml
-
Verify:
Ask your assistant: "call doctor"Successful output shows
reachable: truefor each enabled upstream andtransport: Responses(orChatCompletions).
Pick one transport group. Both Tavily and Firecrawl keys are shared across transports.
| Variable | Default | Purpose |
|---|---|---|
GROK_SEARCH_AUTH_MODE |
api_key |
api_key uses GROK_SEARCH_API_KEY; oauth uses the local token from grok-search-rs login. |
GROK_SEARCH_API_KEY |
— (required in api_key mode) |
Bearer token for the Grok / xAI gateway. |
GROK_SEARCH_AUTH_FILE |
<home>/.config/grok-search-rs/auth.json |
Optional OAuth token file override. |
GROK_SEARCH_URL |
https://api.x.ai |
Root, /v1, or full‑endpoint URL. |
GROK_SEARCH_MODEL |
grok-4-1-fast-reasoning |
Model name. |
GROK_SEARCH_WEB_SEARCH |
true |
Offer web_search tool to Grok. |
GROK_SEARCH_X_SEARCH |
false |
Offer x_search tool (X/Twitter) to Grok. |
Verified upstreams: xAI (https://api.x.ai, both tools), Modelverse (https://api.modelverse.cn, x_search depends on relay).
OAuth mode is a single-binary flow:
grok-search-rs login
grok-search-rs status
grok-search-rs logoutThen configure your MCP client with:
[mcp_servers.grok-search-rs]
command = "grok-search-rs"
[mcp_servers.grok-search-rs.env]
GROK_SEARCH_AUTH_MODE = "oauth"
GROK_SEARCH_MODEL = "grok-4.3"
GROK_SEARCH_WEB_SEARCH = "true"OAuth mode reuses Hermes' xAI OAuth client id and stores auth.json locally. That may violate xAI terms or affect your account; do not share the token file. If xAI changes or blocks that OAuth flow, switch back to api_key mode.
Activate by setting the URL and key while leaving GROK_SEARCH_API_KEY unset. Suitable for any OpenAI‑compatible relay (one‑api, vLLM, LiteLLM, marybrown, Perplexity‑style gateways, etc.).
| Variable | Default | Purpose |
|---|---|---|
OPENAI_COMPATIBLE_API_URL |
— | Root, /v1, or full‑endpoint URL. |
OPENAI_COMPATIBLE_API_KEY |
— | Bearer token for the relay. |
OPENAI_COMPATIBLE_MODEL |
falls back to GROK_SEARCH_MODEL |
Model name to send. |
Notes:
GROK_SEARCH_WEB_SEARCH=true(default) appendstools:[{"type":"web_search"}]to the payload. Relays that auto‑search server‑side simply ignore it.GROK_SEARCH_X_SEARCH=trueis silently ignored on this transport (a one‑line stderr warning prints at startup).x_searchonly exists on the Responses API.- Source extraction reads four parallel paths and de‑duplicates by URL: OpenAI
annotations[].url_citation, Perplexity‑stylecitations, top‑levelsearch_sources[], and inline[[n]](url)markers.
| Variable | Default | Purpose |
|---|---|---|
TAVILY_API_KEY |
— (required for web_fetch / web_map) |
Tavily key. |
TAVILY_API_URL |
https://api.tavily.com |
Tavily base. |
GROK_SEARCH_EXTRA_SOURCES |
3 |
Extra Tavily sources after a Grok answer (0 disables). |
GROK_SEARCH_FALLBACK_SOURCES |
5 |
Fallback source count when the AI step can't verify itself. |
FIRECRAWL_API_KEY |
unset | Enables Firecrawl as web_fetch / source fallback. |
FIRECRAWL_API_URL |
https://api.firecrawl.dev |
Firecrawl base. |
GROK_SEARCH_CACHE_SIZE |
256 |
Max cached web_search sessions. |
GROK_SEARCH_TIMEOUT_SECONDS |
60 |
HTTP timeout for all upstreams. |
GROK_SEARCH_FETCH_MAX_CHARS |
unset | Default char cap on web_fetch. |
| Variable | Default | Purpose |
|---|---|---|
GITHUB_TOKEN |
unset | Authenticates GitHub issue/PR fetches (higher API rate limit; private repos). Specialist works unauthenticated but is rate‑limited. |
GROK_SEARCH_SOURCE_MAX_ANSWERS |
5 |
StackExchange answers rendered before folding. |
GROK_SEARCH_SOURCE_MAX_COMMENTS |
30 |
GitHub / StackExchange comments rendered before folding. |
GROK_SEARCH_ENRICH_CONCURRENCY |
3 |
Parallel source enrichments for web_search include_content (clamped 1..5). |
GROK_SEARCH_ENRICH_MAX_CHARS |
15000 |
Char cap per enriched source body. |
These specialists need no Tavily/Firecrawl key — they hit the public GitHub, StackExchange, arXiv, and Wikipedia APIs directly. Tavily/Firecrawl are only used for the generic fallback path.
- If
GROK_SEARCH_AUTH_MODE=oauth→ Responses transport with the local OAuth token. - Else if
GROK_SEARCH_API_KEYis set → Responses transport with a static Bearer key. - Else if both
OPENAI_COMPATIBLE_API_URLandOPENAI_COMPATIBLE_API_KEYare set → ChatCompletions transport. - Else → server fails with a clear
MissingConfigerror.
Tired of duplicating env blocks across clients? Run grok-search-rs --init once to scaffold <home>/.config/grok-search-rs/config.toml, fill in your keys, and every client can shrink to {"command": "grok-search-rs"}.
| Path order | Location |
|---|---|
| 1 | $GROK_SEARCH_CONFIG (explicit override, any platform) |
| 2 | $HOME/.config/grok-search-rs/config.toml (Unix / macOS / Git Bash) |
| 3 | %USERPROFILE%\.config\grok-search-rs\config.toml (native Windows) |
Precedence: per‑client env > config file > built‑in defaults. File keys are lowercase snake_case (env GROK_SEARCH_MODEL → file grok_model). Unknown keys are rejected. Full reference: docs/CONFIGURATION.md.
| Tool | When to call it |
|---|---|
web_search |
Sourced summary for a topic. Sources cached for follow‑up. Set include_content: true to inline full source text. |
get_sources |
Re‑fetch sources of a previous web_search by session_id. |
web_fetch |
Page content as clean Markdown. Specialist extractors for GitHub / StackExchange / arXiv / Wikipedia; generic Tavily → Firecrawl fallback otherwise. Returns source_type + fallback_reason. |
web_map |
Discover URLs on a domain via Tavily Map. |
doctor |
Live connectivity probe + redacted config. Run first when something looks off. |
git clone https://github.com/Episkey-G/GrokSearch-rs.git
cd GrokSearch-rs
cargo build --releaseThe binary lands at target/release/grok-search-rs. Point your MCP client's command at the absolute path.
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo testMore docs:
- Inspired by GuDaStudio/GrokSearch — the original Python implementation that pioneered the Grok + Tavily + Firecrawl combo this project rewrites in Rust.
- Thanks to the LinuxDo community for the discussions, feedback, and the prior art that inspired this rewrite.
MIT — see LICENSE.
