{"id":"875bb7f5-a12c-4020-84c1-3454ad597abd","slug":"phrona","kind":"mcp","name":"phrona","summary":"Multi-engine meta-search library written in rust, with TLS/browser impersonation, ranked dedup, answer extraction, CLI, MCP server, Prometheus metrics, and a Tavily-compatible drop-in REST API. Ships","owner":"alvaro-co","repoUrl":"https://github.com/alvaro-co/phrona","license":"AGPL-3.0","licenseTier":"strong-copyleft","trust":"community","stars":2,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-08-16T20:26:37.000Z","addedAt":"2026-10-21T17:57:05.546Z","logoUrl":"https://avatars.githubusercontent.com/u/236184638?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["search","developer-tools","cloud-infra"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-22T03:12:21.415Z","sample":false,"url":"https://conduits.dev/tools/phrona","language":"Rust","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# Phrona\n\nHigh-performance metasearch engine library written in Rust. Queries 29 search\nengines across 8 categories in parallel, impersonates real browsers over\nHTTP/2 (wreq), merges, deduplicates and ranks results locally, and exposes\nthe same engine to Rust, Python, a REST API, an MCP server for AI agents and\na web frontend.\n\n```text\ncrates/phrona          core library\ncrates/phrona-api      REST API server (axum)\ncrates/phrona-mcp      MCP server for AI agents (rmcp, stdio + TCP)\ncrates/phrona-cli      phrona: single CLI to everything (search, server, MCP)\ncrates/phrona-python   Python bindings (pyo3)\nexamples/                  runnable Rust and Python examples\ncrates/phrona-api/assets/      Material 3 style static web app\nscripts/                   upstream drift monitor\ndocs/                      full documentation\n```\n\n## Published packages\n\n| Package | Install | crates.io / PyPI | docs.rs |\n| --- | --- | --- | --- |\n| `phrona` (Rust library) | `cargo add phrona` | [crates.io](https://crates.io/crates/phrona) | [docs.rs](https://docs.rs/phrona) |\n| `phrona-api` (REST server) | `cargo add phrona-api` | [crates.io](https://crates.io/crates/phrona-api) | [docs.rs](https://docs.rs/phrona-api) |\n| `phrona-mcp` (MCP server) | `cargo add phrona-mcp` | [crates.io](https://crates.io/crates/phrona-mcp) | [docs.rs](https://docs.rs/phrona-mcp) |\n| `phrona-cli` (binary `phrona`) | `cargo install phrona-cli` | [crates.io](https://crates.io/crates/phrona-cli) | [docs.rs](https://docs.rs/phrona-cli) |\n| `phrona` (Python bindings) | `pip install phrona` | [PyPI](https://pypi.org/project/phrona) | - |\n\nEach package has its own README with a quick start, so check the crates.io or\nPyPI page of the one you need.\n\n## Quick start (CLI)\n\n```bash\ncargo run -p phrona-cli -- search \"rust programming\" --max-results 10\ncargo run -p phrona-cli -- suggest rus\ncargo run -p phrona-cli -- serve         # REST 8080 + MCP-over-TCP 8081\n```\n\n`phrona` is the all-in-one entry point: search, suggest, extract, grounding,\nengine listing, availability tests (`phrona test`), the full REST server, MCP\nover stdio (`phrona mcp`) or TCP (`phrona serve`), and shell completions. See\n[docs/cli.md](docs/cli.md).\n\n## Quick start (Rust)\n\n```rust\nuse phrona::{search_sync, SearchOptions};\n\nlet resp = search_sync(SearchOptions::new(\"rust programming\"))?;\nfor r in resp.web() {\n    println!(\"{} {}\", r.title, r.url);\n}\n```\n\nAsync:\n\n```rust\nuse phrona::{SearchClient, SearchOptions};\n\nlet client = SearchClient::new()?;\nlet resp = client.search(SearchOptions::new(\"rust programming\")).await?;\n```\n\n## Quick start (REST API)\n\n```bash\ncargo run -p phrona-api                 # listens on 127.0.0.1:8080\ncurl \"localhost:8080/v1/search?q=rust&max_results=5\"\n```\n\n## Quick start (Python)\n\n```bash\npip install phrona                       # pre-built wheels from PyPI\n```\n\n```python\nimport phrona\nphrona.search(\"rust programming\", engines=[\"bing\", \"brave\"])\nphrona.suggest(\"rus\")\n```\n\nRequires CPython 3.9-3.14 (pyo3 0.29). To build from source instead:\n\n```bash\nuv build                                    # produces dist/phrona-*.whl\nuv pip install dist/phrona-*.whl --python <venv python3.12>\n```\n\nPre-built wheels are published to PyPI for CPython 3.9-3.14 on linux\n(x86_64 / aarch64), macOS (x86_64 / arm64) and Windows (x86_64), plus an\nsdist for everything else (needs a Rust toolchain + `uv build` / maturin).\nNo musllinux wheels (btls-sys requires a musl C++ toolchain).\n\n## Quick start (MCP)\n\n```bash\ncargo run -p phrona-mcp                 # stdio JSON-RPC\ncargo run -p phrona-cli -- mcp          # same, via phrona\ncargo run -p phrona-cli -- serve        # also serves MCP over TCP 8081\n```\n\nPoint your MCP client (Claude Desktop, claude-code, Cursor...) at the\n`phrona-mcp` binary. Twelve tools: `web_search`, `image_search`,\n`news_search`, `video_search`, `book_search`, `code_search`,\n`papers_search`, `archives_search`, `suggest`, `fetch_page`,\n`search_grounded`, `list_engines`.\n\n## Quick start (web)\n\n```bash\ncargo run -p phrona-api          # or: phrona serve\n# open http://localhost:8080         -> search page (full parameters + JSON view)\n#                                    -> Tools tab: suggest / extract / ground / engines / test\n```\n\nThe web app is served from disk (`crates/phrona-api/assets/`), so editing it takes effect\non reload - no rebuild.\n\n## Features\n\n- 29 engines, 8 categories (web, images, news, videos, books, code, papers, archives), 7 suggestion\n  sources and page extraction (AI grounding).\n- Impersonated HTTP/2 with TLS fingerprint spoofing via wreq profiles\n  (Chrome 100-149, Firefox 139-148, Safari 26, Edge 148, Opera 131, OkHttp).\n- Search merging: cross-engine dedup (tracking parameters stripped), ranking\n  (cross-engine agreement + position + query text match), suggestions and\n  per-engine error reporting in every response.\n- Full sync and async APIs, `SearchClient` reuse with shared token caches\n  (DDG vqd, Startpage sc), time ranges, safesearch, regions, per-engine\n  filter strings, proxies.\n- REST API with Tavily-compatible `/search` endpoint, AI grounding endpoint,\n  page extraction, availability probing (`/v1/test`), suggestions and health.\n- MCP server with compartmentalized tools and grounded search for RAG.\n- Python bindings with the full search surface.\n- Web frontend: one static page with full-parameter search (engines,\n  category, safesearch, region, language, time range, filters, page,\n  JSON view, per-engine report) and a Tools tab that runs every CLI\n  capability in the browser (suggest, extract, ground, engines, test).\n- Fixture-based parser tests: 25 of 29 captured pages are\n  verified-parseable live captures (see `crates/phrona/tests/fixtures/`);\n  the suite is fully network-independent.\n- Upstream drift monitor: `scripts/watch_upstream.sh` + the\n  `upstream-watch` GitHub workflow report when any of the 8 upstream\n  projects moves past its pinned commit, so broken parsers are caught\n  early (see [docs/upstream.md](docs/upstream.md)).\n- Tag-driven releases: push `vX.Y.Z` and the `release` workflow builds\n  binaries for Linux/Windows/macOS plus the Python wheel and publishes a\n  GitHub Release (see [docs/releasing.md](docs/releasing.md)).\n\n## Layout of this documentation\n\n| File | Contents |\n| --- | --- |\n| [docs/library.md](docs/library.md) | Rust library reference |\n| [docs/api.md](docs/api.md) | REST API reference |\n| [docs/mcp.md](docs/mcp.md) | MCP server reference |\n| [docs/python.md](docs/python.md) | Python bindings reference |\n| [docs/cli.md](docs/cli.md) | CLI (`phrona`) reference |\n| [docs/architecture.md](docs/architecture.md) | Architecture and layering |\n| [docs/examples.md](docs/examples.md) | Rust/Python examples |\n| [docs/frontend.md](docs/frontend.md) | Web frontend |\n| [docs/engines.md](docs/engines.md) | Engine-by-engine reference and block status |\n| [docs/upstream.md](docs/upstream.md) | Upstream sources, what was borrowed, how to monitor |\n| [docs/releasing.md](docs/releasing.md) | Tagging and publishing releases |\n| [docs/HISTORY.md](docs/HISTORY.md) | Complete build history |\n\n## Development\n\nBoth `make` and plain `cargo` work; the cargo commands are the same ones\nCI runs:\n\n```bash\ncargo fmt --all --check           # or: make fmt-check\ncargo clippy --workspace --all-targets -- -D warnings   # or: make lint\ncargo test --workspace            # 195 offline tests incl. 29 fixtures, ~15 s  (make test)\nmake check                        # fmt-check + lint + test in one\n\ncargo build --workspace           # or: make build\ncargo build --release -p phrona-cli -p phrona-api -p phrona-mcp  # make release\nuv build                          # Python wheel (make wheel)\n\ncargo run -p phrona --bin fetch_fixtures [engine...]   # re-capture live fixtures\ncargo run -p phrona --bin dbg_parse -- bing            # parse a fixture\ncargo run -p phrona-examples --bin basic -- \"rust\"     # run the examples\n```\n\nA few engines only return full results after a real-browser visit.\nPhrona keeps this strictly opt-in and self-contained:\n\nAny one of these opts in:\n\n```bash\nphrona --auto-bootstrap search ...        # per-invocation\nPHRONA_AUTO_BOOTSTRAP=1 phrona search ... # environment (works for library users too)\n```\n\nor in `phrona.yaml`:\n\n```yaml\nengines:\n  auto_bootstrap: true\n```\n\nWith that enabled, when one of those engines is blocked phrona briefly\nruns your installed Chromium-family browser headless (or downloads the\nofficial `chrome-headless-shell` into `~/.cache/phrona/browser/` once -\nno packages, no privileges), stores the resulting session in\n`phrona.cookies.json` next to your config and retries. Sessions are\nreused across restarts; nothing interactive ever happens.\n\nPrefer to stay fully static? Pin cookies yourself in\n`phrona.yaml` (`engines.bootstrap_cookies`), pass them ad hoc via\n`--cookie engine=...`, or run `phrona bootstrap [engines]` manually -\nqwant in particular is pure HTTP and only needs such a cookie.\nStartpage (Anubis), Mojeek (ALTCHA) and the DuckDuckGo verticals have\ntheir anti-bot flows solved fully natively. When an upstream changes its\nflow, the companion [webrief](https://github.com/alvaro-co/webrief) tool\ncaptures it for reverse engineering. See\n[docs/engines.md](docs/engines.md) for the per-engine status table.\n\n## License\n\n[AGPL-3.0](LICENSE)"}