@slock-ai/daemon 0.2.1 → 0.2.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.
@@ -1,141 +1,109 @@
1
- #!/usr/bin/env npx tsx
1
+ #!/usr/bin/env node
2
+
3
+ // src/chat-bridge.ts
2
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
6
  import { z } from "zod";
5
-
6
- // Parse CLI args
7
- const args = process.argv.slice(2);
8
- let agentId = "";
9
- let serverUrl = "http://localhost:3001";
10
- let authToken = "";
11
-
7
+ var args = process.argv.slice(2);
8
+ var agentId = "";
9
+ var serverUrl = "http://localhost:3001";
10
+ var authToken = "";
12
11
  for (let i = 0; i < args.length; i++) {
13
12
  if (args[i] === "--agent-id" && args[i + 1]) agentId = args[++i];
14
13
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
15
14
  if (args[i] === "--auth-token" && args[i + 1]) authToken = args[++i];
16
15
  }
17
-
18
16
  if (!agentId) {
19
17
  console.error("Missing --agent-id");
20
18
  process.exit(1);
21
19
  }
22
-
23
- // Build common headers for all requests
24
- const commonHeaders: Record<string, string> = { "Content-Type": "application/json" };
20
+ var commonHeaders = { "Content-Type": "application/json" };
25
21
  if (authToken) {
26
22
  commonHeaders["Authorization"] = `Bearer ${authToken}`;
27
23
  }
28
-
29
- const server = new McpServer({
24
+ var server = new McpServer({
30
25
  name: "chat",
31
- version: "1.0.0",
26
+ version: "1.0.0"
32
27
  });
33
-
34
- // send_message tool
35
28
  server.tool(
36
29
  "send_message",
37
30
  "Send a message to a channel or DM. To reply, reuse the channel value from the received message (e.g. channel='#all' or channel='DM:@richard'). To start a NEW DM, use dm_to with the person's name.",
38
31
  {
39
- channel: z
40
- .string()
41
- .optional()
42
- .describe(
43
- "Where to send. Reuse the identifier from received messages: '#channel-name' for channels, 'DM:@peer-name' for DMs. Examples: '#all', '#general', 'DM:@richard'."
44
- ),
45
- dm_to: z
46
- .string()
47
- .optional()
48
- .describe(
49
- "Person's name to start a NEW DM with (e.g. 'richard'). Only for starting a new DM — to reply in an existing DM, use channel instead."
50
- ),
51
- content: z.string().describe("The message content"),
32
+ channel: z.string().optional().describe(
33
+ "Where to send. Reuse the identifier from received messages: '#channel-name' for channels, 'DM:@peer-name' for DMs. Examples: '#all', '#general', 'DM:@richard'."
34
+ ),
35
+ dm_to: z.string().optional().describe(
36
+ "Person's name to start a NEW DM with (e.g. 'richard'). Only for starting a new DM \u2014 to reply in an existing DM, use channel instead."
37
+ ),
38
+ content: z.string().describe("The message content")
52
39
  },
53
40
  async ({ channel, dm_to, content }) => {
54
41
  try {
55
42
  const res = await fetch(`${serverUrl}/internal/agent/${agentId}/send`, {
56
43
  method: "POST",
57
44
  headers: commonHeaders,
58
- body: JSON.stringify({ channel, dm_to, content }),
45
+ body: JSON.stringify({ channel, dm_to, content })
59
46
  });
60
47
  const data = await res.json();
61
48
  if (!res.ok) {
62
49
  return {
63
50
  content: [
64
- { type: "text" as const, text: `Error: ${data.error}` },
65
- ],
51
+ { type: "text", text: `Error: ${data.error}` }
52
+ ]
66
53
  };
67
54
  }
68
55
  return {
69
56
  content: [
70
57
  {
71
- type: "text" as const,
72
- text: `Message sent to ${channel || `new DM with ${dm_to}`}`,
73
- },
74
- ],
58
+ type: "text",
59
+ text: `Message sent to ${channel || `new DM with ${dm_to}`}`
60
+ }
61
+ ]
75
62
  };
76
- } catch (err: any) {
63
+ } catch (err) {
77
64
  return {
78
- content: [{ type: "text" as const, text: `Error: ${err.message}` }],
65
+ content: [{ type: "text", text: `Error: ${err.message}` }]
79
66
  };
80
67
  }
81
68
  }
82
69
  );
