stream0-channel 0.2.1 → 0.4.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.
@@ -6,9 +6,10 @@
6
6
  * Install: npx stream0-channel
7
7
  *
8
8
  * Environment variables:
9
- * STREAM0_URL - Stream0 server URL (default: http://localhost:8080)
10
- * STREAM0_API_KEY - API key for authentication
11
- * STREAM0_AGENT_ID - This agent's ID on Stream0
9
+ * STREAM0_URL - Stream0 server URL (default: http://localhost:8080)
10
+ * STREAM0_API_KEY - API key for group-level auth (register, list agents)
11
+ * STREAM0_AGENT_ID - This agent's ID on Stream0
12
+ * STREAM0_AGENT_TOKEN - Agent token for message operations (optional, obtained at registration)
12
13
  */
13
14
 
14
15
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -27,22 +28,32 @@ if (!AGENT_ID) {
27
28
  process.exit(1);
28
29
  }
29
30
 
30
- const headers = { "Content-Type": "application/json" };
31
- if (STREAM0_API_KEY) headers["X-API-Key"] = STREAM0_API_KEY;
31
+ // Group-level headers (X-API-Key) for registration and discovery
32
+ const groupHeaders = { "Content-Type": "application/json" };
33
+ if (STREAM0_API_KEY) groupHeaders["X-API-Key"] = STREAM0_API_KEY;
34
+
35
+ // Agent-level headers (X-Agent-Token) for send/receive/ack
36
+ let agentToken = process.env.STREAM0_AGENT_TOKEN || "";
37
+ function agentHeaders() {
38
+ return { "Content-Type": "application/json", "X-Agent-Token": agentToken };
39
+ }
32
40
 
33
41
  // --- Stream0 HTTP helpers ---
34
42
 
35
- async function stream0Get(path, params) {
43
+ async function stream0Get(path, params, useAgentAuth = false) {
36
44
  const url = new URL(`${STREAM0_URL}${path}`);
37
45
  if (params) for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
38
- const resp = await fetch(url.toString(), { headers, signal: AbortSignal.timeout(35000) });
46
+ const resp = await fetch(url.toString(), {
47
+ headers: useAgentAuth ? agentHeaders() : groupHeaders,
48
+ signal: AbortSignal.timeout(35000),
49
+ });
39
50
  return resp.json();
40
51
  }
41
52
 
42
- async function stream0Post(path, body) {
53
+ async function stream0Post(path, body, useAgentAuth = false) {
43
54
  const resp = await fetch(`${STREAM0_URL}${path}`, {
44
55
  method: "POST",
45
- headers,
56
+ headers: useAgentAuth ? agentHeaders() : groupHeaders,
46
57
  body: body ? JSON.stringify(body) : undefined,
47
58
  signal: AbortSignal.timeout(10000),
48
59
  });
@@ -56,7 +67,7 @@ function sleep(ms) {
56
67
  // --- MCP Server ---
57
68
 
58
69
  const mcp = new Server(
59
- { name: "stream0-channel", version: "0.2.0" },
70
+ { name: "stream0-channel", version: "0.4.0" },
60
71
  {
61
72
  capabilities: {
62
73
  experimental: { "claude/channel": {} },
@@ -169,7 +180,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
169
180
  mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
170
181
  const { name, arguments: args } = req.params;
171
182
 
172
- // --- discover ---
183
+ // --- discover (group-auth) ---
173
184
  if (name === "discover") {
174
185
  const result = await stream0Get("/agents");
175
186
  const agents = (result?.agents || [])
@@ -206,7 +217,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
206
217
  };
207
218
  }
208
219
 
209
- // --- delegate ---
220
+ // --- delegate (agent-auth) ---
210
221
  if (name === "delegate") {
211
222
  const { to, task, context, timeout: userTimeout } = args;
212
223
 
@@ -218,10 +229,9 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
218
229
 
219
230
  await stream0Post(`/agents/${to}/inbox`, {
220
231
  thread_id: threadId,
221
- from: AGENT_ID,
222
232
  type: "request",
223
233
  content,
224
- });
234
+ }, true);
225
235
 
226
236
  console.error(
227
237
  `[stream0-channel] Delegated to ${to} (thread: ${threadId}), waiting up to ${timeoutSec}s...`
@@ -237,11 +247,11 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
237
247
  status: "unread",
238
248
  thread_id: threadId,
239
249
  timeout: String(pollTimeout),
240
- });
250
+ }, true);
241
251
 
242
252
  const messages = result?.messages || [];
243
253
  for (const msg of messages) {
244
- await stream0Post(`/inbox/messages/${msg.id}/ack`);
254
+ await stream0Post(`/inbox/messages/${msg.id}/ack`, undefined, true);
245
255
 
246
256
  if (msg.type === "done") {
247
257
  const responseText =
@@ -307,7 +317,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
307
317
  };
308
318
  }
309
319
 
310
- // --- reply ---
320
+ // --- reply (agent-auth) ---
311
321
  if (name === "reply") {
312
322
  const { to, thread_id, type, content } = args;
313
323
 
@@ -320,18 +330,17 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
320
330
 
321
331
  await stream0Post(`/agents/${to}/inbox`, {
322
332
  thread_id,
323
- from: AGENT_ID,
324
333
  type,
325
334
  content: contentObj,
326
- });
335
+ }, true);
327
336
 
328
337
  return { content: [{ type: "text", text: `Replied to ${to} (thread: ${thread_id})` }] };
329
338
  }
330
339
 
331
- // --- ack ---
340
+ // --- ack (agent-auth) ---
332
341
  if (name === "ack") {
333
342
  const { message_id } = args;
334
- await stream0Post(`/inbox/messages/${message_id}/ack`);
343
+ await stream0Post(`/inbox/messages/${message_id}/ack`, undefined, true);
335
344
  return { content: [{ type: "text", text: `Acknowledged ${message_id}` }] };
336
345
  }
337
346
 
@@ -342,19 +351,27 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
342
351
 
343
352
  await mcp.connect(new StdioServerTransport());
344
353
 
345
- // Register agent on Stream0
346
- await stream0Post("/agents", { id: AGENT_ID });
354
+ // Register agent on Stream0 (group-auth) and get agent token
355
+ const regResult = await stream0Post("/agents", { id: AGENT_ID });
356
+ if (regResult?.agent_token) {
357
+ agentToken = regResult.agent_token;
358
+ }
347
359
  console.error(`[stream0-channel] Registered as ${AGENT_ID}, polling inbox...`);
348
360
 
361
+ if (!agentToken) {
362
+ console.error("[stream0-channel] Warning: no agent token available. Message operations will fail.");
363
+ }
364
+
349
365
  const pushed = new Set();
350
366
 
367
+ // Poll inbox (agent-auth)
351
368
  async function pollLoop() {
352
369
  while (true) {
353
370
  try {
354
371
  const result = await stream0Get(`/agents/${AGENT_ID}/inbox`, {
355
372
  status: "unread",
356
373
  timeout: "25",
357
- });
374
+ }, true);
358
375
 
359
376
  const messages = result?.messages || [];
360
377
  for (const msg of messages) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stream0-channel",
3
- "version": "0.2.1",
3
+ "version": "0.4.1",
4
4
  "description": "Stream0 MCP channel for Claude Code",
5
5
  "bin": {
6
6
  "stream0-channel": "bin/stream0-channel.mjs"