What rebuilding TinySearch’s installation experience taught me about packaging, configuration, and developer experience
Press enter or click to view image in full size
When I first built TinySearch, the core engineering premise was simple: give LLM agents and local workflows a privacy-focused, source-grounded web research tool. It worked, but it suffered from a classic developer-tooling disease: it was built as a project, not a product.
To run it, you had to clone the git repository, set up a virtual environment, configure absolute file paths, spin up a local SearXNG instance in Docker, and debug why Chromium wasn’t behaving in your local environment.
People clearly wanted local, source-grounded research, but the setup friction was keeping the tool locked behind the git clone barrier.
I set out to fix that. Here is what I learned while transforming TinySearch from a self-hosted repository into a one-command Python library and Model Context Protocol (MCP) server.
1. The Real Friction Isn’t Code, It’s Prerequisites
If you want an AI agent like Claude Desktop, Cursor, or Zed to use a tool via MCP, the distance between discovering the tool and getting the first result needs to be as close to zero as possible.
The Old Workflow (Project Mode)
1. git clone [https://github.com/.../TinySearch.git](https://github.com/.../TinySearch.git)
2. cd TinySearch && python -m venv .venv && source .venv/bin/activate
3. pip install -r requirements.txt
4. Configure local environment variables and absolute script paths.
5. Spin up a SearXNG Docker container.
6. Paste complex multi-line command paths into your claude_desktop_config.json.
The New Workflow (Product Mode)
Add this to your MCP configuration and restart your client:
{
"mcpServers": {
"tinysearch": {
"command": "uvx",
"args": [
"--from",
"tinysuite-search[server]",
"tinysearch"
]
}
}
}No git clone. No virtual environment management. No API keys required for the default path.
Making pip install or uvx work meant re-architecting how the application handles everything from search backends to browser runtimes.
2. Zero-Infrastructure Search as the Default
Previously, TinySearch required a running SearXNG instance as its primary search driver, falling back to a fragile HTML scraper. This meant installation simplicity was blocked by infrastructure requirements.
To solve this, DuckDuckGo Search (DDGS) is now the native default backend.
Removing a required external service eliminates Docker networking headaches, container health checks, and a major source of first-run failure.
Default: DDGS (zero API key, zero setup).
Fallback: Optional Brave Search API key.
Self-Hosted Power Users: SearXNG remains fully supported.
Critically, users can swap between DDGS, Brave, and SearXNG through configuration without changing a single line of their application or agent code.
3. Owning the Runtime: setup and doctor
Making a tool “one-command” often hides a dangerous trap: silent failures.
TinySearch relies on Playwright (Chromium) for headless web scraping and a local ONNX embedding model for semantic context extraction. Running these headlessly through an MCP pipe means that if a dependency download fails silently, the AI agent simply hangs or returns opaque JSON errors.
To fix this, TinySearch now owns its operational lifecycle:
tinysearch setup: Pre-warms the environment by downloading Chromium and pre-caching the ONNX embedding model so the first agent query doesn’t time out.
tinysearch doctor: Runs local diagnostic checks to confirm readiness before an agent connects.
$ tinysearch doctor[✓] Chromium browser runtime available
[✓] Local ONNX embedding model cached
[✓] Configuration path writable (~/.config/tinysearch/config.toml)
[✓] Native search backend (DDGS) reachable
A design rule I adopted: A one-command installation is incomplete unless the tool can also explicitly explain why it cannot start.
(Honest caveat: The very first launch still takes 1–2 minutes to fetch Chromium and the model weights. It is zero-configuration, but it is not zero-download!)
4. One Core Engine, Multiple Adapters
Previously, TinySearch’s research logic was tightly coupled to its HTTP transport. To make it truly reusable, we decoupled the core engine into a standalone Python library.
Now, tinysearch provides a clean public API:
from tinysearch import research, scrape_url, TinySearchConfig# Perform grounded web research programmatically
results = research("latest developments in solid state battery tech")
The core returns a stable, JSON-serializable evidence structure. The transport layers — whether MCP, FastAPI, or the CLI -are now just thin adapters around this core:
Python Library: Returns clean data structures for custom token budgets, prompting, or filtering.
MCP Server: Strips out transport configuration and returns grounded, prompt-ready markdown optimized for LLM contexts.
FastAPI / Docker: Serves structured JSON or rendered prompts over REST endpoints for microservice architectures.
5. Tailoring Interfaces Instead of Exposing Everything
In earlier versions, tool arguments like output_format were exposed across all interfaces. We realized this was bad ergonomics for AI agents. An LLM invoking an MCP tool shouldn’t have to decide between raw HTML, internal JSON, or markdown strings.
We simplified the MCP interface to focus strictly on what the model needs: direct, grounded search context. Meanwhile, Python developers using the package directly retain full control over data transformation, configuration overrides, and schema inspection.
6. Proving “Easy to Install” in CI
A package is only easy to install if the published PyPI artifact actually works outside the maintainer’s local machine. We upgraded our GitHub Actions pipeline from standard unit testing to full release verification:
Clean-wheel testing: Testing package builds across Python 3.12, 3.13, and 3.14 on Linux, macOS, and Windows.
Isolated execution: Verifying imports and CLI commands in clean environments completely detached from the repository source tree.
MCP Smoke Tests: Launching tinysearch mcp over stdio in CI to ensure JSON-RPC handshake stability before publishing.
7. Performance Efficiency Behind the Scenes
Packaging improvements meant nothing if browser crawling melted the host CPU. In our recent updates, we shifted from spawning isolated browser processes per URL to maintaining a unified, lightweight Chromium context across research batches.
By blocking unnecessary assets (images, fonts, ad networks) while keeping JavaScript execution intact, TinySearch retains the page render fidelity required for modern web scraping without the heavy resource footprint.
What’s Next?
By shifting my focus from “how I develop TinySearch” to “how someone consumes TinySearch,” I turned a setup process filled with repository clones and Docker containers into a single uvx command.
If you’re building developer tools or AI agents, try inspecting your installation flow: How many steps sit between your user’s curiosity and their first successful output?
Check out TinySearch on GitHub
Try the Python package: pip install tinysuite-search