virlow-mcp 3.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/cli.js +2030 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Typelets Secure Notes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# virlow-mcp
|
|
2
|
+
|
|
3
|
+
A local [MCP](https://modelcontextprotocol.io) server that gives any MCP-compatible AI
|
|
4
|
+
client (Claude Code, Claude Desktop, Cursor, Codex, and others) durable memory and note
|
|
5
|
+
access backed by [Virlow](https://virlow.com) — with your data end-to-end encrypted the
|
|
6
|
+
entire time.
|
|
7
|
+
|
|
8
|
+
## What it is
|
|
9
|
+
|
|
10
|
+
virlow-mcp runs on your machine as a local process (`npx -y virlow-mcp`, launched by your
|
|
11
|
+
AI client over stdio). It exposes 19 tools for two things:
|
|
12
|
+
|
|
13
|
+
- **Memories** — short, durable facts ("the user prefers TypeScript", "this repo's CI
|
|
14
|
+
runs on self-hosted runners") that any AI tool can add, search semantically, and recall
|
|
15
|
+
across sessions and projects. Each memory is an ordinary encrypted note in your
|
|
16
|
+
**Memories** folder, so you can read, edit, reorganise, and trash them in the Virlow
|
|
17
|
+
app like anything else you have written.
|
|
18
|
+
- **Notes** — full read/write access to your existing Virlow notes: search, list, read,
|
|
19
|
+
create, update, move, star, archive, and trash, plus folder management.
|
|
20
|
+
|
|
21
|
+
Everything is **end-to-end encrypted, zero-knowledge**: the encryption key is derived
|
|
22
|
+
from your Virlow master password inside this local process and never leaves it. Every
|
|
23
|
+
memory and note is encrypted on your machine before it is sent to the Virlow API — the
|
|
24
|
+
server stores only ciphertext. Decryption happens only in this local process, in memory —
|
|
25
|
+
no decrypted text is ever written to disk.
|
|
26
|
+
|
|
27
|
+
Your account credentials and master password are just as protected from the AI: they are
|
|
28
|
+
entered exclusively into a local browser form served by this process, never passed
|
|
29
|
+
through a tool call, and never seen by the AI model.
|
|
30
|
+
|
|
31
|
+
## Install / configure
|
|
32
|
+
|
|
33
|
+
virlow-mcp needs no separate install — `npx -y virlow-mcp` fetches and runs it directly.
|
|
34
|
+
Add it to your client's MCP config:
|
|
35
|
+
|
|
36
|
+
### Claude Code
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
claude mcp add virlow -- npx -y virlow-mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Claude Desktop
|
|
43
|
+
|
|
44
|
+
Add to your `claude_desktop_config.json`
|
|
45
|
+
(macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`,
|
|
46
|
+
Windows: `%APPDATA%\Claude\claude_desktop_config.json`):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"virlow": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "virlow-mcp"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Cursor
|
|
60
|
+
|
|
61
|
+
Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project-local):
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"virlow": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "virlow-mcp"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Codex CLI
|
|
75
|
+
|
|
76
|
+
Codex's MCP config is TOML, not JSON. Add to `~/.codex/config.toml`:
|
|
77
|
+
|
|
78
|
+
```toml
|
|
79
|
+
[mcp_servers.virlow]
|
|
80
|
+
command = "npx"
|
|
81
|
+
args = ["-y", "virlow-mcp"]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Self-hosting Virlow
|
|
85
|
+
|
|
86
|
+
By default virlow-mcp talks to `https://api.virlow.com`. If you run your own Virlow API,
|
|
87
|
+
point it there with an `env` block (JSON clients) or `env` table (Codex's TOML), e.g. for
|
|
88
|
+
Claude Desktop / Cursor:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"virlow": {
|
|
94
|
+
"command": "npx",
|
|
95
|
+
"args": ["-y", "virlow-mcp"],
|
|
96
|
+
"env": { "VIRLOW_API_URL": "https://api.your-domain.com" }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## First use
|
|
103
|
+
|
|
104
|
+
1. Ask your AI client to run the **`unlock`** tool. This opens a browser window pointed
|
|
105
|
+
at a page served by the local virlow-mcp process — sign in with your Virlow account
|
|
106
|
+
email/password (and 2FA code, if enabled) and enter your master password there.
|
|
107
|
+
2. Once the form succeeds, ask the AI to run **`status`** to confirm the vault is
|
|
108
|
+
unlocked and which account it's unlocked as.
|
|
109
|
+
3. From then on, `add_memory`, `search_memory`, `search_notes`, and the rest of the tools
|
|
110
|
+
work normally until the vault locks again (see Security notes).
|
|
111
|
+
|
|
112
|
+
## How memories are stored
|
|
113
|
+
|
|
114
|
+
Memories live in a single folder marked as your memories root — shown with a brain icon
|
|
115
|
+
in the app, and created by this process the first time you add one. Its **subfolders are
|
|
116
|
+
namespaces**: `add_memory` with `namespace: "acme-api"` files the memory under
|
|
117
|
+
`Memories/acme-api`, creating that subfolder if needed. Memories with no namespace sit in
|
|
118
|
+
the root and are visible to every namespace's searches.
|
|
119
|
+
|
|
120
|
+
Each memory is a **code note** holding YAML frontmatter above the fact itself:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
---
|
|
124
|
+
source: claude-code
|
|
125
|
+
tags: [ui, preferences]
|
|
126
|
+
confidence: high
|
|
127
|
+
created: 2026-09-01T00:12:04.000Z
|
|
128
|
+
---
|
|
129
|
+
Prefers dark mode in every editor, including terminal themes.
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The note's title is the memory's label. Because it is a code note, the app opens it in a
|
|
133
|
+
plain text editor rather than the rich-text one, so nothing reformats it behind your back
|
|
134
|
+
and every field is yours to edit.
|
|
135
|
+
|
|
136
|
+
Everything about that is safe to change by hand:
|
|
137
|
+
|
|
138
|
+
- **Edit the fact** and the next search reflects it — the memory re-embeds automatically.
|
|
139
|
+
- **Edit the metadata** and the tools report your values.
|
|
140
|
+
- **Break the YAML** and nothing is lost: the memory is simply read as all fact and no
|
|
141
|
+
metadata, still searchable, and the next `update_memory` rewrites it cleanly.
|
|
142
|
+
- **Drag it to another subfolder** and its namespace follows.
|
|
143
|
+
- **Trash it** and it stops being recalled, recoverable from Trash like any note.
|
|
144
|
+
|
|
145
|
+
Deliberately *not* in the frontmatter: the memory's id, its label, and its namespace.
|
|
146
|
+
Those are the note's id, its title, and the folder it sits in — duplicating them would
|
|
147
|
+
just give you two versions to keep in sync.
|
|
148
|
+
|
|
149
|
+
## Reaching your vault from a session that is not on this machine
|
|
150
|
+
|
|
151
|
+
By default `virlow-mcp` speaks stdio, so your AI client spawns it as a child
|
|
152
|
+
process and everything stays on one machine. `--http` serves the same 19 tools
|
|
153
|
+
over HTTP instead, so a client that cannot spawn a local process — a cloud
|
|
154
|
+
coding session, another laptop — can reach this vault:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npx -y virlow-mcp --http # loopback, random port
|
|
158
|
+
npx -y virlow-mcp --http --port 8787 # pick the port
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
It prints the URL and the path to a bearer token, generated on first run and
|
|
162
|
+
stored `0600` at `~/.virlow-mcp/http-token`. Every request must carry it:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
Authorization: Bearer <token>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Your vault does not move.** The master key is still derived and held in this
|
|
169
|
+
process on your machine; `unlock` still opens its form in your local browser.
|
|
170
|
+
Virlow's servers still see only ciphertext.
|
|
171
|
+
|
|
172
|
+
**What does change:** decrypted note and memory text now travels over that
|
|
173
|
+
connection to whatever client you point at it. So:
|
|
174
|
+
|
|
175
|
+
- The listener binds `127.0.0.1` by default. `--host` will bind elsewhere and
|
|
176
|
+
warns loudly, because anyone who can reach the port and holds the token can
|
|
177
|
+
read your decrypted notes.
|
|
178
|
+
- To reach it from outside, put your own tunnel in front of it
|
|
179
|
+
(`cloudflared`, `ssh -R`, Tailscale) rather than binding to `0.0.0.0`.
|
|
180
|
+
- Treat the token like a password. Delete the file to rotate it; the next start
|
|
181
|
+
mints a new one.
|
|
182
|
+
|
|
183
|
+
## Which folders AI tools can see
|
|
184
|
+
|
|
185
|
+
Every folder in the Virlow app has an **"Expose to MCP"** switch — *"Let the
|
|
186
|
+
local AI assistant read & edit this folder and its subfolders. Off by
|
|
187
|
+
default."* This server now honours it. Until this version it did not, so the
|
|
188
|
+
app was telling you something untrue: the switch saved a value that nothing
|
|
189
|
+
ever read.
|
|
190
|
+
|
|
191
|
+
- A folder is visible when **it or any ancestor** is switched on, matching what
|
|
192
|
+
the label promises. Turning a parent on opens everything beneath it.
|
|
193
|
+
- `read_note` respects it too. Knowing a note's id is not consent.
|
|
194
|
+
- The **Memories** folder is exempt — this server creates it and it exists only
|
|
195
|
+
for AI tools.
|
|
196
|
+
- A note in **no folder** is not visible: exposure is granted per folder, and
|
|
197
|
+
nothing has granted it. File it into an exposed folder to include it.
|
|
198
|
+
|
|
199
|
+
**Filtering is never silent.** Any result that withheld something says so, with
|
|
200
|
+
the count and what to do about it:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
(4 notes hidden in folders that are not exposed to MCP. Turn on "Expose to MCP"
|
|
204
|
+
in a folder's settings in the Virlow app to include it.)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
A shortened list is otherwise indistinguishable from an empty vault, and you
|
|
208
|
+
would have no reason to suspect a setting. Since the switch is off by default,
|
|
209
|
+
expect to see this until you turn some folders on — that is the setting
|
|
210
|
+
working, not a fault.
|
|
211
|
+
|
|
212
|
+
## Turning memories off
|
|
213
|
+
|
|
214
|
+
Memories are **opt-in**. The switch lives in the Virlow app under
|
|
215
|
+
**Settings → Security → AI memories**, and it is off until you turn it on. With
|
|
216
|
+
it off, every memory tool refuses:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
AI memories are switched off for this account. Turn on "AI memories" in
|
|
220
|
+
Settings → Security in the Virlow app to use this tool.
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The setting is re-read from your account on **every** memory tool call, never
|
|
224
|
+
cached for the session. Turning it *on* being slow would be an inconvenience;
|
|
225
|
+
turning it *off* being slow would be a privacy control that does not work — you
|
|
226
|
+
would switch it off, keep being recorded, and have no way to tell. If the
|
|
227
|
+
account cannot be reached, the tools refuse rather than assume consent.
|
|
228
|
+
|
|
229
|
+
`--no-memories` additionally starts the server with the five memory tools
|
|
230
|
+
unregistered, so they do not appear in `tools/list` at all:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
npx -y virlow-mcp --no-memories
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Use it when you want a connector that structurally cannot touch memories,
|
|
237
|
+
regardless of the account setting. Notes tools, `unlock`, `lock`, and `status`
|
|
238
|
+
are unaffected either way (14 tools instead of 19).
|
|
239
|
+
|
|
240
|
+
## Suggested agent rules
|
|
241
|
+
|
|
242
|
+
Drop this into your client's persistent instructions (`.cursorrules`, `CLAUDE.md`,
|
|
243
|
+
Codex's `AGENTS.md`, etc.) so the AI uses memory proactively instead of only when asked:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
You have Virlow memory tools. At the start of a task, call search_memory
|
|
247
|
+
with the task topic to recall relevant facts. When you learn a durable
|
|
248
|
+
fact about the user, their preferences, or this project (a decision, a
|
|
249
|
+
convention, a gotcha), store it with add_memory — one concise fact per
|
|
250
|
+
call, a short label describing it, and namespace set to the project name
|
|
251
|
+
for project-specific facts. Do not store secrets, credentials, or
|
|
252
|
+
transient details.
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Security notes
|
|
256
|
+
|
|
257
|
+
- **What's stored where.** The Virlow API stores only ciphertext: encrypted note titles
|
|
258
|
+
and content, memories included. The plaintext-looking `title`/`content` columns for
|
|
259
|
+
encrypted notes hold a literal `[ENCRYPTED]` placeholder, not your data. No decrypted
|
|
260
|
+
text ever touches disk — decryption happens in this process's memory only, per tool
|
|
261
|
+
call, and is discarded when the vault locks. Folder names are the one exception, and
|
|
262
|
+
always have been: they are stored in the clear, so a namespace name is visible to the
|
|
263
|
+
server even though everything in it is not.
|
|
264
|
+
- **Embedding cache.** Search vectors are cached in `~/.virlow-mcp/cache` so a session
|
|
265
|
+
starts without re-embedding every memory. The file is a single AES-GCM blob under your
|
|
266
|
+
master key — note ids and timestamps included — and its name is a hash of your user id.
|
|
267
|
+
Without your master password it is unreadable, and if it cannot be decrypted (say after
|
|
268
|
+
a password change) it is silently discarded and rebuilt. It holds vectors only, never
|
|
269
|
+
the text they came from.
|
|
270
|
+
- **Credentials never reach the AI.** Login, master password, and any 2FA code are typed
|
|
271
|
+
into the local browser form (served on `127.0.0.1` with a random one-time nonce) and
|
|
272
|
+
go straight from your browser to this process and the Virlow API. No tool call ever
|
|
273
|
+
carries a password, and the AI model never sees one.
|
|
274
|
+
- **Auto-lock.** The vault locks automatically after 4 hours of inactivity, and
|
|
275
|
+
immediately on explicit `lock`. Locking discards the in-memory decryption key and all
|
|
276
|
+
cached memories/notes; any further tool call fails until you `unlock` again. An
|
|
277
|
+
operation still in flight when the vault locks cannot write its results back in
|
|
278
|
+
afterwards.
|
|
279
|
+
- **Model download.** Semantic search over memories runs a small, public embedding
|
|
280
|
+
model (`Xenova/all-MiniLM-L6-v2`, ~30MB of open ONNX weights, pinned to a fixed
|
|
281
|
+
revision) entirely locally via `@huggingface/transformers`. It downloads once, on the
|
|
282
|
+
first memory-tool call, into `~/.virlow-mcp/models`, and is reused after that. This
|
|
283
|
+
model only ever processes your already-decrypted plaintext locally to produce a
|
|
284
|
+
vector — it sends nothing anywhere.
|
|
285
|
+
- **Dedupe.** `add_memory` compares the new fact's embedding against existing memories
|
|
286
|
+
in the same scope (same namespace, or global). At cosine similarity ≥ 0.9 it updates
|
|
287
|
+
the closest match in place instead of creating a duplicate, carrying that memory's
|
|
288
|
+
existing metadata and creation date forward. Between 0.7 and 0.9 it stores the new
|
|
289
|
+
memory and tells the agent which existing ones it resembles. Only the label and the
|
|
290
|
+
fact are embedded — never the frontmatter, which is near-identical across every memory
|
|
291
|
+
and would otherwise distort these thresholds.
|
|
292
|
+
- **Hidden notes.** Hidden notes never appear in `list_notes`/`search_notes` results; they
|
|
293
|
+
can still be read by exact id with `read_note`.
|