Skip to main content
Glama
README.md
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="docs/assets/brand/wordmark-dark.svg">
  <img src="docs/assets/brand/wordmark-light.svg" width="376" alt="search-rotation">
</picture>

**One MCP server for web search and page extraction across multiple providers.**

Built by [Robin Bially](https://github.com/RobinBially) · Part of [LocalFoundry](https://github.com/localfoundry).

Rotate across available quotas, automatically fail over when a provider is unavailable, and return consistent results to your AI assistant. A local dashboard lets you manage API keys, reorder engines, check quotas, and inspect request history.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dashboard-dark.png">
  <img src="docs/assets/dashboard-light.png" alt="search-rotation dashboard showing per-engine call counts, remaining budgets, recent activity, and expanded MCP setup">
</picture>

*Actual dashboard UI, shown in English with illustrative demo data. Calls and provider budgets are displayed separately; activity bars offer time-window and per-engine details.*

## Quick start

### Homebrew

```sh
brew install localfoundry/tap/search-rotation
search-rotation --http --open
```

Homebrew installs the required Node.js runtime. For MCP over stdio, configure your
client to run `search-rotation` with no arguments. See the
[LocalFoundry tap](https://github.com/localfoundry/homebrew-tap) for upgrades and details.

### GitHub / npx

Requires **Node.js 20.3+** and **Git**. Install directly from GitHub — **no npm account needed**.

```sh
npx -y --allow-git=all github:localfoundry/search-rotation#v0.4.7
```

To preview the dashboard:

```sh
npx -y --allow-git=all github:localfoundry/search-rotation#v0.4.7 --http --open
```

Add your provider keys in the dashboard, then connect your assistant using the **[MCP client setup guide](docs/clients.md)** for Codex, Claude, Cursor, or OpenCode.

## What you get

- **Search and fetch:** independent rotation for web search and Markdown page extraction.
- **Automatic failover:** quota-aware ordering, rate-limit cooldowns, and request timeouts.
- **Local dashboard:** API keys, drag-and-drop engine order, per-engine calls and errors, separate quota balances, and interactive request history.
- **Local or remote:** MCP over stdio or authenticated Streamable HTTP.

**Providers:** Tavily · Firecrawl · Parallel · Exa · Google PSE · Jina Reader · DuckDuckGo HTML. [Keyless access and quota accounting](docs/provider-access.md) vary by provider.

## MCP tools

`web_search` · `fetch_url` · `engine_status` · `open_dashboard`

The dashboard's **MCP Tools** tab explains each tool, its parameters, and copyable example calls.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="docs/assets/mcp-tools-dark.png">
  <img src="docs/assets/mcp-tools-light.png" alt="MCP Tools overview with parameters and example calls for all four tools">
</picture>

*Actual dashboard UI, shown in English.*

## Search time filters

`web_search` accepts the following arguments:

| Parameter | Meaning |
| --- | --- |
| `query` | Required search text. |
| `numResults` | Optional result count, 1–20; otherwise the dashboard setting applies. |
| `engine` | Preferred provider, subject to time-filter support and availability; failover stays enabled. |
| `timeRange` | `day`, `week`, `month`, or `year`: UTC date window starting 1, 7, 30, or 365 days ago, through today. |
| `startDate` | Optional lower date bound in `YYYY-MM-DD` format. |
| `endDate` | Optional upper date bound in `YYYY-MM-DD` format. |

Use either `timeRange` or explicit dates. One-sided bounds and equal start/end dates are allowed; invalid calendar dates, reversed bounds, and mixing relative and explicit filters are rejected before any provider request.

```json
{"name":"web_search","arguments":{"query":"AI inference research","timeRange":"week","numResults":5}}
```

```json
{"name":"web_search","arguments":{"query":"AI inference research","startDate":"2026-08-01","endDate":"2026-08-31"}}
```

Relative windows are resolved **once per request** into UTC dates, including across failover. These are date filters, not exact rolling 24-hour windows; `month` and `year` mean 30 and 365 days, not calendar arithmetic. Exa receives the start of the first UTC day and the end of the last UTC day.

### Provider support and rotation

| Provider / access | Relative window | Two date bounds | One date bound |
| --- | --- | --- | --- |
| Tavily, with or without key | Yes | Yes | Yes |
| Firecrawl, with or without key | Yes | Yes | Skipped |
| Exa, direct API with key | Yes | Yes | Yes |
| Exa, hosted MCP without key | Skipped | Skipped | Skipped |
| Parallel, either access mode | Skipped | Skipped | Skipped |
| Google PSE / DuckDuckGo HTML | Skipped | Skipped | Skipped |

This table describes **implemented support in search-rotation**, not every upstream feature. Tavily receives `start_date` / `end_date`, Firecrawl receives a custom `tbs` range, and Exa receives `startPublishedDate` / `endPublishedDate`. Firecrawl is conservatively excluded for one-sided dates; the other skipped paths have no implemented date mapping.

The router excludes incompatible providers **before quota checks and rotation**, including when an incompatible `engine` is preferred. Each eligible provider pool has its own rotation cursor, so mixing filtered and unfiltered searches does not starve providers. Existing quota priorities, cooldowns, strict-free rules, and failover apply within that pool. If no compatible provider is available, the search fails explicitly; it never retries without the filter. Searches without time arguments retain access to all otherwise eligible providers.

Results show a publication date when supplied by the adapter, and request history includes the requested period. Provider date metadata can be estimated or missing: Tavily filters publication **or update** dates, Exa filters estimated publication dates, and Firecrawl uses its search index's date interpretation. Exact boundary inclusion follows the provider; this is not an independent verification of each page's publication date. See the [Tavily](https://docs.tavily.com/documentation/api-reference/endpoint/search), [Firecrawl](https://docs.firecrawl.dev/api-reference/endpoint/search), and [Exa](https://exa.ai/docs/reference/search) references.

After updating, reconnect your MCP client to load the new tool schema.

## Learn more

[Client setup](docs/clients.md) · [Operations & configuration (DE)](docs/operations.md) · [Releases](https://github.com/RobinBially/search-rotation/releases) · [CI](https://github.com/RobinBially/search-rotation/actions/workflows/ci.yml) · [MIT license](LICENSE)

TDQS

A4.1/5.0

Scored across 4 tools

Disambiguation4/5

web_search and fetch_url are clearly distinct (search results vs. page content). engine_status and open_dashboard both relate to configuration/quota, but one is a read-only config query and the other opens a browser UI, so some care is needed but descriptions mitigate ambiguity.

Naming Consistency3/5

Names are all lowercase snake_case, but styles are mixed: fetch_url and open_dashboard follow verb_noun, while web_search and engine_status are noun-style phrases. The pattern is readable but not fully consistent.

Tool Count5/5

Four tools is well-scoped for a search-rotation server: search, fetch, engine status, and dashboard access each cover a distinct need without bloat.

Completeness4/5

The core lifecycle of searching, fetching, inspecting engine status, and opening configuration UI is covered. A minor gap is the lack of programmatic engine configuration, but the dashboard tool reasonably covers that user-driven workflow.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive