Skip to main content
Glama
narimanamiri

MCP Web Fetch Server

by narimanamiri
README.md
# MCP Web Fetch Server

An all-in-one Python MCP server for web research: fetch pages, search the web, batch-fetch, extract links, summarize via client-side sampling, and (optionally) read/write local files — all for LLM agents like Cursor. Supports local **stdio** and **Streamable HTTP** for remote access, and exercises essentially every MCP protocol capability (Tools, Resources, Prompts, Completions, Sampling, Elicitation, Roots, Progress, Logging).

**Documentation:**

| Document | What's inside |
|----------|---------------|
| [User Manual](docs/USER_MANUAL.md) | Full end-user guide: install (Docker/Linux/Windows), Cursor setup, all tools, admin GUI, config, troubleshooting |
| [Project Documentation](docs/PROJECT_DOCUMENTATION.md) | Full technical reference: architecture, modules, MCP APIs, admin API, security, Docker stack, testing |

## Features

### Tools
- `fetch_url` — page content as markdown, with chunked reading (`start_index`, `max_length`)
- `fetch_metadata_tool` — HEAD request metadata
- `batch_fetch` — fetch multiple URLs concurrently, with per-URL error isolation and progress
- `web_search` — DuckDuckGo web search (no API key), with automatic fallback to a local SearXNG instance if DuckDuckGo's scrape fails
- `extract_links` — structured link/image extraction from a page
- `summarize_url` — asks the connected client's LLM to summarize a page (MCP sampling)
- `read_file` / `write_file` / `list_dir` — sandboxed local file access (opt-in, disabled by default)

### Other MCP capabilities
- **Resources**: `config://settings`, `history://recent`, `fetch-cache://{encoded_url}`
- **Prompts**: `fetch`, `research_topic`, `summarize_page`, `extract_key_facts`, `compare_sources`
- **Completions**: URL/depth autocomplete for prompt and resource arguments
- **Elicitation**: `write_file` confirms before overwriting an existing file
- **Roots**: local file tools honor client-exposed directories in addition to `FETCH_LOCAL_FILES_ROOT`
- **Progress notifications**: `batch_fetch` and `summarize_url` report progress as they run
- **Management GUI**: web dashboard at `/admin` for status, config, history, cache, and tools

### Security
- SSRF protection with resolve-then-check and redirect re-validation
- `robots.txt` compliance (override with `ignore_robots_txt=true`)
- Optional domain allowlist
- Local file tools sandboxed to one configured directory, path-traversal safe
- Bearer token auth + rate limiting for HTTP mode
- `/health` endpoint
- Windows `.exe` build (no Python required for end users)

## Quick Start

### Option A: Docker — full stack (recommended for server deployment)

Runs the MCP server, admin GUI, and SearXNG together.

**Linux / macOS:**

```bash
cd mcp-fetch-server
cp .env.docker.example .env          # edit MCP_AUTH_TOKEN
chmod +x scripts/docker-up.sh
./scripts/docker-up.sh
# or: docker compose up -d --build
```

**Windows (PowerShell):**

```powershell
cd "E:\my python projects\MCP\mcp-fetch-server"
copy .env.docker.example .env
.\scripts\docker-up.ps1
```

| Service | URL |
|---------|-----|
| MCP protocol | `http://127.0.0.1:8000/mcp` |
| Admin GUI | `http://127.0.0.1:8000/admin` |
| Health | `http://127.0.0.1:8000/health` |
| SearXNG (search fallback) | `http://127.0.0.1:8080` |

Connect Cursor over HTTP (Bearer token required):

```json
{
  "mcpServers": {
    "web-fetch": {
      "url": "http://127.0.0.1:8000/mcp",
      "headers": { "Authorization": "Bearer your-token-from-env" }
    }
  }
}
```

Local files for `read_file`/`write_file` map to the `./workspace` folder on your host.

### Option B: Windows executable (local / Cursor stdio)

```powershell
cd "E:\my python projects\MCP\mcp-fetch-server"
.\dist\mcp-fetch-server.exe --transport stdio
```

Build the exe yourself: `.\scripts\build_exe.ps1` → outputs `dist\mcp-fetch-server.exe`

