Skip to main content
Glama
krutftw

fetchmux

by krutftw

FetchMux

npm License Node MCP

One search endpoint for AI agents. Put a router in front of Brave, Tavily, Exa, Firecrawl, and Crossref. Every request carries a hard cost ceiling and deadline; every response comes back with a receipt that says which provider ran, why, and what it cost.

Your agents stop hard-coding a provider into prompts and app code. They send one request shape; the gateway picks an eligible provider under a policy you control, enforces the budget and deadline before the call, retries safely on failure, and returns normalized results plus a full trace. You keep your provider keys — they never leave your gateway.

The receipt

Nothing is a black box. Every /v1/search response carries the routing decision:

"route": {
  "selectedProvider": "brave",
  "attemptedProviders": ["brave"],
  "reasonCodes": ["TASK_MATCH", "WITHIN_BUDGET", "RELIABILITY_WEIGHT"],
  "attempts": [
    { "provider": "brave", "outcome": "success", "latencyMs": 640, "estimatedCostUsd": 0.005 }
  ],
  "estimatedCostUsd": 0.005,
  "latencyMs": 640,
  "fallbackUsed": false,
  "traceId": "rt_b400e7c8"
}

Related MCP server: Argus

How it routes

  agent  ──▶  { query · task · maxCostUsd · maxLatencyMs }
                             │
                             ▼
                      ┌──────────────┐        your keys
                      │   FetchMux    │ ─────▶ Brave · Tavily · Exa
                      │    policy     │        Firecrawl · Crossref
                      └──────────────┘ ◀─────  (bring your own)
                             │
                             ▼
  agent  ◀──  evidence[]  +  route receipt

A provider is eligible only when its credentials, task fit, circuit state, spend, and deadline all pass. Budgets and deadlines are eligibility rules, not best-effort hints. Fallback happens only on retryable failures.

Quick start

No provider account needed — the public Crossref route runs out of the box:

git clone https://github.com/krutftw/fetchmux
cd fetchmux
npm install
npm run build

export FETCHMUX_API_KEY="a-long-random-key"
export CROSSREF_ENABLED=true
export CROSSREF_CONTACT_EMAIL="you@example.com"
npm run dev:gateway

From another shell:

curl http://127.0.0.1:8787/v1/search \
  -H "Authorization: Bearer a-long-random-key" \
  -H "Content-Type: application/json" \
  -d '{ "query": "retrieval augmented generation", "task": "scholarly", "maxLatencyMs": 8000 }'

To route real web search, set a provider key and use a web task instead:

export FETCHMUX_API_KEY="a-long-random-key"
export BRAVE_API_KEY="your-brave-key"
export BRAVE_COST_PER_REQUEST_USD="0.005"   # from your provider plan
npm run dev:gateway

New to Firecrawl? New accounts get 10% off the first month through this link (referral — FetchMux earns a small commission, no extra cost to you).

Use it from an agent

Point any MCP client (Claude, Cursor, and friends) at the published server:

{
  "mcpServers": {
    "fetchmux": {
      "command": "npx",
      "args": ["-y", "@fetchmux/mcp"],
      "env": {
        "FETCHMUX_BASE_URL": "http://127.0.0.1:8787/",
        "FETCHMUX_API_KEY": "your-gateway-key"
      }
    }
  }
}

Two read-only tools: search_web and preview_search_route.

Or use the typed SDK, @fetchmux/sdk:

import { FetchMux } from "@fetchmux/sdk";

const client = new FetchMux({
  baseUrl: "http://127.0.0.1:8787/",
  apiKey: process.env.FETCHMUX_API_KEY,
  fetch: globalThis.fetch.bind(globalThis),
});

const res = await client.search({
  query: "latest stable Node.js release",
  task: "fresh_facts",
  maxCostUsd: 0.02,
});

Providers

Bring your own key for each. Set the matching *_API_KEY, plus an optional *_COST_PER_REQUEST_USD if you want dollar budgets enforced.

