Skip to main content
Glama
README.md
# Web MCP Server

A privacy-focused web search MCP (Model Context Protocol) server that provides web search and content extraction capabilities. Uses **SearxNG** as the primary search engine with **Google scraping** as a fallback.

## Features

- **Web Search** - Search the web with category filters (general, news, images, videos, science, files)
- **Content Extraction** - Extract readable content from URLs as markdown
- **Search Suggestions** - Get query suggestions for better searches
- **Privacy-Focused** - Uses SearxNG metasearch engine
- **Fallback Support** - Automatically falls back to Google scraping if SearxNG is unavailable
- **Relevance Ranking** - Query-aware reranking, deduplication, and low-signal filtering
- **Security-Aware Search** - CVE/security queries prioritize trusted advisory sources
- **Rate Limiting** - Built-in rate limiting to prevent abuse
- **Docker Ready** - Single-container deployment with SearxNG included

## Tools Provided

| Tool | Description |
|------|-------------|
| `web_search` | Search the web with query, category, and limit options |
| `fetch_content` | Extract and convert webpage content to markdown |
| `get_suggestions` | Get search query suggestions |

## Installation

### Option 1: Docker (Recommended)

```bash
# Build the image
docker build -t web-mcp:latest .

# Run the container
docker run --rm -i web-mcp:latest
```

### Option 2: Python Package

```bash
# Clone the repository
git clone https://github.com/your-org/web-mcp.git
cd web-mcp

# Install dependencies
pip install -r requirements.txt
pip install -r requirements-dev.txt  # Optional: tests, lint, type checks

# Or install as package
pip install -e .

# Run the server
python -m web_mcp.server
```

### Option 3: With External SearxNG

If you have an existing SearxNG instance:

```bash
# Set the SearxNG URL
export SEARXNG_URL=http://your-searxng-instance:8080

# Run the MCP server
python -m web_mcp.server
```

## Configuration

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `SEARXNG_URL` | `http://localhost:8080` | SearxNG server URL |
| `SEARXNG_TIMEOUT` | `10` | Request timeout in seconds |
| `SEARCH_ENGINE_PROFILE_MODE` | `auto` | Query-aware engine profile mode (`auto` or `off`) |
| `SEARCH_SECURITY_ENGINES` | `brave,bing,duckduckgo,wikipedia,github,stackoverflow` | Engines used for security/CVE queries |
| `SEARCH_GENERAL_ENGINES` | `` | Engines for general queries (empty = SearxNG defaults) |
| `SEARCH_CANDIDATE_MULTIPLIER` | `5` | Candidate expansion before reranking |
| `SEARCH_MAX_CANDIDATES` | `30` | Maximum candidates before reranking |
| `SEARCH_MIN_QUALITY_SCORE` | `2.5` | Security-query quality threshold for fallback merge |
| `FALLBACK_ENABLED` | `true` | Enable Google scraping fallback |
| `RATE_LIMIT_REQUESTS` | `30` | Max requests per period |
| `RATE_LIMIT_PERIOD` | `60` | Rate limit period in seconds |
| `MAX_CONTENT_LENGTH` | `10000` | Max characters in fetched content |
| `FETCH_ALLOW_PRIVATE_NETWORK` | `false` | Allow fetching localhost/private network URLs |
| `DEFAULT_SEARCH_LIMIT` | `5` | Default number of search results |
| `LOG_LEVEL` | `INFO` | Logging level (DEBUG, INFO, WARNING, ERROR) |
| `JSON_LOGS` | `false` | Output logs in JSON format |

### Configuration File

Create a `.env` file in the project root:

```env
SEARXNG_URL=http://localhost:8080
SEARXNG_TIMEOUT=10
SEARCH_ENGINE_PROFILE_MODE=auto
SEARCH_SECURITY_ENGINES=brave,bing,duckduckgo,wikipedia,github,stackoverflow
SEARCH_GENERAL_ENGINES=
SEARCH_CANDIDATE_MULTIPLIER=5
SEARCH_MAX_CANDIDATES=30
SEARCH_MIN_QUALITY_SCORE=2.5
FALLBACK_ENABLED=true
RATE_LIMIT_REQUESTS=30
RATE_LIMIT_PERIOD=60
MAX_CONTENT_LENGTH=10000
FETCH_ALLOW_PRIVATE_NETWORK=false
DEFAULT_SEARCH_LIMIT=5
LOG_LEVEL=INFO
JSON_LOGS=false
```

