@andre.buzeli/git-mcp 16.0.6 → 16.1.2
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/package.json +32 -29
- package/src/index.js +159 -147
- package/src/providers/providerManager.js +31 -104
- package/src/tools/git-branches.js +13 -3
- package/src/tools/git-clone.js +48 -85
- package/src/tools/git-config.js +13 -3
- package/src/tools/git-diff.js +121 -137
- package/src/tools/git-files.js +13 -3
- package/src/tools/git-help.js +322 -284
- package/src/tools/git-history.js +13 -3
- package/src/tools/git-ignore.js +13 -3
- package/src/tools/git-issues.js +13 -3
- package/src/tools/git-merge.js +13 -3
- package/src/tools/git-pulls.js +14 -4
- package/src/tools/git-remote.js +503 -492
- package/src/tools/git-reset.js +23 -4
- package/src/tools/git-stash.js +13 -3
- package/src/tools/git-sync.js +13 -3
- package/src/tools/git-tags.js +13 -3
- package/src/tools/git-workflow.js +599 -469
- package/src/tools/git-worktree.js +180 -0
- package/src/utils/errors.js +434 -433
- package/src/utils/gitAdapter.js +133 -11
- package/src/utils/mcpNotify.js +45 -0
- package/src/utils/repoHelpers.js +5 -31
- package/src/utils/hooks.js +0 -255
- package/src/utils/metrics.js +0 -198
package/package.json
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@andre.buzeli/git-mcp",
|
|
3
|
-
"version": "16.
|
|
4
|
-
"private": false,
|
|
5
|
-
"description": "MCP server para Git com operações locais e sincronização paralela GitHub/Gitea",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "andre.buzeli",
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@andre.buzeli/git-mcp",
|
|
3
|
+
"version": "16.1.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "MCP server para Git com operações locais e sincronização paralela GitHub/Gitea",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "andre.buzeli",
|
|
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
|
+
"start": "node src/index.js"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
26
|
+
"@octokit/rest": "^20.0.0",
|
|
27
|
+
"ajv": "^8.12.0",
|
|
28
|
+
"archiver": "^7.0.0",
|
|
29
|
+
"axios": "^1.7.7",
|
|
30
|
+
"zod": "^3.23.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,147 +1,159 @@
|
|
|
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 {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 { createGitWorktreeTool } from "./tools/git-worktree.js";
|
|
26
|
+
import { createGitHelpTool } from "./tools/git-help.js";
|
|
27
|
+
import { getResources, readResource } from "./resources/index.js";
|
|
28
|
+
import { createPromptsHandler, PROMPTS } from "./prompts/index.js";
|
|
29
|
+
import { sendProgress } from "./utils/mcpNotify.js";
|
|
30
|
+
|
|
31
|
+
// Carrega variáveis de ambiente do arquivo .env (se existir)
|
|
32
|
+
loadEnv();
|
|
33
|
+
|
|
34
|
+
const pm = new ProviderManager();
|
|
35
|
+
const git = new GitAdapter(pm);
|
|
36
|
+
|
|
37
|
+
// Log de inicialização para stderr (não interfere com stdio do MCP)
|
|
38
|
+
const hasGitHub = !!process.env.GITHUB_TOKEN;
|
|
39
|
+
const hasGitea = !!process.env.GITEA_URL && !!process.env.GITEA_TOKEN;
|
|
40
|
+
if (!hasGitHub && !hasGitea) {
|
|
41
|
+
console.error("[git-mcp] ⚠️ Nenhum provider configurado. Operações remotas não funcionarão.");
|
|
42
|
+
console.error("[git-mcp] Configure GITHUB_TOKEN e/ou GITEA_URL + GITEA_TOKEN");
|
|
43
|
+
} else {
|
|
44
|
+
const providers = [];
|
|
45
|
+
if (hasGitHub) providers.push("GitHub");
|
|
46
|
+
if (hasGitea) providers.push("Gitea");
|
|
47
|
+
console.error(`[git-mcp] ✓ Providers ativos: ${providers.join(", ")}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const transport = new StdioServerTransport();
|
|
51
|
+
const server = new Server(
|
|
52
|
+
{ name: "git-mcp", version: "16.0.8" },
|
|
53
|
+
{ capabilities: {
|
|
54
|
+
tools: { listChanged: true },
|
|
55
|
+
resources: { subscribe: true, listChanged: true },
|
|
56
|
+
prompts: {},
|
|
57
|
+
logging: {}
|
|
58
|
+
} }
|
|
59
|
+
);
|
|
60
|
+
server.connect(transport);
|
|
61
|
+
|
|
62
|
+
// Prompts handler
|
|
63
|
+
const promptsHandler = createPromptsHandler(git, pm);
|
|
64
|
+
|
|
65
|
+
// Ajv singleton para validação
|
|
66
|
+
const ajv = new Ajv({ allErrors: true });
|
|
67
|
+
|
|
68
|
+
const tools = [
|
|
69
|
+
createGitHelpTool(), // Primeiro: AI pode pedir ajuda
|
|
70
|
+
createGitWorkflowTool(pm, git, server),
|
|
71
|
+
createGitRemoteTool(pm, git, server),
|
|
72
|
+
createGitBranchesTool(git),
|
|
73
|
+
createGitTagsTool(git),
|
|
74
|
+
createGitStashTool(git),
|
|
75
|
+
createGitResetTool(git, server),
|
|
76
|
+
createGitConfigTool(git),
|
|
77
|
+
createGitIgnoreTool(git),
|
|
78
|
+
createGitFilesTool(git),
|
|
79
|
+
createGitHistoryTool(git),
|
|
80
|
+
createGitSyncTool(git),
|
|
81
|
+
createGitIssuesTool(pm),
|
|
82
|
+
createGitPullsTool(pm),
|
|
83
|
+
createGitMergeTool(git),
|
|
84
|
+
createGitDiffTool(git),
|
|
85
|
+
createGitCloneTool(git),
|
|
86
|
+
createGitWorktreeTool(git),
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
// ============ TOOLS ============
|
|
90
|
+
server.setRequestHandler(
|
|
91
|
+
(await import("@modelcontextprotocol/sdk/types.js")).ListToolsRequestSchema,
|
|
92
|
+
async () => ({
|
|
93
|
+
tools: tools.map(t => ({
|
|
94
|
+
name: t.name,
|
|
95
|
+
description: t.description,
|
|
96
|
+
inputSchema: t.inputSchema,
|
|
97
|
+
annotations: t.annotations
|
|
98
|
+
})),
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
server.setRequestHandler(
|
|
103
|
+
(await import("@modelcontextprotocol/sdk/types.js")).CallToolRequestSchema,
|
|
104
|
+
async (req) => {
|
|
105
|
+
const name = req.params?.name || "";
|
|
106
|
+
const args = req.params?.arguments || {};
|
|
107
|
+
const progressToken = req.params?._meta?.progressToken;
|
|
108
|
+
|
|
109
|
+
const tool = tools.find(t => t.name === name);
|
|
110
|
+
if (!tool) return { content: [{ type: "text", text: `Tool não encontrada: ${name}` }], isError: true };
|
|
111
|
+
try {
|
|
112
|
+
await sendProgress(server, progressToken, 0, 100, `Executando ${name}...`);
|
|
113
|
+
|
|
114
|
+
const result = await tool.handle(args);
|
|
115
|
+
|
|
116
|
+
await sendProgress(server, progressToken, 100, 100, `${name} concluído`);
|
|
117
|
+
|
|
118
|
+
return result;
|
|
119
|
+
} catch (e) {
|
|
120
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// ============ RESOURCES ============
|
|
126
|
+
server.setRequestHandler(
|
|
127
|
+
(await import("@modelcontextprotocol/sdk/types.js")).ListResourcesRequestSchema,
|
|
128
|
+
async () => ({ resources: getResources() })
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
server.setRequestHandler(
|
|
132
|
+
(await import("@modelcontextprotocol/sdk/types.js")).ReadResourceRequestSchema,
|
|
133
|
+
async (req) => {
|
|
134
|
+
const uri = req.params?.uri || "";
|
|
135
|
+
const content = readResource(uri);
|
|
136
|
+
if (!content) {
|
|
137
|
+
return { contents: [{ uri, mimeType: "text/plain", text: `Resource não encontrado: ${uri}` }] };
|
|
138
|
+
}
|
|
139
|
+
return { contents: [{ uri, mimeType: "text/markdown", text: content }] };
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// ============ PROMPTS ============
|
|
144
|
+
server.setRequestHandler(
|
|
145
|
+
(await import("@modelcontextprotocol/sdk/types.js")).ListPromptsRequestSchema,
|
|
146
|
+
async () => ({ prompts: PROMPTS })
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
server.setRequestHandler(
|
|
150
|
+
(await import("@modelcontextprotocol/sdk/types.js")).GetPromptRequestSchema,
|
|
151
|
+
async (req) => {
|
|
152
|
+
const name = req.params?.name || "";
|
|
153
|
+
const args = req.params?.arguments || {};
|
|
154
|
+
return await promptsHandler.get(name, args);
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Keep process alive
|
|
159
|
+
process.stdin.resume();
|
|
@@ -93,7 +93,9 @@ export class ProviderManager {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
async getRemoteUrls(repoName, organization) {
|
|
96
|
-
|
|
96
|
+
// When organization is specified, prefix repo name (no actual org needed)
|
|
97
|
+
const effectiveName = organization ? `${organization}---${repoName}` : repoName;
|
|
98
|
+
const cacheKey = `urls_${effectiveName}`;
|
|
97
99
|
const now = Date.now();
|
|
98
100
|
|
|
99
101
|
// Verificar cache
|
|
@@ -106,20 +108,20 @@ export class ProviderManager {
|
|
|
106
108
|
|
|
107
109
|
const urls = {};
|
|
108
110
|
|
|
109
|
-
// GitHub URL
|
|
111
|
+
// GitHub URL — always personal account
|
|
110
112
|
if (this.github) {
|
|
111
|
-
const owner =
|
|
113
|
+
const owner = await this.getGitHubOwner();
|
|
112
114
|
if (owner) {
|
|
113
|
-
urls.github = `https://github.com/${owner}/${
|
|
115
|
+
urls.github = `https://github.com/${owner}/${effectiveName}.git`;
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
// Gitea URL
|
|
119
|
+
// Gitea URL — always personal account
|
|
118
120
|
if (this.giteaUrl && this.giteaToken) {
|
|
119
|
-
const owner =
|
|
121
|
+
const owner = await this.getGiteaOwner();
|
|
120
122
|
if (owner) {
|
|
121
123
|
const base = this.giteaUrl.replace(/\/$/, "");
|
|
122
|
-
urls.gitea = `${base}/${owner}/${
|
|
124
|
+
urls.gitea = `${base}/${owner}/${effectiveName}.git`;
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
|
|
@@ -130,76 +132,27 @@ export class ProviderManager {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
async ensureRepos({ repoName, createIfMissing = true, description = "Managed by git-mcpv2", isPublic = false, organization }) {
|
|
133
|
-
//
|
|
135
|
+
// When organization is specified, prefix repo name (e.g., "MCP---GIT_MCP")
|
|
136
|
+
const effectiveName = organization ? `${organization}---${repoName}` : repoName;
|
|
134
137
|
const isPrivate = !isPublic;
|
|
135
138
|
const results = { github: null, gitea: null };
|
|
136
|
-
|
|
139
|
+
|
|
140
|
+
// GitHub — always personal account
|
|
137
141
|
if (this.github) {
|
|
138
|
-
const owner =
|
|
142
|
+
const owner = await this.getGitHubOwner();
|
|
139
143
|
if (owner) {
|
|
140
144
|
try {
|
|
141
|
-
const full = `${owner}/${repoName}`;
|
|
142
145
|
try {
|
|
143
|
-
await this.github.rest.repos.get({ owner, repo:
|
|
144
|
-
results.github = { ok: true, repo:
|
|
146
|
+
await this.github.rest.repos.get({ owner, repo: effectiveName });
|
|
147
|
+
results.github = { ok: true, repo: `${owner}/${effectiveName}`, created: false };
|
|
145
148
|
} catch {
|
|
146
149
|
if (createIfMissing) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
name: repoName,
|
|
154
|
-
description,
|
|
155
|
-
private: isPrivate,
|
|
156
|
-
auto_init: false,
|
|
157
|
-
});
|
|
158
|
-
} catch (orgRepoErr) {
|
|
159
|
-
const errMsg = String(orgRepoErr?.message || orgRepoErr).toLowerCase();
|
|
160
|
-
// Se org não existe, criar a org primeiro e depois o repo
|
|
161
|
-
if (errMsg.includes("not found") || errMsg.includes("404")) {
|
|
162
|
-
console.error(`[ProviderManager] GitHub org '${organization}' not found, creating...`);
|
|
163
|
-
try {
|
|
164
|
-
await this.github.rest.orgs.createForAuthenticatedUser
|
|
165
|
-
? await this.github.request("POST /user/orgs", { login: organization, profile_name: organization })
|
|
166
|
-
: null;
|
|
167
|
-
} catch (orgCreateErr) {
|
|
168
|
-
// GitHub free users can't create orgs via API, try alternative
|
|
169
|
-
console.error(`[ProviderManager] GitHub org creation failed: ${orgCreateErr?.message}. Trying as user repo with org topic...`);
|
|
170
|
-
}
|
|
171
|
-
// Retry repo creation in org
|
|
172
|
-
try {
|
|
173
|
-
cr = await this.github.rest.repos.createInOrg({
|
|
174
|
-
org: organization,
|
|
175
|
-
name: repoName,
|
|
176
|
-
description,
|
|
177
|
-
private: isPrivate,
|
|
178
|
-
auto_init: false,
|
|
179
|
-
});
|
|
180
|
-
} catch {
|
|
181
|
-
// Fallback: create as personal repo if org creation not possible
|
|
182
|
-
console.error(`[ProviderManager] Fallback: creating '${repoName}' as personal repo (GitHub org '${organization}' unavailable)`);
|
|
183
|
-
cr = await this.github.rest.repos.createForAuthenticatedUser({
|
|
184
|
-
name: repoName,
|
|
185
|
-
description: `[org:${organization}] ${description}`,
|
|
186
|
-
private: isPrivate,
|
|
187
|
-
auto_init: false,
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
throw orgRepoErr;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
// Criar repo no usuário pessoal
|
|
196
|
-
cr = await this.github.rest.repos.createForAuthenticatedUser({
|
|
197
|
-
name: repoName,
|
|
198
|
-
description,
|
|
199
|
-
private: isPrivate,
|
|
200
|
-
auto_init: false,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
150
|
+
const cr = await this.github.rest.repos.createForAuthenticatedUser({
|
|
151
|
+
name: effectiveName,
|
|
152
|
+
description,
|
|
153
|
+
private: isPrivate,
|
|
154
|
+
auto_init: false,
|
|
155
|
+
});
|
|
203
156
|
results.github = { ok: true, repo: cr.data.full_name, created: true };
|
|
204
157
|
} else {
|
|
205
158
|
results.github = { ok: false, error: "missing" };
|
|
@@ -210,52 +163,26 @@ export class ProviderManager {
|
|
|
210
163
|
}
|
|
211
164
|
}
|
|
212
165
|
}
|
|
213
|
-
|
|
166
|
+
|
|
167
|
+
// Gitea — always personal account
|
|
214
168
|
if (this.giteaUrl && this.giteaToken) {
|
|
215
|
-
const owner =
|
|
169
|
+
const owner = await this.getGiteaOwner();
|
|
216
170
|
if (owner) {
|
|
217
171
|
try {
|
|
218
172
|
const base = this.giteaUrl.replace(/\/$/, "");
|
|
219
|
-
const getRepo = await axios.get(`${base}/api/v1/repos/${owner}/${
|
|
173
|
+
const getRepo = await axios.get(`${base}/api/v1/repos/${owner}/${effectiveName}`,
|
|
220
174
|
{ headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
221
175
|
if (getRepo.status === 200) {
|
|
222
|
-
results.gitea = { ok: true, repo: `${owner}/${
|
|
176
|
+
results.gitea = { ok: true, repo: `${owner}/${effectiveName}`, created: false };
|
|
223
177
|
}
|
|
224
178
|
} catch (e) {
|
|
225
179
|
if (createIfMissing) {
|
|
226
180
|
try {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
await axios.get(`${this.giteaUrl}/api/v1/orgs/${organization}`,
|
|
232
|
-
{ headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
233
|
-
} catch (orgCheckErr) {
|
|
234
|
-
// Org não existe, criar
|
|
235
|
-
console.error(`[ProviderManager] Gitea org '${organization}' not found, creating...`);
|
|
236
|
-
try {
|
|
237
|
-
await axios.post(`${this.giteaUrl}/api/v1/orgs`, {
|
|
238
|
-
username: organization,
|
|
239
|
-
full_name: organization,
|
|
240
|
-
description: `Organization ${organization}`,
|
|
241
|
-
visibility: isPublic ? "public" : "limited",
|
|
242
|
-
}, { headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
243
|
-
console.error(`[ProviderManager] Gitea org '${organization}' created successfully`);
|
|
244
|
-
} catch (orgCreateErr) {
|
|
245
|
-
const msg = String(orgCreateErr?.message || orgCreateErr).toLowerCase();
|
|
246
|
-
if (!msg.includes("already exists") && !msg.includes("409")) {
|
|
247
|
-
console.error(`[ProviderManager] Gitea org creation failed: ${orgCreateErr?.message}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
createUrl = `${this.giteaUrl}/api/v1/orgs/${organization}/repos`;
|
|
252
|
-
} else {
|
|
253
|
-
createUrl = `${this.giteaUrl}/api/v1/user/repos`;
|
|
254
|
-
}
|
|
255
|
-
const cr = await axios.post(createUrl,
|
|
256
|
-
{ name: repoName, description, private: isPrivate, auto_init: false },
|
|
181
|
+
const base = this.giteaUrl.replace(/\/$/, "");
|
|
182
|
+
const cr = await axios.post(`${base}/api/v1/user/repos`,
|
|
183
|
+
{ name: effectiveName, description, private: isPrivate, auto_init: false },
|
|
257
184
|
{ headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
258
|
-
results.gitea = { ok: true, repo: `${cr.data?.owner?.login || owner}/${
|
|
185
|
+
results.gitea = { ok: true, repo: `${cr.data?.owner?.login || owner}/${effectiveName}`, created: true };
|
|
259
186
|
} catch (err) {
|
|
260
187
|
results.gitea = { ok: false, error: String(err?.message || err) };
|
|
261
188
|
}
|
|
@@ -10,7 +10,7 @@ export function createGitBranchesTool(git) {
|
|
|
10
10
|
properties: {
|
|
11
11
|
projectPath: {
|
|
12
12
|
type: "string",
|
|
13
|
-
description: "Caminho absoluto do diretório do projeto"
|
|
13
|
+
description: "Caminho absoluto do diretório do projeto no IDE (ex: '/home/user/meu-projeto' ou 'C:/Users/user/meu-projeto'). IMPORTANTE: este valor não pode ser inferido automaticamente pelo servidor — o agente deve fornecer o path real do projeto sendo trabalhado, não o diretório home do usuário."
|
|
14
14
|
},
|
|
15
15
|
action: {
|
|
16
16
|
type: "string",
|
|
@@ -39,7 +39,11 @@ export function createGitBranchesTool(git) {
|
|
|
39
39
|
additionalProperties: false
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
const description = `
|
|
42
|
+
const description = `IMPORTANTE — projectPath:
|
|
43
|
+
Informe o caminho absoluto do projeto aberto no IDE. O servidor MCP não tem acesso ao
|
|
44
|
+
contexto do IDE e não consegue detectar automaticamente qual projeto está sendo trabalhado.
|
|
45
|
+
|
|
46
|
+
Gerenciamento de branches Git.
|
|
43
47
|
|
|
44
48
|
AÇÕES DISPONÍVEIS:
|
|
45
49
|
- list: Ver branches existentes (locais e remotas)
|
|
@@ -122,5 +126,11 @@ CONVENÇÕES DE NOMES:
|
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
return {
|
|
129
|
+
return {
|
|
130
|
+
name: "git-branches",
|
|
131
|
+
description,
|
|
132
|
+
inputSchema,
|
|
133
|
+
handle,
|
|
134
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
135
|
+
};
|
|
126
136
|
}
|