mcp-chat-connect 1.3.2 → 1.4.0

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 (2) hide show
  1. package/index.js +149 -17
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -67,12 +67,22 @@ async function apiCall(tool, args, token) {
67
67
 
68
68
  // ─── Version check ──────────────────────────────────────────────────────────
69
69
 
70
+ function isNewerVersion(latest, current) {
71
+ const l = latest.split('.').map(Number);
72
+ const c = current.split('.').map(Number);
73
+ for (let i = 0; i < 3; i++) {
74
+ if ((l[i] || 0) > (c[i] || 0)) return true;
75
+ if ((l[i] || 0) < (c[i] || 0)) return false;
76
+ }
77
+ return false;
78
+ }
79
+
70
80
  async function checkForUpdate() {
71
81
  try {
72
82
  const response = await fetch(`${MCP_CHAT_URL}/api/version`);
73
83
  if (!response.ok) return null;
74
84
  const { latest } = await response.json();
75
- if (latest && latest !== LOCAL_VERSION) {
85
+ if (latest && isNewerVersion(latest, LOCAL_VERSION)) {
76
86
  return `UPDATE AVAILABLE: You are running mcp-chat-connect v${LOCAL_VERSION}, but v${latest} is available. Run: npm install -g mcp-chat-connect`;
77
87
  }
78
88
  } catch {}
@@ -127,7 +137,7 @@ function connectWebSocket() {
127
137
  if (msg.session_id === sessionState.sessionToken) return;
128
138
 
129
139
  const senderLabel = msg.session_id
130
- ? `${msg.user_name?.split(' ')[0]}'s Claude`
140
+ ? `${msg.user_name?.split(' ')[0]}'s Claude${msg.session_label ? ` (${msg.session_label})` : ''}`
131
141
  : msg.user_name || 'unknown';
132
142
 
133
143
  pushChannelMessage('mcp-chat', msg.content, {
@@ -136,6 +146,27 @@ function connectWebSocket() {
136
146
  message_type: msg.message_type || 'info',
137
147
  timestamp: msg.created_at || new Date().toISOString(),
138
148
  });
149
+ } else if (data.type === 'session_renamed') {
150
+ // Only react when this session itself was renamed (e.g. from the browser)
151
+ if (data.session_token !== sessionState.sessionToken) return;
152
+ if (data.label === sessionState.sessionLabel) return;
153
+ sessionState.sessionLabel = data.label;
154
+ pushChannelMessage('mcp-chat', `This session has been named "${data.label}". Refer to yourself as "${data.label}" in #${sessionState.channelName}.`, {
155
+ channel: sessionState.channelName,
156
+ event: 'session_renamed',
157
+ session_label: data.label,
158
+ });
159
+ } else if (data.type === 'channel_instructions_updated') {
160
+ // Skip the echo of a change this session just made itself
161
+ if ((data.instructions || null) === sessionState.sessionInstructions) return;
162
+ sessionState.sessionInstructions = data.instructions || null;
163
+ const body = data.instructions
164
+ ? `Channel instructions for #${sessionState.channelName} were updated${data.updated_by ? ` by ${data.updated_by}` : ''}. Follow these instructions for this channel:\n\n${data.instructions}`
165
+ : `Channel instructions for #${sessionState.channelName} were cleared.`;
166
+ pushChannelMessage('mcp-chat', body, {
167
+ channel: sessionState.channelName,
168
+ event: 'channel_instructions_updated',
169
+ });
139
170
  } else if (data.type === 'presence') {
140
171
  // Only push presence for Claude Code sessions (have session_token), not browser refreshes
141
172
  if (!data.session_token) return;
@@ -250,6 +281,7 @@ let sessionState = {
250
281
  userId: null,
251
282
  sessionToken: null,
252
283
  sessionLabel: null,
284
+ sessionInstructions: null,
253
285
  connected: false,
254
286
  };
255
287
 
@@ -282,15 +314,19 @@ if (envToken && envChannel) {
282
314
  userId,
283
315
  sessionToken,
284
316
  sessionLabel: null,
317
+ sessionInstructions: null,
285
318
  connected: true,
286
319
  };
287
320
 
288
- // Register session for sequential label, then connect WebSocket
321
+ // Register session for label + channel instructions, then connect WebSocket
289
322
  apiCall('register_session', {
290
323
  channel_id: sessionState.channelId,
291
324
  session_token: sessionToken,
325
+ label: process.env.MCP_CHAT_SESSION_NAME || undefined,
292
326
  }, envToken).then(result => {
293
327
  sessionState.sessionLabel = result.label || 'Session';
328
+ sessionState.sessionInstructions = result.instructions || null;
329
+ if (result.channel_name) sessionState.channelName = result.channel_name;
294
330
  process.stderr.write(`[mcp-chat] Auto-connected to #${sessionState.channelName} as ${userName} (${sessionState.sessionLabel})\n`);
295
331
  }).catch(() => {
296
332
  process.stderr.write(`[mcp-chat] Auto-connected to #${sessionState.channelName} as ${userName}\n`);
@@ -314,9 +350,15 @@ function getTools() {
314
350
  {
315
351
  name: 'mcp_chat_connect',
316
352
  description: sessionState.connected
317
- ? `Currently connected to #${sessionState.channelName} as ${sessionState.userName}. Live messages are being pushed into this session. Run again to switch channels.`
318
- : 'Connect to MCP Chat. Opens your browser to authenticate and select a channel. Once connected, messages will be pushed into this session in real-time.',
319
- inputSchema: { type: 'object', properties: {}, required: [] },
353
+ ? `Currently connected to #${sessionState.channelName} as ${sessionState.userName} (${sessionState.sessionLabel || 'Session'}). Live messages are being pushed into this session. Run again to switch channels.`
354
+ : 'Connect to MCP Chat. Opens your browser to authenticate and select a channel. Once connected, messages will be pushed into this session in real-time. Optionally pass a label to name this session.',
355
+ inputSchema: {
356
+ type: 'object',
357
+ properties: {
358
+ label: { type: 'string', description: 'Optional name for this session (e.g. "Backend Dev", "QA"). Defaults to a sequential "Session N".' },
359
+ },
360
+ required: [],
361
+ },
320
362
  },
321
363
  {
322
364
  name: 'mcp_chat_send',
@@ -404,6 +446,33 @@ function getTools() {
404
446
  },
405
447
  },
406
448
  },
449
+ {
450
+ name: 'mcp_chat_set_name',
451
+ description: 'Set or change the name of your own session. Other participants (and you) will see this name on every message you send.',
452
+ inputSchema: {
453
+ type: 'object',
454
+ properties: {
455
+ name: { type: 'string', description: 'The name for this session (e.g. "Backend Dev", "QA Agent").' },
456
+ },
457
+ required: ['name'],
458
+ },
459
+ },
460
+ {
461
+ name: 'mcp_chat_instructions',
462
+ description: 'Show the current channel instructions (a shared system prompt set for everyone in the channel).',
463
+ inputSchema: { type: 'object', properties: {} },
464
+ },
465
+ {
466
+ name: 'mcp_chat_set_instructions',
467
+ description: 'Set the channel instructions: a shared system prompt that every connected session in the channel sees. Pass an empty string to clear. Any channel member can set these.',
468
+ inputSchema: {
469
+ type: 'object',
470
+ properties: {
471
+ instructions: { type: 'string', description: 'The shared instructions for the channel. Empty string clears them.' },
472
+ },
473
+ required: ['instructions'],
474
+ },
475
+ },
407
476
  ];
408
477
  }
409
478
 
@@ -432,19 +501,22 @@ async function handleToolCall(name, args) {
432
501
  userId,
433
502
  sessionToken,
434
503
  sessionLabel: null,
504
+ sessionInstructions: null,
435
505
  connected: true,
436
506
  };
437
507
  saveConfig({ token: result.token, userName: result.userName, userId });
438
508
 
439
- // Register session to get sequential label
440
- let sessionLabel = 'Session';
509
+ // Register session to get label (custom or sequential) + channel instructions
510
+ let sessionLabel = args.label || 'Session';
441
511
  try {
442
512
  const regResult = await apiCall('register_session', {
443
513
  channel_id: result.channelId,
444
514
  session_token: sessionToken,
515
+ label: args.label || undefined,
445
516
  }, result.token);
446
- sessionLabel = regResult.label || 'Session';
517
+ sessionLabel = regResult.label || sessionLabel;
447
518
  sessionState.sessionLabel = sessionLabel;
519
+ sessionState.sessionInstructions = regResult.instructions || null;
448
520
  } catch {}
449
521
 
450
522
  // Start WebSocket listener for real-time push
@@ -452,7 +524,10 @@ async function handleToolCall(name, args) {
452
524
 
453
525
  // Check for package updates
454
526
  const updateNotice = await checkForUpdate();
455
- let responseText = `Connected to #${result.channelName} as ${result.userName} (${sessionLabel}). Live messages will now be pushed into this session. You can also use mcp_chat_send to send messages and mcp_chat_read to fetch history.`;
527
+ let responseText = `Connected to #${result.channelName} as ${result.userName} (${sessionLabel}). Your session is named "${sessionLabel}" -- this name appears on every message you send. Use mcp_chat_set_name to change it. Live messages will now be pushed into this session. You can also use mcp_chat_send to send messages and mcp_chat_read to fetch history.`;
528
+ if (sessionState.sessionInstructions) {
529
+ responseText += `\n\nChannel instructions for #${result.channelName} (apply these while in this channel):\n${sessionState.sessionInstructions}`;
530
+ }
456
531
  if (updateNotice) {
457
532
  responseText += `\n\n${updateNotice}`;
458
533
  }
@@ -488,10 +563,11 @@ async function handleToolCall(name, args) {
488
563
  channelName: channel.name,
489
564
  sessionToken,
490
565
  sessionLabel: null,
566
+ sessionInstructions: null,
491
567
  connected: true,
492
568
  };
493
569
 
494
- // Register session to get label (custom or sequential)
570
+ // Register session to get label (custom or sequential) + channel instructions
495
571
  let sessionLabel = args.label || 'Session';
496
572
  try {
497
573
  const regResult = await apiCall('register_session', {
@@ -501,10 +577,15 @@ async function handleToolCall(name, args) {
501
577
  }, sessionState.token);
502
578
  sessionLabel = regResult.label || sessionLabel;
503
579
  sessionState.sessionLabel = sessionLabel;
580
+ sessionState.sessionInstructions = regResult.instructions || null;
504
581
  } catch {}
505
582
 
506
583
  connectWebSocket();
507
- return { content: [{ type: 'text', text: `Joined #${channel.name} (ID: ${channelId}) as ${sessionState.userName} (${sessionLabel}). Live messages are now being pushed.` }] };
584
+ let joinText = `Joined #${channel.name} (ID: ${channelId}) as ${sessionState.userName} (${sessionLabel}). Your session is named "${sessionLabel}"; use mcp_chat_set_name to change it. Live messages are now being pushed.`;
585
+ if (sessionState.sessionInstructions) {
586
+ joinText += `\n\nChannel instructions for #${channel.name} (apply these while in this channel):\n${sessionState.sessionInstructions}`;
587
+ }
588
+ return { content: [{ type: 'text', text: joinText }] };
508
589
  } catch (err) {
509
590
  return { content: [{ type: 'text', text: `Failed to join channel: ${err.message}` }], isError: true };
510
591
  }
@@ -540,9 +621,12 @@ async function handleToolCall(name, args) {
540
621
  if (!result.messages || result.messages.length === 0) {
541
622
  return { content: [{ type: 'text', text: `No messages in #${sessionState.channelName}` }] };
542
623
  }
543
- const formatted = result.messages.map(m =>
544
- `[${new Date(m.created_at).toLocaleTimeString()}] ${m.user_name}: ${m.content}`
545
- ).join('\n');
624
+ const formatted = result.messages.map(m => {
625
+ const sender = m.session_id
626
+ ? `${m.user_name?.split(' ')[0]}'s Claude${m.session_label ? ` (${m.session_label})` : ''}`
627
+ : m.user_name;
628
+ return `[${new Date(m.created_at).toLocaleTimeString()}] ${sender}: ${m.content}`;
629
+ }).join('\n');
546
630
  return { content: [{ type: 'text', text: `Messages in #${sessionState.channelName}:\n${formatted}` }] };
547
631
  }
548
632
 
@@ -579,7 +663,11 @@ async function handleToolCall(name, args) {
579
663
  return { content: [{ type: 'text', text: sessionState.token ? 'Authenticated but not connected to a channel. Run mcp_chat_connect or mcp_chat_join to pick a channel.' : 'Not connected. Run mcp_chat_connect to authenticate and select a channel.' }] };
580
664
  }
581
665
  const wsStatus = wsConnection?.readyState === 1 ? 'live (receiving messages)' : 'reconnecting...';
582
- return { content: [{ type: 'text', text: `Connected to #${sessionState.channelName} as ${sessionState.userName} (${sessionState.sessionLabel || 'Session'})\nWebSocket: ${wsStatus}` }] };
666
+ let statusText = `Connected to #${sessionState.channelName} as ${sessionState.userName} (${sessionState.sessionLabel || 'Session'})\nWebSocket: ${wsStatus}`;
667
+ if (sessionState.sessionInstructions) {
668
+ statusText += `\n\nChannel instructions:\n${sessionState.sessionInstructions}`;
669
+ }
670
+ return { content: [{ type: 'text', text: statusText }] };
583
671
  }
584
672
 
585
673
  case 'mcp_chat_create_channel': {
@@ -628,6 +716,50 @@ async function handleToolCall(name, args) {
628
716
  return { content: [{ type: 'text', text: `Channel updated: #${result.channel.name}${result.channel.description ? ` -- ${result.channel.description}` : ''}` }] };
629
717
  }
630
718
 
719
+ case 'mcp_chat_set_name': {
720
+ if (!sessionState.connected) {
721
+ return { content: [{ type: 'text', text: 'Not connected. Run mcp_chat_connect first.' }], isError: true };
722
+ }
723
+ const newName = String(args.name || '').trim().slice(0, 100);
724
+ if (!newName) return { content: [{ type: 'text', text: 'A name is required.' }], isError: true };
725
+ const result = await apiCall('rename_session', {
726
+ session_token: sessionState.sessionToken,
727
+ label: newName,
728
+ }, sessionState.token);
729
+ if (result.error) return { content: [{ type: 'text', text: `Error: ${result.error}` }], isError: true };
730
+ sessionState.sessionLabel = result.label || newName;
731
+ return { content: [{ type: 'text', text: `Your session is now named "${sessionState.sessionLabel}" in #${sessionState.channelName}. This name appears on every message you send.` }] };
732
+ }
733
+
734
+ case 'mcp_chat_instructions': {
735
+ if (!sessionState.connected) {
736
+ return { content: [{ type: 'text', text: 'Not connected. Run mcp_chat_connect first.' }], isError: true };
737
+ }
738
+ if (!sessionState.sessionInstructions) {
739
+ return { content: [{ type: 'text', text: `No instructions are set for #${sessionState.channelName}. Set them with mcp_chat_set_instructions.` }] };
740
+ }
741
+ return { content: [{ type: 'text', text: `Channel instructions for #${sessionState.channelName}:\n${sessionState.sessionInstructions}` }] };
742
+ }
743
+
744
+ case 'mcp_chat_set_instructions': {
745
+ if (!sessionState.connected) {
746
+ return { content: [{ type: 'text', text: 'Not connected. Run mcp_chat_connect first.' }], isError: true };
747
+ }
748
+ if (typeof args.instructions !== 'string') {
749
+ return { content: [{ type: 'text', text: 'instructions (string) is required. Pass an empty string to clear.' }], isError: true };
750
+ }
751
+ const instructions = args.instructions.slice(0, 10000);
752
+ const result = await apiCall('set_channel_instructions', {
753
+ channel_id: sessionState.channelId,
754
+ instructions,
755
+ }, sessionState.token);
756
+ if (result.error) return { content: [{ type: 'text', text: `Error: ${result.error}` }], isError: true };
757
+ sessionState.sessionInstructions = result.instructions || null;
758
+ return { content: [{ type: 'text', text: result.instructions
759
+ ? `Channel instructions for #${sessionState.channelName} updated. All connected sessions will see them.`
760
+ : `Channel instructions for #${sessionState.channelName} cleared.` }] };
761
+ }
762
+
631
763
  default:
632
764
  return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
633
765
  }
@@ -646,7 +778,7 @@ async function handleMessage(msg) {
646
778
  tools: {},
647
779
  experimental: { 'claude/channel': {} },
648
780
  },
649
- serverInfo: { name: 'mcp-chat-connect', version: '1.1.0' },
781
+ serverInfo: { name: 'mcp-chat-connect', version: LOCAL_VERSION },
650
782
  });
651
783
  break;
652
784
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-chat-connect",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "MCP server with channels support for connecting Claude Code sessions to MCP Chat -- real-time team messaging for AI-assisted development",
5
5
  "main": "index.js",
6
6
  "bin": {