Skip to main content
Glama
mattyatea

Git Conflict MCP

by mattyatea

resolve_conflict

Request Git merge conflict resolution by ID or file path for human review through WebUI. Supports content, delete/modify conflicts with resolution types and safety checks.

Instructions

Request conflict resolution by its ID or file path. The actual resolution will be performed by a human through the WebUI. Supports different resolution types for various conflict scenarios (content conflicts, delete/modify conflicts, etc.). You must run post_resolve before running this tool.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
idNoThe ID of the file to resolve (from list_conflicts).
pathNoThe file path to resolve.
typeNoResolution type. Default is 'resolve'.resolve
reasonNoThe reason why this resolution is valid.
forceNoForce resolution, bypassing the safety check.

Implementation Reference

  • The handler function that implements the core logic of the 'resolve_conflict' tool. It identifies the conflicted file, adds a pending resolution to the WebUI, and returns a status message.
    async ({ id, path: filePath, type, reason, force }) => {
        try {
            const projectPath = state.getProjectPath();
            if (!projectPath) {
                return { content: [{ type: "text", text: "Project not initialized. Run init_project first." }], isError: true };
            }
    
            const files = await getConflictedFiles();
            let fileToResolve: string | undefined;
    
            if (id) {
                fileToResolve = files.find(f => generateId(f) === id);
                if (!fileToResolve) {
                    return { content: [{ type: "text", text: "Invalid ID." }], isError: true };
                }
            } else if (filePath) {
                let normalizedPath = filePath;
                if (path.isAbsolute(filePath)) {
                    normalizedPath = path.relative(projectPath, filePath);
                }
                if (files.includes(normalizedPath)) {
                    fileToResolve = normalizedPath;
                } else {
                    return { content: [{ type: "text", text: `File not found in conflicted files. Searched for: ${normalizedPath}` }], isError: true };
                }
            }
    
            if (!fileToResolve) {
                return { content: [{ type: "text", text: "Could not determine file to resolve. Please provide id or path." }], isError: true };
            }
    
            const absolutePath = path.join(projectPath, fileToResolve);
            const resolutionType = type || "resolve";
    
            const result = await addPendingResolve({
                filePath: fileToResolve,
                absolutePath,
                projectPath,
                type: resolutionType,
                reason,
            });
    
            if (!result.success) {
                return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
            }
    
            // Check for status if returned by addPendingResolve (need to cast until typed)
            const status = (result as any).status;
            let msg = `Request sent!\n\nFile: ${fileToResolve}`;
            if (status === 'draft') {
                msg += `\n\nWARNING: The resolution reason was too generic. This item has been marked as 'Draft' and will NOT appear in the review list until the reason is updated.`;
            }
    
            return {
                content: [{
                    type: "text",
                    text: msg
                }]
            };
    
        } catch (e: any) {
            return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
        }
    }
  • Zod input schema defining parameters for the 'resolve_conflict' tool: id, path, type, reason, force.
    inputSchema: z.object({
        id: z.string().optional().describe("The ID of the file to resolve (from list_conflicts)."),
        path: z.string().optional().describe("The file path to resolve."),
        type: z.enum(["resolve", "delete", "add"]).optional().default("resolve").describe("Resolution type. Default is 'resolve'."),
        reason: z.string().optional().describe("The reason why this resolution is valid."),
        force: z.boolean().optional().describe("Force resolution, bypassing the safety check."),
    }),
  • The registration function that sets up the 'resolve_conflict' tool on the MCP server, including name, description, schema, and handler.
    export function registerResolveConflict(server: McpServer) {
        server.registerTool(
            "resolve_conflict",
            {
                description: "Request conflict resolution by its ID or file path. The actual resolution will be performed by a human through the WebUI. Supports different resolution types for various conflict scenarios (content conflicts, delete/modify conflicts, etc.). You must run post_resolve before running this tool.",
                inputSchema: z.object({
                    id: z.string().optional().describe("The ID of the file to resolve (from list_conflicts)."),
                    path: z.string().optional().describe("The file path to resolve."),
                    type: z.enum(["resolve", "delete", "add"]).optional().default("resolve").describe("Resolution type. Default is 'resolve'."),
                    reason: z.string().optional().describe("The reason why this resolution is valid."),
                    force: z.boolean().optional().describe("Force resolution, bypassing the safety check."),
                }),
            },
            async ({ id, path: filePath, type, reason, force }) => {
                try {
                    const projectPath = state.getProjectPath();
                    if (!projectPath) {
                        return { content: [{ type: "text", text: "Project not initialized. Run init_project first." }], isError: true };
                    }
    
                    const files = await getConflictedFiles();
                    let fileToResolve: string | undefined;
    
                    if (id) {
                        fileToResolve = files.find(f => generateId(f) === id);
                        if (!fileToResolve) {
                            return { content: [{ type: "text", text: "Invalid ID." }], isError: true };
                        }
                    } else if (filePath) {
                        let normalizedPath = filePath;
                        if (path.isAbsolute(filePath)) {
                            normalizedPath = path.relative(projectPath, filePath);
                        }
                        if (files.includes(normalizedPath)) {
                            fileToResolve = normalizedPath;
                        } else {
                            return { content: [{ type: "text", text: `File not found in conflicted files. Searched for: ${normalizedPath}` }], isError: true };
                        }
                    }
    
                    if (!fileToResolve) {
                        return { content: [{ type: "text", text: "Could not determine file to resolve. Please provide id or path." }], isError: true };
                    }
    
                    const absolutePath = path.join(projectPath, fileToResolve);
                    const resolutionType = type || "resolve";
    
                    const result = await addPendingResolve({
                        filePath: fileToResolve,
                        absolutePath,
                        projectPath,
                        type: resolutionType,
                        reason,
                    });
    
                    if (!result.success) {
                        return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
                    }
    
                    // Check for status if returned by addPendingResolve (need to cast until typed)
                    const status = (result as any).status;
                    let msg = `Request sent!\n\nFile: ${fileToResolve}`;
                    if (status === 'draft') {
                        msg += `\n\nWARNING: The resolution reason was too generic. This item has been marked as 'Draft' and will NOT appear in the review list until the reason is updated.`;
                    }
    
                    return {
                        content: [{
                            type: "text",
                            text: msg
                        }]
                    };
    
                } catch (e: any) {
                    return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
                }
            }
        );
    }
  • Invocation of the registerResolveConflict function within the main tools registration module.
    registerResolveConflict(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