start_intensive_chat
Initiate a persistent chat session to collect a sequence of user inputs efficiently. Maintains context for multi-step processes, brainstorming, or project setups, returning a session ID for follow-up questions.
Instructions
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sessionTitle | Yes | Title for the intensive chat session |
Implementation Reference
- src/index.ts:180-228 (handler)Handler registration and execution logic for the 'start_intensive_chat' MCP tool. Extracts sessionTitle from args, calls startIntensiveChatSession, tracks the session, and returns success message with session ID or error.if (isToolEnabled('start_intensive_chat')) { // Use properties from the imported intensiveChatTools object server.tool( 'start_intensive_chat', // Description is a function here typeof intensiveChatTools.start.description === 'function' ? intensiveChatTools.start.description(globalTimeoutSeconds) : intensiveChatTools.start.description, intensiveChatTools.start.schema, // Use schema property async (args) => { // Use inferred args type const { sessionTitle } = args; try { // Start a new intensive chat session, passing global timeout const sessionId = await startIntensiveChatSession( sessionTitle, globalTimeoutSeconds, ); // Track this session for the client activeChatSessions.set(sessionId, sessionTitle); return { content: [ { type: 'text', text: `Intensive chat session started successfully. Session ID: ${sessionId}`, }, ], }; } catch (error: unknown) { let errorMessage = 'Failed to start intensive chat session.'; if (error instanceof Error) { errorMessage = `Failed to start intensive chat session: ${error.message}`; } else if (typeof error === 'string') { errorMessage = `Failed to start intensive chat session: ${error}`; } return { content: [ { type: 'text', text: errorMessage, }, ], }; } }, ); }
- Core helper function implementing the intensive chat session startup: creates temp directory, generates session ID, spawns platform-specific child process for the UI script, stores session info in activeSessions map./** * Start an intensive chat session * @param title Title for the chat session * @param timeoutSeconds Optional timeout for each question in seconds * @returns Session ID for the created session */ export async function startIntensiveChatSession( title: string, timeoutSeconds?: number, ): Promise<string> { // Create a session directory const sessionDir = await createSessionDir(); // Generate a unique session ID const sessionId = path.basename(sessionDir).replace('intensive-chat-', ''); // Path to the UI script - Updated to use the compiled 'ui.js' filename const uiScriptPath = path.join(__dirname, 'ui.js'); // Create options payload for the UI const options = { sessionId, title, outputDir: sessionDir, timeoutSeconds, }; // Encode options as base64 payload const payload = Buffer.from(JSON.stringify(options)).toString('base64'); // Platform-specific spawning const platform = os.platform(); let childProcess: ChildProcess; if (platform === 'darwin') { // macOS // Escape potential special characters in paths/payload for the shell command // For the shell command executed by 'do script', we primarily need to handle spaces // or other characters that might break the command if paths aren't quoted. // The `${...}` interpolation within backticks handles basic variable insertion. // Quoting the paths within nodeCommand handles spaces. const escapedScriptPath = uiScriptPath; // Keep original path, rely on quotes below const escapedPayload = payload; // Keep original payload, rely on quotes below // Construct the command string directly for the shell. Quotes handle paths with spaces. const nodeBin = process.execPath; const nodeCommand = `exec "${nodeBin}" "${escapedScriptPath}" "${escapedPayload}"; exit 0`; // Escape the node command for osascript's AppleScript string: // 1. Escape existing backslashes (\ -> \\) // 2. Escape double quotes (" -> \") const escapedNodeCommand = nodeCommand // Escape backslashes first .replace(/\\/g, '\\\\') // Using /\\/g instead of /\/g // Then escape double quotes .replace(/"/g, '\\"'); // Activate Terminal first, then do script with exec const command = `osascript -e 'tell application "Terminal" to activate' -e 'tell application "Terminal" to do script "${escapedNodeCommand}"'`; const commandArgs: string[] = []; // No args needed when command is a single string for shell // Fallback launcher using .command + open -a Terminal const launchViaOpenCommand = async () => { try { const launcherPath = path.join( sessionDir, `interactive-mcp-intchat-${sessionId}.command`, ); const scriptContent = `#!/bin/bash\nexec "${process.execPath}" "${escapedScriptPath}" "${escapedPayload}"\n`; await fs.writeFile(launcherPath, scriptContent, 'utf8'); await fs.chmod(launcherPath, 0o755); const openProc = spawn('open', ['-a', 'Terminal', launcherPath], { stdio: ['ignore', 'ignore', 'ignore'], detached: true, }); openProc.unref(); } catch (e) { logger.error( { error: e }, 'Fallback open -a Terminal failed (intensive chat)', ); } }; childProcess = spawn(command, commandArgs, { stdio: ['ignore', 'ignore', 'ignore'], shell: true, detached: true, }); childProcess.on('error', () => { void launchViaOpenCommand(); }); childProcess.on('close', (code: number | null) => { if (code !== null && code !== 0) { void launchViaOpenCommand(); } }); } else if (platform === 'win32') { // Windows childProcess = spawn(process.execPath, [uiScriptPath, payload], { stdio: ['ignore', 'ignore', 'ignore'], shell: true, detached: true, windowsHide: false, }); } else { // Linux or other - use original method (might not pop up window) childProcess = spawn(process.execPath, [uiScriptPath, payload], { stdio: ['ignore', 'ignore', 'ignore'], shell: true, detached: true, }); } // Unref the process so it can run independently childProcess.unref(); // Store session info activeSessions[sessionId] = { id: sessionId, process: childProcess, // Use the conditionally spawned process outputDir: sessionDir, lastHeartbeatTime: Date.now(), isActive: true, title, timeoutSeconds, }; // Wait a bit to ensure the UI has started await new Promise((resolve) => setTimeout(resolve, 500)); return sessionId; }
- Zod schema definition for 'start_intensive_chat' tool input validation (sessionTitle) and the complete ToolDefinition object used for registration.const startSchema: ZodRawShape = { sessionTitle: z.string().describe('Title for the intensive chat session'), }; const startToolDefinition: ToolDefinition = { capability: startCapability, description: startDescription, schema: startSchema, };
- src/index.ts:28-34 (registration)Registers the 'start_intensive_chat' tool capability in the allToolCapabilities object, which is later used in MCP server capabilities declaration.const allToolCapabilities = { request_user_input: requestUserInputTool.capability, message_complete_notification: messageCompleteNotificationTool.capability, start_intensive_chat: intensiveChatTools.start.capability, ask_intensive_chat: intensiveChatTools.ask.capability, stop_intensive_chat: intensiveChatTools.stop.capability, } satisfies ToolCapabilitiesStructure;
- JSON Schema (OpenAPI style) in tool capability for 'start_intensive_chat' input parameters, advertised to MCP clients.const startCapability: ToolCapabilityInfo = { description: 'Start an intensive chat session for gathering multiple answers quickly.', parameters: { type: 'object', properties: { sessionTitle: { type: 'string', description: 'Title for the intensive chat session', }, }, required: ['sessionTitle'], }, };