Skip to main content
Glama
am24check

mcp-web-search-server

by am24check
README.md
# mcp-web-search-server

Standalone MCP server exposing `web_search` and `web_fetch` tools over HTTP. Any MCP client (Claude Desktop, Cursor, etc.) can connect to it to perform web searches and fetch web pages.

Works out of the box with no API keys: `web_search`/`web_search_exa` use Exa's public MCP endpoint, which accepts unauthenticated (rate-limited) requests. Set `EXA_API_KEY` for higher limits, or `PARALLEL_API_KEY` to also enable `web_search_parallel`.

## Tools

| Tool | Description |
|------|-------------|
| `web_fetch` | Fetch and parse a URL, returning markdown or plain text |
| `web_search` | Search the web using Exa or Parallel AI (auto-selected per session) |
| `web_search_exa` | Search using Exa AI specifically |
| `web_search_parallel` | Search using Parallel AI specifically |

**How it works:**

```
┌──────────────┐     MCP HTTP      ┌──────────────────────────┐     HTTP      ┌──────────────┐
│  MCP Client   │ ◄───────────────► │  web-search-server        │ ◄──────────► │  Exa AI      │
│  (Claude,     │     Streamable    │  :9877/mcp               │               │  mcp.exa.ai  │
│   Cursor,     │                   │                          │               └──────────────┘
│   etc.)       │                   │  Tools:                  │
└──────────────┘                   │  • web_search (auto)     │ ── HTTP ──► ┌──────────────┐
                                   │  • web_search_exa        │               │  Parallel    │
                                   │  • web_search_parallel   │               │  search.     │
                                   │  • web_fetch             │               │  parallel.ai │
                                   └──────────────────────────┘               └──────────────┘
```

**Key design decisions:**

1. **Plain async/await** — no framework, just native `fetch` (Bun's fetch)
2. **Streamable HTTP transport** — uses `WebStandardStreamableHTTPServerTransport` from `@modelcontextprotocol/sdk`, with one session (transport + server instance) per connecting client
3. **Provider selection** — hash of session ID picks exa/parallel for the generic `web_search` tool, with env var overrides
4. **HTML→Markdown** — uses turndown for markdown extraction, a small state-machine for plain text extraction

## Quick Start

```bash
# Install dependencies
bun install

# Run the server (default port 9877)
bun run src/index.ts
```

## Configuration

Environment variables:

| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `9877` | HTTP port to listen on |
| `HOSTNAME` | `0.0.0.0` | Host to bind to |
| `EXA_API_KEY` | - | Optional Exa API key, for higher rate limits |
| `PARALLEL_API_KEY` | - | Parallel API key (enables `web_search_parallel`) |
| `ENABLE_EXA` | `1` (on by default) | Set to `0` to disable `web_search_exa` |
| `ENABLE_PARALLEL` | inferred from `PARALLEL_API_KEY` | Force-enable Parallel without a key |
| `WEBSEARCH_PROVIDER` | hash-based | Force `exa` or `parallel` for `web_search` |

## Connect from an MCP client

Point your MCP client to: `http://localhost:9877/mcp`

This is a standard MCP server using the Streamable HTTP transport.

## Usage Examples

### Fetch a webpage as markdown
```json
{
  "name": "web_fetch",
  "arguments": {
    "url": "https://example.com",
    "format": "markdown"
  }
}
```

### Search the web
```json
{
  "name": "web_search",
  "arguments": {
    "query": "latest JavaScript features 2026",
    "numResults": 5
  }
}
```

## Development

```bash
# Type check
bunx tsc --noEmit

# Run in foreground
bun run src/index.ts
```

## Architecture

- `web_search` proxies to [Exa](https://exa.ai) and [Parallel](https://parallel.ai) MCP endpoints
- `web_fetch` uses native fetch with HTML→Markdown conversion (turndown) and Cloudflare fallback
- The MCP server uses `@modelcontextprotocol/sdk` with `WebStandardStreamableHTTPServerTransport`
- Listens on standard HTTP (no stdio required)

The `web_search`/`web_fetch` tool implementations were originally adapted from [OpenCode](https://github.com/sst/opencode)'s built-in web search tool, then extracted into this standalone server.

## License

[MIT](./LICENSE)

Maintenance

ActivityMaintained
ResponsivenessNo issues