@cyanheads/git-mcp-server 1.2.4
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/LICENSE +201 -0
- package/README.md +383 -0
- package/build/index.js +54 -0
- package/build/resources/descriptors.js +77 -0
- package/build/resources/diff.js +241 -0
- package/build/resources/file.js +222 -0
- package/build/resources/history.js +242 -0
- package/build/resources/index.js +99 -0
- package/build/resources/repository.js +286 -0
- package/build/server.js +120 -0
- package/build/services/error-service.js +73 -0
- package/build/services/git-service.js +965 -0
- package/build/tools/advanced.js +526 -0
- package/build/tools/branch.js +296 -0
- package/build/tools/index.js +29 -0
- package/build/tools/remote.js +279 -0
- package/build/tools/repository.js +170 -0
- package/build/tools/workdir.js +445 -0
- package/build/types/git.js +7 -0
- package/build/utils/global-settings.js +64 -0
- package/build/utils/validation.js +108 -0
- package/package.json +39 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History Resources
|
|
3
|
+
* ================
|
|
4
|
+
*
|
|
5
|
+
* MCP resources for exposing Git commit history and related information.
|
|
6
|
+
*/
|
|
7
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { GitService } from '../services/git-service.js';
|
|
9
|
+
import { PathValidation } from '../utils/validation.js';
|
|
10
|
+
/**
|
|
11
|
+
* Helper function to ensure a variable is treated as a string
|
|
12
|
+
*
|
|
13
|
+
* @param value - The value to convert to string
|
|
14
|
+
* @returns A string representation of the value
|
|
15
|
+
*/
|
|
16
|
+
function ensureString(value) {
|
|
17
|
+
return Array.isArray(value) ? value[0] : value;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Registers history resources with the MCP server
|
|
21
|
+
*
|
|
22
|
+
* @param server - MCP server instance
|
|
23
|
+
* @param resourceDescriptors - Resource descriptors for metadata
|
|
24
|
+
*/
|
|
25
|
+
export function setupHistoryResources(server, resourceDescriptors) {
|
|
26
|
+
// Commit log resource
|
|
27
|
+
server.resource("commit-log", new ResourceTemplate("git://repo/{repoPath}/log?maxCount={maxCount}&file={file}", { list: undefined }), {
|
|
28
|
+
name: "Commit History",
|
|
29
|
+
description: "Returns the commit history log with author, date, and message details",
|
|
30
|
+
mimeType: "application/json"
|
|
31
|
+
},
|
|
32
|
+
// Returns the commit history for a repository with optional file path filter and commit count limit
|
|
33
|
+
async (uri, variables) => {
|
|
34
|
+
try {
|
|
35
|
+
// Handle variables which might be arrays
|
|
36
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
37
|
+
const maxCountStr = variables.maxCount ? ensureString(variables.maxCount) : '50';
|
|
38
|
+
const fileStr = variables.file ? ensureString(variables.file) : undefined;
|
|
39
|
+
// Normalize paths
|
|
40
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
41
|
+
// Parse max count
|
|
42
|
+
const maxCount = parseInt(maxCountStr, 10);
|
|
43
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
44
|
+
// Check if the path is a Git repository
|
|
45
|
+
const isRepo = await gitService.isGitRepository();
|
|
46
|
+
if (!isRepo) {
|
|
47
|
+
return {
|
|
48
|
+
contents: [{
|
|
49
|
+
uri: uri.href,
|
|
50
|
+
text: JSON.stringify({
|
|
51
|
+
error: "Not a Git repository",
|
|
52
|
+
repoPath: normalizedRepoPath,
|
|
53
|
+
maxCount: isNaN(maxCount) ? undefined : maxCount,
|
|
54
|
+
file: fileStr
|
|
55
|
+
}, null, 2),
|
|
56
|
+
mimeType: "application/json"
|
|
57
|
+
}]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Get commit log
|
|
61
|
+
const logResult = await gitService.getLog({
|
|
62
|
+
maxCount: isNaN(maxCount) ? 50 : maxCount,
|
|
63
|
+
file: fileStr
|
|
64
|
+
});
|
|
65
|
+
if (!logResult.resultSuccessful) {
|
|
66
|
+
return {
|
|
67
|
+
contents: [{
|
|
68
|
+
uri: uri.href,
|
|
69
|
+
text: JSON.stringify({
|
|
70
|
+
error: logResult.resultError.errorMessage,
|
|
71
|
+
repoPath: normalizedRepoPath,
|
|
72
|
+
maxCount: isNaN(maxCount) ? undefined : maxCount,
|
|
73
|
+
file: fileStr
|
|
74
|
+
}, null, 2),
|
|
75
|
+
mimeType: "application/json"
|
|
76
|
+
}]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
contents: [{
|
|
81
|
+
uri: uri.href,
|
|
82
|
+
text: JSON.stringify({
|
|
83
|
+
repoPath: normalizedRepoPath,
|
|
84
|
+
maxCount: isNaN(maxCount) ? 50 : maxCount,
|
|
85
|
+
file: fileStr,
|
|
86
|
+
commits: logResult.resultData
|
|
87
|
+
}, null, 2),
|
|
88
|
+
mimeType: "application/json"
|
|
89
|
+
}]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return {
|
|
94
|
+
contents: [{
|
|
95
|
+
uri: uri.href,
|
|
96
|
+
text: JSON.stringify({
|
|
97
|
+
error: error instanceof Error ? error.message : String(error),
|
|
98
|
+
repoPath: ensureString(variables.repoPath),
|
|
99
|
+
maxCount: variables.maxCount ? parseInt(ensureString(variables.maxCount), 10) : 50,
|
|
100
|
+
file: variables.file ? ensureString(variables.file) : undefined
|
|
101
|
+
}, null, 2),
|
|
102
|
+
mimeType: "application/json"
|
|
103
|
+
}]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// File blame resource
|
|
108
|
+
server.resource("file-blame", new ResourceTemplate("git://repo/{repoPath}/blame/{filePath}", { list: undefined }), {
|
|
109
|
+
name: "File Blame",
|
|
110
|
+
description: "Returns line-by-line attribution showing which commit last modified each line",
|
|
111
|
+
mimeType: "text/plain"
|
|
112
|
+
},
|
|
113
|
+
// Returns the blame information showing which commit last modified each line of a file
|
|
114
|
+
async (uri, variables) => {
|
|
115
|
+
try {
|
|
116
|
+
// Handle variables which might be arrays
|
|
117
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
118
|
+
const filePathStr = ensureString(variables.filePath);
|
|
119
|
+
// Normalize paths
|
|
120
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
121
|
+
const normalizedFilePath = PathValidation.normalizePath(decodeURIComponent(filePathStr));
|
|
122
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
123
|
+
// Check if the path is a Git repository
|
|
124
|
+
const isRepo = await gitService.isGitRepository();
|
|
125
|
+
if (!isRepo) {
|
|
126
|
+
return {
|
|
127
|
+
contents: [{
|
|
128
|
+
uri: uri.href,
|
|
129
|
+
text: JSON.stringify({
|
|
130
|
+
error: "Not a Git repository",
|
|
131
|
+
repoPath: normalizedRepoPath,
|
|
132
|
+
filePath: normalizedFilePath
|
|
133
|
+
}, null, 2),
|
|
134
|
+
mimeType: "application/json"
|
|
135
|
+
}]
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// Get blame information
|
|
139
|
+
const blameResult = await gitService.getBlame(normalizedFilePath);
|
|
140
|
+
if (!blameResult.resultSuccessful) {
|
|
141
|
+
return {
|
|
142
|
+
contents: [{
|
|
143
|
+
uri: uri.href,
|
|
144
|
+
text: JSON.stringify({
|
|
145
|
+
error: blameResult.resultError.errorMessage,
|
|
146
|
+
repoPath: normalizedRepoPath,
|
|
147
|
+
filePath: normalizedFilePath
|
|
148
|
+
}, null, 2),
|
|
149
|
+
mimeType: "application/json"
|
|
150
|
+
}]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
contents: [{
|
|
155
|
+
uri: uri.href,
|
|
156
|
+
text: blameResult.resultData,
|
|
157
|
+
mimeType: "text/plain"
|
|
158
|
+
}]
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return {
|
|
163
|
+
contents: [{
|
|
164
|
+
uri: uri.href,
|
|
165
|
+
text: JSON.stringify({
|
|
166
|
+
error: error instanceof Error ? error.message : String(error),
|
|
167
|
+
repoPath: ensureString(variables.repoPath),
|
|
168
|
+
filePath: ensureString(variables.filePath)
|
|
169
|
+
}, null, 2),
|
|
170
|
+
mimeType: "application/json"
|
|
171
|
+
}]
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
// Show commit details
|
|
176
|
+
server.resource("commit-show", new ResourceTemplate("git://repo/{repoPath}/commit/{commitHash}", { list: undefined }), {
|
|
177
|
+
name: "Commit Details",
|
|
178
|
+
description: "Returns detailed information about a specific commit including diff changes",
|
|
179
|
+
mimeType: "text/plain"
|
|
180
|
+
},
|
|
181
|
+
// Returns the detailed information for a specific commit including diff and metadata
|
|
182
|
+
async (uri, variables) => {
|
|
183
|
+
try {
|
|
184
|
+
// Handle variables which might be arrays
|
|
185
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
186
|
+
const commitHashStr = ensureString(variables.commitHash);
|
|
187
|
+
// Normalize paths
|
|
188
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
189
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
190
|
+
// Check if the path is a Git repository
|
|
191
|
+
const isRepo = await gitService.isGitRepository();
|
|
192
|
+
if (!isRepo) {
|
|
193
|
+
return {
|
|
194
|
+
contents: [{
|
|
195
|
+
uri: uri.href,
|
|
196
|
+
text: JSON.stringify({
|
|
197
|
+
error: "Not a Git repository",
|
|
198
|
+
repoPath: normalizedRepoPath,
|
|
199
|
+
commitHash: commitHashStr
|
|
200
|
+
}, null, 2),
|
|
201
|
+
mimeType: "application/json"
|
|
202
|
+
}]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// Get commit details
|
|
206
|
+
const commitResult = await gitService.showCommit(commitHashStr);
|
|
207
|
+
if (!commitResult.resultSuccessful) {
|
|
208
|
+
return {
|
|
209
|
+
contents: [{
|
|
210
|
+
uri: uri.href,
|
|
211
|
+
text: JSON.stringify({
|
|
212
|
+
error: commitResult.resultError.errorMessage,
|
|
213
|
+
repoPath: normalizedRepoPath,
|
|
214
|
+
commitHash: commitHashStr
|
|
215
|
+
}, null, 2),
|
|
216
|
+
mimeType: "application/json"
|
|
217
|
+
}]
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
contents: [{
|
|
222
|
+
uri: uri.href,
|
|
223
|
+
text: commitResult.resultData,
|
|
224
|
+
mimeType: "text/plain"
|
|
225
|
+
}]
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
return {
|
|
230
|
+
contents: [{
|
|
231
|
+
uri: uri.href,
|
|
232
|
+
text: JSON.stringify({
|
|
233
|
+
error: error instanceof Error ? error.message : String(error),
|
|
234
|
+
repoPath: ensureString(variables.repoPath),
|
|
235
|
+
commitHash: ensureString(variables.commitHash)
|
|
236
|
+
}, null, 2),
|
|
237
|
+
mimeType: "application/json"
|
|
238
|
+
}]
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Handlers
|
|
3
|
+
* ===============
|
|
4
|
+
*
|
|
5
|
+
* Entry point for all MCP resource implementations.
|
|
6
|
+
* This module registers all resource handlers with the MCP server.
|
|
7
|
+
*/
|
|
8
|
+
import { setupRepositoryResources } from './repository.js';
|
|
9
|
+
import { setupFileResources } from './file.js';
|
|
10
|
+
import { setupDiffResources } from './diff.js';
|
|
11
|
+
import { setupHistoryResources } from './history.js';
|
|
12
|
+
import { resourceDescriptors } from './descriptors.js';
|
|
13
|
+
/**
|
|
14
|
+
* Metadata for resource descriptions
|
|
15
|
+
*/
|
|
16
|
+
export const resourceMetadata = {
|
|
17
|
+
// Repository resources
|
|
18
|
+
"repository-info": {
|
|
19
|
+
name: "Repository Information",
|
|
20
|
+
description: "Returns basic Git repository information including current branch, status, and reference details",
|
|
21
|
+
mimeType: "application/json"
|
|
22
|
+
},
|
|
23
|
+
"repository-branches": {
|
|
24
|
+
name: "Repository Branches",
|
|
25
|
+
description: "Returns a list of all branches in the repository with current branch indicator",
|
|
26
|
+
mimeType: "application/json"
|
|
27
|
+
},
|
|
28
|
+
"repository-remotes": {
|
|
29
|
+
name: "Repository Remotes",
|
|
30
|
+
description: "Returns a list of all configured remote repositories with their URLs",
|
|
31
|
+
mimeType: "application/json"
|
|
32
|
+
},
|
|
33
|
+
"repository-tags": {
|
|
34
|
+
name: "Repository Tags",
|
|
35
|
+
description: "Returns a list of all tags in the repository with their references",
|
|
36
|
+
mimeType: "application/json"
|
|
37
|
+
},
|
|
38
|
+
// File resources
|
|
39
|
+
"file-at-ref": {
|
|
40
|
+
name: "File Content",
|
|
41
|
+
description: "Returns the content of a specific file at a given Git reference",
|
|
42
|
+
mimeType: "text/plain"
|
|
43
|
+
},
|
|
44
|
+
"directory-listing": {
|
|
45
|
+
name: "Directory Listing",
|
|
46
|
+
description: "Returns a list of files and directories at a specific path and reference",
|
|
47
|
+
mimeType: "application/json"
|
|
48
|
+
},
|
|
49
|
+
// Diff resources
|
|
50
|
+
"diff-refs": {
|
|
51
|
+
name: "Reference Diff",
|
|
52
|
+
description: "Returns a diff between two Git references (commits, branches, tags)",
|
|
53
|
+
mimeType: "text/plain"
|
|
54
|
+
},
|
|
55
|
+
"diff-unstaged": {
|
|
56
|
+
name: "Unstaged Changes Diff",
|
|
57
|
+
description: "Returns a diff of all unstaged changes in the working directory",
|
|
58
|
+
mimeType: "text/plain"
|
|
59
|
+
},
|
|
60
|
+
"diff-staged": {
|
|
61
|
+
name: "Staged Changes Diff",
|
|
62
|
+
description: "Returns a diff of all staged changes in the index",
|
|
63
|
+
mimeType: "text/plain"
|
|
64
|
+
},
|
|
65
|
+
// History resources
|
|
66
|
+
"commit-log": {
|
|
67
|
+
name: "Commit History",
|
|
68
|
+
description: "Returns the commit history log with author, date, and message details",
|
|
69
|
+
mimeType: "application/json"
|
|
70
|
+
},
|
|
71
|
+
"file-blame": {
|
|
72
|
+
name: "File Blame",
|
|
73
|
+
description: "Returns line-by-line attribution showing which commit last modified each line",
|
|
74
|
+
mimeType: "text/plain"
|
|
75
|
+
},
|
|
76
|
+
"commit-show": {
|
|
77
|
+
name: "Commit Details",
|
|
78
|
+
description: "Returns detailed information about a specific commit including diff changes",
|
|
79
|
+
mimeType: "text/plain"
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Registers all Git MCP resources with the server
|
|
84
|
+
*
|
|
85
|
+
* @param server - MCP server instance
|
|
86
|
+
*/
|
|
87
|
+
export function registerAllResources(server) {
|
|
88
|
+
// Repository info resources (status, branches, remotes, etc.)
|
|
89
|
+
setupRepositoryResources(server, resourceDescriptors);
|
|
90
|
+
// File resources (file content, directory listings)
|
|
91
|
+
setupFileResources(server, resourceDescriptors);
|
|
92
|
+
// Diff resources (changes between refs, unstaged, staged)
|
|
93
|
+
setupDiffResources(server, resourceDescriptors);
|
|
94
|
+
// History resources (log, blame, show commit)
|
|
95
|
+
setupHistoryResources(server, resourceDescriptors);
|
|
96
|
+
// Note: Metadata for resources is defined in the resourceMetadata object,
|
|
97
|
+
// which can be used when registering resources in their respective handlers.
|
|
98
|
+
// The MCP protocol exposes this metadata through the registered resources.
|
|
99
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Resources
|
|
3
|
+
* ===================
|
|
4
|
+
*
|
|
5
|
+
* MCP resources for exposing Git repository information.
|
|
6
|
+
*/
|
|
7
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { GitService } from '../services/git-service.js';
|
|
9
|
+
import { PathValidation } from '../utils/validation.js';
|
|
10
|
+
/**
|
|
11
|
+
* Helper function to ensure a variable is treated as a string
|
|
12
|
+
*
|
|
13
|
+
* @param value - The value to convert to string
|
|
14
|
+
* @returns A string representation of the value
|
|
15
|
+
*/
|
|
16
|
+
function ensureString(value) {
|
|
17
|
+
return Array.isArray(value) ? value[0] : value;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Registers repository resources with the MCP server
|
|
21
|
+
*
|
|
22
|
+
* @param server - MCP server instance
|
|
23
|
+
* @param resourceDescriptors - Resource descriptors for metadata
|
|
24
|
+
*/
|
|
25
|
+
export function setupRepositoryResources(server, resourceDescriptors) {
|
|
26
|
+
// Repository information resource
|
|
27
|
+
// Repository information resource
|
|
28
|
+
server.resource("repository-info", new ResourceTemplate("git://repo/{repoPath}/info", { list: undefined }), {
|
|
29
|
+
name: "Repository Information",
|
|
30
|
+
description: "Basic Git repository information including current branch, status, and reference details",
|
|
31
|
+
mimeType: "application/json"
|
|
32
|
+
}, async (uri, variables) => {
|
|
33
|
+
try {
|
|
34
|
+
// Handle repoPath which might be an array
|
|
35
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
36
|
+
const normalizedPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
37
|
+
const gitService = new GitService(normalizedPath);
|
|
38
|
+
// Check if the path is a Git repository
|
|
39
|
+
const isRepo = await gitService.isGitRepository();
|
|
40
|
+
if (!isRepo) {
|
|
41
|
+
return {
|
|
42
|
+
contents: [{
|
|
43
|
+
uri: uri.href,
|
|
44
|
+
text: JSON.stringify({
|
|
45
|
+
error: "Not a Git repository",
|
|
46
|
+
path: normalizedPath,
|
|
47
|
+
isGitRepository: false
|
|
48
|
+
}, null, 2),
|
|
49
|
+
mimeType: "application/json"
|
|
50
|
+
}]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Get repository status
|
|
54
|
+
const statusResult = await gitService.getStatus();
|
|
55
|
+
if (!statusResult.resultSuccessful) {
|
|
56
|
+
return {
|
|
57
|
+
contents: [{
|
|
58
|
+
uri: uri.href,
|
|
59
|
+
text: JSON.stringify({
|
|
60
|
+
error: statusResult.resultError.errorMessage,
|
|
61
|
+
path: normalizedPath,
|
|
62
|
+
isGitRepository: true
|
|
63
|
+
}, null, 2),
|
|
64
|
+
mimeType: "application/json"
|
|
65
|
+
}]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
contents: [{
|
|
70
|
+
uri: uri.href,
|
|
71
|
+
text: JSON.stringify({
|
|
72
|
+
path: normalizedPath,
|
|
73
|
+
isGitRepository: true,
|
|
74
|
+
status: statusResult.resultData
|
|
75
|
+
}, null, 2),
|
|
76
|
+
mimeType: "application/json"
|
|
77
|
+
}]
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
contents: [{
|
|
83
|
+
uri: uri.href,
|
|
84
|
+
text: JSON.stringify({
|
|
85
|
+
error: error instanceof Error ? error.message : String(error),
|
|
86
|
+
path: ensureString(variables.repoPath),
|
|
87
|
+
isGitRepository: false
|
|
88
|
+
}, null, 2),
|
|
89
|
+
mimeType: "application/json"
|
|
90
|
+
}]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// Repository branches resource
|
|
95
|
+
// Branches resource
|
|
96
|
+
server.resource("repository-branches", new ResourceTemplate("git://repo/{repoPath}/branches", { list: undefined }), {
|
|
97
|
+
name: "Repository Branches",
|
|
98
|
+
description: "List of all branches in the repository with current branch indicator",
|
|
99
|
+
mimeType: "application/json"
|
|
100
|
+
}, async (uri, variables) => {
|
|
101
|
+
try {
|
|
102
|
+
// Handle repoPath which might be an array
|
|
103
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
104
|
+
const normalizedPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
105
|
+
const gitService = new GitService(normalizedPath);
|
|
106
|
+
// Check if the path is a Git repository
|
|
107
|
+
const isRepo = await gitService.isGitRepository();
|
|
108
|
+
if (!isRepo) {
|
|
109
|
+
return {
|
|
110
|
+
contents: [{
|
|
111
|
+
uri: uri.href,
|
|
112
|
+
text: JSON.stringify({
|
|
113
|
+
error: "Not a Git repository",
|
|
114
|
+
path: normalizedPath
|
|
115
|
+
}, null, 2),
|
|
116
|
+
mimeType: "application/json"
|
|
117
|
+
}]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Get branches
|
|
121
|
+
const branchesResult = await gitService.listBranches(true);
|
|
122
|
+
if (!branchesResult.resultSuccessful) {
|
|
123
|
+
return {
|
|
124
|
+
contents: [{
|
|
125
|
+
uri: uri.href,
|
|
126
|
+
text: JSON.stringify({
|
|
127
|
+
error: branchesResult.resultError.errorMessage,
|
|
128
|
+
path: normalizedPath
|
|
129
|
+
}, null, 2),
|
|
130
|
+
mimeType: "application/json"
|
|
131
|
+
}]
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
contents: [{
|
|
136
|
+
uri: uri.href,
|
|
137
|
+
text: JSON.stringify({
|
|
138
|
+
path: normalizedPath,
|
|
139
|
+
branches: branchesResult.resultData
|
|
140
|
+
}, null, 2),
|
|
141
|
+
mimeType: "application/json"
|
|
142
|
+
}]
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
contents: [{
|
|
148
|
+
uri: uri.href,
|
|
149
|
+
text: JSON.stringify({
|
|
150
|
+
error: error instanceof Error ? error.message : String(error),
|
|
151
|
+
path: ensureString(variables.repoPath)
|
|
152
|
+
}, null, 2),
|
|
153
|
+
mimeType: "application/json"
|
|
154
|
+
}]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
// Repository remotes resource
|
|
159
|
+
// Remotes resource
|
|
160
|
+
server.resource("repository-remotes", new ResourceTemplate("git://repo/{repoPath}/remotes", { list: undefined }), {
|
|
161
|
+
name: "Repository Remotes",
|
|
162
|
+
description: "List of all configured remote repositories with their URLs",
|
|
163
|
+
mimeType: "application/json"
|
|
164
|
+
}, async (uri, variables) => {
|
|
165
|
+
try {
|
|
166
|
+
// Handle repoPath which might be an array
|
|
167
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
168
|
+
const normalizedPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
169
|
+
const gitService = new GitService(normalizedPath);
|
|
170
|
+
// Check if the path is a Git repository
|
|
171
|
+
const isRepo = await gitService.isGitRepository();
|
|
172
|
+
if (!isRepo) {
|
|
173
|
+
return {
|
|
174
|
+
contents: [{
|
|
175
|
+
uri: uri.href,
|
|
176
|
+
text: JSON.stringify({
|
|
177
|
+
error: "Not a Git repository",
|
|
178
|
+
path: normalizedPath
|
|
179
|
+
}, null, 2),
|
|
180
|
+
mimeType: "application/json"
|
|
181
|
+
}]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// Get remotes
|
|
185
|
+
const remotesResult = await gitService.listRemotes();
|
|
186
|
+
if (!remotesResult.resultSuccessful) {
|
|
187
|
+
return {
|
|
188
|
+
contents: [{
|
|
189
|
+
uri: uri.href,
|
|
190
|
+
text: JSON.stringify({
|
|
191
|
+
error: remotesResult.resultError.errorMessage,
|
|
192
|
+
path: normalizedPath
|
|
193
|
+
}, null, 2),
|
|
194
|
+
mimeType: "application/json"
|
|
195
|
+
}]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
contents: [{
|
|
200
|
+
uri: uri.href,
|
|
201
|
+
text: JSON.stringify({
|
|
202
|
+
path: normalizedPath,
|
|
203
|
+
remotes: remotesResult.resultData
|
|
204
|
+
}, null, 2),
|
|
205
|
+
mimeType: "application/json"
|
|
206
|
+
}]
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
contents: [{
|
|
212
|
+
uri: uri.href,
|
|
213
|
+
text: JSON.stringify({
|
|
214
|
+
error: error instanceof Error ? error.message : String(error),
|
|
215
|
+
path: ensureString(variables.repoPath)
|
|
216
|
+
}, null, 2),
|
|
217
|
+
mimeType: "application/json"
|
|
218
|
+
}]
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
// Repository tags resource
|
|
223
|
+
// Tags resource
|
|
224
|
+
server.resource("repository-tags", new ResourceTemplate("git://repo/{repoPath}/tags", { list: undefined }), {
|
|
225
|
+
name: "Repository Tags",
|
|
226
|
+
description: "List of all tags in the repository with their references",
|
|
227
|
+
mimeType: "application/json"
|
|
228
|
+
}, async (uri, variables) => {
|
|
229
|
+
try {
|
|
230
|
+
// Handle repoPath which might be an array
|
|
231
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
232
|
+
const normalizedPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
233
|
+
const gitService = new GitService(normalizedPath);
|
|
234
|
+
// Check if the path is a Git repository
|
|
235
|
+
const isRepo = await gitService.isGitRepository();
|
|
236
|
+
if (!isRepo) {
|
|
237
|
+
return {
|
|
238
|
+
contents: [{
|
|
239
|
+
uri: uri.href,
|
|
240
|
+
text: JSON.stringify({
|
|
241
|
+
error: "Not a Git repository",
|
|
242
|
+
path: normalizedPath
|
|
243
|
+
}, null, 2),
|
|
244
|
+
mimeType: "application/json"
|
|
245
|
+
}]
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
// Get tags
|
|
249
|
+
const tagsResult = await gitService.listTags();
|
|
250
|
+
if (!tagsResult.resultSuccessful) {
|
|
251
|
+
return {
|
|
252
|
+
contents: [{
|
|
253
|
+
uri: uri.href,
|
|
254
|
+
text: JSON.stringify({
|
|
255
|
+
error: tagsResult.resultError.errorMessage,
|
|
256
|
+
path: normalizedPath
|
|
257
|
+
}, null, 2),
|
|
258
|
+
mimeType: "application/json"
|
|
259
|
+
}]
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
contents: [{
|
|
264
|
+
uri: uri.href,
|
|
265
|
+
text: JSON.stringify({
|
|
266
|
+
path: normalizedPath,
|
|
267
|
+
tags: tagsResult.resultData
|
|
268
|
+
}, null, 2),
|
|
269
|
+
mimeType: "application/json"
|
|
270
|
+
}]
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
return {
|
|
275
|
+
contents: [{
|
|
276
|
+
uri: uri.href,
|
|
277
|
+
text: JSON.stringify({
|
|
278
|
+
error: error instanceof Error ? error.message : String(error),
|
|
279
|
+
path: ensureString(variables.repoPath)
|
|
280
|
+
}, null, 2),
|
|
281
|
+
mimeType: "application/json"
|
|
282
|
+
}]
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|