fathom-mcp 0.4.1 → 0.4.3
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/README.md +1 -1
- package/fathom-agents.md +3 -30
- package/package.json +1 -1
- package/scripts/fathom-precompact.sh +19 -0
- package/scripts/fathom-recall.sh +11 -0
- package/scripts/fathom-toast.sh +139 -0
- package/src/index.js +8 -4
- package/src/server-client.js +2 -2
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ npx fathom-mcp status # Check server connection + workspace status
|
|
|
70
70
|
| `fathom_vault_vsearch` | Semantic/vector search |
|
|
71
71
|
| `fathom_vault_query` | Hybrid search (BM25 + vectors + reranking) |
|
|
72
72
|
| `fathom_room_post` | Post to a shared room (supports @mentions) |
|
|
73
|
-
| `fathom_room_read` | Read
|
|
73
|
+
| `fathom_room_read` | Read room messages (windowed, anchored to latest; `minutes`/`start` for pagination) |
|
|
74
74
|
| `fathom_room_list` | List all rooms |
|
|
75
75
|
| `fathom_room_describe` | Set a room's description/topic |
|
|
76
76
|
| `fathom_workspaces` | List all configured workspaces |
|
package/fathom-agents.md
CHANGED
|
@@ -2,34 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
{{DESCRIPTION}}
|
|
4
4
|
|
|
5
|
-
## Memory — Memento Protocol
|
|
6
|
-
|
|
7
|
-
Working memory is managed by Memento (workspace: `{{WORKSPACE_NAME}}`).
|
|
8
|
-
|
|
9
|
-
**On session start:**
|
|
10
|
-
1. `memento_health` — verify connection
|
|
11
|
-
2. `memento_item_list` — check active work items and their next actions
|
|
12
|
-
3. `memento_recall` with current task context — find relevant past memories
|
|
13
|
-
|
|
14
|
-
**During work — actively manage your memories:**
|
|
15
|
-
- `memento_store` when you learn something, make a decision, or discover a pattern
|
|
16
|
-
- `memento_recall` before starting any subtask — someone may have already figured it out
|
|
17
|
-
- `memento_item_update` as you make progress — don't wait until the end
|
|
18
|
-
- `memento_item_create` when new work emerges
|
|
19
|
-
- `memento_skip_add` the moment you hit a dead end (with expiry)
|
|
20
|
-
- `memento_consolidate` when recall returns 3+ overlapping memories on the same topic
|
|
21
|
-
- Delete or archive items that are done or wrong — stale memory is worse than no memory
|
|
22
|
-
|
|
23
|
-
**Writing discipline — instructions, not logs:**
|
|
24
|
-
- Write: "API moved to /v2 — update all calls" not "checked API, got 404"
|
|
25
|
-
- Write: "Skip X until condition Y" not "checked X, it was quiet"
|
|
26
|
-
- Tag generously — tags power recall and consolidation
|
|
27
|
-
- Set expiration on time-sensitive facts
|
|
28
|
-
- The test: could a future you, with zero context, read this and know exactly what to do?
|
|
29
|
-
|
|
30
5
|
## Vault
|
|
31
6
|
|
|
32
|
-
Local files live in `{{VAULT_DIR}}/`.
|
|
7
|
+
Local files live in `{{VAULT_DIR}}/`. The vault is for long-form content — things that need to breathe, not be queried.
|
|
33
8
|
|
|
34
9
|
**Folder conventions:**
|
|
35
10
|
- `research/` — reading notes, paper annotations, deep dives
|
|
@@ -50,7 +25,6 @@ status: draft # optional: draft | published | archived
|
|
|
50
25
|
|
|
51
26
|
This workspace is part of a multi-workspace system. Other workspaces exist — you can talk to them.
|
|
52
27
|
|
|
53
|
-
- **Peek at another workspace's memory:** `memento_recall query="..." workspace="other-ws"`
|
|
54
28
|
- **Peek at another workspace's vault:** `fathom_vault_read path="file.md" workspace="other-ws"`
|
|
55
29
|
- **Send a direct message:** `fathom_send workspace="other-ws" message="..."`
|
|
56
30
|
- **Post to a shared room:** `fathom_room_post room="general" message="..."`
|
|
@@ -63,6 +37,5 @@ Your sender identity is automatic — messages are tagged with `{{WORKSPACE_NAME
|
|
|
63
37
|
|
|
64
38
|
1. Research and reading notes → `vault/research/`
|
|
65
39
|
2. Speculative connections and insights → `vault/thinking/`
|
|
66
|
-
3.
|
|
67
|
-
4.
|
|
68
|
-
5. When done — update Memento items, write what you found and what questions remain
|
|
40
|
+
3. Session heartbeats → `vault/daily/`
|
|
41
|
+
4. When done — write what you found and what questions remain
|
package/package.json
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
TOAST="$HOOK_DIR/fathom-toast.sh"
|
|
11
|
+
|
|
9
12
|
# Walk up to find .fathom.json
|
|
10
13
|
find_config() {
|
|
11
14
|
local dir="$PWD"
|
|
@@ -30,18 +33,29 @@ if [ "$HOOK_ENABLED" != "True" ] && [ "$HOOK_ENABLED" != "true" ]; then
|
|
|
30
33
|
exit 0
|
|
31
34
|
fi
|
|
32
35
|
|
|
36
|
+
# Toast: start multi-stage
|
|
37
|
+
"$TOAST" memento --status precompact &>/dev/null
|
|
38
|
+
"$TOAST" --update precompact "⏳ Getting context..." &>/dev/null
|
|
39
|
+
|
|
33
40
|
# Read PreCompact input (contains transcript_path)
|
|
34
41
|
INPUT=$(cat)
|
|
35
42
|
TRANSCRIPT_PATH=$(echo "$INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('transcript_path',''))" 2>/dev/null || echo "")
|
|
36
43
|
|
|
37
44
|
if [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
45
|
+
"$TOAST" --update precompact "✗ No transcript found" &>/dev/null
|
|
46
|
+
"$TOAST" --close precompact &>/dev/null &
|
|
38
47
|
exit 0
|
|
39
48
|
fi
|
|
40
49
|
|
|
50
|
+
# Toast: extracting
|
|
51
|
+
"$TOAST" --update precompact "⏳ Extracting memories..." &>/dev/null
|
|
52
|
+
|
|
41
53
|
# Extract vault file paths from transcript
|
|
42
54
|
VAULT_FILES=$(grep -oP 'vault/[a-zA-Z0-9_/.-]+\.md' "$TRANSCRIPT_PATH" 2>/dev/null | sort -u || echo "")
|
|
43
55
|
|
|
44
56
|
if [ -z "$VAULT_FILES" ]; then
|
|
57
|
+
"$TOAST" --update precompact "✓ No vault files to record" &>/dev/null
|
|
58
|
+
("$TOAST" --close precompact &>/dev/null &)
|
|
45
59
|
exit 0
|
|
46
60
|
fi
|
|
47
61
|
|
|
@@ -59,6 +73,11 @@ done
|
|
|
59
73
|
|
|
60
74
|
# Output summary
|
|
61
75
|
FILE_COUNT=$(echo "$VAULT_FILES" | wc -l)
|
|
76
|
+
|
|
77
|
+
# Toast: done
|
|
78
|
+
"$TOAST" --update precompact "✓ Stored ${FILE_COUNT} vault file(s)" &>/dev/null
|
|
79
|
+
("$TOAST" --close precompact &>/dev/null &)
|
|
80
|
+
|
|
62
81
|
python3 -c "
|
|
63
82
|
import json, sys
|
|
64
83
|
result = {
|
package/scripts/fathom-recall.sh
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
set -o pipefail
|
|
12
12
|
|
|
13
13
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
14
|
+
TOAST="$HOOK_DIR/fathom-toast.sh"
|
|
14
15
|
VSEARCH_CACHE="/tmp/fathom-vsearch-cache.json"
|
|
15
16
|
VSEARCH_LOCK="/tmp/fathom-vsearch.lock"
|
|
16
17
|
STALE_LOCK_SECONDS=180 # 3 minutes — lock older than this is considered stale
|
|
@@ -75,6 +76,9 @@ fi
|
|
|
75
76
|
|
|
76
77
|
QUERY="${USER_MESSAGE:0:500}"
|
|
77
78
|
|
|
79
|
+
# Toast: start retrieving
|
|
80
|
+
"$TOAST" fathom "⏳ Retrieving docs..." &>/dev/null
|
|
81
|
+
|
|
78
82
|
# --- Phase 1: Read cached vsearch results from previous query ---
|
|
79
83
|
CACHED_VSEARCH=""
|
|
80
84
|
if [ -f "$VSEARCH_CACHE" ]; then
|
|
@@ -151,6 +155,10 @@ if [ -n "$BM25_RESULTS" ] || [ -n "$CACHED_VSEARCH" ]; then
|
|
|
151
155
|
fi
|
|
152
156
|
|
|
153
157
|
SUMMARY="Fathom Vault: ${VAULT_COUNT} memories"
|
|
158
|
+
|
|
159
|
+
# Toast: result
|
|
160
|
+
"$TOAST" fathom "✓ ${VAULT_COUNT} docs recalled" &>/dev/null
|
|
161
|
+
|
|
154
162
|
python3 -c "
|
|
155
163
|
import json, sys
|
|
156
164
|
summary = sys.argv[1]
|
|
@@ -163,6 +171,9 @@ print(json.dumps({
|
|
|
163
171
|
}
|
|
164
172
|
}))
|
|
165
173
|
" "$SUMMARY" "$DETAIL_TEXT"
|
|
174
|
+
else
|
|
175
|
+
# Toast: no results
|
|
176
|
+
"$TOAST" fathom "✓ No docs matched" &>/dev/null
|
|
166
177
|
fi
|
|
167
178
|
|
|
168
179
|
# --- Phase 3: Launch background vsearch for current query ---
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# fathom-toast.sh — tmux popup notifications for Fathom/Memento hooks.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# fathom-toast.sh <system> <message> # one-shot toast (auto-closes after 2s)
|
|
6
|
+
# fathom-toast.sh <system> --status <id> # start a multi-stage toast (poll mode)
|
|
7
|
+
# fathom-toast.sh --update <id> <message> # update a running toast
|
|
8
|
+
# fathom-toast.sh --close <id> # close a running toast (after delay)
|
|
9
|
+
#
|
|
10
|
+
# Systems: fathom, memento
|
|
11
|
+
# fathom → 📝 purple (colour141)
|
|
12
|
+
# memento → 🧠 teal (colour37)
|
|
13
|
+
#
|
|
14
|
+
# Multi-stage example (PreCompact):
|
|
15
|
+
# fathom-toast.sh memento --status precompact
|
|
16
|
+
# fathom-toast.sh --update precompact "⏳ Getting context..."
|
|
17
|
+
# fathom-toast.sh --update precompact "⏳ Extracting memories..."
|
|
18
|
+
# fathom-toast.sh --update precompact "✓ Stored 7 memories"
|
|
19
|
+
# fathom-toast.sh --close precompact
|
|
20
|
+
|
|
21
|
+
TOAST_DIR="/tmp/fathom-toast"
|
|
22
|
+
mkdir -p "$TOAST_DIR"
|
|
23
|
+
|
|
24
|
+
# Bail silently if not in tmux
|
|
25
|
+
if ! tmux info &>/dev/null; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
get_style() {
|
|
30
|
+
local system="$1"
|
|
31
|
+
case "$system" in
|
|
32
|
+
memento) echo "colour37" ;;
|
|
33
|
+
fathom) echo "colour141" ;;
|
|
34
|
+
*) echo "colour245" ;;
|
|
35
|
+
esac
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get_icon() {
|
|
39
|
+
local system="$1"
|
|
40
|
+
case "$system" in
|
|
41
|
+
memento) echo "🧠" ;;
|
|
42
|
+
fathom) echo "📝" ;;
|
|
43
|
+
*) echo "📌" ;;
|
|
44
|
+
esac
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get_title() {
|
|
48
|
+
local system="$1"
|
|
49
|
+
case "$system" in
|
|
50
|
+
memento) echo "Memento" ;;
|
|
51
|
+
fathom) echo "Fathom" ;;
|
|
52
|
+
*) echo "$system" ;;
|
|
53
|
+
esac
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# One-shot toast
|
|
57
|
+
toast_oneshot() {
|
|
58
|
+
local system="$1"
|
|
59
|
+
local message="$2"
|
|
60
|
+
local colour icon title
|
|
61
|
+
colour=$(get_style "$system")
|
|
62
|
+
icon=$(get_icon "$system")
|
|
63
|
+
title=$(get_title "$system")
|
|
64
|
+
|
|
65
|
+
(tmux display-popup -x R -y 0 -w 42 -h 3 \
|
|
66
|
+
-s "fg=$colour" -T " $icon $title " -E \
|
|
67
|
+
"echo ' $message'; sleep 2" &>/dev/null &)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Start a multi-stage toast (polling status file)
|
|
71
|
+
toast_start() {
|
|
72
|
+
local system="$1"
|
|
73
|
+
local id="$2"
|
|
74
|
+
local status_file="$TOAST_DIR/$id"
|
|
75
|
+
local colour icon title
|
|
76
|
+
colour=$(get_style "$system")
|
|
77
|
+
icon=$(get_icon "$system")
|
|
78
|
+
title=$(get_title "$system")
|
|
79
|
+
|
|
80
|
+
echo "⏳ Starting..." > "$status_file"
|
|
81
|
+
|
|
82
|
+
(tmux display-popup -x R -y 0 -w 42 -h 3 \
|
|
83
|
+
-s "fg=$colour" -T " $icon $title " -E "
|
|
84
|
+
LAST=''
|
|
85
|
+
while [ -f '$status_file' ]; do
|
|
86
|
+
MSG=\$(cat '$status_file' 2>/dev/null)
|
|
87
|
+
if [ \"\$MSG\" != \"\$LAST\" ]; then
|
|
88
|
+
clear
|
|
89
|
+
echo \" \$MSG\"
|
|
90
|
+
LAST=\"\$MSG\"
|
|
91
|
+
fi
|
|
92
|
+
case \"\$MSG\" in ✓*|✗*) sleep 2; break ;; esac
|
|
93
|
+
sleep 0.2
|
|
94
|
+
done
|
|
95
|
+
" &>/dev/null &)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Update a running toast
|
|
99
|
+
toast_update() {
|
|
100
|
+
local id="$1"
|
|
101
|
+
local message="$2"
|
|
102
|
+
local status_file="$TOAST_DIR/$id"
|
|
103
|
+
echo "$message" > "$status_file"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Close a running toast (remove status file — popup exits on next poll)
|
|
107
|
+
toast_close() {
|
|
108
|
+
local id="$1"
|
|
109
|
+
local status_file="$TOAST_DIR/$id"
|
|
110
|
+
# Give the popup time to display the final message
|
|
111
|
+
sleep 2
|
|
112
|
+
rm -f "$status_file"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# --- Argument parsing ---
|
|
116
|
+
case "${1:-}" in
|
|
117
|
+
--update)
|
|
118
|
+
toast_update "$2" "$3"
|
|
119
|
+
;;
|
|
120
|
+
--close)
|
|
121
|
+
toast_close "$2"
|
|
122
|
+
;;
|
|
123
|
+
fathom|memento)
|
|
124
|
+
SYSTEM="$1"
|
|
125
|
+
shift
|
|
126
|
+
if [ "${1:-}" = "--status" ]; then
|
|
127
|
+
toast_start "$SYSTEM" "$2"
|
|
128
|
+
else
|
|
129
|
+
toast_oneshot "$SYSTEM" "$*"
|
|
130
|
+
fi
|
|
131
|
+
;;
|
|
132
|
+
*)
|
|
133
|
+
echo "Usage: fathom-toast.sh <fathom|memento> <message>" >&2
|
|
134
|
+
echo " fathom-toast.sh <fathom|memento> --status <id>" >&2
|
|
135
|
+
echo " fathom-toast.sh --update <id> <message>" >&2
|
|
136
|
+
echo " fathom-toast.sh --close <id>" >&2
|
|
137
|
+
exit 1
|
|
138
|
+
;;
|
|
139
|
+
esac
|
package/src/index.js
CHANGED
|
@@ -220,13 +220,17 @@ const tools = [
|
|
|
220
220
|
{
|
|
221
221
|
name: "fathom_room_read",
|
|
222
222
|
description:
|
|
223
|
-
"Read recent messages from a shared room. Returns messages
|
|
224
|
-
"
|
|
223
|
+
"Read recent messages from a shared room. Returns messages within a time window anchored " +
|
|
224
|
+
"to the latest message. Default: 60 minutes before the latest message. Use start to look " +
|
|
225
|
+
"further back. Example: minutes=15, start=120 returns 15 minutes of conversation starting " +
|
|
226
|
+
"2 hours before the latest message. Response includes window metadata with has_older flag " +
|
|
227
|
+
"for pseudo-pagination.",
|
|
225
228
|
inputSchema: {
|
|
226
229
|
type: "object",
|
|
227
230
|
properties: {
|
|
228
231
|
room: { type: "string", description: "Room name to read from" },
|
|
229
|
-
|
|
232
|
+
minutes: { type: "number", description: "Window duration in minutes. Default: 60." },
|
|
233
|
+
start: { type: "number", description: "Offset in minutes from the latest message. Default: 0 (window ends at latest message). Set to 120 to look back starting 2 hours before the latest message." },
|
|
230
234
|
},
|
|
231
235
|
required: ["room"],
|
|
232
236
|
},
|
|
@@ -412,7 +416,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
412
416
|
result = await client.roomPost(args.room, args.message, config.workspace);
|
|
413
417
|
break;
|
|
414
418
|
case "fathom_room_read":
|
|
415
|
-
result = await client.roomRead(args.room, args.
|
|
419
|
+
result = await client.roomRead(args.room, args.minutes, args.start);
|
|
416
420
|
break;
|
|
417
421
|
case "fathom_room_list":
|
|
418
422
|
result = await client.roomList();
|
package/src/server-client.js
CHANGED
|
@@ -82,9 +82,9 @@ export function createClient(config) {
|
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
async function roomRead(room,
|
|
85
|
+
async function roomRead(room, minutes, start) {
|
|
86
86
|
return request("GET", `/api/room/${encodeURIComponent(room)}`, {
|
|
87
|
-
params: {
|
|
87
|
+
params: { minutes, start },
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
|