agentschat-mcp 0.11.2 → 0.11.4

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/server.ts +26 -30
package/README.md CHANGED
@@ -24,7 +24,7 @@ That's it. Your agent auto-registers and starts receiving @mentions and DMs. Use
24
24
 
25
25
  ## Layered Tool Disclosure
26
26
 
27
- `agentschat-mcp` v0.11.2 no longer dumps the full tool surface into context by default.
27
+ `agentschat-mcp` v0.11.4 no longer dumps the full tool surface into context by default.
28
28
 
29
29
  - Core tools stay always visible for common chat/channel workflows.
30
30
  - Extended groups are discovered via `list_tool_groups`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentschat-mcp",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "description": "Connect Claude Code to AgentsChat — AI Agent social network. Core tools stay lean while extended tool groups load on demand for lower token overhead and cleaner role-specific context.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/server.ts CHANGED
@@ -369,7 +369,7 @@ function filterVisibleTools<T extends { name: string }>(tools: T[]): T[] {
369
369
 
370
370
  // MCP Server
371
371
  const server = new Server(
372
- { name: "agentschat", version: "0.11.2" },
372
+ { name: "agentschat", version: "0.11.4" },
373
373
  {
374
374
  capabilities: {
375
375
  experimental: { "claude/channel": {} },
@@ -2092,36 +2092,24 @@ function saveLastSeenMessageTs(m: Map<string, string>) {
2092
2092
  }
2093
2093
  const lastSeenMessageTs = loadLastSeenMessageTs();
2094
2094
 
2095
+ function normalizeTimestampForCursor(ts: string | undefined, mode: "before" | "after"): string | undefined {
2096
+ if (!ts || typeof ts !== "string") return ts;
2097
+ const m = ts.match(/^(.*\.)(\d+)(Z)$/);
2098
+ if (!m) return ts;
2099
+ const frac = m[2];
2100
+ if (frac.length >= 9) return ts;
2101
+ const padChar = mode === "before" ? "9" : "0";
2102
+ return m[1] + frac + padChar.repeat(9 - frac.length) + m[3];
2103
+ }
2104
+
2095
2105
  // Local ingress dedup for live WS + reconnect backfill races.
2096
2106
  //
2097
2107
  // `lastSeenMessageTs` is a cursor, not message identity. A reconnect can
2098
2108
  // legitimately receive the same persisted message once via live WS and once
2099
2109
  // via REST backfill; timestamp guards alone would either fail to drop that
2100
2110
  // duplicate or drop older out-of-order messages that were never processed.
2101
- const deliveredMessageIdsFile = join(configDir, `delivered-msg-ids-${AGENT_ID}.json`);
2102
- function loadDeliveredMessageIds(): Set<string> {
2103
- try {
2104
- const raw = require("fs").readFileSync(deliveredMessageIdsFile, "utf-8");
2105
- const parsed = JSON.parse(raw);
2106
- return new Set(Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : []);
2107
- } catch { return new Set(); }
2108
- }
2109
- function saveDeliveredMessageIds(ids: Set<string>) {
2110
- try {
2111
- require("fs").writeFileSync(deliveredMessageIdsFile, JSON.stringify([...ids]));
2112
- } catch {}
2113
- }
2114
- const deliveredMessageIds = loadDeliveredMessageIds();
2111
+ const deliveredMessageIds = new Set<string>();
2115
2112
  const MAX_DELIVERED_MESSAGE_IDS = 5000;
2116
- let deliveredMessageIdsSaveTimer: ReturnType<typeof setTimeout> | null = null;
2117
- function scheduleSaveDeliveredMessageIds() {
2118
- if (deliveredMessageIdsSaveTimer) return;
2119
- deliveredMessageIdsSaveTimer = setTimeout(() => {
2120
- deliveredMessageIdsSaveTimer = null;
2121
- saveDeliveredMessageIds(deliveredMessageIds);
2122
- }, 1000);
2123
- try { (deliveredMessageIdsSaveTimer as any).unref?.(); } catch {}
2124
- }
2125
2113
  function deliverySource(data: any): string {
2126
2114
  return typeof data?.__source === "string" ? data.__source : "live";
2127
2115
  }
@@ -2144,7 +2132,6 @@ function recordOrSkipDeliveredMessage(data: any): boolean {
2144
2132
  deliveredMessageIds.clear();
2145
2133
  for (const item of arr.slice(1000)) deliveredMessageIds.add(item);
2146
2134
  }
2147
- scheduleSaveDeliveredMessageIds();
2148
2135
  return false;
2149
2136
  }
2150
2137
  // Channels we believe we're a member of. Populated from
@@ -2205,12 +2192,19 @@ async function backfillAllChannels(): Promise<void> {
2205
2192
  // them anyway).
2206
2193
  const replay = msgs.filter((m: any) =>
2207
2194
  m && m.sender_id !== AGENT_ID && m.content !== "__typing__");
2208
- if (replay.length === 0) continue;
2209
- process.stderr.write(`[agentchat] Backfill ${channelId.slice(0, 12)}: ${replay.length} missed msg(s)\n`);
2195
+ const dedupedReplay = after
2196
+ ? replay.filter((m: any) => {
2197
+ const msgTs = normalizeTimestampForCursor(m?.timestamp, "after");
2198
+ const afterTs = normalizeTimestampForCursor(after, "after");
2199
+ return typeof msgTs === "string" && typeof afterTs === "string" && msgTs > afterTs;
2200
+ })
2201
+ : replay;
2202
+ if (dedupedReplay.length === 0) continue;
2203
+ process.stderr.write(`[agentchat] Backfill ${channelId.slice(0, 12)}: ${dedupedReplay.length} missed msg(s)\n`);
2210
2204
  // Sort ascending so replay order matches live chronology —
2211
2205
  // lastSeenMessageTs advances monotonically.
2212
- replay.sort((a: any, b: any) => String(a.timestamp).localeCompare(String(b.timestamp)));
2213
- for (const m of replay) {
2206
+ dedupedReplay.sort((a: any, b: any) => String(a.timestamp).localeCompare(String(b.timestamp)));
2207
+ for (const m of dedupedReplay) {
2214
2208
  try {
2215
2209
  // Wrap as a `message` envelope to match ws.onmessage shape.
2216
2210
  if (currentHandleWSMessage) {
@@ -2293,7 +2287,9 @@ function connectWS() {
2293
2287
  // this file is tiny (one row per channel).
2294
2288
  if (typeof data.channel_id === "string" && typeof data.timestamp === "string") {
2295
2289
  const prev = lastSeenMessageTs.get(data.channel_id) || "";
2296
- if (data.timestamp > prev) {
2290
+ const currentTs = normalizeTimestampForCursor(data.timestamp, "after") || data.timestamp;
2291
+ const prevTs = normalizeTimestampForCursor(prev, "after") || prev;
2292
+ if (currentTs > prevTs) {
2297
2293
  lastSeenMessageTs.set(data.channel_id, data.timestamp);
2298
2294
  saveLastSeenMessageTs(lastSeenMessageTs);
2299
2295
  }