### Option C: Python + uv (development)

```powershell
cd "E:\my python projects\MCP\mcp-fetch-server"
uv sync --dev
copy .env.example .env
uv run mcp-fetch-server --transport stdio
```

## Connect to Cursor

See the [User Manual — Connect to Cursor](docs/USER_MANUAL.md#4-connect-to-cursor) for step-by-step instructions.

Minimal `.cursor/mcp.json` using the executable:

```json
{
  "mcpServers": {
    "web-fetch": {
      "command": "E:/my python projects/MCP/mcp-fetch-server/dist/mcp-fetch-server.exe",
      "args": ["--transport", "stdio"],
      "env": { "PYTHONIOENCODING": "utf-8" }
    }
  }
}
```

## Management GUI

A built-in web dashboard lets you monitor and manage the server without using Cursor:

| Mode | URL |
|------|-----|
| **stdio** (Cursor default) | `http://127.0.0.1:8001/admin` |
| **streamable-http** | `http://127.0.0.1:8000/admin` (same port as MCP) |

The dashboard shows uptime, registered tools, redacted configuration, recent fetch
history, cached page previews, and a button to clear history/cache. It auto-refreshes
every 30 seconds.

If `MCP_AUTH_TOKEN` is set, enter it in the dashboard's auth bar (stored in your
browser session only). Disable the GUI with `FETCH_ADMIN_ENABLED=false`.

## Optional: SearXNG search fallback

`web_search` uses DuckDuckGo by default and falls back to SearXNG when that scrape fails.
When you use **Docker** (`docker compose up`), SearXNG is started automatically and the MCP
container is preconfigured to reach it at `http://searxng:8080`.

For **local** (non-Docker) use, you can still run only SearXNG:

```powershell
docker compose up -d searxng
```

Set `FETCH_SEARXNG_URL=http://localhost:8080` in `.env`. See `searxng/settings.yml`.

## Remote HTTP Mode (without full Docker stack)

```powershell
$env:MCP_AUTH_TOKEN = "your-long-random-token"
.\dist\mcp-fetch-server.exe --transport streamable-http --host 127.0.0.1 --port 8000
```

- MCP endpoint: `http://127.0.0.1:8000/mcp`
- Health check: `http://127.0.0.1:8000/health`

## Project Structure

```
mcp-fetch-server/
├── dist/mcp-fetch-server.exe   # Windows executable
├── src/mcp_fetch_server/       # Source code (tools, resources, prompts, security, ...)
├── tests/                      # 84 pytest tests
├── docs/                       # User manual + technical docs
├── scripts/docker-up.sh        # Linux/macOS stack startup
├── scripts/docker-up.ps1       # Windows stack startup
├── docker-compose.yml          # Full stack: MCP server + SearXNG
├── .env.docker.example         # Environment template for Docker Compose
├── Dockerfile                  # MCP server image
├── workspace/                  # Host folder mounted for local file tools (Docker)
├── searxng/settings.yml        # SearXNG config (JSON API enabled)
├── src/mcp_fetch_server/admin.py  # Management web GUI
└── .cursor/mcp.json            # Cursor config
```

## Development

```bash
uv run pytest
uv run ruff check .
./scripts/docker-up.sh          # Linux / macOS
docker compose down
```

Windows only:

```powershell
.\scripts\build_exe.ps1
.\scripts\docker-up.ps1
```

## License

MIT

TDQS

A3.9/5.0

Scored across 9 tools

Disambiguation5/5

Each tool has a distinct purpose: fetching URLs (single, batch, metadata, links, search, summarize) and local file operations (list, read, write). No overlap between tools.

Naming Consistency4/5

Most tools follow a verb_noun pattern in snake_case. The one minor inconsistency is 'fetch_metadata_tool' which includes an unnecessary 'tool' suffix, but otherwise naming is consistent.

Tool Count5/5

9 tools is appropriate for a web fetch server that also includes local file capabilities. Not too many, not too few.

Completeness4/5

Web fetching covers single, batch, metadata, links, search, and summarize. Local file ops cover list, read, write but lack a delete tool. Minor gap.

Maintenance

ActivitySlowing
ResponsivenessNo issues