Skip to main content
Glama
zyx1121

pad-core

by zyx1121
README.md
```
██████╗  █████╗ ██████╗
██╔══██╗██╔══██╗██╔══██╗
██████╔╝███████║██║  ██║
██╔═══╝ ██╔══██║██║  ██║
██║     ██║  ██║██████╔╝
╚═╝     ╚═╝  ╚═╝╚═════╝
```

# pad

> A HackMD your agents can write to directly: no browser, no human relay.

`mcp` · `bun` · `postgres` · `drizzle` · `agent-to-agent`

[![CI](https://github.com/zyx1121/pad/actions/workflows/build.yml/badge.svg)](https://github.com/zyx1121/pad/actions)  [![version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fzyx1121%2Fpad%2Fmain%2Fpackage.json&query=%24.version&label=version&color=111111)](package.json)  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](#license)

```
> "draft the winlab meeting notes as a pad doc, share it with the lab"
  ⚡ doc_create { title: "winlab meeting notes", visibility: "shared" }
✓ doc created · id k3f9pQz2mTxA · version 1
```

<sub>One tool call, one markdown doc, owned by the agent that wrote it.</sub>

Every agent used to keep its own scratch notes nobody else could reach, so drafts died in one context window and feedback meant pasting text back and forth by hand. pad gives them a shared surface instead: a document store agents write to directly, with real optimistic-lock updates and threaded comments a human can leave without touching an SDK. Storage is boring Postgres; the whole interface is seven MCP tools.

## Install

```bash
claude mcp add --transport http pad https://pad-mcp.app.zyx.tw/mcp --header "Authorization: Bearer <token>"
```

Any other MCP client:

```jsonc
{
  "mcpServers": {
    "pad": {
      "type": "http",
      "url": "https://pad-mcp.app.zyx.tw/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

> [!NOTE]
> The endpoint above is Loki's own cluster; bring your own token, or self-host with `bun run apps/mcp/src/server.ts` (see Dev below).
> Every request is bearer-gated and fails closed: no valid token, no access, ever.

## What it gives you

- **Create and edit docs**: `doc_create` / `doc_update`, optimistic locking via `base_version` so concurrent writes never silently clobber each other.
- **List and filter your own drafts**: `doc_list` by `status` (`draft` · `in_review` · `changes_requested` · `approved`) or tag.
- **Read threaded comments**: `comments_list` re-anchors each comment's line range to the current revision, or flags it outdated if the quoted text is gone.
- **Reply and resolve**: `comment_reply` threads under a parent comment, `comment_resolve` closes the loop.

## Tools

| Tool | Description |
|------|-------------|
| `doc_create` | Create a doc you own; defaults to `private` visibility |
| `doc_read` | Read a doc, returns the current `version` for the next update |
| `doc_update` | Update content; requires `base_version`, returns `version_conflict` if stale |
| `doc_list` | List your docs, optional `status` / `tag` filter |
| `comments_list` | List comments with live-recomputed trust and re-anchored positions |
| `comment_reply` | Reply to a comment thread, posted as the doc owner |
| `comment_resolve` | Mark a comment resolved |

## Trust model

Every comment carries a live-recomputed `trust`: `owner`, `shared`, or `public`. Only `owner` trust is a real instruction to the agent; `shared` and `public` are data for a human to triage. The domain layer also blocks the agent from ever changing a doc's visibility or ACL, so a hostile public comment cannot talk it into publishing or re-sharing something private.

## Dev

```bash
bun install
bun run typecheck
# needs DATABASE_URL, MCP_WRITE_TOKEN (>=16 chars), OWNER_EMAIL
bun run apps/mcp/src/server.ts
```

`packages/domain` is the single authz source, imported by every consumer. `apps/mcp` (**pad-core**) is the only one that exists today: a Bun server that hand-rolls JSON-RPC over `/mcp`, since `Bun.serve` speaks the WHATWG `fetch` API and the standard MCP SDK transport expects Node's `http.ServerResponse`.

## Deploy

CI builds `ghcr.io/zyx1121/pad-core` (SHA-pinned) on every push to `main`; the **vivarium** GitOps repo deploys it to k3s (namespace `pad`) alongside its own Postgres. Reachable at `pad-mcp.app.zyx.tw` (external, token) and `pad-mcp.internal` (in-cluster).

> [!WARNING]
> A human read/comment web app (`apps/web` + `packages/ui`) used to live here and was removed; it is still in git history if it ever comes back, but for now pad only speaks MCP.

## Contributing

Issues and PRs welcome: start with [CONTRIBUTING.md](https://github.com/zyx1121/.github/blob/main/CONTRIBUTING.md).

## License

[MIT](LICENSE) · the only reader that never complains about the markdown formatting