fathom-mcp 0.1.5 → 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.5",
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,18 +281,57 @@ async function runInit() {
142
281
  // 3. Description (optional)
143
282
  const description = await ask(rl, " Workspace description (optional)", "");
144
283
 
145
- // 3b. Architecture/platform (optional)
146
- const architecture = await ask(rl, " Architecture/platform (e.g., claude-code, diffusion)", "");
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);
147
308
 
148
- // 4. Server URL
149
- const serverUrl = await ask(rl, " Fathom server URL", "http://localhost:4243");
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]))];
150
314
 
151
- // 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
152
324
  const apiKey = await ask(rl, " API key (from dashboard or server first-run output)", "");
153
325
 
154
- // 6. Hooks
155
- const enableRecallHook = await askYesNo(rl, " Enable vault recall on every message (UserPromptSubmit)?", true);
156
- 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
+ }
157
335
 
158
336
  rl.close();
159
337
 
@@ -168,7 +346,7 @@ async function runInit() {
168
346
  server: serverUrl,
169
347
  apiKey,
170
348
  description,
171
- architecture,
349
+ agents: selectedAgents,
172
350
  hooks: {
173
351
  "vault-recall": { enabled: enableRecallHook },
174
352
  "precompact-snapshot": { enabled: enablePrecompactHook },
@@ -193,60 +371,53 @@ async function runInit() {
193
371
  console.log(` · ${vault}/ (already exists)`);
194
372
  }
195
373
 
196
- // .mcp.json
197
- const mcpJsonPath = path.join(cwd, ".mcp.json");
198
- const mcpJson = readJsonFile(mcpJsonPath) || {};
199
- deepMerge(mcpJson, {
200
- mcpServers: {
201
- "fathom-vault": {
202
- command: "npx",
203
- args: ["-y", "fathom-mcp"],
204
- },
205
- },
206
- });
207
- writeJsonFile(mcpJsonPath, mcpJson);
208
- console.log(" ✓ .mcp.json");
209
-
210
- // .claude/settings.local.json — hook registrations
211
- const claudeSettingsPath = path.join(cwd, ".claude", "settings.local.json");
212
- const claudeSettings = readJsonFile(claudeSettingsPath) || {};
213
-
214
- // Claude Code hooks use matcher + hooks array format:
215
- // { hooks: [{ type: "command", command: "...", timeout: N }] }
216
- const hooks = {};
217
- if (enableRecallHook) {
218
- hooks["UserPromptSubmit"] = [
219
- ...(claudeSettings.hooks?.["UserPromptSubmit"] || []),
220
- ];
221
- const recallCmd = "bash .fathom/scripts/fathom-recall.sh";
222
- const hasFathomRecall = hooks["UserPromptSubmit"].some((entry) =>
223
- entry.hooks?.some((h) => h.command === recallCmd)
224
- );
225
- if (!hasFathomRecall) {
226
- hooks["UserPromptSubmit"].push({
227
- hooks: [{ type: "command", command: recallCmd, timeout: 10000 }],
228
- });
229
- }
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}`);
230
379
  }
231
- if (enablePrecompactHook) {
232
- hooks["PreCompact"] = [
233
- ...(claudeSettings.hooks?.["PreCompact"] || []),
234
- ];
235
- const precompactCmd = "bash .fathom/scripts/fathom-precompact.sh";
236
- const hasFathomPrecompact = hooks["PreCompact"].some((entry) =>
237
- entry.hooks?.some((h) => h.command === precompactCmd)
238
- );
239
- if (!hasFathomPrecompact) {
240
- hooks["PreCompact"].push({
241
- hooks: [{ type: "command", command: precompactCmd, timeout: 30000 }],
242
- });
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
+ }
243
414
  }
244
- }
245
415
 
246
- if (Object.keys(hooks).length > 0) {
247
- claudeSettings.hooks = { ...(claudeSettings.hooks || {}), ...hooks };
248
- writeJsonFile(claudeSettingsPath, claudeSettings);
249
- 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
+ }
250
421
  }
251
422
 
252
423
  // .gitignore
@@ -258,7 +429,11 @@ async function runInit() {
258
429
  const regClient = createClient({ server: serverUrl, apiKey, workspace });
259
430
  const isUp = await regClient.healthCheck();
260
431
  if (isUp) {
261
- const regResult = await regClient.registerWorkspace(workspace, cwd, { vault, description, architecture });
432
+ const regResult = await regClient.registerWorkspace(workspace, cwd, {
433
+ vault,
434
+ description,
435
+ agents: selectedAgents,
436
+ });
262
437
  if (regResult.ok) {
263
438
  console.log(` ✓ Registered workspace "${workspace}" with server`);
264
439
  } else if (regResult.error) {
@@ -267,17 +442,15 @@ async function runInit() {
267
442
  }
268
443
  }
269
444
 
270
- console.log(`
271
- Done! Fathom MCP is configured for workspace "${workspace}".
272
-
273
- Next steps:
274
- 1. Start the server: cd fathom-server && python app.py
275
- 2. Open Claude Code in this directory — fathom tools will be available.
276
- 3. Add the following to your CLAUDE.md:
277
-
278
- ## Fathom Vault
279
- Load fathom tools on startup: \`ToolSearch query="+fathom" max_results=20\`
280
- `);
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();
281
454
  }
