memoryintelligence-mcp 0.1.6__tar.gz → 0.1.8__tar.gz

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.
Files changed (18) hide show
  1. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/CHANGELOG.md +41 -5
  2. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/PKG-INFO +51 -20
  3. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/README.md +50 -19
  4. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/pyproject.toml +1 -1
  5. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/__init__.py +1 -1
  6. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/cli.py +65 -36
  7. memoryintelligence_mcp-0.1.8/src/mi_mcp/paths.py +129 -0
  8. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/server.py +3 -3
  9. memoryintelligence_mcp-0.1.6/src/mi_mcp/paths.py +0 -86
  10. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/.gitignore +0 -0
  11. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/CONTRIBUTING.md +0 -0
  12. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/LICENSE +0 -0
  13. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/__main__.py +0 -0
  14. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/client.py +0 -0
  15. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/config.py +0 -0
  16. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/keys.py +0 -0
  17. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/umo_format.py +0 -0
  18. {memoryintelligence_mcp-0.1.6 → memoryintelligence_mcp-0.1.8}/src/mi_mcp/vault.py +0 -0
@@ -3,6 +3,41 @@
3
3
  All notable changes to `memoryintelligence-mcp` are documented here.
4
4
  The format follows [Keep a Changelog](https://keepachangelog.com/); this project uses [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.1.8] — 2026-06-10
7
+
8
+ ### Changed
9
+ - **The MCP server id is now `memoryintelligence`** (was `memory-intelligence`).
10
+ This is the id under `mcpServers` in your config and the name in
11
+ `claude mcp add …`. It now matches the brand/package token everywhere else
12
+ (`MemoryIntelligence`, `memoryintelligence-mcp`) instead of splitting the word
13
+ with a dash.
14
+ - **Auto-migration:** `mi-mcp wire`/`setup` removes the old `memory-intelligence`
15
+ entry from every surface (file configs **and** `claude mcp remove`) before
16
+ adding the new id, so an upgrade leaves no duplicate/orphan. `mi-mcp doctor`
17
+ flags a leftover legacy entry and tells you to re-wire. **Action on upgrade:
18
+ run `mi-mcp wire` once.** (Hand-edited configs: rename the key yourself.)
19
+
20
+ ## [0.1.7] — 2026-06-09
21
+
22
+ ### Changed
23
+ - **Hidden files now live under `~/.memoryintelligence/`** (was `~/.mi/`), matching
24
+ the visible `~/MemoryIntelligence/` vault — one on-brand namespace:
25
+ - launcher → `~/.memoryintelligence/mcp/run-mi-mcp.sh`
26
+ - capture opt-in allowlist → `~/.memoryintelligence/mcp/opt-in-paths`
27
+ - keyfile (the Keychain fallback) → `~/.memoryintelligence/.env`
28
+
29
+ `mi-mcp setup`/`wire` write the new layout and migrate an existing
30
+ `~/.mi/opt-in-paths` forward non-destructively. The legacy `~/.mi/` launcher
31
+ and `~/.mi-env` keyfile are still **read** for one release, so existing
32
+ installs keep working until you re-run `wire`. `paths.py` is now the single
33
+ source of truth for the layout (it previously declared the new layout while the
34
+ CLI still wrote `~/.mi/` — that split is fixed here).
35
+
36
+ ### Added
37
+ - **README "Names & locations" map** — explains the package / command / server-id
38
+ names (`memoryintelligence-mcp` / `mi-mcp` / `memory-intelligence`) and where
39
+ every file lives, so the naming no longer looks arbitrary.
40
+
6
41
  ## [0.1.6] — 2026-06-09
7
42
 
8
43
  ### Security
@@ -12,11 +47,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/); this project
12
47
  the key resolved from (Keychain / keyfile / env), never the key itself.
13
48
 
14
49
  ### Added
15
- - **MCP ↔ API contract tests** (`tests/test_api_contract.py`) pin that every
16
- client method sends only parameter values the API accepts (`explain`,
17
- `pii_handling`, `retention_policy`, `scope`), so an API enum/type change can
18
- never silently 422 a real call. This is the general form of the `explain`
19
- bool→enum bug fixed in 0.1.3.
50
+ - **MCP ↔ API contract tests** (`tests/test_api_contract.py`, run in CI — not
51
+ shipped in the published package) pin that every client method sends only
52
+ parameter values the API accepts (`explain`, `pii_handling`,
53
+ `retention_policy`, `scope`), so an API enum/type change can never silently
54
+ 422 a real call. This is the general form of the `explain` bool→enum bug
55
+ fixed in 0.1.3.
20
56
 
21
57
  ### Changed
