Attachpad
by akshatbatra
README.md
# Attachpad
Attachpad is a browser-scoped context control center for MCP clients. Upload files, import public web pages, arrange sources by importance, and let an MCP-compatible client retrieve focused context instead of sending an entire file library every time.
## Built with GPT-5.6 and Codex
Attachpad was fully built with GPT-5.6 and Codex: from the React Router interface and MCP server to SQLite/Drizzle persistence, document normalization, retrieval, and deployment configuration. Codex was used as the implementation partner throughout the project.
## What it does
- Stores files in a local SQLite database using Drizzle ORM.
- Associates every source with a browser-local session ID.
- Accepts drag-and-drop uploads and public URL imports.
- Converts uploaded PDFs and imported HTML pages into agent-readable Markdown.
- Preserves manual source order and a 1–5 priority weight.
- Indexes normalized content locally with SQLite FTS5.
- Exposes focused retrieval tools through a React Router MCP endpoint.
- Makes no external embedding or search API calls.
## Architecture
```mermaid
flowchart LR
browser["Browser workspace<br/>local session ID"]
client["MCP client<br/>VS Code, ChatGPT, or another client"]
subgraph app["Attachpad React Router server"]
ui["Context control center"]
filesApi["Files API<br/>uploads, imports, ordering"]
mcp["MCP endpoint<br/>JSON-RPC over HTTP"]
importer["Document normalizer<br/>PDF and HTML to Markdown"]
indexer["Chunking and ranking<br/>priority and manual order"]
end
subgraph data["SQLite database"]
files[("files")]
chunks[("attachment chunks")]
fts[("FTS5 search index")]
end
browser --> ui
browser -->|"session-scoped changes"| filesApi
filesApi --> importer
filesApi --> files
filesApi --> indexer
importer --> files
indexer --> chunks
indexer --> fts
client -->|"MCP URL with session ID"| mcp
mcp --> fts
mcp --> files
```
### Request flow
1. The browser creates a UUID and stores it in `localStorage` under `attachpad-session`.
2. Uploads and URL imports send that ID as `x-session-id`.
3. The server stores raw content and normalized Markdown in SQLite.
4. Content is split into chunks and indexed in SQLite FTS5.
5. The MCP client calls a retrieval tool with the same session ID in the MCP URL.
6. Search results combine text relevance, manual order, and priority weight.
## MCP endpoint
The workspace displays a session-specific URL such as:
```text
http://localhost:5173/mcp?session_id=YOUR_SESSION_ID
```
Use that URL as an HTTP MCP server. Keep it private: the URL grants access to that browser session's context.
### Exposed tools
| Tool | Purpose |
| --- | --- |
| `list_attached_files` | Returns metadata only, in manual context order. |
| `search_attached_files` | Searches local SQLite FTS5 chunks using keyword and prefix matching. |
| `read_attached_file` | Reads one file by ID with a character limit. |
| `get_relevant_context` | Searches and assembles ranked chunks within a character budget. |
| `get_attached_files` | Compatibility tool that returns all attached context, or one file by ID. |
Priority semantics are communicated to the MCP client: earlier manual position means higher user preference, and priority `1–5` provides an additional retrieval boost. Relevance still matters.
## Local development
### Requirements
- Node.js 20+
- npm
### Install and run
```bash
npm install
npm run dev
```
Open `http://localhost:5173` in a browser.
### SQLite and Drizzle
The default database is `file:./data/attachpad.db`. The application bootstraps missing tables and indexes when the server first receives a request, including the FTS5 search table.
Optional Drizzle commands:
```bash
npm run db:generate
npm run db:migrate
npm run db:push
```
For the current local bootstrap flow, no database command is required before `npm run dev`.
To use another supported libSQL-compatible URL, set `SQLITE_DATABASE_URL`. PostgreSQL URLs are intentionally ignored because Attachpad uses SQLite/libSQL.
## VS Code setup
Create `.vscode/mcp.json` in the workspace:
```json
{
"servers": {
"attachpad": {
"type": "http",
"url": "http://localhost:5173/mcp?session_id=YOUR_SESSION_ID"
}
}
}
```
Replace `YOUR_SESSION_ID` with the URL copied from the Attachpad MCP settings drawer. Restart the server with `npm run dev`, then use **MCP: List Servers** in VS Code to start or refresh Attachpad.
## Project structure
```text
app/
├── db/
│ ├── db.server.ts # SQLite connection, bootstrap, indexing, retrieval
│ └── schema.ts # Drizzle tables
├── lib/
│ ├── document.server.ts # PDF-to-Markdown normalization
│ └── web-import.server.ts # Safe URL fetching and HTML-to-Markdown
├── routes/
│ ├── home.tsx # Context control center UI
│ ├── api.files.ts # Upload, import, delete, priority, reorder
│ └── mcp.ts # MCP JSON-RPC endpoint and tools
└── routes.ts # React Router route configuration
data/ # Local SQLite database directory
drizzle/ # Drizzle SQL migrations
```
## Security notes
- There is no account system or authentication in this setup.
- The session-specific MCP URL functions as a bearer secret.
- URL imports allow public HTTP(S) targets only and block local/private network addresses.
- Imported pages are limited to 5 MB and a small redirect budget.
- Uploaded scanned PDFs are stored, but text extraction may require OCR in a future step.This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues