@andrebuzeli/git-mcp 5.8.3 → 5.9.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/index.js +48 -166
- package/dist/tools/gitIssues.js +53 -32
- package/dist/tools/gitRemote.js +39 -8
- package/dist/utils/repoHelpers.d.ts +37 -0
- package/dist/utils/repoHelpers.js +77 -0
- package/package.json +84 -84
package/dist/index.js
CHANGED
|
@@ -1,43 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
-
if (k2 === undefined) k2 = k;
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
5
|
};
|
|
39
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
const
|
|
7
|
+
const server_1 = require("./server");
|
|
41
8
|
const providerManager_1 = require("./providers/providerManager");
|
|
42
9
|
const config_1 = require("./config");
|
|
43
10
|
const gitFiles_1 = require("./tools/gitFiles");
|
|
@@ -98,148 +65,63 @@ async function main() {
|
|
|
98
65
|
const resources = [
|
|
99
66
|
toolsGuide_1.default
|
|
100
67
|
];
|
|
101
|
-
const toolRegistry = new Map();
|
|
102
|
-
for (const t of tools)
|
|
103
|
-
toolRegistry.set(t.name, t);
|
|
104
|
-
const resourceRegistry = new Map();
|
|
105
|
-
for (const r of resources)
|
|
106
|
-
resourceRegistry.set(r.uri, r);
|
|
107
68
|
// Silent mode for MCP clients - only log to stderr in debug mode
|
|
108
69
|
if (process.env.DEBUG) {
|
|
109
70
|
console.error(`Registered ${tools.length} Git tools`);
|
|
110
71
|
console.error(`Registered ${resources.length} resource(s)`);
|
|
111
72
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
id = request.id ?? null;
|
|
124
|
-
const { jsonrpc, method, params } = request;
|
|
125
|
-
let response = { jsonrpc: '2.0', id };
|
|
126
|
-
switch (method) {
|
|
127
|
-
case 'initialize':
|
|
128
|
-
response.result = {
|
|
129
|
-
protocolVersion: '2024-11-05',
|
|
130
|
-
capabilities: {
|
|
131
|
-
tools: {},
|
|
132
|
-
resources: {}
|
|
133
|
-
},
|
|
134
|
-
serverInfo: {
|
|
135
|
-
name: 'git-mcp',
|
|
136
|
-
version: '5.8.1'
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
break;
|
|
140
|
-
case 'tools/list':
|
|
141
|
-
response.result = {
|
|
142
|
-
tools: tools.map(t => ({
|
|
143
|
-
name: t.name,
|
|
144
|
-
description: t.description,
|
|
145
|
-
inputSchema: {
|
|
146
|
-
type: 'object',
|
|
147
|
-
properties: {},
|
|
148
|
-
required: []
|
|
149
|
-
}
|
|
150
|
-
}))
|
|
151
|
-
};
|
|
152
|
-
break;
|
|
153
|
-
case 'tools/call':
|
|
154
|
-
const toolName = params?.name;
|
|
155
|
-
const tool = toolRegistry.get(toolName);
|
|
156
|
-
if (!tool) {
|
|
157
|
-
response.error = {
|
|
158
|
-
code: -32601,
|
|
159
|
-
message: `Tool not found: ${toolName}`
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
try {
|
|
164
|
-
const result = await tool.handle(params?.arguments ?? {}, { providerManager });
|
|
165
|
-
response.result = {
|
|
166
|
-
content: [
|
|
167
|
-
{
|
|
168
|
-
type: 'text',
|
|
169
|
-
text: JSON.stringify(result, null, 2)
|
|
170
|
-
}
|
|
171
|
-
]
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
catch (err) {
|
|
175
|
-
response.error = {
|
|
176
|
-
code: -32603,
|
|
177
|
-
message: err.message || String(err)
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
break;
|
|
182
|
-
case 'resources/list':
|
|
183
|
-
response.result = {
|
|
184
|
-
resources: Array.from(resourceRegistry.values()).map(r => ({
|
|
185
|
-
uri: r.uri,
|
|
186
|
-
name: r.name,
|
|
187
|
-
description: r.description,
|
|
188
|
-
mimeType: r.mimeType
|
|
189
|
-
}))
|
|
190
|
-
};
|
|
191
|
-
break;
|
|
192
|
-
case 'resources/read':
|
|
193
|
-
const uri = params?.uri;
|
|
194
|
-
const resource = resourceRegistry.get(uri);
|
|
195
|
-
if (!resource) {
|
|
196
|
-
response.error = {
|
|
197
|
-
code: -32601,
|
|
198
|
-
message: `Resource not found: ${uri}`
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
response.result = {
|
|
203
|
-
contents: [
|
|
204
|
-
{
|
|
205
|
-
uri: resource.uri,
|
|
206
|
-
mimeType: resource.mimeType,
|
|
207
|
-
text: resource.content
|
|
208
|
-
}
|
|
209
|
-
]
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
break;
|
|
213
|
-
default:
|
|
214
|
-
response.error = {
|
|
215
|
-
code: -32601,
|
|
216
|
-
message: `Method not found: ${method}`
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
console.log(JSON.stringify(response));
|
|
220
|
-
}
|
|
221
|
-
catch (err) {
|
|
222
|
-
// Send error response with id if we have it
|
|
223
|
-
const errorResponse = {
|
|
224
|
-
jsonrpc: '2.0',
|
|
225
|
-
id: id,
|
|
226
|
-
error: {
|
|
227
|
-
code: -32700,
|
|
228
|
-
message: `Parse error: ${err.message || String(err)}`
|
|
73
|
+
const app = (0, server_1.createServer)({ tools, providerManager, resources });
|
|
74
|
+
// Try default port, then find available port if occupied
|
|
75
|
+
let port = parseInt(process.env.PORT || '3210');
|
|
76
|
+
const startServer = (attemptPort) => {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const server = app.listen(attemptPort, () => {
|
|
79
|
+
// Send success message to stderr (MCP clients expect JSON on stdout)
|
|
80
|
+
if (process.env.DEBUG) {
|
|
81
|
+
console.error(`✅ git-mcp server ready on http://localhost:${attemptPort}`);
|
|
82
|
+
console.error(`Tools: ${tools.map(t => t.name).join(', ')}`);
|
|
83
|
+
console.error(`Resources: ${resources.map(r => r.uri).join(', ')}`);
|
|
229
84
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
85
|
+
resolve(server);
|
|
86
|
+
});
|
|
87
|
+
server.on('error', (err) => {
|
|
88
|
+
if (err.code === 'EADDRINUSE') {
|
|
89
|
+
// Port in use, try next port
|
|
90
|
+
server.close();
|
|
91
|
+
resolve(null);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
reject(err);
|
|
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;
|
|
235
108
|
}
|
|
109
|
+
}
|
|
110
|
+
if (!server) {
|
|
111
|
+
console.error(`❌ Failed to find available port after ${maxAttempts} attempts`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
server.on('error', (err) => {
|
|
115
|
+
console.error('❌ Server error:', err);
|
|
116
|
+
process.exit(1);
|
|
236
117
|
});
|
|
237
118
|
// Keep process alive
|
|
238
119
|
process.on('SIGINT', () => {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
120
|
+
console.log('\n👋 Shutting down server...');
|
|
121
|
+
server.close(() => {
|
|
122
|
+
console.log('Server closed');
|
|
123
|
+
process.exit(0);
|
|
124
|
+
});
|
|
243
125
|
});
|
|
244
126
|
}
|
|
245
127
|
main().catch(err => {
|
package/dist/tools/gitIssues.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GitIssuesTool = void 0;
|
|
4
4
|
const errors_1 = require("../utils/errors");
|
|
5
|
+
const repoHelpers_1 = require("../utils/repoHelpers");
|
|
5
6
|
class GitIssuesTool {
|
|
6
7
|
constructor() {
|
|
7
8
|
this.name = 'git-issues';
|
|
@@ -11,18 +12,29 @@ class GitIssuesTool {
|
|
|
11
12
|
const action = params.action;
|
|
12
13
|
if (!action)
|
|
13
14
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'action is required');
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
// Auto-extract repo info if projectPath is provided
|
|
16
|
+
let owner = params.owner;
|
|
17
|
+
let repo = params.repo;
|
|
18
|
+
if (!repo && params.projectPath) {
|
|
19
|
+
const repoInfo = (0, repoHelpers_1.getRepoInfo)(params.projectPath);
|
|
20
|
+
repo = repoInfo.repoName;
|
|
21
|
+
owner = owner || repoInfo.githubOwner || repoInfo.giteaOwner;
|
|
22
|
+
}
|
|
23
|
+
// Use env vars as fallback
|
|
24
|
+
const githubOwner = owner || process.env.GITHUB_USERNAME;
|
|
25
|
+
const giteaOwner = owner || process.env.GITEA_USERNAME;
|
|
16
26
|
switch (action) {
|
|
17
27
|
case 'create': {
|
|
18
28
|
if (!params.title)
|
|
19
29
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'title is required');
|
|
30
|
+
if (!repo)
|
|
31
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
|
|
20
32
|
const results = { success: true, providers: {} };
|
|
21
33
|
// GitHub
|
|
22
|
-
if (ctx.providerManager.github) {
|
|
34
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
23
35
|
try {
|
|
24
36
|
const result = await ctx.providerManager.github.rest.issues.create({
|
|
25
|
-
owner:
|
|
37
|
+
owner: githubOwner,
|
|
26
38
|
repo: repo,
|
|
27
39
|
title: params.title,
|
|
28
40
|
body: params.body,
|
|
@@ -37,10 +49,10 @@ class GitIssuesTool {
|
|
|
37
49
|
}
|
|
38
50
|
}
|
|
39
51
|
// Gitea
|
|
40
|
-
if (ctx.providerManager.giteaBaseUrl) {
|
|
52
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
41
53
|
try {
|
|
42
54
|
const axios = require('axios');
|
|
43
|
-
const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${
|
|
55
|
+
const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues`, {
|
|
44
56
|
title: params.title,
|
|
45
57
|
body: params.body,
|
|
46
58
|
labels: params.labels?.map((l) => ({ name: l })),
|
|
@@ -57,12 +69,14 @@ class GitIssuesTool {
|
|
|
57
69
|
return results;
|
|
58
70
|
}
|
|
59
71
|
case 'list': {
|
|
72
|
+
if (!repo)
|
|
73
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
|
|
60
74
|
const results = { success: true, providers: {} };
|
|
61
75
|
// GitHub
|
|
62
|
-
if (ctx.providerManager.github) {
|
|
76
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
63
77
|
try {
|
|
64
78
|
const result = await ctx.providerManager.github.rest.issues.listForRepo({
|
|
65
|
-
owner:
|
|
79
|
+
owner: githubOwner,
|
|
66
80
|
repo: repo,
|
|
67
81
|
state: params.state_filter || 'open',
|
|
68
82
|
sort: params.sort || 'created',
|
|
@@ -76,10 +90,10 @@ class GitIssuesTool {
|
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
// Gitea
|
|
79
|
-
if (ctx.providerManager.giteaBaseUrl) {
|
|
93
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
80
94
|
try {
|
|
81
95
|
const axios = require('axios');
|
|
82
|
-
const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${
|
|
96
|
+
const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues`, {
|
|
83
97
|
params: {
|
|
84
98
|
state: params.state_filter || 'open',
|
|
85
99
|
sort: params.sort || 'created',
|
|
@@ -99,12 +113,14 @@ class GitIssuesTool {
|
|
|
99
113
|
const issue_number = params.issue_number;
|
|
100
114
|
if (!issue_number)
|
|
101
115
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
116
|
+
if (!repo)
|
|
117
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
|
|
102
118
|
const results = { success: true, providers: {} };
|
|
103
119
|
// GitHub
|
|
104
|
-
if (ctx.providerManager.github) {
|
|
120
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
105
121
|
try {
|
|
106
122
|
const result = await ctx.providerManager.github.rest.issues.get({
|
|
107
|
-
owner:
|
|
123
|
+
owner: githubOwner,
|
|
108
124
|
repo: repo,
|
|
109
125
|
issue_number,
|
|
110
126
|
});
|
|
@@ -115,10 +131,10 @@ class GitIssuesTool {
|
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
// Gitea
|
|
118
|
-
if (ctx.providerManager.giteaBaseUrl) {
|
|
134
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
119
135
|
try {
|
|
120
136
|
const axios = require('axios');
|
|
121
|
-
const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${
|
|
137
|
+
const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}`, {
|
|
122
138
|
headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
|
|
123
139
|
});
|
|
124
140
|
results.providers.gitea = { success: true, issue: result.data };
|
|
@@ -133,20 +149,19 @@ class GitIssuesTool {
|
|
|
133
149
|
const issue_number = params.issue_number;
|
|
134
150
|
if (!issue_number)
|
|
135
151
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
152
|
+
if (!repo)
|
|
153
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
|
|
136
154
|
const results = { success: true, providers: {} };
|
|
137
155
|
// GitHub
|
|
138
|
-
if (ctx.providerManager.github) {
|
|
156
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
139
157
|
try {
|
|
140
158
|
const result = await ctx.providerManager.github.rest.issues.update({
|
|
141
|
-
owner:
|
|
159
|
+
owner: githubOwner,
|
|
142
160
|
repo: repo,
|
|
143
|
-
issue_number,
|
|
161
|
+
issue_number: issue_number,
|
|
144
162
|
title: params.title,
|
|
145
163
|
body: params.body,
|
|
146
164
|
state: params.state,
|
|
147
|
-
labels: params.labels,
|
|
148
|
-
assignees: params.assignees,
|
|
149
|
-
milestone: params.milestone,
|
|
150
165
|
});
|
|
151
166
|
results.providers.github = { success: true, issue: result.data };
|
|
152
167
|
}
|
|
@@ -155,10 +170,10 @@ class GitIssuesTool {
|
|
|
155
170
|
}
|
|
156
171
|
}
|
|
157
172
|
// Gitea
|
|
158
|
-
if (ctx.providerManager.giteaBaseUrl) {
|
|
173
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
159
174
|
try {
|
|
160
175
|
const axios = require('axios');
|
|
161
|
-
const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${
|
|
176
|
+
const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}`, {
|
|
162
177
|
title: params.title,
|
|
163
178
|
body: params.body,
|
|
164
179
|
state: params.state,
|
|
@@ -177,12 +192,14 @@ class GitIssuesTool {
|
|
|
177
192
|
const issue_number = params.issue_number;
|
|
178
193
|
if (!issue_number)
|
|
179
194
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
195
|
+
if (!repo)
|
|
196
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
|
|
180
197
|
const results = { success: true, providers: {} };
|
|
181
198
|
// GitHub
|
|
182
|
-
if (ctx.providerManager.github) {
|
|
199
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
183
200
|
try {
|
|
184
201
|
const result = await ctx.providerManager.github.rest.issues.update({
|
|
185
|
-
owner:
|
|
202
|
+
owner: githubOwner,
|
|
186
203
|
repo: repo,
|
|
187
204
|
issue_number,
|
|
188
205
|
state: 'closed',
|
|
@@ -194,10 +211,10 @@ class GitIssuesTool {
|
|
|
194
211
|
}
|
|
195
212
|
}
|
|
196
213
|
// Gitea
|
|
197
|
-
if (ctx.providerManager.giteaBaseUrl) {
|
|
214
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
198
215
|
try {
|
|
199
216
|
const axios = require('axios');
|
|
200
|
-
const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${
|
|
217
|
+
const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}`, { state: 'closed' }, {
|
|
201
218
|
headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
|
|
202
219
|
});
|
|
203
220
|
results.providers.gitea = { success: true, issue: result.data };
|
|
@@ -215,12 +232,14 @@ class GitIssuesTool {
|
|
|
215
232
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
216
233
|
if (!comment_body)
|
|
217
234
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'comment_body is required');
|
|
235
|
+
if (!repo)
|
|
236
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
|
|
218
237
|
const results = { success: true, providers: {} };
|
|
219
238
|
// GitHub
|
|
220
|
-
if (ctx.providerManager.github) {
|
|
239
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
221
240
|
try {
|
|
222
241
|
const result = await ctx.providerManager.github.rest.issues.createComment({
|
|
223
|
-
owner:
|
|
242
|
+
owner: githubOwner,
|
|
224
243
|
repo: repo,
|
|
225
244
|
issue_number,
|
|
226
245
|
body: comment_body,
|
|
@@ -232,10 +251,10 @@ class GitIssuesTool {
|
|
|
232
251
|
}
|
|
233
252
|
}
|
|
234
253
|
// Gitea
|
|
235
|
-
if (ctx.providerManager.giteaBaseUrl) {
|
|
254
|
+
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
236
255
|
try {
|
|
237
256
|
const axios = require('axios');
|
|
238
|
-
const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${
|
|
257
|
+
const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}/comments`, { body: comment_body }, {
|
|
239
258
|
headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
|
|
240
259
|
});
|
|
241
260
|
results.providers.gitea = { success: true, comment: result.data };
|
|
@@ -250,12 +269,14 @@ class GitIssuesTool {
|
|
|
250
269
|
const query = params.query;
|
|
251
270
|
if (!query)
|
|
252
271
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'query is required');
|
|
272
|
+
if (!repo)
|
|
273
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
|
|
253
274
|
const results = { success: true, providers: {} };
|
|
254
275
|
// GitHub
|
|
255
|
-
if (ctx.providerManager.github) {
|
|
276
|
+
if (ctx.providerManager.github && githubOwner) {
|
|
256
277
|
try {
|
|
257
278
|
const result = await ctx.providerManager.github.rest.search.issuesAndPullRequests({
|
|
258
|
-
q: `${query} repo:${
|
|
279
|
+
q: `${query} repo:${githubOwner}/${repo} type:issue`,
|
|
259
280
|
sort: params.search_sort || 'created',
|
|
260
281
|
order: params.search_order || 'desc',
|
|
261
282
|
});
|
package/dist/tools/gitRemote.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.GitRemoteTool = 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");
|
|
9
10
|
class GitRemoteTool {
|
|
10
11
|
constructor() {
|
|
11
12
|
this.name = 'git-remote';
|
|
@@ -20,10 +21,25 @@ class GitRemoteTool {
|
|
|
20
21
|
const git = (0, simple_git_1.default)({ baseDir: projectPath });
|
|
21
22
|
switch (action) {
|
|
22
23
|
case 'add': {
|
|
23
|
-
const name = params.name;
|
|
24
|
-
|
|
25
|
-
if
|
|
26
|
-
|
|
24
|
+
const name = params.name || 'origin';
|
|
25
|
+
let url = params.url;
|
|
26
|
+
// Auto-construct URL if not provided
|
|
27
|
+
if (!url) {
|
|
28
|
+
const { repoName, githubOwner, giteaOwner } = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
29
|
+
if (name === 'github' && githubOwner) {
|
|
30
|
+
url = (0, repoHelpers_1.buildGitHubUrl)(githubOwner, repoName);
|
|
31
|
+
}
|
|
32
|
+
else if (name === 'gitea' && giteaOwner) {
|
|
33
|
+
url = (0, repoHelpers_1.buildGiteaUrl)(giteaOwner, repoName);
|
|
34
|
+
}
|
|
35
|
+
else if (name === 'origin' && githubOwner) {
|
|
36
|
+
// Default to GitHub for 'origin'
|
|
37
|
+
url = (0, repoHelpers_1.buildGitHubUrl)(githubOwner, repoName);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'url is required or set GITHUB_USERNAME/GITEA_USERNAME in environment');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
27
43
|
await git.addRemote(name, url);
|
|
28
44
|
return { success: true, remote: name, url };
|
|
29
45
|
}
|
|
@@ -48,10 +64,25 @@ class GitRemoteTool {
|
|
|
48
64
|
return { success: true, remotes };
|
|
49
65
|
}
|
|
50
66
|
case 'set-url': {
|
|
51
|
-
const name = params.name;
|
|
52
|
-
|
|
53
|
-
if
|
|
54
|
-
|
|
67
|
+
const name = params.name || 'origin';
|
|
68
|
+
let url = params.url;
|
|
69
|
+
// Auto-construct URL if not provided
|
|
70
|
+
if (!url) {
|
|
71
|
+
const { repoName, githubOwner, giteaOwner } = (0, repoHelpers_1.getRepoInfo)(projectPath);
|
|
72
|
+
if (name === 'github' && githubOwner) {
|
|
73
|
+
url = (0, repoHelpers_1.buildGitHubUrl)(githubOwner, repoName);
|
|
74
|
+
}
|
|
75
|
+
else if (name === 'gitea' && giteaOwner) {
|
|
76
|
+
url = (0, repoHelpers_1.buildGiteaUrl)(giteaOwner, repoName);
|
|
77
|
+
}
|
|
78
|
+
else if (name === 'origin' && githubOwner) {
|
|
79
|
+
// Default to GitHub for 'origin'
|
|
80
|
+
url = (0, repoHelpers_1.buildGitHubUrl)(githubOwner, repoName);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'url is required or set GITHUB_USERNAME/GITEA_USERNAME in environment');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
55
86
|
await git.remote(['set-url', name, url]);
|
|
56
87
|
return { success: true, remote: name, url };
|
|
57
88
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions to extract repository information automatically
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Extract repository name from project path
|
|
6
|
+
*/
|
|
7
|
+
export declare function getRepoNameFromPath(projectPath: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Get GitHub owner from environment
|
|
10
|
+
*/
|
|
11
|
+
export declare function getGitHubOwner(): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Get Gitea owner from environment
|
|
14
|
+
*/
|
|
15
|
+
export declare function getGiteaOwner(): string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Get Gitea base URL from environment
|
|
18
|
+
*/
|
|
19
|
+
export declare function getGiteaUrl(): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Build GitHub repository URL
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildGitHubUrl(owner: string, repo: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Build Gitea repository URL with credentials
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildGiteaUrl(owner: string, repo: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Get repository info from project path and environment
|
|
30
|
+
*/
|
|
31
|
+
export declare function getRepoInfo(projectPath: string): {
|
|
32
|
+
repoName: string;
|
|
33
|
+
githubOwner: string | undefined;
|
|
34
|
+
giteaOwner: string | undefined;
|
|
35
|
+
githubUrl: string | undefined;
|
|
36
|
+
giteaUrl: string | undefined;
|
|
37
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getRepoNameFromPath = getRepoNameFromPath;
|
|
7
|
+
exports.getGitHubOwner = getGitHubOwner;
|
|
8
|
+
exports.getGiteaOwner = getGiteaOwner;
|
|
9
|
+
exports.getGiteaUrl = getGiteaUrl;
|
|
10
|
+
exports.buildGitHubUrl = buildGitHubUrl;
|
|
11
|
+
exports.buildGiteaUrl = buildGiteaUrl;
|
|
12
|
+
exports.getRepoInfo = getRepoInfo;
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
/**
|
|
15
|
+
* Helper functions to extract repository information automatically
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Extract repository name from project path
|
|
19
|
+
*/
|
|
20
|
+
function getRepoNameFromPath(projectPath) {
|
|
21
|
+
return path_1.default.basename(projectPath).replace(/\s+/g, '-');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get GitHub owner from environment
|
|
25
|
+
*/
|
|
26
|
+
function getGitHubOwner() {
|
|
27
|
+
return process.env.GITHUB_USERNAME;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get Gitea owner from environment
|
|
31
|
+
*/
|
|
32
|
+
function getGiteaOwner() {
|
|
33
|
+
return process.env.GITEA_USERNAME;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get Gitea base URL from environment
|
|
37
|
+
*/
|
|
38
|
+
function getGiteaUrl() {
|
|
39
|
+
return process.env.GITEA_URL;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build GitHub repository URL
|
|
43
|
+
*/
|
|
44
|
+
function buildGitHubUrl(owner, repo) {
|
|
45
|
+
return `https://github.com/${owner}/${repo}.git`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build Gitea repository URL with credentials
|
|
49
|
+
*/
|
|
50
|
+
function buildGiteaUrl(owner, repo) {
|
|
51
|
+
const baseUrl = getGiteaUrl();
|
|
52
|
+
const token = process.env.GITEA_TOKEN;
|
|
53
|
+
if (!baseUrl) {
|
|
54
|
+
throw new Error('GITEA_URL not configured');
|
|
55
|
+
}
|
|
56
|
+
if (!token) {
|
|
57
|
+
throw new Error('GITEA_TOKEN not configured');
|
|
58
|
+
}
|
|
59
|
+
// Remove protocol from baseUrl
|
|
60
|
+
const urlWithoutProtocol = baseUrl.replace(/^https?:\/\//, '');
|
|
61
|
+
return `http://${owner}:${token}@${urlWithoutProtocol}/${owner}/${repo}.git`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get repository info from project path and environment
|
|
65
|
+
*/
|
|
66
|
+
function getRepoInfo(projectPath) {
|
|
67
|
+
const repoName = getRepoNameFromPath(projectPath);
|
|
68
|
+
const githubOwner = getGitHubOwner();
|
|
69
|
+
const giteaOwner = getGiteaOwner();
|
|
70
|
+
return {
|
|
71
|
+
repoName,
|
|
72
|
+
githubOwner,
|
|
73
|
+
giteaOwner,
|
|
74
|
+
githubUrl: githubOwner ? buildGitHubUrl(githubOwner, repoName) : undefined,
|
|
75
|
+
giteaUrl: giteaOwner ? buildGiteaUrl(giteaOwner, repoName) : undefined,
|
|
76
|
+
};
|
|
77
|
+
}
|
package/package.json
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "Professional MCP server for Git operations - automatic dual-provider execution (GitHub + Gitea), no
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"bin": {
|
|
8
|
-
"git-mcp": "dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist/",
|
|
12
|
-
"README.md",
|
|
13
|
-
"LICENSE",
|
|
14
|
-
"package.json"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc",
|
|
18
|
-
"start": "node dist/index.js",
|
|
19
|
-
"dev": "ts-node src/index.ts",
|
|
20
|
-
"test": "jest",
|
|
21
|
-
"test:watch": "jest --watch",
|
|
22
|
-
"semantic-release": "semantic-release",
|
|
23
|
-
"prepublishOnly": "npm run build",
|
|
24
|
-
"clean": "rimraf dist",
|
|
25
|
-
"release": "npm run build && npm version patch && git push origin --follow-tags"
|
|
26
|
-
},
|
|
27
|
-
"keywords": [
|
|
28
|
-
"mcp",
|
|
29
|
-
"git",
|
|
30
|
-
"github",
|
|
31
|
-
"gitea",
|
|
32
|
-
"universal-mode",
|
|
33
|
-
"multi-provider",
|
|
34
|
-
"model-context-protocol",
|
|
35
|
-
"ai-agent",
|
|
36
|
-
"version-control",
|
|
37
|
-
"automation",
|
|
38
|
-
"cli",
|
|
39
|
-
"developer-tools",
|
|
40
|
-
"workflow",
|
|
41
|
-
"repository-management"
|
|
42
|
-
],
|
|
43
|
-
"author": "Andre Buzeli",
|
|
44
|
-
"license": "MIT",
|
|
45
|
-
"repository": {
|
|
46
|
-
"type": "git",
|
|
47
|
-
"url": "https://github.com/andrebuzeli/git-mcp.git"
|
|
48
|
-
},
|
|
49
|
-
"bugs": {
|
|
50
|
-
"url": "https://github.com/andrebuzeli/git-mcp/issues"
|
|
51
|
-
},
|
|
52
|
-
"homepage": "https://github.com/andrebuzeli/git-mcp#readme",
|
|
53
|
-
"dependencies": {
|
|
54
|
-
"@modelcontextprotocol/sdk": "^0.4.0",
|
|
55
|
-
"@octokit/rest": "^20.0.0",
|
|
56
|
-
"axios": "^1.6.0",
|
|
57
|
-
"simple-git": "^3.20.0",
|
|
58
|
-
"express": "^4.18.2",
|
|
59
|
-
"body-parser": "^1.20.2",
|
|
60
|
-
"ajv": "^8.12.0"
|
|
61
|
-
},
|
|
62
|
-
"devDependencies": {
|
|
63
|
-
"semantic-release": "^20.1.0",
|
|
64
|
-
"@semantic-release/npm": "^10.0.0",
|
|
65
|
-
"@semantic-release/github": "^9.0.0",
|
|
66
|
-
"@semantic-release/commit-analyzer": "^10.0.0",
|
|
67
|
-
"@semantic-release/release-notes-generator": "^10.0.0",
|
|
68
|
-
"@types/node": "^20.0.0",
|
|
69
|
-
"@types/express": "^4.17.17",
|
|
70
|
-
"@types/body-parser": "^1.19.2",
|
|
71
|
-
"@types/jest": "^29.0.0",
|
|
72
|
-
"typescript": "^5.0.0",
|
|
73
|
-
"jest": "^29.0.0",
|
|
74
|
-
"ts-node": "^10.0.0",
|
|
75
|
-
"ts-jest": "^29.0.0",
|
|
76
|
-
"rimraf": "^5.0.0"
|
|
77
|
-
},
|
|
78
|
-
"engines": {
|
|
79
|
-
"node": ">=18.0.0"
|
|
80
|
-
},
|
|
81
|
-
"publishConfig": {
|
|
82
|
-
"access": "public"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@andrebuzeli/git-mcp",
|
|
3
|
+
"version": "5.9.0",
|
|
4
|
+
"description": "Professional MCP server for Git operations - automatic dual-provider execution (GitHub + Gitea), intelligent parameter extraction from environment and projectPath, no redundant parameters needed, organized responses by provider, enhanced security and safety features",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"git-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"package.json"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"dev": "ts-node src/index.ts",
|
|
20
|
+
"test": "jest",
|
|
21
|
+
"test:watch": "jest --watch",
|
|
22
|
+
"semantic-release": "semantic-release",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"clean": "rimraf dist",
|
|
25
|
+
"release": "npm run build && npm version patch && git push origin --follow-tags"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"mcp",
|
|
29
|
+
"git",
|
|
30
|
+
"github",
|
|
31
|
+
"gitea",
|
|
32
|
+
"universal-mode",
|
|
33
|
+
"multi-provider",
|
|
34
|
+
"model-context-protocol",
|
|
35
|
+
"ai-agent",
|
|
36
|
+
"version-control",
|
|
37
|
+
"automation",
|
|
38
|
+
"cli",
|
|
39
|
+
"developer-tools",
|
|
40
|
+
"workflow",
|
|
41
|
+
"repository-management"
|
|
42
|
+
],
|
|
43
|
+
"author": "Andre Buzeli",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/andrebuzeli/git-mcp.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/andrebuzeli/git-mcp/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/andrebuzeli/git-mcp#readme",
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@modelcontextprotocol/sdk": "^0.4.0",
|
|
55
|
+
"@octokit/rest": "^20.0.0",
|
|
56
|
+
"axios": "^1.6.0",
|
|
57
|
+
"simple-git": "^3.20.0",
|
|
58
|
+
"express": "^4.18.2",
|
|
59
|
+
"body-parser": "^1.20.2",
|
|
60
|
+
"ajv": "^8.12.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"semantic-release": "^20.1.0",
|
|
64
|
+
"@semantic-release/npm": "^10.0.0",
|
|
65
|
+
"@semantic-release/github": "^9.0.0",
|
|
66
|
+
"@semantic-release/commit-analyzer": "^10.0.0",
|
|
67
|
+
"@semantic-release/release-notes-generator": "^10.0.0",
|
|
68
|
+
"@types/node": "^20.0.0",
|
|
69
|
+
"@types/express": "^4.17.17",
|
|
70
|
+
"@types/body-parser": "^1.19.2",
|
|
71
|
+
"@types/jest": "^29.0.0",
|
|
72
|
+
"typescript": "^5.0.0",
|
|
73
|
+
"jest": "^29.0.0",
|
|
74
|
+
"ts-node": "^10.0.0",
|
|
75
|
+
"ts-jest": "^29.0.0",
|
|
76
|
+
"rimraf": "^5.0.0"
|
|
77
|
+
},
|
|
78
|
+
"engines": {
|
|
79
|
+
"node": ">=18.0.0"
|
|
80
|
+
},
|
|
81
|
+
"publishConfig": {
|
|
82
|
+
"access": "public"
|
|
83
|
+
}
|
|
84
|
+
}
|