@andrebuzeli/git-mcp 10.0.4 → 10.0.6
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 +50 -10
- package/dist/server-new.d.ts +2 -0
- package/dist/server-new.js +224 -0
- package/dist/tools/gitFiles-new.d.ts +89 -0
- package/dist/tools/gitFiles-new.js +335 -0
- package/dist/tools/gitFiles.d.ts +18 -15
- package/dist/tools/gitFiles.js +54 -15
- package/dist/tools/gitUpload.d.ts +1 -0
- package/dist/tools/gitUpload.js +76 -29
- package/dist/tools/gitWorkflow.d.ts +8 -0
- package/dist/tools/gitWorkflow.js +37 -3
- package/dist/utils/cache.d.ts +96 -0
- package/dist/utils/cache.js +208 -0
- package/dist/utils/gitAdapter.js +19 -3
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.js +140 -0
- package/dist/utils/rateLimiter.d.ts +113 -0
- package/dist/utils/rateLimiter.js +257 -0
- package/dist/utils/validation.d.ts +115 -0
- package/dist/utils/validation.js +270 -0
- package/package.json +1 -1
- package/dist/config.d.ts +0 -5
- package/dist/config.js +0 -35
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sistema de validação e tratamento de erros aprimorado para Git-MCP
|
|
3
|
+
* Fornece validação robusta de parâmetros e mensagens de erro detalhadas
|
|
4
|
+
*/
|
|
5
|
+
import { MCPError } from './errors.js';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Tipos de validação disponíveis
|
|
9
|
+
*/
|
|
10
|
+
export var ValidationType;
|
|
11
|
+
(function (ValidationType) {
|
|
12
|
+
ValidationType["REQUIRED"] = "required";
|
|
13
|
+
ValidationType["STRING"] = "string";
|
|
14
|
+
ValidationType["NUMBER"] = "number";
|
|
15
|
+
ValidationType["BOOLEAN"] = "boolean";
|
|
16
|
+
ValidationType["ARRAY"] = "array";
|
|
17
|
+
ValidationType["OBJECT"] = "object";
|
|
18
|
+
ValidationType["PATH"] = "path";
|
|
19
|
+
ValidationType["URL"] = "url";
|
|
20
|
+
ValidationType["EMAIL"] = "email";
|
|
21
|
+
ValidationType["ENUM"] = "enum";
|
|
22
|
+
ValidationType["PATTERN"] = "pattern";
|
|
23
|
+
ValidationType["RANGE"] = "range";
|
|
24
|
+
ValidationType["LENGTH"] = "length";
|
|
25
|
+
})(ValidationType || (ValidationType = {}));
|
|
26
|
+
/**
|
|
27
|
+
* Validador robusto de parâmetros
|
|
28
|
+
*/
|
|
29
|
+
export class ParameterValidator {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.rules = [];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Adiciona regra de validação
|
|
35
|
+
*/
|
|
36
|
+
addRule(rule) {
|
|
37
|
+
this.rules.push(rule);
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Valida parâmetros contra as regras
|
|
42
|
+
*/
|
|
43
|
+
validate(params) {
|
|
44
|
+
const errors = [];
|
|
45
|
+
const warnings = [];
|
|
46
|
+
for (const rule of this.rules) {
|
|
47
|
+
const value = params[rule.field];
|
|
48
|
+
// Validação de campo obrigatório
|
|
49
|
+
if (rule.required && (value === undefined || value === null || value === '')) {
|
|
50
|
+
errors.push({
|
|
51
|
+
field: rule.field,
|
|
52
|
+
type: ValidationType.REQUIRED,
|
|
53
|
+
message: rule.message || `Field '${rule.field}' is required`,
|
|
54
|
+
value,
|
|
55
|
+
});
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Se não é obrigatório e não tem valor, pular validações adicionais
|
|
59
|
+
if (!rule.required && (value === undefined || value === null || value === '')) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// Validação por tipo
|
|
63
|
+
const typeError = this.validateType(rule, value, params);
|
|
64
|
+
if (typeError) {
|
|
65
|
+
errors.push(typeError);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Validação customizada
|
|
69
|
+
if (rule.custom) {
|
|
70
|
+
const customResult = rule.custom(value, params);
|
|
71
|
+
if (customResult !== true) {
|
|
72
|
+
errors.push({
|
|
73
|
+
field: rule.field,
|
|
74
|
+
type: ValidationType.PATTERN,
|
|
75
|
+
message: typeof customResult === 'string' ? customResult : `Field '${rule.field}' failed custom validation`,
|
|
76
|
+
value,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Avisos e sugestões
|
|
81
|
+
this.addWarnings(rule, value, warnings);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
isValid: errors.length === 0,
|
|
85
|
+
errors,
|
|
86
|
+
warnings,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Valida tipo do valor
|
|
91
|
+
*/
|
|
92
|
+
validateType(rule, value, allParams) {
|
|
93
|
+
switch (rule.type) {
|
|
94
|
+
case ValidationType.STRING:
|
|
95
|
+
if (typeof value !== 'string') {
|
|
96
|
+
return {
|
|
97
|
+
field: rule.field,
|
|
98
|
+
type: ValidationType.STRING,
|
|
99
|
+
message: rule.message || `Field '${rule.field}' must be a string`,
|
|
100
|
+
value,
|
|
101
|
+
expected: 'string',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Validação de comprimento
|
|
105
|
+
if (rule.minLength && value.length < rule.minLength) {
|
|
106
|
+
return {
|
|
107
|
+
field: rule.field,
|
|
108
|
+
type: ValidationType.LENGTH,
|
|
109
|
+
message: rule.message || `Field '${rule.field}' must be at least ${rule.minLength} characters`,
|
|
110
|
+
value,
|
|
111
|
+
expected: `>= ${rule.minLength}`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (rule.maxLength && value.length > rule.maxLength) {
|
|
115
|
+
return {
|
|
116
|
+
field: rule.field,
|
|
117
|
+
type: ValidationType.LENGTH,
|
|
118
|
+
message: rule.message || `Field '${rule.field}' must be at most ${rule.maxLength} characters`,
|
|
119
|
+
value,
|
|
120
|
+
expected: `<= ${rule.maxLength}`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Validação de padrão
|
|
124
|
+
if (rule.pattern && !rule.pattern.test(value)) {
|
|
125
|
+
return {
|
|
126
|
+
field: rule.field,
|
|
127
|
+
type: ValidationType.PATTERN,
|
|
128
|
+
message: rule.message || `Field '${rule.field}' does not match required pattern`,
|
|
129
|
+
value,
|
|
130
|
+
expected: rule.pattern.toString(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// Validação de enum
|
|
134
|
+
if (rule.enum && !rule.enum.includes(value)) {
|
|
135
|
+
return {
|
|
136
|
+
field: rule.field,
|
|
137
|
+
type: ValidationType.ENUM,
|
|
138
|
+
message: rule.message || `Field '${rule.field}' must be one of: ${rule.enum.join(', ')}`,
|
|
139
|
+
value,
|
|
140
|
+
expected: rule.enum,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case ValidationType.PATH:
|
|
145
|
+
if (typeof value !== 'string') {
|
|
146
|
+
return {
|
|
147
|
+
field: rule.field,
|
|
148
|
+
type: ValidationType.PATH,
|
|
149
|
+
message: rule.message || `Field '${rule.field}' must be a path string`,
|
|
150
|
+
value,
|
|
151
|
+
expected: 'string',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// Verificar path traversal
|
|
155
|
+
if (value.includes('..') || value.includes('~')) {
|
|
156
|
+
return {
|
|
157
|
+
field: rule.field,
|
|
158
|
+
type: ValidationType.PATH,
|
|
159
|
+
message: rule.message || `Field '${rule.field}' contains invalid path characters`,
|
|
160
|
+
value,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
// Para outros tipos, implementação básica
|
|
166
|
+
if (rule.type === ValidationType.NUMBER && typeof value !== 'number') {
|
|
167
|
+
return {
|
|
168
|
+
field: rule.field,
|
|
169
|
+
type: ValidationType.NUMBER,
|
|
170
|
+
message: rule.message || `Field '${rule.field}' must be a number`,
|
|
171
|
+
value,
|
|
172
|
+
expected: 'number',
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Adiciona avisos baseados na validação
|
|
181
|
+
*/
|
|
182
|
+
addWarnings(rule, value, warnings) {
|
|
183
|
+
if (rule.type === ValidationType.STRING && typeof value === 'string') {
|
|
184
|
+
// Aviso para strings vazias em campos não obrigatórios
|
|
185
|
+
if (value.trim() === '' && !rule.required) {
|
|
186
|
+
warnings.push({
|
|
187
|
+
field: rule.field,
|
|
188
|
+
message: `Field '${rule.field}' is empty`,
|
|
189
|
+
suggestion: 'Consider removing this field if not needed',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (rule.type === ValidationType.PATH && typeof value === 'string') {
|
|
194
|
+
// Aviso para paths absolutos
|
|
195
|
+
if (path.isAbsolute(value)) {
|
|
196
|
+
warnings.push({
|
|
197
|
+
field: rule.field,
|
|
198
|
+
message: `Field '${rule.field}' contains an absolute path`,
|
|
199
|
+
suggestion: 'Consider using relative paths for better portability',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Limpa todas as regras
|
|
206
|
+
*/
|
|
207
|
+
clear() {
|
|
208
|
+
this.rules = [];
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Validadores pré-configurados para casos comuns
|
|
213
|
+
*/
|
|
214
|
+
export const commonValidators = {
|
|
215
|
+
/**
|
|
216
|
+
* Validador para parâmetros Git básicos
|
|
217
|
+
*/
|
|
218
|
+
gitParams: () => new ParameterValidator()
|
|
219
|
+
.addRule({ field: 'projectPath', type: ValidationType.STRING, required: true })
|
|
220
|
+
.addRule({
|
|
221
|
+
field: 'projectPath',
|
|
222
|
+
type: ValidationType.PATH,
|
|
223
|
+
custom: (value) => {
|
|
224
|
+
if (value.includes('..'))
|
|
225
|
+
return 'Path cannot contain .. (parent directory)';
|
|
226
|
+
if (value.includes('~'))
|
|
227
|
+
return 'Path cannot contain ~ (home directory)';
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
}),
|
|
231
|
+
/**
|
|
232
|
+
* Validador para ações Git
|
|
233
|
+
*/
|
|
234
|
+
gitAction: (allowedActions) => new ParameterValidator()
|
|
235
|
+
.addRule({
|
|
236
|
+
field: 'action',
|
|
237
|
+
type: ValidationType.STRING,
|
|
238
|
+
required: true,
|
|
239
|
+
enum: allowedActions,
|
|
240
|
+
}),
|
|
241
|
+
/**
|
|
242
|
+
* Validador para branches
|
|
243
|
+
*/
|
|
244
|
+
branchName: () => new ParameterValidator()
|
|
245
|
+
.addRule({
|
|
246
|
+
field: 'branchName',
|
|
247
|
+
type: ValidationType.STRING,
|
|
248
|
+
required: true,
|
|
249
|
+
pattern: /^[a-zA-Z0-9._/-]+$/,
|
|
250
|
+
message: 'Branch name can only contain letters, numbers, dots, underscores, hyphens and forward slashes',
|
|
251
|
+
}),
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Função helper para validação rápida
|
|
255
|
+
*/
|
|
256
|
+
export function validateParams(params, rules) {
|
|
257
|
+
const validator = new ParameterValidator();
|
|
258
|
+
rules.forEach(rule => validator.addRule(rule));
|
|
259
|
+
return validator.validate(params);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Função helper para validação com throw de erro
|
|
263
|
+
*/
|
|
264
|
+
export function validateOrThrow(params, rules) {
|
|
265
|
+
const result = validateParams(params, rules);
|
|
266
|
+
if (!result.isValid) {
|
|
267
|
+
const errorMessages = result.errors.map((e) => e.message).join('; ');
|
|
268
|
+
throw new MCPError('VALIDATION_ERROR', errorMessages, result.errors.map((e) => e.message));
|
|
269
|
+
}
|
|
270
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop, Trae AI, Kiro.dev). Fully autonomous DUAL execution (GitHub + Gitea APIs) with automatic username detection. Smart parameter normalization handles both 'action' and 'command' formats. All tools execute on BOTH providers simultaneously. No manual parameters needed.",
|
|
6
6
|
"main": "dist/index.js",
|
package/dist/config.d.ts
DELETED
package/dist/config.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
function maskSecret(value) {
|
|
4
|
-
if (!value)
|
|
5
|
-
return 'undefined';
|
|
6
|
-
if (value.length <= 8)
|
|
7
|
-
return '****';
|
|
8
|
-
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
9
|
-
}
|
|
10
|
-
export function loadMCPConfig(configPath) {
|
|
11
|
-
const cfgPath = path.resolve(process.cwd(), configPath ?? 'mcp.json');
|
|
12
|
-
if (!fs.existsSync(cfgPath))
|
|
13
|
-
return null;
|
|
14
|
-
try {
|
|
15
|
-
const raw = fs.readFileSync(cfgPath, 'utf8');
|
|
16
|
-
const cfg = JSON.parse(raw);
|
|
17
|
-
if (cfg.env) {
|
|
18
|
-
for (const [k, v] of Object.entries(cfg.env)) {
|
|
19
|
-
// Only set if not already present in process.env
|
|
20
|
-
if (process.env[k] == null) {
|
|
21
|
-
process.env[k] = v;
|
|
22
|
-
}
|
|
23
|
-
// Log to stderr only (stdout is used for MCP protocol)
|
|
24
|
-
if (process.env.DEBUG) {
|
|
25
|
-
console.error(`MCP config: env.${k}=${maskSecret(process.env[k])}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return cfg;
|
|
30
|
-
}
|
|
31
|
-
catch (err) {
|
|
32
|
-
console.error('Failed to parse mcp.json:', err.message ?? String(err));
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|