An POP-like inbox for my note taking system. Rather than pushing things directly into my notes, I hold them here until I'm ready to thoughtfully engage with them. This separation allows me to capture freely in the moment, then process intentionally when I'm ready.
When I yeet raw information into my vault, I don't get any benefit from it. This POP-like approach forces me to engage with things before storing them more permanently.
Also, more technically, Obsidian doesn't have a push-friendly API - you can't just send data to your vault from anywhere. Instead, you have to pull data into the vault from a client. Doing so requires a unified receptacle, that's this val!
See CLAUDE.md for AI assistant guidelines and PROJECT.md for detailed architecture.
-
Remix on Val.town
-
Run one-time database setup
Run the one-time setup script to create the database table in your val.town sqlite database.
-
Configure Environment Variables
In val.town, set these secrets:
INBOX_API_TOKEN- Create a secure random token for API auth (via e.g.openssl rand -hex 32)INCOMING_EMAIL_ALLOWLIST- Comma-separated email addresses allowed to send records to the inbox (e.g.name@gmail.com,name@work.org)
-
Set up MCP Server (for Claude Desktop integration)
Create
~/Library/Application Support/Claude/claude_desktop_config.json:{ "mcpServers": { "inbox": { "command": "deno", "args": [ "run", "--allow-net", "--allow-env", "/path/to/inbox/localMcpServer.ts" ], "env": { "INBOX_API_URL": "https://your-username-httphandler.web.val.run", "INBOX_API_TOKEN": "your-api-token" } } } }Alternatively, use the published val URL:
"command": "deno", "args": [ "run", "--allow-net", "--allow-env", "https://esm.town/v/your-username/inbox/localMcpServer.ts" ]
obsidian-inbox/
├── server/ # Core http server logic
├── mcp/ # Model Context Protocol server
├── emailHandler.ts # Val.town email trigger
├── httpHandler.ts # Val.town HTTP endpoint
├── cleanupCron.ts # Val.town cron job
├── localMcpServer.ts # Entry point for local MCP
└── apiClient.ts # HTTP client (used by MCP)
Via Email: Send email to your Val.town email address from an allowlisted sender. The email body and attachments are automatically saved.
Via HTTP API:
curl -X POST https://your-val.web.val.run/api/record \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "raw": "Content to process later", "subject": "Meeting notes", "from": "manual-entry", "date": "2025-10-21T10:00:00Z", "attachmentBlobIds": [] }'
With Claude Desktop (MCP): In Claude Desktop, ask:
- "What's in my inbox?"
- "Download the attachments from inbox item XYZ"
- "Mark inbox item ABC as processed"
With Custom Scripts:
# Get unprocessed items curl https://your-val.web.val.run/api/inbox \ -H "Authorization: Bearer YOUR_TOKEN" # Mark item as processed curl -X PATCH https://your-val.web.val.run/api/record/{id}/processed \ -H "Authorization: Bearer YOUR_TOKEN"
All endpoints require Authorization: Bearer YOUR_TOKEN header.
| Method | Path | Description |
|---|---|---|
GET | / | Health check (no auth required) |
GET | /api/inbox | Retrieve all unprocessed records |
POST | /api/record | Create new inbox record |
PATCH | /api/record/:id/processed | Mark record as processed |
GET | /api/record/:recordId/attachments | Get attachment metadata |
GET | /api/record/:recordId/attachments/:blobId | Download attachment file |
Go nuts, but know this was largely vibe-coded