@azure-devops/mcp 2.7.0-nightly.20260618 → 2.7.0-nightly.20260619

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.
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
  import { z } from "zod";
4
- import { apiVersion, extractAdoStreamError } from "../utils.js";
4
+ import { apiVersion, extractAdoStreamError, getOrgFromUrl } from "../utils.js";
5
5
  import { createExternalContentResponse } from "../shared/content-safety.js";
6
6
  const WIKI_TOOLS = {
7
7
  list_wikis: "wiki_list_wikis",
@@ -165,6 +165,22 @@ function configureWikiTools(server, tokenProvider, connectionProvider, userAgent
165
165
  if ("error" in parsed) {
166
166
  return { content: [{ type: "text", text: `Error fetching wiki page content: ${parsed.error}` }], isError: true };
167
167
  }
168
+ // Guard against cross-organization requests: a user-supplied URL must target the
169
+ // same organization the server is connected to. Otherwise the org segment in the
170
+ // URL would be silently ignored and content fetched from the configured org instead.
171
+ const configuredOrg = getOrgFromUrl(connection.serverUrl);
172
+ const urlOrg = getOrgFromUrl(url);
173
+ if (configuredOrg && urlOrg !== configuredOrg) {
174
+ return {
175
+ content: [
176
+ {
177
+ type: "text",
178
+ text: `Error fetching wiki page content: The provided URL targets organization '${urlOrg ?? "unknown"}', which does not match the configured organization '${configuredOrg}'. Cross-organization requests are not allowed.`,
179
+ },
180
+ ],
181
+ isError: true,
182
+ };
183
+ }
168
184
  resolvedProject = parsed.project;
169
185
  resolvedWiki = parsed.wikiIdentifier;
170
186
  if (parsed.pagePath) {
package/dist/utils.js CHANGED
@@ -90,6 +90,37 @@ export function extractAdoStreamError(content) {
90
90
  }
91
91
  return null;
92
92
  }
93
+ /**
94
+ * Extracts the Azure DevOps organization identifier from a URL.
95
+ *
96
+ * Only recognized Azure DevOps hosts are accepted; any other host returns null
97
+ * so that callers can treat unrecognized URLs as a boundary violation.
98
+ *
99
+ * Supports both modern and legacy organization URL forms:
100
+ * - https://dev.azure.com/{org}/... -> org is the first path segment
101
+ * - https://{org}.visualstudio.com/... -> org is the host subdomain
102
+ *
103
+ * @param url Any Azure DevOps URL (e.g. a wiki page link or a connection serverUrl).
104
+ * @returns The lowercased organization name, or null if it cannot be determined.
105
+ */
106
+ export function getOrgFromUrl(url) {
107
+ try {
108
+ const u = new URL(url);
109
+ const host = u.hostname.toLowerCase();
110
+ if (host === "visualstudio.com" || host.endsWith(".visualstudio.com")) {
111
+ const subdomain = host.split(".")[0];
112
+ return subdomain && subdomain !== "visualstudio" ? subdomain : null;
113
+ }
114
+ if (host === "dev.azure.com" || host.endsWith(".dev.azure.com")) {
115
+ const firstSegment = u.pathname.split("/").filter(Boolean)[0];
116
+ return firstSegment ? firstSegment.toLowerCase() : null;
117
+ }
118
+ return null;
119
+ }
120
+ catch {
121
+ return null;
122
+ }
123
+ }
93
124
  /**
94
125
  * Convert a Node.js ReadableStream to a string.
95
126
  * Shared utility for consistent stream handling across tools.
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const packageVersion = "2.7.0-nightly.20260618";
1
+ export const packageVersion = "2.7.0-nightly.20260619";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-devops/mcp",
3
- "version": "2.7.0-nightly.20260618",
3
+ "version": "2.7.0-nightly.20260619",
4
4
  "mcpName": "microsoft.com/azure-devops",
5
5
  "description": "MCP server for interacting with Azure DevOps",
6
6
  "license": "MIT",