parse_error_log
Parse and compress error logs by extracting essential information and filtering out node_modules frames to reduce token usage.
Instructions
Parses and compresses error logs / stack traces to extract only the essential error information. Filters out node_modules frames, keeps only your source code references. Use this instead of reading raw stderr output to save 90%+ tokens.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| logContent | Yes | The raw error log / stderr output to compress | |
| workspaceRoot | No | Absolute path of the workspace root, used to identify business code frames. | |
| maxFrames | No | Maximum number of stack frames to include. Default: 10. |
Implementation Reference
- src/tools/log_trimmer.ts:8-93 (handler)Core handler function `trimErrorLog` that parses and compresses error logs/stack traces. Extracts error types/messages, filters out node_modules frames, keeps only business code frames up to maxFrames, and returns a compressed summary with stats.
export function trimErrorLog( log: string, workspaceRoot?: string, maxFrames: number = 10 ): LogTrimResult { const lines = log.split("\n"); const originalLines = lines.length; // 1. 提取错误类型和消息 const errorPattern = /^(\w*Error|\w*Exception|FATAL|PANIC)[:\s]+(.*)$/; const errorMatches: Array<{ type: string; message: string; line: number }> = []; lines.forEach((line, i) => { const match = line.match(errorPattern); if (match) errorMatches.push({ type: match[1], message: match[2], line: i }); }); // 2. 提取堆栈帧,过滤噪音 const framePattern = /^\s+at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)$/; const alternativeFramePattern = /^\s+at\s+(.+?)$/; const nodeModulesPattern = /node_modules/; const workspacePattern = workspaceRoot ? new RegExp(workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) : /\/src\//; const businessFrames: string[] = []; let totalFrames = 0; for (const line of lines) { const match = line.match(framePattern); if (match) { totalFrames++; const [, func, file] = match; if (!nodeModulesPattern.test(file)) { businessFrames.push(line.trim()); } continue; } const altMatch = line.match(alternativeFramePattern); if (altMatch) { totalFrames++; businessFrames.push(line.trim()); } } // 3. 组装摘要 const parts: string[] = []; if (errorMatches.length > 0) { const primary = errorMatches[0]; parts.push(`[${primary.type}] ${primary.message}`); if (errorMatches.length > 1) { const secondary = errorMatches[1]; parts.push(`Caused by: [${secondary.type}] ${secondary.message}`); } } if (businessFrames.length > 0) { parts.push("\nRelevant stack frames:"); businessFrames.slice(0, maxFrames).forEach((f) => parts.push(` ${f}`)); } // 4. 添加尾部摘要 const omittedFrames = totalFrames - Math.min(businessFrames.length, maxFrames); if (omittedFrames > 0) { parts.push(` ... ${omittedFrames} frames omitted`); } // 如果没有提取到任何有用信息,返回截断的原始日志 if (parts.length === 0) { const maxLogLines = 50; const truncated = lines.slice(0, maxLogLines).join("\n"); return { summary: truncated + (lines.length > maxLogLines ? `\n... [${lines.length - maxLogLines} more lines omitted by ContextGC]` : ""), stats: { originalLines, summaryLines: Math.min(lines.length, maxLogLines) }, }; } const summary = parts.join("\n"); return { summary, stats: { originalLines, summaryLines: summary.split("\n").length, }, }; } - src/tools/registry.ts:153-177 (registration)Registration of the 'parse_error_log' tool on the MCP server using `server.tool()`. Defines the schema (logContent, workspaceRoot, maxFrames), description, and calls `trimErrorLog` as the handler.
// ───────────────────────────────────────────────────── // Tool 3: parse_error_log // ───────────────────────────────────────────────────── server.tool( "parse_error_log", "Parses and compresses error logs / stack traces to extract only the essential error information. Filters out node_modules frames, keeps only your source code references. Use this instead of reading raw stderr output to save 90%+ tokens.", { logContent: z.string().describe("The raw error log / stderr output to compress"), workspaceRoot: z.string().optional().describe("Absolute path of the workspace root, used to identify business code frames."), maxFrames: z.number().optional().describe("Maximum number of stack frames to include. Default: 10."), }, async (args): Promise<ToolResult> => { const result = trimErrorLog( args.logContent, args.workspaceRoot, args.maxFrames ?? config.logTrimmer.maxFrames ); return { content: [{ type: "text", text: result.summary + `\n// [ContextGC] ${result.stats.originalLines} → ${result.stats.summaryLines} lines`, }], }; } ); - src/tools/registry.ts:159-163 (schema)Zod schema for the tool's input parameters: logContent (required string), workspaceRoot (optional string), maxFrames (optional number).
{ logContent: z.string().describe("The raw error log / stderr output to compress"), workspaceRoot: z.string().optional().describe("Absolute path of the workspace root, used to identify business code frames."), maxFrames: z.number().optional().describe("Maximum number of stack frames to include. Default: 10."), }, - src/config/schema.ts:20-23 (helper)Type definition for `logTrimmer` config, including maxFrames and filterPatterns.
logTrimmer: { maxFrames: number; filterPatterns: string[]; }; - src/config/schema.ts:45-48 (helper)Default configuration values for logTrimmer: maxFrames=10, filterPatterns=['node_modules', '.next', '.cache', 'dist', 'build'].
logTrimmer: { maxFrames: 10, filterPatterns: ["node_modules", ".next", ".cache", "dist", "build"], },