fathom-mcp 0.4.11 → 0.4.13
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/src/cli.js +4 -3
- package/src/index.js +226 -8
- package/src/server-client.js +70 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -129,7 +129,7 @@ function copyScripts(targetDir) {
|
|
|
129
129
|
// --- Headless agent integration ----------------------------------------------
|
|
130
130
|
|
|
131
131
|
const HEADLESS_CMDS = {
|
|
132
|
-
"claude-code": (prompt) => ["claude", "-p", prompt],
|
|
132
|
+
"claude-code": (prompt) => ["claude", "-p", "--dangerously-skip-permissions", prompt],
|
|
133
133
|
"codex": (prompt) => ["codex", "exec", prompt],
|
|
134
134
|
"gemini": (prompt) => ["gemini", prompt],
|
|
135
135
|
"opencode": (prompt) => ["opencode", "run", prompt],
|
|
@@ -679,12 +679,13 @@ async function runInit(flags = {}) {
|
|
|
679
679
|
} else {
|
|
680
680
|
if (cmdParts) {
|
|
681
681
|
const [cmd, ...args] = cmdParts;
|
|
682
|
-
const
|
|
682
|
+
const flagArgs = args.slice(0, -1).join(" ");
|
|
683
|
+
const displayCmd = `${cmd} ${flagArgs} <prompt>`;
|
|
683
684
|
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
684
685
|
console.log("\n" + "─".repeat(60));
|
|
685
686
|
const integrate = await askYesNo(
|
|
686
687
|
rl2,
|
|
687
|
-
`\n Auto-integrate instructions into your project?\n This will run: ${displayCmd}\n\n Proceed?`,
|
|
688
|
+
`\n Auto-integrate instructions into your project?\n This will run: ${displayCmd}\n\n ⚠ This uses --dangerously-skip-permissions so the agent can\n write to CLAUDE.md without prompting. If you prefer, decline\n and we'll print the instructions for you to add manually.\n\n Proceed?`,
|
|
688
689
|
true,
|
|
689
690
|
);
|
|
690
691
|
rl2.close();
|
package/src/index.js
CHANGED
|
@@ -209,8 +209,8 @@ const tools = [
|
|
|
209
209
|
"Post a message to a shared room. Rooms are created implicitly on first post. " +
|
|
210
210
|
"Use this for ambient, multilateral communication — unlike fathom_send (point-to-point DM), " +
|
|
211
211
|
"room messages are visible to all participants. Responding is optional — use `<...>` for active silence. " +
|
|
212
|
-
"Supports @workspace mentions (e.g. @fathom, @navier-stokes) — mentioned workspaces
|
|
213
|
-
"
|
|
212
|
+
"Supports @workspace mentions (e.g. @fathom, @navier-stokes) — mentioned workspaces receive " +
|
|
213
|
+
"notifications in their mentions:{workspace} virtual room via fathom_room_list. Use @all to notify every workspace except sender.",
|
|
214
214
|
inputSchema: {
|
|
215
215
|
type: "object",
|
|
216
216
|
properties: {
|
|
@@ -227,7 +227,8 @@ const tools = [
|
|
|
227
227
|
"to the latest message. Default: 60 minutes before the latest message. Use start to look " +
|
|
228
228
|
"further back. Example: minutes=15, start=120 returns 15 minutes of conversation starting " +
|
|
229
229
|
"2 hours before the latest message. Response includes window metadata with has_older flag " +
|
|
230
|
-
"for pseudo-pagination.
|
|
230
|
+
"for pseudo-pagination. All rooms are persistent — messages are never deleted on read. " +
|
|
231
|
+
"Automatically marks the room as read unless mark_read=false.",
|
|
231
232
|
inputSchema: {
|
|
232
233
|
type: "object",
|
|
233
234
|
properties: {
|
|
@@ -244,7 +245,8 @@ const tools = [
|
|
|
244
245
|
description:
|
|
245
246
|
"List all rooms with activity summary — message count, last activity time, last sender, " +
|
|
246
247
|
"description, and per-room unread_count for this workspace. Use to discover active rooms " +
|
|
247
|
-
"and see which have new messages."
|
|
248
|
+
"and see which have new messages. DM rooms (dm:a+b) are filtered by workspace param — " +
|
|
249
|
+
"only participants see them. Mention rooms (mentions:{workspace}) visible only to the target workspace.",
|
|
248
250
|
inputSchema: {
|
|
249
251
|
type: "object",
|
|
250
252
|
properties: {},
|
|
@@ -279,9 +281,9 @@ const tools = [
|
|
|
279
281
|
{
|
|
280
282
|
name: "fathom_send",
|
|
281
283
|
description:
|
|
282
|
-
"Send a message to another workspace's agent instance —
|
|
283
|
-
"
|
|
284
|
-
"
|
|
284
|
+
"Send a message to another workspace's agent instance — stored in a shared dm:a+b room " +
|
|
285
|
+
"visible to both participants. Use fathom_workspaces first to discover valid targets. " +
|
|
286
|
+
"DMs are persistent and appear in both participants' room lists via fathom_room_list.",
|
|
285
287
|
inputSchema: {
|
|
286
288
|
type: "object",
|
|
287
289
|
properties: {
|
|
@@ -340,6 +342,99 @@ const tools = [
|
|
|
340
342
|
required: [],
|
|
341
343
|
},
|
|
342
344
|
},
|
|
345
|
+
{
|
|
346
|
+
name: "fathom_routine_list",
|
|
347
|
+
description:
|
|
348
|
+
"List all ping routines for a workspace with their status, intervals, " +
|
|
349
|
+
"enabled state, and next fire time. Use this to see what routines exist " +
|
|
350
|
+
"before updating or deleting them.",
|
|
351
|
+
inputSchema: {
|
|
352
|
+
type: "object",
|
|
353
|
+
properties: {
|
|
354
|
+
workspace: WORKSPACE_PROP,
|
|
355
|
+
},
|
|
356
|
+
required: [],
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: "fathom_routine_update",
|
|
361
|
+
description:
|
|
362
|
+
"Update an existing ping routine. Only provided fields are changed — " +
|
|
363
|
+
"omitted fields keep their current values. Use fathom_routine_list first " +
|
|
364
|
+
"to find the routine_id.",
|
|
365
|
+
inputSchema: {
|
|
366
|
+
type: "object",
|
|
367
|
+
properties: {
|
|
368
|
+
routine_id: { type: "string", description: "The routine ID to update. Use fathom_routine_list to find IDs." },
|
|
369
|
+
name: { type: "string", description: "New routine name." },
|
|
370
|
+
enabled: { type: "boolean", description: "Enable or disable the routine." },
|
|
371
|
+
interval_minutes: { type: "integer", description: "New interval in minutes.", minimum: 1 },
|
|
372
|
+
single_fire: { type: "boolean", description: "Auto-disable after firing once." },
|
|
373
|
+
workspace: WORKSPACE_PROP,
|
|
374
|
+
context_sources: {
|
|
375
|
+
type: "object",
|
|
376
|
+
description: "What to inject on each ping.",
|
|
377
|
+
properties: {
|
|
378
|
+
time: { type: "boolean", description: "Include current time/date. Default: true." },
|
|
379
|
+
scripts: {
|
|
380
|
+
type: "array",
|
|
381
|
+
description: "Shell commands to run and inject output.",
|
|
382
|
+
items: {
|
|
383
|
+
type: "object",
|
|
384
|
+
properties: {
|
|
385
|
+
label: { type: "string" },
|
|
386
|
+
command: { type: "string" },
|
|
387
|
+
enabled: { type: "boolean" },
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
texts: {
|
|
392
|
+
type: "array",
|
|
393
|
+
description: "Static text blocks to inject.",
|
|
394
|
+
items: {
|
|
395
|
+
type: "object",
|
|
396
|
+
properties: {
|
|
397
|
+
label: { type: "string" },
|
|
398
|
+
content: { type: "string" },
|
|
399
|
+
enabled: { type: "boolean" },
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
required: ["routine_id"],
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "fathom_routine_delete",
|
|
411
|
+
description:
|
|
412
|
+
"Delete a ping routine permanently. Use fathom_routine_list first to find " +
|
|
413
|
+
"the routine_id. This cannot be undone — create a new routine if needed.",
|
|
414
|
+
inputSchema: {
|
|
415
|
+
type: "object",
|
|
416
|
+
properties: {
|
|
417
|
+
routine_id: { type: "string", description: "The routine ID to delete." },
|
|
418
|
+
workspace: WORKSPACE_PROP,
|
|
419
|
+
},
|
|
420
|
+
required: ["routine_id"],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: "fathom_routine_fire",
|
|
425
|
+
description:
|
|
426
|
+
"Fire a ping routine immediately, regardless of its schedule. Non-blocking — " +
|
|
427
|
+
"returns immediately while the routine fires in the background. The routine's " +
|
|
428
|
+
"next scheduled fire time is not affected.",
|
|
429
|
+
inputSchema: {
|
|
430
|
+
type: "object",
|
|
431
|
+
properties: {
|
|
432
|
+
routine_id: { type: "string", description: "The routine ID to fire." },
|
|
433
|
+
workspace: WORKSPACE_PROP,
|
|
434
|
+
},
|
|
435
|
+
required: ["routine_id"],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
343
438
|
];
|
|
344
439
|
|
|
345
440
|
// --- Vault routing by mode ---------------------------------------------------
|
|
@@ -377,6 +472,47 @@ function resolveVault(args) {
|
|
|
377
472
|
}
|
|
378
473
|
}
|
|
379
474
|
|
|
475
|
+
// --- Telegram tools (primary agent only) -------------------------------------
|
|
476
|
+
|
|
477
|
+
const telegramTools = [
|
|
478
|
+
{
|
|
479
|
+
name: "fathom_telegram_contacts",
|
|
480
|
+
description:
|
|
481
|
+
"List Telegram contacts who have messaged. Returns name, username, chat_id, " +
|
|
482
|
+
"last_message time, and unread count.",
|
|
483
|
+
inputSchema: { type: "object", properties: {} },
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "fathom_telegram_read",
|
|
487
|
+
description:
|
|
488
|
+
"Read recent Telegram messages from a contact. Same windowing as fathom_room_read — " +
|
|
489
|
+
"anchored to latest message, default 60 minutes. Use start to look further back.",
|
|
490
|
+
inputSchema: {
|
|
491
|
+
type: "object",
|
|
492
|
+
properties: {
|
|
493
|
+
contact: { type: "string", description: "Contact name, @username, or chat_id" },
|
|
494
|
+
minutes: { type: "number", description: "Window duration in minutes. Default: 60." },
|
|
495
|
+
start: { type: "number", description: "Offset in minutes from latest message. Default: 0." },
|
|
496
|
+
mark_read: { type: "boolean", description: "Mark messages as read. Default: true." },
|
|
497
|
+
},
|
|
498
|
+
required: ["contact"],
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: "fathom_telegram_send",
|
|
503
|
+
description:
|
|
504
|
+
"Send a Telegram message to a contact via the persistent Telethon client.",
|
|
505
|
+
inputSchema: {
|
|
506
|
+
type: "object",
|
|
507
|
+
properties: {
|
|
508
|
+
contact: { type: "string", description: "Contact name, @username, or chat_id" },
|
|
509
|
+
message: { type: "string", description: "Message text to send" },
|
|
510
|
+
},
|
|
511
|
+
required: ["contact", "message"],
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
];
|
|
515
|
+
|
|
380
516
|
// --- Server setup & dispatch -------------------------------------------------
|
|
381
517
|
|
|
382
518
|
const server = new Server(
|
|
@@ -384,7 +520,19 @@ const server = new Server(
|
|
|
384
520
|
{ capabilities: { tools: {} } },
|
|
385
521
|
);
|
|
386
522
|
|
|
387
|
-
server.setRequestHandler(ListToolsRequestSchema, async () =>
|
|
523
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
524
|
+
// Telegram tools only visible to the primary (default) workspace
|
|
525
|
+
let isPrimary = false;
|
|
526
|
+
try {
|
|
527
|
+
const settings = await client.getSettings();
|
|
528
|
+
const primaryAgent = settings.default_workspace;
|
|
529
|
+
isPrimary = config.workspace === primaryAgent;
|
|
530
|
+
} catch {
|
|
531
|
+
// If settings unavailable, hide telegram tools
|
|
532
|
+
}
|
|
533
|
+
const allTools = [...tools, ...(isPrimary ? telegramTools : [])];
|
|
534
|
+
return { tools: allTools };
|
|
535
|
+
});
|
|
388
536
|
|
|
389
537
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
390
538
|
const { name, arguments: args } = request.params;
|
|
@@ -539,6 +687,76 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
539
687
|
result = await client.createRoutine(routineParams, args.workspace || config.workspace);
|
|
540
688
|
break;
|
|
541
689
|
}
|
|
690
|
+
case "fathom_routine_list":
|
|
691
|
+
result = await client.listRoutines(args.workspace || config.workspace);
|
|
692
|
+
break;
|
|
693
|
+
case "fathom_routine_update": {
|
|
694
|
+
const updateParams = {};
|
|
695
|
+
if (args.name != null) updateParams.name = args.name;
|
|
696
|
+
if (args.enabled != null) updateParams.enabled = args.enabled;
|
|
697
|
+
if (args.interval_minutes != null) updateParams.intervalMinutes = args.interval_minutes;
|
|
698
|
+
if (args.single_fire != null) updateParams.singleFire = args.single_fire;
|
|
699
|
+
if (args.context_sources != null) updateParams.contextSources = args.context_sources;
|
|
700
|
+
result = await client.updateRoutine(args.routine_id, updateParams, args.workspace || config.workspace);
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
case "fathom_routine_delete":
|
|
704
|
+
result = await client.deleteRoutine(args.routine_id, args.workspace || config.workspace);
|
|
705
|
+
break;
|
|
706
|
+
case "fathom_routine_fire":
|
|
707
|
+
result = await client.fireRoutine(args.routine_id, args.workspace || config.workspace);
|
|
708
|
+
break;
|
|
709
|
+
// --- Telegram ---
|
|
710
|
+
case "fathom_telegram_contacts":
|
|
711
|
+
result = await client.telegramContacts(config.workspace);
|
|
712
|
+
break;
|
|
713
|
+
case "fathom_telegram_read": {
|
|
714
|
+
// Resolve contact name to chat_id via contacts list
|
|
715
|
+
const contacts = await client.telegramContacts(config.workspace);
|
|
716
|
+
const contactList = contacts?.contacts || [];
|
|
717
|
+
const contactArg = (args.contact || "").trim();
|
|
718
|
+
let chatId = parseInt(contactArg, 10);
|
|
719
|
+
if (isNaN(chatId)) {
|
|
720
|
+
const lower = contactArg.toLowerCase().replace(/^@/, "");
|
|
721
|
+
const match = contactList.find(c =>
|
|
722
|
+
(c.username || "").toLowerCase() === lower ||
|
|
723
|
+
(c.first_name || "").toLowerCase() === lower ||
|
|
724
|
+
(c.first_name || "").toLowerCase().includes(lower)
|
|
725
|
+
);
|
|
726
|
+
chatId = match ? match.chat_id : null;
|
|
727
|
+
}
|
|
728
|
+
if (!chatId) {
|
|
729
|
+
result = { error: `Contact not found: ${contactArg}. Use fathom_telegram_contacts to list known contacts.` };
|
|
730
|
+
} else {
|
|
731
|
+
result = await client.telegramRead(
|
|
732
|
+
chatId, args.minutes, args.start,
|
|
733
|
+
args.mark_read !== false ? config.workspace : undefined,
|
|
734
|
+
args.mark_read,
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
case "fathom_telegram_send": {
|
|
740
|
+
const sendContacts = await client.telegramContacts(config.workspace);
|
|
741
|
+
const sendList = sendContacts?.contacts || [];
|
|
742
|
+
const sendArg = (args.contact || "").trim();
|
|
743
|
+
let sendChatId = parseInt(sendArg, 10);
|
|
744
|
+
if (isNaN(sendChatId)) {
|
|
745
|
+
const lower = sendArg.toLowerCase().replace(/^@/, "");
|
|
746
|
+
const match = sendList.find(c =>
|
|
747
|
+
(c.username || "").toLowerCase() === lower ||
|
|
748
|
+
(c.first_name || "").toLowerCase() === lower ||
|
|
749
|
+
(c.first_name || "").toLowerCase().includes(lower)
|
|
750
|
+
);
|
|
751
|
+
sendChatId = match ? match.chat_id : null;
|
|
752
|
+
}
|
|
753
|
+
if (!sendChatId) {
|
|
754
|
+
result = { error: `Contact not found: ${sendArg}. Use fathom_telegram_contacts to list known contacts.` };
|
|
755
|
+
} else {
|
|
756
|
+
result = await client.telegramSend(sendChatId, args.message);
|
|
757
|
+
}
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
542
760
|
default:
|
|
543
761
|
result = { error: `Unknown tool: ${name}` };
|
|
544
762
|
}
|
package/src/server-client.js
CHANGED
|
@@ -205,6 +205,37 @@ export function createClient(config) {
|
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
async function listRoutines(ws) {
|
|
209
|
+
return request("GET", "/api/activation/ping/routines", {
|
|
210
|
+
params: { workspace: ws },
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function updateRoutine(routineId, params, ws) {
|
|
215
|
+
const body = {};
|
|
216
|
+
if (params.name != null) body.name = params.name;
|
|
217
|
+
if (params.enabled != null) body.enabled = params.enabled;
|
|
218
|
+
if (params.intervalMinutes != null) body.intervalMinutes = params.intervalMinutes;
|
|
219
|
+
if (params.singleFire != null) body.singleFire = params.singleFire;
|
|
220
|
+
if (params.contextSources != null) body.contextSources = params.contextSources;
|
|
221
|
+
return request("POST", `/api/activation/ping/routines/${encodeURIComponent(routineId)}`, {
|
|
222
|
+
params: { workspace: ws },
|
|
223
|
+
body,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function deleteRoutine(routineId, ws) {
|
|
228
|
+
return request("DELETE", `/api/activation/ping/routines/${encodeURIComponent(routineId)}`, {
|
|
229
|
+
params: { workspace: ws },
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function fireRoutine(routineId, ws) {
|
|
234
|
+
return request("POST", `/api/activation/ping/routines/${encodeURIComponent(routineId)}/now`, {
|
|
235
|
+
params: { workspace: ws },
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
208
239
|
// --- Heartbeat -------------------------------------------------------------
|
|
209
240
|
|
|
210
241
|
async function heartbeat(ws, agent, vaultMode) {
|
|
@@ -214,6 +245,36 @@ export function createClient(config) {
|
|
|
214
245
|
return request("POST", `/api/workspaces/${encodeURIComponent(ws)}/heartbeat`, { body });
|
|
215
246
|
}
|
|
216
247
|
|
|
248
|
+
// --- Telegram --------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
async function telegramContacts(ws) {
|
|
251
|
+
return request("GET", "/api/telegram/contacts", {
|
|
252
|
+
params: { workspace: ws },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function telegramRead(chatId, minutes, start, ws, markRead) {
|
|
257
|
+
return request("GET", `/api/telegram/messages/${chatId}`, {
|
|
258
|
+
params: { minutes, start, workspace: ws, mark_read: markRead },
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function telegramSend(chatId, message) {
|
|
263
|
+
return request("POST", `/api/telegram/send/${chatId}`, {
|
|
264
|
+
body: { message },
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function telegramStatus() {
|
|
269
|
+
return request("GET", "/api/telegram/status");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// --- Settings --------------------------------------------------------------
|
|
273
|
+
|
|
274
|
+
async function getSettings() {
|
|
275
|
+
return request("GET", "/api/settings");
|
|
276
|
+
}
|
|
277
|
+
|
|
217
278
|
// --- Auth ------------------------------------------------------------------
|
|
218
279
|
|
|
219
280
|
async function getApiKey() {
|
|
@@ -254,7 +315,16 @@ export function createClient(config) {
|
|
|
254
315
|
pushFile,
|
|
255
316
|
syncManifest,
|
|
256
317
|
createRoutine,
|
|
318
|
+
listRoutines,
|
|
319
|
+
updateRoutine,
|
|
320
|
+
deleteRoutine,
|
|
321
|
+
fireRoutine,
|
|
257
322
|
heartbeat,
|
|
323
|
+
telegramContacts,
|
|
324
|
+
telegramRead,
|
|
325
|
+
telegramSend,
|
|
326
|
+
telegramStatus,
|
|
327
|
+
getSettings,
|
|
258
328
|
getApiKey,
|
|
259
329
|
healthCheck,
|
|
260
330
|
};
|