Skip to main content
Glama
fo0

ClawStash

by fo0
README.md
# ClawStash

**Persistent storage for AI agents.** Store text, code, configs, and files — retrieve them via MCP or REST API.

Built for agents like [OpenClaw](https://github.com/openclaw/openclaw) that need a reliable place to save and recall information across sessions.

## Why ClawStash?

AI agents lose context between sessions. ClawStash gives them a persistent memory:

- **Store anything** — code snippets, configs, notes, multi-file projects
- **Organize with tags & metadata** — structured key-value metadata and tags for easy retrieval
- **Full-text search** — find stashes by content, name, description, or tags
- **Token-efficient** — MCP tools return summaries first, full content only on demand
- **Version history** — every change is tracked, diffable, and restorable; the last 200 snapshots per stash are kept (`STASH_VERSION_LIMIT`, `0` = keep everything — [what gets deleted, and when](docs/deployment.md#stash_version_limit--the-one-variable-that-deletes-data))
- **Duplicate a stash** — open any stash as a pre-filled new one and use it as a template
- **Import files from disk** — drop text files onto the editor (or pick them) to add them as file rows, instead of copy-pasting each one
- **GitHub backup** — mirror all stashes into a GitHub repo (scheduled, on change, or manual) with "Sign in with GitHub" or a PAT — see [docs/backup.md](docs/backup.md)
- **Mermaid diagrams** — `.mmd` files and inline ` ```mermaid ` blocks in Markdown render as diagrams (lazy-loaded, no bundle bloat)
- **One-click code copy** — every fenced code block in rendered Markdown gets a copy button (keyboard reachable, always visible on touch)
- **Maximize a file** — blow any one file of a stash (HTML preview, Mermaid diagram, Markdown, code) up to fill the screen; the Restore button or a backdrop click restores it, Esc too once focus is outside the HTML preview
- **Web GUI included** — dark-themed dashboard to browse, search, and manage stashes manually, with a resizable sidebar that remembers its width
- **Update notice** — the footer says when a newer commit is on `main` and links to the commits since your build

## Get Started

### 1. Let your agent do it (recommended)

Copy this into your OpenClaw agent — it installs ClawStash, creates test stashes, and sets up MCP automatically:

```
Install ClawStash (ghcr.io/fo0/clawstash) on my server and set it up as your default persistent storage via MCP. Server: <HOST_OR_IP>, User: <SSH_USER>, Auth: <PASSWORD_OR_KEY>. Use port <PORT> (docker compose port mapping "<PORT>:3000"). Set ADMIN_PASSWORD to a secure value. After install: create an API token (scopes: read, write, mcp), create 2 test stashes to verify, then fetch /api/mcp-onboarding to read the full MCP spec and configure yourself. Details: https://raw.githubusercontent.com/fo0/clawstash/main/docs/openclaw-onboarding-prompt.md
```

Replace the `<...>` placeholders with your server details — your agent handles the rest.

> Step-by-step version: [docs/openclaw-onboarding-prompt.md](docs/openclaw-onboarding-prompt.md)

### 2. Manual setup

Run this on your server — no clone needed:

```bash
mkdir clawstash && cd clawstash && cat > docker-compose.yml <<'EOF'
services:
  clawstash:
    image: ghcr.io/fo0/clawstash:latest
    ports:
      - "3000:3000"
    volumes:
      - ./data:/app/data
    environment:
      - NODE_ENV=production
      - DATABASE_PATH=/app/data/clawstash.db
      # - ADMIN_PASSWORD=your-secret-password
    restart: unless-stopped
EOF
docker compose up -d
```

Open http://localhost:3000 — done. Database persists in `./data/`.

> Change port mapping (e.g. `"8080:3000"`) for a different port. Uncomment `ADMIN_PASSWORD` to protect the instance.
> If login fails and the logs show `SQLITE_READONLY` (data directory created by an older version), run `sudo chown -R 1000:1000 ./data` once and restart — details in [Deployment → Bind mounts & file permissions](docs/deployment.md#bind-mounts--file-permissions).

After starting, hand your AI agent the onboarding prompt from **Settings → API & Tokens → MCP API** ("Copy onboarding prompt for your agent" — the token banner offers the same prompt with a freshly created token filled in). Or point the agent at the server's own guides:

```
GET http://<HOST_OR_IP>:<PORT>/api/agent-skill      # SKILL.md — when to store, workflow, conventions, limits, errors, maintenance
GET http://<HOST_OR_IP>:<PORT>/api/mcp-onboarding   # the skill plus the complete MCP specification (every tool's JSON Schema)
GET http://<HOST_OR_IP>:<PORT>/llms.txt             # discovery index when the agent only knows the host
```

Connected via MCP, the agent gets the same guidance without fetching anything: the server hands out usage `instructions` on `initialize`, exposes the two guides as resources (`clawstash://guide/skill`, `clawstash://guide/onboarding`), and `get_server_info` returns the token's scopes, the callable tools, the size limits and every endpoint in one call.

## MCP Connection

Add to your MCP client config (OpenClaw, Claude Code, Cursor, etc.):

```json
{
  "mcpServers": {
    "clawstash": {
      "type": "streamable-http",
      "url": "http://<HOST_OR_IP>:<PORT>/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}
```

Create API tokens in the web GUI under **Settings > API & Tokens** (scopes: `read`, `write`, `mcp`).

> **Breaking change — MCP tokens need `read` / `write`.** The `mcp` scope is a transport
> gate: it lets a token connect to `/mcp`, and nothing more. Each MCP tool is authorized
> separately, with the same scope its REST equivalent requires — `read` to read stashes,
> `write` for `create_stash`, `update_stash`, `archive_stash` and `delete_stash`. Until
> now the MCP endpoint checked only `mcp`, so a token carrying just that scope could write
> through MCP while every REST write route rejected it.
>
> **Who is affected:** anyone whose agent uses a token with `mcp` but without `write` (or
> without `read`). Its calls now come back as MCP tool errors naming the missing scope.
> **Fix:** issue a new token with `read`, `write` and `mcp` and swap it into the agent's
> MCP config. Expect to need this: the scope combination was recommended here and in
> [docs/openclaw-onboarding-prompt.md](docs/openclaw-onboarding-prompt.md), but the
> self-onboarding endpoint `GET /api/mcp-onboarding` and [docs/mcp.md](docs/mcp.md) named
> only `mcp` until this release — a token created by following those two must be reissued.
> Unaffected: tokens that already carry `read`/`write`/`mcp` (or `admin`), and the local
> **stdio** transport, which carries no token at all.

## MCP Tools

| Tool                | What it does                                       |
| ------------------- | -------------------------------------------------- |
| `create_stash`      | Store new content with files, tags, metadata       |
| `read_stash`        | Get stash metadata + file list (content on demand) |
| `read_stash_file`   | Read a single file — most token-efficient          |
| `list_stashes`      | Browse all stashes with summaries                  |
| `search_stashes`    | Full-text search with ranked results               |
| `update_stash`      | Update existing stash content                      |
| `delete_stash`      | Remove a stash                                     |
| `archive_stash`     | Archive/unarchive a stash without deleting         |
| `list_tags`         | List all tags with usage counts                    |
| `get_tag_graph`     | Explore tag relationships                          |
| `get_stats`         | Storage statistics                                 |
| `get_rest_api_spec` | Fetch the OpenAPI 3.0 schema (JSON)                |
| `get_mcp_spec`      | Fetch the full MCP specification (markdown)        |
| `refresh_tools`     | Get latest tool specs (for connected agents)       |
| `check_version`     | Check for updates (with upgrade instructions)      |
| `get_server_info`   | Orient: scopes, callable tools, limits, endpoints  |

## Documentation

| Doc                                                       | Content                                                |
| --------------------------------------------------------- | ------------------------------------------------------ |
| [OpenClaw Onboarding](docs/openclaw-onboarding-prompt.md) | Copy-paste prompt for full agent-driven setup          |
| [API Reference](docs/api-reference.md)                    | REST endpoints, examples, query parameters             |
| [MCP Guide](docs/mcp.md)                                  | MCP tools, token-efficient patterns, transport options |
| [Authentication](docs/authentication.md)                  | Admin login, API tokens, scopes                        |
| [GitHub Backup](docs/backup.md)                           | Mirror stashes into a GitHub repo: setup, security     |
| [Deployment](docs/deployment.md)                          | Docker, CI/CD, GHCR, production setup                  |

## Development

**Prerequisites:** Node.js 22+ in practice. `package.json` declares `engines: { "node": ">=20.9.0" }`, so `npm install` prints an `EBADENGINE` warning below 20.9 — but `better-sqlite3` 13.x declares `engines: { "node": ">=22" }`, which warns on Node 20.x and 21.x as well. Docker and CI run Node 26.

```bash
git clone https://github.com/fo0/clawstash.git
cd clawstash
npm install
cp .env.example .env   # adjust DATABASE_PATH / ADMIN_PASSWORD as needed

npm run dev            # Next.js dev server at http://localhost:3000
npm run format         # auto-format with Prettier (CI verifies formatting via format:check)
npm run lint           # ESLint (correctness rules; formatting stays with Prettier)
npx tsc --noEmit       # TypeScript type check (no npm script — CI runs this exact command)
npm test               # vitest test suite (npm run test:watch for watch mode)
npm run build          # production build
npm start              # serve the production build
npm run mcp            # MCP server (stdio transport, for local MCP client testing)
```

`docker-publish.yml` runs that same chain (`format:check` → `tsc --noEmit` → `lint` → `test` → `build`, in
that order) before it builds the image — but it is `workflow_dispatch`-only, so it does **not** run
on a push or a pull request. The only checks that run automatically on a PR are `docs-format.yml`
(Prettier on `**.md`) and GitHub's CodeQL analysis. The local chain above is therefore the real gate
for correctness: run it before every push.

See [CONTRIBUTING.md](CONTRIBUTING.md) for code-style rules and the PR workflow.

## License

[MIT](LICENSE)