flori
# flori
Web research tools exposed over MCP: `search`, `read`, `open`, `grep`, `corpus_search`,
`corpus_list`, `cite`. See `spec.md` for the design, `DECISIONS.md` for the open questions
resolved during the build, and `HANDOFF.md` for what still needs a Kagi key.
## Layout
- `src/flori/` tool server (types, ports, tools module, SQLite store, adapters, MCP wrapper)
- `fetcher/` isolated fetch process (OCI image; Crawl4AI, headless Chromium, PyMuPDF)
- `tests/` tool-server tests; `tests/fetcher/` and `tests/e2e/` need the container up
- `fetcher/tests/` fetcher unit tests, runnable on the host
## Run
```sh
# 1. build and start the fetcher (Docker/OrbStack; Apple container works the same way)
cd fetcher && DOCKER_BUILDKIT=0 /usr/local/bin/docker build -f Containerfile -t flori-fetcher:dev . && cd ..
./scripts/fetcher-up.sh # also starts the test fixture server
# 2. test
uv run pytest # container tests skip if the fetcher is down
(cd fetcher && uv run pytest)
KAGI_API_KEY=... uv run pytest -m live
# 3. serve over MCP (stdio)
KAGI_API_KEY=... FETCHER_URL=http://127.0.0.1:8765 uv run flori
FLORI_TRANSPORT=http FLORI_PORT=8000 uv run flori # streamable HTTP instead
```
MCP client config (stdio):
```json
{
"mcpServers": {
"flori": {
"command": "uv",
"args": ["run", "--directory", "/path/to/flori", "flori"],
"env": {"KAGI_API_KEY": "...", "FETCHER_URL": "http://127.0.0.1:8765"}
}
}
}
```
Configuration: `[tool.flori]` in `pyproject.toml` for non-secrets (`search_engine`,
`db_path`, `fetcher_url`, `search_limit`); env vars `KAGI_API_KEY`, `FETCHER_URL`,
`FLORI_DB_PATH`, `FLORI_SESSION_ID`, `FLORI_TRANSPORT`.
## Acceptance
```sh
./scripts/fetcher-up.sh
KAGI_API_KEY=... uv run python tests/e2e/acceptance.py
```
TDQS
Scored across 7 tools
Each tool has a fairly distinct role: search discovers URLs, read extracts goal-relevant evidence, open inspects raw windows, grep is regex retrieval, corpus_search/corpus_list retrieve across the accumulated corpus, and cite builds citations. The main soft spot is that read, open, and grep all surface text from stored pages, so an agent must read carefully to pick the right one, but the descriptions differentiate them well.
Most names are bare verbs (search, read, open, grep, cite) while the corpus family uses a prefix (corpus_search, corpus_list), so conventions are mixed. It is still readable and the corpus_ prefix sensibly groups related tools, but there is no single predictable pattern.
Seven tools is well-scoped for a search-read-cite research workflow, with each tool earning a clear place in the pipeline (discovery, reading, inspection, corpus retrieval, citation). No redundancy or padding.
The core research lifecycle is covered: discover sources, read them against a goal, inspect/grep stored text, search the corpus, and create citations. Minor gaps exist, such as no explicit bibliography export, no tool to list/prioritize collected evidence, and no way to unregister a source, but agents can work around these.