Skip to main content
Glama

list_conflicts

Identify files with Git merge conflicts, display conflict types, and provide resolution suggestions to streamline conflict resolution workflows.

Instructions

List files with git conflicts including conflict types. Returns a map of ID to file info with conflict type and suggested resolution. (Rate limit: 2 calls per minute). IMPORTANT: You must run init_project before using this tool.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pageNoPage number (1-based). Default is 1.
extensionNoFilter by file extension (e.g. 'ts', '.ts').
pathNoFilter by file path (substring match).
showTypesNoShow detailed conflict types and resolution suggestions. Default is true.

Implementation Reference

  • The core handler function for the 'list_conflicts' tool. It handles rate limiting, fetches conflicted files, applies pagination and filters, generates IDs and suggestions, excludes pending resolves, and returns a JSON map of ID to file info.
    async ({ page, extension, path, showTypes }) => { if (!rateLimiter.check("list_conflicts", 2, 60 * 1000)) { return { content: [{ type: "text", text: "Please fix the conflicted files that have already been provided." }], isError: true }; } const pageNum = page || 1; const pageSize = 20; const includeTypes = showTypes !== false; // Default to true try { const allConflicts = await getConflictedFilesWithStatus(); // Assign consistent IDs based on file path const conflictsWithIds = allConflicts.map((conflict) => ({ ...conflict, id: generateId(conflict.file) })); // Fetch pending resolves (local or external) const pendingResolves = await getPendingResolves(); const pendingPaths = new Set(pendingResolves.map(p => p.filePath)); // Filter out conflicts that are already in the pending list let filteredConflicts = conflictsWithIds.filter(c => !pendingPaths.has(c.file)); // Apply filters if (extension) { const ext = extension.startsWith('.') ? extension : `.${extension}`; filteredConflicts = filteredConflicts.filter(c => c.file.endsWith(ext)); } if (path) { filteredConflicts = filteredConflicts.filter(c => c.file.includes(path)); } const start = (pageNum - 1) * pageSize; const end = start + pageSize; const slice = filteredConflicts.slice(start, end); const result: Record<string, any> = {}; if (includeTypes) { // Include detailed information with conflict types slice.forEach((item) => { const suggestion = getResolutionSuggestion(item.status); const rejectionReason = state.getRejection(item.file); result[item.id] = { file: item.file, conflictType: item.conflictType, fileSize: item.fileSize !== undefined ? `${item.fileSize} bytes` : "N/A (file deleted)", suggestion: suggestion, ...(rejectionReason ? { previousRejectionReason: rejectionReason } : {}) }; }); } else { // Simple format: just ID to file path slice.forEach((item) => { result[item.id] = item.file; }); } if (filteredConflicts.length > end) { result["isMoreConflict"] = "true"; } return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (e: any) { return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true }; } }
  • Key helper function that retrieves detailed conflict information (status, type, size) by running 'git status --porcelain' and parsing the output. Used by the handler.
    export async function getConflictedFilesWithStatus(): Promise<ConflictInfo[]> { try { const output = await runGit(["status", "--porcelain"]); const lines = output.split("\n").map(s => s.trim()).filter(s => s.length > 0); const conflicts: ConflictInfo[] = []; const projectPath = state.getProjectPath(); if (!projectPath) { throw new Error("Project not initialized. Call init_project first."); } for (const line of lines) { // Format: XY filename // X = index status, Y = working tree status const status = line.substring(0, 2); const file = line.substring(3); // Only include unmerged files (conflicts) if (["DD", "AU", "UD", "UA", "DU", "AA", "UU"].includes(status)) { let conflictType: string; switch (status) { case "DD": conflictType = "both deleted"; break; case "AU": conflictType = "added by us"; break; case "UD": conflictType = "deleted by them"; break; case "UA": conflictType = "added by them"; break; case "DU": conflictType = "deleted by us"; break; case "AA": conflictType = "both added"; break; case "UU": conflictType = "both modified"; break; default: conflictType = "unknown"; } // Get file size if file exists let fileSize: number | undefined; try { const filePath = join(projectPath, file); const stats = await stat(filePath); fileSize = stats.size; } catch (e) { // File doesn't exist (deleted), fileSize remains undefined fileSize = undefined; } conflicts.push({ file, status, conflictType, fileSize }); } } return conflicts.sort((a, b) => a.file.localeCompare(b.file)); } catch (e) { throw e; } }
  • Zod input schema defining optional parameters for pagination, filtering by extension/path, and showing types/suggestions.
    inputSchema: z.object({ page: z.number().optional().describe("Page number (1-based). Default is 1."), extension: z.string().optional().describe("Filter by file extension (e.g. 'ts', '.ts')."), path: z.string().optional().describe("Filter by file path (substring match)."), showTypes: z.boolean().optional().describe("Show detailed conflict types and resolution suggestions. Default is true."), }),
  • Function that registers the 'list_conflicts' tool with the MCP server, including name, description, schema, and handler.
    export function registerListConflicts(server: McpServer) { server.registerTool( "list_conflicts", { description: "List files with git conflicts including conflict types. Returns a map of ID to file info with conflict type and suggested resolution. (Rate limit: 2 calls per minute). IMPORTANT: You must run init_project before using this tool.", inputSchema: z.object({ page: z.number().optional().describe("Page number (1-based). Default is 1."), extension: z.string().optional().describe("Filter by file extension (e.g. 'ts', '.ts')."), path: z.string().optional().describe("Filter by file path (substring match)."), showTypes: z.boolean().optional().describe("Show detailed conflict types and resolution suggestions. Default is true."), }), }, async ({ page, extension, path, showTypes }) => { if (!rateLimiter.check("list_conflicts", 2, 60 * 1000)) { return { content: [{ type: "text", text: "Please fix the conflicted files that have already been provided." }], isError: true }; } const pageNum = page || 1; const pageSize = 20; const includeTypes = showTypes !== false; // Default to true try { const allConflicts = await getConflictedFilesWithStatus(); // Assign consistent IDs based on file path const conflictsWithIds = allConflicts.map((conflict) => ({ ...conflict, id: generateId(conflict.file) })); // Fetch pending resolves (local or external) const pendingResolves = await getPendingResolves(); const pendingPaths = new Set(pendingResolves.map(p => p.filePath)); // Filter out conflicts that are already in the pending list let filteredConflicts = conflictsWithIds.filter(c => !pendingPaths.has(c.file)); // Apply filters if (extension) { const ext = extension.startsWith('.') ? extension : `.${extension}`; filteredConflicts = filteredConflicts.filter(c => c.file.endsWith(ext)); } if (path) { filteredConflicts = filteredConflicts.filter(c => c.file.includes(path)); } const start = (pageNum - 1) * pageSize; const end = start + pageSize; const slice = filteredConflicts.slice(start, end); const result: Record<string, any> = {}; if (includeTypes) { // Include detailed information with conflict types slice.forEach((item) => { const suggestion = getResolutionSuggestion(item.status); const rejectionReason = state.getRejection(item.file); result[item.id] = { file: item.file, conflictType: item.conflictType, fileSize: item.fileSize !== undefined ? `${item.fileSize} bytes` : "N/A (file deleted)", suggestion: suggestion, ...(rejectionReason ? { previousRejectionReason: rejectionReason } : {}) }; }); } else { // Simple format: just ID to file path slice.forEach((item) => { result[item.id] = item.file; }); } if (filteredConflicts.length > end) { result["isMoreConflict"] = "true"; } return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (e: any) { return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true }; } } ); }
  • Invocation of registerListConflicts within the main registerTools function to include the tool in the server.
    registerListConflicts(server);

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/mattyatea/git-conflict-mcp'

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