@cyanheads/git-mcp-server 2.8.1 → 2.8.3
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 +1 -1
- package/dist/index.js +170 -103
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [](https://modelcontextprotocol.io/) [](./LICENSE) [](https://github.com/cyanheads/git-mcp-server/issues) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
package/dist/index.js
CHANGED
|
@@ -15335,7 +15335,7 @@ var package_default;
|
|
|
15335
15335
|
var init_package = __esm(() => {
|
|
15336
15336
|
package_default = {
|
|
15337
15337
|
name: "@cyanheads/git-mcp-server",
|
|
15338
|
-
version: "2.8.
|
|
15338
|
+
version: "2.8.3",
|
|
15339
15339
|
mcpName: "io.github.cyanheads/git-mcp-server",
|
|
15340
15340
|
description: "A secure and scalable Git MCP server enabling AI agents to perform comprehensive Git version control operations via STDIO and Streamable HTTP.",
|
|
15341
15341
|
main: "dist/index.js",
|
|
@@ -15855,9 +15855,9 @@ var init_config = __esm(() => {
|
|
|
15855
15855
|
git: exports_external.object({
|
|
15856
15856
|
provider: exports_external.preprocess(emptyStringAsUndefined, exports_external.enum(["auto", "cli", "isomorphic"]).default("auto")),
|
|
15857
15857
|
signCommits: exports_external.coerce.boolean().default(false),
|
|
15858
|
-
authorName: exports_external.string().optional(),
|
|
15858
|
+
authorName: exports_external.string().regex(/^[^\n\r\0]*$/, "Git author name must not contain newlines or null bytes").optional(),
|
|
15859
15859
|
authorEmail: exports_external.string().email().optional(),
|
|
15860
|
-
committerName: exports_external.string().optional(),
|
|
15860
|
+
committerName: exports_external.string().regex(/^[^\n\r\0]*$/, "Git committer name must not contain newlines or null bytes").optional(),
|
|
15861
15861
|
committerEmail: exports_external.string().email().optional(),
|
|
15862
15862
|
wrapupInstructionsPath: exports_external.preprocess(expandTildePath, exports_external.string().optional()),
|
|
15863
15863
|
baseDir: exports_external.preprocess((val) => expandTildePath(emptyStringAsUndefined(val)), exports_external.string().refine((p) => !p || isAbsolutePath(p), {
|
|
@@ -194057,6 +194057,25 @@ async function resolveWorkingDirectory(pathInput, appContext, storage) {
|
|
|
194057
194057
|
});
|
|
194058
194058
|
return sanitizedPath;
|
|
194059
194059
|
}
|
|
194060
|
+
var DEFAULT_PROTECTION = {
|
|
194061
|
+
protectedBranches: ["main", "master", "production", "prod", "develop", "dev"],
|
|
194062
|
+
enforce: true
|
|
194063
|
+
};
|
|
194064
|
+
function isProtectedBranch(branchName, config3 = DEFAULT_PROTECTION) {
|
|
194065
|
+
return config3.protectedBranches.includes(branchName.toLowerCase());
|
|
194066
|
+
}
|
|
194067
|
+
function validateProtectedBranchOperation(branchName, operation, confirmed, config3 = DEFAULT_PROTECTION) {
|
|
194068
|
+
if (!config3.enforce) {
|
|
194069
|
+
return;
|
|
194070
|
+
}
|
|
194071
|
+
if (isProtectedBranch(branchName, config3) && !confirmed) {
|
|
194072
|
+
throw new McpError(-32007 /* ValidationError */, `Cannot perform '${operation}' on protected branch '${branchName}' without explicit confirmation.`, {
|
|
194073
|
+
branch: branchName,
|
|
194074
|
+
operation,
|
|
194075
|
+
hint: "Set the confirmation parameter to true to proceed."
|
|
194076
|
+
});
|
|
194077
|
+
}
|
|
194078
|
+
}
|
|
194060
194079
|
|
|
194061
194080
|
// src/mcp-server/tools/utils/toolHandlerFactory.ts
|
|
194062
194081
|
function validateSdkContext(ctx) {
|
|
@@ -196243,7 +196262,8 @@ var InputSchema23 = exports_external.object({
|
|
|
196243
196262
|
tags: exports_external.boolean().default(false).describe("Push all tags to the remote."),
|
|
196244
196263
|
dryRun: DryRunSchema,
|
|
196245
196264
|
delete: exports_external.boolean().default(false).describe("Delete the specified remote branch."),
|
|
196246
|
-
remoteBranch: BranchNameSchema.optional().describe("Remote branch name to push to (if different from local branch name).")
|
|
196265
|
+
remoteBranch: BranchNameSchema.optional().describe("Remote branch name to push to (if different from local branch name)."),
|
|
196266
|
+
confirmed: exports_external.boolean().default(false).describe("Explicit confirmation required for force push or branch deletion on protected branches (main, master, production, etc.).")
|
|
196247
196267
|
});
|
|
196248
196268
|
var OutputSchema24 = exports_external.object({
|
|
196249
196269
|
success: exports_external.boolean().describe("Indicates if the operation was successful."),
|
|
@@ -196254,6 +196274,21 @@ var OutputSchema24 = exports_external.object({
|
|
|
196254
196274
|
rejectedRefs: exports_external.array(exports_external.string()).describe("References that were rejected by the remote.")
|
|
196255
196275
|
});
|
|
196256
196276
|
async function gitPushLogic(input, { provider, targetPath, appContext }) {
|
|
196277
|
+
if (input.force || input.delete) {
|
|
196278
|
+
let branchToCheck = input.branch;
|
|
196279
|
+
if (!branchToCheck) {
|
|
196280
|
+
const status = await provider.status({ includeUntracked: false }, {
|
|
196281
|
+
workingDirectory: targetPath,
|
|
196282
|
+
requestContext: appContext,
|
|
196283
|
+
tenantId: appContext.tenantId || "default-tenant"
|
|
196284
|
+
});
|
|
196285
|
+
branchToCheck = status?.currentBranch ?? undefined;
|
|
196286
|
+
}
|
|
196287
|
+
if (branchToCheck) {
|
|
196288
|
+
const operation = input.force ? "force push" : "branch deletion";
|
|
196289
|
+
validateProtectedBranchOperation(branchToCheck, operation, input.confirmed);
|
|
196290
|
+
}
|
|
196291
|
+
}
|
|
196257
196292
|
const pushOptions = {};
|
|
196258
196293
|
if (input.remote !== undefined) {
|
|
196259
196294
|
pushOptions.remote = input.remote;
|
|
@@ -196414,7 +196449,8 @@ var InputSchema25 = exports_external.object({
|
|
|
196414
196449
|
path: PathSchema,
|
|
196415
196450
|
mode: exports_external.enum(["soft", "mixed", "hard", "merge", "keep"]).default("mixed").describe("Reset mode: soft (keep changes staged), mixed (unstage changes), hard (discard all changes), merge (reset and merge), keep (reset but keep local changes)."),
|
|
196416
196451
|
target: CommitRefSchema.optional().describe("Target commit to reset to (default: HEAD)."),
|
|
196417
|
-
paths: exports_external.array(exports_external.string()).optional().describe("Specific file paths to reset (leaves HEAD unchanged).")
|
|
196452
|
+
paths: exports_external.array(exports_external.string()).optional().describe("Specific file paths to reset (leaves HEAD unchanged)."),
|
|
196453
|
+
confirmed: exports_external.boolean().default(false).describe("Explicit confirmation required for hard reset on protected branches (main, master, production, etc.).")
|
|
196418
196454
|
});
|
|
196419
196455
|
var OutputSchema26 = exports_external.object({
|
|
196420
196456
|
success: exports_external.boolean().describe("Indicates if the operation was successful."),
|
|
@@ -196423,6 +196459,16 @@ var OutputSchema26 = exports_external.object({
|
|
|
196423
196459
|
filesReset: exports_external.array(exports_external.string()).describe("Files that were affected by the reset.")
|
|
196424
196460
|
});
|
|
196425
196461
|
async function gitResetLogic(input, { provider, targetPath, appContext }) {
|
|
196462
|
+
if (input.mode === "hard") {
|
|
196463
|
+
const status = await provider.status({ includeUntracked: false }, {
|
|
196464
|
+
workingDirectory: targetPath,
|
|
196465
|
+
requestContext: appContext,
|
|
196466
|
+
tenantId: appContext.tenantId || "default-tenant"
|
|
196467
|
+
});
|
|
196468
|
+
if (status?.currentBranch) {
|
|
196469
|
+
validateProtectedBranchOperation(status.currentBranch, "reset --hard", input.confirmed);
|
|
196470
|
+
}
|
|
196471
|
+
}
|
|
196426
196472
|
const resetOptions = {
|
|
196427
196473
|
mode: input.mode
|
|
196428
196474
|
};
|
|
@@ -201929,6 +201975,7 @@ class SessionManager {
|
|
|
201929
201975
|
cleanupIntervalId = null;
|
|
201930
201976
|
staleTimeoutMs;
|
|
201931
201977
|
cleanupIntervalMs;
|
|
201978
|
+
onSessionExpired = null;
|
|
201932
201979
|
constructor(staleTimeoutMs = 30 * 60 * 1000, cleanupIntervalMs = 5 * 60 * 1000) {
|
|
201933
201980
|
this.staleTimeoutMs = staleTimeoutMs;
|
|
201934
201981
|
this.cleanupIntervalMs = cleanupIntervalMs;
|
|
@@ -201984,6 +202031,7 @@ class SessionManager {
|
|
|
201984
202031
|
staleTimeoutMs: this.staleTimeoutMs
|
|
201985
202032
|
});
|
|
201986
202033
|
this.sessions.delete(sessionId);
|
|
202034
|
+
this.onSessionExpired?.(sessionId);
|
|
201987
202035
|
return false;
|
|
201988
202036
|
}
|
|
201989
202037
|
return true;
|
|
@@ -202051,6 +202099,7 @@ class SessionManager {
|
|
|
202051
202099
|
const age = now2 - metadata.lastActivityAt;
|
|
202052
202100
|
if (age > this.staleTimeoutMs) {
|
|
202053
202101
|
this.sessions.delete(sessionId);
|
|
202102
|
+
this.onSessionExpired?.(sessionId);
|
|
202054
202103
|
removedCount++;
|
|
202055
202104
|
}
|
|
202056
202105
|
}
|
|
@@ -202079,26 +202128,36 @@ class SessionManager {
|
|
|
202079
202128
|
|
|
202080
202129
|
// src/mcp-server/transports/http/httpTransport.ts
|
|
202081
202130
|
init_utils();
|
|
202082
|
-
|
|
202083
|
-
class McpSessionTransport extends StreamableHTTPTransport {
|
|
202084
|
-
sessionId;
|
|
202085
|
-
constructor(sessionId) {
|
|
202086
|
-
super();
|
|
202087
|
-
this.sessionId = sessionId;
|
|
202088
|
-
}
|
|
202089
|
-
}
|
|
202090
|
-
function createHttpApp(mcpServer, parentContext) {
|
|
202131
|
+
function createHttpApp(createMcpServer, parentContext) {
|
|
202091
202132
|
const app = new Hono2;
|
|
202092
202133
|
const transportContext = {
|
|
202093
202134
|
...parentContext,
|
|
202094
202135
|
component: "HttpTransportSetup"
|
|
202095
202136
|
};
|
|
202137
|
+
const transports = new Map;
|
|
202096
202138
|
const sessionManager = SessionManager.getInstance(config2.mcpStatefulSessionStaleTimeoutMs);
|
|
202139
|
+
sessionManager.onSessionExpired = (sessionId) => {
|
|
202140
|
+
const transport = transports.get(sessionId);
|
|
202141
|
+
if (transport) {
|
|
202142
|
+
transport.close().catch((err) => {
|
|
202143
|
+
logger.warning("Failed to close transport for expired session", {
|
|
202144
|
+
...transportContext,
|
|
202145
|
+
sessionId,
|
|
202146
|
+
error: err instanceof Error ? err.message : String(err)
|
|
202147
|
+
});
|
|
202148
|
+
});
|
|
202149
|
+
transports.delete(sessionId);
|
|
202150
|
+
}
|
|
202151
|
+
};
|
|
202097
202152
|
logger.info("Session manager initialized", {
|
|
202098
202153
|
...transportContext,
|
|
202099
202154
|
staleTimeoutMs: config2.mcpStatefulSessionStaleTimeoutMs
|
|
202100
202155
|
});
|
|
202101
|
-
const
|
|
202156
|
+
const explicitOrigins = config2.mcpAllowedOrigins;
|
|
202157
|
+
const allowedOrigin = explicitOrigins && explicitOrigins.length > 0 ? explicitOrigins : "*";
|
|
202158
|
+
if (allowedOrigin === "*" && config2.environment === "production") {
|
|
202159
|
+
logger.warning("MCP_ALLOWED_ORIGINS is not configured. CORS will allow all origins. " + "Set MCP_ALLOWED_ORIGINS to restrict cross-origin access in production.", transportContext);
|
|
202160
|
+
}
|
|
202102
202161
|
app.use("*", cors({
|
|
202103
202162
|
origin: allowedOrigin,
|
|
202104
202163
|
allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
|
|
@@ -202125,19 +202184,6 @@ function createHttpApp(mcpServer, parentContext) {
|
|
|
202125
202184
|
...config2.oauthJwksUri && { jwks_uri: config2.oauthJwksUri }
|
|
202126
202185
|
});
|
|
202127
202186
|
});
|
|
202128
|
-
app.get(config2.mcpHttpEndpointPath, (c) => {
|
|
202129
|
-
return c.json({
|
|
202130
|
-
status: "ok",
|
|
202131
|
-
server: {
|
|
202132
|
-
name: config2.mcpServerName,
|
|
202133
|
-
version: config2.mcpServerVersion,
|
|
202134
|
-
description: config2.mcpServerDescription,
|
|
202135
|
-
environment: config2.environment,
|
|
202136
|
-
transport: config2.mcpTransportType,
|
|
202137
|
-
sessionMode: config2.mcpSessionMode
|
|
202138
|
-
}
|
|
202139
|
-
});
|
|
202140
|
-
});
|
|
202141
202187
|
const authStrategy = createAuthStrategy();
|
|
202142
202188
|
if (authStrategy) {
|
|
202143
202189
|
const authMiddleware = createAuthMiddleware(authStrategy);
|
|
@@ -202146,113 +202192,134 @@ function createHttpApp(mcpServer, parentContext) {
|
|
|
202146
202192
|
} else {
|
|
202147
202193
|
logger.info("Authentication is disabled; MCP endpoint is unprotected.", transportContext);
|
|
202148
202194
|
}
|
|
202149
|
-
|
|
202150
|
-
|
|
202151
|
-
|
|
202152
|
-
|
|
202195
|
+
const getSessionTransport = (sessionId) => {
|
|
202196
|
+
if (!sessionManager.isSessionValid(sessionId)) {
|
|
202197
|
+
const stale = transports.get(sessionId);
|
|
202198
|
+
if (stale) {
|
|
202199
|
+
stale.close().catch(() => {});
|
|
202200
|
+
transports.delete(sessionId);
|
|
202201
|
+
}
|
|
202202
|
+
return Response.json({
|
|
202153
202203
|
jsonrpc: "2.0",
|
|
202154
202204
|
error: {
|
|
202155
|
-
code: -
|
|
202156
|
-
message: "
|
|
202205
|
+
code: -32001,
|
|
202206
|
+
message: "Session expired or invalid. Please reinitialize."
|
|
202157
202207
|
},
|
|
202158
202208
|
id: null
|
|
202159
|
-
},
|
|
202209
|
+
}, { status: 404 });
|
|
202160
202210
|
}
|
|
202161
|
-
const
|
|
202162
|
-
if (!
|
|
202163
|
-
return
|
|
202211
|
+
const transport = transports.get(sessionId);
|
|
202212
|
+
if (!transport) {
|
|
202213
|
+
return Response.json({
|
|
202164
202214
|
jsonrpc: "2.0",
|
|
202165
202215
|
error: {
|
|
202166
202216
|
code: -32001,
|
|
202167
|
-
message: "Session not found
|
|
202217
|
+
message: "Session not found. Please reinitialize."
|
|
202168
202218
|
},
|
|
202169
202219
|
id: null
|
|
202170
|
-
}, 404);
|
|
202220
|
+
}, { status: 404 });
|
|
202171
202221
|
}
|
|
202172
|
-
|
|
202173
|
-
|
|
202174
|
-
|
|
202175
|
-
|
|
202176
|
-
|
|
202177
|
-
|
|
202178
|
-
|
|
202179
|
-
|
|
202180
|
-
|
|
202181
|
-
|
|
202182
|
-
|
|
202183
|
-
|
|
202184
|
-
|
|
202185
|
-
|
|
202186
|
-
|
|
202187
|
-
if (!supportedVersions.includes(protocolVersion)) {
|
|
202188
|
-
logger.warning("Unsupported MCP protocol version requested.", {
|
|
202189
|
-
...transportContext,
|
|
202190
|
-
protocolVersion,
|
|
202191
|
-
supportedVersions
|
|
202222
|
+
sessionManager.touchSession(sessionId);
|
|
202223
|
+
return transport;
|
|
202224
|
+
};
|
|
202225
|
+
app.get(config2.mcpHttpEndpointPath, async (c) => {
|
|
202226
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
202227
|
+
if (!sessionId) {
|
|
202228
|
+
return c.json({
|
|
202229
|
+
status: "ok",
|
|
202230
|
+
server: {
|
|
202231
|
+
name: config2.mcpServerName,
|
|
202232
|
+
version: config2.mcpServerVersion,
|
|
202233
|
+
description: config2.mcpServerDescription,
|
|
202234
|
+
transport: config2.mcpTransportType,
|
|
202235
|
+
sessionMode: config2.mcpSessionMode
|
|
202236
|
+
}
|
|
202192
202237
|
});
|
|
202238
|
+
}
|
|
202239
|
+
const transportOrError = getSessionTransport(sessionId);
|
|
202240
|
+
if (transportOrError instanceof Response)
|
|
202241
|
+
return transportOrError;
|
|
202242
|
+
const response = await transportOrError.handleRequest(c);
|
|
202243
|
+
return response ?? c.body(null, 204);
|
|
202244
|
+
});
|
|
202245
|
+
app.delete(config2.mcpHttpEndpointPath, async (c) => {
|
|
202246
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
202247
|
+
if (!sessionId) {
|
|
202193
202248
|
return c.json({
|
|
202194
202249
|
jsonrpc: "2.0",
|
|
202195
202250
|
error: {
|
|
202196
202251
|
code: -32600,
|
|
202197
|
-
message:
|
|
202198
|
-
data: {
|
|
202199
|
-
requested: protocolVersion,
|
|
202200
|
-
supported: supportedVersions
|
|
202201
|
-
}
|
|
202252
|
+
message: "Mcp-Session-Id header required for DELETE"
|
|
202202
202253
|
},
|
|
202203
202254
|
id: null
|
|
202204
202255
|
}, 400);
|
|
202205
202256
|
}
|
|
202206
|
-
const
|
|
202207
|
-
if (
|
|
202208
|
-
logger.warning("Invalid or expired session ID", {
|
|
202209
|
-
...transportContext,
|
|
202210
|
-
sessionId
|
|
202211
|
-
});
|
|
202257
|
+
const transport = transports.get(sessionId);
|
|
202258
|
+
if (!transport) {
|
|
202212
202259
|
return c.json({
|
|
202213
202260
|
jsonrpc: "2.0",
|
|
202214
202261
|
error: {
|
|
202215
202262
|
code: -32001,
|
|
202216
|
-
message: "Session
|
|
202263
|
+
message: "Session not found or already expired"
|
|
202217
202264
|
},
|
|
202218
202265
|
id: null
|
|
202219
202266
|
}, 404);
|
|
202220
202267
|
}
|
|
202221
|
-
|
|
202222
|
-
|
|
202223
|
-
|
|
202224
|
-
|
|
202268
|
+
const response = await transport.handleRequest(c);
|
|
202269
|
+
transports.delete(sessionId);
|
|
202270
|
+
sessionManager.terminateSession(sessionId);
|
|
202271
|
+
logger.info("Session terminated via DELETE", {
|
|
202272
|
+
...transportContext,
|
|
202273
|
+
sessionId
|
|
202274
|
+
});
|
|
202275
|
+
return response ?? c.body(null, 204);
|
|
202276
|
+
});
|
|
202277
|
+
app.post(config2.mcpHttpEndpointPath, async (c) => {
|
|
202278
|
+
logger.debug("Handling MCP POST request.", {
|
|
202279
|
+
...transportContext,
|
|
202280
|
+
path: c.req.path
|
|
202281
|
+
});
|
|
202282
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
202283
|
+
const handleRequest = async () => {
|
|
202284
|
+
if (sessionId) {
|
|
202285
|
+
const transportOrError = getSessionTransport(sessionId);
|
|
202286
|
+
if (transportOrError instanceof Response)
|
|
202287
|
+
return transportOrError;
|
|
202288
|
+
const response2 = await transportOrError.handleRequest(c);
|
|
202289
|
+
return response2 ?? c.body(null, 204);
|
|
202290
|
+
}
|
|
202291
|
+
const server = await createMcpServer();
|
|
202292
|
+
const transport = new StreamableHTTPTransport({
|
|
202293
|
+
sessionIdGenerator: () => randomUUID(),
|
|
202294
|
+
onsessioninitialized: (sid) => {
|
|
202295
|
+
transports.set(sid, transport);
|
|
202296
|
+
const store = authContext.getStore();
|
|
202297
|
+
sessionManager.createSession(sid, store?.authInfo.clientId, store?.authInfo.tenantId);
|
|
202298
|
+
logger.debug("New MCP session initialized", {
|
|
202299
|
+
...transportContext,
|
|
202300
|
+
sessionId: sid
|
|
202301
|
+
});
|
|
202302
|
+
},
|
|
202303
|
+
onsessionclosed: (sid) => {
|
|
202304
|
+
transports.delete(sid);
|
|
202305
|
+
sessionManager.terminateSession(sid);
|
|
202306
|
+
logger.debug("MCP session closed via transport", {
|
|
202307
|
+
...transportContext,
|
|
202308
|
+
sessionId: sid
|
|
202309
|
+
});
|
|
202310
|
+
}
|
|
202225
202311
|
});
|
|
202226
|
-
|
|
202227
|
-
sessionManager.touchSession(sessionId);
|
|
202228
|
-
}
|
|
202229
|
-
const transport = new McpSessionTransport(sessionId);
|
|
202230
|
-
const handleRpc = async () => {
|
|
202231
|
-
await mcpServer.connect(transport);
|
|
202312
|
+
await server.connect(transport);
|
|
202232
202313
|
const response = await transport.handleRequest(c);
|
|
202233
|
-
|
|
202234
|
-
const store = authContext.getStore();
|
|
202235
|
-
sessionManager.createSession(sessionId, store?.authInfo.clientId, store?.authInfo.tenantId);
|
|
202236
|
-
}
|
|
202237
|
-
if (response) {
|
|
202238
|
-
return response;
|
|
202239
|
-
}
|
|
202240
|
-
return c.body(null, 204);
|
|
202314
|
+
return response ?? c.body(null, 204);
|
|
202241
202315
|
};
|
|
202242
202316
|
try {
|
|
202243
202317
|
const store = authContext.getStore();
|
|
202244
202318
|
if (store) {
|
|
202245
|
-
return await authContext.run(store,
|
|
202319
|
+
return await authContext.run(store, handleRequest);
|
|
202246
202320
|
}
|
|
202247
|
-
return await
|
|
202321
|
+
return await handleRequest();
|
|
202248
202322
|
} catch (err) {
|
|
202249
|
-
await transport.close?.().catch((closeErr) => {
|
|
202250
|
-
logger.warning("Failed to close transport after error", {
|
|
202251
|
-
...transportContext,
|
|
202252
|
-
sessionId,
|
|
202253
|
-
error: closeErr instanceof Error ? closeErr.message : String(closeErr)
|
|
202254
|
-
});
|
|
202255
|
-
});
|
|
202256
202323
|
throw err instanceof Error ? err : new Error(String(err));
|
|
202257
202324
|
}
|
|
202258
202325
|
});
|
|
@@ -202311,13 +202378,13 @@ function startHttpServerWithRetry(app, initialPort, host, maxRetries, parentCont
|
|
|
202311
202378
|
tryBind(initialPort, 1);
|
|
202312
202379
|
});
|
|
202313
202380
|
}
|
|
202314
|
-
async function startHttpTransport(
|
|
202381
|
+
async function startHttpTransport(createMcpServer, parentContext) {
|
|
202315
202382
|
const transportContext = {
|
|
202316
202383
|
...parentContext,
|
|
202317
202384
|
component: "HttpTransportStart"
|
|
202318
202385
|
};
|
|
202319
202386
|
logger.info("Starting HTTP transport.", transportContext);
|
|
202320
|
-
const app = createHttpApp(
|
|
202387
|
+
const app = createHttpApp(createMcpServer, transportContext);
|
|
202321
202388
|
const server = await startHttpServerWithRetry(app, config2.mcpHttpPort, config2.mcpHttpHost, config2.mcpHttpMaxPortRetries, transportContext);
|
|
202322
202389
|
logger.info("HTTP transport started successfully.", transportContext);
|
|
202323
202390
|
return server;
|
|
@@ -202494,10 +202561,10 @@ class TransportManager {
|
|
|
202494
202561
|
transport: this.config.mcpTransportType
|
|
202495
202562
|
});
|
|
202496
202563
|
this.logger.info(`Starting transport: ${this.config.mcpTransportType}`, context);
|
|
202497
|
-
const mcpServer = await this.createMcpServer();
|
|
202498
202564
|
if (this.config.mcpTransportType === "http") {
|
|
202499
|
-
this.serverInstance = await startHttpTransport(
|
|
202565
|
+
this.serverInstance = await startHttpTransport(this.createMcpServer, context);
|
|
202500
202566
|
} else if (this.config.mcpTransportType === "stdio") {
|
|
202567
|
+
const mcpServer = await this.createMcpServer();
|
|
202501
202568
|
this.serverInstance = await startStdioTransport(mcpServer, context);
|
|
202502
202569
|
} else {
|
|
202503
202570
|
const transportType = String(this.config.mcpTransportType);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/git-mcp-server",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.3",
|
|
4
4
|
"mcpName": "io.github.cyanheads/git-mcp-server",
|
|
5
5
|
"description": "A secure and scalable Git MCP server enabling AI agents to perform comprehensive Git version control operations via STDIO and Streamable HTTP.",
|
|
6
6
|
"main": "dist/index.js",
|