TypeSense Research
Context
Uvilo OS uses Astro Starlight with Pagefind for full-text search. Pagefind is fast and zero-config but is keyword-only — it cannot find conceptually related results when exact terms don’t match. As the documentation grows, semantic search becomes valuable for finding related architecture concepts, specs, and prompts even when users don’t know the exact terminology.
Additionally, Forge agents need a way to discover knowledge files across the entire repo without relying on explicit cross-references. Currently, an agent can only find a file if it’s linked from a previously loaded file or matched by a glob/grep. This is the primary discoverability bottleneck in the system. Typesense with an MCP server solves this by giving agents semantic search over the full repo.
What is Typesense?
Typesense is an open-source, typo-tolerant search engine designed for instant search (sub-50ms). It supports full-text keyword search, faceted search, and — since v0.25.1 — semantic/vector search using built-in ML models (S-BERT) or external APIs (OpenAI, Google). Hybrid search (keyword + semantic combined) is also supported.
Two Consumers, One Index
| Consumer | Interface | Use Case |
|---|---|---|
| Humans (website) | Starlight DocSearch UI plugin | Browse/search published docs in browser |
| Humans (terminal) | CLI tool (forge-search) | Search repo from command line |
| Agents (LLM) | MCP server (typesense-mcp) | Semantic search over all repo markdown during agent sessions |
All three consumers query the same Typesense instance and collection. The MCP server and CLI are required — without them, agents and terminal users have no access to the search index.
Starlight Integration
There is an official community plugin: starlight-docsearch-typesense. It replaces Pagefind with a DocSearch-style interface powered by Typesense. The setup requires:
- A running Typesense server (self-hosted or Typesense Cloud)
- The
typesense-docsearch-scraperto index the site content - The Starlight plugin to provide the search UI
Hosting Options
Option A: Self-hosted on Railway (recommended)
Typesense can be deployed on Railway using an official template. Cost is approximately $5–10/month for small deployments. The data lives on a persistent Railway volume.
Advantages:
- Free open-source software
- Already have Railway infrastructure for LibreChat
- Full data ownership
- ~$5–10/month
Disadvantages:
- Need to manage the server
- Need to set up the scraper as a build step or cron job
Option B: Typesense Cloud
Managed service starting at ~$40/month. Includes a one-time free tier of 720 hours (30 days).
Advantages:
- Zero infrastructure management
- Monitoring and backups included
Disadvantages:
- More expensive (~$40/month vs ~$5–10/month self-hosted)
- Free tier is one-time only, not recurring
Recommendation
Self-host on Railway. We already have Railway infrastructure, and the cost is minimal. The scraper can run as a post-build step in the Vercel deployment or as a scheduled Railway cron service.
Implementation Plan
Phase 1: Deploy Typesense on Railway
- Use the Railway Typesense template to deploy a Typesense instance
- Configure a persistent volume for data storage
- Set API key via Railway environment variable
- Expose the service via Railway internal networking or public URL (behind API key)
Phase 2: Repo-Wide Indexing
Unlike the website scraper (Phase 4), this phase indexes the raw repo — all markdown files across all departments, including non-published files like specs, state docs, knowledge files, and agent instructions.
- Write a Node.js indexing script (
scripts/index-repo.ts) that:- Walks the repo tree and finds all
.mdfiles - Extracts frontmatter (title, status, owner, visibility) as facet fields
- Extracts the body text as the searchable content
- Computes the file path relative to repo root as a unique document ID
- Chunks files >800 words into overlapping segments (~400 words with ~50 word overlap) for better retrieval
- Walks the repo tree and finds all
- Configure the Typesense collection schema:
id: relative file pathtitle: from frontmatter or first headingdepartment: root-level folder namecontent: body text (chunked)path: full relative pathtype:skill|knowledge|project|infrastructure(derived from path)embedding: vector field using built-ints/all-MiniLM-L12-v2model
- Run indexing as a git post-commit hook or CI step
- Support incremental re-indexing (only changed files)
Phase 3: MCP Server (typesense-mcp)
Build an MCP server that agents can call during sessions. This is the critical piece for agent discoverability.
Tools exposed:
| Tool | Parameters | Returns |
|---|---|---|
search_knowledge | query (string), department (optional), type (optional), limit (default 5) | Array of {path, title, snippet, score} |
get_file_summary | path (string) | First 200 words + frontmatter of the file |
Implementation:
- TypeScript MCP server using the MCP SDK
- Connects to the Railway-hosted Typesense instance
- Uses hybrid search (keyword + semantic) by default
- Facet filtering by department and type
- Deploy as a Railway service or run locally as a stdio MCP server
- Add to LibreChat MCP server configuration
Phase 4: CLI Tool (forge-search)
A simple CLI for humans to search the repo from the terminal.
Implementation:
- Thin Node.js CLI wrapper around the Typesense search API
- Formats results as a table: path, title, snippet
- Installable via
npm linkfrom the repo - Uses the same Typesense instance and collection as the MCP server
Phase 5: Integrate with Starlight (website search)
- Install
starlight-docsearch-typesenseplugin - Configure the plugin with Typesense server URL, API key (search-only), and collection name
- This replaces Pagefind — the search UI becomes DocSearch-style
- The website uses the same Typesense instance but may use a separate collection that only indexes published pages (vs the full repo collection used by agents)
- Configure the
typesense-docsearch-scraperto index the deployed site - Set up scraper as a Vercel deploy hook or Railway cron
Phase 6: Enable Semantic Search
- Configure the Typesense collection schema with an
embeddingfield using the built-ints/all-MiniLM-L12-v2model - Adjust both the repo indexer and website scraper to populate the embedding field
- Enable hybrid search (keyword + semantic) in MCP server, CLI, and website plugin
- Note: Built-in ML models are CPU-intensive; for a small repo (~100 files), this should be manageable. If slow, consider using OpenAI embeddings API instead.
Dependencies
- Railway account (already have)
- Typesense Docker image
typesense-docsearch-scraperDocker image (for website indexing)starlight-docsearch-typesensenpm package- MCP SDK (
@modelcontextprotocol/sdk) - Typesense API key (search-only key for frontend/agents, admin key for indexer/scraper)
Estimated Effort
| Phase | Effort | Dependencies |
|---|---|---|
| Phase 1: Deploy Typesense | 1 session | None |
| Phase 2: Repo-wide indexing | 1-2 sessions | Phase 1 |
| Phase 3: MCP server | 1-2 sessions | Phase 1, Phase 2 |
| Phase 4: CLI tool | 0.5 session | Phase 1, Phase 2 |
| Phase 5: Starlight integration | 1 session | Phase 1 |
| Phase 6: Semantic search | 1 session | Phase 1 |
Total: ~6-8 sessions. Phases 2-4 (agent discoverability) should be prioritized over Phase 5 (website search), since agent discoverability is the more critical gap.
Open Questions
- Should the website scraper run against the Vercel preview URL or against a local build?
- Do we need the Vercel Auth bypass for the scraper, or should we scrape a local build directory?
- Should we keep Pagefind as a fallback during the transition?
- Should the MCP server run as a Railway service (accessible from LibreChat cloud) or as a local stdio server (accessible from local Claude Code)?
- Should we use one Typesense collection for both repo and website, or separate collections with different schemas?
- Is
ts/all-MiniLM-L12-v2sufficient for our use case, or should we use OpenAI embeddings for better semantic quality?