import { z } from 'zod';
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
/**
* Git Tools
* Provides comprehensive git operations for autonomous development
* Enables full version control workflow without human intervention
*/
// ========== HELPER TYPE ==========
type ToolResponse = {
content: Array<{ type: "text"; text: string }>;
isError?: boolean;
};
// Helper function to execute git commands
async function executeGitCommand(command: string, cwd?: string): Promise<ToolResponse> {
try {
const { stdout, stderr } = await execAsync(command, {
cwd: cwd || process.cwd(),
shell: '/bin/bash',
maxBuffer: 10 * 1024 * 1024 // 10MB buffer
});
return {
content: [
{
type: "text" as const,
text: JSON.stringify({
success: true,
command: command,
stdout: stdout.trim(),
stderr: stderr.trim(),
cwd: cwd || process.cwd()
}, null, 2)
}
]
};
} catch (error: any) {
return {
content: [
{
type: "text" as const,
text: JSON.stringify({
success: false,
command: command,
stdout: error.stdout?.trim() || '',
stderr: error.stderr?.trim() || error.message,
exitCode: error.code || 1,
cwd: cwd || process.cwd()
}, null, 2)
}
],
isError: true
};
}
}
// ========== TOOL SCHEMAS ==========
export const gitStatusSchema = z.object({
cwd: z.string().optional().describe('Repository directory (defaults to current directory)'),
short: z.boolean().optional().default(false).describe('Show short format status')
});
export const gitAddSchema = z.object({
files: z.union([z.string(), z.array(z.string())]).describe('File(s) to stage. Use "." for all files'),
cwd: z.string().optional().describe('Repository directory')
});
export const gitCommitSchema = z.object({
message: z.string().describe('Commit message'),
cwd: z.string().optional().describe('Repository directory'),
amend: z.boolean().optional().default(false).describe('Amend previous commit'),
noVerify: z.boolean().optional().default(false).describe('Skip pre-commit hooks')
});
export const gitDiffSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
cached: z.boolean().optional().default(false).describe('Show staged changes'),
files: z.string().optional().describe('Specific file(s) to diff'),
commit: z.string().optional().describe('Compare against specific commit/branch')
});
export const gitLogSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
limit: z.number().optional().default(10).describe('Number of commits to show'),
oneline: z.boolean().optional().default(false).describe('Show one line per commit'),
graph: z.boolean().optional().default(false).describe('Show commit graph'),
all: z.boolean().optional().default(false).describe('Show all branches')
});
export const gitBranchSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
action: z.enum(['list', 'create', 'delete', 'rename']).optional().default('list').describe('Branch action'),
name: z.string().optional().describe('Branch name (required for create/delete/rename)'),
newName: z.string().optional().describe('New branch name (required for rename)'),
force: z.boolean().optional().default(false).describe('Force delete or creation'),
remote: z.boolean().optional().default(false).describe('List remote branches')
});
export const gitCheckoutSchema = z.object({
target: z.string().describe('Branch name, commit hash, or file path to checkout'),
cwd: z.string().optional().describe('Repository directory'),
createBranch: z.boolean().optional().default(false).describe('Create new branch'),
force: z.boolean().optional().default(false).describe('Force checkout, discarding local changes')
});
export const gitPullSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
remote: z.string().optional().default('origin').describe('Remote name'),
branch: z.string().optional().describe('Branch to pull (defaults to current branch)'),
rebase: z.boolean().optional().default(false).describe('Rebase instead of merge')
});
export const gitPushSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
remote: z.string().optional().default('origin').describe('Remote name'),
branch: z.string().optional().describe('Branch to push (defaults to current branch)'),
force: z.boolean().optional().default(false).describe('Force push (use with caution)'),
setUpstream: z.boolean().optional().default(false).describe('Set upstream tracking')
});
export const gitCloneSchema = z.object({
url: z.string().describe('Repository URL to clone'),
destination: z.string().optional().describe('Destination directory (defaults to repo name)'),
cwd: z.string().optional().describe('Directory to clone into'),
branch: z.string().optional().describe('Specific branch to clone'),
depth: z.number().optional().describe('Create shallow clone with depth')
});
export const gitInitSchema = z.object({
cwd: z.string().optional().describe('Directory to initialize as git repository'),
bare: z.boolean().optional().default(false).describe('Create bare repository')
});
export const gitMergeSchema = z.object({
branch: z.string().describe('Branch to merge into current branch'),
cwd: z.string().optional().describe('Repository directory'),
noFf: z.boolean().optional().default(false).describe('Create merge commit even if fast-forward is possible'),
abort: z.boolean().optional().default(false).describe('Abort current merge')
});
export const gitRebaseSchema = z.object({
branch: z.string().optional().describe('Branch to rebase onto'),
cwd: z.string().optional().describe('Repository directory'),
interactive: z.boolean().optional().default(false).describe('Interactive rebase'),
abort: z.boolean().optional().default(false).describe('Abort current rebase'),
continue: z.boolean().optional().default(false).describe('Continue rebase after resolving conflicts')
});
export const gitFetchSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
remote: z.string().optional().default('origin').describe('Remote name'),
prune: z.boolean().optional().default(false).describe('Remove deleted remote branches')
});
export const gitRemoteSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
action: z.enum(['list', 'add', 'remove', 'show']).optional().default('list').describe('Remote action'),
name: z.string().optional().describe('Remote name (required for add/remove/show)'),
url: z.string().optional().describe('Remote URL (required for add)')
});
export const gitStashSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
action: z.enum(['push', 'pop', 'list', 'apply', 'drop', 'clear']).optional().default('push').describe('Stash action'),
message: z.string().optional().describe('Stash message (for push)'),
index: z.number().optional().describe('Stash index (for pop/apply/drop)')
});
export const gitResetSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
mode: z.enum(['soft', 'mixed', 'hard']).optional().default('mixed').describe('Reset mode'),
commit: z.string().optional().default('HEAD').describe('Commit to reset to'),
files: z.string().optional().describe('Specific file(s) to reset')
});
export const gitRevertSchema = z.object({
commit: z.string().describe('Commit hash to revert'),
cwd: z.string().optional().describe('Repository directory'),
noCommit: z.boolean().optional().default(false).describe('Apply changes without committing')
});
export const gitTagSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
action: z.enum(['list', 'create', 'delete']).optional().default('list').describe('Tag action'),
name: z.string().optional().describe('Tag name (required for create/delete)'),
message: z.string().optional().describe('Tag message (for annotated tags)'),
commit: z.string().optional().describe('Commit to tag (defaults to HEAD)')
});
export const gitShowSchema = z.object({
commit: z.string().optional().default('HEAD').describe('Commit hash or reference to show'),
cwd: z.string().optional().describe('Repository directory')
});
export const gitConfigSchema = z.object({
cwd: z.string().optional().describe('Repository directory'),
action: z.enum(['get', 'set', 'list']).optional().default('list').describe('Config action'),
key: z.string().optional().describe('Config key (required for get/set)'),
value: z.string().optional().describe('Config value (required for set)'),
global: z.boolean().optional().default(false).describe('Use global config instead of repository config')
});
// ========== TOOL IMPLEMENTATIONS ==========
export async function gitStatus(args: z.infer<typeof gitStatusSchema>): Promise<ToolResponse> {
const shortFlag = args.short ? '--short' : '';
return executeGitCommand(`git status ${shortFlag}`, args.cwd);
}
export async function gitAdd(args: z.infer<typeof gitAddSchema>): Promise<ToolResponse> {
const files = Array.isArray(args.files) ? args.files.join(' ') : args.files;
return executeGitCommand(`git add ${files}`, args.cwd);
}
export async function gitCommit(args: z.infer<typeof gitCommitSchema>): Promise<ToolResponse> {
const amendFlag = args.amend ? '--amend' : '';
const noVerifyFlag = args.noVerify ? '--no-verify' : '';
// Escape message for shell
const escapedMessage = args.message.replace(/'/g, "'\\''");
return executeGitCommand(`git commit ${amendFlag} ${noVerifyFlag} -m '${escapedMessage}'`, args.cwd);
}
export async function gitDiff(args: z.infer<typeof gitDiffSchema>): Promise<ToolResponse> {
const cachedFlag = args.cached ? '--cached' : '';
const files = args.files || '';
const commit = args.commit || '';
return executeGitCommand(`git diff ${cachedFlag} ${commit} ${files}`.trim(), args.cwd);
}
export async function gitLog(args: z.infer<typeof gitLogSchema>): Promise<ToolResponse> {
const onelineFlag = args.oneline ? '--oneline' : '--pretty=format:"%H | %an | %ar | %s"';
const graphFlag = args.graph ? '--graph' : '';
const allFlag = args.all ? '--all' : '';
const limit = `-${args.limit}`;
return executeGitCommand(`git log ${onelineFlag} ${graphFlag} ${allFlag} ${limit}`.trim(), args.cwd);
}
export async function gitBranch(args: z.infer<typeof gitBranchSchema>): Promise<ToolResponse> {
switch (args.action) {
case 'list':
const remoteFlag = args.remote ? '-r' : '-a';
return executeGitCommand(`git branch ${remoteFlag}`, args.cwd);
case 'create':
if (!args.name) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Branch name required for create action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git branch ${args.name}`, args.cwd);
case 'delete':
if (!args.name) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Branch name required for delete action' }, null, 2) }],
isError: true
};
}
const deleteFlag = args.force ? '-D' : '-d';
return executeGitCommand(`git branch ${deleteFlag} ${args.name}`, args.cwd);
case 'rename':
if (!args.name || !args.newName) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Both name and newName required for rename action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git branch -m ${args.name} ${args.newName}`, args.cwd);
default:
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Invalid branch action' }, null, 2) }],
isError: true
};
}
}
export async function gitCheckout(args: z.infer<typeof gitCheckoutSchema>): Promise<ToolResponse> {
const createFlag = args.createBranch ? '-b' : '';
const forceFlag = args.force ? '-f' : '';
return executeGitCommand(`git checkout ${createFlag} ${forceFlag} ${args.target}`.trim(), args.cwd);
}
export async function gitPull(args: z.infer<typeof gitPullSchema>): Promise<ToolResponse> {
const rebaseFlag = args.rebase ? '--rebase' : '';
const branch = args.branch || '';
return executeGitCommand(`git pull ${rebaseFlag} ${args.remote} ${branch}`.trim(), args.cwd);
}
export async function gitPush(args: z.infer<typeof gitPushSchema>): Promise<ToolResponse> {
const forceFlag = args.force ? '--force' : '';
const upstreamFlag = args.setUpstream ? '-u' : '';
const branch = args.branch || '';
return executeGitCommand(`git push ${upstreamFlag} ${forceFlag} ${args.remote} ${branch}`.trim(), args.cwd);
}
export async function gitClone(args: z.infer<typeof gitCloneSchema>): Promise<ToolResponse> {
const branchFlag = args.branch ? `-b ${args.branch}` : '';
const depthFlag = args.depth ? `--depth ${args.depth}` : '';
const destination = args.destination || '';
return executeGitCommand(`git clone ${branchFlag} ${depthFlag} ${args.url} ${destination}`.trim(), args.cwd);
}
export async function gitInit(args: z.infer<typeof gitInitSchema>): Promise<ToolResponse> {
const bareFlag = args.bare ? '--bare' : '';
return executeGitCommand(`git init ${bareFlag}`.trim(), args.cwd);
}
export async function gitMerge(args: z.infer<typeof gitMergeSchema>): Promise<ToolResponse> {
if (args.abort) {
return executeGitCommand('git merge --abort', args.cwd);
}
const noFfFlag = args.noFf ? '--no-ff' : '';
return executeGitCommand(`git merge ${noFfFlag} ${args.branch}`.trim(), args.cwd);
}
export async function gitRebase(args: z.infer<typeof gitRebaseSchema>): Promise<ToolResponse> {
if (args.abort) {
return executeGitCommand('git rebase --abort', args.cwd);
}
if (args.continue) {
return executeGitCommand('git rebase --continue', args.cwd);
}
if (args.interactive) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Interactive rebase not supported in non-interactive environment' }, null, 2) }],
isError: true
};
}
if (!args.branch) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Branch required for rebase' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git rebase ${args.branch}`, args.cwd);
}
export async function gitFetch(args: z.infer<typeof gitFetchSchema>): Promise<ToolResponse> {
const pruneFlag = args.prune ? '--prune' : '';
return executeGitCommand(`git fetch ${pruneFlag} ${args.remote}`.trim(), args.cwd);
}
export async function gitRemote(args: z.infer<typeof gitRemoteSchema>): Promise<ToolResponse> {
switch (args.action) {
case 'list':
return executeGitCommand('git remote -v', args.cwd);
case 'add':
if (!args.name || !args.url) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Name and URL required for add action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git remote add ${args.name} ${args.url}`, args.cwd);
case 'remove':
if (!args.name) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Name required for remove action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git remote remove ${args.name}`, args.cwd);
case 'show':
if (!args.name) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Name required for show action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git remote show ${args.name}`, args.cwd);
default:
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Invalid remote action' }, null, 2) }],
isError: true
};
}
}
export async function gitStash(args: z.infer<typeof gitStashSchema>): Promise<ToolResponse> {
switch (args.action) {
case 'push':
const message = args.message ? `-m "${args.message}"` : '';
return executeGitCommand(`git stash push ${message}`.trim(), args.cwd);
case 'pop':
const popIndex = args.index !== undefined ? `stash@{${args.index}}` : '';
return executeGitCommand(`git stash pop ${popIndex}`.trim(), args.cwd);
case 'list':
return executeGitCommand('git stash list', args.cwd);
case 'apply':
const applyIndex = args.index !== undefined ? `stash@{${args.index}}` : '';
return executeGitCommand(`git stash apply ${applyIndex}`.trim(), args.cwd);
case 'drop':
const dropIndex = args.index !== undefined ? `stash@{${args.index}}` : '';
return executeGitCommand(`git stash drop ${dropIndex}`.trim(), args.cwd);
case 'clear':
return executeGitCommand('git stash clear', args.cwd);
default:
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Invalid stash action' }, null, 2) }],
isError: true
};
}
}
export async function gitReset(args: z.infer<typeof gitResetSchema>): Promise<ToolResponse> {
if (args.files) {
return executeGitCommand(`git reset ${args.commit} -- ${args.files}`, args.cwd);
}
const modeFlag = `--${args.mode}`;
return executeGitCommand(`git reset ${modeFlag} ${args.commit}`, args.cwd);
}
export async function gitRevert(args: z.infer<typeof gitRevertSchema>): Promise<ToolResponse> {
const noCommitFlag = args.noCommit ? '--no-commit' : '';
return executeGitCommand(`git revert ${noCommitFlag} ${args.commit}`.trim(), args.cwd);
}
export async function gitTag(args: z.infer<typeof gitTagSchema>): Promise<ToolResponse> {
switch (args.action) {
case 'list':
return executeGitCommand('git tag -l', args.cwd);
case 'create':
if (!args.name) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Tag name required for create action' }, null, 2) }],
isError: true
};
}
const messageFlag = args.message ? `-a -m "${args.message}"` : '';
const commit = args.commit || '';
return executeGitCommand(`git tag ${messageFlag} ${args.name} ${commit}`.trim(), args.cwd);
case 'delete':
if (!args.name) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Tag name required for delete action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git tag -d ${args.name}`, args.cwd);
default:
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Invalid tag action' }, null, 2) }],
isError: true
};
}
}
export async function gitShow(args: z.infer<typeof gitShowSchema>): Promise<ToolResponse> {
return executeGitCommand(`git show ${args.commit}`, args.cwd);
}
export async function gitConfig(args: z.infer<typeof gitConfigSchema>): Promise<ToolResponse> {
const globalFlag = args.global ? '--global' : '';
switch (args.action) {
case 'list':
return executeGitCommand(`git config ${globalFlag} --list`.trim(), args.cwd);
case 'get':
if (!args.key) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Key required for get action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git config ${globalFlag} ${args.key}`.trim(), args.cwd);
case 'set':
if (!args.key || !args.value) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Key and value required for set action' }, null, 2) }],
isError: true
};
}
return executeGitCommand(`git config ${globalFlag} ${args.key} "${args.value}"`.trim(), args.cwd);
default:
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: 'Invalid config action' }, null, 2) }],
isError: true
};
}
}
// ========== TOOL DEFINITIONS FOR MCP ==========
export const gitTools = [
{
name: 'git_status',
description: 'Get repository status showing staged, unstaged, and untracked files',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory (defaults to current directory)' },
short: { type: 'boolean', default: false, description: 'Show short format status' }
}
}
},
{
name: 'git_add',
description: 'Stage files for commit. Use "." to stage all changes',
inputSchema: {
type: 'object',
properties: {
files: {
oneOf: [
{ type: 'string' },
{ type: 'array', items: { type: 'string' } }
],
description: 'File(s) to stage. Use "." for all files'
},
cwd: { type: 'string', description: 'Repository directory' }
},
required: ['files']
}
},
{
name: 'git_commit',
description: 'Create a commit with staged changes',
inputSchema: {
type: 'object',
properties: {
message: { type: 'string', description: 'Commit message' },
cwd: { type: 'string', description: 'Repository directory' },
amend: { type: 'boolean', default: false, description: 'Amend previous commit' },
noVerify: { type: 'boolean', default: false, description: 'Skip pre-commit hooks' }
},
required: ['message']
}
},
{
name: 'git_diff',
description: 'Show changes between commits, working tree, and staging area',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
cached: { type: 'boolean', default: false, description: 'Show staged changes' },
files: { type: 'string', description: 'Specific file(s) to diff' },
commit: { type: 'string', description: 'Compare against specific commit/branch' }
}
}
},
{
name: 'git_log',
description: 'Show commit history with details',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
limit: { type: 'number', default: 10, description: 'Number of commits to show' },
oneline: { type: 'boolean', default: false, description: 'Show one line per commit' },
graph: { type: 'boolean', default: false, description: 'Show commit graph' },
all: { type: 'boolean', default: false, description: 'Show all branches' }
}
}
},
{
name: 'git_branch',
description: 'List, create, delete, or rename branches',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
action: { type: 'string', enum: ['list', 'create', 'delete', 'rename'], default: 'list', description: 'Branch action' },
name: { type: 'string', description: 'Branch name (required for create/delete/rename)' },
newName: { type: 'string', description: 'New branch name (required for rename)' },
force: { type: 'boolean', default: false, description: 'Force delete or creation' },
remote: { type: 'boolean', default: false, description: 'List remote branches' }
}
}
},
{
name: 'git_checkout',
description: 'Switch branches or restore files',
inputSchema: {
type: 'object',
properties: {
target: { type: 'string', description: 'Branch name, commit hash, or file path to checkout' },
cwd: { type: 'string', description: 'Repository directory' },
createBranch: { type: 'boolean', default: false, description: 'Create new branch' },
force: { type: 'boolean', default: false, description: 'Force checkout, discarding local changes' }
},
required: ['target']
}
},
{
name: 'git_pull',
description: 'Fetch and integrate changes from remote repository',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
remote: { type: 'string', default: 'origin', description: 'Remote name' },
branch: { type: 'string', description: 'Branch to pull (defaults to current branch)' },
rebase: { type: 'boolean', default: false, description: 'Rebase instead of merge' }
}
}
},
{
name: 'git_push',
description: 'Push commits to remote repository',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
remote: { type: 'string', default: 'origin', description: 'Remote name' },
branch: { type: 'string', description: 'Branch to push (defaults to current branch)' },
force: { type: 'boolean', default: false, description: 'Force push (use with caution)' },
setUpstream: { type: 'boolean', default: false, description: 'Set upstream tracking' }
}
}
},
{
name: 'git_clone',
description: 'Clone a repository into a new directory',
inputSchema: {
type: 'object',
properties: {
url: { type: 'string', description: 'Repository URL to clone' },
destination: { type: 'string', description: 'Destination directory (defaults to repo name)' },
cwd: { type: 'string', description: 'Directory to clone into' },
branch: { type: 'string', description: 'Specific branch to clone' },
depth: { type: 'number', description: 'Create shallow clone with depth' }
},
required: ['url']
}
},
{
name: 'git_init',
description: 'Initialize a new git repository',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Directory to initialize as git repository' },
bare: { type: 'boolean', default: false, description: 'Create bare repository' }
}
}
},
{
name: 'git_merge',
description: 'Merge a branch into current branch',
inputSchema: {
type: 'object',
properties: {
branch: { type: 'string', description: 'Branch to merge into current branch' },
cwd: { type: 'string', description: 'Repository directory' },
noFf: { type: 'boolean', default: false, description: 'Create merge commit even if fast-forward is possible' },
abort: { type: 'boolean', default: false, description: 'Abort current merge' }
},
required: ['branch']
}
},
{
name: 'git_rebase',
description: 'Reapply commits on top of another branch',
inputSchema: {
type: 'object',
properties: {
branch: { type: 'string', description: 'Branch to rebase onto' },
cwd: { type: 'string', description: 'Repository directory' },
interactive: { type: 'boolean', default: false, description: 'Interactive rebase (not supported)' },
abort: { type: 'boolean', default: false, description: 'Abort current rebase' },
continue: { type: 'boolean', default: false, description: 'Continue rebase after resolving conflicts' }
}
}
},
{
name: 'git_fetch',
description: 'Download objects and refs from remote repository',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
remote: { type: 'string', default: 'origin', description: 'Remote name' },
prune: { type: 'boolean', default: false, description: 'Remove deleted remote branches' }
}
}
},
{
name: 'git_remote',
description: 'Manage remote repositories',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
action: { type: 'string', enum: ['list', 'add', 'remove', 'show'], default: 'list', description: 'Remote action' },
name: { type: 'string', description: 'Remote name (required for add/remove/show)' },
url: { type: 'string', description: 'Remote URL (required for add)' }
}
}
},
{
name: 'git_stash',
description: 'Stash changes in working directory',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
action: { type: 'string', enum: ['push', 'pop', 'list', 'apply', 'drop', 'clear'], default: 'push', description: 'Stash action' },
message: { type: 'string', description: 'Stash message (for push)' },
index: { type: 'number', description: 'Stash index (for pop/apply/drop)' }
}
}
},
{
name: 'git_reset',
description: 'Reset current HEAD to specified state',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
mode: { type: 'string', enum: ['soft', 'mixed', 'hard'], default: 'mixed', description: 'Reset mode' },
commit: { type: 'string', default: 'HEAD', description: 'Commit to reset to' },
files: { type: 'string', description: 'Specific file(s) to reset' }
}
}
},
{
name: 'git_revert',
description: 'Revert a commit by creating a new commit',
inputSchema: {
type: 'object',
properties: {
commit: { type: 'string', description: 'Commit hash to revert' },
cwd: { type: 'string', description: 'Repository directory' },
noCommit: { type: 'boolean', default: false, description: 'Apply changes without committing' }
},
required: ['commit']
}
},
{
name: 'git_tag',
description: 'Create, list, or delete tags',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
action: { type: 'string', enum: ['list', 'create', 'delete'], default: 'list', description: 'Tag action' },
name: { type: 'string', description: 'Tag name (required for create/delete)' },
message: { type: 'string', description: 'Tag message (for annotated tags)' },
commit: { type: 'string', description: 'Commit to tag (defaults to HEAD)' }
}
}
},
{
name: 'git_show',
description: 'Show details of a commit',
inputSchema: {
type: 'object',
properties: {
commit: { type: 'string', default: 'HEAD', description: 'Commit hash or reference to show' },
cwd: { type: 'string', description: 'Repository directory' }
}
}
},
{
name: 'git_config',
description: 'Get or set git configuration',
inputSchema: {
type: 'object',
properties: {
cwd: { type: 'string', description: 'Repository directory' },
action: { type: 'string', enum: ['get', 'set', 'list'], default: 'list', description: 'Config action' },
key: { type: 'string', description: 'Config key (required for get/set)' },
value: { type: 'string', description: 'Config value (required for set)' },
global: { type: 'boolean', default: false, description: 'Use global config instead of repository config' }
}
}
}
];