grok-web-search-mcp
by NakanoSanku
README.md
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
<a id="readme-top"></a>
<!-- LANGUAGE -->
**Language:** English | [中文](./README_CN.md)
<!-- PROJECT SHIELDS -->
[![Python][python-shield]][python-url]
[![License: MIT][license-shield]][license-url]
[![MCP][mcp-shield]][mcp-url]
[![xAI][xai-shield]][xai-web-search-url]
[![GitHub][github-shield]][github-url]
<!-- PROJECT LOGO / TITLE -->
<br />
<div align="center">
<h3 align="center">grok-web-search-mcp</h3>
<p align="center">
A lean FastMCP server that exposes Grok / xAI Responses API
<code>web_search</code> + <code>x_search</code> to any MCP host — with custom <code>base_url</code>
for official API or compatible proxies.
<br />
<br />
<a href="https://github.com/NakanoSanku/grok-web-search-mcp"><strong>Explore the repo »</strong></a>
·
<a href="https://docs.x.ai/developers/tools/web-search"><strong>xAI Web Search docs »</strong></a>
·
<a href="./README_CN.md"><strong>中文文档 »</strong></a>
</p>
</div>
<!-- TABLE OF CONTENTS -->
<details>
<summary>Table of Contents</summary>
<ol>
<li>
<a href="#about-the-project">About The Project</a>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#built-with">Built With</a></li>
</ul>
</li>
<li>
<a href="#getting-started">Getting Started</a>
<ul>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#quick-start-uvx-from-github">Quick start (uvx from GitHub)</a></li>
<li><a href="#local-development-install">Local development install</a></li>
</ul>
</li>
<li><a href="#configuration">Configuration</a></li>
<li>
<a href="#usage">Usage</a>
<ul>
<li><a href="#run-the-server">Run the Server</a></li>
<li><a href="#mcp-host-config">MCP Host Config</a></li>
<li><a href="#tool-web_search">Tool: web_search</a></li>
<li><a href="#response-shape">Response Shape</a></li>
<li><a href="#python-client-example">Python Client Example</a></li>
</ul>
</li>
<li><a href="#development">Development</a></li>
<li><a href="#roadmap">Roadmap</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="#license">License</a></li>
<li><a href="#acknowledgments">Acknowledgments</a></li>
</ol>
</details>
<!-- ABOUT THE PROJECT -->
## About The Project
Agents need live web and X access with citations, not just a chat completion. This project wraps xAI’s server-side [`web_search`](https://docs.x.ai/developers/tools/web-search) and [`x_search`](https://docs.x.ai/developers/tools/x-search) tools as a **single MCP tool**, so hosts like Grok, Cursor, or Claude Desktop can call them without embedding xAI client logic.
**Repository:** [https://github.com/NakanoSanku/grok-web-search-mcp](https://github.com/NakanoSanku/grok-web-search-mcp)
Upstream call (simplified):
```http
POST {base_url}/responses
Authorization: Bearer <api_key>
Content-Type: application/json
{
"model": "grok-4.5",
"input": [{"role": "user", "content": "<query>"}],
"tools": [
{"type": "web_search", "enable_image_understanding": true},
{
"type": "x_search",
"allowed_x_handles": ["xai"],
"from_date": "2025-10-01",
"to_date": "2025-10-10",
"enable_image_understanding": true,
"enable_video_understanding": true
}
]
}
```
Design goals:
- **One MCP tool, one calling contract** — models may only pass `query` / `scope` / `recency` / `images`
- **Lean results** — `query` / `text` / `citations` / `sources_used` (no raw upstream dump)
- **Custom base URL** — official `https://api.x.ai/v1` or OpenAI-compatible proxies
- **Optional vision input** — attach https URLs or data URIs (local paths are opt-in)
- **No PyPI required** — run directly from GitHub with `uvx --from git+...`
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### Features
| Capability | Notes |
| --- | --- |
| Live web search | Grok synthesizes an answer with source URLs |
| Live X search | Included by default; set `scope="web"` or `scope="x"` to restrict |
| X filters | Handle allow/deny lists (max 20, `@` stripped) and inclusive date range |
| Domain filters | Allowlist **or** denylist (max 5, mutually exclusive; scheme/path stripped) |
| Search media understanding | Images on web pages and X posts; videos on X posts |
| Client image input | Optional `images` (https / data URI; local paths opt-in) |
| Lean JSON output | No `model` / `base_url` / annotations / raw payload in tool results |
| Protocol errors | Upstream/validation failures set MCP `isError` (not a fake `ok: false` payload) |
| Retries | 429 / 502 / 503 / 504 and transport timeouts, with backoff |
| Proxy-friendly | `GROK_BASE_URL` / `XAI_BASE_URL` |
| GitHub install | `uvx --from git+https://github.com/NakanoSanku/grok-web-search-mcp.git` |
**Not included:** `enable_image_search` (web image gallery embedding). Use `images` when *you* provide a picture; use `enable_image_understanding` for images on browsed pages and X posts.
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### Built With
* [![Python][python-shield]][python-url]
* [![FastMCP][fastmcp-shield]][fastmcp-url]
* [![httpx][httpx-shield]][httpx-url]
* [![xAI API][xai-shield]][xai-url]
* [![MCP][mcp-shield]][mcp-url]
* [![uv][uv-shield]][uv-url]
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- GETTING STARTED -->
## Getting Started
### Prerequisites
* Python **3.10+**
* An xAI API key (or a key for a compatible gateway)
* [uv](https://docs.astral.sh/uv/) (recommended for `uvx` from GitHub)
```sh
# optional: install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
```
### Quick start (uvx from GitHub)
No local clone required for day-to-day MCP use:
```sh
export GROK_API_KEY=xai-...
uvx --from git+https://github.com/NakanoSanku/grok-web-search-mcp.git grok-web-search-mcp
```
Pin a branch, tag, or commit when you need reproducibility:
```sh
uvx --from git+https://github.com/NakanoSanku/grok-web-search-mcp.git@main grok-web-search-mcp
# uvx --from git+https://github.com/NakanoSanku/grok-web-search-mcp.git@v0.3.0 grok-web-search-mcp
```
### Local development install
1. Clone the repository:
```sh
git clone https://github.com/NakanoSanku/grok-web-search-mcp.git
cd grok-web-search-mcp
```
2. Install dependencies:
```sh
uv sync
# or: pip install -e ".[dev]"
```
3. Create a local env file:
```sh
cp .env.example .env
```
4. Edit `.env` and set at least `GROK_API_KEY` (see [Configuration](#configuration)).
<p align="right">(<a href="#readme-top">back to top</a>)</p>
## Configuration
| Variable | Required | Default | Description |
| --- | --- | --- | --- |
| `GROK_API_KEY` | **Yes** | — | Also accepts `XAI_API_KEY` / `GROK_WEB_SEARCH_API_KEY` |
| `GROK_BASE_URL` | No | `https://api.x.ai/v1` | Also `XAI_BASE_URL` / `GROK_WEB_SEARCH_BASE_URL` |
| `GROK_MODEL` | No | `grok-4.5` | Also `XAI_MODEL` |
| `GROK_TIMEOUT` | No | `300` | Request timeout in seconds (1–3600). High reasoning + search can need minutes |
| `GROK_CONNECT_TIMEOUT` | No | `15` | TCP/TLS connect timeout (capped by `GROK_TIMEOUT`) |
| `GROK_ENABLE_IMAGE_UNDERSTANDING` | No | `true` | Analyze images on browsed pages and X posts |
| `GROK_REASONING_EFFORT` | No | `low` | Default thinking length: `low` / `medium` / `high`; also `XAI_REASONING_EFFORT` |
| `GROK_ALLOW_LOCAL_IMAGES` | No | `false` | Allow `images` to read local files (jailed to cwd / `GROK_LOCAL_IMAGE_ROOT`) |
| `GROK_LOCAL_IMAGE_ROOT` | No | cwd | Directory jail for local images when enabled |
| `GROK_MAX_RETRIES` | No | `3` | Retries for 429/5xx/timeouts (0–8) |
| `GROK_LOG_LEVEL` | No | `INFO` | `DEBUG` / `INFO` / `WARNING` / `ERROR` |
| `GROK_ENABLE_VIDEO_UNDERSTANDING` | No | `false` | Analyze videos in X posts (operator-only; not a tool argument) |
| `GROK_ALLOWED_DOMAINS` | No | — | Operator web allowlist (max 5). Callers cannot set this |
| `GROK_EXCLUDED_DOMAINS` | No | — | Operator web denylist (max 5) |
| `GROK_ALLOWED_X_HANDLES` | No | — | Operator X handle allowlist (max 20) |
| `GROK_EXCLUDED_X_HANDLES` | No | — | Operator X handle denylist (max 20) |
| `GROK_SEARCH_INSTRUCTIONS` | No | — | Extra rules appended to the server-owned system prompt |
> Keep secrets out of git. Prefer host-injected env for MCP configs when possible.
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- USAGE -->
## Usage
### Run the Server
**Recommended (from GitHub):**
```sh
export GROK_API_KEY=xai-...
uvx --from git+https://github.com/NakanoSanku/grok-web-search-mcp.git grok-web-search-mcp
```
**From a local checkout:**
```sh
export GROK_API_KEY=xai-...
# Windows PowerShell: $env:GROK_API_KEY="xai-..."
uv run grok-web-search-mcp
# or
uv run python -m grok_web_search_mcp
```
Compatible proxy example:
```sh
export GROK_API_KEY=sk-xxx
export GROK_BASE_URL=http://127.0.0.1:8317/v1
export GROK_MODEL=grok-4.5
uvx --from git+https://github.com/NakanoSanku/grok-web-search-mcp.git grok-web-search-mcp
```
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### MCP Host Config
**Preferred: run from GitHub with `uvx` (no local path).**
JSON-style hosts (Cursor / Claude Desktop, etc.):
```json
{
"mcpServers": {
"grok-web-search": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/NakanoSanku/grok-web-search-mcp.git",
"grok-web-search-mcp"
],
"env": {
"GROK_API_KEY": "xai-your-key",
"GROK_BASE_URL": "https://api.x.ai/v1",
"GROK_MODEL": "grok-4.5"
}
}
}
}
```
Grok user config (`~/.grok/config.toml`):
```toml
[mcp_servers.grok-web-search]
command = "uvx"
args = [
"--from",
"git+https://github.com/NakanoSanku/grok-web-search-mcp.git",
"grok-web-search-mcp",
]
enabled = true
[mcp_servers.grok-web-search.env]
GROK_API_KEY = "xai-your-key"
GROK_BASE_URL = "https://api.x.ai/v1"
GROK_MODEL = "grok-4.5"
```
Pin a ref (branch / tag / commit):
```toml
args = [
"--from",
"git+https://github.com/NakanoSanku/grok-web-search-mcp.git@main",
"grok-web-search-mcp",
]
```
**Local development only** (absolute path to a checkout):
```toml
[mcp_servers.grok-web-search]
command = "uv"
args = [
"run",
"--directory",
"/absolute/path/to/grok-web-search-mcp",
"grok-web-search-mcp",
]
enabled = true
```
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### Tool: `web_search`
Every host model must use the same four-key contract. Extra arguments (`model`, `reasoning_effort`, `system_prompt`, domain/handle filters) are **rejected**. Quality knobs live in environment variables so search behavior does not drift between models.
| Parameter | Type | Description |
| --- | --- | --- |
| `query` | string | **Required.** A natural-language question, 2–600 characters. Not a keyword list (`xAI Grok valuation`) and not chat history. Keyword bags are rewritten server-side. |
| `scope` | `"all"` \| `"web"` \| `"x"` | Default `all` (web + X). Use `web` for general facts; `x` only for posts/accounts. |
| `recency` | `"any"` \| `"day"` \| `"week"` \| `"month"` \| `"year"` | Default `any`. Set only when the user asked for a time window. |
| `images` | string[]? | Optional picture URLs (http(s) / data URI, max 5). Only if the user provided a picture. |
Canonical example:
```json
{ "query": "What is xAI's latest valuation?" }
```
The server then: normalizes `query`, injects a fixed system prompt, applies operator filters from env, maps `recency` to X date bounds, and always uses the configured model / reasoning effort.
`images` are Responses API `input_image` parts. Local filesystem paths are **disabled by default**. This is **not** “search the web for stock images.”
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### Response Shape
Success (MCP `isError: false`, structured content):
```json
{
"query": "What is xAI?",
"text": "...",
"citations": [{"url": "https://x.ai", "title": "xAI"}],
"sources_used": ["web", "x"],
"scope": "all",
"recency": "any"
}
```
Failure is a protocol-level tool error (`isError: true`) with a short message, for example `Grok API error (401): Invalid API key`. Incomplete or empty upstream responses are also errors, not silent success.
Intentionally **not** returned: API key, `model`, `base_url`, raw upstream JSON, or annotation blobs (URLs are mined into `citations` only). Diagnose config outside the tool result (env / host MCP settings / stderr logs).
<p align="right">(<a href="#readme-top">back to top</a>)</p>
### Python Client Example
```python
import asyncio
from grok_web_search_mcp.client import GrokWebSearchClient
from grok_web_search_mcp.config import Settings
async def main():
async with GrokWebSearchClient(Settings.from_env()) as client:
result = await client.web_search("What is xAI?")
print(result.to_dict())
asyncio.run(main())
```
Real calls consume model + server-side search quota. Unit tests use mocks and do not hit the network.
<p align="right">(<a href="#readme-top">back to top</a>)</p>
## Development
```sh
git clone https://github.com/NakanoSanku/grok-web-search-mcp.git
cd grok-web-search-mcp
uv sync --extra dev
uv run pytest
# live API (optional): GROK_LIVE=1 uv run pytest -m live
```
Project layout:
```text
src/grok_web_search_mcp/
server.py # MCP tool surface
client.py # Responses API client + image helpers
config.py # Environment settings
tests/
```
<p align="right">(<a href="#readme-top">back to top</a>)</p>
## Roadmap
- [x] Single lean `web_search` MCP tool
- [x] Enable upstream `web_search` and `x_search` by default
- [x] X handle/date filters and image/video understanding
- [x] Custom `base_url` / proxy support
- [x] Domain allow/deny filters
- [x] Optional multimodal image input
- [x] Install / run from GitHub via `uvx`
- [x] Protocol-level errors, retries, timeout/reasoning defaults
- [x] Local-image jail (disabled by default)
- [x] Canonical MCP calling contract (`query` / `scope` / `recency` / `images`)
- [ ] Optional Streamable HTTP transport docs/examples
- [ ] Golden-set evaluation harness for search quality
See the [open issues](https://github.com/NakanoSanku/grok-web-search-mcp/issues).
<p align="right">(<a href="#readme-top">back to top</a>)</p>
## Contributing
Contributions are welcome.
1. Fork the project
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
Please keep the tool surface lean: prefer one well-documented tool over many thin wrappers.
<p align="right">(<a href="#readme-top">back to top</a>)</p>
## License
Distributed under the MIT License. See `LICENSE` for more information.
<p align="right">(<a href="#readme-top">back to top</a>)</p>
## Acknowledgments
* [Best-README-Template](https://github.com/othneildrew/Best-README-Template)
* [xAI Web Search](https://docs.x.ai/developers/tools/web-search)
* [xAI X Search](https://docs.x.ai/developers/tools/x-search)
* [Model Context Protocol](https://modelcontextprotocol.io/)
* [FastMCP](https://gofastmcp.com/)
* [uv](https://docs.astral.sh/uv/)
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- MARKDOWN LINKS & IMAGES -->
[python-shield]: https://img.shields.io/badge/Python-3.10%2B-blue?style=for-the-badge&logo=python&logoColor=white
[python-url]: https://www.python.org/
[license-shield]: https://img.shields.io/badge/License-MIT-green?style=for-the-badge
[license-url]: ./LICENSE
[mcp-shield]: https://img.shields.io/badge/MCP-Server-purple?style=for-the-badge
[mcp-url]: https://modelcontextprotocol.io/
[xai-shield]: https://img.shields.io/badge/xAI-Grok-black?style=for-the-badge
[xai-url]: https://docs.x.ai/
[xai-web-search-url]: https://docs.x.ai/developers/tools/web-search
[fastmcp-shield]: https://img.shields.io/badge/FastMCP-3.x-orange?style=for-the-badge
[fastmcp-url]: https://gofastmcp.com/
[httpx-shield]: https://img.shields.io/badge/httpx-async-teal?style=for-the-badge
[httpx-url]: https://www.python-httpx.org/
[uv-shield]: https://img.shields.io/badge/uv-package%20manager-DE5FE9?style=for-the-badge
[uv-url]: https://docs.astral.sh/uv/
[github-shield]: https://img.shields.io/badge/GitHub-NakanoSanku-181717?style=for-the-badge&logo=github
[github-url]: https://github.com/NakanoSanku/grok-web-search-mcp
TDQS
A4.5/5.0
Scored across 1 tool
Disambiguation5/5
Only one tool exists, so there is no possibility of confusion or overlap with other tools.
Naming Consistency5/5
With a single tool, naming consistency is inherently perfect as there is no pattern to break.
Tool Count4/5
A single tool is slightly minimal but reasonably scoped for a focused web search server, as the tool itself is comprehensive.
Completeness5/5
The tool covers web search, X search, image understanding, domain and date filters, and reasoning effort, leaving no obvious gaps for its stated purpose.
Maintenance
ActivitySlowing
ResponsivenessNo issues