## Usage with MCP Clients

### Claude Desktop

Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "web-mcp": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "web-mcp:latest"]
    }
  }
}
```

Or with Python:

```json
{
  "mcpServers": {
    "web-mcp": {
      "command": "python",
      "args": ["-m", "web_mcp.server"],
      "env": {
        "SEARXNG_URL": "http://localhost:8080"
      }
    }
  }
}
```

### Other MCP Clients

The server uses stdio transport, making it compatible with any MCP-compatible client.

## Tool Reference

### web_search

Search the web for information.

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | The search query |
| `category` | string | No | Search category: `general`, `images`, `videos`, `news`, `science`, `files` |
| `limit` | integer | No | Maximum results (default: 5, min: 1, max: 10) |

**Example:**

```json
{
  "name": "web_search",
  "arguments": {
    "query": "Python async programming",
    "category": "general",
    "limit": 5
  }
}
```

**Response:**

```markdown
# Search Results for: Python async programming

*Provider: searxng | 5 results*

---

## 1. Async IO in Python: A Complete Guide
**URL:** https://realpython.com/async-io-python/

Complete guide to async programming in Python...

## 2. Python asyncio Documentation
**URL:** https://docs.python.org/3/library/asyncio.html

Official Python asyncio documentation...
```

### fetch_content

Extract readable content from a URL.
By default, only public `http`/`https` targets are allowed (`FETCH_ALLOW_PRIVATE_NETWORK=false`).

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | The URL to fetch content from |
| `max_length` | integer | No | Maximum content length (default: 10000, min: 500, max: 20000) |

**Example:**

```json
{
  "name": "fetch_content",
  "arguments": {
    "url": "https://example.com/article",
    "max_length": 5000
  }
}
```

**Response:**

```markdown
# Article Title

> Brief description of the article

**Author:** John Doe
**Source:** example.com
**URL:** https://example.com/article

---

[Article content in markdown format...]
```

### get_suggestions

Get search query suggestions.

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | The partial search query |

**Example:**

```json
{
  "name": "get_suggestions",
  "arguments": {
    "query": "python asyn"
  }
}
```

**Response:**

```markdown
# Suggestions for: python asyn

1. python async await
2. python asyncio tutorial
3. python async http requests
4. python async context manager
5. python asyncio vs threading
```

## Development

### Setup

```bash
# Create virtual environment
python -m venv venv
source venv/bin/activate

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run linting
ruff check src tests

# Run type checking
mypy src
```

### Manual MCP Smoke Test (Container + stdio)

Use this to verify the real MCP integration path used by CLI agents.

`test.py` starts the containerized MCP server as a child process with:
`docker run --rm -i web-mcp:latest`
and validates `initialize`, `list_tools`, and `call_tool` flows.

The container contract is stdio-only. Detached mode (`docker run -d ...`) is intentionally not supported for MCP clients.

```bash
# 1) Build image
docker build -t web-mcp:latest .

# 2) Run smoke script from repo root (with your virtualenv active)
.venv/bin/python test.py

# Optional: custom inputs
.venv/bin/python test.py \
  --image web-mcp:latest \
  --query "python asyncio" \
  --suggest-query "python asyn" \
  --content-url "https://example.com" \
  --limit 3 \
  --max-length 800

