@azure-devops/mcp 2.7.0 โ†’ 2.8.0-nightly.20260624

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
@@ -3,22 +3,23 @@
3
3
  > [!IMPORTANT]
4
4
  > The Azure DevOps Remote MCP Server is now available in public preview for all organizations. We recommend migrating to the [Remote MCP Server](https://learn.microsoft.com/en-us/azure/devops/mcp-server/remote-mcp-server) going forward.
5
5
  >
6
- > [Learn more](#-remote-mcp-server)
6
+ > [Learn more](#-remote-mcp-server-recommended)
7
7
 
8
- This TypeScript project provides a **local** MCP server for Azure DevOps, enabling you to perform a wide range of Azure DevOps tasks directly from your code editor.
8
+ This project provides Azure DevOps MCP tooling for AI agents, with a **remote-first** onboarding experience and a local server option when you need it.
9
9
 
10
10
  ## ๐Ÿ“„ Table of Contents
11
11
 
12
12
  1. [๐Ÿ“บ Overview](#-overview)
13
13
  2. [๐Ÿ† Expectations](#-expectations)
14
- 3. [๐Ÿš€ Remote MCP Server](#-remote-mcp-server)
14
+ 3. [๐Ÿš€ Remote MCP Server (Recommended)](#-remote-mcp-server-recommended)
15
15
  4. [โš™๏ธ Supported Tools](#๏ธ-supported-tools)
16
- 5. [๐Ÿ”Œ Installation & Getting Started](#-installation--getting-started)
17
- 6. [๐ŸŒ Using Domains](#-using-domains)
18
- 7. [๐Ÿ“ Troubleshooting](#-troubleshooting)
19
- 8. [๐ŸŽฉ Examples & Best Practices](#-examples--best-practices)
20
- 9. [๐Ÿ™‹โ€โ™€๏ธ Frequently Asked Questions](#๏ธ-frequently-asked-questions)
21
- 10. [๐Ÿ“Œ Contributing](#-contributing)
16
+ 5. [๐Ÿ”Œ Local MCP Server Installation (Optional)](#-local-mcp-server-installation-optional)
17
+ 6. [๐ŸŒ Using Domains (local)](#-using-domains-local)
18
+ 7. [๐Ÿฅ Project and Team Defaults (local)](#-project-and-team-defaults-local)
19
+ 8. [๐Ÿ“ Troubleshooting](#-troubleshooting)
20
+ 9. [๐ŸŽฉ Examples & Best Practices](#-examples--best-practices)
21
+ 10. [๐Ÿ™‹โ€โ™€๏ธ Frequently Asked Questions](#๏ธ-frequently-asked-questions)
22
+ 11. [๐Ÿ“Œ Contributing](#-contributing)
22
23
 
23
24
  ## ๐Ÿ“บ Overview
24
25
 
@@ -39,9 +40,9 @@ The Azure DevOps MCP Server brings Azure DevOps context to your agents. Try prom
39
40
 
40
41
  ## ๐Ÿ† Expectations
41
42
 
42
- The Azure DevOps MCP Server is built from tools that are concise, simple, focused, and easy to useโ€”each designed for a specific scenario. We intentionally avoid complex tools that try to do too much. The goal is to provide a thin abstraction layer over the REST APIs, making data access straightforward and letting the language model handle complex reasoning.
43
+ The Azure DevOps MCP Server is built around tools that are concise, simple, focused, and easy to use, with each one designed for a specific scenario. We intentionally avoid creating complex tools that try to do too much. The goal is to provide a thin abstraction layer over the REST APIs that makes data access straightforward while allowing the language model to handle the more complex reasoning.
43
44
 
44
- ## ๐Ÿš€ Remote MCP Server
45
+ ## ๐Ÿš€ Remote MCP Server (Recommended)
45
46
 
46
47
  The Azure DevOps **Remote MCP Server** is now available in [public preview](https://devblogs.microsoft.com/devops/azure-devops-remote-mcp-server-public-preview).
47
48
 
@@ -54,15 +55,42 @@ If you encounter issues with tools, need support, or have a feature request, you
54
55
  > [!WARNING]
55
56
  > Internal Microsoft users of the Remote MCP Server should **not** create issues in this repository. Please use the dedicated Teams channel instead.
56
57
 
57
- For instructions on how to get started with the Remote MCP Server, see the [onboarding documentation](https://learn.microsoft.com/en-us/azure/devops/mcp-server/remote-mcp-server).
58
+ For complete instructions, see the [Remote MCP Server onboarding documentation](https://learn.microsoft.com/en-us/azure/devops/mcp-server/remote-mcp-server?view=azure-devops).
59
+
60
+ ### Quick start with `.vscode/mcp.json`
61
+
62
+ Use this configuration to connect directly to the Azure DevOps-hosted endpoint using streamable HTTP transport:
63
+
64
+ ```json
65
+ {
66
+ "servers": {
67
+ "ado-remote-mcp": {
68
+ "url": "https://mcp.dev.azure.com/{organization}",
69
+ "type": "http"
70
+ }
71
+ },
72
+ "inputs": []
73
+ }
74
+ ```
75
+
76
+ See [documentation](https://learn.microsoft.com/en-us/azure/devops/mcp-server/remote-mcp-server?view=azure-devops#mcpjson-configuration) for additional configuration options.
77
+
78
+ After saving `.vscode/mcp.json`, start the server from the MCP view in VS Code, then run a prompt like `List ADO projects`.
58
79
 
59
80
  ## โš™๏ธ Supported Tools
60
81
 
61
- See [TOOLSET.md](./docs/TOOLSET.md) for a comprehensive list.
82
+ See the [Available Tools](https://learn.microsoft.com/en-us/azure/devops/mcp-server/remote-mcp-server?view=azure-devops#available-tools) documentation for the complete list of available remote tools.
83
+
84
+ For a comprehensive list of local tools, see [TOOLSET.md](./docs/TOOLSET.md).
85
+
86
+ ## ๐Ÿ”Œ Local MCP Server Installation (Optional)
87
+
88
+ > [!IMPORTANT]
89
+ > Start with the Remote MCP Server first. Use the local MCP Server only if your scenario specifically requires a local `stdio` setup.
62
90
 
63
- ## ๐Ÿ”Œ Installation & Getting Started
91
+ Use this section if you specifically need the local `stdio` server experience. For most users, start with the [Remote MCP Server](#-remote-mcp-server-recommended) section above.
64
92
 
65
- For the best experience, use Visual Studio Code and GitHub Copilot. See the [getting started documentation](./docs/GETTINGSTARTED.md) to use our MCP Server with other tools such as Visual Studio 2022, Claude Code, Cursor, Opencode, and Kilocode.
93
+ For the best experience, use Visual Studio Code and GitHub Copilot. See the [getting started documentation](./docs/GETTINGSTARTED.md) to use our MCP Server with other tools such as Visual Studio 2022, Codex, Claude Code, Cursor, Opencode, and Kilocode.
66
94
 
67
95
  ### Prerequisites
68
96
 
@@ -72,7 +100,7 @@ For the best experience, use Visual Studio Code and GitHub Copilot. See the [get
72
100
 
73
101
  ### Installation
74
102
 
75
- #### ๐Ÿงจ Install from Public Feed (Recommended)
103
+ #### ๐Ÿงจ Install from Public Feed
76
104
 
77
105
  This installation method is the easiest for all users of Visual Studio Code.
78
106
 
@@ -137,9 +165,9 @@ Open GitHub Copilot Chat and try a prompt like `List ADO projects`. The first ti
137
165
  > ๐Ÿ’ฅ We strongly recommend creating a `.github\copilot-instructions.md` in your project. This will enhance your experience using the Azure DevOps MCP Server with GitHub Copilot Chat.
138
166
  > To start, just include "`This project uses Azure DevOps. Always check to see if the Azure DevOps MCP server has a tool relevant to the user's request`" in your copilot instructions file.
139
167
 
140
- See the [getting started documentation](./docs/GETTINGSTARTED.md) to use our MCP Server with other tools such as Visual Studio 2022, Claude Code, and Cursor.
168
+ See the [getting started documentation](./docs/GETTINGSTARTED.md) to use our MCP Server with other tools such as Visual Studio 2022, Codex, Claude Code, and Cursor.
141
169
 
142
- ## ๐ŸŒ Using Domains
170
+ ## ๐ŸŒ Using Domains (local)
143
171
 
144
172
  Azure DevOps exposes a large surface area. As a result, our Azure DevOps MCP Server includes many tools. To keep the toolset manageable, avoid confusing the model, and respect client limits on loaded tools, use Domains to load only the areas you need. Domains are named groups of related tools (for example: core, work, work-items, repositories, wiki). Add the `-d` argument and the domain names to the server args in your `mcp.json` to list the domains to enable.
145
173
 
@@ -170,6 +198,28 @@ We recommend that you always enable `core` tools so that you can fetch project l
170
198
 
171
199
  > By default all domains are loaded
172
200
 
201
+ ## ๐Ÿฅ Project and Team Defaults (local)
202
+
203
+ You can also configure default Azure DevOps project and team values from `.vscode/mcp.json` using `project` and `team`, so tools can skip selection prompts.
204
+
205
+ ### Example `.vscode/mcp.json`
206
+
207
+ ```json
208
+ {
209
+ "servers": {
210
+ "ado": {
211
+ "type": "stdio",
212
+ "command": "npx",
213
+ "args": ["-y", "@azure-devops/mcp", "myorg", "--authentication", "azcli"],
214
+ "env": {
215
+ "ado_mcp_project": "Contoso",
216
+ "ado_mcp_team": "Fabrikam Team"
217
+ }
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
173
223
  ## ๐Ÿ“ Troubleshooting
174
224
 
175
225
  See the [Troubleshooting guide](./docs/TROUBLESHOOTING.md) for help with common issues and logging.
package/dist/auth.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -111,7 +111,7 @@ async function main() {
111
111
  };
112
112
  logger.debug("PAT mode: global fetch interceptor installed to rewrite Bearer -> Basic auth headers");
113
113
  }
114
- // removing prompts untill further notice
114
+ // removing prompts until further notice
115
115
  // configurePrompts(server);
116
116
  configureAllTools(server, authenticator, getAzureDevOpsClient(authenticator, userAgentComposer, argv.authentication), () => userAgentComposer.userAgent, enabledDomains);
117
117
  const transport = new StdioServerTransport();
package/dist/logger.js CHANGED
File without changes
File without changes
package/dist/prompts.js CHANGED
File without changes
@@ -1,6 +1,11 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
  export async function elicitProject(server, connection, message) {
4
+ // Check for default project from environment variable
5
+ const defaultProject = process.env.ado_mcp_project;
6
+ if (defaultProject) {
7
+ return { resolved: defaultProject };
8
+ }
4
9
  const coreApi = await connection.getCoreApi();
5
10
  const projects = await coreApi.getProjects("wellFormed", 100, 0, undefined, false);
6
11
  if (!projects || projects.length === 0) {
@@ -31,6 +36,11 @@ export async function elicitProject(server, connection, message) {
31
36
  return { resolved: String(result.content.project) };
32
37
  }
33
38
  export async function elicitTeam(server, connection, project, message) {
39
+ // Check for default team from environment variable
40
+ const defaultTeam = process.env.ado_mcp_team;
41
+ if (defaultTeam) {
42
+ return { resolved: defaultTeam };
43
+ }
34
44
  const coreApi = await connection.getCoreApi();
35
45
  const teams = await coreApi.getTeams(project, undefined, undefined, undefined, false);
36
46
  if (!teams || teams.length === 0) {
@@ -27,7 +27,10 @@ const PIPELINE_TOOLS = {
27
27
  function configurePipelineTools(server, tokenProvider, connectionProvider, userAgentProvider) {
28
28
  server.tool(PIPELINE_TOOLS.pipelines_get_build_definitions, "Retrieves a list of build definitions for a given project.", {
29
29
  project: z.string().describe("Project ID or name to get build definitions for"),
30
- repositoryId: z.string().optional().describe("Repository ID to filter build definitions"),
30
+ repositoryId: z
31
+ .string()
32
+ .optional()
33
+ .describe("Repository ID to filter build definitions. Can be a GUID or a repository name; when a name is provided, it is auto-resolved to the repository GUID using the project parameter (Azure Repos / TfsGit only)."),
31
34
  repositoryType: z.enum(["TfsGit", "GitHub", "BitbucketCloud"]).optional().describe("Type of repository to filter build definitions"),
32
35
  name: z.string().optional().describe("Name of the build definition to filter"),
33
36
  path: z.string().optional().describe("Path of the build definition to filter"),
@@ -49,7 +52,24 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
49
52
  }, async ({ project, repositoryId, repositoryType, name, path, queryOrder, top, continuationToken, minMetricsTime, definitionIds, builtAfter, notBuiltAfter, includeAllProperties, includeLatestBuilds, taskIdFilter, processType, yamlFilename, }) => {
50
53
  const connection = await connectionProvider();
51
54
  const buildApi = await connection.getBuildApi();
52
- const buildDefinitions = await buildApi.getDefinitions(project, name, repositoryId, repositoryType, safeEnumConvert(DefinitionQueryOrder, queryOrder), top, continuationToken, minMetricsTime, definitionIds, path, builtAfter, notBuiltAfter, includeAllProperties, includeLatestBuilds, taskIdFilter, processType, yamlFilename);
55
+ // Auto-resolve repositoryId from name to GUID for Azure Repos
56
+ let resolvedRepositoryId = repositoryId;
57
+ if (repositoryId) {
58
+ const isGuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(repositoryId);
59
+ if (!isGuid && (!repositoryType || repositoryType === "TfsGit")) {
60
+ const gitApi = await connection.getGitApi();
61
+ const repositories = await gitApi.getRepositories(project);
62
+ const repo = repositories?.find((r) => r.name === repositoryId);
63
+ if (!repo?.id) {
64
+ return {
65
+ content: [{ type: "text", text: `Error: Repository '${repositoryId}' not found in project '${project}'.` }],
66
+ isError: true,
67
+ };
68
+ }
69
+ resolvedRepositoryId = repo.id;
70
+ }
71
+ }
72
+ const buildDefinitions = await buildApi.getDefinitions(project, name, resolvedRepositoryId, repositoryType, safeEnumConvert(DefinitionQueryOrder, queryOrder), top, continuationToken, minMetricsTime, definitionIds, path, builtAfter, notBuiltAfter, includeAllProperties, includeLatestBuilds, taskIdFilter, processType, yamlFilename);
53
73
  return {
54
74
  content: [{ type: "text", text: JSON.stringify(buildDefinitions, null, 2) }],
55
75
  };
@@ -329,19 +349,21 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
329
349
  content: [{ type: "text", text: JSON.stringify(artifacts, null, 2) }],
330
350
  };
331
351
  });
332
- server.tool(PIPELINE_TOOLS.pipelines_download_artifact, "Downloads a pipeline artifact.", {
352
+ server.tool(PIPELINE_TOOLS.pipelines_download_artifact, "Downloads a pipeline artifact. When destinationPath is provided, it must be a relative local path; absolute paths and path traversal are not allowed.", {
333
353
  project: z.string().describe("The name or ID of the project."),
334
354
  buildId: z.coerce.number().min(1).describe("The ID of the build."),
335
355
  artifactName: z.string().describe("The name of the artifact to download."),
336
- destinationPath: z.string().optional().describe("The local path to download the artifact to. If not provided, returns binary content as base64."),
356
+ destinationPath: z.string().optional().describe("The relative local path to download the artifact to. If not provided, returns binary content as base64."),
337
357
  }, async ({ project, buildId, artifactName, destinationPath }) => {
338
- const isAbsolutePath = (value) => posix.isAbsolute(value) || win32.isAbsolute(value);
358
+ const hasUnsafePathSegment = (value) => value.split(/[\\/]+/).some((segment) => segment === "." || segment === "..");
359
+ const hasPathSeparators = (value) => /[\\/]/.test(value);
339
360
  const hasDriveLetter = (value) => /^[a-zA-Z]:/.test(value);
340
- if (artifactName.includes("..")) {
341
- throw new Error("Invalid artifactName: path traversal is not allowed.");
361
+ const isAbsolutePath = (value) => posix.isAbsolute(value) || win32.isAbsolute(value);
362
+ if (hasUnsafePathSegment(artifactName) || hasPathSeparators(artifactName) || hasDriveLetter(artifactName) || isAbsolutePath(artifactName)) {
363
+ throw new Error("Invalid artifactName: artifactName must be a file name, not a path.");
342
364
  }
343
- if (destinationPath && (destinationPath.includes("..") || isAbsolutePath(destinationPath) || hasDriveLetter(destinationPath))) {
344
- throw new Error("Invalid destinationPath: absolute paths and path traversals are not allowed.");
365
+ if (destinationPath && (hasUnsafePathSegment(destinationPath) || isAbsolutePath(destinationPath) || hasDriveLetter(destinationPath))) {
366
+ throw new Error("Invalid destinationPath: use a relative path without path traversal.");
345
367
  }
346
368
  const connection = await connectionProvider();
347
369
  const buildApi = await connection.getBuildApi();