Skip to main content
Glama

Git MCP Server

git-branch.tool.ts7.37 kB
/** * @fileoverview Git branch tool - manage branches * @module mcp-server/tools/definitions/git-branch */ import { z } from 'zod'; import { withToolAuth } from '@/mcp-server/transports/auth/lib/withAuth.js'; import { AllSchema, BranchNameSchema, CommitRefSchema, ForceSchema, PathSchema, } from '../schemas/common.js'; import type { ToolDefinition } from '../utils/toolDefinition.js'; import { createToolHandler, type ToolLogicDependencies, } from '../utils/toolHandlerFactory.js'; import { createJsonFormatter, type VerbosityLevel, } from '../utils/json-response-formatter.js'; const TOOL_NAME = 'git_branch'; const TOOL_TITLE = 'Git Branch'; const TOOL_DESCRIPTION = 'Manage branches: list all branches, show current branch, create a new branch, delete a branch, or rename a branch.'; const InputSchema = z.object({ path: PathSchema, operation: z .enum(['list', 'create', 'delete', 'rename', 'show-current']) .default('list') .describe('The branch operation to perform.'), name: BranchNameSchema.optional().describe( 'Branch name for create/delete/rename operations.', ), newName: BranchNameSchema.optional().describe( 'New branch name for rename operation.', ), startPoint: CommitRefSchema.optional().describe( 'Starting point (commit/branch) for new branch creation.', ), force: ForceSchema, all: AllSchema.describe( 'For list operation: show both local and remote branches.', ), remote: z .boolean() .default(false) .describe('For list operation: show only remote branches.'), merged: z .union([z.boolean(), CommitRefSchema]) .optional() .describe( 'For list operation: show only branches merged into HEAD (true) or specified commit (string).', ), noMerged: z .union([z.boolean(), CommitRefSchema]) .optional() .describe( 'For list operation: show only branches not merged into HEAD (true) or specified commit (string).', ), }); const BranchInfoSchema = z.object({ name: z.string().describe('Branch name.'), current: z.boolean().describe('True if this is the current branch.'), commitHash: z.string().describe('Commit hash the branch points to.'), upstream: z .string() .optional() .describe('Upstream branch name if configured.'), ahead: z.number().int().optional().describe('Commits ahead of upstream.'), behind: z.number().int().optional().describe('Commits behind upstream.'), }); const OutputSchema = z.object({ success: z.boolean().describe('Indicates if the operation was successful.'), operation: z.enum(['list', 'create', 'delete', 'rename', 'show-current']), branches: z .array(BranchInfoSchema) .optional() .describe('List of branches (for list operation).'), currentBranch: z.string().optional().describe('Name of current branch.'), message: z .string() .optional() .describe('Success message for create/delete/rename operations.'), }); type ToolInput = z.infer<typeof InputSchema>; type ToolOutput = z.infer<typeof OutputSchema>; async function gitBranchLogic( input: ToolInput, { provider, targetPath, appContext }: ToolLogicDependencies, ): Promise<ToolOutput> { // Handle show-current operation separately (lightweight, no need for full branch call) if (input.operation === 'show-current') { const result = await provider.branch( { mode: 'list' }, { workingDirectory: targetPath, requestContext: appContext, tenantId: appContext.tenantId || 'default-tenant', }, ); if (result.mode === 'list') { const current = result.branches.find((b) => b.current); return { success: true, operation: 'show-current', branches: undefined, currentBranch: current?.name, message: current ? `Current branch: ${current.name}` : 'Not on any branch (detached HEAD)', }; } } // Build options object with only defined properties const { path: _path, operation, name, newName, ...rest } = input; const branchOptions: { mode: 'list' | 'create' | 'delete' | 'rename'; branchName?: string; newBranchName?: string; startPoint?: string; force?: boolean; remote?: boolean; merged?: boolean | string; noMerged?: boolean | string; } = { mode: operation as 'list' | 'create' | 'delete' | 'rename', }; if (name !== undefined) { branchOptions.branchName = name; } if (newName !== undefined) { branchOptions.newBranchName = newName; } if (rest.startPoint !== undefined) { branchOptions.startPoint = rest.startPoint; } if (rest.force !== undefined) { branchOptions.force = rest.force; } if (rest.all !== undefined || rest.remote !== undefined) { branchOptions.remote = rest.remote || rest.all; } if (rest.merged !== undefined) { branchOptions.merged = rest.merged; } if (rest.noMerged !== undefined) { branchOptions.noMerged = rest.noMerged; } const result = await provider.branch(branchOptions, { workingDirectory: targetPath, requestContext: appContext, tenantId: appContext.tenantId || 'default-tenant', }); // Handle discriminated union result if (result.mode === 'list') { return { success: true, operation: 'list', branches: result.branches, currentBranch: result.branches.find((b) => b.current)?.name, message: undefined, }; } else if (result.mode === 'create') { return { success: true, operation: 'create', branches: undefined, currentBranch: undefined, message: `Branch '${result.created}' created successfully.`, }; } else if (result.mode === 'delete') { return { success: true, operation: 'delete', branches: undefined, currentBranch: undefined, message: `Branch '${result.deleted}' deleted successfully.`, }; } else { // rename return { success: true, operation: 'rename', branches: undefined, currentBranch: undefined, message: `Branch '${result.renamed.from}' renamed to '${result.renamed.to}'.`, }; } } /** * Filter git_branch output based on verbosity level. * * Verbosity levels: * - minimal: Success, operation, and current branch name only * - standard: Above + complete branches array (for list) or message (for other ops) (RECOMMENDED) * - full: Complete output */ function filterGitBranchOutput( result: ToolOutput, level: VerbosityLevel, ): Partial<ToolOutput> { // minimal: Essential info only if (level === 'minimal') { return { success: result.success, operation: result.operation, currentBranch: result.currentBranch, }; } // standard & full: Complete output // (LLMs need complete context - include all branches or full message) return result; } // Create JSON response formatter with verbosity filtering const responseFormatter = createJsonFormatter<ToolOutput>({ filter: filterGitBranchOutput, }); export const gitBranchTool: ToolDefinition< typeof InputSchema, typeof OutputSchema > = { name: TOOL_NAME, title: TOOL_TITLE, description: TOOL_DESCRIPTION, inputSchema: InputSchema, outputSchema: OutputSchema, annotations: { readOnlyHint: false }, logic: withToolAuth(['tool:git:write'], createToolHandler(gitBranchLogic)), responseFormatter, };

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/cyanheads/git-mcp-server'

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