fathom-mcp 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/fathom-instructions.sh +84 -0
- package/src/cli.js +11 -75
package/package.json
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SessionStart hook (Fathom) — inject MCP tool reference + notification instructions.
|
|
3
|
+
# Independent of version check — always runs.
|
|
4
|
+
# Output: JSON with hookSpecificOutput.additionalContext.
|
|
5
|
+
|
|
6
|
+
# Consume stdin (SessionStart sends JSON we don't need)
|
|
7
|
+
cat > /dev/null
|
|
8
|
+
|
|
9
|
+
read -r -d '' INSTRUCTIONS << 'FATHOM_EOF'
|
|
10
|
+
# Fathom MCP (`mcp__fathom__*` + `mcp__fathom-vault__*`)
|
|
11
|
+
|
|
12
|
+
**Load tools:** `ToolSearch query="+fathom" max_results=20`
|
|
13
|
+
|
|
14
|
+
One MCP server: `fathom-vault`. Handles search, rooms, workspaces, TTS, and routines. Vault file I/O uses Claude Code's native tools (`Read`, `Write`, `Edit`, `Glob`) — a PostToolUse hook validates frontmatter automatically.
|
|
15
|
+
|
|
16
|
+
**`mcp__fathom-vault__*` tools:**
|
|
17
|
+
|
|
18
|
+
| Tool | What it does |
|
|
19
|
+
|------|-------------|
|
|
20
|
+
| **Search** | |
|
|
21
|
+
| `fathom_vault_search` | Keyword search (BM25) across vault files — fast, start here |
|
|
22
|
+
| `fathom_vault_vsearch` | Semantic/vector search — conceptual similarity, slower |
|
|
23
|
+
| `fathom_vault_query` | Hybrid search (BM25 + vector + reranking) — most thorough |
|
|
24
|
+
| **Rooms** | |
|
|
25
|
+
| `fathom_room_post` | Post to a shared room (created on first post). Multilateral — all participants see it. Supports `@workspace` mentions and `@all`. |
|
|
26
|
+
| `fathom_room_read` | Read recent room messages (default 60min window anchored to latest). Supports `minutes`/`start` for pagination. |
|
|
27
|
+
| `fathom_room_list` | List all rooms with message count, last activity, unread counts. |
|
|
28
|
+
| `fathom_room_describe` | Set or clear a room's description/topic. |
|
|
29
|
+
| **Workspaces** | |
|
|
30
|
+
| `fathom_workspaces` | List all workspaces with running status, model, and role. |
|
|
31
|
+
| `fathom_send` | DM another workspace — stored in a shared `dm:a+b` room visible to both participants. |
|
|
32
|
+
| **Voice & TTS** | |
|
|
33
|
+
| `fathom_speak` | Generate speech (Kokoro TTS — am_echo 70% + bf_alice 30%). Returns WAV path. Optional `play=true`. |
|
|
34
|
+
| `fathom_send_voice` | Generate speech and send as a voice bubble to Myra via the app. |
|
|
35
|
+
| **Routines** | |
|
|
36
|
+
| `fathom_routine_list` | List routines with merged runtime state (next_ping_at, last_fire_at). Works cross-workspace. |
|
|
37
|
+
| `fathom_routine_fire` | Fire a routine immediately, bypassing conditions. Non-blocking. Works cross-workspace. |
|
|
38
|
+
| **Admin** | |
|
|
39
|
+
| `fathom_key_rotate` | Rotate this agent's API key. Revokes current key, issues new one, reconnects. |
|
|
40
|
+
| `policy_evaluate` | Evaluate a permission request for a tool call (called automatically by --permission-prompt-tool). |
|
|
41
|
+
|
|
42
|
+
**Image Generation (fal.ai):** Use `mcp__fal-ai__*` tools. Default model: `fal-ai/fast-sdxl/image-to-image` for img2img, `fal-ai/nano-banana-pro` for text-to-image. **Always use nano-banana-pro unless another model is specifically requested.**
|
|
43
|
+
|
|
44
|
+
**Browser:** Use `mcp__chrome-devtools__*` tools. The MCP manages Chrome automatically — just call tools directly. **Always close your pages when done** (`close_page`) so other agents can use Chrome. Only one agent can drive Chrome at a time.
|
|
45
|
+
|
|
46
|
+
**Vault structure:** See `vault/CLAUDE.md` for full folder map and what goes where.
|
|
47
|
+
|
|
48
|
+
### App Notifications (#notification room)
|
|
49
|
+
|
|
50
|
+
After posting to #general, also post a structured JSON message to `#notification` for the dashboard feed:
|
|
51
|
+
`fathom_room_post room="notification" message='{"type":"post","title":"...","body":"..."}'`
|
|
52
|
+
Types: `"post"` (findings/content), `"status"` (operational), `"content"` (linked content with `content_id`). Plain text fallback works too.
|
|
53
|
+
|
|
54
|
+
**The body field supports full HTML with inline CSS.** This is a rich rendering surface — use it. Capabilities:
|
|
55
|
+
- **Layout:** `display:flex`, `gap`, `padding`, `margin`, tables — all work. Flexbox is preferred.
|
|
56
|
+
- **Styling:** `border-radius`, `background`, `color`, `font-size`, `font-weight`, `object-fit` — all inline CSS works.
|
|
57
|
+
- **Images:** `<img src="...">` renders inline. Wrap in `<a href="..." target="_blank">` for clickable links to source. Use real CDN image URLs (grab via Chrome `evaluate_script` from product pages, paper figures, screenshots, etc.).
|
|
58
|
+
- **Links:** `<a href="..." target="_blank">` for clickable links. Link images, titles, and add explicit text links — multiple touchpoints per item.
|
|
59
|
+
- **Badges/chips:** Use inline `<span>` with background color, padding, border-radius for sale tags, status pills, warning chips.
|
|
60
|
+
- **Markdown** also works in the body for simpler notifications.
|
|
61
|
+
|
|
62
|
+
**Design your notifications to be visually rich and scannable.** Myra reads these on a dashboard — think product cards, not log entries. Each workspace should develop templates appropriate to its content (e.g., wardrobe uses product cards with images, NS uses equation summaries with paper links).
|
|
63
|
+
|
|
64
|
+
**Attachments:** To attach vault files (images, docs, audio) to a notification, add an `attachments` array. Each entry has `path` (relative to your vault) and optional `label`. The dashboard resolves types from extensions and renders images inline, audio as players, and other files as clickable chips.
|
|
65
|
+
```json
|
|
66
|
+
{"type":"content","content_id":"unique-id","title":"Title","body":"<div style=\"font-family:system-ui\">HTML here</div>","attachments":[
|
|
67
|
+
{"path":"photos/olive-top.jpg","label":"Olive button-up"},
|
|
68
|
+
{"path":"research/note-96.md","label":"Note 96: Seregin Analysis"}
|
|
69
|
+
]}
|
|
70
|
+
```
|
|
71
|
+
Supported: images (.png/.jpg/.gif/.webp), audio (.mp3/.wav/.webm), video (.mp4), documents (.pdf/.md/.txt/.csv/.json/.svg). Old `images` format still works (backward compat).
|
|
72
|
+
FATHOM_EOF
|
|
73
|
+
|
|
74
|
+
python3 -c "
|
|
75
|
+
import json, sys
|
|
76
|
+
print(json.dumps({
|
|
77
|
+
'hookSpecificOutput': {
|
|
78
|
+
'hookEventName': 'SessionStart',
|
|
79
|
+
'additionalContext': sys.argv[1]
|
|
80
|
+
}
|
|
81
|
+
}))
|
|
82
|
+
" "$INSTRUCTIONS"
|
|
83
|
+
|
|
84
|
+
exit 0
|
package/src/cli.js
CHANGED
|
@@ -154,34 +154,6 @@ function copyScripts(targetDir) {
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
|
-
// CLAUDE.md integration
|
|
159
|
-
// ---------------------------------------------------------------------------
|
|
160
|
-
|
|
161
|
-
const FATHOM_MD_MARKER_START = "<!-- BEGIN fathom-mcp -->";
|
|
162
|
-
const FATHOM_MD_MARKER_END = "<!-- END fathom-mcp -->";
|
|
163
|
-
|
|
164
|
-
function appendToClaudeMd(cwd, blob) {
|
|
165
|
-
const mdPath = path.join(cwd, "CLAUDE.md");
|
|
166
|
-
let content = "";
|
|
167
|
-
try { content = fs.readFileSync(mdPath, "utf-8"); } catch { /* new file */ }
|
|
168
|
-
|
|
169
|
-
const wrappedBlob = `${FATHOM_MD_MARKER_START}\n${blob}\n${FATHOM_MD_MARKER_END}`;
|
|
170
|
-
|
|
171
|
-
const markerRe = new RegExp(
|
|
172
|
-
`${FATHOM_MD_MARKER_START.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${FATHOM_MD_MARKER_END.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`,
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
if (markerRe.test(content)) {
|
|
176
|
-
content = content.replace(markerRe, wrappedBlob);
|
|
177
|
-
} else {
|
|
178
|
-
content = content.trimEnd() + "\n\n" + wrappedBlob + "\n";
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
fs.writeFileSync(mdPath, content, "utf-8");
|
|
182
|
-
return mdPath;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
157
|
/**
|
|
186
158
|
* Check if fathom-server is available on PATH.
|
|
187
159
|
* Returns "installed" or "not-found".
|
|
@@ -562,6 +534,7 @@ async function runInit(flags = {}) {
|
|
|
562
534
|
}
|
|
563
535
|
|
|
564
536
|
// Hook scripts (central location, shared across agents)
|
|
537
|
+
const instructionsCmd = "bash ~/.config/fathom-mcp/scripts/fathom-instructions.sh";
|
|
565
538
|
const sessionStartCmd = "bash ~/.config/fathom-mcp/scripts/fathom-sessionstart.sh";
|
|
566
539
|
const recallCmd = "bash ~/.config/fathom-mcp/scripts/fathom-recall.sh";
|
|
567
540
|
const precompactCmd = "bash ~/.config/fathom-mcp/scripts/fathom-precompact.sh";
|
|
@@ -572,6 +545,8 @@ async function runInit(flags = {}) {
|
|
|
572
545
|
const settingsPath = path.join(cwd, ".claude", "settings.local.json");
|
|
573
546
|
const settings = readJsonFile(settingsPath) || {};
|
|
574
547
|
let changed = ensurePermissions(settings);
|
|
548
|
+
// Instructions hook always registered (injects tool reference tables)
|
|
549
|
+
changed = ensureHook(settings, "SessionStart", instructionsCmd, 5000) || changed;
|
|
575
550
|
changed = ensureHook(settings, "SessionStart", sessionStartCmd, 10000) || changed;
|
|
576
551
|
if (enableRecallHook) changed = ensureHook(settings, "UserPromptSubmit", recallCmd, 10000) || changed;
|
|
577
552
|
if (enablePrecompactHook) changed = ensureHook(settings, "PreCompact", precompactCmd, 30000) || changed;
|
|
@@ -586,7 +561,8 @@ async function runInit(flags = {}) {
|
|
|
586
561
|
if (hasGemini) {
|
|
587
562
|
const settingsPath = path.join(cwd, ".gemini", "settings.json");
|
|
588
563
|
const settings = readJsonFile(settingsPath) || {};
|
|
589
|
-
let changed = ensureHook(settings, "SessionStart",
|
|
564
|
+
let changed = ensureHook(settings, "SessionStart", instructionsCmd, 5000);
|
|
565
|
+
changed = ensureHook(settings, "SessionStart", sessionStartCmd, 10000) || changed;
|
|
590
566
|
if (enableRecallHook) changed = ensureHook(settings, "BeforeAgent", recallCmd, 10000) || changed;
|
|
591
567
|
if (enablePrecompactHook) changed = ensureHook(settings, "PreCompress", precompactCmd, 30000) || changed;
|
|
592
568
|
if (changed) {
|
|
@@ -673,50 +649,6 @@ async function runInit(flags = {}) {
|
|
|
673
649
|
console.log(`\n Non-interactive equivalent:\n ${parts.join(" ")}\n`);
|
|
674
650
|
}
|
|
675
651
|
|
|
676
|
-
// Append agent instructions to CLAUDE.md
|
|
677
|
-
let instructionsBlob = "";
|
|
678
|
-
try {
|
|
679
|
-
instructionsBlob = fs.readFileSync(agentMdDest, "utf-8");
|
|
680
|
-
} catch { /* template wasn't created — skip */ }
|
|
681
|
-
|
|
682
|
-
if (instructionsBlob) {
|
|
683
|
-
if (nonInteractive) {
|
|
684
|
-
const mdPath = appendToClaudeMd(cwd, instructionsBlob);
|
|
685
|
-
console.log(`\n ✓ Instructions written to ${path.relative(cwd, mdPath)}\n`);
|
|
686
|
-
} else {
|
|
687
|
-
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
688
|
-
console.log("\n" + "─".repeat(60));
|
|
689
|
-
const integrate = await askYesNo(
|
|
690
|
-
rl2,
|
|
691
|
-
"\n Append Fathom instructions to CLAUDE.md?",
|
|
692
|
-
true,
|
|
693
|
-
);
|
|
694
|
-
rl2.close();
|
|
695
|
-
|
|
696
|
-
if (integrate) {
|
|
697
|
-
const mdPath = appendToClaudeMd(cwd, instructionsBlob);
|
|
698
|
-
console.log(`\n ✓ Instructions written to ${path.relative(cwd, mdPath)}\n`);
|
|
699
|
-
} else {
|
|
700
|
-
printInstructionsFallback(agentMdDest, selectedAgents);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
} else {
|
|
704
|
-
console.log("\n No agent instructions template found — skipping integration.\n");
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
function printInstructionsFallback(agentMdPath, selectedAgents) {
|
|
709
|
-
const hasNonClaude = selectedAgents.some((k) => k !== "claude-code");
|
|
710
|
-
const docTarget = hasNonClaude
|
|
711
|
-
? "your CLAUDE.md, AGENTS.md, or equivalent"
|
|
712
|
-
: "your CLAUDE.md";
|
|
713
|
-
|
|
714
|
-
console.log(`
|
|
715
|
-
Agent instructions saved to: ${path.relative(process.cwd(), agentMdPath)}
|
|
716
|
-
|
|
717
|
-
Paste into ${docTarget}, or point your agent at the file
|
|
718
|
-
and ask it to integrate the instructions.
|
|
719
|
-
`);
|
|
720
652
|
}
|
|
721
653
|
|
|
722
654
|
// --- Status command ----------------------------------------------------------
|
|
@@ -784,9 +716,10 @@ async function runUpdate() {
|
|
|
784
716
|
const scriptsDir = path.join(process.env.HOME, ".config", "fathom-mcp", "scripts");
|
|
785
717
|
const copiedScripts = copyScripts(scriptsDir);
|
|
786
718
|
|
|
787
|
-
// Ensure SessionStart
|
|
719
|
+
// Ensure SessionStart hooks are registered for agents that support hooks
|
|
788
720
|
// Detect by config agents field or directory presence (older configs may lack agents)
|
|
789
721
|
const agents = found.config.agents || [];
|
|
722
|
+
const instructionsCmd = "bash ~/.config/fathom-mcp/scripts/fathom-instructions.sh";
|
|
790
723
|
const sessionStartCmd = "bash ~/.config/fathom-mcp/scripts/fathom-sessionstart.sh";
|
|
791
724
|
const registeredHooks = [];
|
|
792
725
|
|
|
@@ -797,6 +730,7 @@ async function runUpdate() {
|
|
|
797
730
|
const settingsPath = path.join(projectDir, ".claude", "settings.local.json");
|
|
798
731
|
const settings = readJsonFile(settingsPath) || {};
|
|
799
732
|
let changed = ensurePermissions(settings);
|
|
733
|
+
changed = ensureHook(settings, "SessionStart", instructionsCmd, 5000) || changed;
|
|
800
734
|
changed = ensureHook(settings, "SessionStart", sessionStartCmd, 10000) || changed;
|
|
801
735
|
if (changed) {
|
|
802
736
|
writeJsonFile(settingsPath, settings);
|
|
@@ -810,7 +744,9 @@ async function runUpdate() {
|
|
|
810
744
|
if (hasGemini) {
|
|
811
745
|
const settingsPath = path.join(projectDir, ".gemini", "settings.json");
|
|
812
746
|
const settings = readJsonFile(settingsPath) || {};
|
|
813
|
-
|
|
747
|
+
let changed = ensureHook(settings, "SessionStart", instructionsCmd, 5000);
|
|
748
|
+
changed = ensureHook(settings, "SessionStart", sessionStartCmd, 10000) || changed;
|
|
749
|
+
if (changed) {
|
|
814
750
|
writeJsonFile(settingsPath, settings);
|
|
815
751
|
registeredHooks.push("Gemini CLI → .gemini/settings.json");
|
|
816
752
|
}
|