Skip to main content
Glama
blade47

ShadowGit MCP Server

by blade47

checkpoint

Create a git commit with your changes to save work progress. Use this tool after completing work but before ending a session to generate a clean commit for review.

Instructions

Create a git commit with your changes. Call this AFTER completing your work but BEFORE end_session. Creates a clean commit for the user to review.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
repoYesRepository name
titleYesCommit title (max 50 chars) - REQUIRED. Be specific about what was changed.
messageNoDetailed commit message (optional, max 1000 chars)
authorNoAuthor name (e.g., "Claude", "GPT-4"). Defaults to "AI Assistant"

Implementation Reference

  • Main handler logic for the 'checkpoint' tool. Validates input, checks for changes, stages all files with 'git add -A', creates a git commit with custom message and author, returns detailed success response with commit hash and change summary.
    async handle(args: unknown): Promise<MCPToolResponse> { if (!this.isValidArgs(args)) { return createErrorResponse( "Error: Both 'repo' and 'title' parameters are required.", `Example usage: checkpoint({ repo: "my-project", title: "Fix authentication bug", author: "Claude" }) Use list_repos() to see available repositories.` ); } // Validate title length if (args.title.length > 50) { return createErrorResponse( `Error: Title must be 50 characters or less (current: ${args.title.length} chars).` ); } // Validate message length if provided if (args.message && args.message.length > 1000) { return createErrorResponse( `Error: Message must be 1000 characters or less (current: ${args.message.length} chars).` ); } const repoPath = this.repositoryManager.resolveRepoPath(args.repo); if (!repoPath) { const repos = this.repositoryManager.getRepositories(); if (repos.length === 0) { return createErrorResponse( 'Error: No repositories found. Please add repositories to ShadowGit first.' ); } return createErrorResponse( `Error: Repository '${args.repo}' not found.`, `Available repositories: ${repos.map(r => ` - ${r.name}: ${r.path}`).join('\n')}` ); } // Check for changes const statusOutput = await this.gitExecutor.execute(['status', '--porcelain'], repoPath, true); if (!statusOutput || statusOutput.trim() === '' || statusOutput === '(empty output)') { return createErrorResponse( `❌ **No Changes Detected** ${'='.repeat(50)} πŸ“ Repository has no changes to commit. ⚠️ **Important:** Do NOT call end_session() - no commit was created. πŸ’‘ **NEXT STEP:** Make some changes first, then call checkpoint() again.` ); } // Build commit message const commitTitle = args.title; const commitBody = args.message || ''; const author = args.author || 'AI Assistant'; // Add all changes const addOutput = await this.gitExecutor.execute(['add', '-A'], repoPath, true); if (addOutput.startsWith('Error:')) { return createErrorResponse( `❌ **Failed to Stage Changes** ${'='.repeat(50)} 🚨 **Error:** ${addOutput} ⚠️ **Important:** Do NOT call end_session() - commit was not created. πŸ’‘ **NEXT STEP:** Check the error and try again.` ); } // Build full commit message let fullMessage = commitTitle; if (commitBody) { fullMessage += `\n\n${commitBody}`; } fullMessage += `\n\nAuthor: ${author} (via ShadowGit MCP)`; // Create commit with author information const commitEnv = { GIT_AUTHOR_NAME: author, GIT_AUTHOR_EMAIL: `${author.toLowerCase().replace(/\s+/g, '-')}@shadowgit.local`, GIT_COMMITTER_NAME: 'ShadowGit MCP', GIT_COMMITTER_EMAIL: 'shadowgit-mcp@shadowgit.local' }; // Use array-based command to avoid parsing issues const commitOutput = await this.gitExecutor.execute( ['commit', '-m', fullMessage], repoPath, true, commitEnv ); if (commitOutput.startsWith('Error:')) { return createErrorResponse( `❌ **Failed to Create Commit** ${'='.repeat(50)} 🚨 **Error:** ${commitOutput} ⚠️ **Important:** Do NOT call end_session() - commit was not created. πŸ’‘ **NEXT STEP:** Check the error message and try checkpoint() again.` ); } // Extract commit hash from output let commitHash: string | undefined; const hashMatch = commitOutput.match(/\[[\w\-]+ ([a-f0-9]+)\]/); if (hashMatch) { commitHash = hashMatch[1]; } // Get summary of changes const showOutput = await this.gitExecutor.execute(['show', '--stat', '--format=short', 'HEAD'], repoPath, true); return { content: [ { type: 'text', text: `βœ… **Checkpoint Created Successfully!** ${'='.repeat(50)} πŸ“¦ **Commit Details:** ${commitOutput} πŸ“Š **Changes Summary:** ${showOutput} πŸ”‘ **Commit Hash:** \`${commitHash || 'unknown'}\` ${'='.repeat(50)} πŸ“‹ **Workflow Progress:** - [x] Session started - [x] Changes made - [x] Checkpoint created ✨ - [ ] Session ended 🚨 **REQUIRED NEXT STEP:** You MUST now call \`end_session()\` to resume auto-commits: \`\`\`javascript end_session({ sessionId: "your-session-id", commitHash: "${commitHash || 'unknown'}" }) \`\`\` ⚠️ **Important:** Only call end_session() because the commit was SUCCESSFUL.` } ], success: true }; }
  • Tool registration in ListToolsRequestSchema handler, defining name, description, and input schema for the 'checkpoint' tool.
    { name: 'checkpoint', description: 'Create a git commit with your changes. Call this AFTER completing your work but BEFORE end_session. Creates a clean commit for the user to review.', inputSchema: { type: 'object', properties: { repo: { type: 'string', description: 'Repository name', }, title: { type: 'string', description: 'Commit title (max 50 chars) - REQUIRED. Be specific about what was changed.', }, message: { type: 'string', description: 'Detailed commit message (optional, max 1000 chars)', }, author: { type: 'string', description: 'Author name (e.g., "Claude", "GPT-4"). Defaults to "AI Assistant"', }, }, required: ['repo', 'title'], }, },
  • Switch case in CallToolRequestSchema handler that routes 'checkpoint' calls to CheckpointHandler.handle().
    case 'checkpoint': return await this.checkpointHandler.handle(args);
  • TypeScript interface defining the expected arguments for the checkpoint tool, used for validation in the handler.
    export interface ManualCheckpointArgs { repo: string; title: string; message?: string; author?: string; }
  • Helper method for validating checkpoint arguments against ManualCheckpointArgs type.
    private isValidArgs(args: unknown): args is ManualCheckpointArgs { return ( typeof args === 'object' && args !== null && 'repo' in args && 'title' in args && typeof (args as ManualCheckpointArgs).repo === 'string' && typeof (args as ManualCheckpointArgs).title === 'string' ); }

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/blade47/shadowgit-mcp'

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