Skip to main content
Glama
executeCode.ts6.8 kB
// Execute code in memory without saving to file // Supports Python, Node.js, and R import { z } from 'zod'; import { spawn } from 'child_process'; import { logAudit } from '../audit.js'; import { loadConfig } from '../config.js'; import os from 'os'; const platform = os.platform(); const config = loadConfig(); /** * Truncate output to prevent context stuffing. */ function truncateOutput(output: string, maxChars: number, mode: 'head' | 'tail' | 'both'): { text: string; truncated: boolean; originalLength: number } { if (output.length <= maxChars) { return { text: output, truncated: false, originalLength: output.length }; } const originalLength = output.length; let text: string; if (mode === 'head') { text = output.slice(0, maxChars); text += `\n\n⚠️ OUTPUT TRUNCATED: Showing first ${maxChars.toLocaleString()} of ${originalLength.toLocaleString()} characters.`; } else if (mode === 'tail') { text = output.slice(-maxChars); text = `⚠️ OUTPUT TRUNCATED: Showing last ${maxChars.toLocaleString()} of ${originalLength.toLocaleString()} characters.\n\n` + text; } else { const headSize = Math.floor(maxChars * 0.6); const tailSize = maxChars - headSize; const head = output.slice(0, headSize); const tail = output.slice(-tailSize); const omitted = originalLength - headSize - tailSize; text = head + `\n\n⚠️ OUTPUT TRUNCATED: Omitted ${omitted.toLocaleString()} characters (${Math.round(omitted/originalLength*100)}% of output).\n` + `📊 Total: ${originalLength.toLocaleString()} chars | Showing: first ${headSize.toLocaleString()} + last ${tailSize.toLocaleString()}\n\n` + tail; } return { text, truncated: true, originalLength }; } // Schema export const ExecuteCodeSchema = { language: z.enum(['python', 'node', 'r', 'powershell', 'bash']).describe('Programming language to execute'), code: z.string().describe('Code to execute'), timeout: z.number().optional().describe('Timeout in milliseconds (default: 30000)'), }; interface ExecuteResult { success: boolean; stdout: string; stderr: string; exitCode: number | null; duration: number; } /** * Get the interpreter command for each language */ function getInterpreter(language: string): { command: string; args: string[] } { switch (language) { case 'python': return { command: platform === 'win32' ? 'python' : 'python3', args: ['-c'] }; case 'node': return { command: 'node', args: ['-e'] }; case 'r': return { command: 'Rscript', args: ['-e'] }; case 'powershell': return { command: 'powershell', args: ['-Command'] }; case 'bash': return { command: platform === 'win32' ? 'bash' : '/bin/bash', args: ['-c'] }; default: throw new Error(`Unsupported language: ${language}`); } } /** * Execute code in memory */ async function executeCode( language: string, code: string, timeout: number ): Promise<ExecuteResult> { const startTime = Date.now(); const { command, args } = getInterpreter(language); return new Promise((resolve) => { const proc = spawn(command, [...args, code], { timeout, shell: false, }); let stdout = ''; let stderr = ''; proc.stdout?.on('data', (data: Buffer) => { stdout += data.toString(); }); proc.stderr?.on('data', (data: Buffer) => { stderr += data.toString(); }); proc.on('close', (exitCode) => { const duration = Date.now() - startTime; resolve({ success: exitCode === 0, stdout: stdout.trim(), stderr: stderr.trim(), exitCode, duration, }); }); proc.on('error', (err) => { const duration = Date.now() - startTime; resolve({ success: false, stdout: '', stderr: err.message, exitCode: null, duration, }); }); }); } /** * Handle execute_code tool call */ export async function handleExecuteCode(args: { language: 'python' | 'node' | 'r' | 'powershell' | 'bash'; code: string; timeout?: number; }): Promise<{ content: Array<{ type: string; text: string }>; isError?: boolean }> { const timeout = args.timeout || 30000; const outputConfig = config.cliOutput ?? { maxOutputChars: 50000, warnAtChars: 10000, truncateMode: 'both' as const }; try { const result = await executeCode(args.language, args.code, timeout); // Apply truncation to prevent context stuffing const stdoutResult = truncateOutput(result.stdout, outputConfig.maxOutputChars, outputConfig.truncateMode); const stderrResult = truncateOutput(result.stderr, Math.floor(outputConfig.maxOutputChars / 2), outputConfig.truncateMode); await logAudit('execute_code', { language: args.language, codeLength: args.code.length, timeout, }, { success: result.success, exitCode: result.exitCode, duration: result.duration, truncated: stdoutResult.truncated || stderrResult.truncated, }); return { content: [{ type: 'text', text: JSON.stringify({ language: args.language, success: result.success, exitCode: result.exitCode, duration_ms: result.duration, stdout: stdoutResult.text, stderr: stderrResult.text || undefined, truncated: stdoutResult.truncated || stderrResult.truncated ? { stdout: stdoutResult.truncated, stderr: stderrResult.truncated, originalStdoutLength: stdoutResult.originalLength, originalStderrLength: stderrResult.originalLength, } : undefined, }, null, 2) }], isError: !result.success, }; } catch (error: any) { await logAudit('execute_code', args, null, error.message); return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true, }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Mnehmos/mnehmos.ooda.mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server