@andrebuzeli/git-mcp 6.2.0 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +86 -51
- package/dist/tools/gitBranches.d.ts +0 -6
- package/dist/tools/gitBranches.js +1 -11
- package/dist/tools/gitIssues.js +2 -22
- package/dist/tools/gitSync.d.ts +0 -2
- package/dist/tools/gitSync.js +2 -10
- package/dist/tools/gitUpload.js +1 -8
- package/dist/utils/errors.d.ts +0 -42
- package/dist/utils/errors.js +0 -200
- package/dist/utils/repoHelpers.d.ts +0 -10
- package/dist/utils/repoHelpers.js +0 -52
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4,7 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const
|
|
7
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
8
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
8
10
|
const providerManager_1 = require("./providers/providerManager");
|
|
9
11
|
const config_1 = require("./config");
|
|
10
12
|
const gitFiles_1 = require("./tools/gitFiles");
|
|
@@ -70,59 +72,92 @@ async function main() {
|
|
|
70
72
|
console.error(`Registered ${tools.length} Git tools`);
|
|
71
73
|
console.error(`Registered ${resources.length} resource(s)`);
|
|
72
74
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
};
|
|
99
|
-
// Try to start server on available port
|
|
100
|
-
let server = null;
|
|
101
|
-
const maxAttempts = 10;
|
|
102
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
103
|
-
const tryPort = port + attempt;
|
|
104
|
-
server = await startServer(tryPort);
|
|
105
|
-
if (server) {
|
|
106
|
-
port = tryPort;
|
|
107
|
-
break;
|
|
75
|
+
// Create MCP Server with STDIO transport
|
|
76
|
+
const server = new index_js_1.Server({
|
|
77
|
+
name: '@andrebuzeli/git-mcp',
|
|
78
|
+
version: '6.2.1',
|
|
79
|
+
});
|
|
80
|
+
// Register tool list handler
|
|
81
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
82
|
+
return {
|
|
83
|
+
tools: tools.map(tool => ({
|
|
84
|
+
name: tool.name,
|
|
85
|
+
description: tool.description,
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {},
|
|
89
|
+
additionalProperties: true,
|
|
90
|
+
},
|
|
91
|
+
})),
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
// Register tool execution handler
|
|
95
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
96
|
+
const toolName = request.params.name;
|
|
97
|
+
const tool = tools.find(t => t.name === toolName);
|
|
98
|
+
if (!tool) {
|
|
99
|
+
throw new Error(`Tool not found: ${toolName}`);
|
|
108
100
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
101
|
+
try {
|
|
102
|
+
const result = await tool.handle(request.params.arguments ?? {}, { providerManager });
|
|
103
|
+
return {
|
|
104
|
+
content: [
|
|
105
|
+
{
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: 'text',
|
|
117
|
+
text: `Error: ${error.message || String(error)}`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
isError: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Register resource list handler
|
|
125
|
+
server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => {
|
|
126
|
+
return {
|
|
127
|
+
resources: resources.map(resource => ({
|
|
128
|
+
uri: resource.uri,
|
|
129
|
+
name: resource.name,
|
|
130
|
+
description: resource.description,
|
|
131
|
+
mimeType: resource.mimeType,
|
|
132
|
+
})),
|
|
133
|
+
};
|
|
117
134
|
});
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
135
|
+
// Register resource read handler
|
|
136
|
+
server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
|
|
137
|
+
const uri = request.params.uri;
|
|
138
|
+
const resource = resources.find(r => r.uri === uri);
|
|
139
|
+
if (!resource) {
|
|
140
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
contents: [
|
|
144
|
+
{
|
|
145
|
+
uri: resource.uri,
|
|
146
|
+
mimeType: resource.mimeType,
|
|
147
|
+
text: resource.content,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
125
151
|
});
|
|
152
|
+
// Start server with STDIO transport
|
|
153
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
154
|
+
await server.connect(transport);
|
|
155
|
+
// Only log in debug mode
|
|
156
|
+
if (process.env.DEBUG) {
|
|
157
|
+
console.error(`✅ git-mcp MCP server running via STDIO`);
|
|
158
|
+
console.error(`Tools: ${tools.length} registered`);
|
|
159
|
+
console.error(`Resources: ${resources.length} registered`);
|
|
160
|
+
}
|
|
126
161
|
}
|
|
127
162
|
main().catch(err => {
|
|
128
163
|
console.error('❌ Failed to start git-mcp:', err);
|
|
@@ -8,7 +8,6 @@ export declare class GitBranchesTool implements Tool {
|
|
|
8
8
|
branches?: undefined;
|
|
9
9
|
current?: undefined;
|
|
10
10
|
deleted?: undefined;
|
|
11
|
-
currentBranch?: undefined;
|
|
12
11
|
result?: undefined;
|
|
13
12
|
baseBranch?: undefined;
|
|
14
13
|
compareBranch?: undefined;
|
|
@@ -20,7 +19,6 @@ export declare class GitBranchesTool implements Tool {
|
|
|
20
19
|
success?: undefined;
|
|
21
20
|
branch?: undefined;
|
|
22
21
|
deleted?: undefined;
|
|
23
|
-
currentBranch?: undefined;
|
|
24
22
|
result?: undefined;
|
|
25
23
|
baseBranch?: undefined;
|
|
26
24
|
compareBranch?: undefined;
|
|
@@ -32,7 +30,6 @@ export declare class GitBranchesTool implements Tool {
|
|
|
32
30
|
current: boolean;
|
|
33
31
|
branches?: undefined;
|
|
34
32
|
deleted?: undefined;
|
|
35
|
-
currentBranch?: undefined;
|
|
36
33
|
result?: undefined;
|
|
37
34
|
baseBranch?: undefined;
|
|
38
35
|
compareBranch?: undefined;
|
|
@@ -41,7 +38,6 @@ export declare class GitBranchesTool implements Tool {
|
|
|
41
38
|
} | {
|
|
42
39
|
success: boolean;
|
|
43
40
|
deleted: any;
|
|
44
|
-
currentBranch: string;
|
|
45
41
|
branch?: undefined;
|
|
46
42
|
branches?: undefined;
|
|
47
43
|
current?: undefined;
|
|
@@ -57,7 +53,6 @@ export declare class GitBranchesTool implements Tool {
|
|
|
57
53
|
branches?: undefined;
|
|
58
54
|
current?: undefined;
|
|
59
55
|
deleted?: undefined;
|
|
60
|
-
currentBranch?: undefined;
|
|
61
56
|
baseBranch?: undefined;
|
|
62
57
|
compareBranch?: undefined;
|
|
63
58
|
commits?: undefined;
|
|
@@ -72,7 +67,6 @@ export declare class GitBranchesTool implements Tool {
|
|
|
72
67
|
branches?: undefined;
|
|
73
68
|
current?: undefined;
|
|
74
69
|
deleted?: undefined;
|
|
75
|
-
currentBranch?: undefined;
|
|
76
70
|
result?: undefined;
|
|
77
71
|
}>;
|
|
78
72
|
}
|
|
@@ -7,7 +7,6 @@ exports.GitBranchesTool = void 0;
|
|
|
7
7
|
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
8
|
const errors_1 = require("../utils/errors");
|
|
9
9
|
const safetyController_1 = require("../utils/safetyController");
|
|
10
|
-
const repoHelpers_1 = require("../utils/repoHelpers");
|
|
11
10
|
class GitBranchesTool {
|
|
12
11
|
constructor() {
|
|
13
12
|
this.name = 'git-branches';
|
|
@@ -61,17 +60,8 @@ class GitBranchesTool {
|
|
|
61
60
|
const branchName = params.branchName;
|
|
62
61
|
if (!branchName)
|
|
63
62
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'branchName is required');
|
|
64
|
-
// Safety check: prevent deleting the current active branch
|
|
65
|
-
const currentBranch = (0, repoHelpers_1.getCurrentBranch)(projectPath);
|
|
66
|
-
if (currentBranch && currentBranch === branchName) {
|
|
67
|
-
throw new errors_1.MCPError('SAFETY_ERROR', `Cannot delete the currently active branch '${branchName}'.`, [
|
|
68
|
-
'Switch to another branch first using git checkout',
|
|
69
|
-
'Example: git checkout master',
|
|
70
|
-
'Then retry the delete operation',
|
|
71
|
-
]);
|
|
72
|
-
}
|
|
73
63
|
await git.deleteLocalBranch(branchName, params.force);
|
|
74
|
-
return { success: true, deleted: branchName
|
|
64
|
+
return { success: true, deleted: branchName };
|
|
75
65
|
}
|
|
76
66
|
case 'merge': {
|
|
77
67
|
const branchName = params.branchName;
|
package/dist/tools/gitIssues.js
CHANGED
|
@@ -43,17 +43,7 @@ class GitIssuesTool {
|
|
|
43
43
|
results.providers.github = { success: true, issue: result.data };
|
|
44
44
|
}
|
|
45
45
|
catch (err) {
|
|
46
|
-
|
|
47
|
-
provider: 'github',
|
|
48
|
-
operation: 'create issue',
|
|
49
|
-
resource: `${githubOwner}/${repo}`,
|
|
50
|
-
});
|
|
51
|
-
results.providers.github = {
|
|
52
|
-
success: false,
|
|
53
|
-
error: mcpError.message,
|
|
54
|
-
code: mcpError.code,
|
|
55
|
-
suggestions: mcpError.suggestions,
|
|
56
|
-
};
|
|
46
|
+
results.providers.github = { success: false, error: err.message };
|
|
57
47
|
}
|
|
58
48
|
}
|
|
59
49
|
// Gitea
|
|
@@ -94,17 +84,7 @@ class GitIssuesTool {
|
|
|
94
84
|
results.providers.github = { success: true, issues: result.data };
|
|
95
85
|
}
|
|
96
86
|
catch (err) {
|
|
97
|
-
|
|
98
|
-
provider: 'github',
|
|
99
|
-
operation: 'list issues',
|
|
100
|
-
resource: `${githubOwner}/${repo}`,
|
|
101
|
-
});
|
|
102
|
-
results.providers.github = {
|
|
103
|
-
success: false,
|
|
104
|
-
error: mcpError.message,
|
|
105
|
-
code: mcpError.code,
|
|
106
|
-
suggestions: mcpError.suggestions,
|
|
107
|
-
};
|
|
87
|
+
results.providers.github = { success: false, error: err.message };
|
|
108
88
|
}
|
|
109
89
|
}
|
|
110
90
|
// Gitea
|
package/dist/tools/gitSync.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ export declare class GitSyncTool implements Tool {
|
|
|
6
6
|
success: boolean;
|
|
7
7
|
remote: any;
|
|
8
8
|
branch: any;
|
|
9
|
-
automated: boolean;
|
|
10
9
|
message: string;
|
|
11
10
|
status?: undefined;
|
|
12
11
|
remotes?: undefined;
|
|
@@ -21,7 +20,6 @@ export declare class GitSyncTool implements Tool {
|
|
|
21
20
|
remotes: import("simple-git").RemoteWithRefs[];
|
|
22
21
|
remote?: undefined;
|
|
23
22
|
branch?: undefined;
|
|
24
|
-
automated?: undefined;
|
|
25
23
|
message?: undefined;
|
|
26
24
|
}>;
|
|
27
25
|
}
|
package/dist/tools/gitSync.js
CHANGED
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.GitSyncTool = void 0;
|
|
7
7
|
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
8
|
const errors_1 = require("../utils/errors");
|
|
9
|
-
const repoHelpers_1 = require("../utils/repoHelpers");
|
|
10
9
|
class GitSyncTool {
|
|
11
10
|
constructor() {
|
|
12
11
|
this.name = 'git-sync';
|
|
@@ -22,20 +21,13 @@ class GitSyncTool {
|
|
|
22
21
|
switch (action) {
|
|
23
22
|
case 'sync': {
|
|
24
23
|
const remote = params.remote || 'origin';
|
|
25
|
-
|
|
26
|
-
let branch = params.branch;
|
|
27
|
-
if (!branch) {
|
|
28
|
-
branch = (0, repoHelpers_1.getCurrentBranch)(projectPath);
|
|
29
|
-
if (!branch) {
|
|
30
|
-
throw new errors_1.MCPError('VALIDATION_ERROR', 'Could not detect current branch. Please provide branch parameter.');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
24
|
+
const branch = params.branch;
|
|
33
25
|
if (branch) {
|
|
34
26
|
await git.checkout(branch);
|
|
35
27
|
}
|
|
36
28
|
await git.fetch(remote);
|
|
37
29
|
await git.pull(remote, branch);
|
|
38
|
-
return { success: true, remote, branch,
|
|
30
|
+
return { success: true, remote, branch, message: 'Repository synced' };
|
|
39
31
|
}
|
|
40
32
|
case 'status': {
|
|
41
33
|
const status = await git.status();
|
package/dist/tools/gitUpload.js
CHANGED
|
@@ -60,14 +60,7 @@ class GitUploadTool {
|
|
|
60
60
|
const repoName = params.repoName || (0, repoHelpers_1.getRepoNameFromPath)(projectPath);
|
|
61
61
|
const description = params.description || `Project uploaded via git-upload at ${new Date().toISOString()}`;
|
|
62
62
|
const isPrivate = params.private !== undefined ? params.private : true;
|
|
63
|
-
|
|
64
|
-
let branch = params.branch;
|
|
65
|
-
if (!branch) {
|
|
66
|
-
branch = (0, repoHelpers_1.getCurrentBranch)(projectPath);
|
|
67
|
-
if (!branch) {
|
|
68
|
-
branch = 'master'; // Ultimate fallback
|
|
69
|
-
}
|
|
70
|
-
}
|
|
63
|
+
const branch = params.branch || 'master';
|
|
71
64
|
const git = (0, simple_git_1.default)({ baseDir: projectPath });
|
|
72
65
|
// Resultado com rastreabilidade completa
|
|
73
66
|
const results = {
|
package/dist/utils/errors.d.ts
CHANGED
|
@@ -11,45 +11,3 @@ export declare class MCPError extends Error {
|
|
|
11
11
|
suggestions?: string[];
|
|
12
12
|
constructor(code: string, message: string, suggestions?: string[]);
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Transform GitHub/Gitea API errors into user-friendly messages
|
|
16
|
-
*/
|
|
17
|
-
export declare function handleApiError(error: any, context: {
|
|
18
|
-
provider: 'github' | 'gitea';
|
|
19
|
-
operation: string;
|
|
20
|
-
resource?: string;
|
|
21
|
-
}): MCPError;
|
|
22
|
-
/**
|
|
23
|
-
* Handle Git command errors with helpful context
|
|
24
|
-
*/
|
|
25
|
-
export declare function handleGitError(error: any, context: {
|
|
26
|
-
operation: string;
|
|
27
|
-
path: string;
|
|
28
|
-
}): MCPError;
|
|
29
|
-
/**
|
|
30
|
-
* Wrap API calls with error handling
|
|
31
|
-
*/
|
|
32
|
-
export declare function withApiErrorHandling<T>(fn: () => Promise<T>, context: {
|
|
33
|
-
provider: 'github' | 'gitea';
|
|
34
|
-
operation: string;
|
|
35
|
-
resource?: string;
|
|
36
|
-
}): Promise<{
|
|
37
|
-
success: true;
|
|
38
|
-
data: T;
|
|
39
|
-
} | {
|
|
40
|
-
success: false;
|
|
41
|
-
error: MCPError;
|
|
42
|
-
}>;
|
|
43
|
-
/**
|
|
44
|
-
* Wrap Git commands with error handling
|
|
45
|
-
*/
|
|
46
|
-
export declare function withGitErrorHandling<T>(fn: () => Promise<T>, context: {
|
|
47
|
-
operation: string;
|
|
48
|
-
path: string;
|
|
49
|
-
}): Promise<{
|
|
50
|
-
success: true;
|
|
51
|
-
data: T;
|
|
52
|
-
} | {
|
|
53
|
-
success: false;
|
|
54
|
-
error: MCPError;
|
|
55
|
-
}>;
|
package/dist/utils/errors.js
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MCPError = void 0;
|
|
4
4
|
exports.createErrorResponse = createErrorResponse;
|
|
5
|
-
exports.handleApiError = handleApiError;
|
|
6
|
-
exports.handleGitError = handleGitError;
|
|
7
|
-
exports.withApiErrorHandling = withApiErrorHandling;
|
|
8
|
-
exports.withGitErrorHandling = withGitErrorHandling;
|
|
9
5
|
function createErrorResponse(code, message, suggestions) {
|
|
10
6
|
return {
|
|
11
7
|
success: false,
|
|
@@ -24,199 +20,3 @@ class MCPError extends Error {
|
|
|
24
20
|
}
|
|
25
21
|
}
|
|
26
22
|
exports.MCPError = MCPError;
|
|
27
|
-
/**
|
|
28
|
-
* Transform GitHub/Gitea API errors into user-friendly messages
|
|
29
|
-
*/
|
|
30
|
-
function handleApiError(error, context) {
|
|
31
|
-
const { provider, operation, resource } = context;
|
|
32
|
-
// Extract status code and message
|
|
33
|
-
const status = error.status || error.response?.status;
|
|
34
|
-
const message = error.message || error.response?.data?.message || 'Unknown error';
|
|
35
|
-
// Handle common HTTP errors with context-specific messages
|
|
36
|
-
switch (status) {
|
|
37
|
-
case 401:
|
|
38
|
-
return new MCPError('AUTHENTICATION_ERROR', `❌ Authentication failed for ${provider.toUpperCase()}`, [
|
|
39
|
-
`Check if ${provider === 'github' ? 'GITHUB_TOKEN' : 'GITEA_TOKEN'} is set correctly`,
|
|
40
|
-
'Verify token has not expired',
|
|
41
|
-
`Generate a new token at: ${provider === 'github' ? 'https://github.com/settings/tokens' : 'your Gitea instance settings'}`,
|
|
42
|
-
'Ensure token has required permissions',
|
|
43
|
-
]);
|
|
44
|
-
case 403:
|
|
45
|
-
const rateLimitRemaining = error.response?.headers?.['x-ratelimit-remaining'];
|
|
46
|
-
if (rateLimitRemaining === '0') {
|
|
47
|
-
const resetTime = error.response?.headers?.['x-ratelimit-reset'];
|
|
48
|
-
const resetDate = resetTime ? new Date(parseInt(resetTime) * 1000).toLocaleString() : 'soon';
|
|
49
|
-
return new MCPError('RATE_LIMIT_EXCEEDED', `⏱️ ${provider.toUpperCase()} API rate limit exceeded`, [
|
|
50
|
-
`Rate limit will reset at: ${resetDate}`,
|
|
51
|
-
'Wait for the rate limit to reset',
|
|
52
|
-
`Consider using a different ${provider === 'github' ? 'GitHub' : 'Gitea'} account or token`,
|
|
53
|
-
'For GitHub: Upgrade to GitHub Pro for higher limits',
|
|
54
|
-
]);
|
|
55
|
-
}
|
|
56
|
-
return new MCPError('PERMISSION_DENIED', `🔒 Insufficient permissions for ${operation} on ${provider.toUpperCase()}`, [
|
|
57
|
-
`Token does not have permission to ${operation}`,
|
|
58
|
-
resource ? `Verify you have access to ${resource}` : 'Verify repository access',
|
|
59
|
-
'Check if repository is private and token has appropriate scopes',
|
|
60
|
-
`Required scopes: ${getRequiredScopes(operation, provider)}`,
|
|
61
|
-
]);
|
|
62
|
-
case 404:
|
|
63
|
-
return new MCPError('RESOURCE_NOT_FOUND', `🔍 ${resource || 'Resource'} not found on ${provider.toUpperCase()}`, [
|
|
64
|
-
resource ? `"${resource}" does not exist` : 'The requested resource does not exist',
|
|
65
|
-
'Check if the repository/issue/PR name is correct',
|
|
66
|
-
'Verify you have access to this resource',
|
|
67
|
-
provider === 'github' ? 'Check if owner (GITHUB_USERNAME) is correct' : 'Check if owner (GITEA_USERNAME) is correct',
|
|
68
|
-
'For private resources, ensure your token has access',
|
|
69
|
-
]);
|
|
70
|
-
case 409:
|
|
71
|
-
return new MCPError('CONFLICT_ERROR', `⚠️ Conflict during ${operation} on ${provider.toUpperCase()}`, [
|
|
72
|
-
'Resource already exists or is in conflicting state',
|
|
73
|
-
operation.includes('create') ? 'Try a different name or delete the existing resource' : 'Resolve conflicts first',
|
|
74
|
-
'Check if there are merge conflicts',
|
|
75
|
-
'Pull latest changes before pushing',
|
|
76
|
-
]);
|
|
77
|
-
case 422:
|
|
78
|
-
return new MCPError('VALIDATION_ERROR', `📋 Invalid data for ${operation} on ${provider.toUpperCase()}: ${message}`, [
|
|
79
|
-
'Check if all required fields are provided',
|
|
80
|
-
'Verify field formats (branch names, tags, etc.)',
|
|
81
|
-
'Branch names cannot contain spaces or special characters',
|
|
82
|
-
'Issue/PR titles must not be empty',
|
|
83
|
-
]);
|
|
84
|
-
case 500:
|
|
85
|
-
case 502:
|
|
86
|
-
case 503:
|
|
87
|
-
return new MCPError('SERVER_ERROR', `🔥 ${provider.toUpperCase()} server error during ${operation}`, [
|
|
88
|
-
`${provider === 'github' ? 'GitHub' : 'Gitea'} is experiencing issues`,
|
|
89
|
-
'Check service status: ' + (provider === 'github' ? 'https://www.githubstatus.com/' : 'your Gitea instance status'),
|
|
90
|
-
'Wait a few minutes and try again',
|
|
91
|
-
'If issue persists, the problem is on the server side',
|
|
92
|
-
]);
|
|
93
|
-
default:
|
|
94
|
-
return new MCPError('API_ERROR', `❌ ${provider.toUpperCase()} API error during ${operation}: ${message}`, [
|
|
95
|
-
`HTTP ${status || 'unknown'}: ${message}`,
|
|
96
|
-
'Check your internet connection',
|
|
97
|
-
`Verify ${provider === 'github' ? 'GITHUB_TOKEN' : 'GITEA_TOKEN'} is valid`,
|
|
98
|
-
'Check API endpoint availability',
|
|
99
|
-
]);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Get required token scopes for an operation
|
|
104
|
-
*/
|
|
105
|
-
function getRequiredScopes(operation, provider) {
|
|
106
|
-
if (provider === 'gitea') {
|
|
107
|
-
return 'write:repository, write:issue, write:user';
|
|
108
|
-
}
|
|
109
|
-
// GitHub scopes
|
|
110
|
-
if (operation.includes('delete') || operation.includes('update')) {
|
|
111
|
-
return 'repo (full control)';
|
|
112
|
-
}
|
|
113
|
-
if (operation.includes('create')) {
|
|
114
|
-
return 'repo, user';
|
|
115
|
-
}
|
|
116
|
-
if (operation.includes('read') || operation.includes('list') || operation.includes('get')) {
|
|
117
|
-
return 'repo (or public_repo for public repos)';
|
|
118
|
-
}
|
|
119
|
-
return 'repo';
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Handle Git command errors with helpful context
|
|
123
|
-
*/
|
|
124
|
-
function handleGitError(error, context) {
|
|
125
|
-
const message = error.message || String(error);
|
|
126
|
-
const { operation, path } = context;
|
|
127
|
-
// Not a git repository
|
|
128
|
-
if (message.includes('not a git repository') || message.includes('not found')) {
|
|
129
|
-
return new MCPError('NOT_A_GIT_REPOSITORY', `📁 "${path}" is not a Git repository`, [
|
|
130
|
-
'Initialize a git repository first: git init',
|
|
131
|
-
'Or provide a valid repository path',
|
|
132
|
-
'Check if .git directory exists',
|
|
133
|
-
'Verify you have permissions to access the directory',
|
|
134
|
-
]);
|
|
135
|
-
}
|
|
136
|
-
// Authentication/permission errors
|
|
137
|
-
if (message.includes('Permission denied') || message.includes('authentication failed')) {
|
|
138
|
-
return new MCPError('GIT_AUTH_ERROR', `🔐 Git authentication failed during ${operation}`, [
|
|
139
|
-
'Check if SSH keys are configured correctly',
|
|
140
|
-
'For HTTPS: verify credentials are correct',
|
|
141
|
-
'For SSH: ensure ssh-agent is running',
|
|
142
|
-
'Test connection: git ls-remote <remote-url>',
|
|
143
|
-
]);
|
|
144
|
-
}
|
|
145
|
-
// Remote errors
|
|
146
|
-
if (message.includes('remote') || message.includes('fetch') || message.includes('push')) {
|
|
147
|
-
if (message.includes('does not appear to be a git repository')) {
|
|
148
|
-
return new MCPError('INVALID_REMOTE', `🌐 Remote repository URL is invalid or inaccessible`, [
|
|
149
|
-
'Check if remote URL is correct',
|
|
150
|
-
'Verify repository exists on remote',
|
|
151
|
-
'For private repos: ensure authentication is configured',
|
|
152
|
-
'List remotes: git remote -v',
|
|
153
|
-
]);
|
|
154
|
-
}
|
|
155
|
-
if (message.includes('rejected') || message.includes('non-fast-forward')) {
|
|
156
|
-
return new MCPError('PUSH_REJECTED', `⚠️ Push rejected - remote has changes you don't have`, [
|
|
157
|
-
'Pull latest changes first: git pull',
|
|
158
|
-
'Resolve any merge conflicts',
|
|
159
|
-
'Then try pushing again',
|
|
160
|
-
'Or force push (dangerous): use with caution',
|
|
161
|
-
]);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// Merge conflicts
|
|
165
|
-
if (message.includes('conflict') || message.includes('CONFLICT')) {
|
|
166
|
-
return new MCPError('MERGE_CONFLICT', `⚔️ Merge conflict detected during ${operation}`, [
|
|
167
|
-
'Resolve conflicts in affected files',
|
|
168
|
-
'Stage resolved files: git add <file>',
|
|
169
|
-
'Complete merge: git commit',
|
|
170
|
-
'Or abort: git merge --abort',
|
|
171
|
-
]);
|
|
172
|
-
}
|
|
173
|
-
// Nothing to commit
|
|
174
|
-
if (message.includes('nothing to commit') || message.includes('no changes')) {
|
|
175
|
-
return new MCPError('NOTHING_TO_COMMIT', `✅ No changes to commit - working tree is clean`, [
|
|
176
|
-
'All changes are already committed',
|
|
177
|
-
'Check status: git status',
|
|
178
|
-
'Make changes before committing',
|
|
179
|
-
'Or use --allow-empty to create empty commit',
|
|
180
|
-
]);
|
|
181
|
-
}
|
|
182
|
-
// Branch errors
|
|
183
|
-
if (message.includes('branch') && (message.includes('not found') || message.includes('does not exist'))) {
|
|
184
|
-
return new MCPError('BRANCH_NOT_FOUND', `🌿 Branch not found during ${operation}`, [
|
|
185
|
-
'List available branches: git branch -a',
|
|
186
|
-
'Create the branch first if needed',
|
|
187
|
-
'Check for typos in branch name',
|
|
188
|
-
'Fetch from remote: git fetch',
|
|
189
|
-
]);
|
|
190
|
-
}
|
|
191
|
-
// Generic git error
|
|
192
|
-
return new MCPError('GIT_ERROR', `❌ Git error during ${operation}: ${message}`, [
|
|
193
|
-
'Check git command output for details',
|
|
194
|
-
'Verify repository state is clean',
|
|
195
|
-
'Try: git status',
|
|
196
|
-
'For more info: git --help',
|
|
197
|
-
]);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Wrap API calls with error handling
|
|
201
|
-
*/
|
|
202
|
-
async function withApiErrorHandling(fn, context) {
|
|
203
|
-
try {
|
|
204
|
-
const data = await fn();
|
|
205
|
-
return { success: true, data };
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
return { success: false, error: handleApiError(error, context) };
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Wrap Git commands with error handling
|
|
213
|
-
*/
|
|
214
|
-
async function withGitErrorHandling(fn, context) {
|
|
215
|
-
try {
|
|
216
|
-
const data = await fn();
|
|
217
|
-
return { success: true, data };
|
|
218
|
-
}
|
|
219
|
-
catch (error) {
|
|
220
|
-
return { success: false, error: handleGitError(error, context) };
|
|
221
|
-
}
|
|
222
|
-
}
|
|
@@ -6,16 +6,6 @@
|
|
|
6
6
|
* Normalizes: spaces to hyphens, lowercase, keeps alphanumeric and hyphens/underscores
|
|
7
7
|
*/
|
|
8
8
|
export declare function getRepoNameFromPath(projectPath: string): string;
|
|
9
|
-
/**
|
|
10
|
-
* Get current Git branch from project path
|
|
11
|
-
* Returns empty string if not in a Git repository
|
|
12
|
-
*/
|
|
13
|
-
export declare function getCurrentBranch(projectPath: string): string;
|
|
14
|
-
/**
|
|
15
|
-
* Get default branch from Git repository (usually 'main' or 'master')
|
|
16
|
-
* Falls back to 'master' if unable to determine
|
|
17
|
-
*/
|
|
18
|
-
export declare function getDefaultBranch(projectPath: string): string;
|
|
19
9
|
/**
|
|
20
10
|
* Get GitHub owner from environment
|
|
21
11
|
*/
|
|
@@ -4,8 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getRepoNameFromPath = getRepoNameFromPath;
|
|
7
|
-
exports.getCurrentBranch = getCurrentBranch;
|
|
8
|
-
exports.getDefaultBranch = getDefaultBranch;
|
|
9
7
|
exports.getGitHubOwner = getGitHubOwner;
|
|
10
8
|
exports.getGiteaOwner = getGiteaOwner;
|
|
11
9
|
exports.getGiteaUrl = getGiteaUrl;
|
|
@@ -13,7 +11,6 @@ exports.buildGitHubUrl = buildGitHubUrl;
|
|
|
13
11
|
exports.buildGiteaUrl = buildGiteaUrl;
|
|
14
12
|
exports.getRepoInfo = getRepoInfo;
|
|
15
13
|
const path_1 = __importDefault(require("path"));
|
|
16
|
-
const child_process_1 = require("child_process");
|
|
17
14
|
/**
|
|
18
15
|
* Helper functions to extract repository information automatically
|
|
19
16
|
*/
|
|
@@ -27,55 +24,6 @@ function getRepoNameFromPath(projectPath) {
|
|
|
27
24
|
.replace(/\s+/g, '-')
|
|
28
25
|
.replace(/[^a-z0-9-_]/g, '');
|
|
29
26
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Get current Git branch from project path
|
|
32
|
-
* Returns empty string if not in a Git repository
|
|
33
|
-
*/
|
|
34
|
-
function getCurrentBranch(projectPath) {
|
|
35
|
-
try {
|
|
36
|
-
const branchName = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
|
|
37
|
-
cwd: projectPath,
|
|
38
|
-
encoding: 'utf8',
|
|
39
|
-
stdio: ['pipe', 'pipe', 'ignore'], // Suppress stderr
|
|
40
|
-
});
|
|
41
|
-
return branchName.trim();
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
return '';
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Get default branch from Git repository (usually 'main' or 'master')
|
|
49
|
-
* Falls back to 'master' if unable to determine
|
|
50
|
-
*/
|
|
51
|
-
function getDefaultBranch(projectPath) {
|
|
52
|
-
try {
|
|
53
|
-
// Try to get default branch from remote
|
|
54
|
-
const defaultBranch = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD', {
|
|
55
|
-
cwd: projectPath,
|
|
56
|
-
encoding: 'utf8',
|
|
57
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
58
|
-
});
|
|
59
|
-
return defaultBranch.trim().replace('refs/remotes/origin/', '');
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
// If that fails, try to detect main or master
|
|
63
|
-
try {
|
|
64
|
-
const branches = (0, child_process_1.execSync)('git branch -r', {
|
|
65
|
-
cwd: projectPath,
|
|
66
|
-
encoding: 'utf8',
|
|
67
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
68
|
-
});
|
|
69
|
-
if (branches.includes('origin/main'))
|
|
70
|
-
return 'main';
|
|
71
|
-
if (branches.includes('origin/master'))
|
|
72
|
-
return 'master';
|
|
73
|
-
}
|
|
74
|
-
catch { }
|
|
75
|
-
// Ultimate fallback
|
|
76
|
-
return 'master';
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
27
|
/**
|
|
80
28
|
* Get GitHub owner from environment
|
|
81
29
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "6.2.
|
|
4
|
-
"description": "Professional MCP server for Git operations -
|
|
3
|
+
"version": "6.2.1",
|
|
4
|
+
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop). Fully autonomous with intelligent error handling. Auto-detects branches, owner, repo. User-friendly error messages. Dual-provider execution (GitHub + Gitea)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|