An open-source, self-hostable web app and MCP server for your personal knowledge. Dump raw text into the web UI. Your AI client (Claude.ai, Claude Desktop, any MCP-compatible tool) connects via MCP and pulls from your shelf when answering you.
Shelf has zero LLM calls. No API keys to run. It is purely storage, search, and an MCP server.
- Project page: https://projects.omrajguru.com/shelf
- Repo: https://github.com/omrajguru/shelf
- Dev notes: https://omrajguru.com/devnotes/shelf
| Path | When to use it | Time |
|---|---|---|
| Cloud (Supabase + Vercel) | You want a Shelf you can hit from any device, and you want Claude.ai (web) to connect to it. No terminal commands needed. | ~10 min |
| Local (SQLite + Claude Desktop) | You want everything on your laptop and you're comfortable in a terminal. | ~3 min |
You can also do both at the same time — point your local Claude Desktop at your cloud Supabase and everything stays in sync.
You need three accounts: GitHub, Supabase (free), Vercel (free, sign in with GitHub). Then:
On this page, click Fork (top right). Accept defaults. You now have your own shelf at github.com/<you>/shelf.
- Go to supabase.com → New project.
- Name it
shelf. Pick a database password. Pick a region close to you. Click Create. - Wait ~30 seconds for it to spin up.
- In your Supabase project, click SQL Editor (left sidebar) → New query.
- Open
supabase/migrations/0001_init.sqlin your fork. Copy the whole file. - Paste into the SQL editor. Click Run. You should see "Success. No rows returned."
In Supabase: Settings (gear icon, bottom left) → API. Copy two things:
- Project URL (looks like
https://abcd1234.supabase.co) - anon / public key (a long
eyJ...string — NOT the service role key)
Keep these safe but they're not maximally sensitive — the anon key is read/write but only against your own database, and row-level security is on by default.
Click the Deploy with Vercel button at the top of this README (or go to vercel.com/new and import your fork).
When Vercel asks for environment variables, paste in:
| Name | Value |
|---|---|
SHELF_STORAGE_ADAPTER |
supabase |
SUPABASE_URL |
the Project URL from step 4 |
SUPABASE_ANON_KEY |
the anon key from step 4 |
Click Deploy. First build takes 1–2 minutes.
Vercel gives you a URL like https://shelf-<random>.vercel.app. Open it. Dump your first entry. It should appear in the list immediately.
Health-check: visit <your-vercel-url>/api/health — you should see {"adapter":"supabase","ok":true,...}.
In Claude.ai: Settings → Connectors → Add custom connector. Fill in:
| Field | Value |
|---|---|
| Name | shelf |
| Remote MCP server URL | <your-vercel-url>/api/mcp |
| OAuth Client ID | leave empty |
| OAuth Client Secret | leave empty |
Save. Open a new conversation and try one of these prompts — Claude will call the relevant tool automatically:
What's in my shelf?
Save this to my shelf: I prefer Postgres over MySQL because of better JSON support and stricter types.
Search my shelf for anything I've written about API design.
You're done. Dump from any device — your laptop, your phone, your tablet — and Claude pulls from it grounded in what you wrote.
git clone https://github.com/omrajguru/shelf
cd shelf
npm install
cp .env.example .env.local # no API keys to add
npm run db:init # creates ./shelf.db (SQLite)
npm run dev # web app at http://localhost:3000In a second terminal:
npm run mcp # MCP server over stdio…or skip the second terminal and wire Shelf into Claude Desktop so it spawns the MCP server itself. See docs/connect-claude.md for the exact config.
Requires Node 22.5 or newer (Shelf uses the built-in node:sqlite, no native compilation needed).
| Tool | What it does |
|---|---|
shelf_search(query, limit?) |
Full-text search across all entries |
shelf_dump(content, source?) |
Save a new entry |
shelf_rules() |
Returns all entries you've marked as rules — standing instructions for the AI |
shelf_pinned() |
Returns all pinned entries — your most important reference material |
shelf_list(limit?, offset?) |
List entries in reverse-chronological order (pinned first) — useful for "what's in my shelf?" |
The MCP server (whether stdio or /api/mcp) and the web app share the same storage adapter and database. Anything you dump in one is immediately visible to the other.
- A note-taking app with folders and tags
- A RAG pipeline or AI-powered search engine
- An app that calls any LLM or AI API internally
- A tool that processes or summarizes content on ingest
- A team knowledge base (v1 is personal only)
- Framework: Next.js 14 (App Router for the web app, Pages Router for
/api/mcp) - MCP:
@modelcontextprotocol/sdk— stdio for Claude Desktop, Streamable HTTP for Claude.ai - Language: TypeScript · Styling: TailwindCSS · State: Zustand
- Default storage: SQLite via Node's built-in
node:sqlite(no native compilation, no install) - Cloud storage: Supabase (full implementation) · AWS (stub)
Switch via SHELF_STORAGE_ADAPTER:
| Adapter | Status | Setup |
|---|---|---|
local (default) |
Working | Zero config. Used for local dev. |
supabase |
Working | docs/adapters/supabase.md. Used for cloud / Vercel deploys. |
aws |
Stub | Open issue or PR if you want to contribute one. |
Every adapter implements StorageAdapter — adding a new one is a well-scoped contribution.
src/
├── adapters/ # StorageAdapter implementations + factory
│ ├── types.ts
│ ├── local.ts # node:sqlite (lazy-loaded so Vercel builds don't choke)
│ ├── supabase.ts # @supabase/supabase-js + Postgres FTS
│ ├── aws.ts # stub
│ └── index.ts
├── app/ # App Router — web UI + non-MCP API routes
│ ├── api/
│ │ ├── entries/route.ts
│ │ ├── entries/[id]/route.ts
│ │ ├── search/route.ts
│ │ └── health/route.ts
│ ├── layout.tsx
│ ├── page.tsx # single-screen: dump + list
│ └── globals.css
├── pages/ # Pages Router — required for raw Node req/res
│ └── api/mcp.ts # Streamable HTTP MCP transport
├── components/
│ ├── layout/Navbar.tsx
│ ├── shelf/ # DumpBox, EntryList, EntryDetail
│ └── ui/ # Container, Button
├── lib/utils.ts
├── mcp/
│ ├── server.ts # stdio transport entry (npm run mcp)
│ ├── build-server.ts # shared tool registration (used by both transports)
│ ├── format.ts
│ └── tools/ # search, dump, rules, pinned, list
└── store/shelf.ts # Zustand
supabase/
└── migrations/0001_init.sql # paste-into-SQL-editor migration
docs/
├── adapters/supabase.md
├── deploy-vercel.md
└── connect-claude.md
| Script | Purpose |
|---|---|
npm run dev |
Web app at http://localhost:3000 (and /api/mcp for remote MCP) |
npm run mcp |
MCP server over stdio (for Claude Desktop) |
npm run build |
Production build |
npm run start |
Run the production build |
npm run db:init |
Initialize the local SQLite DB |
npm run lint |
ESLint |
- Branding —
src/components/layout/Navbar.tsxuses the generic lucideLibraryicon. Swap it for your own logo. - Home copy —
src/app/page.tsxhas the headline and subheading. Edit them however you like. - Source-label suggestions —
src/components/shelf/DumpBox.tsxhas theSOURCE_SUGGESTIONSarray.
- Default
localadapter works with zero external accounts - No API keys, no LLM keys, no telemetry, no analytics, no phoning home
.env.localis in.gitignore, never committed- All adapters implement the full
StorageAdapterinterface - MIT licensed
See Contributing for project conventions, adapter requirements, and pull request guidelines.
This project is licensed under the MIT License.
