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.
- package/index.js +149 -17
- 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
|
|
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
|
|
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: {
|
|
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
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
|
|
545
|
-
|
|
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
|
-
|
|
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:
|
|
781
|
+
serverInfo: { name: 'mcp-chat-connect', version: LOCAL_VERSION },
|
|
650
782
|
});
|
|
651
783
|
break;
|
|
652
784
|
|
package/package.json
CHANGED