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