22
58
  - Internal code-quality cleanups (narrowed a few broad `except` clauses, removed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memoryintelligence-mcp
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: MCP server for MemoryIntelligence — give Claude a memory you own, set up in one command
5
5
  Project-URL: Homepage, https://memoryintelligence.io
6
6
  Project-URL: Repository, https://github.com/somewhere11/memoryintelligence-mcp
@@ -72,7 +72,7 @@ API key is ever written into a config file.**
72
72
 
73
73
  ---
74
74
 
75
- ## ✅ What works today (0.1.5)
75
+ ## ✅ What works today (0.1.7)
76
76
 
77
77
  Honest status — this is beta, so here's exactly what's live:
78
78
 
@@ -177,7 +177,7 @@ provenance — runs in the service. Your API key authenticates to *your* account
177
177
 
178
178
  1. **prompts for your API key** (hidden input);
179
179
  2. **stores it securely, outside every config** — macOS **Keychain**, or a
180
- `chmod 600 ~/.mi-env` keyfile on Linux/Windows (or with `--store file`);
180
+ `chmod 600 ~/.memoryintelligence/.env` keyfile on Linux/Windows (or with `--store file`);
181
181
  3. **wires** the server into Claude Desktop + Claude Code (`--surfaces` to choose
182
182
  `desktop,code,cursor`), writing `env: {}` — **no key in any config file**;
183
183
  4. **opts in** the current directory so captures are allowed there (reads work
@@ -187,7 +187,7 @@ provenance — runs in the service. Your API key authenticates to *your* account
187
187
  ```bash
188
188
  mi-mcp setup # the happy path (interactive)
189
189
  mi-mcp setup --surfaces desktop,code,cursor
190
- mi-mcp setup --store file # force the ~/.mi-env keyfile (e.g. on Linux)
190
+ mi-mcp setup --store file # force the ~/.memoryintelligence/.env keyfile (e.g. on Linux)
191
191
  mi-mcp setup --no-opt-in # wire only; opt a folder in later
192
192
  ```
193
193
 
@@ -202,12 +202,12 @@ mi-mcp wire --dry-run # preview wiring changes without writing
202
202
 
203
203
  ### How the key stays out of your configs
204
204
 
205
- `wire` points each host at a small launcher (`~/.mi/run-mi-mcp.sh`) that resolves
206
- `MI_API_KEY` **at launch**, in order:
205
+ `wire` points each host at a small launcher (`~/.memoryintelligence/mcp/run-mi-mcp.sh`)
206
+ that resolves `MI_API_KEY` **at launch**, in order:
207
207
 
208
208
  1. the process environment, then
209
209
  2. the macOS **Keychain** (`security find-generic-password -s MI_API_KEY`), then
210
- 3. a gitignored `~/.mi-env` (`chmod 600`), else it fails.
210
+ 3. a `chmod 600` keyfile (`~/.memoryintelligence/.env`, or the legacy `~/.mi-env`), else it fails.
211
211
 
212
212
  So a leaked or committed config file exposes **nothing**.
213
213
 
@@ -220,12 +220,12 @@ So a leaked or committed config file exposes **nothing**.
220
220
  ## Security
221
221
 
222
222
  - **No key in configs.** `setup`/`wire` write `env: {}`; the launcher resolves the
223
- key from the Keychain (or `~/.mi-env`) at runtime. Nothing sensitive lands in a
224
- config file.
223
+ key from the Keychain (or the `~/.memoryintelligence/.env` keyfile) at runtime.
224
+ Nothing sensitive lands in a config file.
225
225
  - **Capture is opt-in per directory.** Write tools (`mi_capture`/`mi_batch`/
226
226
  `mi_upload`) only run when the server's working directory is on the
227
- `~/.mi/opt-in-paths` allowlist. Reads are never gated. Absent allowlist → all
228
- captures are skipped.
227
+ `~/.memoryintelligence/mcp/opt-in-paths` allowlist. Reads are never gated. Absent
228
+ allowlist → all captures are skipped.
229
229
  - **Destructive ops require confirmation.** `mi_forget` (irreversible delete)
230
230
  requires an explicit `confirm=true` argument — a human-in-the-loop guard against
231
231
  injected or accidental deletes.
@@ -248,8 +248,8 @@ So a leaked or committed config file exposes **nothing**.
248
248
  - **Privacy.** Content you capture is sent to your MemoryIntelligence account over
249
249
  HTTPS; nothing else is transmitted, and the server does not log conversation
250
250
  content or your API key. See [memoryintelligence.io/privacy](https://memoryintelligence.io/privacy).
251
- - **Off switch.** Clear `~/.mi/opt-in-paths` (captures skip) or remove the
252
- `memory-intelligence` entry from your Claude config to fully unwire.
251
+ - **Off switch.** Clear `~/.memoryintelligence/mcp/opt-in-paths` (captures skip) or
252
+ remove the `memoryintelligence` entry from your Claude config to fully unwire.
253
253
 
254
254
  Found a vulnerability? See [SECURITY.md](SECURITY.md) — report privately to
255
255
  connect@somewheremedia.com.
@@ -258,7 +258,7 @@ connect@somewheremedia.com.
258
258
 
259
259
  | Variable | Required | Default | Description |
260
260
  |----------|----------|---------|-------------|
261
- | `MI_API_KEY` | Yes | — | Your MI API key (resolved by the launcher from the Keychain / `~/.mi-env` — don't set inline in configs) |
261
+ | `MI_API_KEY` | Yes | — | Your MI API key (resolved by the launcher from the Keychain / `~/.memoryintelligence/.env` — don't set inline in configs) |
262
262
  | `MI_BASE_URL` | No | `https://api.memoryintelligence.io` | API base URL |
263
263
  | `MI_MCP_FULL` | No | _(off)_ | `1` exposes all 10 tools; otherwise only the 3 core |
264
264
  | `MI_TRANSPORT` | No | `stdio` | `stdio` only in this version (networked transports disabled) |
@@ -268,6 +268,35 @@ connect@somewheremedia.com.
268
268
  | `MI_DEFAULT_RETENTION` | No | `meaning_only` | Default retention policy |
269
269
  | `MI_DEFAULT_PII_HANDLING` | No | `extract_and_redact` | Default PII handling |
270
270
 
271
+ ## Names & locations
272
+
273
+ You'll see a few related names. They differ because each ecosystem has its own
274
+ rules (PyPI lowercases, Python imports can't contain hyphens, MCP server ids are
275
+ lowercase-hyphen) — but they collapse to one **long form** and one **short form**:
276
+
277
+ | You see | What it is | Why this form |
278
+ |---|---|---|
279
+ | `MemoryIntelligence` | the brand | display name |
280
+ | `memoryintelligence-mcp` | the **PyPI package** (`pip install` / `uvx`) | PyPI normalizes to lowercase + hyphens |
281
+ | `mi-mcp` | the **command** you run (`mi-mcp setup`) | short for daily use (`memoryintelligence-mcp` is an alias) |
282
+ | `mi_mcp` | the Python import package | must be a valid identifier — no hyphens |
283
+ | `memoryintelligence` | the **server id** in your MCP config | matches the brand token (renamed from `memory-intelligence` in 0.1.8) |
284
+ | `MI_*` (e.g. `MI_API_KEY`) | environment variables / Keychain service | short prefix |
285
+
286
+ And everything written to disk lives under one on-brand namespace:
287
+
288
+ | Path | What |
289
+ |---|---|
290
+ | `~/MemoryIntelligence/` | visible vault — your `.umo` files (override with `MI_VAULT`) |
291
+ | `~/.memoryintelligence/mcp/run-mi-mcp.sh` | the launcher each MCP host spawns |
292
+ | `~/.memoryintelligence/mcp/opt-in-paths` | per-directory capture allowlist |
293
+ | `~/.memoryintelligence/.env` | `chmod 600` keyfile (the Keychain fallback) |
294
+ | macOS Keychain (`MI_API_KEY`) | preferred key storage — never on disk |
295
+
296
+ > Upgrading from ≤ 0.1.6? The old `~/.mi/` launcher and `~/.mi-env` keyfile still
297
+ > work (they're read as a fallback). Re-run `mi-mcp wire` to move to the new paths;
298
+ > your opt-in list is migrated forward automatically.
299
+
271
300
  ## Manual setup (cross-platform / advanced)
272
301
 
273
302
  `mi-mcp setup` is the recommended path on every OS. If you'd rather do it by hand
@@ -279,15 +308,16 @@ then run `mi-mcp wire`:
279
308
  ```bash
280
309
  read -s K; security add-generic-password -a "$USER" -s "MI_API_KEY" -w "$K" -U; unset K
281
310
  mi-mcp wire
282
- echo "$(pwd)" >> ~/.mi/opt-in-paths
311
+ echo "$(pwd)" >> ~/.memoryintelligence/mcp/opt-in-paths
283
312
  ```
284
313
 
285
- **Linux / Windows (WSL or Git Bash) — `~/.mi-env` keyfile:**
314
+ **Linux / Windows (WSL or Git Bash) — `~/.memoryintelligence/.env` keyfile:**
286
315
 
287
316
  ```bash
288
- umask 077 && printf 'MI_API_KEY="%s"\n' "$YOUR_KEY" > ~/.mi-env # chmod 600
317
+ mkdir -p ~/.memoryintelligence
318
+ umask 077 && printf 'MI_API_KEY="%s"\n' "$YOUR_KEY" > ~/.memoryintelligence/.env # chmod 600
289
319
  mi-mcp wire
290
- echo "$(pwd)" >> ~/.mi/opt-in-paths
320
+ echo "$(pwd)" >> ~/.memoryintelligence/mcp/opt-in-paths
291
321
  ```
292
322
 
293
323
  **Or just an environment variable** (any OS — exported in your shell profile):
@@ -297,8 +327,9 @@ export MI_API_KEY="mi_sk_..." # the launcher reads the inherited env first
297
327
  ```
298
328
 
299
329
  The launcher resolves the key in order: **inherited env → macOS Keychain →
300
- `~/.mi-env`**. `security add-generic-password` is macOS-only, so on Linux/Windows
301
- use the keyfile or env var never paste the key into an MCP client config.
330
+ `~/.memoryintelligence/.env`** (the legacy `~/.mi-env` is still read).
331
+ `security add-generic-password` is macOS-only, so on Linux/Windows use the keyfile
332
+ or env var — never paste the key into an MCP client config.
302
333
 
303
334
  ## Development
304
335
 
@@ -37,7 +37,7 @@ API key is ever written into a config file.**
37
37
 
38
38
  ---
39
39
 
40
- ## ✅ What works today (0.1.5)
40
+ ## ✅ What works today (0.1.7)
41
41
 
42
42
  Honest status — this is beta, so here's exactly what's live:
43
43
 
@@ -142,7 +142,7 @@ provenance — runs in the service. Your API key authenticates to *your* account
142
142
 
143
143
  1. **prompts for your API key** (hidden input);
144
144
  2. **stores it securely, outside every config** — macOS **Keychain**, or a
145
- `chmod 600 ~/.mi-env` keyfile on Linux/Windows (or with `--store file`);
145
+ `chmod 600 ~/.memoryintelligence/.env` keyfile on Linux/Windows (or with `--store file`);
146
146
  3. **wires** the server into Claude Desktop + Claude Code (`--surfaces` to choose
147
147
  `desktop,code,cursor`), writing `env: {}` — **no key in any config file**;
148
148
  4. **opts in** the current directory so captures are allowed there (reads work
@@ -152,7 +152,7 @@ provenance — runs in the service. Your API key authenticates to *your* account
152
152
  ```bash
153
153
  mi-mcp setup # the happy path (interactive)
154
154
  mi-mcp setup --surfaces desktop,code,cursor
155
- mi-mcp setup --store file # force the ~/.mi-env keyfile (e.g. on Linux)
155
+ mi-mcp setup --store file # force the ~/.memoryintelligence/.env keyfile (e.g. on Linux)
156
156
  mi-mcp setup --no-opt-in # wire only; opt a folder in later
157
157
  ```
158
158
 
@@ -167,12 +167,12 @@ mi-mcp wire --dry-run # preview wiring changes without writing
167
167
 
168
168
  ### How the key stays out of your configs
169
169
 
170
- `wire` points each host at a small launcher (`~/.mi/run-mi-mcp.sh`) that resolves
171
- `MI_API_KEY` **at launch**, in order:
170
+ `wire` points each host at a small launcher (`~/.memoryintelligence/mcp/run-mi-mcp.sh`)
171
+ that resolves `MI_API_KEY` **at launch**, in order:
172
172
 
173
173
  1. the process environment, then
174
174
  2. the macOS **Keychain** (`security find-generic-password -s MI_API_KEY`), then
175
- 3. a gitignored `~/.mi-env` (`chmod 600`), else it fails.
175
+ 3. a `chmod 600` keyfile (`~/.memoryintelligence/.env`, or the legacy `~/.mi-env`), else it fails.
176
176
 
177
177
  So a leaked or committed config file exposes **nothing**.
178
178
 
@@ -185,12 +185,12 @@ So a leaked or committed config file exposes **nothing**.
185
185
  ## Security
186
186
 
187
187
  - **No key in configs.** `setup`/`wire` write `env: {}`; the launcher resolves the
188
- key from the Keychain (or `~/.mi-env`) at runtime. Nothing sensitive lands in a
189
- config file.
188
+ key from the Keychain (or the `~/.memoryintelligence/.env` keyfile) at runtime.
189
+ Nothing sensitive lands in a config file.
190
190
  - **Capture is opt-in per directory.** Write tools (`mi_capture`/`mi_batch`/
191
191
  `mi_upload`) only run when the server's working directory is on the
192
- `~/.mi/opt-in-paths` allowlist. Reads are never gated. Absent allowlist → all
193
- captures are skipped.
192
+ `~/.memoryintelligence/mcp/opt-in-paths` allowlist. Reads are never gated. Absent
193
+ allowlist → all captures are skipped.
194
194
  - **Destructive ops require confirmation.** `mi_forget` (irreversible delete)
195
195
  requires an explicit `confirm=true` argument — a human-in-the-loop guard against
196
196
  injected or accidental deletes.
@@ -213,8 +213,8 @@ So a leaked or committed config file exposes **nothing**.
213
213
  - **Privacy.** Content you capture is sent to your MemoryIntelligence account over
214
214
  HTTPS; nothing else is transmitted, and the server does not log conversation
215
215
  content or your API key. See [memoryintelligence.io/privacy](https://memoryintelligence.io/privacy).
216
- - **Off switch.** Clear `~/.mi/opt-in-paths` (captures skip) or remove the
217
- `memory-intelligence` entry from your Claude config to fully unwire.
216
+ - **Off switch.** Clear `~/.memoryintelligence/mcp/opt-in-paths` (captures skip) or
217
+ remove the `memoryintelligence` entry from your Claude config to fully unwire.
218
218
 
219
219
  Found a vulnerability? See [SECURITY.md](SECURITY.md) — report privately to
220
220
  connect@somewheremedia.com.
@@ -223,7 +223,7 @@ connect@somewheremedia.com.
223
223
 
224
224
  | Variable | Required | Default | Description |
225
225
  |----------|----------|---------|-------------|
226
- | `MI_API_KEY` | Yes | — | Your MI API key (resolved by the launcher from the Keychain / `~/.mi-env` — don't set inline in configs) |
226
+ | `MI_API_KEY` | Yes | — | Your MI API key (resolved by the launcher from the Keychain / `~/.memoryintelligence/.env` — don't set inline in configs) |
227
227
  | `MI_BASE_URL` | No | `https://api.memoryintelligence.io` | API base URL |
228
228
  | `MI_MCP_FULL` | No | _(off)_ | `1` exposes all 10 tools; otherwise only the 3 core |
229
229
  | `MI_TRANSPORT` | No | `stdio` | `stdio` only in this version (networked transports disabled) |
@@ -233,6 +233,35 @@ connect@somewheremedia.com.
233
233
  | `MI_DEFAULT_RETENTION` | No | `meaning_only` | Default retention policy |
234
234
  | `MI_DEFAULT_PII_HANDLING` | No | `extract_and_redact` | Default PII handling |
235
235
 
236
+ ## Names & locations
237
+
238
+ You'll see a few related names. They differ because each ecosystem has its own
239
+ rules (PyPI lowercases, Python imports can't contain hyphens, MCP server ids are
240
+ lowercase-hyphen) — but they collapse to one **long form** and one **short form**:
241
+
242
+ | You see | What it is | Why this form |
243
+ |---|---|---|
244
+ | `MemoryIntelligence` | the brand | display name |
245
+ | `memoryintelligence-mcp` | the **PyPI package** (`pip install` / `uvx`) | PyPI normalizes to lowercase + hyphens |
246
+ | `mi-mcp` | the **command** you run (`mi-mcp setup`) | short for daily use (`memoryintelligence-mcp` is an alias) |
247
+ | `mi_mcp` | the Python import package | must be a valid identifier — no hyphens |
248
+ | `memoryintelligence` | the **server id** in your MCP config | matches the brand token (renamed from `memory-intelligence` in 0.1.8) |
249
+ | `MI_*` (e.g. `MI_API_KEY`) | environment variables / Keychain service | short prefix |
250
+
251
+ And everything written to disk lives under one on-brand namespace:
252
+
253
+ | Path | What |
254
+ |---|---|
255
+ | `~/MemoryIntelligence/` | visible vault — your `.umo` files (override with `MI_VAULT`) |
256
+ | `~/.memoryintelligence/mcp/run-mi-mcp.sh` | the launcher each MCP host spawns |
257
+ | `~/.memoryintelligence/mcp/opt-in-paths` | per-directory capture allowlist |
258
+ | `~/.memoryintelligence/.env` | `chmod 600` keyfile (the Keychain fallback) |
259
+ | macOS Keychain (`MI_API_KEY`) | preferred key storage — never on disk |
260
+
261
+ > Upgrading from ≤ 0.1.6? The old `~/.mi/` launcher and `~/.mi-env` keyfile still
262
+ > work (they're read as a fallback). Re-run `mi-mcp wire` to move to the new paths;
263
+ > your opt-in list is migrated forward automatically.
264
+
236
265
  ## Manual setup (cross-platform / advanced)
237
266
 
238
267
  `mi-mcp setup` is the recommended path on every OS. If you'd rather do it by hand
@@ -244,15 +273,16 @@ then run `mi-mcp wire`:
244
273
  ```bash
245
274
  read -s K; security add-generic-password -a "$USER" -s "MI_API_KEY" -w "$K" -U; unset K
246
275
  mi-mcp wire
247
- echo "$(pwd)" >> ~/.mi/opt-in-paths
276
+ echo "$(pwd)" >> ~/.memoryintelligence/mcp/opt-in-paths
248
277
  ```
249
278
 
250
- **Linux / Windows (WSL or Git Bash) — `~/.mi-env` keyfile:**
279
+ **Linux / Windows (WSL or Git Bash) — `~/.memoryintelligence/.env` keyfile:**
251
280
 
252
281
  ```bash
253
- umask 077 && printf 'MI_API_KEY="%s"\n' "$YOUR_KEY" > ~/.mi-env # chmod 600
282
+ mkdir -p ~/.memoryintelligence
283
+ umask 077 && printf 'MI_API_KEY="%s"\n' "$YOUR_KEY" > ~/.memoryintelligence/.env # chmod 600
254
284
  mi-mcp wire
255
- echo "$(pwd)" >> ~/.mi/opt-in-paths
285
+ echo "$(pwd)" >> ~/.memoryintelligence/mcp/opt-in-paths
256
286
  ```
257
287
 
258
288
  **Or just an environment variable** (any OS — exported in your shell profile):
@@ -262,8 +292,9 @@ export MI_API_KEY="mi_sk_..." # the launcher reads the inherited env first
262
292
  ```
263
293
 
264
294
  The launcher resolves the key in order: **inherited env → macOS Keychain →
265
- `~/.mi-env`**. `security add-generic-password` is macOS-only, so on Linux/Windows
266
- use the keyfile or env var never paste the key into an MCP client config.
295
+ `~/.memoryintelligence/.env`** (the legacy `~/.mi-env` is still read).
296
+ `security add-generic-password` is macOS-only, so on Linux/Windows use the keyfile
297
+ or env var — never paste the key into an MCP client config.
267
298
 
268
299
  ## Development
269
300
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "memoryintelligence-mcp"
7
- version = "0.1.6"
7
+ version = "0.1.8"
8
8
  description = "MCP server for MemoryIntelligence — give Claude a memory you own, set up in one command"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """MI MCP Server — MemoryIntelligence tools for Claude."""
2
2
 
3
- __version__ = "0.1.6"
3
+ __version__ = "0.1.8"
@@ -2,9 +2,9 @@
2
2
 
3
3
  Security model (build-spec §13.3): the API key NEVER goes into any MCP client
4
4
  config. ``wire`` writes ``{command: <wrapper>, args: [], env: {}}`` and renders
5
- a wrapper (``~/.mi/run-mi-mcp.sh``) that resolves ``MI_API_KEY`` at launch from
6
- the macOS Keychain, then ``~/.mi-env``, else fails. So a leaked config file
7
- exposes nothing.
5
+ a wrapper (``~/.memoryintelligence/mcp/run-mi-mcp.sh``) that resolves
6
+ ``MI_API_KEY`` at launch from the macOS Keychain, then the on-disk keyfile,
7
+ else fails. So a leaked config file exposes nothing.
8
8
 
9
9
  setup — one command: store the key, wire hosts, opt in this dir, verify.
10
10
  (alias: ``init``). The frictionless front door for new users.
@@ -13,8 +13,12 @@ exposes nothing.
13
13
  status — show which surfaces are wired + the capture opt-in allowlist.
14
14
 
15
15
  The key is stored OUTSIDE every config — in the macOS Keychain (``security``)
16
- or, on Linux/Windows (or by choice), a ``chmod 600 ~/.mi-env`` keyfile. Both are
16
+ or, on Linux/Windows (or by choice), a ``chmod 600 ~/.memoryintelligence/.env``
17
+ keyfile (the legacy ``~/.mi-env`` is still read for back-compat). Both are
17
18
  resolved by the launch wrapper at runtime, so no config file ever holds a secret.
19
+
20
+ The on-disk layout (launcher, opt-in allowlist, keyfile) lives under
21
+ ``~/.memoryintelligence/`` — see ``paths.py``, the single source of truth.
18
22
  """
19
23
 
20
24
  from __future__ import annotations
@@ -29,9 +33,14 @@ import sys
29
33
  import tempfile
30
34
  from pathlib import Path
31
35
 
36
+ from . import paths
32
37
  from .config import load_opt_in_paths
33
38
 
34
- SERVER_KEY = "memory-intelligence"
39
+ SERVER_KEY = "memoryintelligence"
40
+ # Pre-0.1.8 server ids we replace on wire so an upgrade leaves no orphaned entry.
41
+ # (0.1.7 and earlier registered the server as "memory-intelligence" — the one
42
+ # dash-seam that didn't match the brand/package "memoryintelligence".)
43
+ LEGACY_SERVER_KEYS = ("memory-intelligence",)
35
44
 
36
45
  # Wrapper rendered by `wire`. __MI_MCP_BIN__ is replaced with the absolute path
37
46
  # to the mi-mcp binary (resolved at wire time) so the host can spawn it even
@@ -41,15 +50,17 @@ WRAPPER_TEMPLATE = r"""#!/usr/bin/env bash
41
50
  # Resolves MI_API_KEY at launch so the key never lives in any MCP client config.
42
51
  set -euo pipefail
43
52
 
44
- # 1. inherited env → 2. macOS Keychain → 3. ~/.mi-env → fail
53
+ # 1. inherited env → 2. macOS Keychain → 3. keyfile (new then legacy) → fail
45
54
  if [[ -z "${MI_API_KEY:-}" ]]; then
46
55
  MI_API_KEY="$(security find-generic-password -a "${MI_KEYCHAIN_ACCOUNT:-$USER}" -s "MI_API_KEY" -w 2>/dev/null || true)"
47
56
  fi
48
- if [[ -z "${MI_API_KEY:-}" && -f "$HOME/.mi-env" ]]; then
49
- set -a; . "$HOME/.mi-env"; set +a
50
- fi
57
+ for __mi_envf in "$HOME/.memoryintelligence/.env" "$HOME/.mi-env"; do
58
+ if [[ -z "${MI_API_KEY:-}" && -f "$__mi_envf" ]]; then
59
+ set -a; . "$__mi_envf"; set +a
60
+ fi
61
+ done
51
62
  if [[ -z "${MI_API_KEY:-}" ]]; then
52
- echo "run-mi-mcp: MI_API_KEY not found (env, Keychain service 'MI_API_KEY', or ~/.mi-env)" >&2
63
+ echo "run-mi-mcp: MI_API_KEY not found (env, Keychain service 'MI_API_KEY', or ~/.memoryintelligence/.env)" >&2
53
64
  exit 1
54
65
  fi
55
66
  export MI_API_KEY
@@ -72,7 +83,7 @@ def _surface_paths(home: Path) -> dict[str, Path]:
72
83
 
73
84
 
74
85
  def _wrapper_path(home: Path) -> Path:
75
- return home / ".mi" / "run-mi-mcp.sh"
86
+ return paths.wrapper_path(home=home)
76
87
 
77
88
 
78
89
  def _mi_mcp_bin() -> str:
@@ -99,8 +110,10 @@ def _wire_code_via_cli(home: Path, wrapper: Path, dry_run: bool) -> bool:
99
110
  if dry_run:
100
111
  print(f" would run: {' '.join(add_cmd)}")
101
112
  return True
102
- # idempotent: drop any existing entry (any scope) first, then add
103
- subprocess.run([claude, "mcp", "remove", SERVER_KEY], capture_output=True, text=True)
113
+ # idempotent + migrate: drop any existing entry current id AND the legacy
114
+ # pre-0.1.8 ids before re-adding, so an upgrade leaves no orphan.
115
+ for key in (SERVER_KEY, *LEGACY_SERVER_KEYS):
116
+ subprocess.run([claude, "mcp", "remove", key], capture_output=True, text=True)
104
117
  r = subprocess.run(add_cmd, capture_output=True, text=True)
105
118
  if r.returncode != 0:
106
119
  print(f" ! claude mcp add failed ({r.stderr.strip()[:160]}); falling back to file")
@@ -155,13 +168,14 @@ def _resolve_key(home: Path) -> tuple[str, str]:
155
168
  if r.returncode == 0 and r.stdout.strip():
156
169
  return r.stdout.strip(), "keychain"
157
170
  except (OSError, subprocess.SubprocessError):
158
- pass # `security` unavailable/failed — fall through to the ~/.mi-env keyfile
159
- envf = home / ".mi-env"
160
- if envf.exists():
171
+ pass # `security` unavailable/failed — fall through to the keyfile
172
+ envf = paths.resolve_keyfile(home) # new ~/.memoryintelligence/.env, else legacy ~/.mi-env
173
+ if envf is not None:
174
+ src = "~/.mi-env" if envf.name == ".mi-env" else "~/.memoryintelligence/.env"
161
175
  for line in envf.read_text().splitlines():
162
176
  line = line.strip()
163
177
  if line.startswith("MI_API_KEY="):
164
- return line.split("=", 1)[1].strip().strip('"').strip("'"), "~/.mi-env"
178
+ return line.split("=", 1)[1].strip().strip('"').strip("'"), src
165
179
  return "", "none"
166
180
 
167
181
 
@@ -175,26 +189,35 @@ def do_wire(home: Path, surfaces: list[str], dry_run: bool) -> None:
175
189
  Shared by ``cmd_wire`` and ``cmd_setup``. Writes ``env: {}`` only — the key is
176
190
  never placed in a config; the wrapper resolves it at launch.
177
191
  """
178
- paths = _surface_paths(home)
192
+ cfg_paths = _surface_paths(home)
179
193
  wrapper = _wrapper_path(home)
180
194
  bin_path = _mi_mcp_bin()
181
195
  entry = {"command": str(wrapper), "args": [], "env": {}}
182
196
 
183
- print(f"{'DRY-RUN: ' if dry_run else ''}wiring memory-intelligence MCP server")
197
+ print(f"{'DRY-RUN: ' if dry_run else ''}wiring {SERVER_KEY} MCP server")
184
198
  print(f" wrapper → {wrapper}")
185
199
  print(f" execs {bin_path}; resolves MI_API_KEY at launch (no key in configs)")
186
200
  if not dry_run:
187
201
  _atomic_write(wrapper, WRAPPER_TEMPLATE.replace("__MI_MCP_BIN__", bin_path), mode=0o755)
202
+ # Bring a legacy ~/.mi/opt-in-paths forward to the new location (no-op if
203
+ # there's nothing to migrate). Non-destructive — the old file is left.
204
+ if paths.migrate_opt_in_forward(home=home):
205
+ print(f" migrated opt-in allowlist → {paths.opt_in_paths_file(home=home)}")
188
206
 
189
207
  for s in surfaces:
190
208
  if s == "code" and _wire_code_via_cli(home, wrapper, dry_run):
191
209
  continue
192
- if s not in paths:
210
+ if s not in cfg_paths:
193
211
  print(f" ! unknown surface '{s}' (skipped)")
194
212
  continue
195
- cfg_path = paths[s]
213
+ cfg_path = cfg_paths[s]
196
214
  cfg = _load_json(cfg_path)
197
215
  servers = cfg.setdefault("mcpServers", {})
216
+ # Migrate: drop any pre-0.1.8 id (e.g. "memory-intelligence") so the
217
+ # rename to "memoryintelligence" doesn't leave a duplicate/orphan entry.
218
+ migrated = [k for k in LEGACY_SERVER_KEYS if servers.pop(k, None) is not None]
219
+ if migrated:
220
+ print(f" {s:8} migrated id {', '.join(migrated)} → {SERVER_KEY}")
198
221
  action = "update" if SERVER_KEY in servers else "add"
199
222
  nochange = servers.get(SERVER_KEY) == entry
200
223
  servers[SERVER_KEY] = entry
@@ -219,7 +242,7 @@ def cmd_wire(argv: list[str]) -> int:
219
242
  do_wire(home, surfaces, args.dry_run)
220
243
  if not args.dry_run:
221
244
  print("\nNext steps:")
222
- print(f" 1. opt in a project: echo \"$(pwd)\" >> {home / '.mi' / 'opt-in-paths'}")
245
+ print(f" 1. opt in a project: echo \"$(pwd)\" >> {paths.mcp_config_dir(home=home) / 'opt-in-paths'}")
223
246
  print(" 2. restart Claude (MCP servers load at startup)")
224
247
  print(" 3. mi-mcp doctor # verify")
225
248
  return 0
@@ -251,15 +274,20 @@ def cmd_doctor(argv: list[str]) -> int:
251
274
  check("MI_API_KEY resolvable", bool(key),
252
275
  f"source={src}")
253
276
 
254
- optin = home / ".mi" / "opt-in-paths"
277
+ optin = paths.opt_in_paths_file(home=home)
255
278
  n = len(load_opt_in_paths(optin)) if optin.exists() else 0
256
279
  check("opt-in allowlist", True,
257
280
  f"{n} entries" if optin.exists() else "absent — all captures will skip",
258
281
  critical=False)
259
282
 
260
283
  for s, p in _surface_paths(home).items():
261
- wired = SERVER_KEY in _load_json(p).get("mcpServers", {})
262
- check(f"{s} wired", wired, str(p) if wired else "(not wired)", critical=False)
284
+ servers = _load_json(p).get("mcpServers", {})
285
+ wired = SERVER_KEY in servers
286
+ legacy = [k for k in LEGACY_SERVER_KEYS if k in servers]
287
+ detail = str(p) if wired else "(not wired)"
288
+ if legacy and not wired:
289
+ detail = f"legacy id {', '.join(legacy)} present — run `mi-mcp wire` to migrate"
290
+ check(f"{s} wired", wired, detail, critical=False)
263
291
 
264
292
  print(f"\n {'healthy ✓' if ok else 'issues found ✗'}")
265
293
  return 0 if ok else 1
@@ -276,12 +304,12 @@ def cmd_status(argv: list[str]) -> int:
276
304
  wired = SERVER_KEY in _load_json(p).get("mcpServers", {})
277
305
  print(f" {s:8} {'wired ✓ ' if wired else 'not wired'} {p}")
278
306
 
279
- optin = home / ".mi" / "opt-in-paths"
280
- paths = load_opt_in_paths(optin) if optin.exists() else []
281
- print(f"\nOpt-in paths ({len(paths)}):")
282
- for p in paths:
307
+ optin = paths.opt_in_paths_file(home=home)
308
+ opted = load_opt_in_paths(optin) if optin.exists() else []
309
+ print(f"\nOpt-in paths ({len(opted)}):")
310
+ for p in opted:
283
311
  print(f" {p}")
284
- if not paths:
312
+ if not opted:
285
313
  print(" (none — captures will be skipped until you add one)")
286
314
  return 0
287
315
 
@@ -379,13 +407,14 @@ def _store_key_keychain(key: str, account: str) -> None:
379
407
 
380
408
 
381
409
  def _store_key_file(home: Path, key: str) -> Path:
382
- """Write the key to a ``chmod 600`` ``~/.mi-env`` keyfile (the launcher reads it).
410
+ """Write the key to a ``chmod 600`` ``~/.memoryintelligence/.env`` keyfile.
383
411
 
384
412
  The cross-platform fallback when there is no Keychain (Linux/Windows) or when
385
413
  the user explicitly chooses ``--store file``. Still NOT a config file: the
386
- launch wrapper sources it at runtime; no MCP config ever holds the key.
414
+ launch wrapper sources it at runtime; no MCP config ever holds the key. (The
415
+ launcher still reads the legacy ``~/.mi-env`` too, for existing installs.)
387
416
  """
388
- envf = home / ".mi-env"
417
+ envf = paths.keyfile_path(home)
389
418
  content = (
390
419
  "# MemoryIntelligence MCP — API key, resolved at launch by run-mi-mcp.sh.\n"
391
420
  "# Private (chmod 600). Never commit this file.\n"
@@ -396,12 +425,12 @@ def _store_key_file(home: Path, key: str) -> Path:
396
425
 
397
426
 
398
427
  def _opt_in_dir(home: Path, directory: str) -> tuple[Path, bool]:
399
- """Add ``directory`` (realpath) to ``~/.mi/opt-in-paths`` if absent.
428
+ """Add ``directory`` (realpath) to ``~/.memoryintelligence/mcp/opt-in-paths``.
400
429
 
401
430
  Returns ``(file, added)``. Idempotent — re-running setup in the same dir does
402
431
  not duplicate the entry. Realpath normalization matches the consent gate.
403
432
  """
404
- optin = home / ".mi" / "opt-in-paths"
433
+ optin = paths.mcp_config_dir(create=True, home=home) / "opt-in-paths"
405
434
  target = os.path.realpath(os.path.expanduser(directory))
406
435
  existing = load_opt_in_paths(optin) if optin.exists() else []
407
436
  if any(os.path.realpath(os.path.expanduser(p)) == target for p in existing):
@@ -422,7 +451,7 @@ def cmd_setup(argv: list[str]) -> int:
422
451
  ap.add_argument("--api-key", default=None,
423
452
  help="provide the key non-interactively (else you're prompted, hidden)")
424
453
  ap.add_argument("--store", choices=["auto", "keychain", "file"], default="auto",
425
- help="where to keep the key (auto: Keychain on macOS, ~/.mi-env elsewhere)")
454
+ help="where to keep the key (auto: Keychain on macOS, ~/.memoryintelligence/.env elsewhere)")
426
455
  ap.add_argument("--surfaces", default="desktop,code",
427
456
  help="comma list of: desktop, code, cursor (default: desktop,code)")
428
457
  ap.add_argument("--opt-in", default=None, metavar="DIR",
@@ -0,0 +1,129 @@
1
+ """Filesystem layout for Memory Intelligence — one on-brand namespace.
2
+
3
+ Single visible/hidden split, no lookalike names:
4
+
5
+ ~/MemoryIntelligence/ visible vault: the user's *.umo files (relocatable via MI_VAULT)
6
+ ~/.memoryintelligence/mcp/ hidden MCP config — launcher + capture opt-in allowlist
7
+ ~/.memoryintelligence/sdk/ hidden SDK plumbing (models, cache)
8
+ ~/.memoryintelligence/.env on-disk keyfile (chmod 600) — cross-platform fallback to the Keychain
9
+ OS Keychain keys, preferred (never on disk)
10
+
11
+ As of 0.1.7 ``mi-mcp setup``/``wire`` write this layout directly. The retired
12
+ ``~/.mi/`` config dir and ``~/.mi-env`` keyfile are still *read* (one-release
13
+ fallback) so existing installs keep working until the next ``wire``; nothing is
14
+ moved destructively. The opt-in allowlist is copied forward on ``wire``.
15
+
16
+ Every resolver takes an optional ``home`` (used by the CLI's ``--home`` test
17
+ flag and to keep read/write paths in lockstep); when ``None`` it resolves from
18
+ ``$MI_HOME`` then ``~``. This module is the SINGLE source of truth for the
19
+ layout — the CLI and the server's consent gate both call it.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import os
25
+ import shutil
26
+ from pathlib import Path
27
+
28
+ HIDDEN_HOME_ENV = "MI_HOME" # override the hidden plumbing home
29
+ VAULT_ENV = "MI_VAULT" # override the visible vault location
30
+
31
+
32
+ def _home() -> Path:
33
+ return Path(os.path.expanduser("~"))
34
+
35
+
36
+ def _base(home: Path | None) -> Path:
37
+ """The home under which the layout is rooted. An explicit ``home`` (CLI
38
+ ``--home``) wins; else ``~``. ``$MI_HOME`` overrides only the hidden tree."""
39
+ return Path(home) if home is not None else _home()
40
+
41
+
42
+ def hidden_home(home: Path | None = None) -> Path:
43
+ """``~/.memoryintelligence`` (or ``$MI_HOME``, or ``<home>/.memoryintelligence``)."""
44
+ if home is not None:
45
+ return Path(home) / ".memoryintelligence"
46
+ override = os.environ.get(HIDDEN_HOME_ENV)
47
+ return Path(override).expanduser() if override else _home() / ".memoryintelligence"
48
+
49
+
50
+ def mcp_config_dir(create: bool = False, home: Path | None = None) -> Path:
51
+ d = hidden_home(home) / "mcp"
52
+ if create:
53
+ d.mkdir(parents=True, exist_ok=True)
54
+ return d
55
+
56
+
57
+ def sdk_dir(create: bool = False, home: Path | None = None) -> Path:
58
+ d = hidden_home(home) / "sdk"
59
+ if create:
60
+ d.mkdir(parents=True, exist_ok=True)
61
+ return d
62
+
63
+
64
+ def legacy_mcp_dir(home: Path | None = None) -> Path:
65
+ """The retired ``~/.mi/`` directory (read-only fallback)."""
66
+ return _base(home) / ".mi"
67
+
68
+
69
+ def vault_dir(create: bool = False, home: Path | None = None) -> Path:
70
+ """The visible ``~/MemoryIntelligence/`` vault (or ``$MI_VAULT``)."""
71
+ override = os.environ.get(VAULT_ENV)
72
+ if override:
73
+ d = Path(override).expanduser()
74
+ else:
75
+ d = _base(home) / "MemoryIntelligence"
76
+ if create:
77
+ d.mkdir(parents=True, exist_ok=True)
78
+ return d
79
+
80
+
81
+ def wrapper_path(create: bool = False, home: Path | None = None) -> Path:
82
+ """The launch wrapper (``run-mi-mcp.sh``) that resolves MI_API_KEY at spawn."""
83
+ return mcp_config_dir(create=create, home=home) / "run-mi-mcp.sh"
84
+
85
+
86
+ def keyfile_path(home: Path | None = None) -> Path:
87
+ """Preferred on-disk keyfile (``~/.memoryintelligence/.env``, chmod 600) — the
88
+ cross-platform fallback when there is no Keychain. Shared by mcp + sdk."""
89
+ return hidden_home(home) / ".env"
90
+
91
+
92
+ def legacy_keyfile_path(home: Path | None = None) -> Path:
93
+ """The retired ``~/.mi-env`` keyfile, still read for back-compat (and the
94
+ ecosystem digest pipeline that points at it)."""
95
+ return _base(home) / ".mi-env"
96
+
97
+
98
+ def resolve_keyfile(home: Path | None = None) -> Path | None:
99
+ """Existing keyfile to read — new location preferred, legacy fallback — or
100
+ ``None`` if neither exists (then only env/Keychain can supply the key)."""
101
+ for p in (keyfile_path(home), legacy_keyfile_path(home)):
102
+ if p.exists():
103
+ return p
104
+ return None
105
+
106
+
107
+ def opt_in_paths_file(home: Path | None = None) -> Path:
108
+ """The capture-consent allowlist. New location preferred; falls back to the
109
+ legacy ``~/.mi/opt-in-paths`` if it exists and the new one doesn't."""
110
+ new = mcp_config_dir(home=home) / "opt-in-paths"
111
+ if new.exists():
112
+ return new
113
+ legacy = legacy_mcp_dir(home) / "opt-in-paths"
114
+ if legacy.exists():
115
+ return legacy
116
+ return new # default to the new location for writes
117
+
118
+
119
+ def migrate_opt_in_forward(home: Path | None = None) -> bool:
120
+ """Non-destructively copy the legacy opt-in allowlist to the new location if
121
+ the new one doesn't exist yet. Leaves ``~/.mi/`` intact. Returns True if it
122
+ copied."""
123
+ new = mcp_config_dir(home=home) / "opt-in-paths"
124
+ legacy = legacy_mcp_dir(home) / "opt-in-paths"
125
+ if new.exists() or not legacy.exists():
126
+ return False
127
+ new.parent.mkdir(parents=True, exist_ok=True)
128
+ shutil.copy2(legacy, new)
129
+ return True
@@ -42,7 +42,7 @@ logger = logging.getLogger("mi_mcp")
42
42
  # surface reduces agent decision-noise; the hidden tools stay callable by name.
43
43
  V0_VISIBLE_TOOLS = frozenset({"mi_capture", "mi_ask", "mi_list"})
44
44
 
45
- # Write tools — gated by the cwd consent allowlist (~/.mi/opt-in-paths, Story 8).
45
+ # Write tools — gated by the cwd consent allowlist (~/.memoryintelligence/mcp/opt-in-paths, Story 8).
46
46
  # Read tools are never gated; reading your own memory is always safe.
47
47
  WRITE_TOOLS = frozenset({"mi_capture", "mi_batch", "mi_upload"})
48
48
 
@@ -139,7 +139,7 @@ def create_server(config: MIConfig | None = None) -> Server:
139
139
  config = MIConfig.from_env()
140
140
 
141
141
  client = MIClient(config)
142
- server = Server("memory-intelligence", instructions=SERVER_INSTRUCTIONS)
142
+ server = Server("memoryintelligence", instructions=SERVER_INSTRUCTIONS)
143
143
 
144
144
  if os.environ.get("MI_MCP_OPT_IN_ALL") == "1":
145
145
  logger.warning(
@@ -534,7 +534,7 @@ def create_server(config: MIConfig | None = None) -> Server:
534
534
  logger.info(f"[CONSENT] {name} skipped — cwd not opted in: {cwd}")
535
535
  return [TextContent(type="text", text=_fmt({
536
536
  "status": "skipped",
537
- "reason": "cwd not opted in — add it to ~/.mi/opt-in-paths (or set MI_MCP_OPT_IN_ALL=1)",
537
+ "reason": "cwd not opted in — run `mi-mcp setup` here, or add it to ~/.memoryintelligence/mcp/opt-in-paths (or set MI_MCP_OPT_IN_ALL=1)",
538
538
  "cwd": cwd,
539
539
  "tool": name,
540
540
  }))]
@@ -1,86 +0,0 @@
1
- """Filesystem layout for Memory Intelligence (locked 2026-06-05).
2
-
3
- One brand namespace, clear visibility split — no lookalike names:
4
-
5
- ~/MemoryIntelligence/ visible vault: the user's *.umo files (relocatable via MI_VAULT)
6
- ~/.memoryintelligence/mcp/ hidden MCP config (opt-in-paths, launcher)
7
- ~/.memoryintelligence/sdk/ hidden SDK plumbing (models, cache)
8
- OS Keychain keys (never on disk)
9
-
10
- Retires the legacy ``~/.mi/`` config dir: path resolution prefers the new
11
- location but falls back to ``~/.mi/`` for one release so existing wiring keeps
12
- working. Nothing is moved destructively — the launcher relocates on the next
13
- ``mi-mcp wire``; only the opt-in allowlist is (non-destructively) copied forward.
14
- """
15
-
16
- from __future__ import annotations
17
-
18
- import os
19
- import shutil
20
- from pathlib import Path
21
-
22
- HIDDEN_HOME_ENV = "MI_HOME" # override the hidden plumbing home
23
- VAULT_ENV = "MI_VAULT" # override the visible vault location
24
-
25
-
26
- def _home() -> Path:
27
- return Path(os.path.expanduser("~"))
28
-
29
-
30
- def hidden_home() -> Path:
31
- """``~/.memoryintelligence`` (or ``$MI_HOME``)."""
32
- override = os.environ.get(HIDDEN_HOME_ENV)
33
- return Path(override).expanduser() if override else _home() / ".memoryintelligence"
34
-
35
-
36
- def mcp_config_dir(create: bool = False) -> Path:
37
- d = hidden_home() / "mcp"
38
- if create:
39
- d.mkdir(parents=True, exist_ok=True)
40
- return d
41
-
42
-
43
- def sdk_dir(create: bool = False) -> Path:
44
- d = hidden_home() / "sdk"
45
- if create:
46
- d.mkdir(parents=True, exist_ok=True)
47
- return d
48
-
49
-
50
- def legacy_mcp_dir() -> Path:
51
- """The retired ``~/.mi/`` directory."""
52
- return _home() / ".mi"
53
-
54
-
55
- def vault_dir(create: bool = False) -> Path:
56
- """The visible ``~/MemoryIntelligence/`` vault (or ``$MI_VAULT``)."""
57
- override = os.environ.get(VAULT_ENV)
58
- d = Path(override).expanduser() if override else _home() / "MemoryIntelligence"
59
- if create:
60
- d.mkdir(parents=True, exist_ok=True)
61
- return d
62
-
63
-
64
- def opt_in_paths_file() -> Path:
65
- """The capture-consent allowlist. New location preferred; falls back to the
66
- legacy ``~/.mi/opt-in-paths`` if it exists and the new one doesn't."""
67
- new = mcp_config_dir() / "opt-in-paths"
68
- if new.exists():
69
- return new
70
- legacy = legacy_mcp_dir() / "opt-in-paths"
71
- if legacy.exists():
72
- return legacy
73
- return new # default to the new location for writes
74
-
75
-
76
- def migrate_opt_in_forward() -> bool:
77
- """Non-destructively copy the legacy opt-in allowlist to the new location if
78
- the new one doesn't exist yet. Leaves ``~/.mi/`` intact (the launcher there
79
- keeps working until the next ``mi-mcp wire``). Returns True if it copied."""
80
- new = mcp_config_dir() / "opt-in-paths"
81
- legacy = legacy_mcp_dir() / "opt-in-paths"
82
- if new.exists() or not legacy.exists():
83
- return False
84
- new.parent.mkdir(parents=True, exist_ok=True)
85
- shutil.copy2(legacy, new)
86
- return True