Provider

Use

Key

Brave

web search

BRAVE_API_KEY

Tavily

web search, research

TAVILY_API_KEY

Exa

web search, docs

EXA_API_KEY

Firecrawl

page content

FIRECRAWL_API_KEY

Crossref

scholarly metadata

none (CROSSREF_ENABLED=true)

REST endpoints

Method

Path

Auth

Behavior

GET

/health

public

Process health and version

GET

/ready

public

Provider readiness

GET

/v1/providers

bearer

Provider configuration status

POST

/v1/route/preview

bearer

Ranked candidates, no provider call

POST

/v1/search

bearer

Routed retrieval and route receipt

Full contract: docs/openapi.yaml.

The process does not auto-load .env in local Node development; set variables in the shell or a process manager. Docker Compose reads the ignored .env file.

Variable

Default

Purpose

FETCHMUX_API_KEY

none

Protected-route bearer key

FETCHMUX_API_KEYS

none

Comma-separated keys for rotation

FETCHMUX_AUTH_DISABLED

false

Exact true bypasses auth (trusted local use only)

FETCHMUX_ALLOWED_ORIGINS

none

Comma-separated browser origins; no CORS when empty

FETCHMUX_HOST

127.0.0.1

Bind address

FETCHMUX_PORT

8787

TCP port

BRAVE_API_KEY / TAVILY_API_KEY / EXA_API_KEY / FIRECRAWL_API_KEY

none

Provider credentials

CROSSREF_ENABLED

false

Exact true enables the credential-free scholarly route

CROSSREF_CONTACT_EMAIL

none

Monitored contact for Crossref's polite pool

*_COST_PER_REQUEST_USD

none

Per-provider cost estimates used by dollar budgets

See provider configuration before enabling maxCostUsd.

Run in Docker

cp .env.example .env    # add your keys, never commit it
docker compose up --build -d
curl http://127.0.0.1:8787/health

Non-root Distroless image: Linux capabilities dropped, read-only root filesystem, provider credentials passed only at container start.

Benchmark

Validate every case and provider pairing with no network calls or credits:

npm run benchmark -- --workload benchmarks/workloads/founding-v1.json --mode dry-run

Live mode needs provider keys and an explicit --confirm-live. Check each provider's terms before publishing results — see the benchmark methodology.

What it is (and isn't)

Open source, self-hosted, single-tenant, BYOK. Route events go to stdout as JSON and exclude your query text, keys, and result content by default. No database, no telemetry.

It is not a hosted service, a pooled-credit reseller, or a claim that these providers are interchangeable. Provider names are the adapters it ships with, not partnerships. A hosted version is on the roadmap — star the repo to follow.

Development

npm test          # 232 tests
npm run typecheck
npm run lint
npm run build
npm run dev:gateway
npm run dev:site

More docs: product design · local development · deployment · provider configuration · data handling · incident response

Security issues: security@fetchmux.com.

License

Apache-2.0. Free to self-host, modify, and redistribute.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    The open retrieval layer for AI agents. Index your entire project — code, docs, legal, research, data — and serve surgical context via MCP. FTS5 full-text search, optional semantic search (FastEmbed/ONNX), 10 built-in parsers, incremental auto-sync.
    8
    906 PyPI
    24
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    One endpoint, five search providers. Search broker for AI agents with automatic fallback, RRF ranking, and budget enforcement. The LiteLLM of web search.
    13
    148 PyPI
    5
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Web search for AI agents across 6 engines (Serper, Brave, Exa, Tavily, Firecrawl, Perplexity) through one search tool. Routes each query to the cheapest engine that clears a quality bar and caches repeats. Hosted, streamable-HTTP, BYOK supported.
    1
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    A self-hosted, MCP-native web-search backend for AI agents that provides meta-search, clean extraction, RAG with citations, and GitHub project selection.
    3
    MIT