Skip to main content
Glama
git-issues.ts15 kB
/** * Git Issues Tool * * Comprehensive issue management tool for Git repositories. * Supports complete issue lifecycle: create, list, get, update, close, comment, search. * Integrates with GitHub and Gitea providers for remote issue operations. */ import { ParameterValidator, ToolParams } from '../utils/parameter-validator.js'; import { OperationErrorHandler, ToolResult } from '../utils/operation-error-handler.js'; import { ProviderOperationHandler } from '../providers/provider-operation-handler.js'; import { ProviderConfig, ProviderOperation } from '../providers/types.js'; import { configManager } from '../config.js'; export interface GitIssuesParams extends ToolParams { action: 'create' | 'list' | 'get' | 'update' | 'close' | 'comment' | 'search'; // Repository identification repo?: string; // Repository name (auto-detected if not provided) // Issue identification issue_number?: number; // Issue number (for get/update/close/comment) // Issue creation/update parameters title?: string; // Issue title (required for create) body?: string; // Issue body/description labels?: string[]; // Issue labels assignees?: string[]; // Issue assignees milestone?: number; // Milestone number state?: 'open' | 'closed'; // Issue state (for update) // Listing parameters state_filter?: 'open' | 'closed' | 'all'; // Filter issues by state (for list) sort?: 'created' | 'updated' | 'comments'; // Sort criteria (for list) direction?: 'asc' | 'desc'; // Sort direction (for list) since?: string; // Only issues updated after this date (for list) // Comment parameters comment_body?: string; // Comment body (required for comment action) // Search parameters query?: string; // Search query (for search) search_sort?: 'created' | 'updated' | 'comments'; // Sort for search results search_order?: 'asc' | 'desc'; // Order for search results } export class GitIssuesTool { private providerHandler?: ProviderOperationHandler; constructor(providerConfig?: ProviderConfig) { if (providerConfig) { this.providerHandler = new ProviderOperationHandler(providerConfig); } } /** * Execute git-issues operation */ async execute(params: GitIssuesParams): Promise<ToolResult> { const startTime = Date.now(); try { // Validate basic parameters const validation = ParameterValidator.validateToolParams('git-issues', params); if (!validation.isValid) { return OperationErrorHandler.createToolError( 'VALIDATION_ERROR', `Parameter validation failed: ${validation.errors.join(', ')}`, params.action, { validationErrors: validation.errors }, validation.suggestions ); } // Validate operation-specific parameters const operationValidation = ParameterValidator.validateOperationParams('git-issues', params.action, params); if (!operationValidation.isValid) { return OperationErrorHandler.createToolError( 'VALIDATION_ERROR', `Operation validation failed: ${operationValidation.errors.join(', ')}`, params.action, { validationErrors: operationValidation.errors }, operationValidation.suggestions ); } // Route to appropriate handler const isRemoteOperation = this.isRemoteOperation(params.action); if (isRemoteOperation) { return await this.executeRemoteOperation(params, startTime); } else { return await this.executeLocalOperation(params, startTime); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'EXECUTION_ERROR', `Failed to execute ${params.action}: ${errorMessage}`, params.action, { error: errorMessage }, ['Check the error details and try again'] ); } } /** * Check if operation is remote */ private isRemoteOperation(action: string): boolean { // All issue operations are remote const remoteOperations = ['create', 'list', 'get', 'update', 'close', 'comment', 'search']; return remoteOperations.includes(action); } /** * Execute remote issue operations */ private async executeRemoteOperation(params: GitIssuesParams, startTime: number): Promise<ToolResult> { // Check provider configuration if (!this.providerHandler) { return OperationErrorHandler.createToolError( 'PROVIDER_NOT_CONFIGURED', 'Provider handler is not configured for issue operations', params.action, {}, ['Configure GitHub or Gitea provider to use issue operations'] ); } if (!params.provider) { if (configManager.isUniversalMode()) { params.provider = 'both'; console.error(`[Universal Mode] Auto-applying both providers for ${params.action}`); } else { return OperationErrorHandler.createToolError( 'PROVIDER_REQUIRED', 'Provider parameter is required for issue operations', params.action, {}, ['Specify provider as: github, gitea, or both'] ); } } return await this.executeIssueOperation(params, startTime); } /** * Execute local issue operations (if any) */ private async executeLocalOperation(params: GitIssuesParams, startTime: number): Promise<ToolResult> { // Issues are remote-only operations return OperationErrorHandler.createToolError( 'LOCAL_OPERATION_NOT_SUPPORTED', 'Issue operations are not supported locally', params.action, {}, ['Use remote operations with GitHub or Gitea provider'] ); } /** * Execute issue operation through provider */ private async executeIssueOperation(params: GitIssuesParams, startTime: number): Promise<ToolResult> { const operation: ProviderOperation = { provider: params.provider!, operation: this.mapActionToProviderOperation(params.action), parameters: this.extractIssueParameters(params), requiresAuth: true, isRemoteOperation: true }; try { const result = await this.providerHandler!.executeOperation(operation); return { success: result.success, data: result.partialFailure ? result : result.results[0]?.data, error: result.success ? undefined : { code: result.errors[0]?.error?.code || 'ISSUE_OPERATION_ERROR', message: result.errors[0]?.error?.message || 'Issue operation failed', details: result.errors, suggestions: this.getOperationSuggestions(params.action) }, metadata: { provider: params.provider, operation: params.action, timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'ISSUE_OPERATION_ERROR', `Issue operation failed: ${errorMessage}`, params.action, { error: errorMessage }, ['Check provider configuration and network connectivity'] ); } } /** * Extract parameters for issue operations */ private extractIssueParameters(params: GitIssuesParams): Record<string, any> { const issueParams: Record<string, any> = { projectPath: params.projectPath }; // Auto-detect repo if not provided if (params.repo) issueParams.repo = params.repo; // Issue identification if (params.issue_number !== undefined) issueParams.issue_number = params.issue_number; // Operation-specific parameters switch (params.action) { case 'create': if (params.title) issueParams.title = params.title; if (params.body) issueParams.body = params.body; if (params.labels) issueParams.labels = params.labels; if (params.assignees) issueParams.assignees = params.assignees; if (params.milestone) issueParams.milestone = params.milestone; break; case 'list': if (params.state_filter) issueParams.state = params.state_filter; if (params.sort) issueParams.sort = params.sort; if (params.direction) issueParams.direction = params.direction; if (params.since) issueParams.since = params.since; break; case 'get': // Get operations need owner, repo, and issue_number (already handled above) break; case 'update': if (params.title) issueParams.title = params.title; if (params.body) issueParams.body = params.body; if (params.state) issueParams.state = params.state; if (params.labels) issueParams.labels = params.labels; if (params.assignees) issueParams.assignees = params.assignees; if (params.milestone) issueParams.milestone = params.milestone; break; case 'close': issueParams.state = 'closed'; break; case 'comment': if (params.comment_body) issueParams.body = params.comment_body; break; case 'search': if (params.query) issueParams.query = params.query; if (params.search_sort) issueParams.sort = params.search_sort; if (params.search_order) issueParams.order = params.search_order; break; } return issueParams; } /** * Map git-issues actions to provider operations */ private mapActionToProviderOperation(action: string): string { const actionMap: Record<string, string> = { 'create': 'issue-create', 'list': 'issue-list', 'get': 'issue-get', 'update': 'issue-update', 'close': 'issue-close', 'comment': 'issue-comment', 'search': 'issue-search' }; return actionMap[action] || action; } /** * Get operation-specific suggestions */ private getOperationSuggestions(action: string): string[] { const suggestions: Record<string, string[]> = { 'create': [ 'Ensure title is provided and not empty', 'Check that you have permission to create issues in the repository', 'Verify repository exists and is accessible' ], 'list': [ 'Check repository access permissions', 'Verify owner and repo parameters are correct', 'Try different state filters: open, closed, all' ], 'get': [ 'Verify the issue number exists', 'Check repository access permissions', 'Ensure owner and repo parameters are correct' ], 'update': [ 'Verify the issue number exists', 'Check that you have permission to edit the issue', 'Ensure at least one field is being updated' ], 'close': [ 'Verify the issue number exists and is open', 'Check that you have permission to close the issue', 'Ensure the issue is not already closed' ], 'comment': [ 'Verify the issue number exists', 'Ensure comment body is provided and not empty', 'Check that you have permission to comment on the issue' ], 'search': [ 'Provide a search query', 'Check search syntax for the provider', 'Try different sort and order parameters' ] }; return suggestions[action] || ['Check provider configuration and try again']; } /** * Get tool schema for MCP registration */ static getToolSchema() { return { name: 'git-issues', description: 'Comprehensive issue management tool for Git repositories. Supports create, list, get, update, close, comment, and search operations for issues. In universal mode (GIT_MCP_MODE=universal), automatically executes on both GitHub and Gitea providers.', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['create', 'list', 'get', 'update', 'close', 'comment', 'search'], description: 'The issue operation to perform' }, projectPath: { type: 'string', description: 'Absolute path to the project directory' }, provider: { type: 'string', enum: ['github', 'gitea', 'both'], description: 'Provider for issue operations (required)' }, repo: { type: 'string', description: 'Repository name (auto-detected if not provided)' }, issue_number: { type: 'number', description: 'Issue number (required for get/update/close/comment actions)' }, title: { type: 'string', description: 'Issue title (required for create, optional for update)' }, body: { type: 'string', description: 'Issue body/description' }, labels: { type: 'array', items: { type: 'string' }, description: 'Issue labels' }, assignees: { type: 'array', items: { type: 'string' }, description: 'Issue assignees (usernames)' }, milestone: { type: 'number', description: 'Milestone number' }, state: { type: 'string', enum: ['open', 'closed'], description: 'Issue state (for update action)' }, state_filter: { type: 'string', enum: ['open', 'closed', 'all'], description: 'Filter issues by state (for list action, default: open)' }, sort: { type: 'string', enum: ['created', 'updated', 'comments'], description: 'Sort criteria (for list action, default: created)' }, direction: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (for list action, default: desc)' }, since: { type: 'string', description: 'Only issues updated after this date (ISO 8601 format, for list action)' }, comment_body: { type: 'string', description: 'Comment body (required for comment action)' }, query: { type: 'string', description: 'Search query (required for search action)' }, search_sort: { type: 'string', enum: ['created', 'updated', 'comments'], description: 'Sort for search results (default: created)' }, search_order: { type: 'string', enum: ['asc', 'desc'], description: 'Order for search results (default: desc)' } }, required: ['action', 'projectPath'] } }; } }

Implementation Reference

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/Andre-Buzeli/git-mcp'

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