Skip to main content
Glama
newcoast27-hub

Researcher MCP Server

README.md
# Researcher MCP Server

A Model Context Protocol (MCP) server that researches answers to user
questions using full DuckDuckGo web search, DuckDuckGo news search, and
Wikipedia. Connect it to any MCP-capable client (LM Studio, Claude Desktop,
VS Code, etc.) to give a local LLM web-research abilities with current-events
coverage.

## Tools Provided

| Tool | Description |
|------|-------------|
| `research(query)` | Full research report — classifies the query and mixes sources: news queries get dated recent articles, factual questions get a Wikipedia summary plus corroborating web sources and related news, everything else gets full web results |
| `search(query, max_results?)` | Raw web search results (title, URL, snippet) |
| `news(query, timelimit?, max_results?)` | Recent news articles with publication date and source name; `timelimit` is `d`/`w`/`m` (day/week/month) |
| `extract(url)` | Fetches a URL and returns its readable text (HTML parsed with BeautifulSoup); http/https text content only, size-capped, non-public addresses refused |
| `get_current_time(location_hint?)` | Current date and time, labeled with the timezone used |

When a search backend is unavailable (DuckDuckGo rate-limits under heavy use),
the tools report that explicitly — `research` adds a "SOURCES UNAVAILABLE"
section and `search`/`news` raise an error — so an empty result always means
"nothing found" rather than "the lookup failed".

## Transports

The server picks its transport from the `MCP_TRANSPORT` environment variable:

- **`stdio`** (default) — for MCP clients that launch the server directly
- **`streamable-http`** — network mode; listens on `MCP_HOST`:`MCP_PORT`
  (default `0.0.0.0:8000`) at the **`/mcp`** path. This is what
  `docker-compose.yml` uses.

## Configuration

| Variable | Default | Purpose |
|----------|---------|---------|
| `MCP_TRANSPORT` | `stdio` | `stdio` or `streamable-http` |
| `MCP_HOST` / `MCP_PORT` | `0.0.0.0` / `8000` | Bind address for HTTP mode |
| `DEFAULT_TIMEZONE` | `UTC` | IANA zone for timestamps when no hint is given. **Set this** — containers run on UTC, so leaving it default makes the server report a time hours off from yours (and sometimes the wrong day). |
| `ALLOW_PRIVATE_URLS` | unset | Set to `1` to let `extract` reach private/loopback addresses. Leave unset unless you specifically need to fetch internal pages. |
| `LOG_LEVEL` | `INFO` | Logging verbosity (logs go to stderr) |

## Quick Start (Docker, recommended)

```powershell
cd "d:/Local Development/Researcher MCP Server"
docker compose up -d --build
```

The server is then available to MCP clients at `http://localhost:8000/mcp`.

Check status and logs:

```powershell
docker compose ps
docker compose logs -f researcher
```

## Quick Start (Python, stdio)

```powershell
pip install -r requirements.txt
```

Then configure your MCP client to launch it — for example in an
`mcp.json`-style config:

```json
{
  "mcpServers": {
    "researcher": {
      "command": "python",
      "args": ["d:/Local Development/Researcher MCP Server/researcher_server.py"]
    }
  }
}
```

Don't run `python researcher_server.py` standalone and expect an HTTP port —
in stdio mode the client launches and owns the process. For HTTP mode outside
Docker, set the env vars first:

```powershell
$env:MCP_TRANSPORT = "streamable-http"; python researcher_server.py
```

## Connecting LM Studio

See [LM_STUDIO_SETUP.md](./LM_STUDIO_SETUP.md) (both methods) and
[LM_STUDIO_DOCKER_SETUP.md](./LM_STUDIO_DOCKER_SETUP.md) (Docker walkthrough).
The short version — in LM Studio 0.3.17+, edit `mcp.json` via
Chat → Program sidebar → Install → Edit mcp.json:

```json
{
  "mcpServers": {
    "researcher": {
      "url": "http://localhost:8000/mcp"
    }
  }
}
```

## How It Works

```
User question
      ↓
MCP client (LM Studio, Claude, ...) calls the `research` tool
      ↓
Query classification (news / factual / general)
      ↓
Sources queried in parallel:
  - DuckDuckGo web search (full index, via ddgs)
  - DuckDuckGo news search (dated articles with sources)
  - Wikipedia API (encyclopedic summaries)
      ↓
Formatted research report with dates and source URLs → back to the client
```

## Dependencies

- `mcp` — official MCP Python SDK (FastMCP server)
- `httpx` — async HTTP client for Wikipedia and page extraction
- `ddgs` — DuckDuckGo web and news search
- `beautifulsoup4` — HTML parsing for page extraction
- `tzdata` — timezone database (slim/Windows images don't ship one)

## Running the Tests

The test suite covers the classification, timezone, URL-policy, and formatting
logic without touching the network:

```powershell
pip install -r requirements-dev.txt
pytest -q
```

## Troubleshooting

### Container exits immediately
Make sure `MCP_TRANSPORT=streamable-http` is set (docker-compose.yml does
this). Without it the server runs in stdio mode and exits as soon as stdin
closes — which is immediately, in a detached container.

### Client can't connect over HTTP
- Confirm the container is up: `docker compose ps`
- The endpoint is `http://localhost:8000/mcp` — the `/mcp` path is required
- `curl http://localhost:8000/mcp` returning a JSON-RPC error object means the
  server is up (a bare GET is expected to be rejected)

### Empty or thin search results
- Check internet connectivity from where the server runs
- DuckDuckGo occasionally rate-limits heavy usage; the report will say
  "SOURCES UNAVAILABLE" when that happens — wait a moment and retry

### Reported time is wrong
Set `DEFAULT_TIMEZONE` to your IANA zone (e.g. `America/Los_Angeles`) in
`docker-compose.yml`. Containers run on UTC by default.

### `extract` refuses a URL
Non-public addresses (loopback, `10.x`, `192.168.x`, `169.254.x`) and
non-text content types are blocked by design. Set `ALLOW_PRIVATE_URLS=1` if
you genuinely need to fetch internal pages.

## License

MIT License - See LICENSE file for details.

Maintenance

ActivityMaintained
ResponsivenessSyncing