@cyanheads/git-mcp-server 2.8.2 → 2.8.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +155 -134
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-2.8.2-blue.svg?style=flat-square)](./CHANGELOG.md) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2025--11--25-8A2BE2.svg?style=flat-square)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.26.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Status](https://img.shields.io/badge/Status-Stable-brightgreen.svg?style=flat-square)](https://github.com/cyanheads/git-mcp-server/issues) [![TypeScript](https://img.shields.io/badge/TypeScript-^5.9.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.2.21-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-2.8.4-blue.svg?style=flat-square)](./CHANGELOG.md) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2025--11--25-8A2BE2.svg?style=flat-square)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.26.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Status](https://img.shields.io/badge/Status-Stable-brightgreen.svg?style=flat-square)](https://github.com/cyanheads/git-mcp-server/issues) [![TypeScript](https://img.shields.io/badge/TypeScript-^5.9.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.2.21-blueviolet.svg?style=flat-square)](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.2",
15338
+ version: "2.8.4",
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",
@@ -169396,7 +169396,7 @@ var require_compile = __commonJS((exports) => {
169396
169396
  const schOrFunc = root.refs[ref];
169397
169397
  if (schOrFunc)
169398
169398
  return schOrFunc;
169399
- let _sch = resolve.call(this, root, ref);
169399
+ let _sch = resolve2.call(this, root, ref);
169400
169400
  if (_sch === undefined) {
169401
169401
  const schema2 = (_a4 = root.localRefs) === null || _a4 === undefined ? undefined : _a4[ref];
169402
169402
  const { schemaId } = this.opts;
@@ -169423,7 +169423,7 @@ var require_compile = __commonJS((exports) => {
169423
169423
  function sameSchemaEnv(s1, s2) {
169424
169424
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
169425
169425
  }
169426
- function resolve(root, ref) {
169426
+ function resolve2(root, ref) {
169427
169427
  let sch;
169428
169428
  while (typeof (sch = this.refs[ref]) == "string")
169429
169429
  ref = sch;
@@ -169953,7 +169953,7 @@ var require_fast_uri = __commonJS((exports, module) => {
169953
169953
  }
169954
169954
  return uri;
169955
169955
  }
169956
- function resolve(baseURI, relativeURI, options) {
169956
+ function resolve2(baseURI, relativeURI, options) {
169957
169957
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
169958
169958
  const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
169959
169959
  schemelessOptions.skipEscape = true;
@@ -170181,7 +170181,7 @@ var require_fast_uri = __commonJS((exports, module) => {
170181
170181
  var fastUri = {
170182
170182
  SCHEMES,
170183
170183
  normalize,
170184
- resolve,
170184
+ resolve: resolve2,
170185
170185
  resolveComponent,
170186
170186
  equal,
170187
170187
  serialize,
@@ -176024,9 +176024,12 @@ async function executeInit(options, context, execGit) {
176024
176024
  }
176025
176025
  }
176026
176026
  // src/services/git/providers/cli/operations/core/clone.ts
176027
+ import { dirname, resolve } from "node:path";
176027
176028
  async function executeClone(options, context, execGit) {
176028
176029
  try {
176029
- const args = [options.remoteUrl, options.localPath];
176030
+ const resolvedLocalPath = resolve(context.workingDirectory, options.localPath);
176031
+ const cloneCwd = dirname(resolvedLocalPath);
176032
+ const args = [options.remoteUrl, resolvedLocalPath];
176030
176033
  if (options.branch) {
176031
176034
  args.push("--branch", options.branch);
176032
176035
  }
@@ -176043,7 +176046,7 @@ async function executeClone(options, context, execGit) {
176043
176046
  args.push("--recurse-submodules");
176044
176047
  }
176045
176048
  const cmd = buildGitCommand({ command: "clone", args });
176046
- await execGit(cmd, context.workingDirectory, context.requestContext);
176049
+ await execGit(cmd, cloneCwd, context.requestContext);
176047
176050
  const result = {
176048
176051
  success: true,
176049
176052
  localPath: options.localPath,
@@ -177964,7 +177967,7 @@ var safeJSON = (text) => {
177964
177967
  };
177965
177968
 
177966
177969
  // node_modules/openai/internal/utils/sleep.mjs
177967
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
177970
+ var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
177968
177971
 
177969
177972
  // node_modules/openai/version.mjs
177970
177973
  var VERSION = "6.21.0";
@@ -178968,8 +178971,8 @@ var _APIPromise_client;
178968
178971
 
178969
178972
  class APIPromise extends Promise {
178970
178973
  constructor(client, responsePromise, parseResponse = defaultParseResponse) {
178971
- super((resolve) => {
178972
- resolve(null);
178974
+ super((resolve2) => {
178975
+ resolve2(null);
178973
178976
  });
178974
178977
  this.responsePromise = responsePromise;
178975
178978
  this.parseResponse = parseResponse;
@@ -179487,12 +179490,12 @@ class EventStream {
179487
179490
  _EventStream_errored.set(this, false);
179488
179491
  _EventStream_aborted.set(this, false);
179489
179492
  _EventStream_catchingPromiseCreated.set(this, false);
179490
- __classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((resolve, reject) => {
179491
- __classPrivateFieldSet(this, _EventStream_resolveConnectedPromise, resolve, "f");
179493
+ __classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((resolve2, reject) => {
179494
+ __classPrivateFieldSet(this, _EventStream_resolveConnectedPromise, resolve2, "f");
179492
179495
  __classPrivateFieldSet(this, _EventStream_rejectConnectedPromise, reject, "f");
179493
179496
  }), "f");
179494
- __classPrivateFieldSet(this, _EventStream_endPromise, new Promise((resolve, reject) => {
179495
- __classPrivateFieldSet(this, _EventStream_resolveEndPromise, resolve, "f");
179497
+ __classPrivateFieldSet(this, _EventStream_endPromise, new Promise((resolve2, reject) => {
179498
+ __classPrivateFieldSet(this, _EventStream_resolveEndPromise, resolve2, "f");
179496
179499
  __classPrivateFieldSet(this, _EventStream_rejectEndPromise, reject, "f");
179497
179500
  }), "f");
179498
179501
  __classPrivateFieldGet(this, _EventStream_connectedPromise, "f").catch(() => {});
@@ -179544,11 +179547,11 @@ class EventStream {
179544
179547
  return this;
179545
179548
  }
179546
179549
  emitted(event) {
179547
- return new Promise((resolve, reject) => {
179550
+ return new Promise((resolve2, reject) => {
179548
179551
  __classPrivateFieldSet(this, _EventStream_catchingPromiseCreated, true, "f");
179549
179552
  if (event !== "error")
179550
179553
  this.once("error", reject);
179551
- this.once(event, resolve);
179554
+ this.once(event, resolve2);
179552
179555
  });
179553
179556
  }
179554
179557
  async done() {
@@ -180466,7 +180469,7 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
180466
180469
  if (done) {
180467
180470
  return { value: undefined, done: true };
180468
180471
  }
180469
- return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: undefined, done: true });
180472
+ return new Promise((resolve2, reject) => readQueue.push({ resolve: resolve2, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: undefined, done: true });
180470
180473
  }
180471
180474
  const chunk = pushQueue.shift();
180472
180475
  return { value: chunk, done: false };
@@ -181054,7 +181057,7 @@ class AssistantStream extends EventStream {
181054
181057
  if (done) {
181055
181058
  return { value: undefined, done: true };
181056
181059
  }
181057
- return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: undefined, done: true });
181060
+ return new Promise((resolve2, reject) => readQueue.push({ resolve: resolve2, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: undefined, done: true });
181058
181061
  }
181059
181062
  const chunk = pushQueue.shift();
181060
181063
  return { value: chunk, done: false };
@@ -182485,7 +182488,7 @@ class ResponseStream extends EventStream {
182485
182488
  if (done) {
182486
182489
  return { value: undefined, done: true };
182487
182490
  }
182488
- return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: undefined, done: true });
182491
+ return new Promise((resolve2, reject) => readQueue.push({ resolve: resolve2, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: undefined, done: true });
182489
182492
  }
182490
182493
  const event = pushQueue.shift();
182491
182494
  return { value: event, done: false };
@@ -191752,7 +191755,7 @@ class Protocol {
191752
191755
  return;
191753
191756
  }
191754
191757
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
191755
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
191758
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
191756
191759
  options?.signal?.throwIfAborted();
191757
191760
  }
191758
191761
  } catch (error49) {
@@ -191764,7 +191767,7 @@ class Protocol {
191764
191767
  }
191765
191768
  request(request, resultSchema, options) {
191766
191769
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
191767
- return new Promise((resolve, reject) => {
191770
+ return new Promise((resolve2, reject) => {
191768
191771
  const earlyReject = (error49) => {
191769
191772
  reject(error49);
191770
191773
  };
@@ -191842,7 +191845,7 @@ class Protocol {
191842
191845
  if (!parseResult.success) {
191843
191846
  reject(parseResult.error);
191844
191847
  } else {
191845
- resolve(parseResult.data);
191848
+ resolve2(parseResult.data);
191846
191849
  }
191847
191850
  } catch (error49) {
191848
191851
  reject(error49);
@@ -192033,12 +192036,12 @@ class Protocol {
192033
192036
  interval = task.pollInterval;
192034
192037
  }
192035
192038
  } catch {}
192036
- return new Promise((resolve, reject) => {
192039
+ return new Promise((resolve2, reject) => {
192037
192040
  if (signal.aborted) {
192038
192041
  reject(new McpError2(ErrorCode.InvalidRequest, "Request cancelled"));
192039
192042
  return;
192040
192043
  }
192041
- const timeoutId = setTimeout(resolve, interval);
192044
+ const timeoutId = setTimeout(resolve2, interval);
192042
192045
  signal.addEventListener("abort", () => {
192043
192046
  clearTimeout(timeoutId);
192044
192047
  reject(new McpError2(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -193056,7 +193059,7 @@ class McpServer {
193056
193059
  let task = createTaskResult.task;
193057
193060
  const pollInterval = task.pollInterval ?? 5000;
193058
193061
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
193059
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
193062
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
193060
193063
  const updatedTask = await extra.taskStore.getTask(taskId);
193061
193064
  if (!updatedTask) {
193062
193065
  throw new McpError2(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -194610,7 +194613,7 @@ async function gitCloneLogic(input, { provider, appContext }) {
194610
194613
  cloneOptions.depth = input.depth;
194611
194614
  }
194612
194615
  const result = await provider.clone(cloneOptions, {
194613
- workingDirectory: input.localPath,
194616
+ workingDirectory: process.cwd(),
194614
194617
  requestContext: appContext,
194615
194618
  tenantId: appContext.tenantId || "default-tenant"
194616
194619
  });
@@ -199204,14 +199207,14 @@ var StreamableHTTPTransport = class {
199204
199207
  if (!this.#enableJsonResponse && this.sessionId !== undefined)
199205
199208
  ctx.header("mcp-session-id", this.sessionId);
199206
199209
  if (this.#enableJsonResponse)
199207
- return await new Promise((resolve) => {
199210
+ return await new Promise((resolve2) => {
199208
199211
  for (const message of messages)
199209
199212
  if (isJSONRPCRequest(message)) {
199210
199213
  this.#streamMapping.set(streamId, {
199211
199214
  ctx: {
199212
199215
  header: ctx.header,
199213
199216
  json: (data) => {
199214
- resolve(ctx.json(data));
199217
+ resolve2(ctx.json(data));
199215
199218
  }
199216
199219
  },
199217
199220
  cleanup: () => {
@@ -199902,7 +199905,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
199902
199905
  });
199903
199906
  if (!chunk) {
199904
199907
  if (i2 === 1) {
199905
- await new Promise((resolve) => setTimeout(resolve));
199908
+ await new Promise((resolve2) => setTimeout(resolve2));
199906
199909
  maxReadCount = 3;
199907
199910
  continue;
199908
199911
  }
@@ -201975,6 +201978,7 @@ class SessionManager {
201975
201978
  cleanupIntervalId = null;
201976
201979
  staleTimeoutMs;
201977
201980
  cleanupIntervalMs;
201981
+ onSessionExpired = null;
201978
201982
  constructor(staleTimeoutMs = 30 * 60 * 1000, cleanupIntervalMs = 5 * 60 * 1000) {
201979
201983
  this.staleTimeoutMs = staleTimeoutMs;
201980
201984
  this.cleanupIntervalMs = cleanupIntervalMs;
@@ -202030,6 +202034,7 @@ class SessionManager {
202030
202034
  staleTimeoutMs: this.staleTimeoutMs
202031
202035
  });
202032
202036
  this.sessions.delete(sessionId);
202037
+ this.onSessionExpired?.(sessionId);
202033
202038
  return false;
202034
202039
  }
202035
202040
  return true;
@@ -202097,6 +202102,7 @@ class SessionManager {
202097
202102
  const age = now2 - metadata.lastActivityAt;
202098
202103
  if (age > this.staleTimeoutMs) {
202099
202104
  this.sessions.delete(sessionId);
202105
+ this.onSessionExpired?.(sessionId);
202100
202106
  removedCount++;
202101
202107
  }
202102
202108
  }
@@ -202125,21 +202131,27 @@ class SessionManager {
202125
202131
 
202126
202132
  // src/mcp-server/transports/http/httpTransport.ts
202127
202133
  init_utils();
202128
-
202129
- class McpSessionTransport extends StreamableHTTPTransport {
202130
- sessionId;
202131
- constructor(sessionId) {
202132
- super();
202133
- this.sessionId = sessionId;
202134
- }
202135
- }
202136
- function createHttpApp(mcpServer, parentContext) {
202134
+ function createHttpApp(createMcpServer, parentContext) {
202137
202135
  const app = new Hono2;
202138
202136
  const transportContext = {
202139
202137
  ...parentContext,
202140
202138
  component: "HttpTransportSetup"
202141
202139
  };
202140
+ const transports = new Map;
202142
202141
  const sessionManager = SessionManager.getInstance(config2.mcpStatefulSessionStaleTimeoutMs);
202142
+ sessionManager.onSessionExpired = (sessionId) => {
202143
+ const transport = transports.get(sessionId);
202144
+ if (transport) {
202145
+ transport.close().catch((err) => {
202146
+ logger.warning("Failed to close transport for expired session", {
202147
+ ...transportContext,
202148
+ sessionId,
202149
+ error: err instanceof Error ? err.message : String(err)
202150
+ });
202151
+ });
202152
+ transports.delete(sessionId);
202153
+ }
202154
+ };
202143
202155
  logger.info("Session manager initialized", {
202144
202156
  ...transportContext,
202145
202157
  staleTimeoutMs: config2.mcpStatefulSessionStaleTimeoutMs
@@ -202175,18 +202187,6 @@ function createHttpApp(mcpServer, parentContext) {
202175
202187
  ...config2.oauthJwksUri && { jwks_uri: config2.oauthJwksUri }
202176
202188
  });
202177
202189
  });
202178
- app.get(config2.mcpHttpEndpointPath, (c) => {
202179
- return c.json({
202180
- status: "ok",
202181
- server: {
202182
- name: config2.mcpServerName,
202183
- version: config2.mcpServerVersion,
202184
- description: config2.mcpServerDescription,
202185
- transport: config2.mcpTransportType,
202186
- sessionMode: config2.mcpSessionMode
202187
- }
202188
- });
202189
- });
202190
202190
  const authStrategy = createAuthStrategy();
202191
202191
  if (authStrategy) {
202192
202192
  const authMiddleware = createAuthMiddleware(authStrategy);
@@ -202195,113 +202195,134 @@ function createHttpApp(mcpServer, parentContext) {
202195
202195
  } else {
202196
202196
  logger.info("Authentication is disabled; MCP endpoint is unprotected.", transportContext);
202197
202197
  }
202198
- app.delete(config2.mcpHttpEndpointPath, (c) => {
202199
- const sessionId = c.req.header("mcp-session-id");
202200
- if (!sessionId) {
202201
- return c.json({
202198
+ const getSessionTransport = (sessionId) => {
202199
+ if (!sessionManager.isSessionValid(sessionId)) {
202200
+ const stale = transports.get(sessionId);
202201
+ if (stale) {
202202
+ stale.close().catch(() => {});
202203
+ transports.delete(sessionId);
202204
+ }
202205
+ return Response.json({
202202
202206
  jsonrpc: "2.0",
202203
202207
  error: {
202204
- code: -32600,
202205
- message: "Mcp-Session-Id header required for DELETE"
202208
+ code: -32001,
202209
+ message: "Session expired or invalid. Please reinitialize."
202206
202210
  },
202207
202211
  id: null
202208
- }, 400);
202212
+ }, { status: 404 });
202209
202213
  }
202210
- const terminated = sessionManager.terminateSession(sessionId);
202211
- if (!terminated) {
202212
- return c.json({
202214
+ const transport = transports.get(sessionId);
202215
+ if (!transport) {
202216
+ return Response.json({
202213
202217
  jsonrpc: "2.0",
202214
202218
  error: {
202215
202219
  code: -32001,
202216
- message: "Session not found or already expired"
202220
+ message: "Session not found. Please reinitialize."
202217
202221
  },
202218
202222
  id: null
202219
- }, 404);
202223
+ }, { status: 404 });
202220
202224
  }
202221
- logger.info("Session terminated via DELETE", {
202222
- ...transportContext,
202223
- sessionId
202224
- });
202225
- return c.body(null, 204);
202226
- });
202227
- app.all(config2.mcpHttpEndpointPath, async (c) => {
202228
- const protocolVersion = c.req.header("mcp-protocol-version") ?? "2025-03-26";
202229
- logger.debug("Handling MCP request.", {
202230
- ...transportContext,
202231
- path: c.req.path,
202232
- method: c.req.method,
202233
- protocolVersion
202234
- });
202235
- const supportedVersions = ["2025-03-26", "2025-06-18"];
202236
- if (!supportedVersions.includes(protocolVersion)) {
202237
- logger.warning("Unsupported MCP protocol version requested.", {
202238
- ...transportContext,
202239
- protocolVersion,
202240
- supportedVersions
202225
+ sessionManager.touchSession(sessionId);
202226
+ return transport;
202227
+ };
202228
+ app.get(config2.mcpHttpEndpointPath, async (c) => {
202229
+ const sessionId = c.req.header("mcp-session-id");
202230
+ if (!sessionId) {
202231
+ return c.json({
202232
+ status: "ok",
202233
+ server: {
202234
+ name: config2.mcpServerName,
202235
+ version: config2.mcpServerVersion,
202236
+ description: config2.mcpServerDescription,
202237
+ transport: config2.mcpTransportType,
202238
+ sessionMode: config2.mcpSessionMode
202239
+ }
202241
202240
  });
202241
+ }
202242
+ const transportOrError = getSessionTransport(sessionId);
202243
+ if (transportOrError instanceof Response)
202244
+ return transportOrError;
202245
+ const response = await transportOrError.handleRequest(c);
202246
+ return response ?? c.body(null, 204);
202247
+ });
202248
+ app.delete(config2.mcpHttpEndpointPath, async (c) => {
202249
+ const sessionId = c.req.header("mcp-session-id");
202250
+ if (!sessionId) {
202242
202251
  return c.json({
202243
202252
  jsonrpc: "2.0",
202244
202253
  error: {
202245
202254
  code: -32600,
202246
- message: `Unsupported MCP protocol version: ${protocolVersion}`,
202247
- data: {
202248
- requested: protocolVersion,
202249
- supported: supportedVersions
202250
- }
202255
+ message: "Mcp-Session-Id header required for DELETE"
202251
202256
  },
202252
202257
  id: null
202253
202258
  }, 400);
202254
202259
  }
202255
- const sessionId = c.req.header("mcp-session-id") ?? randomUUID();
202256
- if (c.req.header("mcp-session-id") && !sessionManager.isSessionValid(sessionId)) {
202257
- logger.warning("Invalid or expired session ID", {
202258
- ...transportContext,
202259
- sessionId
202260
- });
202260
+ const transport = transports.get(sessionId);
202261
+ if (!transport) {
202261
202262
  return c.json({
202262
202263
  jsonrpc: "2.0",
202263
202264
  error: {
202264
202265
  code: -32001,
202265
- message: "Session expired or invalid. Please reinitialize."
202266
+ message: "Session not found or already expired"
202266
202267
  },
202267
202268
  id: null
202268
202269
  }, 404);
202269
202270
  }
202270
- if (!c.req.header("mcp-session-id")) {
202271
- logger.debug("New session will be created", {
202272
- ...transportContext,
202273
- sessionId
202271
+ const response = await transport.handleRequest(c);
202272
+ transports.delete(sessionId);
202273
+ sessionManager.terminateSession(sessionId);
202274
+ logger.info("Session terminated via DELETE", {
202275
+ ...transportContext,
202276
+ sessionId
202277
+ });
202278
+ return response ?? c.body(null, 204);
202279
+ });
202280
+ app.post(config2.mcpHttpEndpointPath, async (c) => {
202281
+ logger.debug("Handling MCP POST request.", {
202282
+ ...transportContext,
202283
+ path: c.req.path
202284
+ });
202285
+ const sessionId = c.req.header("mcp-session-id");
202286
+ const handleRequest = async () => {
202287
+ if (sessionId) {
202288
+ const transportOrError = getSessionTransport(sessionId);
202289
+ if (transportOrError instanceof Response)
202290
+ return transportOrError;
202291
+ const response2 = await transportOrError.handleRequest(c);
202292
+ return response2 ?? c.body(null, 204);
202293
+ }
202294
+ const server = await createMcpServer();
202295
+ const transport = new StreamableHTTPTransport({
202296
+ sessionIdGenerator: () => randomUUID(),
202297
+ onsessioninitialized: (sid) => {
202298
+ transports.set(sid, transport);
202299
+ const store = authContext.getStore();
202300
+ sessionManager.createSession(sid, store?.authInfo.clientId, store?.authInfo.tenantId);
202301
+ logger.debug("New MCP session initialized", {
202302
+ ...transportContext,
202303
+ sessionId: sid
202304
+ });
202305
+ },
202306
+ onsessionclosed: (sid) => {
202307
+ transports.delete(sid);
202308
+ sessionManager.terminateSession(sid);
202309
+ logger.debug("MCP session closed via transport", {
202310
+ ...transportContext,
202311
+ sessionId: sid
202312
+ });
202313
+ }
202274
202314
  });
202275
- } else {
202276
- sessionManager.touchSession(sessionId);
202277
- }
202278
- const transport = new McpSessionTransport(sessionId);
202279
- const handleRpc = async () => {
202280
- await mcpServer.connect(transport);
202315
+ await server.connect(transport);
202281
202316
  const response = await transport.handleRequest(c);
202282
- if (response && !c.req.header("mcp-session-id")) {
202283
- const store = authContext.getStore();
202284
- sessionManager.createSession(sessionId, store?.authInfo.clientId, store?.authInfo.tenantId);
202285
- }
202286
- if (response) {
202287
- return response;
202288
- }
202289
- return c.body(null, 204);
202317
+ return response ?? c.body(null, 204);
202290
202318
  };
202291
202319
  try {
202292
202320
  const store = authContext.getStore();
202293
202321
  if (store) {
202294
- return await authContext.run(store, handleRpc);
202322
+ return await authContext.run(store, handleRequest);
202295
202323
  }
202296
- return await handleRpc();
202324
+ return await handleRequest();
202297
202325
  } catch (err) {
202298
- await transport.close?.().catch((closeErr) => {
202299
- logger.warning("Failed to close transport after error", {
202300
- ...transportContext,
202301
- sessionId,
202302
- error: closeErr instanceof Error ? closeErr.message : String(closeErr)
202303
- });
202304
- });
202305
202326
  throw err instanceof Error ? err : new Error(String(err));
202306
202327
  }
202307
202328
  });
@@ -202311,9 +202332,9 @@ function createHttpApp(mcpServer, parentContext) {
202311
202332
  async function isPortInUse(port, host, parentContext) {
202312
202333
  const context = { ...parentContext, operation: "isPortInUse", port, host };
202313
202334
  logger.debug(`Checking if port ${port} is in use...`, context);
202314
- return new Promise((resolve) => {
202335
+ return new Promise((resolve2) => {
202315
202336
  const tempServer = http.createServer();
202316
- tempServer.once("error", (err) => resolve(err.code === "EADDRINUSE")).once("listening", () => tempServer.close(() => resolve(false))).listen(port, host);
202337
+ tempServer.once("error", (err) => resolve2(err.code === "EADDRINUSE")).once("listening", () => tempServer.close(() => resolve2(false))).listen(port, host);
202317
202338
  });
202318
202339
  }
202319
202340
  function startHttpServerWithRetry(app, initialPort, host, maxRetries, parentContext) {
@@ -202322,7 +202343,7 @@ function startHttpServerWithRetry(app, initialPort, host, maxRetries, parentCont
202322
202343
  operation: "startHttpServerWithRetry"
202323
202344
  };
202324
202345
  logger.info(`Attempting to start HTTP server on port ${initialPort} with ${maxRetries} retries.`, startContext);
202325
- return new Promise((resolve, reject) => {
202346
+ return new Promise((resolve2, reject) => {
202326
202347
  const tryBind = (port, attempt) => {
202327
202348
  if (attempt > maxRetries + 1) {
202328
202349
  const error49 = new Error(`Failed to bind to any port after ${maxRetries} retries.`);
@@ -202350,7 +202371,7 @@ function startHttpServerWithRetry(app, initialPort, host, maxRetries, parentCont
202350
202371
  logStartupBanner(`
202351
202372
  \uD83D\uDE80 MCP Server running at: ${serverAddress}`, "http");
202352
202373
  });
202353
- resolve(serverInstance);
202374
+ resolve2(serverInstance);
202354
202375
  } catch (err) {
202355
202376
  logger.warning(`Binding attempt failed for port ${port}, retrying...`, { ...startContext, port, attempt, error: String(err) });
202356
202377
  setTimeout(() => tryBind(port + 1, attempt + 1), config2.mcpHttpPortRetryDelayMs);
@@ -202360,13 +202381,13 @@ function startHttpServerWithRetry(app, initialPort, host, maxRetries, parentCont
202360
202381
  tryBind(initialPort, 1);
202361
202382
  });
202362
202383
  }
202363
- async function startHttpTransport(mcpServer, parentContext) {
202384
+ async function startHttpTransport(createMcpServer, parentContext) {
202364
202385
  const transportContext = {
202365
202386
  ...parentContext,
202366
202387
  component: "HttpTransportStart"
202367
202388
  };
202368
202389
  logger.info("Starting HTTP transport.", transportContext);
202369
- const app = createHttpApp(mcpServer, transportContext);
202390
+ const app = createHttpApp(createMcpServer, transportContext);
202370
202391
  const server = await startHttpServerWithRetry(app, config2.mcpHttpPort, config2.mcpHttpHost, config2.mcpHttpMaxPortRetries, transportContext);
202371
202392
  logger.info("HTTP transport started successfully.", transportContext);
202372
202393
  return server;
@@ -202381,14 +202402,14 @@ async function stopHttpTransport(server, parentContext) {
202381
202402
  const sessionManager = SessionManager.getInstance();
202382
202403
  sessionManager.stopCleanupInterval();
202383
202404
  logger.info("Session cleanup interval stopped", operationContext);
202384
- return new Promise((resolve, reject) => {
202405
+ return new Promise((resolve2, reject) => {
202385
202406
  server.close((err) => {
202386
202407
  if (err) {
202387
202408
  logger.error("Error closing HTTP server.", err, operationContext);
202388
202409
  return reject(err);
202389
202410
  }
202390
202411
  logger.info("HTTP server closed successfully.", operationContext);
202391
- resolve();
202412
+ resolve2();
202392
202413
  });
202393
202414
  });
202394
202415
  }
@@ -202473,12 +202494,12 @@ class StdioServerTransport {
202473
202494
  this.onclose?.();
202474
202495
  }
202475
202496
  send(message2) {
202476
- return new Promise((resolve) => {
202497
+ return new Promise((resolve2) => {
202477
202498
  const json3 = serializeMessage(message2);
202478
202499
  if (this._stdout.write(json3)) {
202479
- resolve();
202500
+ resolve2();
202480
202501
  } else {
202481
- this._stdout.once("drain", resolve);
202502
+ this._stdout.once("drain", resolve2);
202482
202503
  }
202483
202504
  });
202484
202505
  }
@@ -202543,10 +202564,10 @@ class TransportManager {
202543
202564
  transport: this.config.mcpTransportType
202544
202565
  });
202545
202566
  this.logger.info(`Starting transport: ${this.config.mcpTransportType}`, context);
202546
- const mcpServer = await this.createMcpServer();
202547
202567
  if (this.config.mcpTransportType === "http") {
202548
- this.serverInstance = await startHttpTransport(mcpServer, context);
202568
+ this.serverInstance = await startHttpTransport(this.createMcpServer, context);
202549
202569
  } else if (this.config.mcpTransportType === "stdio") {
202570
+ const mcpServer = await this.createMcpServer();
202550
202571
  this.serverInstance = await startStdioTransport(mcpServer, context);
202551
202572
  } else {
202552
202573
  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.2",
3
+ "version": "2.8.4",
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",