@cyanheads/git-mcp-server 2.0.6 → 2.0.8
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 +7 -4
- package/dist/mcp-server/tools/gitAdd/logic.js +1 -1
- package/dist/mcp-server/tools/gitBranch/logic.js +1 -1
- package/dist/mcp-server/tools/gitCheckout/logic.js +1 -1
- package/dist/mcp-server/tools/gitCherryPick/logic.js +1 -1
- package/dist/mcp-server/tools/gitClean/logic.js +1 -1
- package/dist/mcp-server/tools/gitClone/logic.js +1 -1
- package/dist/mcp-server/tools/gitCommit/logic.js +1 -1
- package/dist/mcp-server/tools/gitDiff/logic.js +1 -1
- package/dist/mcp-server/tools/gitFetch/logic.js +1 -1
- package/dist/mcp-server/tools/gitInit/logic.js +1 -1
- package/dist/mcp-server/tools/gitInit/registration.js +2 -2
- package/dist/mcp-server/tools/gitLog/logic.js +1 -1
- package/dist/mcp-server/tools/gitPull/logic.js +1 -1
- package/dist/mcp-server/tools/gitPush/logic.js +1 -1
- package/dist/mcp-server/tools/gitRebase/logic.js +1 -1
- package/dist/mcp-server/tools/gitRemote/logic.js +1 -1
- package/dist/mcp-server/tools/gitReset/logic.js +1 -1
- package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +2 -2
- package/dist/mcp-server/tools/gitShow/logic.js +1 -1
- package/dist/mcp-server/tools/gitStash/logic.js +1 -1
- package/dist/mcp-server/tools/gitStatus/logic.js +1 -1
- package/dist/mcp-server/tools/gitTag/logic.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.typescriptlang.org/)
|
|
4
4
|
[](https://modelcontextprotocol.io/)
|
|
5
|
-
[](./CHANGELOG.md)
|
|
6
6
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
7
7
|
[](https://github.com/cyanheads/git-mcp-server/issues)
|
|
8
8
|
[](https://github.com/cyanheads/git-mcp-server)
|
|
@@ -16,7 +16,9 @@ Built on the [`cyanheads/mcp-ts-template`](https://github.com/cyanheads/mcp-ts-t
|
|
|
16
16
|
## Table of Contents
|
|
17
17
|
|
|
18
18
|
| [Overview](#overview) | [Features](#features) | [Installation](#installation) |
|
|
19
|
+
|
|
19
20
|
| [Configuration](#configuration) | [Project Structure](#project-structure) |
|
|
21
|
+
|
|
20
22
|
| [Tools](#tools) | [Resources](#resources) | [Development](#development) | [License](#license) |
|
|
21
23
|
|
|
22
24
|
## Overview
|
|
@@ -68,7 +70,7 @@ Leverages the robust utilities provided by the `mcp-ts-template`:
|
|
|
68
70
|
|
|
69
71
|
1. Install the package globally:
|
|
70
72
|
```bash
|
|
71
|
-
npm install git-mcp-server
|
|
73
|
+
npm install @cyanheads/git-mcp-server
|
|
72
74
|
```
|
|
73
75
|
|
|
74
76
|
### Install from Source
|
|
@@ -97,11 +99,12 @@ Configure the server using environment variables. Create a `.env` file in the pr
|
|
|
97
99
|
| Variable | Description | Default |
|
|
98
100
|
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
|
|
99
101
|
| `MCP_TRANSPORT_TYPE` | Transport mechanism: `stdio` or `http`. | `stdio` |
|
|
100
|
-
| `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). Retries next ports if busy. | `
|
|
102
|
+
| `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). Retries next ports if busy. | `3010` |
|
|
101
103
|
| `MCP_HTTP_HOST` | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `127.0.0.1` |
|
|
102
104
|
| `MCP_ALLOWED_ORIGINS` | Comma-separated list of allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`). | (none) |
|
|
103
105
|
| `MCP_LOG_LEVEL` | Logging level (`debug`, `info`, `notice`, `warning`, `error`, `crit`, `alert`, `emerg`). Inherited from template. | `info` |
|
|
104
106
|
| `GIT_SIGN_COMMITS` | Set to `"true"` to enable signing attempts for commits made by the `git_commit` tool. Requires server-side Git/key setup (see below). | `false` |
|
|
107
|
+
| `MCP_AUTH_SECRET_KEY` | Secret key for signing/verifying authentication tokens (required if auth is enabled in the future). | `''` |
|
|
105
108
|
|
|
106
109
|
### MCP Client Settings
|
|
107
110
|
|
|
@@ -176,7 +179,7 @@ The Git MCP Server provides a suite of tools for interacting with Git repositori
|
|
|
176
179
|
| `git_status` | Gets repository status (branch, staged, modified, untracked files). | `path?` |
|
|
177
180
|
| `git_tag` | Manages tags (list, create annotated/lightweight, delete). | `path?`, `mode`, `tagName?`, `message?`, `commitRef?`, `annotate?` |
|
|
178
181
|
|
|
179
|
-
_Note: The `path` parameter for most tools defaults to the session's working directory if set via `git_set_working_dir
|
|
182
|
+
_Note: The `path` parameter for most tools defaults to the session's working directory if set via `git_set_working_dir`._
|
|
180
183
|
|
|
181
184
|
## Resources
|
|
182
185
|
|
|
@@ -44,7 +44,7 @@ export async function addGitFiles(input, context // Add getter to context
|
|
|
44
44
|
logger.debug(`Using session working directory: ${targetPath}`, { ...context, operation, sessionId: context.sessionId });
|
|
45
45
|
}
|
|
46
46
|
// Sanitize the resolved path
|
|
47
|
-
const sanitizedPath = sanitization.sanitizePath(targetPath);
|
|
47
|
+
const sanitizedPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
48
48
|
logger.debug('Sanitized repository path', { ...context, operation, sanitizedPath });
|
|
49
49
|
targetPath = sanitizedPath; // Use the sanitized path going forward
|
|
50
50
|
}
|
|
@@ -56,7 +56,7 @@ export async function gitBranchLogic(input, context) {
|
|
|
56
56
|
else {
|
|
57
57
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
58
58
|
}
|
|
59
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
59
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
60
60
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
61
61
|
}
|
|
62
62
|
catch (error) {
|
|
@@ -41,7 +41,7 @@ export async function checkoutGit(input, context) {
|
|
|
41
41
|
}
|
|
42
42
|
targetPath = workingDir;
|
|
43
43
|
}
|
|
44
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
44
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
45
45
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
@@ -47,7 +47,7 @@ export async function gitCherryPickLogic(input, context) {
|
|
|
47
47
|
else {
|
|
48
48
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
49
49
|
}
|
|
50
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
50
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
51
51
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
52
52
|
}
|
|
53
53
|
catch (error) {
|
|
@@ -55,7 +55,7 @@ export async function gitCleanLogic(input, context) {
|
|
|
55
55
|
else {
|
|
56
56
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
57
57
|
}
|
|
58
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
58
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
59
59
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
60
60
|
}
|
|
61
61
|
catch (error) {
|
|
@@ -33,7 +33,7 @@ export async function gitCloneLogic(input, context) {
|
|
|
33
33
|
let sanitizedRepoUrl;
|
|
34
34
|
try {
|
|
35
35
|
// Sanitize the target path (must be absolute)
|
|
36
|
-
sanitizedTargetPath = sanitization.sanitizePath(input.targetPath);
|
|
36
|
+
sanitizedTargetPath = sanitization.sanitizePath(input.targetPath, { allowAbsolute: true });
|
|
37
37
|
logger.debug('Sanitized target path', { ...context, operation, sanitizedTargetPath });
|
|
38
38
|
// Basic sanitization/validation for URL (Zod already checks format)
|
|
39
39
|
// Further sanitization might be needed depending on how it's used in the shell command
|
|
@@ -50,7 +50,7 @@ export async function commitGitChanges(input, context // Add getter to context
|
|
|
50
50
|
logger.debug(`Using session working directory: ${targetPath}`, { ...context, operation, sessionId: context.sessionId });
|
|
51
51
|
}
|
|
52
52
|
// Sanitize the resolved path
|
|
53
|
-
const sanitizedPath = sanitization.sanitizePath(targetPath);
|
|
53
|
+
const sanitizedPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
54
54
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath });
|
|
55
55
|
targetPath = sanitizedPath; // Use the sanitized path going forward
|
|
56
56
|
}
|
|
@@ -48,7 +48,7 @@ export async function diffGitChanges(input, context) {
|
|
|
48
48
|
}
|
|
49
49
|
targetPath = workingDir;
|
|
50
50
|
}
|
|
51
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
51
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
52
52
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
53
53
|
}
|
|
54
54
|
catch (error) {
|
|
@@ -41,7 +41,7 @@ export async function fetchGitRemote(input, context) {
|
|
|
41
41
|
}
|
|
42
42
|
targetPath = workingDir;
|
|
43
43
|
}
|
|
44
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
44
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
45
45
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
@@ -31,7 +31,7 @@ export async function gitInitLogic(input, context) {
|
|
|
31
31
|
let targetPath;
|
|
32
32
|
try {
|
|
33
33
|
// Sanitize the provided absolute path
|
|
34
|
-
targetPath = sanitization.sanitizePath(input.path);
|
|
34
|
+
targetPath = sanitization.sanitizePath(input.path, { allowAbsolute: true });
|
|
35
35
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
36
36
|
// Ensure the target directory exists before trying to init inside it
|
|
37
37
|
// git init creates the directory if it doesn't exist, but we might want to ensure the parent exists
|
|
@@ -55,11 +55,11 @@ export const registerGitInitTool = async (server) => {
|
|
|
55
55
|
let resolvedPath;
|
|
56
56
|
try {
|
|
57
57
|
if (path.isAbsolute(inputPath)) {
|
|
58
|
-
resolvedPath = sanitization.sanitizePath(inputPath);
|
|
58
|
+
resolvedPath = sanitization.sanitizePath(inputPath, { allowAbsolute: true });
|
|
59
59
|
logger.debug(`Using absolute path: ${resolvedPath}`, requestContext);
|
|
60
60
|
}
|
|
61
61
|
else if (sessionWorkingDirectory) {
|
|
62
|
-
resolvedPath = sanitization.sanitizePath(path.resolve(sessionWorkingDirectory, inputPath));
|
|
62
|
+
resolvedPath = sanitization.sanitizePath(path.resolve(sessionWorkingDirectory, inputPath), { allowAbsolute: true });
|
|
63
63
|
logger.debug(`Resolved relative path '${inputPath}' to absolute path: ${resolvedPath} using session CWD`, requestContext);
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
@@ -56,7 +56,7 @@ export async function logGitHistory(input, context) {
|
|
|
56
56
|
}
|
|
57
57
|
targetPath = workingDir;
|
|
58
58
|
}
|
|
59
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
59
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
60
60
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
61
61
|
}
|
|
62
62
|
catch (error) {
|
|
@@ -44,7 +44,7 @@ export async function pullGitChanges(input, context) {
|
|
|
44
44
|
logger.debug(`Using session working directory: ${targetPath}`, { ...context, operation, sessionId: context.sessionId });
|
|
45
45
|
}
|
|
46
46
|
// Sanitize the resolved path
|
|
47
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
47
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
48
48
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
@@ -45,7 +45,7 @@ export async function pushGitChanges(input, context) {
|
|
|
45
45
|
}
|
|
46
46
|
targetPath = workingDir;
|
|
47
47
|
}
|
|
48
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
48
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
49
49
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
50
50
|
}
|
|
51
51
|
catch (error) {
|
|
@@ -55,7 +55,7 @@ export async function gitRebaseLogic(input, context) {
|
|
|
55
55
|
else {
|
|
56
56
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
57
57
|
}
|
|
58
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
58
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
59
59
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
60
60
|
}
|
|
61
61
|
catch (error) {
|
|
@@ -42,7 +42,7 @@ export async function gitRemoteLogic(input, context) {
|
|
|
42
42
|
else {
|
|
43
43
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
44
44
|
}
|
|
45
|
-
targetPath = sanitization.sanitizePath(targetPath); // Sanitize the final resolved path
|
|
45
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true }); // Sanitize the final resolved path
|
|
46
46
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
47
47
|
}
|
|
48
48
|
catch (error) {
|
|
@@ -44,7 +44,7 @@ export async function resetGitState(input, context) {
|
|
|
44
44
|
}
|
|
45
45
|
targetPath = workingDir;
|
|
46
46
|
}
|
|
47
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
47
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
48
48
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
@@ -26,9 +26,9 @@ export async function gitSetWorkingDirLogic(input, context // Assuming context p
|
|
|
26
26
|
logger.info('Executing git_set_working_dir logic', { ...context, operation, inputPath: input.path });
|
|
27
27
|
let sanitizedPath;
|
|
28
28
|
try {
|
|
29
|
-
// Sanitize the path.
|
|
29
|
+
// Sanitize the path. Must explicitly allow absolute paths for this tool.
|
|
30
30
|
// It normalizes and checks for traversal issues.
|
|
31
|
-
sanitizedPath = sanitization.sanitizePath(input.path);
|
|
31
|
+
sanitizedPath = sanitization.sanitizePath(input.path, { allowAbsolute: true });
|
|
32
32
|
logger.debug(`Sanitized path: ${sanitizedPath}`, { ...context, operation });
|
|
33
33
|
}
|
|
34
34
|
catch (error) {
|
|
@@ -41,7 +41,7 @@ export async function gitShowLogic(input, context) {
|
|
|
41
41
|
else {
|
|
42
42
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
43
43
|
}
|
|
44
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
44
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
45
45
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
@@ -47,7 +47,7 @@ export async function gitStashLogic(input, context) {
|
|
|
47
47
|
else {
|
|
48
48
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
49
49
|
}
|
|
50
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
50
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
51
51
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
52
52
|
}
|
|
53
53
|
catch (error) {
|
|
@@ -160,7 +160,7 @@ export async function getGitStatus(input, context // Add getter to context
|
|
|
160
160
|
logger.debug(`Using session working directory: ${targetPath}`, { ...context, operation, sessionId: context.sessionId });
|
|
161
161
|
}
|
|
162
162
|
// Sanitize the resolved path
|
|
163
|
-
const sanitizedPath = sanitization.sanitizePath(targetPath);
|
|
163
|
+
const sanitizedPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
164
164
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath });
|
|
165
165
|
targetPath = sanitizedPath; // Use the sanitized path going forward
|
|
166
166
|
}
|
|
@@ -55,7 +55,7 @@ export async function gitTagLogic(input, context) {
|
|
|
55
55
|
else {
|
|
56
56
|
logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
|
|
57
57
|
}
|
|
58
|
-
targetPath = sanitization.sanitizePath(targetPath);
|
|
58
|
+
targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
|
|
59
59
|
logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
|
|
60
60
|
}
|
|
61
61
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/git-mcp-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"description": "An MCP (Model Context Protocol) server providing tools to interact with Git repositories. Enables LLMs and AI agents to perform Git operations like clone, commit, push, pull, branch, diff, log, status, and more via the MCP standard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
40
40
|
"@types/jsonwebtoken": "^9.0.9",
|
|
41
|
-
"@types/node": "^22.15.
|
|
41
|
+
"@types/node": "^22.15.15",
|
|
42
42
|
"@types/sanitize-html": "^2.16.0",
|
|
43
43
|
"@types/validator": "^13.15.0",
|
|
44
44
|
"chrono-node": "^2.8.0",
|