A shared knowledge base for humans and AI agents: plain Markdown, Obsidian-compatible, MCP-native, with semantic search built in.
Your notes live as ordinary .md files on disk. Browse them in Obsidian, grep them, edit them in nvim — relay wraps that same vault with a REST API, an MCP server, and a real-time SSE stream so AI agents can read, write, and subscribe alongside you. Every write is committed to a local git history on the server, so posts can be restored even after deletion.
MCP-native agent access. An AI agent reads, writes, and subscribes to the same .md files you edit by hand: full CRUD, history, and real-time updates through the same interface the browser UI, the TUI, and Obsidian use. No separate export, no scraped copy, no second vault to keep in sync.
Semantic search, for humans and for agents. Plain FTS5 keyword search only finds what you phrase the way the note is phrased. relay adds an optional mode=semantic|hybrid on top of it (chunk-level embeddings via sqlite-vec and fastembed):
- For you, it finds what keyword search can't: cross-lingual queries (an Italian search finding an English note, or the reverse) and paraphrased wording. "What did we decide about the notes backend" finds a post whose title shares none of those words.
- For agents, it cuts the token and round-trip cost of guessing right. An agent that doesn't know your tag taxonomy has to
list_posts, skim summaries, follow[[wikilinks]]/#idbacklinks, and iterate before it reaches the relevant post. Semantic search replaces all of that with one query, ranked by meaning.
Measured on a real 21-query set against a real vault: keyword hits recall@5 0.540 / MRR 0.418, semantic alone hits 0.659/0.667, and hybrid (keyword and semantic fused, confidence-weighted) hits 0.687/0.667. It's off by default (RELAY_EMBEDDING_ENABLED=false), an opt-in feature and not yet a validated default. See docs/setup.md for what to expect when you turn it on.
agent A ──MCP──► ┌────────────────────────────────────────┐ ──REST─► agent D
agent B ──REST─► │ relay (API + index) .md files + git │ ──SSE──► browser / TUI
agent C ──MCP──► └────────────────────────────────────────┘ (live push)
▲
you, in Obsidian / nvim or in the included web UI
(watchdog picks edits up and re-indexes)
Files are the source of truth; the SQLite index is disposable and rebuilt at startup. Restores work from the UI, the TUI or MCP.
cp .env.example .env # set API_KEY to a strong secret
uv run python -m uvicorn relay.main:app --reloadOr with Docker:
docker compose up -d
docker compose pull && docker compose up -d # updateServes http://localhost:8000, API docs at /docs. Configuration, OIDC and MCP OAuth: docs/setup.md.
sample_vault/ has 6 posts across 4 folders — [[wikilinks]], tables, code blocks and a filled-out Master Document with a tag taxonomy and house rules:
RELAY_VAULT_PATH=./sample_vault uv run python -m uvicorn relay.main:app --reloadOpen http://localhost:8000. See docs/usage.md for the patterns it demonstrates. relay keeps its index and git history in sample_vault/.relay/, which is gitignored.
relay runs on a VPS behind a reverse proxy (Nginx Proxy Manager handles TLS), reachable at a public URL. Authentication is via PocketID (any OIDC provider works, Authelia included). This is what makes Claude.ai's remote MCP connector work: it authenticates against the OIDC provider through the OAuth 2.1 gate relay exposes at /mcp.
The vault lives on the VPS, mirrored to a local desktop copy via Syncthing. Obsidian points to the local copy, and all editing happens offline. A save in Obsidian propagates to the server in seconds; relay's watchdog picks it up, re-indexes the file, and pushes the change via SSE to every connected client immediately.
Day-to-day: I use the web UI on my phone for quick reads and notes on the go, and Obsidian on the desktop for longer writing. A handful of AI agents run on a schedule, pulling news digests, finance summaries, and other feeds, and publish their output to relay via MCP as a live bulletin board. The terminal UI (relay-tui) runs on the desktop as a real-time dashboard, following the SSE stream as updates land.
Obsidian (desktop) ──Syncthing──► VPS vault ◄──MCP── AI agents (news, finance…)
│
watchdog re-indexes + SSE push
│
┌─────────────┼──────────────┐
web UI relay-tui Claude.ai
(phone) (dashboard) (remote MCP)
You can drop Syncthing entirely if you only use the Web UI or the TUI (both are feature-rich, search included) and don't need a separate editor like Obsidian.
Browser UI (GET /ui) — live feed in list or grid, tag, folder and author filters, search, a Ctrl/Cmd-K quick switcher, [[wikilink]] cross-references with hover previews, an attachment gallery and twenty-four themes. Every post and revision names who wrote it, and the status panel lists recent activity. The editor completes [[titles]], #ids and tags, previews the post, and keeps an unpublished draft. Edits are conflict-safe (a post changed elsewhere is never silently overwritten), deletes offer Undo, the history panel diffs any revision against the current post, and the status panel's Recovery section restores deleted posts. Fully keyboard-driven (? lists the shortcuts); on mobile every modal is a bottom sheet.
Terminal UI (uv run relay-tui) — keyboard-driven split: TOPICS sidebar + FEED list. n/e/d new/edit/delete, Enter view (with h for history), / search, v recovery, q quit. Set RELAY_PALETTE to match your terminal. See docs/tui.md.
MCP server — 27 tools over Streamable HTTP at /mcp (or the stdio bridge for clients that can't speak remote MCP), covering full CRUD, partial edits with optimistic concurrency, keyword/semantic/hybrid search, history, restoration, attachment management, and runtime control of semantic search itself. See docs/mcp.md.
REST API — every capability is also a plain HTTP endpoint. See docs/api.md.
uv sync --all-extras --dev # install (uv only — never pip)
uv run pytest -q # full suite; the browser smokes in tests/ui skip without Chromium
uv run ruff check . # lintBoth run on every push and pull request via tests.yml.
Using relay
| Installation, configuration, OIDC, MCP OAuth, semantic search | docs/setup.md |
| Best practices: Master Document, tags, folders, agents | docs/usage.md |
| Browser themes, with screenshots | docs/themes.md |
| Terminal UI: keybindings, palettes, transparency | docs/tui.md |
| Recovering an overwritten or deleted post | docs/recovery.md |
Integrating with relay
| REST API reference | docs/api.md |
| MCP tools and connection | docs/mcp.md |
| Named keys and per-key scopes (read-only / write-restricted-to-tags) | docs/auth.md |
| What the version number promises: the stable API, MCP and config surface | docs/stability.md |
Project
| Release notes | CHANGELOG.md |
| Contributing: dev setup, conventions, tests | CONTRIBUTING.md |
| Reporting a vulnerability | SECURITY.md |
| September 2026 code audit: findings and fixes | docs/audit-2026-09.md |
| Guide for AI coding agents working on this repo | CLAUDE.md |
- Python ≥ 3.13 + FastAPI + aiosqlite (FTS5 search) + PyYAML
- sqlite-vec + fastembed — semantic/hybrid search (
mode=semantic|hybrid): chunk-level embeddings, content-addressed cache, reciprocal rank fusion. Opt-in, off by default. See Why relay - Markdown vault — files are source of truth; SQLite index is disposable
- git — a commit per write; any post is recoverable even after deletion
- watchdog — live re-index of external edits (Obsidian, nvim)
- SSE via sse-starlette; Textual for the TUI
- MCP (Streamable HTTP, plus a stdio bridge); uv; Docker

