Encrypted, ephemeral, private memos on atproto
1# @cistern/mcp
2
3Model Context Protocol (MCP) server for Cistern, enabling AI assistants to retrieve and manage encrypted memos.
4
5## Features
6
7- **Dual Transport Support**: stdio for local integrations (Claude Desktop) and HTTP for remote deployments
8- **Automatic Keypair Management**: Generates and persists keypairs in Deno KV on first launch
9- **Two MCP Tools**:
10 - `next_memo`: Retrieve the next outstanding memo
11 - `delete_memo`: Delete a memo after handling it
12
13## Installation
14
15### Prerequisites
16
17- Deno 2.0+
18- AT Protocol account with app password
19- Bluesky handle (e.g., `yourname.bsky.social`)
20
21### Environment Variables
22
23**Required:**
24```bash
25CISTERN_MCP_HANDLE=yourname.bsky.social
26CISTERN_MCP_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
27```
28
29**Optional (for existing keypair):**
30```bash
31CISTERN_MCP_PRIVATE_KEY=base64-encoded-private-key
32CISTERN_MCP_PUBLIC_KEY_URI=at://did:plc:abc.../app.cistern.pubkey/xyz
33```
34
35**Required for HTTP mode:**
36```bash
37CISTERN_MCP_BEARER_TOKEN=your-secret-bearer-token
38```
39
40## Usage
41
42### stdio Mode (Claude Desktop)
43
44Run the server in stdio mode for local integrations:
45
46```bash
47cd packages/mcp
48deno task stdio
49```
50
51Add to Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json`):
52
53```json
54{
55 "mcpServers": {
56 "cistern": {
57 "command": "deno",
58 "args": [
59 "task",
60 "--cwd",
61 "/path/to/cistern/packages/mcp",
62 "stdio"
63 ],
64 "env": {
65 "CISTERN_MCP_HANDLE": "yourname.bsky.social",
66 "CISTERN_MCP_APP_PASSWORD": "xxxx-xxxx-xxxx-xxxx"
67 }
68 }
69 }
70}
71```
72
73### HTTP Mode (Remote Deployment)
74
75Run the server in HTTP mode for remote access:
76
77```bash
78cd packages/mcp
79deno task http
80```
81
82The server listens on port 8000 by default. Configure your MCP client to connect via HTTP:
83
84```json
85{
86 "url": "http://localhost:8000/mcp",
87 "headers": {
88 "Authorization": "Bearer your-secret-bearer-token"
89 }
90}
91```
92
93## Keypair Management
94
95On first launch without `CISTERN_MCP_PRIVATE_KEY` and `CISTERN_MCP_PUBLIC_KEY_URI`, the server will:
96
971. Check Deno KV for a stored keypair (keyed by handle)
982. If not found, generate a new X-Wing keypair
993. Upload the public key to your PDS as an `app.cistern.pubkey` record
1004. Store the keypair in Deno KV at `./cistern-mcp.db`
1015. Log the public key URI for reference
102
103The keypair persists across restarts and is isolated per handle.
104
105### Example First Launch Log
106
107```
108[cistern:mcp] starting in stdio mode
109[cistern:mcp] no keypair found; generating new keypair for yourname.bsky.social
110[cistern:mcp] generated new keypair with public key URI: at://did:plc:abc123.../app.cistern.pubkey/xyz789
111[cistern:mcp] stored keypair for yourname.bsky.social
112```
113
114### Example Subsequent Launch Log
115
116```
117[cistern:mcp] starting in stdio mode
118[cistern:mcp] using stored keypair for yourname.bsky.social
119```
120
121## MCP Tools
122
123### `next_memo`
124
125Retrieves the next outstanding memo from your PDS.
126
127**Output:**
128```json
129{
130 "key": "3kbxyz789abc",
131 "tid": "3kbxyz789abc",
132 "text": "Remember to buy milk"
133}
134```
135
136Returns `"no memos remaining"` when all memos have been retrieved.
137
138### `delete_memo`
139
140Deletes a memo by record key after it has been handled.
141
142**Input:**
143```json
144{
145 "key": "3kbxyz789abc"
146}
147```
148
149**Output:**
150```json
151{
152 "success": true
153}
154```
155
156## Development
157
158### Testing with MCP Inspector
159
160```bash
161deno task stdio:inspect
162```
163
164This launches the MCP Inspector UI for interactive testing of the stdio server.
165
166### Logs
167
168The server uses LogTape for structured logging:
169
170- **`[cistern:mcp]`**: Server lifecycle, keypair operations
171- **`[cistern:http]`**: HTTP request/response logs (HTTP mode only)
172
173## Security
174
175- **Bearer Authentication**: Required for HTTP mode
176- **Private Keys**: Never transmitted; stored locally in Deno KV
177- **Session Isolation**: Each HTTP session gets its own Consumer instance
178- **CORS**: Configured for MCP protocol headers
179
180## Limitations
181
182- **No Keypair Deletion**: The Consumer SDK doesn't currently support deleting public keys from the PDS. If you want to use a different keypair, you can either set `CISTERN_MCP_PRIVATE_KEY` and `CISTERN_MCP_PUBLIC_KEY_URI` environment variables, or delete the `cistern-mcp.db` SQLite files to force regeneration. You'll need to manually delete the old public key record from your PDS using a tool like [pdsls.dev](https://pdsls.dev).