83
-
84
- // receive_message tool
85
70
  server.tool(
86
71
  "receive_message",
87
72
  "Receive new messages. Use block=true to wait for new messages. Returns messages formatted as [#channel-name] or [DM:@peer-name] followed by the sender and content.",
88
73
  {
89
- block: z
90
- .boolean()
91
- .default(true)
92
- .describe("Whether to block (wait) for new messages"),
93
- timeout_ms: z
94
- .number()
95
- .default(59000)
96
- .describe("How long to wait in ms when blocking (default 59s, just under MCP tool call timeout)"),
74
+ block: z.boolean().default(true).describe("Whether to block (wait) for new messages"),
75
+ timeout_ms: z.number().default(59e3).describe("How long to wait in ms when blocking (default 59s, just under MCP tool call timeout)")
97
76
  },
98
77
  async ({ block, timeout_ms }) => {
99
78
  try {
100
79
  const params = new URLSearchParams();
101
80
  if (block) params.set("block", "true");
102
81
  params.set("timeout", String(timeout_ms));
103
-
104
82
  const res = await fetch(
105
83
  `${serverUrl}/internal/agent/${agentId}/receive?${params}`,
106
84
  { method: "GET", headers: commonHeaders }
107
85
  );
108
86
  const data = await res.json();
109
-
110
87
  if (!data.messages || data.messages.length === 0) {
111
88
  return {
112
- content: [{ type: "text" as const, text: "No new messages." }],
89
+ content: [{ type: "text", text: "No new messages." }]
113
90
  };
114
91
  }
115
-
116
- // Format with human-readable channel identifiers (no UUIDs)
117
- const formatted = data.messages
118
- .map((m: any) => {
119
- const channel = m.channel_type === "dm"
120
- ? `DM:@${m.channel_name}`
121
- : `#${m.channel_name}`;
122
- const senderPrefix = m.sender_type === "agent" ? "(agent) " : "";
123
- return `[${channel}] ${senderPrefix}@${m.sender_name}: ${m.content}`;
124
- })
125
- .join("\n");
126
-
92
+ const formatted = data.messages.map((m) => {
93
+ const channel = m.channel_type === "dm" ? `DM:@${m.channel_name}` : `#${m.channel_name}`;
94
+ const senderPrefix = m.sender_type === "agent" ? "(agent) " : "";
95
+ return `[${channel}] ${senderPrefix}@${m.sender_name}: ${m.content}`;
96
+ }).join("\n");
127
97
  return {
128
- content: [{ type: "text" as const, text: formatted }],
98
+ content: [{ type: "text", text: formatted }]
129
99
  };
130
- } catch (err: any) {
100
+ } catch (err) {
131
101
  return {
132
- content: [{ type: "text" as const, text: `Error: ${err.message}` }],
102
+ content: [{ type: "text", text: `Error: ${err.message}` }]
133
103
  };
134
104
  }
135
105
  }
136
106
  );
137
-
138
- // list_server tool
139
107
  server.tool(
140
108
  "list_server",
141
109
  "List all channels you are in, all agents, and all humans in this server. Use this to discover who and where you can message.",
@@ -147,68 +115,55 @@ server.tool(
147
115
  { method: "GET", headers: commonHeaders }
148
116
  );
149
117
  const data = await res.json();
150
-
151
118
  let text = "## Server\n\n";
152
-
153
119
  text += "### Your Channels\n";
154
120
  text += "Use `#channel-name` with send_message to post in a channel.\n";
155
121
  if (data.channels?.length > 0) {
156
122
  for (const t of data.channels) {
157
- text += ` - #${t.name}\n`;
123
+ text += ` - #${t.name}
124
+ `;
158
125
  }
159
126
  } else {
160
127
  text += " (none)\n";
161
128
  }
162
-
163
129
  text += "\n### Agents\n";
164
130
  text += "Other AI agents in this server.\n";
165
131
  if (data.agents?.length > 0) {
166
132
  for (const a of data.agents) {
167
- text += ` - @${a.name} (${a.status})\n`;
133
+ text += ` - @${a.name} (${a.status})
134
+ `;
168
135
  }
169
136
  } else {
170
137
  text += " (none)\n";
171
138
  }
172
-
173
139
  text += "\n### Humans\n";
174
- text += "To start a new DM: send_message(dm_to=\"<name>\"). To reply in an existing DM: reuse channel from the received message.\n";
140
+ text += 'To start a new DM: send_message(dm_to="<name>"). To reply in an existing DM: reuse channel from the received message.\n';
175
141
  if (data.humans?.length > 0) {
176
142
  for (const u of data.humans) {
177
- text += ` - @${u.name}\n`;
143
+ text += ` - @${u.name}
144
+ `;
178
145
  }
179
146
  } else {
180
147
  text += " (none)\n";
181
148
  }
182
-
183
149
  return {
184
- content: [{ type: "text" as const, text }],
150
+ content: [{ type: "text", text }]
185
151
  };
186
- } catch (err: any) {
152
+ } catch (err) {
187
153
  return {
188
- content: [{ type: "text" as const, text: `Error: ${err.message}` }],
154
+ content: [{ type: "text", text: `Error: ${err.message}` }]
189
155
  };
190
156
  }
191
157
  }
192
158
  );
