Skip to main content
Glama

Git MCP Server

git-worktree.tool.ts6.05 kB
/** * @fileoverview Git worktree tool - manage multiple working trees * @module mcp-server/tools/definitions/git-worktree */ import { z } from 'zod'; import type { ToolDefinition } from '../utils/toolDefinition.js'; import { withToolAuth } from '@/mcp-server/transports/auth/lib/withAuth.js'; import { PathSchema, BranchNameSchema, CommitRefSchema, } from '../schemas/common.js'; import { createToolHandler, type ToolLogicDependencies, } from '../utils/toolHandlerFactory.js'; import { createJsonFormatter, type VerbosityLevel, } from '../utils/json-response-formatter.js'; const TOOL_NAME = 'git_worktree'; const TOOL_TITLE = 'Git Worktree'; const TOOL_DESCRIPTION = 'Manage multiple working trees: list worktrees, add new worktrees for parallel work, remove worktrees, or move worktrees to new locations.'; const InputSchema = z.object({ path: PathSchema, mode: z .enum(['list', 'add', 'remove', 'move', 'prune']) .default('list') .describe('The worktree operation to perform.'), worktreePath: z .string() .optional() .describe('Path for the new worktree (for add/move operations).'), branch: BranchNameSchema.optional().describe( 'Branch to checkout in the new worktree (for add operation).', ), commitish: CommitRefSchema.optional().describe( 'Commit/branch to base the worktree on (for add operation).', ), force: z .boolean() .default(false) .describe( 'Force operation (for remove operation with uncommitted changes).', ), newPath: z .string() .optional() .describe('New path for the worktree (for move operation).'), detach: z .boolean() .default(false) .describe('Create worktree with detached HEAD (for add operation).'), verbose: z .boolean() .default(false) .describe('Provide detailed output for worktree operations.'), dryRun: z .boolean() .default(false) .describe( 'Preview the operation without executing it (for prune operation).', ), }); const WorktreeInfoSchema = z.object({ path: z.string().describe('Absolute path to the worktree.'), head: z.string().describe('HEAD commit hash in this worktree.'), branch: z .string() .optional() .describe('Branch checked out (if not detached).'), bare: z.boolean().describe('Whether worktree is bare.'), detached: z.boolean().describe('Whether HEAD is detached.'), locked: z.boolean().describe('Whether the worktree is locked.'), prunable: z.boolean().describe('Whether the worktree can be pruned.'), }); const OutputSchema = z.object({ success: z.boolean().describe('Indicates if the operation was successful.'), mode: z.string().describe('Operation mode that was performed.'), worktrees: z .array(WorktreeInfoSchema) .optional() .describe('List of worktrees (for list mode).'), added: z.string().optional().describe('Added worktree path (for add mode).'), removed: z .string() .optional() .describe('Removed worktree path (for remove mode).'), moved: z .object({ from: z.string(), to: z.string(), }) .optional() .describe('Move operation info (for move mode).'), pruned: z .array(z.string()) .optional() .describe('Pruned worktree paths (for prune mode).'), }); type ToolInput = z.infer<typeof InputSchema>; type ToolOutput = z.infer<typeof OutputSchema>; async function gitWorktreeLogic( input: ToolInput, { provider, targetPath, appContext }: ToolLogicDependencies, ): Promise<ToolOutput> { const worktreeOptions: { mode: 'list' | 'add' | 'remove' | 'move' | 'prune'; path?: string; branch?: string; commitish?: string; force?: boolean; newPath?: string; detach?: boolean; verbose?: boolean; dryRun?: boolean; } = { mode: input.mode, }; if (input.worktreePath !== undefined) { worktreeOptions.path = input.worktreePath; } if (input.branch !== undefined) { worktreeOptions.branch = input.branch; } if (input.commitish !== undefined) { worktreeOptions.commitish = input.commitish; } if (input.force !== undefined) { worktreeOptions.force = input.force; } if (input.newPath !== undefined) { worktreeOptions.newPath = input.newPath; } if (input.detach !== undefined) { worktreeOptions.detach = input.detach; } if (input.verbose !== undefined) { worktreeOptions.verbose = input.verbose; } if (input.dryRun !== undefined) { worktreeOptions.dryRun = input.dryRun; } const result = await provider.worktree(worktreeOptions, { workingDirectory: targetPath, requestContext: appContext, tenantId: appContext.tenantId || 'default-tenant', }); return { success: true, mode: result.mode, worktrees: result.worktrees, added: result.added, removed: result.removed, moved: result.moved, pruned: result.pruned, }; } /** * Filter git_worktree output based on verbosity level. * * Verbosity levels: * - minimal: Success and mode only * - standard: Above + complete worktrees array (for list) or operation results (RECOMMENDED) * - full: Complete output */ function filterGitWorktreeOutput( result: ToolOutput, level: VerbosityLevel, ): Partial<ToolOutput> { // minimal: Essential info only if (level === 'minimal') { return { success: result.success, mode: result.mode, }; } // standard & full: Complete output // (LLMs need complete context - include all worktrees or operation results) return result; } // Create JSON response formatter with verbosity filtering const responseFormatter = createJsonFormatter<ToolOutput>({ filter: filterGitWorktreeOutput, }); export const gitWorktreeTool: 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(gitWorktreeLogic)), 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