Skip to main content
Glama

xcresult_export_attachment

Export specific attachments from Xcode test results files, converting App UI hierarchies to JSON format for analysis.

Instructions

Export a specific attachment by index - can convert App UI hierarchy attachments to JSON

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
xcresult_pathYesAbsolute path to the .xcresult file
test_idYesTest ID or index number that contains the attachment
attachment_indexYesIndex number of the attachment to export (1-based, from xcresult-list-attachments)
convert_to_jsonNoIf true and attachment is an App UI hierarchy, convert to JSON format

Implementation Reference

  • Main handler implementation: Exports specific test attachments from XCResult files using xcresulttool. Handles validation, attachment extraction, type detection, special UI hierarchy processing, and JSON conversion.
    /** * Export a specific attachment by index */ public static async xcresultExportAttachment( xcresultPath: string, testId: string, attachmentIndex: number, convertToJson: boolean = false ): Promise<McpResult> { // Validate xcresult path if (!existsSync(xcresultPath)) { throw new McpError( ErrorCode.InvalidParams, `XCResult file not found: ${xcresultPath}` ); } if (!xcresultPath.endsWith('.xcresult')) { throw new McpError( ErrorCode.InvalidParams, `Path must be an .xcresult file: ${xcresultPath}` ); } // Check if xcresult is readable if (!XCResultParser.isXCResultReadable(xcresultPath)) { throw new McpError( ErrorCode.InternalError, `XCResult file is not readable or incomplete: ${xcresultPath}` ); } if (!testId || testId.trim() === '') { throw new McpError( ErrorCode.InvalidParams, 'Test ID or index is required' ); } if (attachmentIndex < 1) { throw new McpError( ErrorCode.InvalidParams, 'Attachment index must be 1 or greater' ); } try { const parser = new XCResultParser(xcresultPath); // First find the test node to get the actual test identifier const testNode = await parser.findTestNode(testId); if (!testNode) { throw new McpError( ErrorCode.InvalidParams, `Test '${testId}' not found. Run xcresult_browse "${xcresultPath}" to see all available tests` ); } if (!testNode.nodeIdentifier) { throw new McpError( ErrorCode.InvalidParams, `Test '${testId}' does not have a valid identifier for attachment retrieval` ); } // Get test attachments const attachments = await parser.getTestAttachments(testNode.nodeIdentifier); if (attachments.length === 0) { throw new McpError( ErrorCode.InvalidParams, `No attachments found for test '${testNode.name}'.` ); } if (attachmentIndex > attachments.length) { throw new McpError( ErrorCode.InvalidParams, `Invalid attachment index ${attachmentIndex}. Test has ${attachments.length} attachments.` ); } const attachment = attachments[attachmentIndex - 1]; if (!attachment) { throw new McpError( ErrorCode.InternalError, `Attachment at index ${attachmentIndex} not found` ); } const attachmentId = attachment.payloadId || attachment.payload_uuid || attachment.payloadUUID; if (!attachmentId) { throw new McpError( ErrorCode.InternalError, 'Attachment does not have a valid ID for export' ); } const filename = attachment.filename || attachment.name || `attachment_${attachmentIndex}`; // Determine type from identifier or filename first let type = attachment.uniform_type_identifier || attachment.uniformTypeIdentifier || ''; if (!type || type === 'unknown') { // Infer type from filename extension or special patterns const ext = filename.toLowerCase().split('.').pop(); if (ext === 'jpeg' || ext === 'jpg') type = 'public.jpeg'; else if (ext === 'png') type = 'public.png'; else if (ext === 'mp4') type = 'public.mpeg-4'; else if (ext === 'mov') type = 'com.apple.quicktime-movie'; else if (ext === 'txt') type = 'public.plain-text'; else if (filename.toLowerCase().includes('app ui hierarchy')) type = 'ui-hierarchy'; else if (filename.toLowerCase().includes('ui snapshot')) type = 'ui-snapshot'; else if (filename.toLowerCase().includes('synthesized event')) type = 'synthesized-event'; else type = 'unknown'; } const exportedPath = await parser.exportAttachment(attachmentId, filename); // Handle UI hierarchy files specially if (type === 'ui-hierarchy') { if (convertToJson) { const hierarchyJson = await this.convertUIHierarchyToJSON(exportedPath); return { content: [{ type: 'text', text: JSON.stringify(hierarchyJson) }] }; } // Return the raw UI hierarchy content (it's already AI-friendly) const { readFile } = await import('fs/promises'); const hierarchyContent = await readFile(exportedPath, 'utf-8'); return { content: [{ type: 'text', text: `UI Hierarchy for: ${filename}\nType: ${type}\n\n${hierarchyContent}` }] }; } return { content: [{ type: 'text', text: `Attachment exported to: ${exportedPath}\nFilename: ${filename}\nType: ${type}` }] }; } catch (error) { if (error instanceof McpError) { throw error; } const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes('xcresulttool')) { throw new McpError( ErrorCode.InternalError, `XCResult parsing failed. Make sure Xcode Command Line Tools are installed: ${errorMessage}` ); } throw new McpError( ErrorCode.InternalError, `Failed to export attachment: ${errorMessage}` ); } }
  • Input schema definition for the xcresult_export_attachment tool, defining parameters and validation rules
    name: 'xcresult_export_attachment', description: 'Export a specific attachment by index - can convert App UI hierarchy attachments to JSON', inputSchema: { type: 'object', properties: { xcresult_path: { type: 'string', description: 'Absolute path to the .xcresult file', }, test_id: { type: 'string', description: 'Test ID or index number that contains the attachment', }, attachment_index: { type: 'number', description: 'Index number of the attachment to export (1-based, from xcresult-list-attachments)', }, convert_to_json: { type: 'boolean', description: 'If true and attachment is an App UI hierarchy, convert to JSON format', }, }, required: ['xcresult_path', 'test_id', 'attachment_index'], }, },
  • MCP server registration: Dispatches xcresult_export_attachment tool calls to XCResultTools.xcresultExportAttachment handler in CallToolRequestSchema
    case 'xcresult_export_attachment': if (!args.xcresult_path) { throw new McpError(ErrorCode.InvalidParams, `Missing required parameter: xcresult_path`); } if (!args.test_id) { throw new McpError(ErrorCode.InvalidParams, `Missing required parameter: test_id`); } if (args.attachment_index === undefined) { throw new McpError(ErrorCode.InvalidParams, `Missing required parameter: attachment_index`); } return await XCResultTools.xcresultExportAttachment( args.xcresult_path as string, args.test_id as string, args.attachment_index as number, args.convert_to_json as boolean | undefined );
  • Fallback schema definition used by MCP library getTools() when CLI is unavailable
    name: 'xcresult_export_attachment', description: 'Export a specific attachment by index - can convert App UI hierarchy attachments to JSON', inputSchema: { type: 'object', properties: { xcresult_path: { type: 'string', description: 'Absolute path to the .xcresult file', }, test_id: { type: 'string', description: 'Test ID or index number that contains the attachment', }, attachment_index: { type: 'number', description: 'Index number of the attachment to export (1-based, from xcresult-list-attachments)', }, convert_to_json: { type: 'boolean', description: 'If true and attachment is an App UI hierarchy, convert to JSON format', }, }, required: ['xcresult_path', 'test_id', 'attachment_index'], },

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/lapfelix/XcodeMCP'

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