193
-
194
- // read_history tool
195
159
  server.tool(
196
160
  "read_history",
197
161
  "Read message history for a channel or DM. Use #channel-name for channels or DM:@name for DMs. Supports pagination: use 'before' to load older messages, 'after' to load messages after a seq number (e.g. to catch up on unread).",
198
162
  {
199
- channel: z.string().describe("The channel to read history from e.g. '#all', '#general', 'DM:@richard'"),
200
- limit: z
201
- .number()
202
- .default(50)
203
- .describe("Max number of messages to return (default 50, max 100)"),
204
- before: z
205
- .number()
206
- .optional()
207
- .describe("Return messages before this seq number (for backward pagination). Omit for latest messages."),
208
- after: z
209
- .number()
210
- .optional()
211
- .describe("Return messages after this seq number (for catching up on unread). Returns oldest-first."),
163
+ channel: z.string().describe("The channel to read history from \u2014 e.g. '#all', '#general', 'DM:@richard'"),
164
+ limit: z.number().default(50).describe("Max number of messages to return (default 50, max 100)"),
165
+ before: z.number().optional().describe("Return messages before this seq number (for backward pagination). Omit for latest messages."),
166
+ after: z.number().optional().describe("Return messages after this seq number (for catching up on unread). Returns oldest-first.")
212
167
  },
213
168
  async ({ channel, limit, before, after }) => {
214
169
  try {
@@ -217,68 +172,68 @@ server.tool(
217
172
  params.set("limit", String(Math.min(limit, 100)));
218
173
  if (before) params.set("before", String(before));
219
174
  if (after) params.set("after", String(after));
220
-
221
175
  const res = await fetch(
222
176
  `${serverUrl}/internal/agent/${agentId}/history?${params}`,
223
177
  { method: "GET", headers: commonHeaders }
224
178
  );
225
179
  const data = await res.json();
226
-
227
180
  if (!res.ok) {
228
181
  return {
229
182
  content: [
230
- { type: "text" as const, text: `Error: ${data.error}` },
231
- ],
183
+ { type: "text", text: `Error: ${data.error}` }
184
+ ]
232
185
  };
233
186
  }
234
-
235
187
  if (!data.messages || data.messages.length === 0) {
236
188
  return {
237
189
  content: [
238
- { type: "text" as const, text: "No messages in this channel." },
239
- ],
190
+ { type: "text", text: "No messages in this channel." }
191
+ ]
240
192
  };
241
193
  }
242
-
243
- const formatted = data.messages
244
- .map((m: any) => {
245
- const senderPrefix = m.senderType === "agent" ? "(agent) " : "";
246
- return `[seq:${m.seq}] ${senderPrefix}@${m.senderName}: ${m.content}`;
247
- })
248
- .join("\n");
249
-
194
+ const formatted = data.messages.map((m) => {
195
+ const senderPrefix = m.senderType === "agent" ? "(agent) " : "";
196
+ return `[seq:${m.seq}] ${senderPrefix}@${m.senderName}: ${m.content}`;
197
+ }).join("\n");
250
198
  let footer = "";
251
- if (data.has_more && data.messages.length > 0) {
199
+ if (data.historyLimited) {
200
+ footer = `
201
+
202
+ --- ${data.historyLimitMessage || "Message history is limited on this plan."} ---`;
203
+ } else if (data.has_more && data.messages.length > 0) {
252
204
  if (after) {
253
205
  const maxSeq = data.messages[data.messages.length - 1].seq;
254
- footer = `\n\n--- ${data.messages.length} messages shown. Use after=${maxSeq} to load more recent messages. ---`;
206
+ footer = `
207
+
208
+ --- ${data.messages.length} messages shown. Use after=${maxSeq} to load more recent messages. ---`;
255
209
  } else {
256
210
  const minSeq = data.messages[0].seq;
257
- footer = `\n\n--- ${data.messages.length} messages shown. Use before=${minSeq} to load older messages. ---`;
211
+ footer = `
212
+
213
+ --- ${data.messages.length} messages shown. Use before=${minSeq} to load older messages. ---`;
258
214
  }
259
215
  }
260
-
261
216
  let header = `## Message History for ${channel} (${data.messages.length} messages)`;
262
217
  if (data.last_read_seq > 0 && !after && !before) {
263
- header += `\nYour last read position: seq ${data.last_read_seq}. Use read_history(channel="${channel}", after=${data.last_read_seq}) to see only unread messages.`;
218
+ header += `
219
+ Your last read position: seq ${data.last_read_seq}. Use read_history(channel="${channel}", after=${data.last_read_seq}) to see only unread messages.`;
264
220
  }
265
-
266
221
  return {
267
222
  content: [
268
223
  {
269
- type: "text" as const,
270
- text: `${header}\n\n${formatted}${footer}`,
271
- },
272
- ],
224
+ type: "text",
225
+ text: `${header}
226
+
227
+ ${formatted}${footer}`
228
+ }
229
+ ]
273
230
  };
274
- } catch (err: any) {
231
+ } catch (err) {
275
232
  return {
276
- content: [{ type: "text" as const, text: `Error: ${err.message}` }],
233
+ content: [{ type: "text", text: `Error: ${err.message}` }]
277
234
  };
278
235
  }
279
236
  }
280
237
  );
281
-
282
- // Start MCP server
283
- const transport = new StdioServerTransport();
238
+ var transport = new StdioServerTransport();
284
239
  await server.connect(transport);