@andrebuzeli/git-mcp 10.0.3 → 10.0.5
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/README.md +37 -56
- package/dist/index.js +50 -13
- package/dist/server-new.d.ts +2 -0
- package/dist/server-new.js +224 -0
- package/dist/tools/gitArchive.d.ts +0 -8
- package/dist/tools/gitArchive.js +4 -12
- package/dist/tools/gitBackup.js +7 -7
- 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/gitFix.js +7 -7
- package/dist/tools/gitFix.tool.js +4 -4
- package/dist/tools/gitHistory.d.ts +2 -25
- package/dist/tools/gitHistory.js +4 -25
- package/dist/tools/gitIgnore.d.ts +2 -14
- package/dist/tools/gitIgnore.js +5 -17
- package/dist/tools/gitIssues.d.ts +0 -4
- package/dist/tools/gitIssues.js +12 -18
- package/dist/tools/gitMonitor.js +1 -1
- package/dist/tools/gitPackages.d.ts +28 -0
- package/dist/tools/gitPackages.js +29 -1
- package/dist/tools/gitPulls.d.ts +60 -0
- package/dist/tools/gitPulls.js +68 -4
- package/dist/tools/gitRelease.d.ts +43 -0
- package/dist/tools/gitRelease.js +48 -4
- package/dist/tools/gitRemote.d.ts +23 -0
- package/dist/tools/gitRemote.js +23 -0
- package/dist/tools/gitReset.d.ts +23 -0
- package/dist/tools/gitReset.js +23 -0
- package/dist/tools/gitStash.d.ts +31 -0
- package/dist/tools/gitStash.js +31 -0
- package/dist/tools/gitSync.d.ts +6 -2
- package/dist/tools/gitSync.js +10 -6
- package/dist/tools/gitTags.d.ts +31 -0
- package/dist/tools/gitTags.js +31 -0
- package/dist/tools/gitUpdate.d.ts +0 -27
- package/dist/tools/gitUpdate.js +1 -26
- package/dist/tools/gitUpload.d.ts +2 -9
- package/dist/tools/gitUpload.js +81 -42
- 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
package/README.md
CHANGED
|
@@ -102,26 +102,47 @@ npx @andrebuzeli/git-mcp@latest
|
|
|
102
102
|
|
|
103
103
|
### Configuration
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
Configure the git-mcp server in your MCP client's configuration file (e.g., Cursor's mcp.json, Claude Desktop's config, etc.):
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"git-mcp": {
|
|
111
|
+
"command": "npx",
|
|
112
|
+
"args": ["@andrebuzeli/git-mcp@latest"],
|
|
113
|
+
"env": {
|
|
114
|
+
"GITHUB_USERNAME": "your-github-username",
|
|
115
|
+
"GITHUB_TOKEN": "ghp_your_github_token_here",
|
|
116
|
+
"GITEA_USERNAME": "your-gitea-username",
|
|
117
|
+
"GITEA_TOKEN": "your_gitea_token_here",
|
|
118
|
+
"GITEA_URL": "https://your-gitea-instance.com/",
|
|
119
|
+
"DEBUG": "false"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
111
124
|
```
|
|
112
125
|
|
|
113
|
-
|
|
126
|
+
**The MCP client automatically passes these environment variables to the git-mcp server - no additional configuration files needed!**
|
|
127
|
+
|
|
128
|
+
#### Alternative: Direct Environment Variables
|
|
129
|
+
If running the server directly (not through MCP client):
|
|
130
|
+
|
|
114
131
|
```bash
|
|
132
|
+
export GITHUB_TOKEN="your_github_token"
|
|
133
|
+
export GITHUB_USERNAME="your_username"
|
|
115
134
|
export GITEA_URL="https://your-gitea-instance.com"
|
|
116
135
|
export GITEA_TOKEN="your_gitea_token"
|
|
117
136
|
export GITEA_USERNAME="your_username"
|
|
118
137
|
```
|
|
119
138
|
|
|
120
|
-
PowerShell (Windows)
|
|
139
|
+
#### PowerShell (Windows)
|
|
121
140
|
```powershell
|
|
122
141
|
$env:GITEA_URL = 'https://your-gitea-instance.com'
|
|
123
142
|
$env:GITEA_TOKEN = 'your_gitea_token'
|
|
124
143
|
$env:GITEA_USERNAME = 'your_username'
|
|
144
|
+
$env:GITHUB_TOKEN = 'your_github_token'
|
|
145
|
+
$env:GITHUB_USERNAME = 'your_username'
|
|
125
146
|
```
|
|
126
147
|
|
|
127
148
|
#### Multi-Provider Support
|
|
@@ -332,63 +353,23 @@ All tools provide detailed error messages with actionable solutions:
|
|
|
332
353
|
| `DETACHED_HEAD` | Repository in detached HEAD state | Create branch or checkout existing |
|
|
333
354
|
| `REF_LOCK_ERROR` | Cannot lock Git reference | Wait for other operations to complete |
|
|
334
355
|
|
|
335
|
-
##
|
|
336
|
-
|
|
337
|
-
### Environment Variables
|
|
338
|
-
|
|
339
|
-
**Required for GitHub:**
|
|
340
|
-
- `GITHUB_TOKEN` - Personal access token with repo permissions
|
|
341
|
-
- `GITHUB_USERNAME` - Your GitHub username
|
|
356
|
+
## Setup Instructions
|
|
342
357
|
|
|
343
|
-
**
|
|
344
|
-
- `GITEA_URL` - Your Gitea instance URL (e.g., `https://git.example.com`)
|
|
345
|
-
- `GITEA_TOKEN` - Personal access token
|
|
346
|
-
- `GITEA_USERNAME` - Your Gitea username
|
|
358
|
+
1. **Configure your MCP client** with the git-mcp server and your credentials (see Configuration section above)
|
|
347
359
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
The server automatically validates credentials on startup:
|
|
351
|
-
|
|
352
|
-
- ✅ **Valid credentials** - API connectivity confirmed
|
|
353
|
-
- ❌ **Invalid token** - Authentication failed
|
|
354
|
-
- 🌐 **Network error** - Cannot reach provider API
|
|
355
|
-
- ⚠️ **Partial failure** - Some providers failed validation
|
|
356
|
-
|
|
357
|
-
### Setup Instructions
|
|
358
|
-
|
|
359
|
-
1. **Generate GitHub Token:**
|
|
360
|
+
2. **Generate GitHub Token:**
|
|
360
361
|
- Go to GitHub Settings → Developer settings → Personal access tokens
|
|
361
362
|
- Create token with `repo` scope
|
|
362
|
-
-
|
|
363
|
+
- Add to your MCP client configuration
|
|
363
364
|
|
|
364
|
-
|
|
365
|
+
3. **Generate Gitea Token:**
|
|
365
366
|
- Go to your Gitea instance → Settings → Applications
|
|
366
367
|
- Generate new token with appropriate permissions
|
|
367
|
-
-
|
|
368
|
-
|
|
369
|
-
3. **Test Configuration:**
|
|
370
|
-
```bash
|
|
371
|
-
# Start the server to see validation results
|
|
372
|
-
npx @andrebuzeli/git-mcp@latest
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Optional: use a local `mcp.json` (NOT committed) to keep credentials for local testing.
|
|
376
|
-
|
|
377
|
-
1. Create `mcp.json` next to package.json with this shape (use the `.example` as a starting point):
|
|
378
|
-
|
|
379
|
-
```json
|
|
380
|
-
{
|
|
381
|
-
"env": {
|
|
382
|
-
"GITEA_URL": "http://nas-ubuntu:3000",
|
|
383
|
-
"GITEA_TOKEN": "<GITEA_TOKEN>",
|
|
384
|
-
"GITEA_USERNAME": "<GITEA_USERNAME>",
|
|
385
|
-
"GITHUB_TOKEN": "<GITHUB_TOKEN>",
|
|
386
|
-
"GITHUB_USERNAME": "<GITHUB_USERNAME>"
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
```
|
|
368
|
+
- Add to your MCP client configuration
|
|
390
369
|
|
|
391
|
-
|
|
370
|
+
4. **Test Configuration:**
|
|
371
|
+
- Restart your MCP client
|
|
372
|
+
- Try using git-mcp tools in your IDE
|
|
392
373
|
|
|
393
374
|
## Troubleshooting
|
|
394
375
|
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { ProviderManager } from './providers/providerManager.js';
|
|
6
|
-
import {
|
|
6
|
+
import { MCPError } from './utils/errors.js';
|
|
7
7
|
import { IsomorphicGitAdapter } from './utils/gitAdapter.js';
|
|
8
8
|
import { GitFilesTool } from './tools/gitFiles.js';
|
|
9
9
|
import { GitWorkflowTool } from './tools/gitWorkflow.js';
|
|
@@ -29,11 +29,11 @@ import { GitFixTool } from './tools/gitFix.tool.js';
|
|
|
29
29
|
import { GitIgnoreTool } from './tools/gitIgnore.js';
|
|
30
30
|
import { GIT_PROMPTS } from './prompts/gitPrompts.js';
|
|
31
31
|
import TOOLS_GUIDE from './resources/toolsGuide.js';
|
|
32
|
+
import { Logger, logToolExecution, logSecurityEvent, logPerformanceMetric } from './utils/logger.js';
|
|
32
33
|
async function main() {
|
|
33
|
-
// Load optional mcp.json configuration (will populate process.env if values present)
|
|
34
|
-
loadMCPConfig();
|
|
35
34
|
const providerManager = new ProviderManager();
|
|
36
35
|
const gitAdapter = new IsomorphicGitAdapter(providerManager);
|
|
36
|
+
const logger = Logger.getInstance();
|
|
37
37
|
// Skip validation on startup to prevent hanging (validation happens on first use)
|
|
38
38
|
// Provider validation moved to lazy initialization
|
|
39
39
|
// Register all 22 Git tools
|
|
@@ -67,17 +67,18 @@ async function main() {
|
|
|
67
67
|
];
|
|
68
68
|
// Silent mode for MCP clients - only log to stderr in debug mode
|
|
69
69
|
if (process.env.DEBUG) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
logger.info(`Registered ${tools.length} Git tools`);
|
|
71
|
+
logger.info(`Registered ${resources.length} resource(s)`);
|
|
72
|
+
logger.info(`Registered ${GIT_PROMPTS.length} prompt(s)`);
|
|
73
73
|
}
|
|
74
74
|
// Create MCP Server with STDIO transport
|
|
75
75
|
const server = new Server({
|
|
76
76
|
name: '@andrebuzeli/git-mcp',
|
|
77
|
-
version: '10.0.
|
|
77
|
+
version: '10.0.4',
|
|
78
78
|
});
|
|
79
79
|
// Register tool list handler
|
|
80
80
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
81
|
+
logger.debug('Listing available tools');
|
|
81
82
|
return {
|
|
82
83
|
tools: tools.map(tool => ({
|
|
83
84
|
name: tool.name,
|
|
@@ -90,15 +91,42 @@ async function main() {
|
|
|
90
91
|
})),
|
|
91
92
|
};
|
|
92
93
|
});
|
|
93
|
-
// Register tool execution handler
|
|
94
|
+
// Register tool execution handler with logging e segurança aprimorados
|
|
94
95
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
95
96
|
const toolName = request.params.name;
|
|
96
97
|
const tool = tools.find(t => t.name === toolName);
|
|
98
|
+
const startTime = Date.now();
|
|
97
99
|
if (!tool) {
|
|
100
|
+
logSecurityEvent('TOOL_NOT_FOUND', { toolName });
|
|
98
101
|
throw new Error(`Tool not found: ${toolName}`);
|
|
99
102
|
}
|
|
100
103
|
try {
|
|
101
|
-
|
|
104
|
+
// Validar argumentos contra o schema da ferramenta
|
|
105
|
+
const args = request.params.arguments ?? {};
|
|
106
|
+
// Validação básica de segurança
|
|
107
|
+
if (typeof args !== 'object' || args === null) {
|
|
108
|
+
logSecurityEvent('INVALID_ARGUMENTS', { toolName, args });
|
|
109
|
+
throw new MCPError('VALIDATION_ERROR', 'Arguments must be an object');
|
|
110
|
+
}
|
|
111
|
+
// Validar projectPath se existir
|
|
112
|
+
if ('projectPath' in args) {
|
|
113
|
+
const projectPath = args.projectPath;
|
|
114
|
+
if (typeof projectPath !== 'string') {
|
|
115
|
+
logSecurityEvent('INVALID_PROJECT_PATH_TYPE', { toolName, projectPath });
|
|
116
|
+
throw new MCPError('VALIDATION_ERROR', 'projectPath must be a string');
|
|
117
|
+
}
|
|
118
|
+
if (!projectPath || projectPath.includes('..')) {
|
|
119
|
+
logSecurityEvent('PATH_TRAVERSION_ATTEMPT', { toolName, projectPath });
|
|
120
|
+
throw new MCPError('VALIDATION_ERROR', 'Invalid project path');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Log da execução
|
|
124
|
+
logger.info(`Executing tool: ${toolName}`, { args }, toolName);
|
|
125
|
+
// Executar a ferramenta
|
|
126
|
+
const result = await tool.handle(args, { providerManager, gitAdapter });
|
|
127
|
+
const duration = Date.now() - startTime;
|
|
128
|
+
logPerformanceMetric(`tool_${toolName}`, duration, { success: true });
|
|
129
|
+
logToolExecution(toolName, args, result);
|
|
102
130
|
return {
|
|
103
131
|
content: [
|
|
104
132
|
{
|
|
@@ -109,6 +137,9 @@ async function main() {
|
|
|
109
137
|
};
|
|
110
138
|
}
|
|
111
139
|
catch (error) {
|
|
140
|
+
const duration = Date.now() - startTime;
|
|
141
|
+
logPerformanceMetric(`tool_${toolName}`, duration, { success: false, error: error.message });
|
|
142
|
+
logToolExecution(toolName, request.params.arguments ?? {}, undefined, error);
|
|
112
143
|
return {
|
|
113
144
|
content: [
|
|
114
145
|
{
|
|
@@ -122,6 +153,7 @@ async function main() {
|
|
|
122
153
|
});
|
|
123
154
|
// Register resource list handler
|
|
124
155
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
156
|
+
logger.debug('Listing available resources');
|
|
125
157
|
return {
|
|
126
158
|
resources: resources.map(resource => ({
|
|
127
159
|
uri: resource.uri,
|
|
@@ -136,8 +168,10 @@ async function main() {
|
|
|
136
168
|
const uri = request.params.uri;
|
|
137
169
|
const resource = resources.find(r => r.uri === uri);
|
|
138
170
|
if (!resource) {
|
|
171
|
+
logger.warn(`Resource not found: ${uri}`);
|
|
139
172
|
throw new Error(`Resource not found: ${uri}`);
|
|
140
173
|
}
|
|
174
|
+
logger.debug(`Reading resource: ${uri}`);
|
|
141
175
|
return {
|
|
142
176
|
contents: [
|
|
143
177
|
{
|
|
@@ -150,6 +184,7 @@ async function main() {
|
|
|
150
184
|
});
|
|
151
185
|
// Register prompt list handler
|
|
152
186
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
187
|
+
logger.debug('Listing available prompts');
|
|
153
188
|
return {
|
|
154
189
|
prompts: GIT_PROMPTS.map(prompt => ({
|
|
155
190
|
name: prompt.name,
|
|
@@ -163,8 +198,10 @@ async function main() {
|
|
|
163
198
|
const promptName = request.params.name;
|
|
164
199
|
const prompt = GIT_PROMPTS.find(p => p.name === promptName);
|
|
165
200
|
if (!prompt) {
|
|
201
|
+
logger.warn(`Prompt not found: ${promptName}`);
|
|
166
202
|
throw new Error(`Prompt not found: ${promptName}`);
|
|
167
203
|
}
|
|
204
|
+
logger.debug(`Generating prompt: ${promptName}`);
|
|
168
205
|
const result = await prompt.generate(request.params.arguments ?? {}, { providerManager, gitAdapter });
|
|
169
206
|
return {
|
|
170
207
|
description: result.description,
|
|
@@ -175,10 +212,10 @@ async function main() {
|
|
|
175
212
|
await server.connect(transport);
|
|
176
213
|
// Only log in debug mode
|
|
177
214
|
if (process.env.DEBUG) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
215
|
+
logger.info(`✅ git-mcp MCP server running via STDIO`);
|
|
216
|
+
logger.info(`Tools: ${tools.length} registered`);
|
|
217
|
+
logger.info(`Resources: ${resources.length} registered`);
|
|
218
|
+
logger.info(`Prompts: ${GIT_PROMPTS.length} registered`);
|
|
182
219
|
}
|
|
183
220
|
}
|
|
184
221
|
main().catch(err => {
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { ProviderManager } from './providers/providerManager.js';
|
|
6
|
+
import { MCPError } from './utils/errors.js';
|
|
7
|
+
import { IsomorphicGitAdapter } from './utils/gitAdapter.js';
|
|
8
|
+
import { GitFilesTool } from './tools/gitFiles.js';
|
|
9
|
+
import { GitWorkflowTool } from './tools/gitWorkflow.js';
|
|
10
|
+
import { GitBranchesTool } from './tools/gitBranches.js';
|
|
11
|
+
import { GitIssuesTool } from './tools/gitIssues.js';
|
|
12
|
+
import { GitPullsTool } from './tools/gitPulls.js';
|
|
13
|
+
import { GitTagsTool } from './tools/gitTags.js';
|
|
14
|
+
import { GitReleaseTool } from './tools/gitRelease.js';
|
|
15
|
+
import { GitRemoteTool } from './tools/gitRemote.js';
|
|
16
|
+
import { GitResetTool } from './tools/gitReset.js';
|
|
17
|
+
import { GitStashTool } from './tools/gitStash.js';
|
|
18
|
+
import { GitConfigTool } from './tools/gitConfig.js';
|
|
19
|
+
import { GitMonitorTool } from './tools/gitMonitor.js';
|
|
20
|
+
import { GitBackupTool } from './tools/gitBackup.js';
|
|
21
|
+
import { GitArchiveTool } from './tools/gitArchive.js';
|
|
22
|
+
import { GitSyncTool } from './tools/gitSync.js';
|
|
23
|
+
import { GitPackagesTool } from './tools/gitPackages.js';
|
|
24
|
+
import { GitAnalyticsTool } from './tools/gitAnalytics.js';
|
|
25
|
+
import { GitUploadTool } from './tools/gitUpload.js';
|
|
26
|
+
import { GitUpdateTool } from './tools/gitUpdate.js';
|
|
27
|
+
import { GitHistoryTool } from './tools/gitHistory.js';
|
|
28
|
+
import { GitFixTool } from './tools/gitFix.tool.js';
|
|
29
|
+
import { GitIgnoreTool } from './tools/gitIgnore.js';
|
|
30
|
+
import { GIT_PROMPTS } from './prompts/gitPrompts.js';
|
|
31
|
+
import TOOLS_GUIDE from './resources/toolsGuide.js';
|
|
32
|
+
import { Logger, logToolExecution, logSecurityEvent, logPerformanceMetric } from './utils/logger.js';
|
|
33
|
+
async function main() {
|
|
34
|
+
const providerManager = new ProviderManager();
|
|
35
|
+
const gitAdapter = new IsomorphicGitAdapter(providerManager);
|
|
36
|
+
const logger = Logger.getInstance();
|
|
37
|
+
// Skip validation on startup to prevent hanging (validation happens on first use)
|
|
38
|
+
// Provider validation moved to lazy initialization
|
|
39
|
+
// Register all 22 Git tools
|
|
40
|
+
const tools = [
|
|
41
|
+
new GitWorkflowTool(),
|
|
42
|
+
new GitFilesTool(),
|
|
43
|
+
new GitBranchesTool(),
|
|
44
|
+
new GitIssuesTool(),
|
|
45
|
+
new GitPullsTool(),
|
|
46
|
+
new GitTagsTool(),
|
|
47
|
+
new GitReleaseTool(),
|
|
48
|
+
new GitRemoteTool(),
|
|
49
|
+
new GitResetTool(),
|
|
50
|
+
new GitStashTool(),
|
|
51
|
+
new GitConfigTool(),
|
|
52
|
+
new GitMonitorTool(),
|
|
53
|
+
new GitBackupTool(),
|
|
54
|
+
new GitArchiveTool(),
|
|
55
|
+
new GitSyncTool(),
|
|
56
|
+
new GitPackagesTool(),
|
|
57
|
+
new GitAnalyticsTool(),
|
|
58
|
+
new GitUploadTool(),
|
|
59
|
+
new GitUpdateTool(),
|
|
60
|
+
new GitHistoryTool(),
|
|
61
|
+
new GitFixTool(),
|
|
62
|
+
new GitIgnoreTool(),
|
|
63
|
+
];
|
|
64
|
+
// Register resources
|
|
65
|
+
const resources = [
|
|
66
|
+
TOOLS_GUIDE
|
|
67
|
+
];
|
|
68
|
+
// Silent mode for MCP clients - only log to stderr in debug mode
|
|
69
|
+
if (process.env.DEBUG) {
|
|
70
|
+
logger.info(`Registered ${tools.length} Git tools`);
|
|
71
|
+
logger.info(`Registered ${resources.length} resource(s)`);
|
|
72
|
+
logger.info(`Registered ${GIT_PROMPTS.length} prompt(s)`);
|
|
73
|
+
}
|
|
74
|
+
// Create MCP Server with STDIO transport
|
|
75
|
+
const server = new Server({
|
|
76
|
+
name: '@andrebuzeli/git-mcp',
|
|
77
|
+
version: '10.0.4',
|
|
78
|
+
});
|
|
79
|
+
// Register tool list handler
|
|
80
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
81
|
+
logger.debug('Listing available tools');
|
|
82
|
+
return {
|
|
83
|
+
tools: tools.map(tool => ({
|
|
84
|
+
name: tool.name,
|
|
85
|
+
description: tool.description,
|
|
86
|
+
inputSchema: tool.inputSchema || {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {},
|
|
89
|
+
additionalProperties: true,
|
|
90
|
+
},
|
|
91
|
+
})),
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
// Register tool execution handler with logging e segurança aprimorados
|
|
95
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
96
|
+
const toolName = request.params.name;
|
|
97
|
+
const tool = tools.find(t => t.name === toolName);
|
|
98
|
+
const startTime = Date.now();
|
|
99
|
+
if (!tool) {
|
|
100
|
+
logSecurityEvent('TOOL_NOT_FOUND', { toolName });
|
|
101
|
+
throw new Error(`Tool not found: ${toolName}`);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
// Validar argumentos contra o schema da ferramenta
|
|
105
|
+
const args = request.params.arguments ?? {};
|
|
106
|
+
// Validação básica de segurança
|
|
107
|
+
if (typeof args !== 'object' || args === null) {
|
|
108
|
+
logSecurityEvent('INVALID_ARGUMENTS', { toolName, args });
|
|
109
|
+
throw new MCPError('VALIDATION_ERROR', 'Arguments must be an object');
|
|
110
|
+
}
|
|
111
|
+
// Validar projectPath se existir
|
|
112
|
+
if ('projectPath' in args) {
|
|
113
|
+
const projectPath = args.projectPath;
|
|
114
|
+
if (typeof projectPath !== 'string') {
|
|
115
|
+
logSecurityEvent('INVALID_PROJECT_PATH_TYPE', { toolName, projectPath });
|
|
116
|
+
throw new MCPError('VALIDATION_ERROR', 'projectPath must be a string');
|
|
117
|
+
}
|
|
118
|
+
if (!projectPath || projectPath.includes('..')) {
|
|
119
|
+
logSecurityEvent('PATH_TRAVERSION_ATTEMPT', { toolName, projectPath });
|
|
120
|
+
throw new MCPError('VALIDATION_ERROR', 'Invalid project path');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Log da execução
|
|
124
|
+
logger.info(`Executing tool: ${toolName}`, { args }, toolName);
|
|
125
|
+
// Executar a ferramenta
|
|
126
|
+
const result = await tool.handle(args, { providerManager, gitAdapter });
|
|
127
|
+
const duration = Date.now() - startTime;
|
|
128
|
+
logPerformanceMetric(`tool_${toolName}`, duration, { success: true });
|
|
129
|
+
logToolExecution(toolName, args, result);
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: 'text',
|
|
134
|
+
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const duration = Date.now() - startTime;
|
|
141
|
+
logPerformanceMetric(`tool_${toolName}`, duration, { success: false, error: error.message });
|
|
142
|
+
logToolExecution(toolName, request.params.arguments ?? {}, undefined, error);
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: 'text',
|
|
147
|
+
text: `Error: ${error.message || String(error)}`,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
isError: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
// Register resource list handler
|
|
155
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
156
|
+
logger.debug('Listing available resources');
|
|
157
|
+
return {
|
|
158
|
+
resources: resources.map(resource => ({
|
|
159
|
+
uri: resource.uri,
|
|
160
|
+
name: resource.name,
|
|
161
|
+
description: resource.description,
|
|
162
|
+
mimeType: resource.mimeType,
|
|
163
|
+
})),
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
// Register resource read handler
|
|
167
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
168
|
+
const uri = request.params.uri;
|
|
169
|
+
const resource = resources.find(r => r.uri === uri);
|
|
170
|
+
if (!resource) {
|
|
171
|
+
logger.warn(`Resource not found: ${uri}`);
|
|
172
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
173
|
+
}
|
|
174
|
+
logger.debug(`Reading resource: ${uri}`);
|
|
175
|
+
return {
|
|
176
|
+
contents: [
|
|
177
|
+
{
|
|
178
|
+
uri: resource.uri,
|
|
179
|
+
mimeType: resource.mimeType,
|
|
180
|
+
text: resource.content,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
// Register prompt list handler
|
|
186
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
187
|
+
logger.debug('Listing available prompts');
|
|
188
|
+
return {
|
|
189
|
+
prompts: GIT_PROMPTS.map(prompt => ({
|
|
190
|
+
name: prompt.name,
|
|
191
|
+
description: prompt.description,
|
|
192
|
+
arguments: prompt.arguments,
|
|
193
|
+
})),
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
// Register prompt get handler
|
|
197
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
198
|
+
const promptName = request.params.name;
|
|
199
|
+
const prompt = GIT_PROMPTS.find(p => p.name === promptName);
|
|
200
|
+
if (!prompt) {
|
|
201
|
+
logger.warn(`Prompt not found: ${promptName}`);
|
|
202
|
+
throw new Error(`Prompt not found: ${promptName}`);
|
|
203
|
+
}
|
|
204
|
+
logger.debug(`Generating prompt: ${promptName}`);
|
|
205
|
+
const result = await prompt.generate(request.params.arguments ?? {}, { providerManager, gitAdapter });
|
|
206
|
+
return {
|
|
207
|
+
description: result.description,
|
|
208
|
+
messages: result.messages,
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
const transport = new StdioServerTransport();
|
|
212
|
+
await server.connect(transport);
|
|
213
|
+
// Only log in debug mode
|
|
214
|
+
if (process.env.DEBUG) {
|
|
215
|
+
logger.info(`✅ git-mcp MCP server running via STDIO`);
|
|
216
|
+
logger.info(`Tools: ${tools.length} registered`);
|
|
217
|
+
logger.info(`Resources: ${resources.length} registered`);
|
|
218
|
+
logger.info(`Prompts: ${GIT_PROMPTS.length} registered`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
main().catch(err => {
|
|
222
|
+
console.error('❌ Failed to start git-mcp:', err);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
});
|
|
@@ -14,10 +14,6 @@ export declare class GitArchiveTool implements Tool {
|
|
|
14
14
|
enum: string[];
|
|
15
15
|
description: string;
|
|
16
16
|
};
|
|
17
|
-
archiveName: {
|
|
18
|
-
type: string;
|
|
19
|
-
description: string;
|
|
20
|
-
};
|
|
21
17
|
outputPath: {
|
|
22
18
|
type: string;
|
|
23
19
|
description: string;
|
|
@@ -31,10 +27,6 @@ export declare class GitArchiveTool implements Tool {
|
|
|
31
27
|
type: string;
|
|
32
28
|
description: string;
|
|
33
29
|
};
|
|
34
|
-
archivePath: {
|
|
35
|
-
type: string;
|
|
36
|
-
description: string;
|
|
37
|
-
};
|
|
38
30
|
};
|
|
39
31
|
required: string[];
|
|
40
32
|
additionalProperties: boolean;
|
package/dist/tools/gitArchive.js
CHANGED
|
@@ -10,20 +10,16 @@ export class GitArchiveTool {
|
|
|
10
10
|
properties: {
|
|
11
11
|
projectPath: {
|
|
12
12
|
type: "string",
|
|
13
|
-
description: "
|
|
13
|
+
description: "The absolute path to the Git repository directory on the local filesystem. This should be the root directory containing the .git folder. For example: '/home/user/my-project' on Linux/Mac or 'C:\\Users\\user\\my-project' on Windows."
|
|
14
14
|
},
|
|
15
15
|
action: {
|
|
16
16
|
type: "string",
|
|
17
|
-
enum: ["create", "
|
|
18
|
-
description: "
|
|
19
|
-
},
|
|
20
|
-
archiveName: {
|
|
21
|
-
type: "string",
|
|
22
|
-
description: "Name for the archive file (optional for create, default: timestamp-based)"
|
|
17
|
+
enum: ["create", "extract"],
|
|
18
|
+
description: "Archive operation to perform: create (create archive), extract (extract archive)"
|
|
23
19
|
},
|
|
24
20
|
outputPath: {
|
|
25
21
|
type: "string",
|
|
26
|
-
description: "Output
|
|
22
|
+
description: "Output archive file path (required for create)"
|
|
27
23
|
},
|
|
28
24
|
format: {
|
|
29
25
|
type: "string",
|
|
@@ -33,10 +29,6 @@ export class GitArchiveTool {
|
|
|
33
29
|
ref: {
|
|
34
30
|
type: "string",
|
|
35
31
|
description: "Git reference to archive (optional for create, default: HEAD)"
|
|
36
|
-
},
|
|
37
|
-
archivePath: {
|
|
38
|
-
type: "string",
|
|
39
|
-
description: "Path to archive file (required for verify/extract)"
|
|
40
32
|
}
|
|
41
33
|
},
|
|
42
34
|
required: ["projectPath", "action"],
|
package/dist/tools/gitBackup.js
CHANGED
|
@@ -11,28 +11,28 @@ export class GitBackupTool {
|
|
|
11
11
|
properties: {
|
|
12
12
|
projectPath: {
|
|
13
13
|
type: "string",
|
|
14
|
-
description: "
|
|
14
|
+
description: "The absolute path to the Git repository directory on the local filesystem. This should be the root directory containing the .git folder. For example: '/home/user/my-project' on Linux/Mac or 'C:\\Users\\user\\my-project' on Windows."
|
|
15
15
|
},
|
|
16
16
|
action: {
|
|
17
17
|
type: "string",
|
|
18
|
-
enum: ["
|
|
19
|
-
description: "
|
|
18
|
+
enum: ["create", "restore", "list", "delete"],
|
|
19
|
+
description: "Backup operation to perform: create (create backup), restore (restore from backup), list (list backups), delete (delete backup)"
|
|
20
20
|
},
|
|
21
21
|
backupPath: {
|
|
22
22
|
type: "string",
|
|
23
|
-
description: "Path to backup file (required for restore/
|
|
23
|
+
description: "Path to backup file (required for create/restore/delete)"
|
|
24
24
|
},
|
|
25
25
|
compress: {
|
|
26
26
|
type: "boolean",
|
|
27
|
-
description: "Compress backup (optional for
|
|
27
|
+
description: "Compress backup (optional for create, default: true)"
|
|
28
28
|
},
|
|
29
29
|
confirm: {
|
|
30
30
|
type: "boolean",
|
|
31
|
-
description: "Confirm destructive operations (optional for restore)"
|
|
31
|
+
description: "Confirm destructive operations (optional for restore/delete)"
|
|
32
32
|
},
|
|
33
33
|
name: {
|
|
34
34
|
type: "string",
|
|
35
|
-
description: "Backup name (optional for
|
|
35
|
+
description: "Backup name (optional for create)"
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
required: ["projectPath", "action"],
|