fathom-mcp 0.4.2 → 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/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/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
|
|