Skip to main content
Glama
NakanoSanku

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