# Optional: full response blocks
.venv/bin/python test.py --verbose
```

What `test.py` verifies:

- MCP session initialization against containerized server
- Expected tools are registered: `web_search`, `fetch_content`, `get_suggestions`
- Tool calls succeed over MCP stdio transport

Script behavior notes:

- If you pass only one of `--query` or `--suggest-query`, that value is reused for both
- `test.py` prints compact pass/fail summaries by default; use `--verbose` to show full tool outputs
- Use `--docker-command` if your environment uses a different container runtime command

### Project Structure

```
web-mcp/
├── src/web_mcp/
│   ├── __init__.py
│   ├── config.py           # Configuration management
│   ├── server.py           # MCP server entry point
│   ├── search/
│   │   ├── base.py         # SearchResult, SearchResponse, SearchProvider ABC
│   │   ├── searxng.py      # SearxNG provider
│   │   ├── google.py       # Google scraping fallback
│   │   ├── fallback.py     # Fallback orchestration + quality gate
│   │   ├── relevance.py    # Scoring, ranking, dedup, snippet cleaning
│   │   └── provider_registry.py # Shared provider singleton
│   ├── tools/
│   │   ├── web_search.py   # web_search tool
│   │   ├── fetch_content.py # fetch_content tool
│   │   └── suggestions.py  # get_suggestions tool
│   └── utils/
│       ├── logger.py       # Structured logging
│       ├── rate_limiter.py  # Rate limiting
│       ├── content_extractor.py # HTML-to-markdown extraction
│       └── validation.py   # Shared input validation
├── tests/                  # Test suite
├── docker/                 # Docker configuration
│   ├── searxng/           # SearxNG settings
│   └── entrypoint.sh      # Container entrypoint
├── Dockerfile             # Single-container Docker build
├── pyproject.toml         # Python project config
├── requirements.txt       # Runtime dependencies
└── requirements-dev.txt   # Test/lint/type dependencies
```

## Troubleshooting

### Common Issues

**1. SearxNG Connection Refused**

```
Error: Failed to connect to SearxNG
```

- Ensure SearxNG is running: `curl http://localhost:8080/config`
- Check `SEARXNG_URL` environment variable
- If using Docker via MCP stdio, ensure the image is current (`docker build -t web-mcp:latest .`)

**2. Google Rate Limiting**

```
Error: Google rate limit hit (429)
```

- Reduce request frequency
- SearxNG should be used as primary; Google is fallback only
- Wait a few minutes before retrying

**3. Content Extraction Failed**

```
Error: Failed to extract content from page
```

- The page may use JavaScript rendering (not supported)
- The page may block automated requests
- Try with a different URL

**4. Import Errors**

```
ModuleNotFoundError: No module named 'web_mcp'
```

- Ensure you're in the virtual environment
- Install the package: `pip install -e .`
- Check `PYTHONPATH` includes `src/`

### Debug Mode

Enable debug logging:

```bash
export LOG_LEVEL=DEBUG
python -m web_mcp.server
```

### Docker Debugging

```bash
# Run container interactively
docker run -it --entrypoint /bin/sh web-mcp:latest

# View logs
docker logs <container>
```

## Security Considerations

- **SearxNG Secret**: Change `SEARXNG_SECRET` in production
- **Rate Limiting**: Configure `RATE_LIMIT_REQUESTS` to prevent abuse
- **Network**: Container exposes port 8080 (for debugging only)
- **User Permissions**: Container defaults to root-managed processes; harden users/permissions for production

## License

MIT License - see [LICENSE](LICENSE) for details.

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests: `pytest`
5. Submit a pull request

## Acknowledgments

- [SearxNG](https://github.com/searxng/searxng) - Privacy-respecting metasearch engine
- [MCP](https://modelcontextprotocol.io/) - Model Context Protocol
- [Trafilatura](https://github.com/adbar/trafilatura) - Web content extraction

TDQS

A3.5/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: fetching content from a URL, getting search suggestions, and performing a web search. There is no overlap or ambiguity between them.

Naming Consistency5/5

All tool names use a consistent verb_noun pattern in snake_case: fetch_content, get_suggestions, web_search. The naming is predictable and follows the same convention.

Tool Count5/5

With 3 tools covering search, suggestions, and content retrieval, the set is well-scoped. It is neither too few nor too many for a search-focused server.

Completeness4/5

The tool surface covers the core search workflow: querying, getting suggestions, and fetching results. A minor gap is the absence of a tool for advanced search parameters or filtering, but it is not critical.

Maintenance

ActivityInactive
ResponsivenessNo issues