@nvhien20vn/git-mcp-server
Version:
105 lines (98 loc) • 5.56 kB
JavaScript
// Import utils from barrel (ErrorHandler from ../utils/internal/errorHandler.js)
import { ErrorHandler } from '../../../utils/index.js';
// Import utils from barrel (logger from ../utils/internal/logger.js)
import { logger } from '../../../utils/index.js';
// Import utils from barrel (requestContextService from ../utils/internal/requestContext.js)
import { requestContextService } from '../../../utils/index.js';
// Import the result type along with the function and input schema
import { BaseErrorCode } from '../../../types-global/errors.js'; // Keep direct import for types-global
import { commitGitChanges, GitCommitInputSchema } from './logic.js';
let _getWorkingDirectory;
let _getSessionId;
/**
* Initializes the state accessors needed by the tool registration.
* This should be called once during server setup.
* @param getWdFn - Function to get the working directory for a session.
* @param getSidFn - Function to get the session ID from context.
*/
export function initializeGitCommitStateAccessors(getWdFn, getSidFn) {
_getWorkingDirectory = getWdFn;
_getSessionId = getSidFn;
logger.info('State accessors initialized for git_commit tool registration.');
}
const TOOL_NAME = 'git_commit';
const TOOL_DESCRIPTION = `Commits staged changes to the Git repository index with a descriptive message. Supports author override, amending, and empty commits. Returns a JSON result.
**Commit Message Guidance:**
Write clear, concise commit messages using the Conventional Commits format: \`type(scope): subject\`.
- \`type\`: feat, fix, docs, style, refactor, test, chore, etc.
- \`(scope)\`: Optional context (e.g., \`auth\`, \`ui\`, filename).
- \`subject\`: Imperative, present tense description (e.g., "add login button", not "added login button").
I want to understand what you did and why. Use the body for detailed explanations, if necessary.
**Example Commit Message:**
\`\`\`
feat(auth): implement password reset endpoint
- Adds the /api/auth/reset-password endpoint to allow users to reset their password via an email link.
- Includes input validation and rate limiting.
Closes #123 (if applicable).
\`\`\`
**Tool Options & Behavior:**
- Commit related changes logically. Use the optional \`filesToStage\` parameter to auto-stage specific files before committing.
- The \`path\` defaults to the session's working directory unless overridden. If \`GIT_SIGN_COMMITS=true\` is set, commits are signed (\`-S\`), with an optional \`forceUnsignedOnFailure\` fallback.`;
/**
* Registers the git_commit tool with the MCP server.
* Uses the high-level server.tool() method for registration, schema validation, and routing.
*
* @param {McpServer} server - The McpServer instance to register the tool with.
* @returns {Promise<void>}
* @throws {Error} If registration fails or state accessors are not initialized.
*/
export const registerGitCommitTool = async (server) => {
if (!_getWorkingDirectory || !_getSessionId) {
throw new Error('State accessors for git_commit must be initialized before registration.');
}
const operation = 'registerGitCommitTool';
const context = requestContextService.createRequestContext({ operation });
await ErrorHandler.tryCatch(async () => {
server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitCommitInputSchema.shape, // Provide the Zod schema shape
async (validatedArgs, callContext) => {
const toolOperation = 'tool:git_commit';
const requestContext = requestContextService.createRequestContext({ operation: toolOperation, parentContext: callContext });
const sessionId = _getSessionId(requestContext);
const getWorkingDirectoryForSession = () => {
return _getWorkingDirectory(sessionId);
};
const logicContext = {
...requestContext,
sessionId: sessionId,
getWorkingDirectory: getWorkingDirectoryForSession,
};
logger.info(`Executing tool: ${TOOL_NAME}`, logicContext);
return await ErrorHandler.tryCatch(async () => {
// Call the core logic function which now returns a GitCommitResult object
const commitResult = await commitGitChanges(validatedArgs, logicContext);
// Format the result as a JSON string within TextContent
const resultContent = {
type: 'text',
// Stringify the JSON object for the response content
text: JSON.stringify(commitResult, null, 2), // Pretty-print JSON
contentType: 'application/json',
};
// Log based on the success flag in the result
if (commitResult.success) {
logger.info(`Tool ${TOOL_NAME} executed successfully, returning JSON`, logicContext);
}
else {
logger.info(`Tool ${TOOL_NAME} completed with non-fatal condition (e.g., nothing to commit), returning JSON`, logicContext);
}
// Even if success is false (e.g., nothing to commit), it's not a tool execution error
return { content: [resultContent] };
}, {
operation: toolOperation,
context: logicContext,
input: validatedArgs,
errorCode: BaseErrorCode.INTERNAL_ERROR,
});
});
logger.info(`Tool registered: ${TOOL_NAME}`, context);
}, { operation, context, critical: true });
};