@tjamescouch/agentchat-mcp 0.7.0 → 0.8.1
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 +2 -2
- package/tools/connect.js +10 -1
- package/tools/listen.js +47 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tjamescouch/agentchat-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "MCP server for AgentChat - real-time AI agent communication",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
30
|
-
"@tjamescouch/agentchat": "^0.
|
|
30
|
+
"@tjamescouch/agentchat": "^0.22.0",
|
|
31
31
|
"zod": "^3.25.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
package/tools/connect.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
import { AgentChatClient } from '@tjamescouch/agentchat';
|
|
7
|
+
import { AgentChatClient, checkDirectorySafety } from '@tjamescouch/agentchat';
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import {
|
|
@@ -61,6 +61,15 @@ export function registerConnectTool(server) {
|
|
|
61
61
|
},
|
|
62
62
|
async ({ server_url, name, identity_path }) => {
|
|
63
63
|
try {
|
|
64
|
+
// Security check: prevent running in root/system directories
|
|
65
|
+
const safetyCheck = checkDirectorySafety(process.cwd());
|
|
66
|
+
if (safetyCheck.level === 'error') {
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: 'text', text: `Security Error: ${safetyCheck.error}` }],
|
|
69
|
+
isError: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
64
73
|
// Stop existing keepalive
|
|
65
74
|
if (keepaliveInterval) {
|
|
66
75
|
clearInterval(keepaliveInterval);
|
package/tools/listen.js
CHANGED
|
@@ -7,6 +7,7 @@ import { z } from 'zod';
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import { getDaemonPaths } from '@tjamescouch/agentchat/lib/daemon.js';
|
|
9
9
|
import { addJitter } from '@tjamescouch/agentchat/lib/jitter.js';
|
|
10
|
+
import { ClientMessageType } from '@tjamescouch/agentchat/lib/protocol.js';
|
|
10
11
|
import { client, getLastSeen, updateLastSeen } from '../state.js';
|
|
11
12
|
|
|
12
13
|
// Timeouts - agent cannot override these
|
|
@@ -32,14 +33,33 @@ export function registerListenTool(server) {
|
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
const startTime = Date.now();
|
|
37
|
+
|
|
38
|
+
// Collect replay messages during channel joins
|
|
39
|
+
// The server sends buffered messages with { replay: true } on join,
|
|
40
|
+
// so we must capture them before they're emitted and lost
|
|
41
|
+
const replayMessages = [];
|
|
42
|
+
const replayHandler = (msg) => {
|
|
43
|
+
if (msg.replay && msg.from !== client.agentId && msg.from !== '@server') {
|
|
44
|
+
replayMessages.push({
|
|
45
|
+
from: msg.from,
|
|
46
|
+
to: msg.to,
|
|
47
|
+
content: msg.content,
|
|
48
|
+
ts: msg.ts,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
client.on('message', replayHandler);
|
|
53
|
+
|
|
54
|
+
// Join channels (replay messages arrive here)
|
|
36
55
|
for (const channel of channels) {
|
|
37
56
|
if (!client.channels.has(channel)) {
|
|
38
57
|
await client.join(channel);
|
|
39
58
|
}
|
|
40
59
|
}
|
|
41
60
|
|
|
42
|
-
|
|
61
|
+
// Done collecting replays
|
|
62
|
+
client.removeListener('message', replayHandler);
|
|
43
63
|
|
|
44
64
|
// Check channel occupancy to determine timeout behavior
|
|
45
65
|
let othersPresent = false;
|
|
@@ -61,9 +81,15 @@ export function registerListenTool(server) {
|
|
|
61
81
|
}
|
|
62
82
|
const lastSeen = getLastSeen();
|
|
63
83
|
|
|
64
|
-
//
|
|
84
|
+
// Set presence to 'listening' so other agents see we're active
|
|
85
|
+
const setPresence = (status) => {
|
|
86
|
+
client.sendRaw({ type: ClientMessageType.SET_PRESENCE, status });
|
|
87
|
+
};
|
|
88
|
+
setPresence('listening');
|
|
89
|
+
|
|
90
|
+
// Start with any replay messages captured during join
|
|
65
91
|
const paths = getDaemonPaths('default');
|
|
66
|
-
let missedMessages = [];
|
|
92
|
+
let missedMessages = [...replayMessages];
|
|
67
93
|
|
|
68
94
|
if (fs.existsSync(paths.inbox)) {
|
|
69
95
|
try {
|
|
@@ -106,19 +132,33 @@ export function registerListenTool(server) {
|
|
|
106
132
|
}
|
|
107
133
|
}
|
|
108
134
|
|
|
109
|
-
// If we have missed messages, return them immediately
|
|
135
|
+
// If we have missed messages (from replay or inbox), return them immediately
|
|
110
136
|
if (missedMessages.length > 0) {
|
|
137
|
+
// Deduplicate by timestamp + from (replay and inbox may overlap)
|
|
138
|
+
const seen = new Set();
|
|
139
|
+
missedMessages = missedMessages.filter((m) => {
|
|
140
|
+
const key = `${m.ts}:${m.from}`;
|
|
141
|
+
if (seen.has(key)) return false;
|
|
142
|
+
seen.add(key);
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Sort by timestamp ascending (oldest first)
|
|
147
|
+
missedMessages.sort((a, b) => a.ts - b.ts);
|
|
148
|
+
|
|
111
149
|
// Update last seen to the newest message timestamp
|
|
112
150
|
const newestTs = missedMessages[missedMessages.length - 1].ts;
|
|
113
151
|
updateLastSeen(newestTs);
|
|
114
152
|
|
|
153
|
+
setPresence('online');
|
|
115
154
|
return {
|
|
116
155
|
content: [
|
|
117
156
|
{
|
|
118
157
|
type: 'text',
|
|
119
158
|
text: JSON.stringify({
|
|
120
159
|
messages: missedMessages,
|
|
121
|
-
from_inbox:
|
|
160
|
+
from_inbox: replayMessages.length === 0,
|
|
161
|
+
from_replay: replayMessages.length > 0,
|
|
122
162
|
elapsed_ms: Date.now() - startTime,
|
|
123
163
|
}),
|
|
124
164
|
},
|
|
@@ -165,6 +205,7 @@ export function registerListenTool(server) {
|
|
|
165
205
|
const cleanup = () => {
|
|
166
206
|
client.removeListener('message', messageHandler);
|
|
167
207
|
if (timeoutId) clearTimeout(timeoutId);
|
|
208
|
+
setPresence('online');
|
|
168
209
|
};
|
|
169
210
|
|
|
170
211
|
client.on('message', messageHandler);
|