282
455
 
283
456
  // --- Status command ----------------------------------------------------------
@@ -292,6 +465,7 @@ async function runStatus() {
292
465
  console.log(` Vault: ${config.vault}`);
293
466
  console.log(` Server: ${config.server}`);
294
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)"}`);
295
469
 
296
470
  // Check vault directory
297
471
  const vaultExists = fs.existsSync(config.vault);
@@ -307,8 +481,15 @@ async function runStatus() {
307
481
  const names = Object.keys(wsResult.profiles);
308
482
  console.log(` Workspaces: ${names.join(", ") || "(none)"}`);
309
483
  for (const [name, profile] of Object.entries(wsResult.profiles)) {
310
- const status = profile.running ? "running" : "stopped";
311
- 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
+ }
312
493
  }
313
494
  }
314
495
  }
@@ -318,23 +499,30 @@ async function runStatus() {
318
499
 
319
500
  // --- Main --------------------------------------------------------------------
320
501
 
321
- 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
+ );
322
506
 
323
- if (command === "init") {
324
- runInit().catch((e) => {
325
- console.error(`Error: ${e.message}`);
326
- process.exit(1);
327
- });
328
- } else if (command === "status") {
329
- runStatus().catch((e) => {
330
- 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]");
331
526
  process.exit(1);
332
- });
333
- } else if (!command || command === "serve") {
334
- // Default: start MCP server
335
- import("./index.js");
336
- } else {
337
- console.error(`Unknown command: ${command}`);
338
- console.error("Usage: fathom-mcp [init|status|serve]");
339
- process.exit(1);
527
+ }
340
528
  }
package/src/config.js CHANGED
@@ -18,7 +18,7 @@ const DEFAULTS = {
18
18
  server: "http://localhost:4243",
19
19
  apiKey: "",
20
20
  description: "",
21
- architecture: "",
21
+ agents: [],
22
22
  hooks: {
23
23
  "context-inject": { enabled: true },
24
24
  "precompact-snapshot": { enabled: true },
@@ -68,7 +68,12 @@ export function resolveConfig(startDir = process.cwd()) {
68
68
  if (config.server) result.server = config.server;
69
69
  if (config.apiKey) result.apiKey = config.apiKey;
70
70
  if (config.description) result.description = config.description;
71
- if (config.architecture) result.architecture = config.architecture;
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
+ }
72
77
  if (config.hooks) {
73
78
  result.hooks = { ...result.hooks, ...config.hooks };
74
79
  }
@@ -113,7 +118,7 @@ export function writeConfig(dir, config) {
113
118
  server: config.server || DEFAULTS.server,
114
119
  apiKey: config.apiKey || "",
115
120
  description: config.description || "",
116
- architecture: config.architecture || "",
121
+ agents: config.agents || [],
117
122
  hooks: config.hooks || DEFAULTS.hooks,
118
123
  };
119
124
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
@@ -104,11 +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, architecture } = {}) {
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 (architecture) body.architecture = architecture;
111
+ if (agents && agents.length > 0) body.agents = agents;
112
+ // Legacy fallback
113
+ if (architecture && !agents?.length) body.architecture = architecture;
112
114
  return request("POST", "/api/workspaces", { body });
113
115
  }
114
116