@andrebuzeli/git-mcp 5.8.4 → 6.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/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 readline = __importStar(require("readline"));
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,157 +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
- // MCP JSON-RPC over stdio
113
- const rl = readline.createInterface({
114
- input: process.stdin,
115
- output: process.stdout,
116
- terminal: false
117
- });
118
- rl.on('line', async (line) => {
119
- let request;
120
- let id = undefined;
121
- try {
122
- request = JSON.parse(line);
123
- id = request.id;
124
- const { jsonrpc, method, params } = request;
125
- // Only respond if there's an id (not a notification)
126
- if (id === undefined) {
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)
127
80
  if (process.env.DEBUG) {
128
- console.error('Received notification (no id), not responding');
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(', ')}`);
129
84
  }
130
- return;
131
- }
132
- let response = { jsonrpc: '2.0', id };
133
- switch (method) {
134
- case 'initialize':
135
- response.result = {
136
- protocolVersion: '2024-11-05',
137
- capabilities: {
138
- tools: {},
139
- resources: {}
140
- },
141
- serverInfo: {
142
- name: 'git-mcp',
143
- version: '5.8.3'
144
- }
145
- };
146
- break;
147
- case 'tools/list':
148
- response.result = {
149
- tools: tools.map(t => ({
150
- name: t.name,
151
- description: t.description,
152
- inputSchema: {
153
- type: 'object',
154
- properties: {},
155
- required: []
156
- }
157
- }))
158
- };
159
- break;
160
- case 'tools/call':
161
- const toolName = params?.name;
162
- const tool = toolRegistry.get(toolName);
163
- if (!tool) {
164
- response.error = {
165
- code: -32601,
166
- message: `Tool not found: ${toolName}`
167
- };
168
- }
169
- else {
170
- try {
171
- const result = await tool.handle(params?.arguments ?? {}, { providerManager });
172
- response.result = {
173
- content: [
174
- {
175
- type: 'text',
176
- text: JSON.stringify(result, null, 2)
177
- }
178
- ]
179
- };
180
- }
181
- catch (err) {
182
- response.error = {
183
- code: -32603,
184
- message: err.message || String(err)
185
- };
186
- }
187
- }
188
- break;
189
- case 'resources/list':
190
- response.result = {
191
- resources: Array.from(resourceRegistry.values()).map(r => ({
192
- uri: r.uri,
193
- name: r.name,
194
- description: r.description,
195
- mimeType: r.mimeType
196
- }))
197
- };
198
- break;
199
- case 'resources/read':
200
- const uri = params?.uri;
201
- const resource = resourceRegistry.get(uri);
202
- if (!resource) {
203
- response.error = {
204
- code: -32601,
205
- message: `Resource not found: ${uri}`
206
- };
207
- }
208
- else {
209
- response.result = {
210
- contents: [
211
- {
212
- uri: resource.uri,
213
- mimeType: resource.mimeType,
214
- text: resource.content
215
- }
216
- ]
217
- };
218
- }
219
- break;
220
- default:
221
- response.error = {
222
- code: -32601,
223
- message: `Method not found: ${method}`
224
- };
225
- }
226
- console.log(JSON.stringify(response));
227
- }
228
- catch (err) {
229
- // Only send error response if we have a valid id
230
- if (id !== undefined) {
231
- const errorResponse = {
232
- jsonrpc: '2.0',
233
- id: id,
234
- error: {
235
- code: -32700,
236
- message: `Parse error: ${err.message || String(err)}`
237
- }
238
- };
239
- console.log(JSON.stringify(errorResponse));
240
- }
241
- if (process.env.DEBUG) {
242
- console.error('Error processing request:', err);
243
- }
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;
244
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);
245
117
  });
246
118
  // Keep process alive
247
119
  process.on('SIGINT', () => {
248
- if (process.env.DEBUG) {
249
- console.error('\n👋 Shutting down server...');
250
- }
251
- process.exit(0);
120
+ console.log('\n👋 Shutting down server...');
121
+ server.close(() => {
122
+ console.log('Server closed');
123
+ process.exit(0);
124
+ });
252
125
  });
253
126
  }
254
127
  main().catch(err => {
@@ -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,27 @@ 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
- const owner = params.owner;
15
- const repo = params.repo;
15
+ // Auto-extract repo info if projectPath is provided
16
+ let repo = params.repo;
17
+ if (!repo && params.projectPath) {
18
+ const repoInfo = (0, repoHelpers_1.getRepoInfo)(params.projectPath);
19
+ repo = repoInfo.repoName;
20
+ }
21
+ // Each provider uses its own username from env
22
+ const githubOwner = process.env.GITHUB_USERNAME;
23
+ const giteaOwner = process.env.GITEA_USERNAME;
16
24
  switch (action) {
17
25
  case 'create': {
18
26
  if (!params.title)
19
27
  throw new errors_1.MCPError('VALIDATION_ERROR', 'title is required');
28
+ if (!repo)
29
+ throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
20
30
  const results = { success: true, providers: {} };
21
31
  // GitHub
22
- if (ctx.providerManager.github) {
32
+ if (ctx.providerManager.github && githubOwner) {
23
33
  try {
24
34
  const result = await ctx.providerManager.github.rest.issues.create({
25
- owner: owner || process.env.GITHUB_USERNAME,
35
+ owner: githubOwner,
26
36
  repo: repo,
27
37
  title: params.title,
28
38
  body: params.body,
@@ -37,10 +47,10 @@ class GitIssuesTool {
37
47
  }
38
48
  }
39
49
  // Gitea
40
- if (ctx.providerManager.giteaBaseUrl) {
50
+ if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
41
51
  try {
42
52
  const axios = require('axios');
43
- const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${owner || process.env.GITEA_USERNAME}/${repo}/issues`, {
53
+ const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues`, {
44
54
  title: params.title,
45
55
  body: params.body,
46
56
  labels: params.labels?.map((l) => ({ name: l })),
@@ -57,12 +67,14 @@ class GitIssuesTool {
57
67
  return results;
58
68
  }
59
69
  case 'list': {
70
+ if (!repo)
71
+ throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
60
72
  const results = { success: true, providers: {} };
61
73
  // GitHub
62
- if (ctx.providerManager.github) {
74
+ if (ctx.providerManager.github && githubOwner) {
63
75
  try {
64
76
  const result = await ctx.providerManager.github.rest.issues.listForRepo({
65
- owner: owner || process.env.GITHUB_USERNAME,
77
+ owner: githubOwner,
66
78
  repo: repo,
67
79
  state: params.state_filter || 'open',
68
80
  sort: params.sort || 'created',
@@ -76,10 +88,10 @@ class GitIssuesTool {
76
88
  }
77
89
  }
78
90
  // Gitea
79
- if (ctx.providerManager.giteaBaseUrl) {
91
+ if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
80
92
  try {
81
93
  const axios = require('axios');
82
- const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${owner || process.env.GITEA_USERNAME}/${repo}/issues`, {
94
+ const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues`, {
83
95
  params: {
84
96
  state: params.state_filter || 'open',
85
97
  sort: params.sort || 'created',
@@ -99,12 +111,14 @@ class GitIssuesTool {
99
111
  const issue_number = params.issue_number;
100
112
  if (!issue_number)
101
113
  throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
114
+ if (!repo)
115
+ throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
102
116
  const results = { success: true, providers: {} };
103
117
  // GitHub
104
- if (ctx.providerManager.github) {
118
+ if (ctx.providerManager.github && githubOwner) {
105
119
  try {
106
120
  const result = await ctx.providerManager.github.rest.issues.get({
107
- owner: owner || process.env.GITHUB_USERNAME,
121
+ owner: githubOwner,
108
122
  repo: repo,
109
123
  issue_number,
110
124
  });
@@ -115,10 +129,10 @@ class GitIssuesTool {
115
129
  }
116
130
  }
117
131
  // Gitea
118
- if (ctx.providerManager.giteaBaseUrl) {
132
+ if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
119
133
  try {
120
134
  const axios = require('axios');
121
- const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${owner || process.env.GITEA_USERNAME}/${repo}/issues/${issue_number}`, {
135
+ const result = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}`, {
122
136
  headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
123
137
  });
124
138
  results.providers.gitea = { success: true, issue: result.data };
@@ -133,20 +147,19 @@ class GitIssuesTool {
133
147
  const issue_number = params.issue_number;
134
148
  if (!issue_number)
135
149
  throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
150
+ if (!repo)
151
+ throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
136
152
  const results = { success: true, providers: {} };
137
153
  // GitHub
138
- if (ctx.providerManager.github) {
154
+ if (ctx.providerManager.github && githubOwner) {
139
155
  try {
140
156
  const result = await ctx.providerManager.github.rest.issues.update({
141
- owner: owner || process.env.GITHUB_USERNAME,
157
+ owner: githubOwner,
142
158
  repo: repo,
143
- issue_number,
159
+ issue_number: issue_number,
144
160
  title: params.title,
145
161
  body: params.body,
146
162
  state: params.state,
147
- labels: params.labels,
148
- assignees: params.assignees,
149
- milestone: params.milestone,
150
163
  });
151
164
  results.providers.github = { success: true, issue: result.data };
152
165
  }
@@ -155,10 +168,10 @@ class GitIssuesTool {
155
168
  }
156
169
  }
157
170
  // Gitea
158
- if (ctx.providerManager.giteaBaseUrl) {
171
+ if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
159
172
  try {
160
173
  const axios = require('axios');
161
- const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${owner || process.env.GITEA_USERNAME}/${repo}/issues/${issue_number}`, {
174
+ const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}`, {
162
175
  title: params.title,
163
176
  body: params.body,
164
177
  state: params.state,
@@ -177,12 +190,14 @@ class GitIssuesTool {
177
190
  const issue_number = params.issue_number;
178
191
  if (!issue_number)
179
192
  throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
193
+ if (!repo)
194
+ throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
180
195
  const results = { success: true, providers: {} };
181
196
  // GitHub
182
- if (ctx.providerManager.github) {
197
+ if (ctx.providerManager.github && githubOwner) {
183
198
  try {
184
199
  const result = await ctx.providerManager.github.rest.issues.update({
185
- owner: owner || process.env.GITHUB_USERNAME,
200
+ owner: githubOwner,
186
201
  repo: repo,
187
202
  issue_number,
188
203
  state: 'closed',
@@ -194,10 +209,10 @@ class GitIssuesTool {
194
209
  }
195
210
  }
196
211
  // Gitea
197
- if (ctx.providerManager.giteaBaseUrl) {
212
+ if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
198
213
  try {
199
214
  const axios = require('axios');
200
- const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${owner || process.env.GITEA_USERNAME}/${repo}/issues/${issue_number}`, { state: 'closed' }, {
215
+ const result = await axios.patch(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}`, { state: 'closed' }, {
201
216
  headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
202
217
  });
203
218
  results.providers.gitea = { success: true, issue: result.data };
@@ -215,12 +230,14 @@ class GitIssuesTool {
215
230
  throw new errors_1.MCPError('VALIDATION_ERROR', 'issue_number is required');
216
231
  if (!comment_body)
217
232
  throw new errors_1.MCPError('VALIDATION_ERROR', 'comment_body is required');
233
+ if (!repo)
234
+ throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
218
235
  const results = { success: true, providers: {} };
219
236
  // GitHub
220
- if (ctx.providerManager.github) {
237
+ if (ctx.providerManager.github && githubOwner) {
221
238
  try {
222
239
  const result = await ctx.providerManager.github.rest.issues.createComment({
223
- owner: owner || process.env.GITHUB_USERNAME,
240
+ owner: githubOwner,
224
241
  repo: repo,
225
242
  issue_number,
226
243
  body: comment_body,
@@ -232,10 +249,10 @@ class GitIssuesTool {
232
249
  }
233
250
  }
234
251
  // Gitea
235
- if (ctx.providerManager.giteaBaseUrl) {
252
+ if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
236
253
  try {
237
254
  const axios = require('axios');
238
- const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${owner || process.env.GITEA_USERNAME}/${repo}/issues/${issue_number}/comments`, { body: comment_body }, {
255
+ const result = await axios.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues/${issue_number}/comments`, { body: comment_body }, {
239
256
  headers: { Authorization: `token ${ctx.providerManager.giteaToken}` }
240
257
  });
241
258
  results.providers.gitea = { success: true, comment: result.data };
@@ -250,12 +267,14 @@ class GitIssuesTool {
250
267
  const query = params.query;
251
268
  if (!query)
252
269
  throw new errors_1.MCPError('VALIDATION_ERROR', 'query is required');
270
+ if (!repo)
271
+ throw new errors_1.MCPError('VALIDATION_ERROR', 'repo is required (or provide projectPath)');
253
272
  const results = { success: true, providers: {} };
254
273
  // GitHub
255
- if (ctx.providerManager.github) {
274
+ if (ctx.providerManager.github && githubOwner) {
256
275
  try {
257
276
  const result = await ctx.providerManager.github.rest.search.issuesAndPullRequests({
258
- q: `${query} repo:${owner || process.env.GITHUB_USERNAME}/${repo} type:issue`,
277
+ q: `${query} repo:${githubOwner}/${repo} type:issue`,
259
278
  sort: params.search_sort || 'created',
260
279
  order: params.search_order || 'desc',
261
280
  });
@@ -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
- const url = params.url;
25
- if (!name || !url)
26
- throw new errors_1.MCPError('VALIDATION_ERROR', 'name and url are required');
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
- const url = params.url;
53
- if (!name || !url)
54
- throw new errors_1.MCPError('VALIDATION_ERROR', 'name and url are required');
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
  }
@@ -42,6 +42,7 @@ const errors_1 = require("../utils/errors");
42
42
  const axios_1 = __importDefault(require("axios"));
43
43
  const fs = __importStar(require("fs/promises"));
44
44
  const path = __importStar(require("path"));
45
+ const repoHelpers_1 = require("../utils/repoHelpers");
45
46
  /**
46
47
  * Git Upload Tool - Envia projeto completo para GitHub e Gitea automaticamente
47
48
  * Modo DUAL automático com rastreabilidade completa
@@ -56,7 +57,7 @@ class GitUploadTool {
56
57
  if (!projectPath) {
57
58
  throw new errors_1.MCPError('VALIDATION_ERROR', 'projectPath is required');
58
59
  }
59
- const repoName = params.repoName || path.basename(projectPath);
60
+ const repoName = params.repoName || (0, repoHelpers_1.getRepoNameFromPath)(projectPath);
60
61
  const description = params.description || `Project uploaded via git-upload at ${new Date().toISOString()}`;
61
62
  const isPrivate = params.private !== undefined ? params.private : true;
62
63
  const branch = params.branch || 'master';
@@ -130,8 +131,8 @@ class GitUploadTool {
130
131
  }
131
132
  // 4. Criar repositórios remotos em ambos providers
132
133
  // Nota: Cada provider precisa usar seu próprio username (Gitea é case-sensitive)
133
- const githubOwner = params.owner || process.env.GITHUB_USERNAME;
134
- const giteaOwner = params.owner || process.env.GITEA_USERNAME;
134
+ const githubOwner = process.env.GITHUB_USERNAME;
135
+ const giteaOwner = process.env.GITEA_USERNAME;
135
136
  // GITHUB
136
137
  if (ctx.providerManager.github) {
137
138
  results.traceability.uploadSteps.push({
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Helper functions to extract repository information automatically
3
+ */
4
+ /**
5
+ * Extract repository name from project path
6
+ * Normalizes: spaces to hyphens, lowercase, removes special chars
7
+ */
8
+ export declare function getRepoNameFromPath(projectPath: string): string;
9
+ /**
10
+ * Get GitHub owner from environment
11
+ */
12
+ export declare function getGitHubOwner(): string | undefined;
13
+ /**
14
+ * Get Gitea owner from environment
15
+ */
16
+ export declare function getGiteaOwner(): string | undefined;
17
+ /**
18
+ * Get Gitea base URL from environment
19
+ */
20
+ export declare function getGiteaUrl(): string | undefined;
21
+ /**
22
+ * Build GitHub repository URL
23
+ */
24
+ export declare function buildGitHubUrl(owner: string, repo: string): string;
25
+ /**
26
+ * Build Gitea repository URL with credentials
27
+ */
28
+ export declare function buildGiteaUrl(owner: string, repo: string): string;
29
+ /**
30
+ * Get repository info from project path and environment
31
+ */
32
+ export declare function getRepoInfo(projectPath: string): {
33
+ repoName: string;
34
+ githubOwner: string | undefined;
35
+ giteaOwner: string | undefined;
36
+ githubUrl: string | undefined;
37
+ giteaUrl: string | undefined;
38
+ };
@@ -0,0 +1,81 @@
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
+ * Normalizes: spaces to hyphens, lowercase, removes special chars
20
+ */
21
+ function getRepoNameFromPath(projectPath) {
22
+ return path_1.default.basename(projectPath)
23
+ .toLowerCase()
24
+ .replace(/\s+/g, '-')
25
+ .replace(/[^a-z0-9-]/g, '');
26
+ }
27
+ /**
28
+ * Get GitHub owner from environment
29
+ */
30
+ function getGitHubOwner() {
31
+ return process.env.GITHUB_USERNAME;
32
+ }
33
+ /**
34
+ * Get Gitea owner from environment
35
+ */
36
+ function getGiteaOwner() {
37
+ return process.env.GITEA_USERNAME;
38
+ }
39
+ /**
40
+ * Get Gitea base URL from environment
41
+ */
42
+ function getGiteaUrl() {
43
+ return process.env.GITEA_URL;
44
+ }
45
+ /**
46
+ * Build GitHub repository URL
47
+ */
48
+ function buildGitHubUrl(owner, repo) {
49
+ return `https://github.com/${owner}/${repo}.git`;
50
+ }
51
+ /**
52
+ * Build Gitea repository URL with credentials
53
+ */
54
+ function buildGiteaUrl(owner, repo) {
55
+ const baseUrl = getGiteaUrl();
56
+ const token = process.env.GITEA_TOKEN;
57
+ if (!baseUrl) {
58
+ throw new Error('GITEA_URL not configured');
59
+ }
60
+ if (!token) {
61
+ throw new Error('GITEA_TOKEN not configured');
62
+ }
63
+ // Remove protocol from baseUrl
64
+ const urlWithoutProtocol = baseUrl.replace(/^https?:\/\//, '');
65
+ return `http://${owner}:${token}@${urlWithoutProtocol}/${owner}/${repo}.git`;
66
+ }
67
+ /**
68
+ * Get repository info from project path and environment
69
+ */
70
+ function getRepoInfo(projectPath) {
71
+ const repoName = getRepoNameFromPath(projectPath);
72
+ const githubOwner = getGitHubOwner();
73
+ const giteaOwner = getGiteaOwner();
74
+ return {
75
+ repoName,
76
+ githubOwner,
77
+ giteaOwner,
78
+ githubUrl: githubOwner ? buildGitHubUrl(githubOwner, repoName) : undefined,
79
+ giteaUrl: giteaOwner ? buildGiteaUrl(giteaOwner, repoName) : undefined,
80
+ };
81
+ }
package/package.json CHANGED
@@ -1,84 +1,84 @@
1
- {
2
- "name": "@andrebuzeli/git-mcp",
3
- "version": "5.8.4",
4
- "description": "Professional MCP server for Git operations - automatic dual-provider execution (GitHub + Gitea), no provider parameter 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
- }
1
+ {
2
+ "name": "@andrebuzeli/git-mcp",
3
+ "version": "6.0.0",
4
+ "description": "Professional MCP server for Git operations - FULLY AUTONOMOUS: each provider uses own username from env, repo name auto-normalized from projectPath (spaces→hyphens, lowercase, safe chars), zero redundant parameters, automatic dual-provider execution (GitHub + Gitea)",
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
+ }