( result: unknown, tool: string, // Tool name for validation (e.g., "search") name: string, // Server name for IDE check and transformation (e.g., "slack") )
| 2718 | } |
| 2719 | |
| 2720 | export async function processMCPResult( |
| 2721 | result: unknown, |
| 2722 | tool: string, // Tool name for validation (e.g., "search") |
| 2723 | name: string, // Server name for IDE check and transformation (e.g., "slack") |
| 2724 | ): Promise<MCPToolResult> { |
| 2725 | const { content, type, schema } = await transformMCPResult(result, tool, name) |
| 2726 | |
| 2727 | // IDE tools are not going to the model directly, so we don't need to |
| 2728 | // handle large output. |
| 2729 | if (name === 'ide') { |
| 2730 | return content |
| 2731 | } |
| 2732 | |
| 2733 | // Check if content needs truncation (i.e., is too large) |
| 2734 | if (!(await mcpContentNeedsTruncation(content))) { |
| 2735 | return content |
| 2736 | } |
| 2737 | |
| 2738 | const sizeEstimateTokens = getContentSizeEstimate(content) |
| 2739 | |
| 2740 | // If large output files feature is disabled, fall back to old truncation behavior |
| 2741 | if (isEnvDefinedFalsy(process.env.ENABLE_MCP_LARGE_OUTPUT_FILES)) { |
| 2742 | logEvent('tengu_mcp_large_result_handled', { |
| 2743 | outcome: 'truncated', |
| 2744 | reason: 'env_disabled', |
| 2745 | sizeEstimateTokens, |
| 2746 | } as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS) |
| 2747 | return await truncateMcpContentIfNeeded(content) |
| 2748 | } |
| 2749 | |
| 2750 | // Save large output to file and return instructions for reading it |
| 2751 | // Content is guaranteed to exist at this point (we checked mcpContentNeedsTruncation) |
| 2752 | if (!content) { |
| 2753 | return content |
| 2754 | } |
| 2755 | |
| 2756 | // If content contains images, fall back to truncation - persisting images as JSON |
| 2757 | // defeats the image compression logic and makes them non-viewable |
| 2758 | if (contentContainsImages(content)) { |
| 2759 | logEvent('tengu_mcp_large_result_handled', { |
| 2760 | outcome: 'truncated', |
| 2761 | reason: 'contains_images', |
| 2762 | sizeEstimateTokens, |
| 2763 | } as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS) |
| 2764 | return await truncateMcpContentIfNeeded(content) |
| 2765 | } |
| 2766 | |
| 2767 | // Generate a unique ID for the persisted file (server__tool-timestamp) |
| 2768 | const timestamp = Date.now() |
| 2769 | const persistId = `mcp-${normalizeNameForMCP(name)}-${normalizeNameForMCP(tool)}-${timestamp}` |
| 2770 | // Convert to string for persistence (persistToolResult expects string or specific block types) |
| 2771 | const contentStr = |
| 2772 | typeof content === 'string' ? content : jsonStringify(content, null, 2) |
| 2773 | const persistResult = await persistToolResult(contentStr, persistId) |
| 2774 | |
| 2775 | if (isPersistError(persistResult)) { |
| 2776 | // If file save failed, fall back to returning truncated content info |
| 2777 | const contentLength = contentStr.length |
no test coverage detected