fathom-mcp 0.1.4 → 0.2.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 (2026-02-26)
4
+
5
+ Multi-agent support.
6
+
7
+ - **Multi-agent init wizard** — auto-detects installed agents and generates per-agent MCP configs
8
+ - **Supported agents:** Claude Code, OpenAI Codex, Gemini CLI, Cursor, VS Code Copilot, Windsurf
9
+ - **Per-agent config writers** — `.mcp.json`, `.codex/config.toml`, `.gemini/settings.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, `~/.codeium/windsurf/mcp_config.json`
10
+ - **Conditional hooks** — hook setup only for Claude Code (other agents don't support hooks)
11
+ - **`agents` array** replaces legacy `architecture` string in `.fathom.json` — backward compatible
12
+ - **Server-side agent dispatch** — persistent sessions launch the correct agent CLI per workspace
13
+ - **Status command** — now shows configured agents per workspace
14
+
3
15
  ## 0.1.0 (2026-02-25)
4
16
 
5
17
  Initial release.
package/README.md CHANGED
@@ -9,9 +9,20 @@
9
9
  hifathom.com · fathom@myrakrusemark.com
10
10
  ```
11
11
 
12
- MCP server for [Fathom](https://hifathom.com) — vault operations, search, rooms, and cross-workspace communication.
12
+ MCP server for [Fathom](https://hifathom.com) — vault operations, search, rooms, and cross-workspace communication. Works with any MCP-compatible agent.
13
13
 
14
- The MCP tools that let Claude Code interact with your vault. Reads/writes happen locally (fast, no network hop). Search, rooms, and workspace management go through your [fathom-server](https://github.com/myrakrusemark/fathom-vault) instance.
14
+ ## Supported Agents
15
+
16
+ | Agent | Config file | Auto-detected by |
17
+ |-------|------------|------------------|
18
+ | **Claude Code** | `.mcp.json` | `.claude/` directory |
19
+ | **OpenAI Codex** | `.codex/config.toml` | `.codex/` directory |
20
+ | **Gemini CLI** | `.gemini/settings.json` | `.gemini/` directory |
21
+ | **Cursor** | `.cursor/mcp.json` | `.cursor/` directory |
22
+ | **VS Code Copilot** | `.vscode/mcp.json` | `.vscode/` directory |
23
+ | **Windsurf** | `~/.codeium/windsurf/mcp_config.json` | `~/.codeium/windsurf/` directory |
24
+
25
+ The init wizard auto-detects which agents you have and generates the right config for each.
15
26
 
16
27
  ## Quick Start
17
28
 
@@ -19,13 +30,14 @@ The MCP tools that let Claude Code interact with your vault. Reads/writes happen
19
30
  npx fathom-mcp init
20
31
  ```
21
32
 
22
- That's it. Restart Claude Code and fathom tools will be available.
33
+ The wizard will:
34
+ 1. Detect installed agents (Claude Code, Codex, Gemini, etc.)
35
+ 2. Let you pick which ones to configure
36
+ 3. Write per-agent MCP config files
37
+ 4. Set up hooks (Claude Code only)
38
+ 5. Register the workspace with your fathom-server
23
39
 
24
- The init wizard creates:
25
- - `.fathom.json` — workspace config (server URL, API key, vault path)
26
- - `.mcp.json` — registers `npx fathom-mcp` as an MCP server
27
- - `.claude/settings.local.json` — hooks for context injection and precompact snapshots
28
- - `vault/` — creates the directory if it doesn't exist
40
+ Restart your agent and fathom tools will be available.
29
41
 
30
42
  ## Prerequisites
31
43
 
@@ -35,7 +47,7 @@ The init wizard creates:
35
47
  ## Commands
36
48
 
37
49
  ```bash
38
- npx fathom-mcp # Start MCP server (stdio — used by .mcp.json)
50
+ npx fathom-mcp # Start MCP server (stdio — used by agent configs)
39
51
  npx fathom-mcp init # Interactive setup wizard
40
52
  npx fathom-mcp status # Check server connection + workspace status
41
53
  ```
@@ -64,7 +76,7 @@ npx fathom-mcp status # Check server connection + workspace status
64
76
  | `fathom_room_list` | List all rooms |
65
77
  | `fathom_room_describe` | Set a room's description/topic |
66
78
  | `fathom_workspaces` | List all configured workspaces |
67
- | `fathom_send` | Send a message to another workspace's Claude instance |
79
+ | `fathom_send` | Send a message to another workspace's agent instance |
68
80
 
69
81
  ## Configuration
70
82
 
@@ -76,8 +88,9 @@ npx fathom-mcp status # Check server connection + workspace status
76
88
  "vault": "vault",
77
89
  "server": "http://localhost:4243",
78
90
  "apiKey": "fv_abc123...",
91
+ "agents": ["claude-code", "gemini"],
79
92
  "hooks": {
80
- "context-inject": { "enabled": true },
93
+ "vault-recall": { "enabled": true },
81
94
  "precompact-snapshot": { "enabled": true }
82
95
  }
83
96
  }
@@ -89,11 +102,19 @@ npx fathom-mcp status # Check server connection + workspace status
89
102
  2. `.fathom.json` (walked up from cwd to filesystem root)
90
103
  3. Built-in defaults
91
104
 
92
- ## Hooks
105
+ ### Backward compatibility
106
+
107
+ The `agents` array replaces the legacy `architecture` string field. Old configs with `architecture: "claude-code"` are automatically migrated to `agents: ["claude-code"]` at read time.
108
+
109
+ ## Hooks (Claude Code only)
110
+
111
+ Hooks are only available in Claude Code and are configured in `.claude/settings.local.json`.
112
+
113
+ **UserPromptSubmit** (`fathom-recall.sh`): Runs vault recall on every message — injects relevant context.
93
114
 
94
- **SessionStart / UserPromptSubmit** (`fathom-context.sh`): Injects recent vault activity into Claude's context.
115
+ **PreCompact** (`fathom-precompact.sh`): Records which vault files were active before context compaction.
95
116
 
96
- **PreCompact** (`fathom-precompact.sh`): Records which vault files were active in the session before context compaction.
117
+ Other agents don't support hooks they get the same MCP tools but without automatic context injection.
97
118
 
98
119
  ## Vault Frontmatter Schema
99
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fathom-mcp",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for Fathom — vault operations, search, rooms, and cross-workspace communication",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,6 +37,11 @@
37
37
  "vault",
38
38
  "fathom",
39
39
  "claude",
40
+ "codex",
41
+ "gemini",
42
+ "cursor",
43
+ "copilot",
44
+ "windsurf",
40
45
  "ai-agent",
41
46
  "memory"
42
47
  ]
package/src/cli.js CHANGED
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import fs from "fs";
13
+ import os from "os";
13
14
  import path from "path";
14
15
  import readline from "readline";
15
16
  import { fileURLToPath } from "url";
@@ -105,6 +106,143 @@ function copyScripts(targetDir) {
105
106
  }
106
107
  }
107
108
 
109
+ // --- Agent registry ----------------------------------------------------------
110
+
111
+ const MCP_SERVER_ENTRY = {
112
+ command: "npx",
113
+ args: ["-y", "fathom-mcp"],
114
+ };
115
+
116
+ /**
117
+ * Per-agent config writers. Each writes the appropriate MCP config file
118
+ * for that agent, merging with existing config if present.
119
+ */
120
+
121
+ function writeMcpJson(cwd) {
122
+ const filePath = path.join(cwd, ".mcp.json");
123
+ const existing = readJsonFile(filePath) || {};
124
+ deepMerge(existing, { mcpServers: { "fathom-vault": MCP_SERVER_ENTRY } });
125
+ writeJsonFile(filePath, existing);
126
+ return ".mcp.json";
127
+ }
128
+
129
+ function writeCodexToml(cwd) {
130
+ const dir = path.join(cwd, ".codex");
131
+ fs.mkdirSync(dir, { recursive: true });
132
+ const filePath = path.join(dir, "config.toml");
133
+
134
+ let content = "";
135
+ try {
136
+ content = fs.readFileSync(filePath, "utf-8");
137
+ } catch { /* file doesn't exist */ }
138
+
139
+ // Check if fathom-vault section already exists
140
+ if (/\[mcp_servers\.fathom-vault\]/.test(content)) {
141
+ return ".codex/config.toml (already configured)";
142
+ }
143
+
144
+ const section = `\n[mcp_servers.fathom-vault]\ncommand = "npx"\nargs = ["-y", "fathom-mcp"]\n`;
145
+ const separator = content && !content.endsWith("\n") ? "\n" : "";
146
+ fs.writeFileSync(filePath, content + separator + section);
147
+ return ".codex/config.toml";
148
+ }
149
+
150
+ function writeGeminiJson(cwd) {
151
+ const dir = path.join(cwd, ".gemini");
152
+ fs.mkdirSync(dir, { recursive: true });
153
+ const filePath = path.join(dir, "settings.json");
154
+ const existing = readJsonFile(filePath) || {};
155
+ deepMerge(existing, { mcpServers: { "fathom-vault": MCP_SERVER_ENTRY } });
156
+ writeJsonFile(filePath, existing);
157
+ return ".gemini/settings.json";
158
+ }
159
+
160
+ function writeCursorJson(cwd) {
161
+ const dir = path.join(cwd, ".cursor");
162
+ fs.mkdirSync(dir, { recursive: true });
163
+ const filePath = path.join(dir, "mcp.json");
164
+ const existing = readJsonFile(filePath) || {};
165
+ deepMerge(existing, { mcpServers: { "fathom-vault": MCP_SERVER_ENTRY } });
166
+ writeJsonFile(filePath, existing);
167
+ return ".cursor/mcp.json";
168
+ }
169
+
170
+ function writeVscodeJson(cwd) {
171
+ const dir = path.join(cwd, ".vscode");
172
+ fs.mkdirSync(dir, { recursive: true });
173
+ const filePath = path.join(dir, "mcp.json");
174
+ const existing = readJsonFile(filePath) || {};
175
+ deepMerge(existing, {
176
+ servers: {
177
+ "fathom-vault": {
178
+ type: "stdio",
179
+ command: "npx",
180
+ args: ["-y", "fathom-mcp"],
181
+ },
182
+ },
183
+ });
184
+ writeJsonFile(filePath, existing);
185
+ return ".vscode/mcp.json";
186
+ }
187
+
188
+ function writeWindsurfJson(_cwd) {
189
+ const dir = path.join(os.homedir(), ".codeium", "windsurf");
190
+ fs.mkdirSync(dir, { recursive: true });
191
+ const filePath = path.join(dir, "mcp_config.json");
192
+ const existing = readJsonFile(filePath) || {};
193
+ deepMerge(existing, { mcpServers: { "fathom-vault": MCP_SERVER_ENTRY } });
194
+ writeJsonFile(filePath, existing);
195
+ return "~/.codeium/windsurf/mcp_config.json";
196
+ }
197
+
198
+ const AGENTS = {
199
+ "claude-code": {
200
+ name: "Claude Code",
201
+ detect: (cwd) => fs.existsSync(path.join(cwd, ".claude")),
202
+ configWriter: writeMcpJson,
203
+ hasHooks: true,
204
+ nextSteps: 'Add to CLAUDE.md: `ToolSearch query="+fathom" max_results=20`',
205
+ },
206
+ "codex": {
207
+ name: "OpenAI Codex",
208
+ detect: (cwd) => fs.existsSync(path.join(cwd, ".codex")),
209
+ configWriter: writeCodexToml,
210
+ hasHooks: false,
211
+ nextSteps: "Run `codex` in this directory — fathom tools load automatically.",
212
+ },
213
+ "gemini": {
214
+ name: "Gemini CLI",
215
+ detect: (cwd) => fs.existsSync(path.join(cwd, ".gemini")),
216
+ configWriter: writeGeminiJson,
217
+ hasHooks: false,
218
+ nextSteps: "Run `gemini` in this directory — fathom tools load automatically.",
219
+ },
220
+ "cursor": {
221
+ name: "Cursor",
222
+ detect: (cwd) => fs.existsSync(path.join(cwd, ".cursor")),
223
+ configWriter: writeCursorJson,
224
+ hasHooks: false,
225
+ nextSteps: "Restart Cursor — fathom tools appear in MCP settings.",
226
+ },
227
+ "vscode": {
228
+ name: "VS Code Copilot",
229
+ detect: (cwd) => fs.existsSync(path.join(cwd, ".vscode")),
230
+ configWriter: writeVscodeJson,
231
+ hasHooks: false,
232
+ nextSteps: "Reload VS Code — fathom tools appear in Copilot.",
233
+ },
234
+ "windsurf": {
235
+ name: "Windsurf",
236
+ detect: () => fs.existsSync(path.join(os.homedir(), ".codeium", "windsurf")),
237
+ configWriter: writeWindsurfJson,
238
+ hasHooks: false,
239
+ nextSteps: "Restart Windsurf — fathom tools appear in Cascade.",
240
+ },
241
+ };
242
+
243
+ // Exported for testing
244
+ export { AGENTS, writeMcpJson, writeCodexToml, writeGeminiJson, writeCursorJson, writeVscodeJson, writeWindsurfJson };
245
+
108
246
  // --- Init wizard -------------------------------------------------------------
109
247
 
110
248
  async function runInit() {
@@ -120,10 +258,11 @@ async function runInit() {
120
258
  hifathom.com · fathom@myrakrusemark.com
121
259
  `);
122
260
 
123
- // Check for existing config
124
- const existing = findConfigFile(cwd);
125
- if (existing) {
126
- console.log(` Found existing config at: ${existing.path}`);
261
+ // Check for existing config in *this* directory only (don't walk up —
262
+ // a parent's .fathom.json belongs to a different workspace)
263
+ const localConfigPath = path.join(cwd, ".fathom.json");
264
+ if (fs.existsSync(localConfigPath)) {
265
+ console.log(` Found existing config at: ${localConfigPath}`);
127
266
  const proceed = await askYesNo(rl, " Overwrite?", false);
128
267
  if (!proceed) {
129
268
  console.log(" Aborted.");
@@ -142,15 +281,57 @@ async function runInit() {
142
281
  // 3. Description (optional)
143
282
  const description = await ask(rl, " Workspace description (optional)", "");
144
283
 
145
- // 4. Server URL
146
- const serverUrl = await ask(rl, " Fathom server URL", "http://localhost:4243");
284
+ // 4. Agent selection — auto-detect and let user choose
285
+ const agentKeys = Object.keys(AGENTS);
286
+ const detected = agentKeys.filter((key) => AGENTS[key].detect(cwd));
287
+
288
+ console.log("\n Detected agents:");
289
+ for (const key of agentKeys) {
290
+ const agent = AGENTS[key];
291
+ const isDetected = detected.includes(key);
292
+ const mark = isDetected ? "✓" : " ";
293
+ const hint = isDetected ? ` (${key === "windsurf" ? "~/.codeium/windsurf/ found" : `.${key === "claude-code" ? "claude" : key === "vscode" ? "vscode" : key}/ found`})` : "";
294
+ console.log(` ${mark} ${agent.name}${hint}`);
295
+ }
296
+
297
+ console.log("\n Configure for which agents?");
298
+ agentKeys.forEach((key, i) => {
299
+ const agent = AGENTS[key];
300
+ const mark = detected.includes(key) ? " ✓" : "";
301
+ console.log(` ${i + 1}. ${agent.name}${mark}`);
302
+ });
303
+
304
+ const defaultSelection = detected.length > 0
305
+ ? detected.map((key) => agentKeys.indexOf(key) + 1).join(",")
306
+ : "1";
307
+ const selectionStr = await ask(rl, "\n Enter numbers, comma-separated", defaultSelection);
308
+
309
+ const selectedIndices = selectionStr
310
+ .split(",")
311
+ .map((s) => parseInt(s.trim(), 10))
312
+ .filter((n) => n >= 1 && n <= agentKeys.length);
313
+ const selectedAgents = [...new Set(selectedIndices.map((i) => agentKeys[i - 1]))];
147
314
 
148
- // 5. API key — user gets this from the dashboard or server first-run output
315
+ if (selectedAgents.length === 0) {
316
+ console.log(" No agents selected. Defaulting to Claude Code.");
317
+ selectedAgents.push("claude-code");
318
+ }
319
+
320
+ // 5. Server URL
321
+ const serverUrl = await ask(rl, "\n Fathom server URL", "http://localhost:4243");
322
+
323
+ // 6. API key
149
324
  const apiKey = await ask(rl, " API key (from dashboard or server first-run output)", "");
150
325
 
151
- // 6. Hooks
152
- const enableRecallHook = await askYesNo(rl, " Enable vault recall on every message (UserPromptSubmit)?", true);
153
- const enablePrecompactHook = await askYesNo(rl, " Enable PreCompact vault snapshot hook?", true);
326
+ // 7. Hooks — only ask if Claude Code is selected
327
+ const hasClaude = selectedAgents.includes("claude-code");
328
+ let enableRecallHook = false;
329
+ let enablePrecompactHook = false;
330
+ if (hasClaude) {
331
+ console.log();
332
+ enableRecallHook = await askYesNo(rl, " Enable vault recall on every message (UserPromptSubmit)?", true);
333
+ enablePrecompactHook = await askYesNo(rl, " Enable PreCompact vault snapshot hook?", true);
334
+ }
154
335
 
155
336
  rl.close();
156
337
 
@@ -165,6 +346,7 @@ async function runInit() {
165
346
  server: serverUrl,
166
347
  apiKey,
167
348
  description,
349
+ agents: selectedAgents,
168
350
  hooks: {
169
351
  "vault-recall": { enabled: enableRecallHook },
170
352
  "precompact-snapshot": { enabled: enablePrecompactHook },
@@ -189,60 +371,53 @@ async function runInit() {
189
371
  console.log(` · ${vault}/ (already exists)`);
190
372
  }
191
373
 
192
- // .mcp.json
193
- const mcpJsonPath = path.join(cwd, ".mcp.json");
194
- const mcpJson = readJsonFile(mcpJsonPath) || {};
195
- deepMerge(mcpJson, {
196
- mcpServers: {
197
- "fathom-vault": {
198
- command: "npx",
199
- args: ["-y", "fathom-mcp"],
200
- },
201
- },
202
- });
203
- writeJsonFile(mcpJsonPath, mcpJson);
204
- console.log(" ✓ .mcp.json");
205
-
206
- // .claude/settings.local.json — hook registrations
207
- const claudeSettingsPath = path.join(cwd, ".claude", "settings.local.json");
208
- const claudeSettings = readJsonFile(claudeSettingsPath) || {};
209
-
210
- // Claude Code hooks use matcher + hooks array format:
211
- // { hooks: [{ type: "command", command: "...", timeout: N }] }
212
- const hooks = {};
213
- if (enableRecallHook) {
214
- hooks["UserPromptSubmit"] = [
215
- ...(claudeSettings.hooks?.["UserPromptSubmit"] || []),
216
- ];
217
- const recallCmd = "bash .fathom/scripts/fathom-recall.sh";
218
- const hasFathomRecall = hooks["UserPromptSubmit"].some((entry) =>
219
- entry.hooks?.some((h) => h.command === recallCmd)
220
- );
221
- if (!hasFathomRecall) {
222
- hooks["UserPromptSubmit"].push({
223
- hooks: [{ type: "command", command: recallCmd, timeout: 10000 }],
224
- });
225
- }
374
+ // Per-agent config files
375
+ for (const agentKey of selectedAgents) {
376
+ const agent = AGENTS[agentKey];
377
+ const result = agent.configWriter(cwd);
378
+ console.log(` ✓ ${result}`);
226
379
  }
227
- if (enablePrecompactHook) {
228
- hooks["PreCompact"] = [
229
- ...(claudeSettings.hooks?.["PreCompact"] || []),
230
- ];
231
- const precompactCmd = "bash .fathom/scripts/fathom-precompact.sh";
232
- const hasFathomPrecompact = hooks["PreCompact"].some((entry) =>
233
- entry.hooks?.some((h) => h.command === precompactCmd)
234
- );
235
- if (!hasFathomPrecompact) {
236
- hooks["PreCompact"].push({
237
- hooks: [{ type: "command", command: precompactCmd, timeout: 30000 }],
238
- });
380
+
381
+ // Claude Code hooks only if claude-code is selected
382
+ if (hasClaude && (enableRecallHook || enablePrecompactHook)) {
383
+ const claudeSettingsPath = path.join(cwd, ".claude", "settings.local.json");
384
+ const claudeSettings = readJsonFile(claudeSettingsPath) || {};
385
+
386
+ const hooks = {};
387
+ if (enableRecallHook) {
388
+ hooks["UserPromptSubmit"] = [
389
+ ...(claudeSettings.hooks?.["UserPromptSubmit"] || []),
390
+ ];
391
+ const recallCmd = "bash .fathom/scripts/fathom-recall.sh";
392
+ const hasFathomRecall = hooks["UserPromptSubmit"].some((entry) =>
393
+ entry.hooks?.some((h) => h.command === recallCmd)
394
+ );
395
+ if (!hasFathomRecall) {
396
+ hooks["UserPromptSubmit"].push({
397
+ hooks: [{ type: "command", command: recallCmd, timeout: 10000 }],
398
+ });
399
+ }
400
+ }
401
+ if (enablePrecompactHook) {
402
+ hooks["PreCompact"] = [
403
+ ...(claudeSettings.hooks?.["PreCompact"] || []),
404
+ ];
405
+ const precompactCmd = "bash .fathom/scripts/fathom-precompact.sh";
406
+ const hasFathomPrecompact = hooks["PreCompact"].some((entry) =>
407
+ entry.hooks?.some((h) => h.command === precompactCmd)
408
+ );
409
+ if (!hasFathomPrecompact) {
410
+ hooks["PreCompact"].push({
411
+ hooks: [{ type: "command", command: precompactCmd, timeout: 30000 }],
412
+ });
413
+ }
239
414
  }
240
- }
241
415
 
242
- if (Object.keys(hooks).length > 0) {
243
- claudeSettings.hooks = { ...(claudeSettings.hooks || {}), ...hooks };
244
- writeJsonFile(claudeSettingsPath, claudeSettings);
245
- console.log(" ✓ .claude/settings.local.json (hooks)");
416
+ if (Object.keys(hooks).length > 0) {
417
+ claudeSettings.hooks = { ...(claudeSettings.hooks || {}), ...hooks };
418
+ writeJsonFile(claudeSettingsPath, claudeSettings);
419
+ console.log(" ✓ .claude/settings.local.json (hooks)");
420
+ }
246
421
  }
247
422
 
248
423
  // .gitignore
@@ -254,7 +429,11 @@ async function runInit() {
254
429
  const regClient = createClient({ server: serverUrl, apiKey, workspace });
255
430
  const isUp = await regClient.healthCheck();
256
431
  if (isUp) {
257
- const regResult = await regClient.registerWorkspace(workspace, cwd, { vault, description });
432
+ const regResult = await regClient.registerWorkspace(workspace, cwd, {
433
+ vault,
434
+ description,
435
+ agents: selectedAgents,
436
+ });
258
437
  if (regResult.ok) {
259
438
  console.log(` ✓ Registered workspace "${workspace}" with server`);
260
439
  } else if (regResult.error) {
@@ -263,17 +442,15 @@ async function runInit() {
263
442
  }
264
443
  }
265
444
 
266
- console.log(`
267
- Done! Fathom MCP is configured for workspace "${workspace}".
268
-
269
- Next steps:
270
- 1. Start the server: cd fathom-server && python app.py
271
- 2. Open Claude Code in this directory — fathom tools will be available.
272
- 3. Add the following to your CLAUDE.md:
273
-
274
- ## Fathom Vault
275
- Load fathom tools on startup: \`ToolSearch query="+fathom" max_results=20\`
276
- `);
445
+ // Per-agent next steps
446
+ console.log(`\n Done! Fathom MCP is configured for workspace "${workspace}".`);
447
+ console.log("\n Next steps:");
448
+ console.log(" 1. Start the server: cd fathom-server && python app.py");
449
+ for (const agentKey of selectedAgents) {
450
+ const agent = AGENTS[agentKey];
451
+ console.log(` · ${agent.name}: ${agent.nextSteps}`);
452
+ }
453
+ console.log();
277
454
  }
278
455
 
279
456
  // --- Status command ----------------------------------------------------------
@@ -288,6 +465,7 @@ async function runStatus() {
288
465
  console.log(` Vault: ${config.vault}`);
289
466
  console.log(` Server: ${config.server}`);
290
467
  console.log(` API Key: ${config.apiKey ? config.apiKey.slice(0, 7) + "..." + config.apiKey.slice(-4) : "(not set)"}`);
468
+ console.log(` Agents: ${config.agents.length > 0 ? config.agents.join(", ") : "(none)"}`);
291
469
 
292
470
  // Check vault directory
293
471
  const vaultExists = fs.existsSync(config.vault);
@@ -303,8 +481,15 @@ async function runStatus() {
303
481
  const names = Object.keys(wsResult.profiles);
304
482
  console.log(` Workspaces: ${names.join(", ") || "(none)"}`);
305
483
  for (const [name, profile] of Object.entries(wsResult.profiles)) {
306
- const status = profile.running ? "running" : "stopped";
307
- console.log(` ${name}: ${status}${profile.model ? ` (${profile.model})` : ""}`);
484
+ if (profile.type === "human") {
485
+ console.log(` ${name}: human`);
486
+ } else {
487
+ const agentLabel = profile.agents?.length > 0
488
+ ? ` [${profile.agents.join(", ")}]`
489
+ : profile.architecture ? ` [${profile.architecture}]` : "";
490
+ const runStatus = profile.running ? "running" : "stopped";
491
+ console.log(` ${name}: ${runStatus}${agentLabel}`);
492
+ }
308
493
  }
309
494
  }
310
495
  }
@@ -314,23 +499,30 @@ async function runStatus() {
314
499
 
315
500
  // --- Main --------------------------------------------------------------------
316
501
 
317
- const command = process.argv[2];
502
+ // Guard: only run CLI when this module is the entry point (not when imported by tests)
503
+ const isMain = process.argv[1] && (
504
+ process.argv[1].endsWith("/cli.js") || process.argv[1].endsWith("fathom-mcp")
505
+ );
318
506
 
319
- if (command === "init") {
320
- runInit().catch((e) => {
321
- console.error(`Error: ${e.message}`);
322
- process.exit(1);
323
- });
324
- } else if (command === "status") {
325
- runStatus().catch((e) => {
326
- console.error(`Error: ${e.message}`);
507
+ if (isMain) {
508
+ const command = process.argv[2];
509
+
510
+ if (command === "init") {
511
+ runInit().catch((e) => {
512
+ console.error(`Error: ${e.message}`);
513
+ process.exit(1);
514
+ });
515
+ } else if (command === "status") {
516
+ runStatus().catch((e) => {
517
+ console.error(`Error: ${e.message}`);
518
+ process.exit(1);
519
+ });
520
+ } else if (!command || command === "serve") {
521
+ // Default: start MCP server
522
+ import("./index.js");
523
+ } else {
524
+ console.error(`Unknown command: ${command}`);
525
+ console.error("Usage: fathom-mcp [init|status|serve]");
327
526
  process.exit(1);
328
- });
329
- } else if (!command || command === "serve") {
330
- // Default: start MCP server
331
- import("./index.js");
332
- } else {
333
- console.error(`Unknown command: ${command}`);
334
- console.error("Usage: fathom-mcp [init|status|serve]");
335
- process.exit(1);
527
+ }
336
528
  }
package/src/config.js CHANGED
@@ -18,6 +18,7 @@ const DEFAULTS = {
18
18
  server: "http://localhost:4243",
19
19
  apiKey: "",
20
20
  description: "",
21
+ agents: [],
21
22
  hooks: {
22
23
  "context-inject": { enabled: true },
23
24
  "precompact-snapshot": { enabled: true },
@@ -67,6 +68,12 @@ export function resolveConfig(startDir = process.cwd()) {
67
68
  if (config.server) result.server = config.server;
68
69
  if (config.apiKey) result.apiKey = config.apiKey;
69
70
  if (config.description) result.description = config.description;
71
+ // Backward compat: migrate legacy `architecture` string to `agents` array
72
+ if (config.agents && Array.isArray(config.agents)) {
73
+ result.agents = config.agents;
74
+ } else if (config.architecture) {
75
+ result.agents = [config.architecture];
76
+ }
70
77
  if (config.hooks) {
71
78
  result.hooks = { ...result.hooks, ...config.hooks };
72
79
  }
@@ -111,6 +118,7 @@ export function writeConfig(dir, config) {
111
118
  server: config.server || DEFAULTS.server,
112
119
  apiKey: config.apiKey || "",
113
120
  description: config.description || "",
121
+ agents: config.agents || [],
114
122
  hooks: config.hooks || DEFAULTS.hooks,
115
123
  };
116
124
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
package/src/index.js CHANGED
@@ -458,6 +458,7 @@ async function main() {
458
458
  client.registerWorkspace(config.workspace, config._projectDir, {
459
459
  vault: config._rawVault,
460
460
  description: config.description,
461
+ architecture: config.architecture,
461
462
  }).catch(() => {});
462
463
  }
463
464
 
@@ -104,10 +104,13 @@ export function createClient(config) {
104
104
  return request("GET", "/api/workspaces/profiles");
105
105
  }
106
106
 
107
- async function registerWorkspace(name, projectPath, { vault, description } = {}) {
107
+ async function registerWorkspace(name, projectPath, { vault, description, agents, architecture } = {}) {
108
108
  const body = { name, path: projectPath };
109
109
  if (vault) body.vault = vault;
110
110
  if (description) body.description = description;
111
+ if (agents && agents.length > 0) body.agents = agents;
112
+ // Legacy fallback
113
+ if (architecture && !agents?.length) body.architecture = architecture;
111
114
  return request("POST", "/api/workspaces", { body });
112
115
  }
113
116