@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 +68 -18
- package/dist/auth.js +0 -0
- package/dist/index.js +1 -1
- package/dist/logger.js +0 -0
- package/dist/org-tenants.js +0 -0
- package/dist/prompts.js +0 -0
- package/dist/shared/elicitations.js +10 -0
- package/dist/tools/pipelines.js +31 -9
- package/dist/tools/repositories.js +248 -207
- package/dist/tools/test-plans.js +6 -3
- package/dist/tools/wiki.js +30 -6
- package/dist/tools/work-items.js +48 -8
- package/dist/tools/work.js +1 -1
- package/dist/tools.js +0 -0
- package/dist/useragent.js +0 -0
- package/dist/utils.js +54 -0
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/dist/tools/builds.js +0 -271
- package/dist/tools/releases.js +0 -97
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
|
|
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. [๐
|
|
17
|
-
6. [๐ Using Domains](#-using-domains)
|
|
18
|
-
7. [
|
|
19
|
-
8. [
|
|
20
|
-
9. [
|
|
21
|
-
10. [
|
|
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
|
|
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
|
|
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 [
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
package/dist/org-tenants.js
CHANGED
|
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) {
|
package/dist/tools/pipelines.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
341
|
-
|
|
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
|
|
344
|
-
throw new Error("Invalid destinationPath:
|
|
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();
|