@azure-devops/mcp 2.10.0-nightly.20261007 → 2.10.0-nightly.20261009

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.
@@ -440,7 +440,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
440
440
  return { content: [{ type: "text", text: "attachmentId is required for download" }], isError: true };
441
441
  if (savePath) {
442
442
  const localFileName = fileName ?? attachmentId;
443
- if (localFileName.includes("\0") || path.posix.basename(localFileName) !== localFileName || path.win32.basename(localFileName) !== localFileName) {
443
+ if (!localFileName || localFileName.includes("\0") || path.posix.basename(localFileName) !== localFileName || path.win32.basename(localFileName) !== localFileName) {
444
444
  throw new Error("Invalid fileName: path components are not allowed.");
445
445
  }
446
446
  }
@@ -455,6 +455,12 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
455
455
  }
456
456
  const workItemApi = await connection.getWorkItemTrackingApi();
457
457
  const stream = await workItemApi.getAttachmentContent(attachmentId, fileName, resolvedProject);
458
+ const responseStream = stream;
459
+ if (responseStream.statusCode !== undefined && (responseStream.statusCode < 200 || responseStream.statusCode >= 300)) {
460
+ responseStream.resume();
461
+ const statusMessage = responseStream.statusMessage ? ` ${responseStream.statusMessage}` : "";
462
+ throw new Error(`Attachment download failed: HTTP ${responseStream.statusCode}${statusMessage}`);
463
+ }
458
464
  const chunks = [];
459
465
  await new Promise((resolve, reject) => {
460
466
  stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
@@ -469,7 +475,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
469
475
  if (relativeDirectory === ".." || relativeDirectory.startsWith(`..${path.sep}`) || path.isAbsolute(relativeDirectory)) {
470
476
  throw new Error("Invalid savePath: destination must remain inside the workspace.");
471
477
  }
472
- const localFilePath = path.join(resolvedDirectory, fileName ?? attachmentId);
478
+ const localFilePath = path.resolve(resolvedDirectory, fileName ?? attachmentId);
473
479
  // O_EXCL fails if the file (or a symlink) already exists, so there is no check-then-write race.
474
480
  const file = await open(localFilePath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | (constants.O_NOFOLLOW ?? 0), 0o600);
475
481
  try {
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const packageVersion = "2.10.0-nightly.20261007";
1
+ export const packageVersion = "2.10.0-nightly.20261009";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-devops/mcp",
3
- "version": "2.10.0-nightly.20261007",
3
+ "version": "2.10.0-nightly.20261009",
4
4
  "mcpName": "microsoft.com/azure-devops",
5
5
  "description": "MCP server for interacting with Azure DevOps",
6
6
  "license": "MIT",