@tjamescouch/agentchat-mcp 0.8.1 → 0.8.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/agentchat-mcp",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
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.22.0",
30
+ "@tjamescouch/agentchat": "^0.22.1",
31
31
  "zod": "^3.25.0"
32
32
  },
33
33
  "peerDependencies": {
package/state.js CHANGED
@@ -12,8 +12,25 @@ export let keepaliveInterval = null;
12
12
  // Message tracking - timestamp of last message we returned to the caller
13
13
  export let lastSeenTimestamp = 0;
14
14
 
15
+ // Message buffer - captures messages between listen() calls
16
+ const MAX_BUFFER_SIZE = 200;
17
+ let messageBuffer = [];
18
+
15
19
  // Default server
16
- export const DEFAULT_SERVER_URL = 'wss://agentchat-server.fly.dev';
20
+ export const DEFAULT_SERVER_URL = (() => {
21
+ const explicit = process.env.AGENTCHAT_URL;
22
+ if (explicit) {
23
+ const parsed = new URL(explicit);
24
+ const isLocal = parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1' || parsed.hostname === '::1';
25
+ if (!isLocal && process.env.AGENTCHAT_PUBLIC !== 'true') {
26
+ console.error(`ERROR: AGENTCHAT_URL points to remote host "${parsed.hostname}" but AGENTCHAT_PUBLIC is not set.`);
27
+ console.error('Set AGENTCHAT_PUBLIC=true to allow connections to non-localhost servers.');
28
+ process.exit(1);
29
+ }
30
+ return explicit;
31
+ }
32
+ return process.env.AGENTCHAT_PUBLIC === 'true' ? 'wss://agentchat-server.fly.dev' : 'ws://localhost:6667';
33
+ })();
17
34
 
18
35
  // Keepalive settings
19
36
  export const KEEPALIVE_INTERVAL_MS = 30000;
@@ -69,3 +86,29 @@ export function resetLastSeen() {
69
86
  export function getLastSeen() {
70
87
  return lastSeenTimestamp;
71
88
  }
89
+
90
+ /**
91
+ * Push a message into the buffer (called by persistent handler on client)
92
+ */
93
+ export function bufferMessage(msg) {
94
+ messageBuffer.push(msg);
95
+ if (messageBuffer.length > MAX_BUFFER_SIZE) {
96
+ messageBuffer = messageBuffer.slice(-MAX_BUFFER_SIZE);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Drain all buffered messages and clear the buffer
102
+ */
103
+ export function drainMessageBuffer() {
104
+ const messages = messageBuffer;
105
+ messageBuffer = [];
106
+ return messages;
107
+ }
108
+
109
+ /**
110
+ * Clear the message buffer (e.g., on reconnect)
111
+ */
112
+ export function clearMessageBuffer() {
113
+ messageBuffer = [];
114
+ }
package/tools/connect.js CHANGED
@@ -10,7 +10,7 @@ import path from 'path';
10
10
  import {
11
11
  client, keepaliveInterval,
12
12
  setClient, setServerUrl, setKeepaliveInterval,
13
- resetLastSeen,
13
+ resetLastSeen, clearMessageBuffer, bufferMessage,
14
14
  DEFAULT_SERVER_URL, KEEPALIVE_INTERVAL_MS
15
15
  } from '../state.js';
16
16
 
@@ -55,7 +55,7 @@ export function registerConnectTool(server) {
55
55
  'agentchat_connect',
56
56
  'Connect to an AgentChat server for real-time agent communication',
57
57
  {
58
- server_url: z.string().optional().describe('WebSocket URL (default: wss://agentchat-server.fly.dev)'),
58
+ server_url: z.string().optional().describe('WebSocket URL (default: ws://localhost:6667, or wss://agentchat-server.fly.dev if AGENTCHAT_PUBLIC=true)'),
59
59
  name: z.string().optional().describe('Agent name for persistent identity. Creates .agentchat/identities/<name>.json. Omit for ephemeral identity.'),
60
60
  identity_path: z.string().optional().describe('Custom path to identity file (overrides name)'),
61
61
  },
@@ -132,8 +132,23 @@ export function registerConnectTool(server) {
132
132
  setClient(newClient);
133
133
  setServerUrl(actualServerUrl);
134
134
 
135
- // Reset last seen timestamp on new connection
135
+ // Reset last seen timestamp and message buffer on new connection
136
136
  resetLastSeen();
137
+ clearMessageBuffer();
138
+
139
+ // Persistent message handler - buffers ALL messages between listen() calls
140
+ // This is the fix for the listen() message drop bug: messages that arrive
141
+ // while the agent is busy with other tools are captured here instead of lost.
142
+ newClient.on('message', (msg) => {
143
+ // Skip own messages, server noise, and replays
144
+ if (msg.from === newClient.agentId || msg.from === '@server' || msg.replay) return;
145
+ bufferMessage({
146
+ from: msg.from,
147
+ to: msg.to,
148
+ content: msg.content,
149
+ ts: msg.ts,
150
+ });
151
+ });
137
152
 
138
153
  // Start keepalive ping to prevent connection timeout
139
154
  const interval = setInterval(() => {
package/tools/listen.js CHANGED
@@ -8,7 +8,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
10
  import { ClientMessageType } from '@tjamescouch/agentchat/lib/protocol.js';
11
- import { client, getLastSeen, updateLastSeen } from '../state.js';
11
+ import { client, getLastSeen, updateLastSeen, drainMessageBuffer } from '../state.js';
12
12
 
13
13
  // Timeouts - agent cannot override these
14
14
  const ENFORCED_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour when alone
@@ -87,9 +87,16 @@ export function registerListenTool(server) {
87
87
  };
88
88
  setPresence('listening');
89
89
 
90
- // Start with any replay messages captured during join
90
+ // Drain the persistent message buffer (messages captured between listen calls)
91
+ const buffered = drainMessageBuffer().filter(m => {
92
+ // Filter to relevant channels/DMs, skip already-seen messages
93
+ const isRelevant = channels.includes(m.to) || m.to === client.agentId;
94
+ return isRelevant && (!m.ts || m.ts > lastSeen);
95
+ });
96
+
97
+ // Start with buffered messages + replay messages captured during join
91
98
  const paths = getDaemonPaths('default');
92
- let missedMessages = [...replayMessages];
99
+ let missedMessages = [...buffered, ...replayMessages];
93
100
 
94
101
  if (fs.existsSync(paths.inbox)) {
95
102
  try {