@andrebuzeli/git-mcp 15.8.4 → 15.8.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/README.md CHANGED
@@ -1,125 +1,39 @@
1
- # @andrebuzeli/git-mcp
2
-
3
- Servidor MCP (Model Context Protocol) para operações Git locais sem git instalado, com sincronização paralela para GitHub e Gitea.
4
-
5
- ## Melhorias Implementadas (v15.3.0)
6
-
7
- ### 🔴 Correções Críticas
8
- - ✅ **Métodos Faltantes**: `getMergeStatus`, `abortMerge`, `resetHardClean` implementados
9
- - ✅ **Paralelização runBoth**: GitHub e Gitea executados em paralelo (2x mais rápido)
10
- - ✅ **withRetry Integrado**: Operações remotas com retry automático
11
-
12
- ### 🔒 Segurança
13
- - ✅ **Validação de Path**: Prevenção de path traversal e validação rigorosa
14
- - ✅ **Logging Configurável**: Debug logging controlado por `DEBUG_AGENT_LOG`
15
- - ✅ **Limite de Comprimento**: Paths limitados a 260 caracteres (MAX_PATH)
16
-
17
- ### ⚡ Performance
18
- - ✅ **Timeout Configurável**: `GIT_TIMEOUT_MS` (default: 60s) para operações Git
19
- - ✅ **Cache de URLs**: URLs de remotes cacheadas por 10 minutos
20
- - ✅ **Paginação listAllRepos**: Suporta contas com milhares de repositórios
21
- - ✅ **Rate Limit Handling**: Backoff automático com Retry-After header
22
-
23
- ### 🧪 Testabilidade & Desenvolvimento
24
- - ✅ **Dry Run Mode**: Todas as operações suportam simulação
25
- - **Testes Unitários**: Suite Jest com 13 testes passando
26
- - **Suporte .env File**: Carrega variáveis de `.env` automaticamente
27
- - **Métricas/Telemetria**: Opt-in via `ENABLE_METRICS=true`
28
-
29
- ### 🔧 Novas Funcionalidades
30
- - **Git LFS**: Suporte completo (track, pull, push, status)
31
- - **Sistema de Hooks**: Pre/post hooks customizáveis
32
- - **Consistência Naming**: camelCase em todas as respostas
33
-
34
- ### 📊 Variáveis de Ambiente
35
-
36
- | Variável | Descrição | Default |
37
- |----------|-----------|---------|
38
- | `GITHUB_TOKEN` | Token de autenticação GitHub | - |
39
- | `GITEA_URL` | URL do servidor Gitea | - |
40
- | `GITEA_TOKEN` | Token de autenticação Gitea | - |
41
- | `GIT_TIMEOUT_MS` | Timeout para operações Git | 60000 |
42
- | `DEBUG_AGENT_LOG` | Habilita logging de debug | false |
43
- | `ENABLE_METRICS` | Habilita métricas | false |
44
-
45
- ## Status dos Testes
46
-
47
- ```
48
- Test Suites: 2 passed, 2 total
49
- Tests: 13 passed, 13 total
50
- ```
51
-
52
- ### Configuração Padrão (npx)
53
-
54
- ```json
55
- {
56
- "mcpServers": {
57
- "git-mcp": {
58
- "command": "npx",
59
- "args": ["-y", "@andrebuzeli/git-mcp@latest"],
60
- "env": {
61
- "GITEA_URL": "https://seu-gitea",
62
- "GITEA_TOKEN": "...",
63
- "GITHUB_TOKEN": "..."
64
- }
65
- }
66
- }
67
- }
68
- ```
69
-
70
- ## 🐧 SSH Remote / Linux Server
71
-
72
- Se você usa **VS Code Remote SSH**, **Cursor SSH** ou similar, o `npx` pode dar timeout em redes lentas.
73
-
74
- ### Solução: Instalação Global
75
-
76
- **1. No servidor Linux, execute:**
77
- ```bash
78
- # Instalação rápida
79
- curl -fsSL https://raw.githubusercontent.com/andrebuzeli/git-mcp/main/install.sh | bash
80
-
81
- # Ou manualmente:
82
- npm install -g @andrebuzeli/git-mcp@latest
83
- ```
84
-
85
- **2. Configure a IDE para usar o comando local:**
86
- ```json
87
- {
88
- "mcpServers": {
89
- "git-mcp": {
90
- "command": "git-mcp",
91
- "env": {
92
- "GITHUB_TOKEN": "...",
93
- "GITEA_URL": "https://seu-gitea",
94
- "GITEA_TOKEN": "..."
95
- }
96
- }
97
- }
98
- }
99
- ```
100
-
101
- ### Troubleshooting
102
-
103
- | Erro | Solução |
104
- |------|---------|
105
- | `ETIMEDOUT` / `Request timed out` | Use instalação global ao invés de npx |
106
- | `command not found: git-mcp` | Verifique se npm global está no PATH |
107
- | Permissão negada | Use `sudo npm install -g @andrebuzeli/git-mcp` |
108
-
109
- ## Tools
110
-
111
- - git-workflow: init, status, add, remove, commit, ensure-remotes, push
112
- - git-remote: list, ensure
113
- - git-branches: list, create, delete, rename, checkout
114
- - git-tags: list, create, delete, push
115
- - git-stash: list, save, apply, pop, drop, clear
116
- - git-reset: soft, mixed, hard
117
- - git-config: get, set, unset, list
118
- - git-ignore: list, create, add, remove
119
- - git-files: list, read
120
- - git-history: log
121
- - git-sync: fetch, pull
122
- - git-issues: create, list, comment
123
- - git-pulls: create, list, files
124
-
125
- Todas as tools exigem `projectPath` e operam com derivação automática do nome do repositório a partir do caminho.
1
+ # @andrebuzeli/git-mcp
2
+
3
+ Servidor MCP (Model Context Protocol) para operações Git locais sem git instalado, com sincronização paralela para GitHub e Gitea.
4
+
5
+ ## Configuração MCP
6
+
7
+ ```json
8
+ {
9
+ "mcpServers": {
10
+ "git-mcp": {
11
+ "command": "npx",
12
+ "args": ["@andrebuzeli/git-mcp@latest"],
13
+ "env": {
14
+ "GITEA_URL": "https://seu-gitea",
15
+ "GITEA_TOKEN": "...",
16
+ "GITHUB_TOKEN": "..."
17
+ }
18
+ }
19
+ }
20
+ }
21
+ ```
22
+
23
+ ## Tools
24
+
25
+ - git-workflow: init, status, add, remove, commit, ensure-remotes, push
26
+ - git-remote: list, ensure
27
+ - git-branches: list, create, delete, rename, checkout
28
+ - git-tags: list, create, delete, push
29
+ - git-stash: list, save, apply, pop, drop, clear
30
+ - git-reset: soft, mixed, hard
31
+ - git-config: get, set, unset, list
32
+ - git-ignore: list, create, add, remove
33
+ - git-files: list, read
34
+ - git-history: log
35
+ - git-sync: fetch, pull
36
+ - git-issues: create, list, comment
37
+ - git-pulls: create, list, files
38
+
39
+ Todas as tools exigem `projectPath` e operam com derivação automática do nome do repositório a partir do caminho.
package/package.json CHANGED
@@ -1,45 +1,29 @@
1
- {
2
- "name": "@andrebuzeli/git-mcp",
3
- "version": "15.8.4",
4
- "private": false,
5
- "description": "MCP server para Git com operações locais e sincronização paralela GitHub/Gitea",
6
- "license": "MIT",
7
- "author": "andrebuzeli",
8
- "type": "module",
9
- "publishConfig": {
10
- "access": "public"
11
- },
12
- "files": [
13
- "src",
14
- "README.md",
15
- "install.sh"
16
- ],
17
- "main": "src/index.js",
18
- "bin": {
19
- "git-mcp": "src/index.js",
20
- "git-mcpv2": "src/index.js"
21
- },
22
- "scripts": {
23
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
24
- "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
25
- "test:comprehensive": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js",
26
- "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js --coverage",
27
- "test:utils": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js test/comprehensive/test-utils.js",
28
- "test:gitadapter": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js test/comprehensive/test-gitadapter.js",
29
- "test:providers": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js test/comprehensive/test-provider-manager.js",
30
- "test:tools": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js test/comprehensive/test-tools.js",
31
- "test:resources": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js test/comprehensive/test-resources.js",
32
- "test:integration": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js test/comprehensive/test-integration.js",
33
- "test:security": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.comprehensive.config.js test/comprehensive/test-security.js"
34
- },
35
- "dependencies": {
36
- "@modelcontextprotocol/sdk": "^0.4.0",
37
- "@octokit/rest": "^20.0.0",
38
- "ajv": "^8.12.0",
39
- "archiver": "^7.0.1",
40
- "axios": "^1.7.7"
41
- },
42
- "devDependencies": {
43
- "jest": "^30.2.0"
44
- }
1
+ {
2
+ "name": "@andrebuzeli/git-mcp",
3
+ "version": "15.8.6",
4
+ "private": false,
5
+ "description": "MCP server para Git com operações locais e sincronização paralela GitHub/Gitea",
6
+ "license": "MIT",
7
+ "author": "andrebuzeli",
8
+ "type": "module",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "files": [
13
+ "src",
14
+ "README.md"
15
+ ],
16
+ "main": "src/index.js",
17
+ "bin": {
18
+ "git-mcp": "src/index.js",
19
+ "git-mcpv2": "src/index.js"
20
+ },
21
+ "scripts": {},
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^0.4.0",
24
+ "@octokit/rest": "^20.0.0",
25
+ "ajv": "^8.12.0",
26
+ "archiver": "^7.0.0",
27
+ "axios": "^1.7.7"
28
+ }
45
29
  }
