@cyanheads/git-mcp-server 2.8.1 → 2.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.1-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.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/)
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.1",
15338
+ version: "2.8.2",
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
  };
@@ -202098,7 +202144,11 @@ function createHttpApp(mcpServer, parentContext) {
202098
202144
  ...transportContext,
202099
202145
  staleTimeoutMs: config2.mcpStatefulSessionStaleTimeoutMs
202100
202146
  });
202101
- const allowedOrigin = Array.isArray(config2.mcpAllowedOrigins) && config2.mcpAllowedOrigins.length > 0 ? config2.mcpAllowedOrigins : "*";
202147
+ const explicitOrigins = config2.mcpAllowedOrigins;
202148
+ const allowedOrigin = explicitOrigins && explicitOrigins.length > 0 ? explicitOrigins : "*";
202149
+ if (allowedOrigin === "*" && config2.environment === "production") {
202150
+ 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);
202151
+ }
202102
202152
  app.use("*", cors({
202103
202153
  origin: allowedOrigin,
202104
202154
  allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
@@ -202132,7 +202182,6 @@ function createHttpApp(mcpServer, parentContext) {
202132
202182
  name: config2.mcpServerName,
202133
202183
  version: config2.mcpServerVersion,
202134
202184
  description: config2.mcpServerDescription,
202135
- environment: config2.environment,
202136
202185
  transport: config2.mcpTransportType,
202137
202186
  sessionMode: config2.mcpSessionMode
202138
202187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/git-mcp-server",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
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",