UNPKG

@cyanheads/git-mcp-server

Version:

An MCP (Model Context Protocol) server enabling LLMs and AI agents to interact with Git repositories. Provides tools for comprehensive Git operations including clone, commit, branch, diff, log, status, push, pull, merge, rebase, worktree, tag management,

84 lines 3.3 kB
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; import { execFile } from "child_process"; import { promisify } from "util"; import { z } from "zod"; import { logger, sanitization } from "../../../utils/index.js"; import { getGitStatus, GitStatusOutputSchema } from "../gitStatus/logic.js"; const execFileAsync = promisify(execFile); export const GitAddInputSchema = z.object({ path: z .string() .min(1) .optional() .default(".") .describe("Path to the Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."), files: z .union([z.string().min(1), z.array(z.string().min(1))]) .default(".") .describe("Files or patterns to stage, defaults to all changes ('.')"), }); export const GitAddOutputSchema = z.object({ success: z .boolean() .describe("Indicates whether the operation was successful."), statusMessage: z .string() .describe("A message describing the result of the operation."), filesStaged: z .union([z.string(), z.array(z.string())]) .describe("The files or patterns that were staged."), status: GitStatusOutputSchema.optional().describe("The status of the repository after the add operation."), }); export async function addGitFiles(params, context) { const operation = "addGitFiles"; logger.debug(`Executing ${operation}`, { ...context, params }); const workingDir = context.getWorkingDirectory(); if (params.path === "." && !workingDir) { throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No session working directory set. Please specify a 'path' or use 'git_set_working_dir' first."); } const targetPath = sanitization.sanitizePath(params.path === "." ? workingDir : params.path, { allowAbsolute: true }).sanitizedPath; const filesToStage = Array.isArray(params.files) ? params.files : [params.files]; if (filesToStage.length === 0) { filesToStage.push("."); } const args = [ "-C", targetPath, "add", "--", ...filesToStage.map((file) => (file.startsWith("-") ? `./${file}` : file)), ]; logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation, }); const { stderr } = await execFileAsync("git", args); if (stderr) { logger.warning(`Git add command produced stderr`, { ...context, operation, stderr, }); } const filesAddedDesc = Array.isArray(filesToStage) ? filesToStage.join(", ") : filesToStage; const successMessage = `Successfully staged: ${filesAddedDesc}`; logger.info(successMessage, { ...context, operation, path: targetPath, files: filesToStage, }); const reminder = "Remember to write clear, concise commit messages using the Conventional Commits format (e.g., 'feat(scope): subject')."; const status = await getGitStatus({ path: targetPath }, context); return { success: true, statusMessage: `${successMessage}. ${reminder}`, filesStaged: filesToStage, status, }; } //# sourceMappingURL=logic.js.map