package/src/index.js CHANGED
@@ -1,139 +1,146 @@
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 Ajv from "ajv";
5
- import { loadEnv } from "./utils/env.js";
6
- import { asToolError } from "./utils/errors.js";
7
- import { ProviderManager } from "./providers/providerManager.js";
8
- import { GitAdapter } from "./utils/gitAdapter.js";
9
- import { createGitWorkflowTool } from "./tools/git-workflow.js";
10
- import { createGitRemoteTool } from "./tools/git-remote.js";
11
- import { createGitBranchesTool } from "./tools/git-branches.js";
12
- import { createGitTagsTool } from "./tools/git-tags.js";
13
- import { createGitStashTool } from "./tools/git-stash.js";
14
- import { createGitResetTool } from "./tools/git-reset.js";
15
- import { createGitConfigTool } from "./tools/git-config.js";
16
- import { createGitIgnoreTool } from "./tools/git-ignore.js";
17
- import { createGitFilesTool } from "./tools/git-files.js";
18
- import { createGitHistoryTool } from "./tools/git-history.js";
19
- import { createGitSyncTool } from "./tools/git-sync.js";
20
- import { createGitIssuesTool } from "./tools/git-issues.js";
21
- import { createGitPullsTool } from "./tools/git-pulls.js";
22
- import { createGitMergeTool } from "./tools/git-merge.js";
23
- import { createGitDiffTool } from "./tools/git-diff.js";
24
- import { createGitCloneTool } from "./tools/git-clone.js";
25
- import { createGitHelpTool } from "./tools/git-help.js";
26
- import { getResources, readResource } from "./resources/index.js";
27
- import { createPromptsHandler, PROMPTS } from "./prompts/index.js";
28
-
29
- // Carrega variáveis de ambiente do arquivo .env (se existir)
30
- loadEnv();
31
-
32
- const pm = new ProviderManager();
33
- const git = new GitAdapter(pm);
34
-
35
- // Log de inicialização para stderr (não interfere com stdio do MCP)
36
- const hasGitHub = !!process.env.GITHUB_TOKEN;
37
- const hasGitea = !!process.env.GITEA_URL && !!process.env.GITEA_TOKEN;
38
- if (!hasGitHub && !hasGitea) {
39
- console.error("[git-mcp] ⚠️ Nenhum provider configurado. Operações remotas não funcionarão.");
40
- console.error("[git-mcp] Configure GITHUB_TOKEN e/ou GITEA_URL + GITEA_TOKEN");
41
- } else {
42
- const providers = [];
43
- if (hasGitHub) providers.push("GitHub");
44
- if (hasGitea) providers.push("Gitea");
45
- console.error(`[git-mcp] ✓ Providers ativos: ${providers.join(", ")}`);
46
- }
47
-
48
- const transport = new StdioServerTransport();
49
- const server = new Server(
50
- { name: "git-mcpv2", version: "15.8.0" },
51
- { capabilities: { tools: {}, resources: {}, prompts: {} } }
52
- );
53
- server.connect(transport);
54
-
55
- // Prompts handler
56
- const promptsHandler = createPromptsHandler(git, pm);
57
-
58
- // Ajv singleton para validação
59
- const ajv = new Ajv({ allErrors: true });
60
-
61
- const tools = [
62
- createGitHelpTool(), // Primeiro: AI pode pedir ajuda
63
- createGitWorkflowTool(pm, git),
64
- createGitRemoteTool(pm, git),
65
- createGitBranchesTool(git),
66
- createGitTagsTool(git),
67
- createGitStashTool(git),
68
- createGitResetTool(git),
69
- createGitConfigTool(git),
70
- createGitIgnoreTool(git),
71
- createGitFilesTool(git),
72
- createGitHistoryTool(git),
73
- createGitSyncTool(git),
74
- createGitIssuesTool(pm),
75
- createGitPullsTool(pm),
76
- createGitMergeTool(git),
77
- createGitDiffTool(git),
78
- createGitCloneTool(git),
79
- ];
80
-
81
- // ============ TOOLS ============
82
- server.setRequestHandler(
83
- (await import("@modelcontextprotocol/sdk/types.js")).ListToolsRequestSchema,
84
- async () => ({
85
- tools: tools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
86
- })
87
- );
88
-
89
- server.setRequestHandler(
90
- (await import("@modelcontextprotocol/sdk/types.js")).CallToolRequestSchema,
91
- async (req) => {
92
- const name = req.params?.name || "";
93
- const args = req.params?.arguments || {};
94
- const tool = tools.find(t => t.name === name);
95
- if (!tool) return { content: [{ type: "text", text: `Tool não encontrada: ${name}` }], isError: true };
96
- try {
97
- const result = await tool.handle(args);
98
- return result;
99
- } catch (e) {
100
- return asToolError(e.code || "ERROR", e.message || String(e));
101
- }
102
- }
103
- );
104
-
105
- // ============ RESOURCES ============
106
- server.setRequestHandler(
107
- (await import("@modelcontextprotocol/sdk/types.js")).ListResourcesRequestSchema,
108
- async () => ({ resources: getResources() })
109
- );
110
-
111
- server.setRequestHandler(
112
- (await import("@modelcontextprotocol/sdk/types.js")).ReadResourceRequestSchema,
113
- async (req) => {
114
- const uri = req.params?.uri || "";
115
- const content = readResource(uri);
116
- if (!content) {
117
- return { contents: [{ uri, mimeType: "text/plain", text: `Resource não encontrado: ${uri}` }] };
118
- }
119
- return { contents: [{ uri, mimeType: "text/markdown", text: content }] };
120
- }
121
- );
122
-
123
- // ============ PROMPTS ============
124
- server.setRequestHandler(
125
- (await import("@modelcontextprotocol/sdk/types.js")).ListPromptsRequestSchema,
126
- async () => ({ prompts: PROMPTS })
127
- );
128
-
129
- server.setRequestHandler(
130
- (await import("@modelcontextprotocol/sdk/types.js")).GetPromptRequestSchema,
131
- async (req) => {
132
- const name = req.params?.name || "";
133
- const args = req.params?.arguments || {};
134
- return await promptsHandler.get(name, args);
135
- }
136
- );
137
-
138
- // Keep process alive
139
- process.stdin.resume();
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 Ajv from "ajv";
5
+ import { loadEnv } from "./utils/env.js";
6
+ import { asToolError } from "./utils/errors.js";
7
+ import { ProviderManager } from "./providers/providerManager.js";
8
+ import { GitAdapter } from "./utils/gitAdapter.js";
9
+ import { createGitWorkflowTool } from "./tools/git-workflow.js";
10
+ import { createGitRemoteTool } from "./tools/git-remote.js";
11
+ import { createGitBranchesTool } from "./tools/git-branches.js";
12
+ import { createGitTagsTool } from "./tools/git-tags.js";
13
+ import { createGitStashTool } from "./tools/git-stash.js";
14
+ import { createGitResetTool } from "./tools/git-reset.js";
15
+ import { createGitConfigTool } from "./tools/git-config.js";
16
+ import { createGitIgnoreTool } from "./tools/git-ignore.js";
17
+ import { createGitFilesTool } from "./tools/git-files.js";
18
+ import { createGitHistoryTool } from "./tools/git-history.js";
19
+ import { createGitSyncTool } from "./tools/git-sync.js";
20
+ import { createGitIssuesTool } from "./tools/git-issues.js";
21
+ import { createGitPullsTool } from "./tools/git-pulls.js";
22
+ import { createGitMergeTool } from "./tools/git-merge.js";
23
+ import { createGitDiffTool } from "./tools/git-diff.js";
24
+ import { createGitCloneTool } from "./tools/git-clone.js";
25
+ import { createGitHelpTool } from "./tools/git-help.js";
26
+ import { getResources, readResource } from "./resources/index.js";
27
+ import { createPromptsHandler, PROMPTS } from "./prompts/index.js";
28
+
29
+ // Carrega variáveis de ambiente do arquivo .env (se existir)
30
+ loadEnv();
31
+
32
+ const pm = new ProviderManager();
33
+ const git = new GitAdapter(pm);
34
+
35
+ // Log de inicialização para stderr (não interfere com stdio do MCP)
36
+ const hasGitHub = !!process.env.GITHUB_TOKEN;
37
+ const hasGitea = !!process.env.GITEA_URL && !!process.env.GITEA_TOKEN;
38
+ if (!hasGitHub && !hasGitea) {
39
+ console.error("[git-mcp] ⚠️ Nenhum provider configurado. Operações remotas não funcionarão.");
40
+ console.error("[git-mcp] Configure GITHUB_TOKEN e/ou GITEA_URL + GITEA_TOKEN");
41
+ } else {
42
+ const providers = [];
43
+ if (hasGitHub) providers.push("GitHub");
44
+ if (hasGitea) providers.push("Gitea");
45
+ console.error(`[git-mcp] ✓ Providers ativos: ${providers.join(", ")}`);
46
+ }
47
+
48
+ const transport = new StdioServerTransport();
49
+ const server = new Server(
50
+ { name: "git-mcpv2", version: "15.8.0" },
51
+ { capabilities: { tools: {}, resources: {}, prompts: {} } }
52
+ );
53
+ server.connect(transport);
54
+
55
+ // Prompts handler
56
+ const promptsHandler = createPromptsHandler(git, pm);
57
+
58
+ // Ajv singleton para validação
59
+ const ajv = new Ajv({ allErrors: true });
60
+
61
+ const tools = [
62
+ createGitHelpTool(), // Primeiro: AI pode pedir ajuda
63
+ createGitWorkflowTool(pm, git),
64
+ createGitRemoteTool(pm, git),
65
+ createGitBranchesTool(git),
66
+ createGitTagsTool(git),
67
+ createGitStashTool(git),
68
+ createGitResetTool(git),
69
+ createGitConfigTool(git),
70
+ createGitIgnoreTool(git),
71
+ createGitFilesTool(git),
72
+ createGitHistoryTool(git),
73
+ createGitSyncTool(git),
74
+ createGitIssuesTool(pm),
75
+ createGitPullsTool(pm),
76
+ createGitMergeTool(git),
77
+ createGitDiffTool(git),
78
+ createGitCloneTool(git),
79
+ ];
80
+
81
+ // ============ TOOLS ============
82
+ server.setRequestHandler(
83
+ (await import("@modelcontextprotocol/sdk/types.js")).ListToolsRequestSchema,
84
+ async () => ({
85
+ tools: tools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
86
+ })
87
+ );
88
+
89
+ server.setRequestHandler(
90
+ (await import("@modelcontextprotocol/sdk/types.js")).CallToolRequestSchema,
91
+ async (req) => {
92
+ const name = req.params?.name || "";
93
+ const args = req.params?.arguments || {};
94
+ const progressToken = req.params?._meta?.progressToken;
95
+ const tool = tools.find(t => t.name === name);
96
+ if (!tool) return { content: [{ type: "text", text: `Tool não encontrada: ${name}` }], isError: true };
97
+ try {
98
+ if (progressToken) {
99
+ await server.notification({ method: "notifications/progress", params: { progressToken, progress: 0 } });
100
+ }
101
+ const result = await tool.handle(args);
102
+ if (progressToken) {
103
+ await server.notification({ method: "notifications/progress", params: { progressToken, progress: 100 } });
104
+ }
105
+ return result;
106
+ } catch (e) {
107
+ return asToolError(e.code || "ERROR", e.message || String(e));
108
+ }
109
+ }
110
+ );
111
+
112
+ // ============ RESOURCES ============
113
+ server.setRequestHandler(
114
+ (await import("@modelcontextprotocol/sdk/types.js")).ListResourcesRequestSchema,
115
+ async () => ({ resources: getResources() })
116
+ );
117
+
118
+ server.setRequestHandler(
119
+ (await import("@modelcontextprotocol/sdk/types.js")).ReadResourceRequestSchema,
120
+ async (req) => {
121
+ const uri = req.params?.uri || "";
122
+ const content = readResource(uri);
123
+ if (!content) {
124
+ return { contents: [{ uri, mimeType: "text/plain", text: `Resource não encontrado: ${uri}` }] };
125
+ }
126
+ return { contents: [{ uri, mimeType: "text/markdown", text: content }] };
127
+ }
128
+ );
129
+
130
+ // ============ PROMPTS ============
131
+ server.setRequestHandler(
132
+ (await import("@modelcontextprotocol/sdk/types.js")).ListPromptsRequestSchema,
133
+ async () => ({ prompts: PROMPTS })
134
+ );
135
+
136
+ server.setRequestHandler(
137
+ (await import("@modelcontextprotocol/sdk/types.js")).GetPromptRequestSchema,
138
+ async (req) => {
139
+ const name = req.params?.name || "";
140
+ const args = req.params?.arguments || {};
141
+ return await promptsHandler.get(name, args);
142
+ }
143
+ );
144
+
145
+ // Keep process alive
146
+ process.stdin.resume();