@andrebuzeli/git-mcp 10.0.7 → 10.0.9
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/LICENSE +0 -0
- package/README.md +1 -1
- package/dist/providers/providerManager.js +2 -4
- package/dist/scripts/test_e2e.d.ts +1 -0
- package/dist/scripts/test_e2e.js +199 -0
- package/dist/scripts/test_exhaustive.d.ts +1 -0
- package/dist/scripts/test_exhaustive.js +275 -0
- package/dist/scripts/test_gitea_creation.d.ts +1 -0
- package/dist/scripts/test_gitea_creation.js +116 -0
- package/dist/scripts/verify_setup.d.ts +1 -0
- package/dist/scripts/verify_setup.js +61 -0
- package/dist/tools/gitAnalytics.js +3 -3
- package/dist/tools/gitBackup.d.ts +2 -2
- package/dist/tools/gitBackup.js +5 -5
- package/dist/tools/gitBranches.js +40 -29
- package/dist/tools/gitConfig.d.ts +2 -2
- package/dist/tools/gitConfig.js +12 -13
- package/dist/tools/gitFix.d.ts +2 -1
- package/dist/tools/gitFix.js +20 -24
- package/dist/tools/gitFix.tool.d.ts +2 -2
- package/dist/tools/gitFix.tool.js +2 -2
- package/dist/tools/gitHistory.js +4 -5
- package/dist/tools/gitIssues.js +8 -8
- package/dist/tools/gitMonitor.js +28 -33
- package/dist/tools/gitPulls.js +8 -8
- package/dist/tools/gitRelease.js +5 -6
- package/dist/tools/gitRemote.d.ts +1 -11
- package/dist/tools/gitRemote.js +14 -20
- package/dist/tools/gitReset.js +6 -7
- package/dist/tools/gitStash.d.ts +1 -12
- package/dist/tools/gitStash.js +11 -22
- package/dist/tools/gitSync.js +44 -36
- package/dist/tools/gitTags.d.ts +8 -0
- package/dist/tools/gitTags.js +44 -34
- package/dist/tools/gitUpdate.d.ts +27 -0
- package/dist/tools/gitUpdate.js +61 -34
- package/dist/tools/gitUpload.js +29 -44
- package/dist/tools/gitWorkflow.js +27 -46
- package/dist/utils/gitAdapter.js +10 -0
- package/dist/utils/repoHelpers.js +7 -12
- package/package.json +2 -3
- package/dist/server-new.d.ts +0 -2
- package/dist/server-new.js +0 -224
- package/dist/tools/gitFiles-new.d.ts +0 -89
- package/dist/tools/gitFiles-new.js +0 -335
package/dist/tools/gitUpload.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
1
|
import { MCPError } from '../utils/errors.js';
|
|
3
2
|
import axios from 'axios';
|
|
4
3
|
import * as fs from 'fs/promises';
|
|
@@ -42,11 +41,13 @@ export class GitUploadTool {
|
|
|
42
41
|
if (!projectPath) {
|
|
43
42
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
44
43
|
}
|
|
44
|
+
if (!ctx.gitAdapter) {
|
|
45
|
+
throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
|
|
46
|
+
}
|
|
45
47
|
const repoName = params.repoName || getRepoNameFromPath(projectPath);
|
|
46
48
|
const description = params.description || `Project uploaded via git-upload at ${new Date().toISOString()}`;
|
|
47
49
|
const isPrivate = params.private !== undefined ? params.private : true;
|
|
48
50
|
const branch = params.branch || 'master';
|
|
49
|
-
const git = simpleGit({ baseDir: projectPath });
|
|
50
51
|
// Resultado com rastreabilidade completa
|
|
51
52
|
const results = {
|
|
52
53
|
success: true,
|
|
@@ -64,20 +65,16 @@ export class GitUploadTool {
|
|
|
64
65
|
try {
|
|
65
66
|
// 0. Verificar se é um repositório Git, se não for, inicializar
|
|
66
67
|
try {
|
|
67
|
-
await
|
|
68
|
+
await ctx.gitAdapter.status(projectPath);
|
|
68
69
|
}
|
|
69
70
|
catch (err) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
throw err;
|
|
80
|
-
}
|
|
71
|
+
// If status fails, assume not a git repo or other error
|
|
72
|
+
results.traceability.uploadSteps.push({
|
|
73
|
+
step: 0,
|
|
74
|
+
action: 'init_git_repository',
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
});
|
|
77
|
+
await ctx.gitAdapter.init(projectPath, branch);
|
|
81
78
|
}
|
|
82
79
|
// 1. Verificar status local
|
|
83
80
|
results.traceability.uploadSteps.push({
|
|
@@ -85,7 +82,7 @@ export class GitUploadTool {
|
|
|
85
82
|
action: 'check_local_status',
|
|
86
83
|
timestamp: new Date().toISOString(),
|
|
87
84
|
});
|
|
88
|
-
const status = await
|
|
85
|
+
const status = await ctx.gitAdapter.status(projectPath);
|
|
89
86
|
results.traceability.localChanges = {
|
|
90
87
|
modified: status.modified,
|
|
91
88
|
created: status.created,
|
|
@@ -93,17 +90,17 @@ export class GitUploadTool {
|
|
|
93
90
|
renamed: status.renamed,
|
|
94
91
|
staged: status.staged,
|
|
95
92
|
conflicted: status.conflicted,
|
|
96
|
-
isClean: status.isClean
|
|
93
|
+
isClean: status.isClean,
|
|
97
94
|
};
|
|
98
95
|
// 2. Verificar se tem mudanças
|
|
99
|
-
if (!status.isClean
|
|
96
|
+
if (!status.isClean) {
|
|
100
97
|
results.traceability.uploadSteps.push({
|
|
101
98
|
step: 2,
|
|
102
99
|
action: 'stage_all_changes',
|
|
103
100
|
timestamp: new Date().toISOString(),
|
|
104
101
|
files: [...status.modified, ...status.created, ...status.deleted],
|
|
105
102
|
});
|
|
106
|
-
await
|
|
103
|
+
await ctx.gitAdapter.add(projectPath, ['.']);
|
|
107
104
|
}
|
|
108
105
|
// 3. Commit com rastreabilidade
|
|
109
106
|
const commitMessage = params.commitMessage || `[git-upload] Upload project at ${new Date().toISOString()}`;
|
|
@@ -115,7 +112,13 @@ export class GitUploadTool {
|
|
|
115
112
|
});
|
|
116
113
|
let commitResult;
|
|
117
114
|
try {
|
|
118
|
-
|
|
115
|
+
const sha = await ctx.gitAdapter.commit(projectPath, commitMessage);
|
|
116
|
+
commitResult = {
|
|
117
|
+
commit: sha,
|
|
118
|
+
summary: { changes: 0, insertions: 0, deletions: 0 },
|
|
119
|
+
branch: await ctx.gitAdapter.getCurrentBranch(projectPath),
|
|
120
|
+
author: { name: 'unknown', email: 'unknown' }
|
|
121
|
+
};
|
|
119
122
|
results.traceability.commit = {
|
|
120
123
|
hash: commitResult.commit,
|
|
121
124
|
summary: commitResult.summary,
|
|
@@ -124,7 +127,7 @@ export class GitUploadTool {
|
|
|
124
127
|
};
|
|
125
128
|
}
|
|
126
129
|
catch (err) {
|
|
127
|
-
if (err.message.includes('nothing to commit')) {
|
|
130
|
+
if (err.message.includes('nothing to commit') || (status.isClean && status.staged.length === 0)) {
|
|
128
131
|
results.traceability.commit = { message: 'No changes to commit' };
|
|
129
132
|
}
|
|
130
133
|
else {
|
|
@@ -174,12 +177,12 @@ export class GitUploadTool {
|
|
|
174
177
|
};
|
|
175
178
|
}
|
|
176
179
|
// Adicionar remote se necessário (URL LIMPA sem token)
|
|
177
|
-
const remotes = await
|
|
180
|
+
const remotes = await ctx.gitAdapter.listRemotes(projectPath);
|
|
178
181
|
const githubRemote = remotes.find(r => r.name === 'github');
|
|
179
182
|
if (!githubRemote) {
|
|
180
183
|
// Usar URL limpa sem token para segurança
|
|
181
184
|
const remoteUrl = `https://github.com/${githubOwner}/${repoName}.git`;
|
|
182
|
-
await
|
|
185
|
+
await ctx.gitAdapter.addRemote(projectPath, 'github', remoteUrl);
|
|
183
186
|
results.traceability.uploadSteps.push({
|
|
184
187
|
step: 5,
|
|
185
188
|
action: 'add_github_remote',
|
|
@@ -195,19 +198,12 @@ export class GitUploadTool {
|
|
|
195
198
|
branch,
|
|
196
199
|
});
|
|
197
200
|
try {
|
|
198
|
-
// Configurar credential helper temporário para este push
|
|
199
|
-
await git.addConfig('credential.https://github.com.helper', 'store');
|
|
200
|
-
await git.addConfig(`credential.https://github.com.username`, githubOwner);
|
|
201
201
|
// Push com --set-upstream mas SEM --force (segurança)
|
|
202
|
-
await
|
|
203
|
-
// Limpar credential helper após push
|
|
204
|
-
await git.addConfig('credential.https://github.com.helper', '');
|
|
202
|
+
await ctx.gitAdapter.push(projectPath, 'github', branch, false, true);
|
|
205
203
|
results.providers.github.pushed = true;
|
|
206
204
|
results.providers.github.pushStatus = 'success';
|
|
207
205
|
}
|
|
208
206
|
catch (pushErr) {
|
|
209
|
-
// Limpar credential helper em caso de erro
|
|
210
|
-
await git.addConfig('credential.https://github.com.helper', '');
|
|
211
207
|
results.providers.github.pushed = false;
|
|
212
208
|
results.providers.github.pushError = pushErr.message;
|
|
213
209
|
results.traceability.errors.push({
|
|
@@ -271,14 +267,14 @@ export class GitUploadTool {
|
|
|
271
267
|
};
|
|
272
268
|
}
|
|
273
269
|
// Adicionar remote se necessário (URL LIMPA sem token)
|
|
274
|
-
const remotes = await
|
|
270
|
+
const remotes = await ctx.gitAdapter.listRemotes(projectPath);
|
|
275
271
|
const giteaRemote = remotes.find(r => r.name === 'gitea');
|
|
276
272
|
if (!giteaRemote) {
|
|
277
273
|
// Usar URL limpa sem token para segurança
|
|
278
274
|
const baseUrl = ctx.providerManager.giteaBaseUrl;
|
|
279
275
|
const cleanUrl = baseUrl.replace(/\/$/, ''); // Remover barra final se houver
|
|
280
276
|
const remoteUrl = `${cleanUrl}/${giteaOwner}/${repoName}.git`;
|
|
281
|
-
await
|
|
277
|
+
await ctx.gitAdapter.addRemote(projectPath, 'gitea', remoteUrl);
|
|
282
278
|
results.traceability.uploadSteps.push({
|
|
283
279
|
step: 8,
|
|
284
280
|
action: 'add_gitea_remote',
|
|
@@ -294,23 +290,12 @@ export class GitUploadTool {
|
|
|
294
290
|
branch,
|
|
295
291
|
});
|
|
296
292
|
try {
|
|
297
|
-
const baseUrl = ctx.providerManager.giteaBaseUrl;
|
|
298
|
-
const cleanUrl = baseUrl.replace(/\/$/, '');
|
|
299
|
-
// Configurar credential helper temporário
|
|
300
|
-
await git.addConfig(`credential.${cleanUrl}.helper`, 'store');
|
|
301
|
-
await git.addConfig(`credential.${cleanUrl}.username`, giteaOwner);
|
|
302
293
|
// Push com --set-upstream mas SEM --force (segurança)
|
|
303
|
-
await
|
|
304
|
-
// Limpar credential helper após push
|
|
305
|
-
await git.addConfig(`credential.${cleanUrl}.helper`, '');
|
|
294
|
+
await ctx.gitAdapter.push(projectPath, 'gitea', branch, false, true);
|
|
306
295
|
results.providers.gitea.pushed = true;
|
|
307
296
|
results.providers.gitea.pushStatus = 'success';
|
|
308
297
|
}
|
|
309
298
|
catch (pushErr) {
|
|
310
|
-
// Limpar credential helper em caso de erro
|
|
311
|
-
const baseUrl = ctx.providerManager.giteaBaseUrl;
|
|
312
|
-
const cleanUrl = baseUrl.replace(/\/$/, '');
|
|
313
|
-
await git.addConfig(`credential.${cleanUrl}.helper`, '');
|
|
314
299
|
results.providers.gitea.pushed = false;
|
|
315
300
|
results.providers.gitea.pushError = pushErr.message;
|
|
316
301
|
results.traceability.errors.push({
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
1
|
import { MCPError } from '../utils/errors.js';
|
|
3
2
|
import { normalizeToolParams } from '../utils/repoHelpers.js';
|
|
4
3
|
import axios from 'axios';
|
|
@@ -123,7 +122,6 @@ export class GitWorkflowTool {
|
|
|
123
122
|
if (!action)
|
|
124
123
|
throw new MCPError('VALIDATION_ERROR', 'action is required. You can use either "action" or "command" parameter.');
|
|
125
124
|
const projectPath = params.projectPath;
|
|
126
|
-
const git = projectPath ? simpleGit({ baseDir: projectPath }) : null;
|
|
127
125
|
// Validação de path para segurança
|
|
128
126
|
if (projectPath) {
|
|
129
127
|
const path = await import('path');
|
|
@@ -133,21 +131,24 @@ export class GitWorkflowTool {
|
|
|
133
131
|
throw new MCPError('VALIDATION_ERROR', 'Invalid project path provided');
|
|
134
132
|
}
|
|
135
133
|
}
|
|
134
|
+
if (!ctx.gitAdapter) {
|
|
135
|
+
throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
|
|
136
|
+
}
|
|
136
137
|
switch (action) {
|
|
137
138
|
case 'init': {
|
|
138
|
-
if (!projectPath
|
|
139
|
+
if (!projectPath)
|
|
139
140
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required for init');
|
|
140
|
-
await
|
|
141
|
+
await ctx.gitAdapter.init(projectPath);
|
|
141
142
|
return { success: true, path: projectPath };
|
|
142
143
|
}
|
|
143
144
|
case 'status': {
|
|
144
|
-
if (!projectPath
|
|
145
|
+
if (!projectPath)
|
|
145
146
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required for status');
|
|
146
|
-
const status = await
|
|
147
|
+
const status = await ctx.gitAdapter.status(projectPath);
|
|
147
148
|
return { success: true, status };
|
|
148
149
|
}
|
|
149
150
|
case 'commit': {
|
|
150
|
-
if (!projectPath
|
|
151
|
+
if (!projectPath)
|
|
151
152
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required for commit');
|
|
152
153
|
const message = params.message;
|
|
153
154
|
if (!message)
|
|
@@ -162,40 +163,26 @@ export class GitWorkflowTool {
|
|
|
162
163
|
files = [files];
|
|
163
164
|
}
|
|
164
165
|
try {
|
|
165
|
-
await
|
|
166
|
+
await ctx.gitAdapter.add(projectPath, files);
|
|
166
167
|
}
|
|
167
168
|
catch (addErr) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
for (const file of files) {
|
|
171
|
-
try {
|
|
172
|
-
await git.add(file);
|
|
173
|
-
}
|
|
174
|
-
catch (singleErr) {
|
|
175
|
-
console.warn(`Failed to add ${file}: ${singleErr.message}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
throw addErr;
|
|
181
|
-
}
|
|
169
|
+
console.warn(`Failed to add files: ${addErr.message}`);
|
|
170
|
+
throw addErr;
|
|
182
171
|
}
|
|
183
|
-
const result = await
|
|
172
|
+
const result = await ctx.gitAdapter.commit(projectPath, message);
|
|
184
173
|
return { success: true, commit: result };
|
|
185
174
|
}
|
|
186
175
|
case 'sync': {
|
|
187
|
-
if (!projectPath
|
|
176
|
+
if (!projectPath)
|
|
188
177
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required for sync');
|
|
189
178
|
const remote = params.remote || 'origin';
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
await git.pull(remote, branch);
|
|
179
|
+
let branch = params.branch;
|
|
180
|
+
if (!branch) {
|
|
181
|
+
branch = await ctx.gitAdapter.getCurrentBranch(projectPath);
|
|
194
182
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
await git.push();
|
|
183
|
+
await ctx.gitAdapter.fetch(projectPath, remote);
|
|
184
|
+
await ctx.gitAdapter.pull(projectPath, remote, branch);
|
|
185
|
+
await ctx.gitAdapter.push(projectPath, remote, branch);
|
|
199
186
|
return { success: true, message: 'Repository synced' };
|
|
200
187
|
}
|
|
201
188
|
case 'backup': {
|
|
@@ -445,10 +432,10 @@ export class GitWorkflowTool {
|
|
|
445
432
|
return results;
|
|
446
433
|
}
|
|
447
434
|
case 'push': {
|
|
448
|
-
if (!projectPath
|
|
435
|
+
if (!projectPath)
|
|
449
436
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required for push');
|
|
450
437
|
const remote = params.remote || 'origin';
|
|
451
|
-
|
|
438
|
+
let branch = params.branch;
|
|
452
439
|
const force = params.force || false;
|
|
453
440
|
const setUpstream = params.setUpstream !== undefined ? params.setUpstream : true;
|
|
454
441
|
// Segurança: verificar confirmação para operações destrutivas
|
|
@@ -460,24 +447,18 @@ export class GitWorkflowTool {
|
|
|
460
447
|
'If you must force push, ensure you understand the consequences.',
|
|
461
448
|
]);
|
|
462
449
|
}
|
|
463
|
-
const pushOptions = [];
|
|
464
|
-
if (force)
|
|
465
|
-
pushOptions.push('--force');
|
|
466
|
-
if (setUpstream)
|
|
467
|
-
pushOptions.push('--set-upstream');
|
|
468
450
|
try {
|
|
469
|
-
|
|
451
|
+
if (!branch) {
|
|
452
|
+
branch = await ctx.gitAdapter.getCurrentBranch(projectPath);
|
|
453
|
+
}
|
|
454
|
+
await ctx.gitAdapter.push(projectPath, remote, branch, force, setUpstream);
|
|
470
455
|
return {
|
|
471
456
|
success: true,
|
|
472
457
|
remote,
|
|
473
|
-
branch
|
|
458
|
+
branch,
|
|
474
459
|
force,
|
|
475
460
|
setUpstream,
|
|
476
|
-
|
|
477
|
-
pushed: pushResult.pushed || [],
|
|
478
|
-
remoteMessages: pushResult.remoteMessages?.all || [],
|
|
479
|
-
update: pushResult.update,
|
|
480
|
-
},
|
|
461
|
+
message: 'Push successful'
|
|
481
462
|
};
|
|
482
463
|
}
|
|
483
464
|
catch (err) {
|
package/dist/utils/gitAdapter.js
CHANGED
|
@@ -251,6 +251,15 @@ export class IsomorphicGitAdapter {
|
|
|
251
251
|
// ========================================================================
|
|
252
252
|
async commit(dir, message, providedAuthor) {
|
|
253
253
|
const author = await this.getAuthor(dir, providedAuthor);
|
|
254
|
+
// Check if HEAD exists to determine if this is the initial commit
|
|
255
|
+
let parent;
|
|
256
|
+
try {
|
|
257
|
+
await git.resolveRef({ fs, dir, ref: 'HEAD' });
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
// HEAD not found, this is the first commit
|
|
261
|
+
parent = [];
|
|
262
|
+
}
|
|
254
263
|
const sha = await git.commit({
|
|
255
264
|
fs,
|
|
256
265
|
dir,
|
|
@@ -261,6 +270,7 @@ export class IsomorphicGitAdapter {
|
|
|
261
270
|
timestamp: author.timestamp,
|
|
262
271
|
timezoneOffset: author.timezoneOffset,
|
|
263
272
|
},
|
|
273
|
+
parent,
|
|
264
274
|
});
|
|
265
275
|
return sha;
|
|
266
276
|
}
|
|
@@ -28,12 +28,7 @@ export function getGiteaOwner() {
|
|
|
28
28
|
* Get Gitea base URL from environment
|
|
29
29
|
*/
|
|
30
30
|
export function getGiteaUrl() {
|
|
31
|
-
|
|
32
|
-
if (!raw)
|
|
33
|
-
return undefined;
|
|
34
|
-
// Trim whitespace and strip accidental surrounding quotes/backticks
|
|
35
|
-
const trimmed = raw.trim().replace(/^['"`]+|['"`]+$/g, '');
|
|
36
|
-
return trimmed;
|
|
31
|
+
return process.env.GITEA_URL;
|
|
37
32
|
}
|
|
38
33
|
/**
|
|
39
34
|
* Build GitHub repository URL
|
|
@@ -50,12 +45,12 @@ export function buildGiteaUrl(owner, repo) {
|
|
|
50
45
|
if (!baseUrl) {
|
|
51
46
|
throw new Error('GITEA_URL not configured');
|
|
52
47
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
return
|
|
48
|
+
if (!token) {
|
|
49
|
+
throw new Error('GITEA_TOKEN not configured');
|
|
50
|
+
}
|
|
51
|
+
// Remove protocol from baseUrl
|
|
52
|
+
const urlWithoutProtocol = baseUrl.replace(/^https?:\/\//, '');
|
|
53
|
+
return `http://${owner}:${token}@${urlWithoutProtocol}/${owner}/${repo}.git`;
|
|
59
54
|
}
|
|
60
55
|
/**
|
|
61
56
|
* Get repository info from project path and environment
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop, Trae AI, Kiro.dev). Fully autonomous DUAL execution (GitHub + Gitea APIs) with automatic username detection. Smart parameter normalization handles both 'action' and 'command' formats. All tools execute on BOTH providers simultaneously. No manual parameters needed.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -59,8 +59,7 @@
|
|
|
59
59
|
"axios": "^1.6.0",
|
|
60
60
|
"body-parser": "^1.20.2",
|
|
61
61
|
"express": "^4.18.2",
|
|
62
|
-
"isomorphic-git": "^1.34.0"
|
|
63
|
-
"simple-git": "^3.20.0"
|
|
62
|
+
"isomorphic-git": "^1.34.0"
|
|
64
63
|
},
|
|
65
64
|
"devDependencies": {
|
|
66
65
|
"@semantic-release/commit-analyzer": "^10.0.0",
|
package/dist/server-new.d.ts
DELETED
package/dist/server-new.js
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
-
import { ProviderManager } from './providers/providerManager.js';
|
|
6
|
-
import { MCPError } from './utils/errors.js';
|
|
7
|
-
import { IsomorphicGitAdapter } from './utils/gitAdapter.js';
|
|
8
|
-
import { GitFilesTool } from './tools/gitFiles.js';
|
|
9
|
-
import { GitWorkflowTool } from './tools/gitWorkflow.js';
|
|
10
|
-
import { GitBranchesTool } from './tools/gitBranches.js';
|
|
11
|
-
import { GitIssuesTool } from './tools/gitIssues.js';
|
|
12
|
-
import { GitPullsTool } from './tools/gitPulls.js';
|
|
13
|
-
import { GitTagsTool } from './tools/gitTags.js';
|
|
14
|
-
import { GitReleaseTool } from './tools/gitRelease.js';
|
|
15
|
-
import { GitRemoteTool } from './tools/gitRemote.js';
|
|
16
|
-
import { GitResetTool } from './tools/gitReset.js';
|
|
17
|
-
import { GitStashTool } from './tools/gitStash.js';
|
|
18
|
-
import { GitConfigTool } from './tools/gitConfig.js';
|
|
19
|
-
import { GitMonitorTool } from './tools/gitMonitor.js';
|
|
20
|
-
import { GitBackupTool } from './tools/gitBackup.js';
|
|
21
|
-
import { GitArchiveTool } from './tools/gitArchive.js';
|
|
22
|
-
import { GitSyncTool } from './tools/gitSync.js';
|
|
23
|
-
import { GitPackagesTool } from './tools/gitPackages.js';
|
|
24
|
-
import { GitAnalyticsTool } from './tools/gitAnalytics.js';
|
|
25
|
-
import { GitUploadTool } from './tools/gitUpload.js';
|
|
26
|
-
import { GitUpdateTool } from './tools/gitUpdate.js';
|
|
27
|
-
import { GitHistoryTool } from './tools/gitHistory.js';
|
|
28
|
-
import { GitFixTool } from './tools/gitFix.tool.js';
|
|
29
|
-
import { GitIgnoreTool } from './tools/gitIgnore.js';
|
|
30
|
-
import { GIT_PROMPTS } from './prompts/gitPrompts.js';
|
|
31
|
-
import TOOLS_GUIDE from './resources/toolsGuide.js';
|
|
32
|
-
import { Logger, logToolExecution, logSecurityEvent, logPerformanceMetric } from './utils/logger.js';
|
|
33
|
-
async function main() {
|
|
34
|
-
const providerManager = new ProviderManager();
|
|
35
|
-
const gitAdapter = new IsomorphicGitAdapter(providerManager);
|
|
36
|
-
const logger = Logger.getInstance();
|
|
37
|
-
// Skip validation on startup to prevent hanging (validation happens on first use)
|
|
38
|
-
// Provider validation moved to lazy initialization
|
|
39
|
-
// Register all 22 Git tools
|
|
40
|
-
const tools = [
|
|
41
|
-
new GitWorkflowTool(),
|
|
42
|
-
new GitFilesTool(),
|
|
43
|
-
new GitBranchesTool(),
|
|
44
|
-
new GitIssuesTool(),
|
|
45
|
-
new GitPullsTool(),
|
|
46
|
-
new GitTagsTool(),
|
|
47
|
-
new GitReleaseTool(),
|
|
48
|
-
new GitRemoteTool(),
|
|
49
|
-
new GitResetTool(),
|
|
50
|
-
new GitStashTool(),
|
|
51
|
-
new GitConfigTool(),
|
|
52
|
-
new GitMonitorTool(),
|
|
53
|
-
new GitBackupTool(),
|
|
54
|
-
new GitArchiveTool(),
|
|
55
|
-
new GitSyncTool(),
|
|
56
|
-
new GitPackagesTool(),
|
|
57
|
-
new GitAnalyticsTool(),
|
|
58
|
-
new GitUploadTool(),
|
|
59
|
-
new GitUpdateTool(),
|
|
60
|
-
new GitHistoryTool(),
|
|
61
|
-
new GitFixTool(),
|
|
62
|
-
new GitIgnoreTool(),
|
|
63
|
-
];
|
|
64
|
-
// Register resources
|
|
65
|
-
const resources = [
|
|
66
|
-
TOOLS_GUIDE
|
|
67
|
-
];
|
|
68
|
-
// Silent mode for MCP clients - only log to stderr in debug mode
|
|
69
|
-
if (process.env.DEBUG) {
|
|
70
|
-
logger.info(`Registered ${tools.length} Git tools`);
|
|
71
|
-
logger.info(`Registered ${resources.length} resource(s)`);
|
|
72
|
-
logger.info(`Registered ${GIT_PROMPTS.length} prompt(s)`);
|
|
73
|
-
}
|
|
74
|
-
// Create MCP Server with STDIO transport
|
|
75
|
-
const server = new Server({
|
|
76
|
-
name: '@andrebuzeli/git-mcp',
|
|
77
|
-
version: '10.0.4',
|
|
78
|
-
});
|
|
79
|
-
// Register tool list handler
|
|
80
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
81
|
-
logger.debug('Listing available tools');
|
|
82
|
-
return {
|
|
83
|
-
tools: tools.map(tool => ({
|
|
84
|
-
name: tool.name,
|
|
85
|
-
description: tool.description,
|
|
86
|
-
inputSchema: tool.inputSchema || {
|
|
87
|
-
type: 'object',
|
|
88
|
-
properties: {},
|
|
89
|
-
additionalProperties: true,
|
|
90
|
-
},
|
|
91
|
-
})),
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
// Register tool execution handler with logging e segurança aprimorados
|
|
95
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
96
|
-
const toolName = request.params.name;
|
|
97
|
-
const tool = tools.find(t => t.name === toolName);
|
|
98
|
-
const startTime = Date.now();
|
|
99
|
-
if (!tool) {
|
|
100
|
-
logSecurityEvent('TOOL_NOT_FOUND', { toolName });
|
|
101
|
-
throw new Error(`Tool not found: ${toolName}`);
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
// Validar argumentos contra o schema da ferramenta
|
|
105
|
-
const args = request.params.arguments ?? {};
|
|
106
|
-
// Validação básica de segurança
|
|
107
|
-
if (typeof args !== 'object' || args === null) {
|
|
108
|
-
logSecurityEvent('INVALID_ARGUMENTS', { toolName, args });
|
|
109
|
-
throw new MCPError('VALIDATION_ERROR', 'Arguments must be an object');
|
|
110
|
-
}
|
|
111
|
-
// Validar projectPath se existir
|
|
112
|
-
if ('projectPath' in args) {
|
|
113
|
-
const projectPath = args.projectPath;
|
|
114
|
-
if (typeof projectPath !== 'string') {
|
|
115
|
-
logSecurityEvent('INVALID_PROJECT_PATH_TYPE', { toolName, projectPath });
|
|
116
|
-
throw new MCPError('VALIDATION_ERROR', 'projectPath must be a string');
|
|
117
|
-
}
|
|
118
|
-
if (!projectPath || projectPath.includes('..')) {
|
|
119
|
-
logSecurityEvent('PATH_TRAVERSION_ATTEMPT', { toolName, projectPath });
|
|
120
|
-
throw new MCPError('VALIDATION_ERROR', 'Invalid project path');
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
// Log da execução
|
|
124
|
-
logger.info(`Executing tool: ${toolName}`, { args }, toolName);
|
|
125
|
-
// Executar a ferramenta
|
|
126
|
-
const result = await tool.handle(args, { providerManager, gitAdapter });
|
|
127
|
-
const duration = Date.now() - startTime;
|
|
128
|
-
logPerformanceMetric(`tool_${toolName}`, duration, { success: true });
|
|
129
|
-
logToolExecution(toolName, args, result);
|
|
130
|
-
return {
|
|
131
|
-
content: [
|
|
132
|
-
{
|
|
133
|
-
type: 'text',
|
|
134
|
-
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
const duration = Date.now() - startTime;
|
|
141
|
-
logPerformanceMetric(`tool_${toolName}`, duration, { success: false, error: error.message });
|
|
142
|
-
logToolExecution(toolName, request.params.arguments ?? {}, undefined, error);
|
|
143
|
-
return {
|
|
144
|
-
content: [
|
|
145
|
-
{
|
|
146
|
-
type: 'text',
|
|
147
|
-
text: `Error: ${error.message || String(error)}`,
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
isError: true,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
// Register resource list handler
|
|
155
|
-
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
156
|
-
logger.debug('Listing available resources');
|
|
157
|
-
return {
|
|
158
|
-
resources: resources.map(resource => ({
|
|
159
|
-
uri: resource.uri,
|
|
160
|
-
name: resource.name,
|
|
161
|
-
description: resource.description,
|
|
162
|
-
mimeType: resource.mimeType,
|
|
163
|
-
})),
|
|
164
|
-
};
|
|
165
|
-
});
|
|
166
|
-
// Register resource read handler
|
|
167
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
168
|
-
const uri = request.params.uri;
|
|
169
|
-
const resource = resources.find(r => r.uri === uri);
|
|
170
|
-
if (!resource) {
|
|
171
|
-
logger.warn(`Resource not found: ${uri}`);
|
|
172
|
-
throw new Error(`Resource not found: ${uri}`);
|
|
173
|
-
}
|
|
174
|
-
logger.debug(`Reading resource: ${uri}`);
|
|
175
|
-
return {
|
|
176
|
-
contents: [
|
|
177
|
-
{
|
|
178
|
-
uri: resource.uri,
|
|
179
|
-
mimeType: resource.mimeType,
|
|
180
|
-
text: resource.content,
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
};
|
|
184
|
-
});
|
|
185
|
-
// Register prompt list handler
|
|
186
|
-
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
187
|
-
logger.debug('Listing available prompts');
|
|
188
|
-
return {
|
|
189
|
-
prompts: GIT_PROMPTS.map(prompt => ({
|
|
190
|
-
name: prompt.name,
|
|
191
|
-
description: prompt.description,
|
|
192
|
-
arguments: prompt.arguments,
|
|
193
|
-
})),
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
// Register prompt get handler
|
|
197
|
-
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
198
|
-
const promptName = request.params.name;
|
|
199
|
-
const prompt = GIT_PROMPTS.find(p => p.name === promptName);
|
|
200
|
-
if (!prompt) {
|
|
201
|
-
logger.warn(`Prompt not found: ${promptName}`);
|
|
202
|
-
throw new Error(`Prompt not found: ${promptName}`);
|
|
203
|
-
}
|
|
204
|
-
logger.debug(`Generating prompt: ${promptName}`);
|
|
205
|
-
const result = await prompt.generate(request.params.arguments ?? {}, { providerManager, gitAdapter });
|
|
206
|
-
return {
|
|
207
|
-
description: result.description,
|
|
208
|
-
messages: result.messages,
|
|
209
|
-
};
|
|
210
|
-
});
|
|
211
|
-
const transport = new StdioServerTransport();
|
|
212
|
-
await server.connect(transport);
|
|
213
|
-
// Only log in debug mode
|
|
214
|
-
if (process.env.DEBUG) {
|
|
215
|
-
logger.info(`✅ git-mcp MCP server running via STDIO`);
|
|
216
|
-
logger.info(`Tools: ${tools.length} registered`);
|
|
217
|
-
logger.info(`Resources: ${resources.length} registered`);
|
|
218
|
-
logger.info(`Prompts: ${GIT_PROMPTS.length} registered`);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
main().catch(err => {
|
|
222
|
-
console.error('❌ Failed to start git-mcp:', err);
|
|
223
|
-
process.exit(1);
|
|
224
|
-
});
|