@andrebuzeli/git-mcp 6.3.2 → 7.0.0
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/resources/toolsGuide.js +23 -3
- package/dist/tools/gitAnalytics.d.ts +1 -24
- package/dist/tools/gitAnalytics.js +168 -43
- package/dist/tools/gitBranches.d.ts +1 -67
- package/dist/tools/gitBranches.js +151 -19
- package/dist/tools/gitMonitor.d.ts +1 -46
- package/dist/tools/gitMonitor.js +184 -14
- package/dist/tools/gitTags.d.ts +1 -25
- package/dist/tools/gitTags.js +161 -13
- package/dist/utils/dualExecution.d.ts +49 -0
- package/dist/utils/dualExecution.js +96 -0
- package/package.json +2 -2
|
@@ -10,11 +10,31 @@ exports.TOOLS_GUIDE = {
|
|
|
10
10
|
name: "Git-MCP Tools Complete Guide",
|
|
11
11
|
description: "Detailed documentation and usage instructions for all 20 Git tools with 102 actions",
|
|
12
12
|
mimeType: "text/markdown",
|
|
13
|
-
content: `# 🛠️ Git-MCP
|
|
13
|
+
content: `# 🛠️ Git-MCP v7.0.0 - Complete Tools Guide
|
|
14
14
|
|
|
15
15
|
> **Comprehensive documentation for all 21 Git tools with 110+ actions**
|
|
16
|
-
> **Supports:**
|
|
17
|
-
> **
|
|
16
|
+
> **Supports:** Automatic DUAL execution on GitHub + Gitea APIs
|
|
17
|
+
> **BREAKING CHANGES in v7.0.0:** ALL tools now execute DUAL automatically with env var usernames
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 🚀 v7.0.0 MAJOR UPDATE: AUTOMATIC DUAL EXECUTION
|
|
22
|
+
|
|
23
|
+
**ALL TOOLS NOW:**
|
|
24
|
+
- ✅ Execute on BOTH GitHub and Gitea APIs automatically
|
|
25
|
+
- ✅ Use GITHUB_USERNAME and GITEA_USERNAME from environment variables
|
|
26
|
+
- ✅ Return separated responses: \`{ github: {...}, gitea: {...} }\`
|
|
27
|
+
- ✅ NO manual owner/user/author parameters needed
|
|
28
|
+
- ✅ Each provider uses its own credentials automatically
|
|
29
|
+
|
|
30
|
+
### Environment Variables Required:
|
|
31
|
+
\\\`\\\`\\\`bash
|
|
32
|
+
GITHUB_TOKEN=your_github_token
|
|
33
|
+
GITHUB_USERNAME=YourGitHubUsername
|
|
34
|
+
GITEA_TOKEN=your_gitea_token
|
|
35
|
+
GITEA_USERNAME=yourGiteaUsername
|
|
36
|
+
GITEA_URL=http://your-gitea-server:port
|
|
37
|
+
\\\`\\\`\\\`
|
|
18
38
|
|
|
19
39
|
---
|
|
20
40
|
|
|
@@ -2,28 +2,5 @@ import { Tool, MCPContext } from '../types';
|
|
|
2
2
|
export declare class GitAnalyticsTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
handle(params: Record<string, any>, ctx: MCPContext): Promise<
|
|
6
|
-
success: boolean;
|
|
7
|
-
stats: {
|
|
8
|
-
totalCommits: number;
|
|
9
|
-
branches: number;
|
|
10
|
-
tags: number;
|
|
11
|
-
latestCommit: (import("simple-git").DefaultLogFields & import("simple-git").ListLogLine) | null;
|
|
12
|
-
};
|
|
13
|
-
commits?: undefined;
|
|
14
|
-
details?: undefined;
|
|
15
|
-
contributors?: undefined;
|
|
16
|
-
} | {
|
|
17
|
-
success: boolean;
|
|
18
|
-
commits: number;
|
|
19
|
-
details: readonly (import("simple-git").DefaultLogFields & import("simple-git").ListLogLine)[];
|
|
20
|
-
stats?: undefined;
|
|
21
|
-
contributors?: undefined;
|
|
22
|
-
} | {
|
|
23
|
-
success: boolean;
|
|
24
|
-
contributors: any[];
|
|
25
|
-
stats?: undefined;
|
|
26
|
-
commits?: undefined;
|
|
27
|
-
details?: undefined;
|
|
28
|
-
}>;
|
|
5
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<any>;
|
|
29
6
|
}
|
|
@@ -4,65 +4,190 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GitAnalyticsTool = void 0;
|
|
7
|
-
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
7
|
const errors_1 = require("../utils/errors");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
const repoHelpers_1 = require("../utils/repoHelpers");
|
|
9
10
|
class GitAnalyticsTool {
|
|
10
11
|
constructor() {
|
|
11
12
|
this.name = 'git-analytics';
|
|
12
|
-
this.description = 'Repository analytics and statistics';
|
|
13
|
+
this.description = 'Repository analytics and statistics - automatic dual-provider execution using GitHub and Gitea APIs';
|
|
13
14
|
}
|
|
14
15
|
async handle(params, ctx) {
|
|
15
16
|
const action = params.action;
|
|
16
17
|
const projectPath = params.projectPath;
|
|
17
|
-
if (!action
|
|
18
|
-
throw new errors_1.MCPError('VALIDATION_ERROR', 'action
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!action)
|
|
19
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'action is required');
|
|
20
|
+
if (!projectPath)
|
|
21
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
22
|
+
// Auto-extract repo info from projectPath
|
|
23
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
24
|
+
const repo = repoInfo.repoName;
|
|
25
|
+
// Each provider uses its own username from env
|
|
26
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
27
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
21
28
|
switch (action) {
|
|
22
29
|
case 'stats': {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const results = { success: true, providers: {} };
|
|
31
|
+
// GitHub API
|
|
32
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
33
|
+
try {
|
|
34
|
+
const repoData = await ctx.providerManager.github.rest.repos.get({
|
|
35
|
+
owner: githubOwner,
|
|
36
|
+
repo: repo,
|
|
37
|
+
});
|
|
38
|
+
const branches = await ctx.providerManager.github.rest.repos.listBranches({
|
|
39
|
+
owner: githubOwner,
|
|
40
|
+
repo: repo,
|
|
41
|
+
});
|
|
42
|
+
const tags = await ctx.providerManager.github.rest.repos.listTags({
|
|
43
|
+
owner: githubOwner,
|
|
44
|
+
repo: repo,
|
|
45
|
+
});
|
|
46
|
+
const commits = await ctx.providerManager.github.rest.repos.listCommits({
|
|
47
|
+
owner: githubOwner,
|
|
48
|
+
repo: repo,
|
|
49
|
+
per_page: 1,
|
|
50
|
+
});
|
|
51
|
+
results.providers.github = {
|
|
52
|
+
success: true,
|
|
53
|
+
stats: {
|
|
54
|
+
totalCommits: repoData.data.size,
|
|
55
|
+
branches: branches.data.length,
|
|
56
|
+
tags: tags.data.length,
|
|
57
|
+
defaultBranch: repoData.data.default_branch,
|
|
58
|
+
latestCommit: commits.data[0] || null,
|
|
59
|
+
size: repoData.data.size,
|
|
60
|
+
language: repoData.data.language,
|
|
61
|
+
stars: repoData.data.stargazers_count,
|
|
62
|
+
forks: repoData.data.forks_count,
|
|
63
|
+
openIssues: repoData.data.open_issues_count,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
results.providers.github = { success: false, error: err.message };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Gitea API
|
|
72
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
73
|
+
try {
|
|
74
|
+
const repoData = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
75
|
+
const branches = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/branches`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
76
|
+
const tags = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
77
|
+
const commits = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/commits`, {
|
|
78
|
+
params: { limit: 1 },
|
|
79
|
+
headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
|
|
80
|
+
});
|
|
81
|
+
results.providers.gitea = {
|
|
82
|
+
success: true,
|
|
83
|
+
stats: {
|
|
84
|
+
totalCommits: repoData.data.size,
|
|
85
|
+
branches: branches.data.length,
|
|
86
|
+
tags: tags.data.length,
|
|
87
|
+
defaultBranch: repoData.data.default_branch,
|
|
88
|
+
latestCommit: commits.data[0] || null,
|
|
89
|
+
size: repoData.data.size,
|
|
90
|
+
language: repoData.data.language,
|
|
91
|
+
stars: repoData.data.stars_count,
|
|
92
|
+
forks: repoData.data.forks_count,
|
|
93
|
+
openIssues: repoData.data.open_issues_count,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
35
102
|
}
|
|
36
103
|
case 'commits': {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
104
|
+
const results = { success: true, providers: {} };
|
|
105
|
+
// GitHub API
|
|
106
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
107
|
+
try {
|
|
108
|
+
const commits = await ctx.providerManager.github.rest.repos.listCommits({
|
|
109
|
+
owner: githubOwner,
|
|
110
|
+
repo: repo,
|
|
111
|
+
since: params.since,
|
|
112
|
+
until: params.until,
|
|
113
|
+
per_page: params.limit || 100,
|
|
114
|
+
});
|
|
115
|
+
results.providers.github = {
|
|
116
|
+
success: true,
|
|
117
|
+
commits: commits.data.length,
|
|
118
|
+
details: commits.data,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
results.providers.github = { success: false, error: err.message };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Gitea API
|
|
126
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
127
|
+
try {
|
|
128
|
+
const commits = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/commits`, {
|
|
129
|
+
params: {
|
|
130
|
+
since: params.since,
|
|
131
|
+
until: params.until,
|
|
132
|
+
limit: params.limit || 100,
|
|
133
|
+
},
|
|
134
|
+
headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
|
|
135
|
+
});
|
|
136
|
+
results.providers.gitea = {
|
|
137
|
+
success: true,
|
|
138
|
+
commits: commits.data.length,
|
|
139
|
+
details: commits.data,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return results;
|
|
49
147
|
}
|
|
50
148
|
case 'contributors': {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
149
|
+
const results = { success: true, providers: {} };
|
|
150
|
+
// GitHub API
|
|
151
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
152
|
+
try {
|
|
153
|
+
const contributors = await ctx.providerManager.github.rest.repos.listContributors({
|
|
154
|
+
owner: githubOwner,
|
|
155
|
+
repo: repo,
|
|
156
|
+
per_page: 100,
|
|
157
|
+
});
|
|
158
|
+
results.providers.github = {
|
|
159
|
+
success: true,
|
|
160
|
+
contributors: contributors.data.map((c) => ({
|
|
161
|
+
login: c.login,
|
|
162
|
+
name: c.login,
|
|
163
|
+
contributions: c.contributions,
|
|
164
|
+
avatar_url: c.avatar_url,
|
|
165
|
+
})),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
results.providers.github = { success: false, error: err.message };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Gitea API
|
|
173
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
174
|
+
try {
|
|
175
|
+
const contributors = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/contributors`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
176
|
+
results.providers.gitea = {
|
|
177
|
+
success: true,
|
|
178
|
+
contributors: contributors.data.map((c) => ({
|
|
179
|
+
login: c.login,
|
|
180
|
+
name: c.name || c.login,
|
|
181
|
+
contributions: c.contributions,
|
|
182
|
+
avatar_url: c.avatar_url,
|
|
183
|
+
})),
|
|
184
|
+
};
|
|
57
185
|
}
|
|
58
|
-
|
|
59
|
-
|
|
186
|
+
catch (err) {
|
|
187
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
60
188
|
}
|
|
61
|
-
}
|
|
62
|
-
return
|
|
63
|
-
success: true,
|
|
64
|
-
contributors: Array.from(contributors.values()).sort((a, b) => b.count - a.count),
|
|
65
|
-
};
|
|
189
|
+
}
|
|
190
|
+
return results;
|
|
66
191
|
}
|
|
67
192
|
default:
|
|
68
193
|
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
@@ -2,71 +2,5 @@ import { Tool, MCPContext } from '../types';
|
|
|
2
2
|
export declare class GitBranchesTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
handle(params: Record<string, any>, ctx: MCPContext): Promise<
|
|
6
|
-
success: boolean;
|
|
7
|
-
branch: any;
|
|
8
|
-
branches?: undefined;
|
|
9
|
-
current?: undefined;
|
|
10
|
-
deleted?: undefined;
|
|
11
|
-
result?: undefined;
|
|
12
|
-
baseBranch?: undefined;
|
|
13
|
-
compareBranch?: undefined;
|
|
14
|
-
commits?: undefined;
|
|
15
|
-
diff?: undefined;
|
|
16
|
-
} | {
|
|
17
|
-
branches: string[];
|
|
18
|
-
current: string;
|
|
19
|
-
success?: undefined;
|
|
20
|
-
branch?: undefined;
|
|
21
|
-
deleted?: undefined;
|
|
22
|
-
result?: undefined;
|
|
23
|
-
baseBranch?: undefined;
|
|
24
|
-
compareBranch?: undefined;
|
|
25
|
-
commits?: undefined;
|
|
26
|
-
diff?: undefined;
|
|
27
|
-
} | {
|
|
28
|
-
success: boolean;
|
|
29
|
-
branch: string;
|
|
30
|
-
current: boolean;
|
|
31
|
-
branches?: undefined;
|
|
32
|
-
deleted?: undefined;
|
|
33
|
-
result?: undefined;
|
|
34
|
-
baseBranch?: undefined;
|
|
35
|
-
compareBranch?: undefined;
|
|
36
|
-
commits?: undefined;
|
|
37
|
-
diff?: undefined;
|
|
38
|
-
} | {
|
|
39
|
-
success: boolean;
|
|
40
|
-
deleted: any;
|
|
41
|
-
branch?: undefined;
|
|
42
|
-
branches?: undefined;
|
|
43
|
-
current?: undefined;
|
|
44
|
-
result?: undefined;
|
|
45
|
-
baseBranch?: undefined;
|
|
46
|
-
compareBranch?: undefined;
|
|
47
|
-
commits?: undefined;
|
|
48
|
-
diff?: undefined;
|
|
49
|
-
} | {
|
|
50
|
-
success: boolean;
|
|
51
|
-
result: import("simple-git").MergeResult;
|
|
52
|
-
branch?: undefined;
|
|
53
|
-
branches?: undefined;
|
|
54
|
-
current?: undefined;
|
|
55
|
-
deleted?: undefined;
|
|
56
|
-
baseBranch?: undefined;
|
|
57
|
-
compareBranch?: undefined;
|
|
58
|
-
commits?: undefined;
|
|
59
|
-
diff?: undefined;
|
|
60
|
-
} | {
|
|
61
|
-
success: boolean;
|
|
62
|
-
baseBranch: any;
|
|
63
|
-
compareBranch: any;
|
|
64
|
-
commits: readonly (import("simple-git").DefaultLogFields & import("simple-git").ListLogLine)[];
|
|
65
|
-
diff: string;
|
|
66
|
-
branch?: undefined;
|
|
67
|
-
branches?: undefined;
|
|
68
|
-
current?: undefined;
|
|
69
|
-
deleted?: undefined;
|
|
70
|
-
result?: undefined;
|
|
71
|
-
}>;
|
|
5
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<any>;
|
|
72
6
|
}
|
|
@@ -7,10 +7,12 @@ 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
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
12
|
class GitBranchesTool {
|
|
11
13
|
constructor() {
|
|
12
14
|
this.name = 'git-branches';
|
|
13
|
-
this.description = 'Branch management operations';
|
|
15
|
+
this.description = 'Branch management operations - automatic dual-provider execution for remote queries';
|
|
14
16
|
}
|
|
15
17
|
async handle(params, ctx) {
|
|
16
18
|
const action = params.action;
|
|
@@ -32,14 +34,60 @@ class GitBranchesTool {
|
|
|
32
34
|
if (params.checkout !== false) {
|
|
33
35
|
await git.checkout(branchName);
|
|
34
36
|
}
|
|
35
|
-
return { success: true, branch: branchName };
|
|
37
|
+
return { success: true, branch: branchName, local: true };
|
|
36
38
|
}
|
|
37
39
|
case 'list': {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const localBranches = await git.branch();
|
|
41
|
+
const results = {
|
|
42
|
+
success: true,
|
|
43
|
+
local: {
|
|
44
|
+
branches: localBranches.all,
|
|
45
|
+
current: localBranches.current,
|
|
46
|
+
},
|
|
47
|
+
providers: {}
|
|
42
48
|
};
|
|
49
|
+
// Also query remote APIs
|
|
50
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
51
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
52
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
53
|
+
// GitHub
|
|
54
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
55
|
+
try {
|
|
56
|
+
const branches = await ctx.providerManager.github.rest.repos.listBranches({
|
|
57
|
+
owner: githubOwner,
|
|
58
|
+
repo: repoInfo.repoName,
|
|
59
|
+
});
|
|
60
|
+
results.providers.github = {
|
|
61
|
+
success: true,
|
|
62
|
+
branches: branches.data.map((b) => ({
|
|
63
|
+
name: b.name,
|
|
64
|
+
protected: b.protected,
|
|
65
|
+
sha: b.commit.sha,
|
|
66
|
+
})),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
results.providers.github = { success: false, error: err.message };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Gitea
|
|
74
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
75
|
+
try {
|
|
76
|
+
const branches = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/branches`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
77
|
+
results.providers.gitea = {
|
|
78
|
+
success: true,
|
|
79
|
+
branches: branches.data.map((b) => ({
|
|
80
|
+
name: b.name,
|
|
81
|
+
protected: b.protected,
|
|
82
|
+
sha: b.commit.id,
|
|
83
|
+
})),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return results;
|
|
43
91
|
}
|
|
44
92
|
case 'get': {
|
|
45
93
|
const branchName = params.branchName;
|
|
@@ -47,13 +95,56 @@ class GitBranchesTool {
|
|
|
47
95
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'branchName is required');
|
|
48
96
|
const branches = await git.branch();
|
|
49
97
|
const branch = branches.all.find(b => b === branchName);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
98
|
+
const results = {
|
|
99
|
+
success: true,
|
|
100
|
+
local: branch ? { branch, current: branches.current === branchName } : { found: false },
|
|
101
|
+
providers: {}
|
|
102
|
+
};
|
|
103
|
+
// Also query remote APIs
|
|
104
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
105
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
106
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
107
|
+
// GitHub
|
|
108
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
109
|
+
try {
|
|
110
|
+
const branchData = await ctx.providerManager.github.rest.repos.getBranch({
|
|
111
|
+
owner: githubOwner,
|
|
112
|
+
repo: repoInfo.repoName,
|
|
113
|
+
branch: branchName,
|
|
114
|
+
});
|
|
115
|
+
results.providers.github = {
|
|
116
|
+
success: true,
|
|
117
|
+
branch: {
|
|
118
|
+
name: branchData.data.name,
|
|
119
|
+
protected: branchData.data.protected,
|
|
120
|
+
sha: branchData.data.commit.sha,
|
|
121
|
+
commit: branchData.data.commit,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
results.providers.github = { success: false, error: err.message };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Gitea
|
|
130
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
131
|
+
try {
|
|
132
|
+
const branchData = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/branches/${branchName}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
133
|
+
results.providers.gitea = {
|
|
134
|
+
success: true,
|
|
135
|
+
branch: {
|
|
136
|
+
name: branchData.data.name,
|
|
137
|
+
protected: branchData.data.protected,
|
|
138
|
+
sha: branchData.data.commit.id,
|
|
139
|
+
commit: branchData.data.commit,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
145
|
+
}
|
|
55
146
|
}
|
|
56
|
-
return
|
|
147
|
+
return results;
|
|
57
148
|
}
|
|
58
149
|
case 'delete': {
|
|
59
150
|
(0, safetyController_1.requireConfirmationIfDestructive)('delete', params);
|
|
@@ -61,7 +152,7 @@ class GitBranchesTool {
|
|
|
61
152
|
if (!branchName)
|
|
62
153
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'branchName is required');
|
|
63
154
|
await git.deleteLocalBranch(branchName, params.force);
|
|
64
|
-
return { success: true, deleted: branchName };
|
|
155
|
+
return { success: true, deleted: branchName, local: true };
|
|
65
156
|
}
|
|
66
157
|
case 'merge': {
|
|
67
158
|
const branchName = params.branchName;
|
|
@@ -72,7 +163,7 @@ class GitBranchesTool {
|
|
|
72
163
|
await git.checkout(targetBranch);
|
|
73
164
|
}
|
|
74
165
|
const result = await git.merge([branchName]);
|
|
75
|
-
return { success: true, result };
|
|
166
|
+
return { success: true, result, local: true };
|
|
76
167
|
}
|
|
77
168
|
case 'compare': {
|
|
78
169
|
const baseBranch = params.baseBranch;
|
|
@@ -82,13 +173,54 @@ class GitBranchesTool {
|
|
|
82
173
|
}
|
|
83
174
|
const diff = await git.diff([`${baseBranch}...${compareBranch}`]);
|
|
84
175
|
const log = await git.log({ from: baseBranch, to: compareBranch });
|
|
85
|
-
|
|
176
|
+
const results = {
|
|
86
177
|
success: true,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
178
|
+
local: {
|
|
179
|
+
baseBranch,
|
|
180
|
+
compareBranch,
|
|
181
|
+
commits: log.all,
|
|
182
|
+
diff,
|
|
183
|
+
},
|
|
184
|
+
providers: {}
|
|
91
185
|
};
|
|
186
|
+
// Also compare on remote APIs
|
|
187
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
188
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
189
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
190
|
+
// GitHub
|
|
191
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
192
|
+
try {
|
|
193
|
+
const comparison = await ctx.providerManager.github.rest.repos.compareCommits({
|
|
194
|
+
owner: githubOwner,
|
|
195
|
+
repo: repoInfo.repoName,
|
|
196
|
+
base: baseBranch,
|
|
197
|
+
head: compareBranch,
|
|
198
|
+
});
|
|
199
|
+
results.providers.github = {
|
|
200
|
+
success: true,
|
|
201
|
+
comparison: {
|
|
202
|
+
status: comparison.data.status,
|
|
203
|
+
ahead_by: comparison.data.ahead_by,
|
|
204
|
+
behind_by: comparison.data.behind_by,
|
|
205
|
+
total_commits: comparison.data.total_commits,
|
|
206
|
+
commits: comparison.data.commits.map((c) => ({
|
|
207
|
+
sha: c.sha,
|
|
208
|
+
message: c.commit.message,
|
|
209
|
+
author: c.commit.author.name,
|
|
210
|
+
date: c.commit.author.date,
|
|
211
|
+
})),
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
results.providers.github = { success: false, error: err.message };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Gitea - comparison API might not be available
|
|
220
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
221
|
+
results.providers.gitea = { success: false, error: 'Comparison API not available in Gitea' };
|
|
222
|
+
}
|
|
223
|
+
return results;
|
|
92
224
|
}
|
|
93
225
|
default:
|
|
94
226
|
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
@@ -2,50 +2,5 @@ import { Tool, MCPContext } from '../types';
|
|
|
2
2
|
export declare class GitMonitorTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
handle(params: Record<string, any>, ctx: MCPContext): Promise<
|
|
6
|
-
success: boolean;
|
|
7
|
-
commits: readonly (import("simple-git").DefaultLogFields & import("simple-git").ListLogLine)[];
|
|
8
|
-
total: number;
|
|
9
|
-
status?: undefined;
|
|
10
|
-
contributors?: undefined;
|
|
11
|
-
} | {
|
|
12
|
-
success: boolean;
|
|
13
|
-
status: import("simple-git").StatusResult | {
|
|
14
|
-
branch: string | null;
|
|
15
|
-
ahead: number;
|
|
16
|
-
behind: number;
|
|
17
|
-
staged: string[];
|
|
18
|
-
modified: string[];
|
|
19
|
-
deleted: string[];
|
|
20
|
-
created: string[];
|
|
21
|
-
renamed: import("simple-git").StatusResultRenamed[];
|
|
22
|
-
conflicted: string[];
|
|
23
|
-
not_added: string[];
|
|
24
|
-
};
|
|
25
|
-
commits?: undefined;
|
|
26
|
-
total?: undefined;
|
|
27
|
-
contributors?: undefined;
|
|
28
|
-
} | {
|
|
29
|
-
success: boolean;
|
|
30
|
-
commits: {
|
|
31
|
-
hash: string;
|
|
32
|
-
date: string;
|
|
33
|
-
message: string;
|
|
34
|
-
author: string;
|
|
35
|
-
email: string;
|
|
36
|
-
}[];
|
|
37
|
-
total?: undefined;
|
|
38
|
-
status?: undefined;
|
|
39
|
-
contributors?: undefined;
|
|
40
|
-
} | {
|
|
41
|
-
success: boolean;
|
|
42
|
-
contributors: {
|
|
43
|
-
name: string;
|
|
44
|
-
email: string;
|
|
45
|
-
commits: number;
|
|
46
|
-
}[];
|
|
47
|
-
commits?: undefined;
|
|
48
|
-
total?: undefined;
|
|
49
|
-
status?: undefined;
|
|
50
|
-
}>;
|
|
5
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<any>;
|
|
51
6
|
}
|
package/dist/tools/gitMonitor.js
CHANGED
|
@@ -6,10 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.GitMonitorTool = 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
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
11
|
class GitMonitorTool {
|
|
10
12
|
constructor() {
|
|
11
13
|
this.name = 'git-monitor';
|
|
12
|
-
this.description = 'Repository monitoring and status operations';
|
|
14
|
+
this.description = 'Repository monitoring and status operations - automatic dual-provider execution for remote queries';
|
|
13
15
|
}
|
|
14
16
|
async handle(params, ctx) {
|
|
15
17
|
const action = params.action;
|
|
@@ -30,11 +32,72 @@ class GitMonitorTool {
|
|
|
30
32
|
if (params.author)
|
|
31
33
|
options.author = params.author;
|
|
32
34
|
const log = await git.log(options);
|
|
33
|
-
|
|
35
|
+
const results = {
|
|
34
36
|
success: true,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
local: {
|
|
38
|
+
commits: log.all,
|
|
39
|
+
total: log.total,
|
|
40
|
+
},
|
|
41
|
+
providers: {}
|
|
37
42
|
};
|
|
43
|
+
// Also query remote APIs
|
|
44
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
45
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
46
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
47
|
+
// GitHub
|
|
48
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
49
|
+
try {
|
|
50
|
+
const commits = await ctx.providerManager.github.rest.repos.listCommits({
|
|
51
|
+
owner: githubOwner,
|
|
52
|
+
repo: repoInfo.repoName,
|
|
53
|
+
since: params.since,
|
|
54
|
+
until: params.until,
|
|
55
|
+
per_page: params.limit || 10,
|
|
56
|
+
});
|
|
57
|
+
results.providers.github = {
|
|
58
|
+
success: true,
|
|
59
|
+
commits: commits.data.map((c) => ({
|
|
60
|
+
sha: c.sha,
|
|
61
|
+
message: c.commit.message,
|
|
62
|
+
author: c.commit.author.name,
|
|
63
|
+
email: c.commit.author.email,
|
|
64
|
+
date: c.commit.author.date,
|
|
65
|
+
url: c.html_url,
|
|
66
|
+
})),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
results.providers.github = { success: false, error: err.message };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Gitea
|
|
74
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
75
|
+
try {
|
|
76
|
+
const commits = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/commits`, {
|
|
77
|
+
params: {
|
|
78
|
+
since: params.since,
|
|
79
|
+
until: params.until,
|
|
80
|
+
limit: params.limit || 10,
|
|
81
|
+
},
|
|
82
|
+
headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
|
|
83
|
+
});
|
|
84
|
+
results.providers.gitea = {
|
|
85
|
+
success: true,
|
|
86
|
+
commits: commits.data.map((c) => ({
|
|
87
|
+
sha: c.sha,
|
|
88
|
+
message: c.commit.message,
|
|
89
|
+
author: c.commit.author.name,
|
|
90
|
+
email: c.commit.author.email,
|
|
91
|
+
date: c.commit.author.date,
|
|
92
|
+
url: c.html_url,
|
|
93
|
+
})),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return results;
|
|
38
101
|
}
|
|
39
102
|
case 'status': {
|
|
40
103
|
const status = await git.status();
|
|
@@ -50,7 +113,7 @@ class GitMonitorTool {
|
|
|
50
113
|
conflicted: status.conflicted,
|
|
51
114
|
not_added: status.not_added,
|
|
52
115
|
} : status;
|
|
53
|
-
return { success: true, status: detailed };
|
|
116
|
+
return { success: true, local: { status: detailed } };
|
|
54
117
|
}
|
|
55
118
|
case 'commits': {
|
|
56
119
|
const branch = params.branch;
|
|
@@ -64,16 +127,75 @@ class GitMonitorTool {
|
|
|
64
127
|
if (params.author)
|
|
65
128
|
options.author = params.author;
|
|
66
129
|
const log = await git.log(options);
|
|
67
|
-
|
|
130
|
+
const results = {
|
|
68
131
|
success: true,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
132
|
+
local: {
|
|
133
|
+
commits: log.all.map(c => ({
|
|
134
|
+
hash: c.hash,
|
|
135
|
+
date: c.date,
|
|
136
|
+
message: c.message,
|
|
137
|
+
author: c.author_name,
|
|
138
|
+
email: c.author_email,
|
|
139
|
+
})),
|
|
140
|
+
},
|
|
141
|
+
providers: {}
|
|
76
142
|
};
|
|
143
|
+
// Also query remote APIs
|
|
144
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
145
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
146
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
147
|
+
// GitHub
|
|
148
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
149
|
+
try {
|
|
150
|
+
const commits = await ctx.providerManager.github.rest.repos.listCommits({
|
|
151
|
+
owner: githubOwner,
|
|
152
|
+
repo: repoInfo.repoName,
|
|
153
|
+
sha: branch,
|
|
154
|
+
since: params.since,
|
|
155
|
+
per_page: params.limit || 50,
|
|
156
|
+
});
|
|
157
|
+
results.providers.github = {
|
|
158
|
+
success: true,
|
|
159
|
+
commits: commits.data.map((c) => ({
|
|
160
|
+
sha: c.sha,
|
|
161
|
+
message: c.commit.message,
|
|
162
|
+
author: c.commit.author.name,
|
|
163
|
+
email: c.commit.author.email,
|
|
164
|
+
date: c.commit.author.date,
|
|
165
|
+
})),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
results.providers.github = { success: false, error: err.message };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Gitea
|
|
173
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
174
|
+
try {
|
|
175
|
+
const commits = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/commits`, {
|
|
176
|
+
params: {
|
|
177
|
+
sha: branch,
|
|
178
|
+
since: params.since,
|
|
179
|
+
limit: params.limit || 50,
|
|
180
|
+
},
|
|
181
|
+
headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
|
|
182
|
+
});
|
|
183
|
+
results.providers.gitea = {
|
|
184
|
+
success: true,
|
|
185
|
+
commits: commits.data.map((c) => ({
|
|
186
|
+
sha: c.sha,
|
|
187
|
+
message: c.commit.message,
|
|
188
|
+
author: c.commit.author.name,
|
|
189
|
+
email: c.commit.author.email,
|
|
190
|
+
date: c.commit.author.date,
|
|
191
|
+
})),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return results;
|
|
77
199
|
}
|
|
78
200
|
case 'contributors': {
|
|
79
201
|
const log = await git.log();
|
|
@@ -92,7 +214,55 @@ class GitMonitorTool {
|
|
|
92
214
|
}
|
|
93
215
|
});
|
|
94
216
|
const sorted = Array.from(contributors.values()).sort((a, b) => b.commits - a.commits);
|
|
95
|
-
|
|
217
|
+
const results = {
|
|
218
|
+
success: true,
|
|
219
|
+
local: { contributors: sorted },
|
|
220
|
+
providers: {}
|
|
221
|
+
};
|
|
222
|
+
// Also query remote APIs
|
|
223
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
224
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
225
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
226
|
+
// GitHub
|
|
227
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
228
|
+
try {
|
|
229
|
+
const contributors = await ctx.providerManager.github.rest.repos.listContributors({
|
|
230
|
+
owner: githubOwner,
|
|
231
|
+
repo: repoInfo.repoName,
|
|
232
|
+
});
|
|
233
|
+
results.providers.github = {
|
|
234
|
+
success: true,
|
|
235
|
+
contributors: contributors.data.map((c) => ({
|
|
236
|
+
login: c.login,
|
|
237
|
+
contributions: c.contributions,
|
|
238
|
+
avatar_url: c.avatar_url,
|
|
239
|
+
url: c.html_url,
|
|
240
|
+
})),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
results.providers.github = { success: false, error: err.message };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Gitea
|
|
248
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
249
|
+
try {
|
|
250
|
+
const contributors = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/contributors`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
251
|
+
results.providers.gitea = {
|
|
252
|
+
success: true,
|
|
253
|
+
contributors: contributors.data.map((c) => ({
|
|
254
|
+
login: c.login,
|
|
255
|
+
name: c.name || c.login,
|
|
256
|
+
contributions: c.contributions,
|
|
257
|
+
avatar_url: c.avatar_url,
|
|
258
|
+
})),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return results;
|
|
96
266
|
}
|
|
97
267
|
default:
|
|
98
268
|
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
package/dist/tools/gitTags.d.ts
CHANGED
|
@@ -2,29 +2,5 @@ import { Tool, MCPContext } from '../types';
|
|
|
2
2
|
export declare class GitTagsTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
handle(params: Record<string, any>, ctx: MCPContext): Promise<
|
|
6
|
-
success: boolean;
|
|
7
|
-
tag: any;
|
|
8
|
-
tags?: undefined;
|
|
9
|
-
details?: undefined;
|
|
10
|
-
deleted?: undefined;
|
|
11
|
-
} | {
|
|
12
|
-
success: boolean;
|
|
13
|
-
tags: string[];
|
|
14
|
-
tag?: undefined;
|
|
15
|
-
details?: undefined;
|
|
16
|
-
deleted?: undefined;
|
|
17
|
-
} | {
|
|
18
|
-
success: boolean;
|
|
19
|
-
tag: any;
|
|
20
|
-
details: string;
|
|
21
|
-
tags?: undefined;
|
|
22
|
-
deleted?: undefined;
|
|
23
|
-
} | {
|
|
24
|
-
success: boolean;
|
|
25
|
-
deleted: any;
|
|
26
|
-
tag?: undefined;
|
|
27
|
-
tags?: undefined;
|
|
28
|
-
details?: undefined;
|
|
29
|
-
}>;
|
|
5
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<any>;
|
|
30
6
|
}
|
package/dist/tools/gitTags.js
CHANGED
|
@@ -6,10 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.GitTagsTool = void 0;
|
|
7
7
|
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
8
|
const errors_1 = require("../utils/errors");
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const repoHelpers_1 = require("../utils/repoHelpers");
|
|
9
11
|
class GitTagsTool {
|
|
10
12
|
constructor() {
|
|
11
13
|
this.name = 'git-tags';
|
|
12
|
-
this.description = 'Tag management operations';
|
|
14
|
+
this.description = 'Tag management operations - automatic dual-provider execution for remote queries';
|
|
13
15
|
}
|
|
14
16
|
async handle(params, ctx) {
|
|
15
17
|
const action = params.action;
|
|
@@ -29,37 +31,183 @@ class GitTagsTool {
|
|
|
29
31
|
else {
|
|
30
32
|
await git.addTag(tagName);
|
|
31
33
|
}
|
|
32
|
-
return { success: true, tag: tagName };
|
|
34
|
+
return { success: true, tag: tagName, local: true };
|
|
33
35
|
}
|
|
34
36
|
case 'list': {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
+
const localTags = await git.tags();
|
|
38
|
+
const results = {
|
|
39
|
+
success: true,
|
|
40
|
+
local: { tags: localTags.all },
|
|
41
|
+
providers: {}
|
|
42
|
+
};
|
|
43
|
+
// Also query remote APIs
|
|
44
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
45
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
46
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
47
|
+
// GitHub
|
|
48
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
49
|
+
try {
|
|
50
|
+
const tags = await ctx.providerManager.github.rest.repos.listTags({
|
|
51
|
+
owner: githubOwner,
|
|
52
|
+
repo: repoInfo.repoName,
|
|
53
|
+
});
|
|
54
|
+
results.providers.github = {
|
|
55
|
+
success: true,
|
|
56
|
+
tags: tags.data.map((t) => ({
|
|
57
|
+
name: t.name,
|
|
58
|
+
sha: t.commit.sha,
|
|
59
|
+
zipball_url: t.zipball_url,
|
|
60
|
+
tarball_url: t.tarball_url,
|
|
61
|
+
})),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
results.providers.github = { success: false, error: err.message };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Gitea
|
|
69
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
70
|
+
try {
|
|
71
|
+
const tags = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
72
|
+
results.providers.gitea = {
|
|
73
|
+
success: true,
|
|
74
|
+
tags: tags.data.map((t) => ({
|
|
75
|
+
name: t.name,
|
|
76
|
+
sha: t.commit?.id || t.id,
|
|
77
|
+
zipball_url: t.zipball_url,
|
|
78
|
+
tarball_url: t.tarball_url,
|
|
79
|
+
})),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return results;
|
|
37
87
|
}
|
|
38
88
|
case 'get': {
|
|
39
89
|
const tagName = params.tagName;
|
|
40
90
|
if (!tagName)
|
|
41
91
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'tagName is required');
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
92
|
+
const localTags = await git.tags();
|
|
93
|
+
const localTag = localTags.all.find(t => t === tagName);
|
|
94
|
+
const results = {
|
|
95
|
+
success: true,
|
|
96
|
+
local: localTag ? { found: true, tag: tagName } : { found: false },
|
|
97
|
+
providers: {}
|
|
98
|
+
};
|
|
99
|
+
if (localTag) {
|
|
100
|
+
try {
|
|
101
|
+
const show = await git.show([tagName]);
|
|
102
|
+
results.local.details = show;
|
|
103
|
+
}
|
|
104
|
+
catch { }
|
|
105
|
+
}
|
|
106
|
+
// Also query remote APIs
|
|
107
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
108
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
109
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
110
|
+
// GitHub
|
|
111
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
112
|
+
try {
|
|
113
|
+
const tag = await ctx.providerManager.github.rest.git.getRef({
|
|
114
|
+
owner: githubOwner,
|
|
115
|
+
repo: repoInfo.repoName,
|
|
116
|
+
ref: `tags/${tagName}`,
|
|
117
|
+
});
|
|
118
|
+
results.providers.github = {
|
|
119
|
+
success: true,
|
|
120
|
+
tag: {
|
|
121
|
+
name: tagName,
|
|
122
|
+
sha: tag.data.object.sha,
|
|
123
|
+
type: tag.data.object.type,
|
|
124
|
+
url: tag.data.url,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
results.providers.github = { success: false, error: err.message };
|
|
130
|
+
}
|
|
45
131
|
}
|
|
46
|
-
|
|
47
|
-
|
|
132
|
+
// Gitea
|
|
133
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
134
|
+
try {
|
|
135
|
+
const tags = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
136
|
+
const tag = tags.data.find((t) => t.name === tagName);
|
|
137
|
+
if (tag) {
|
|
138
|
+
results.providers.gitea = {
|
|
139
|
+
success: true,
|
|
140
|
+
tag: {
|
|
141
|
+
name: tag.name,
|
|
142
|
+
sha: tag.commit?.id || tag.id,
|
|
143
|
+
message: tag.message,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
results.providers.gitea = { success: false, error: 'Tag not found' };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return results;
|
|
48
156
|
}
|
|
49
157
|
case 'delete': {
|
|
50
158
|
const tagName = params.tagName;
|
|
51
159
|
if (!tagName)
|
|
52
160
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'tagName is required');
|
|
53
161
|
await git.tag(['-d', tagName]);
|
|
54
|
-
return { success: true, deleted: tagName };
|
|
162
|
+
return { success: true, deleted: tagName, local: true };
|
|
55
163
|
}
|
|
56
164
|
case 'search': {
|
|
57
165
|
const pattern = params.pattern;
|
|
58
166
|
if (!pattern)
|
|
59
167
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'pattern is required');
|
|
60
|
-
const
|
|
61
|
-
const filtered =
|
|
62
|
-
|
|
168
|
+
const localTags = await git.tags();
|
|
169
|
+
const filtered = localTags.all.filter(t => t.includes(pattern));
|
|
170
|
+
const results = {
|
|
171
|
+
success: true,
|
|
172
|
+
local: { tags: filtered },
|
|
173
|
+
providers: {}
|
|
174
|
+
};
|
|
175
|
+
// Also search remote APIs
|
|
176
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
177
|
+
const githubOwner = process.env.GITHUB_USERNAME;
|
|
178
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
179
|
+
// GitHub
|
|
180
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
181
|
+
try {
|
|
182
|
+
const tags = await ctx.providerManager.github.rest.repos.listTags({
|
|
183
|
+
owner: githubOwner,
|
|
184
|
+
repo: repoInfo.repoName,
|
|
185
|
+
});
|
|
186
|
+
const matchedTags = tags.data.filter((t) => t.name.includes(pattern));
|
|
187
|
+
results.providers.github = {
|
|
188
|
+
success: true,
|
|
189
|
+
tags: matchedTags.map((t) => t.name),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
results.providers.github = { success: false, error: err.message };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Gitea
|
|
197
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
198
|
+
try {
|
|
199
|
+
const tags = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
200
|
+
const matchedTags = tags.data.filter((t) => t.name.includes(pattern));
|
|
201
|
+
results.providers.gitea = {
|
|
202
|
+
success: true,
|
|
203
|
+
tags: matchedTags.map((t) => t.name),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
results.providers.gitea = { success: false, error: err.message };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return results;
|
|
63
211
|
}
|
|
64
212
|
default:
|
|
65
213
|
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dual Provider Execution Helper
|
|
3
|
+
* Executes operations on BOTH GitHub and Gitea simultaneously
|
|
4
|
+
* Uses GITHUB_USERNAME and GITEA_USERNAME from env vars automatically
|
|
5
|
+
*/
|
|
6
|
+
import { ProviderManager } from '../providers/providerManager';
|
|
7
|
+
export interface DualExecutionResult<T> {
|
|
8
|
+
github: {
|
|
9
|
+
success: boolean;
|
|
10
|
+
data?: T;
|
|
11
|
+
error?: string;
|
|
12
|
+
};
|
|
13
|
+
gitea: {
|
|
14
|
+
success: boolean;
|
|
15
|
+
data?: T;
|
|
16
|
+
error?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Execute function on both providers simultaneously
|
|
21
|
+
* @param providerManager Provider manager instance
|
|
22
|
+
* @param githubFn Function to execute on GitHub
|
|
23
|
+
* @param giteaFn Function to execute on Gitea
|
|
24
|
+
* @returns Dual execution result with separate responses
|
|
25
|
+
*/
|
|
26
|
+
export declare function executeDual<T>(providerManager: ProviderManager, githubFn: () => Promise<T>, giteaFn: () => Promise<T>): Promise<DualExecutionResult<T>>;
|
|
27
|
+
/**
|
|
28
|
+
* Get GitHub username from env vars
|
|
29
|
+
* @throws Error if GITHUB_USERNAME not found
|
|
30
|
+
*/
|
|
31
|
+
export declare function getGitHubUsername(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Get Gitea username from env vars
|
|
34
|
+
* @throws Error if GITEA_USERNAME not found
|
|
35
|
+
*/
|
|
36
|
+
export declare function getGiteaUsername(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Get repository name from projectPath
|
|
39
|
+
* @param projectPath Absolute path to project root
|
|
40
|
+
* @returns Repository name (folder name)
|
|
41
|
+
*/
|
|
42
|
+
export declare function getRepoNameFromPath(projectPath: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Execute same function on both providers with automatic username injection
|
|
45
|
+
* @param providerManager Provider manager instance
|
|
46
|
+
* @param fn Function that takes (provider, username) and returns data
|
|
47
|
+
* @returns Dual execution result
|
|
48
|
+
*/
|
|
49
|
+
export declare function executeDualAuto<T>(providerManager: ProviderManager, fn: (provider: any, username: string) => Promise<T>): Promise<DualExecutionResult<T>>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dual Provider Execution Helper
|
|
4
|
+
* Executes operations on BOTH GitHub and Gitea simultaneously
|
|
5
|
+
* Uses GITHUB_USERNAME and GITEA_USERNAME from env vars automatically
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.executeDual = executeDual;
|
|
9
|
+
exports.getGitHubUsername = getGitHubUsername;
|
|
10
|
+
exports.getGiteaUsername = getGiteaUsername;
|
|
11
|
+
exports.getRepoNameFromPath = getRepoNameFromPath;
|
|
12
|
+
exports.executeDualAuto = executeDualAuto;
|
|
13
|
+
/**
|
|
14
|
+
* Execute function on both providers simultaneously
|
|
15
|
+
* @param providerManager Provider manager instance
|
|
16
|
+
* @param githubFn Function to execute on GitHub
|
|
17
|
+
* @param giteaFn Function to execute on Gitea
|
|
18
|
+
* @returns Dual execution result with separate responses
|
|
19
|
+
*/
|
|
20
|
+
async function executeDual(providerManager, githubFn, giteaFn) {
|
|
21
|
+
const result = {
|
|
22
|
+
github: { success: false },
|
|
23
|
+
gitea: { success: false }
|
|
24
|
+
};
|
|
25
|
+
// Execute GitHub
|
|
26
|
+
try {
|
|
27
|
+
const githubData = await githubFn();
|
|
28
|
+
result.github = {
|
|
29
|
+
success: true,
|
|
30
|
+
data: githubData
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
result.github = {
|
|
35
|
+
success: false,
|
|
36
|
+
error: error.message || String(error)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Execute Gitea
|
|
40
|
+
try {
|
|
41
|
+
const giteaData = await giteaFn();
|
|
42
|
+
result.gitea = {
|
|
43
|
+
success: true,
|
|
44
|
+
data: giteaData
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
result.gitea = {
|
|
49
|
+
success: false,
|
|
50
|
+
error: error.message || String(error)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get GitHub username from env vars
|
|
57
|
+
* @throws Error if GITHUB_USERNAME not found
|
|
58
|
+
*/
|
|
59
|
+
function getGitHubUsername() {
|
|
60
|
+
const username = process.env.GITHUB_USERNAME;
|
|
61
|
+
if (!username) {
|
|
62
|
+
throw new Error('GITHUB_USERNAME not found in environment variables');
|
|
63
|
+
}
|
|
64
|
+
return username;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get Gitea username from env vars
|
|
68
|
+
* @throws Error if GITEA_USERNAME not found
|
|
69
|
+
*/
|
|
70
|
+
function getGiteaUsername() {
|
|
71
|
+
const username = process.env.GITEA_USERNAME;
|
|
72
|
+
if (!username) {
|
|
73
|
+
throw new Error('GITEA_USERNAME not found in environment variables');
|
|
74
|
+
}
|
|
75
|
+
return username;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get repository name from projectPath
|
|
79
|
+
* @param projectPath Absolute path to project root
|
|
80
|
+
* @returns Repository name (folder name)
|
|
81
|
+
*/
|
|
82
|
+
function getRepoNameFromPath(projectPath) {
|
|
83
|
+
const parts = projectPath.replace(/\\/g, '/').split('/');
|
|
84
|
+
return parts[parts.length - 1];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Execute same function on both providers with automatic username injection
|
|
88
|
+
* @param providerManager Provider manager instance
|
|
89
|
+
* @param fn Function that takes (provider, username) and returns data
|
|
90
|
+
* @returns Dual execution result
|
|
91
|
+
*/
|
|
92
|
+
async function executeDualAuto(providerManager, fn) {
|
|
93
|
+
const githubUsername = getGitHubUsername();
|
|
94
|
+
const giteaUsername = getGiteaUsername();
|
|
95
|
+
return executeDual(providerManager, () => fn(providerManager.getProvider('github'), githubUsername), () => fn(providerManager.getProvider('gitea'), giteaUsername));
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop). Fully autonomous with
|
|
3
|
+
"version": "7.0.0",
|
|
4
|
+
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop). Fully autonomous DUAL execution (GitHub + Gitea APIs) with automatic username detection. All tools execute on BOTH providers simultaneously. No manual parameters needed.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|