@azure-devops/mcp 0.1.0 โ 1.0.0
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 +58 -12
- package/dist/http.js +52 -0
- package/dist/index.js +2 -4
- package/dist/server.js +36 -0
- package/dist/tools/core.js +36 -12
- package/dist/tools/work.js +72 -32
- package/dist/tools/workitems.js +33 -26
- package/dist/tools.js +2 -0
- package/dist/version.js +1 -1
- package/package.json +6 -7
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Easily install the Azure DevOps MCP Server for VS Code or VS Code Insiders:
|
|
4
4
|
|
|
5
|
-
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%
|
|
6
|
-
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&quality=insiders&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%
|
|
5
|
+
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%40azure-devops%2Fmcp%22%2C%20%22%24%7Binput%3Aado_org%7D%22%5D%7D&inputs=%5B%7B%22id%22%3A%20%22ado_org%22%2C%20%22type%22%3A%20%22promptString%22%2C%20%22description%22%3A%20%22Azure%20DevOps%20organization%20name%20%20%28e.g.%20%27contoso%27%29%22%7D%5D)
|
|
6
|
+
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&quality=insiders&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%40azure-devops%2Fmcp%22%2C%20%22%24%7Binput%3Aado_org%7D%22%5D%7D&inputs=%5B%7B%22id%22%3A%20%22ado_org%22%2C%20%22type%22%3A%20%22promptString%22%2C%20%22description%22%3A%20%22Azure%20DevOps%20organization%20name%20%20%28e.g.%20%27contoso%27%29%22%7D%5D)
|
|
7
7
|
|
|
8
8
|
This TypeScript project defines the **local** MCP server for Azure DevOps, enabling you to perform a wide range of Azure DevOps tasks directly from your code editor.
|
|
9
9
|
|
|
@@ -17,7 +17,8 @@ This TypeScript project defines the **local** MCP server for Azure DevOps, enabl
|
|
|
17
17
|
4. [๐ฆ Usage](#-usage)
|
|
18
18
|
5. [๐ Troubleshooting](#-troubleshooting)
|
|
19
19
|
6. [๐ฉ Samples & best practices](#-samples--best-practices)
|
|
20
|
-
7. [
|
|
20
|
+
7. [๐โโ๏ธ Frequently asked questions](#๏ธ-frequently-asked-questions)
|
|
21
|
+
8. [๐ Contributing](#๏ธ-contributing)
|
|
21
22
|
|
|
22
23
|
## ๐บ Overview
|
|
23
24
|
|
|
@@ -129,7 +130,8 @@ For the best experience, use Visual Studio Code and GitHub Copilot.
|
|
|
129
130
|
|
|
130
131
|
1. Install [VS Code](https://code.visualstudio.com/download) or [VS Code Insiders](https://code.visualstudio.com/insiders)
|
|
131
132
|
2. Install [Node.js](https://nodejs.org/en/download) 20+
|
|
132
|
-
3.
|
|
133
|
+
3. Install [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest)
|
|
134
|
+
4. Open VS Code in an empty folder
|
|
133
135
|
|
|
134
136
|
### Azure Login
|
|
135
137
|
|
|
@@ -143,11 +145,51 @@ az login
|
|
|
143
145
|
|
|
144
146
|
#### โจ One-Click install
|
|
145
147
|
|
|
146
|
-
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%
|
|
147
|
-
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&quality=insiders&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%
|
|
148
|
+
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%40azure-devops%2Fmcp%22%2C%20%22%24%7Binput%3Aado_org%7D%22%5D%7D&inputs=%5B%7B%22id%22%3A%20%22ado_org%22%2C%20%22type%22%3A%20%22promptString%22%2C%20%22description%22%3A%20%22Azure%20DevOps%20organization%20name%20%20%28e.g.%20%27contoso%27%29%22%7D%5D)
|
|
149
|
+
[](https://insiders.vscode.dev/redirect/mcp/install?name=ado&quality=insiders&config=%7B%20%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%40azure-devops%2Fmcp%22%2C%20%22%24%7Binput%3Aado_org%7D%22%5D%7D&inputs=%5B%7B%22id%22%3A%20%22ado_org%22%2C%20%22type%22%3A%20%22promptString%22%2C%20%22description%22%3A%20%22Azure%20DevOps%20organization%20name%20%20%28e.g.%20%27contoso%27%29%22%7D%5D)
|
|
148
150
|
|
|
149
151
|
After installation, select GitHub Copilot Agent Mode and refresh the tools list. Learn more about Agent Mode in the [VS Code Documentation](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode).
|
|
150
152
|
|
|
153
|
+
#### ๐งจ Installing from public feed (recommended)
|
|
154
|
+
|
|
155
|
+
This installation method is the easiest for all users using Visual Studio Code.
|
|
156
|
+
|
|
157
|
+
๐ฅ [Watch this quick start video to get up and running in under two minutes!](https://youtu.be/EUmFM6qXoYk)
|
|
158
|
+
|
|
159
|
+
##### Steps
|
|
160
|
+
|
|
161
|
+
1. In your project, add a `.vscode\mcp.json` file and add the following:
|
|
162
|
+
|
|
163
|
+
``` json
|
|
164
|
+
{
|
|
165
|
+
"inputs": [
|
|
166
|
+
{
|
|
167
|
+
"id": "ado_org",
|
|
168
|
+
"type": "promptString",
|
|
169
|
+
"description": "Azure DevOps organization name (e.g. 'contoso')"
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"servers": {
|
|
173
|
+
"ado": {
|
|
174
|
+
"type": "stdio",
|
|
175
|
+
"command": "npx",
|
|
176
|
+
"args": [
|
|
177
|
+
"-y",
|
|
178
|
+
"@azure-devops/mcp",
|
|
179
|
+
"${input:ado_org}"
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
2. Save the file and click 'Start`
|
|
186
|
+
|
|
187
|
+
<img src="./docs/media/start-mcp-server.gif" alt="start mcp server" width="250"/>
|
|
188
|
+
|
|
189
|
+
3. In chat, switch to [Agent Mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode).
|
|
190
|
+
4. Click "Select Tools" and choose the available tools.
|
|
191
|
+
5. We strongly recommend that you create a `.github\copilot-instructions.md` in your project and copy and paste the contents from this [copilot-instructions.md](./.github/copilot-instructions.md) file. This will help your experience when it comes to using the Azure DevOps MCP Server in GitHub Copilot Chat.
|
|
192
|
+
|
|
151
193
|
#### ๐ ๏ธ Installing from source (dev mode)
|
|
152
194
|
|
|
153
195
|
This installation method is recommended for advanced users and contributors who want immediate access to the latest updates from the main branch. It is ideal if you are developing new tools, enhancing existing features, or maintaining a custom fork.
|
|
@@ -182,16 +224,16 @@ This installation method is recommended for advanced users and contributors who
|
|
|
182
224
|
}
|
|
183
225
|
```
|
|
184
226
|
|
|
185
|
-
4. Start the Azure DevOps MCP Server
|
|
227
|
+
4. Start the Azure DevOps MCP Server
|
|
228
|
+
|
|
229
|
+
<img src="./docs/media/start-mcp-server.gif" alt="start mcp server" width="250"/>
|
|
230
|
+
|
|
186
231
|
5. In chat, switch to [Agent Mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode).
|
|
187
|
-
6. Click "Select Tools" and choose the available
|
|
232
|
+
6. Click "Select Tools" and choose the available tools.
|
|
233
|
+
7. We strongly recommend that you create a `.github\copilot-instructions.md` in your project and copy and paste the contents from this [copilot-instructions.md](./.github/copilot-instructions.md) file. This will help your experience when it comes to using the Azure DevOps MCP Server in GitHub Copilot Chat.
|
|
188
234
|
|
|
189
235
|
See [How To](./docs/HOWTO.md) section for details
|
|
190
236
|
|
|
191
|
-
#### Placeholder for public feed
|
|
192
|
-
|
|
193
|
-
Update for Public Feed
|
|
194
|
-
|
|
195
237
|
## ๐ฆ Usage
|
|
196
238
|
|
|
197
239
|
### Visual Studio Code + GitHub Copilot
|
|
@@ -220,6 +262,10 @@ See the [Troubleshooting guide](./docs/TROUBLESHOOTING.md) for help with common
|
|
|
220
262
|
|
|
221
263
|
Find sample prompts and best practices in our [How-to Guide](./docs/HOWTO.md).
|
|
222
264
|
|
|
265
|
+
## ๐โโ๏ธ Frequently asked questions
|
|
266
|
+
|
|
267
|
+
For answers to common questions about the Azure DevOps MCP Server, see the [Frequently Asked Questions](./docs/FAQ.md).
|
|
268
|
+
|
|
223
269
|
## ๐ Contributing
|
|
224
270
|
|
|
225
271
|
We welcome contributions! During preview, please file Issues for bugs, enhancements, or documentation improvements.
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import express from "express";
|
|
4
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import { serverBuildAndConnect } from "./server.js";
|
|
6
|
+
import { packageVersion } from "./version.js";
|
|
7
|
+
const app = express();
|
|
8
|
+
app.use(express.json());
|
|
9
|
+
app.post('/mcp/:orgName', async (req, res) => {
|
|
10
|
+
// In stateless mode, create a new instance of transport and server for each request
|
|
11
|
+
// to ensure complete isolation. A single instance would cause request ID collisions
|
|
12
|
+
// when multiple clients connect concurrently.
|
|
13
|
+
try {
|
|
14
|
+
const transport = new StreamableHTTPServerTransport({
|
|
15
|
+
sessionIdGenerator: undefined,
|
|
16
|
+
});
|
|
17
|
+
const server = await serverBuildAndConnect(req.params.orgName, transport);
|
|
18
|
+
res.on('close', () => {
|
|
19
|
+
transport.close();
|
|
20
|
+
server.close();
|
|
21
|
+
});
|
|
22
|
+
await transport.handleRequest(req, res, req.body);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error('Error handling MCP request:', error);
|
|
26
|
+
if (!res.headersSent) {
|
|
27
|
+
res.status(500).json({
|
|
28
|
+
jsonrpc: '2.0',
|
|
29
|
+
error: {
|
|
30
|
+
code: -32603,
|
|
31
|
+
message: 'Internal server error',
|
|
32
|
+
},
|
|
33
|
+
id: null,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
app.get('/mcp/:orgName', async (req, res) => {
|
|
39
|
+
console.log('Received GET MCP request');
|
|
40
|
+
res.writeHead(405).end(JSON.stringify({
|
|
41
|
+
jsonrpc: "2.0",
|
|
42
|
+
error: {
|
|
43
|
+
code: -32000,
|
|
44
|
+
message: "Method not allowed."
|
|
45
|
+
},
|
|
46
|
+
id: null
|
|
47
|
+
}));
|
|
48
|
+
});
|
|
49
|
+
const PORT = 3000;
|
|
50
|
+
app.listen(PORT, () => {
|
|
51
|
+
console.log(`Azure DevOps MCP Server with http transport listening on port ${PORT}. Version: ${packageVersion}`);
|
|
52
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -33,17 +33,15 @@ async function getAzureDevOpsClient() {
|
|
|
33
33
|
return connection;
|
|
34
34
|
}
|
|
35
35
|
async function main() {
|
|
36
|
-
console.error("Starting Azure DevOps MCP Server...");
|
|
37
36
|
const server = new McpServer({
|
|
38
37
|
name: "Azure DevOps MCP Server",
|
|
39
|
-
version:
|
|
38
|
+
version: packageVersion,
|
|
40
39
|
});
|
|
41
40
|
configurePrompts(server);
|
|
42
41
|
configureAllTools(server, getAzureDevOpsToken, getAzureDevOpsClient);
|
|
43
42
|
const transport = new StdioServerTransport();
|
|
44
|
-
console.
|
|
43
|
+
console.log("Azure DevOps MCP Server version : " + packageVersion);
|
|
45
44
|
await server.connect(transport);
|
|
46
|
-
console.error("Azure DevOps MCP Server running on stdio");
|
|
47
45
|
}
|
|
48
46
|
main().catch((error) => {
|
|
49
47
|
console.error("Fatal error in main():", error);
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import * as azdev from "azure-devops-node-api";
|
|
5
|
+
import { DefaultAzureCredential } from "@azure/identity";
|
|
6
|
+
import { configurePrompts } from "./prompts.js";
|
|
7
|
+
import { configureAllTools } from "./tools.js";
|
|
8
|
+
import { userAgent } from "./utils.js";
|
|
9
|
+
import { packageVersion } from "./version.js";
|
|
10
|
+
async function getAzureDevOpsToken() {
|
|
11
|
+
process.env.AZURE_TOKEN_CREDENTIALS = "dev";
|
|
12
|
+
const credential = new DefaultAzureCredential(); // CodeQL [SM05138] resolved by explicitly setting AZURE_TOKEN_CREDENTIALS
|
|
13
|
+
const token = await credential.getToken("499b84ac-1321-427f-aa17-267ca6975798/.default");
|
|
14
|
+
return token;
|
|
15
|
+
}
|
|
16
|
+
async function getAzureDevOpsClient(orgUrl) {
|
|
17
|
+
const token = await getAzureDevOpsToken();
|
|
18
|
+
const authHandler = azdev.getBearerHandler(token.token);
|
|
19
|
+
const connection = new azdev.WebApi(orgUrl, authHandler, undefined, {
|
|
20
|
+
productName: "AzureDevOps.MCP",
|
|
21
|
+
productVersion: packageVersion,
|
|
22
|
+
userAgent: userAgent
|
|
23
|
+
});
|
|
24
|
+
return connection;
|
|
25
|
+
}
|
|
26
|
+
export async function serverBuildAndConnect(orgName, transport) {
|
|
27
|
+
const server = new McpServer({
|
|
28
|
+
name: "Azure DevOps MCP Server",
|
|
29
|
+
version: packageVersion,
|
|
30
|
+
});
|
|
31
|
+
const orgUrl = "https://dev.azure.com/" + orgName;
|
|
32
|
+
configurePrompts(server);
|
|
33
|
+
configureAllTools(server, () => orgName, getAzureDevOpsToken, () => getAzureDevOpsClient(orgUrl));
|
|
34
|
+
await server.connect(transport);
|
|
35
|
+
return server;
|
|
36
|
+
}
|
package/dist/tools/core.js
CHANGED
|
@@ -12,12 +12,24 @@ function configureCoreTools(server, tokenProvider, connectionProvider) {
|
|
|
12
12
|
top: z.number().optional().describe("The maximum number of teams to return. Defaults to 100."),
|
|
13
13
|
skip: z.number().optional().describe("The number of teams to skip for pagination. Defaults to 0."),
|
|
14
14
|
}, async ({ project, mine, top, skip }) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
try {
|
|
16
|
+
const connection = await connectionProvider();
|
|
17
|
+
const coreApi = await connection.getCoreApi();
|
|
18
|
+
const teams = await coreApi.getTeams(project, mine, top, skip, false);
|
|
19
|
+
if (!teams) {
|
|
20
|
+
return { content: [{ type: "text", text: "No teams found" }], isError: true };
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: JSON.stringify(teams, null, 2) }],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: `Error fetching project teams: ${errorMessage}` }],
|
|
30
|
+
isError: true
|
|
31
|
+
};
|
|
32
|
+
}
|
|
21
33
|
});
|
|
22
34
|
server.tool(CORE_TOOLS.list_projects, "Retrieve a list of projects in your Azure DevOps organization.", {
|
|
23
35
|
stateFilter: z.enum(["all", "wellFormed", "createPending", "deleted"]).default("wellFormed").describe("Filter projects by their state. Defaults to 'wellFormed'."),
|
|
@@ -25,12 +37,24 @@ function configureCoreTools(server, tokenProvider, connectionProvider) {
|
|
|
25
37
|
skip: z.number().optional().describe("The number of projects to skip for pagination. Defaults to 0."),
|
|
26
38
|
continuationToken: z.number().optional().describe("Continuation token for pagination. Used to fetch the next set of results if available."),
|
|
27
39
|
}, async ({ stateFilter, top, skip, continuationToken }) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
try {
|
|
41
|
+
const connection = await connectionProvider();
|
|
42
|
+
const coreApi = await connection.getCoreApi();
|
|
43
|
+
const projects = await coreApi.getProjects(stateFilter, top, skip, continuationToken, false);
|
|
44
|
+
if (!projects) {
|
|
45
|
+
return { content: [{ type: "text", text: "No projects found" }], isError: true };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: "text", text: `Error fetching projects: ${errorMessage}` }],
|
|
55
|
+
isError: true
|
|
56
|
+
};
|
|
57
|
+
}
|
|
34
58
|
});
|
|
35
59
|
}
|
|
36
60
|
export { CORE_TOOLS, configureCoreTools };
|
package/dist/tools/work.js
CHANGED
|
@@ -13,12 +13,24 @@ function configureWorkTools(server, tokenProvider, connectionProvider) {
|
|
|
13
13
|
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
14
14
|
timeframe: z.enum(["current"]).optional().describe("The timeframe for which to retrieve iterations. Currently, only 'current' is supported."),
|
|
15
15
|
}, async ({ project, team, timeframe }) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
try {
|
|
17
|
+
const connection = await connectionProvider();
|
|
18
|
+
const workApi = await connection.getWorkApi();
|
|
19
|
+
const iterations = await workApi.getTeamIterations({ project, team }, timeframe);
|
|
20
|
+
if (!iterations) {
|
|
21
|
+
return { content: [{ type: "text", text: "No iterations found" }], isError: true };
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify(iterations, null, 2) }],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: `Error fetching team iterations: ${errorMessage}` }],
|
|
31
|
+
isError: true
|
|
32
|
+
};
|
|
33
|
+
}
|
|
22
34
|
});
|
|
23
35
|
server.tool(WORK_TOOLS.create_iterations, "Create new iterations in a specified Azure DevOps project.", {
|
|
24
36
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -28,23 +40,37 @@ function configureWorkTools(server, tokenProvider, connectionProvider) {
|
|
|
28
40
|
finishDate: z.string().optional().describe("The finish date of the iteration in ISO format (e.g., '2023-01-31T23:59:59Z'). Optional.")
|
|
29
41
|
})).describe("An array of iterations to create. Each iteration must have a name and can optionally have start and finish dates in ISO format.")
|
|
30
42
|
}, async ({ project, iterations }) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
try {
|
|
44
|
+
const connection = await connectionProvider();
|
|
45
|
+
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
46
|
+
const results = [];
|
|
47
|
+
for (const { iterationName, startDate, finishDate } of iterations) {
|
|
48
|
+
// Step 1: Create the iteration
|
|
49
|
+
const iteration = await workItemTrackingApi.createOrUpdateClassificationNode({
|
|
50
|
+
name: iterationName,
|
|
51
|
+
attributes: {
|
|
52
|
+
startDate: startDate ? new Date(startDate) : undefined,
|
|
53
|
+
finishDate: finishDate ? new Date(finishDate) : undefined,
|
|
54
|
+
},
|
|
55
|
+
}, project, TreeStructureGroup.Iterations);
|
|
56
|
+
if (iteration) {
|
|
57
|
+
results.push(iteration);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (results.length === 0) {
|
|
61
|
+
return { content: [{ type: "text", text: "No iterations were created" }], isError: true };
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: "text", text: `Error creating iterations: ${errorMessage}` }],
|
|
71
|
+
isError: true
|
|
72
|
+
};
|
|
44
73
|
}
|
|
45
|
-
return {
|
|
46
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
47
|
-
};
|
|
48
74
|
});
|
|
49
75
|
server.tool(WORK_TOOLS.assign_iterations, "Assign existing iterations to a specific team in a project.", {
|
|
50
76
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -54,17 +80,31 @@ function configureWorkTools(server, tokenProvider, connectionProvider) {
|
|
|
54
80
|
path: z.string().describe("The path of the iteration to assign, e.g., 'Project/Iteration'.")
|
|
55
81
|
})).describe("An array of iterations to assign. Each iteration must have an identifier and a path."),
|
|
56
82
|
}, async ({ project, team, iterations }) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
83
|
+
try {
|
|
84
|
+
const connection = await connectionProvider();
|
|
85
|
+
const workApi = await connection.getWorkApi();
|
|
86
|
+
const teamContext = { project, team };
|
|
87
|
+
const results = [];
|
|
88
|
+
for (const { identifier, path } of iterations) {
|
|
89
|
+
const assignment = await workApi.postTeamIteration({ path: path, id: identifier }, teamContext);
|
|
90
|
+
if (assignment) {
|
|
91
|
+
results.push(assignment);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (results.length === 0) {
|
|
95
|
+
return { content: [{ type: "text", text: "No iterations were assigned to the team" }], isError: true };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: "text", text: `Error assigning iterations: ${errorMessage}` }],
|
|
105
|
+
isError: true
|
|
106
|
+
};
|
|
64
107
|
}
|
|
65
|
-
return {
|
|
66
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
67
|
-
};
|
|
68
108
|
});
|
|
69
109
|
}
|
|
70
110
|
export { WORK_TOOLS, configureWorkTools };
|
package/dist/tools/workitems.js
CHANGED
|
@@ -194,14 +194,14 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
194
194
|
};
|
|
195
195
|
});
|
|
196
196
|
server.tool(WORKITEM_TOOLS.link_work_item_to_pull_request, "Link a single work item to an existing pull request.", {
|
|
197
|
-
project: z.string().describe,
|
|
197
|
+
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
198
198
|
repositoryId: z.string().describe("The ID of the repository containing the pull request. Do not use the repository name here, use the ID instead."),
|
|
199
199
|
pullRequestId: z.number().describe("The ID of the pull request to link to."),
|
|
200
200
|
workItemId: z.number().describe("The ID of the work item to link to the pull request."),
|
|
201
201
|
}, async ({ project, repositoryId, pullRequestId, workItemId }) => {
|
|
202
|
-
const connection = await connectionProvider();
|
|
203
|
-
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
204
202
|
try {
|
|
203
|
+
const connection = await connectionProvider();
|
|
204
|
+
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
205
205
|
// Create artifact link relation using vstfs format
|
|
206
206
|
// Format: vstfs:///Git/PullRequestId/{project}/{repositoryId}/{pullRequestId}
|
|
207
207
|
const artifactPathValue = `${project}/${repositoryId}/${pullRequestId}`;
|
|
@@ -221,7 +221,10 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
221
221
|
},
|
|
222
222
|
];
|
|
223
223
|
// Use the WorkItem API to update the work item with the new relation
|
|
224
|
-
await workItemTrackingApi.updateWorkItem({}, patchDocument, workItemId, project);
|
|
224
|
+
const workItem = await workItemTrackingApi.updateWorkItem({}, patchDocument, workItemId, project);
|
|
225
|
+
if (!workItem) {
|
|
226
|
+
return { content: [{ type: "text", text: "Work item update failed" }], isError: true };
|
|
227
|
+
}
|
|
225
228
|
return {
|
|
226
229
|
content: [
|
|
227
230
|
{
|
|
@@ -236,18 +239,10 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
236
239
|
};
|
|
237
240
|
}
|
|
238
241
|
catch (error) {
|
|
239
|
-
|
|
242
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
240
243
|
return {
|
|
241
|
-
content: [
|
|
242
|
-
|
|
243
|
-
type: "text",
|
|
244
|
-
text: JSON.stringify({
|
|
245
|
-
workItemId,
|
|
246
|
-
pullRequestId,
|
|
247
|
-
success: false,
|
|
248
|
-
}, null, 2),
|
|
249
|
-
},
|
|
250
|
-
],
|
|
244
|
+
content: [{ type: "text", text: `Error linking work item to pull request: ${errorMessage}` }],
|
|
245
|
+
isError: true
|
|
251
246
|
};
|
|
252
247
|
}
|
|
253
248
|
});
|
|
@@ -299,17 +294,29 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
299
294
|
workItemType: z.string().describe("The type of work item to create, e.g., 'Task', 'Bug', etc."),
|
|
300
295
|
fields: z.record(z.string(), z.string()).describe("A record of field names and values to set on the new work item. Each key is a field name, and each value is the corresponding value to set for that field."),
|
|
301
296
|
}, async ({ project, workItemType, fields }) => {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
297
|
+
try {
|
|
298
|
+
const connection = await connectionProvider();
|
|
299
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
300
|
+
const document = Object.entries(fields).map(([key, value]) => ({
|
|
301
|
+
op: "add",
|
|
302
|
+
path: `/fields/${key}`,
|
|
303
|
+
value,
|
|
304
|
+
}));
|
|
305
|
+
const newWorkItem = await workItemApi.createWorkItem(null, document, project, workItemType);
|
|
306
|
+
if (!newWorkItem) {
|
|
307
|
+
return { content: [{ type: "text", text: "Work item was not created" }], isError: true };
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
content: [{ type: "text", text: JSON.stringify(newWorkItem, null, 2) }],
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
315
|
+
return {
|
|
316
|
+
content: [{ type: "text", text: `Error creating work item: ${errorMessage}` }],
|
|
317
|
+
isError: true
|
|
318
|
+
};
|
|
319
|
+
}
|
|
313
320
|
});
|
|
314
321
|
server.tool(WORKITEM_TOOLS.get_query, "Get a query by its ID or path.", {
|
|
315
322
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
package/dist/tools.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import { configureCoreTools } from "./tools/core.js";
|
|
4
|
+
import { configureWorkTools } from "./tools/work.js";
|
|
4
5
|
import { configureBuildTools } from "./tools/builds.js";
|
|
5
6
|
import { configureRepoTools } from "./tools/repos.js";
|
|
6
7
|
import { configureWorkItemTools } from "./tools/workitems.js";
|
|
@@ -10,6 +11,7 @@ import { configureTestPlanTools } from "./tools/testplans.js";
|
|
|
10
11
|
import { configureSearchTools } from "./tools/search.js";
|
|
11
12
|
function configureAllTools(server, tokenProvider, connectionProvider) {
|
|
12
13
|
configureCoreTools(server, tokenProvider, connectionProvider);
|
|
14
|
+
configureWorkTools(server, tokenProvider, connectionProvider);
|
|
13
15
|
configureBuildTools(server, tokenProvider, connectionProvider);
|
|
14
16
|
configureRepoTools(server, tokenProvider, connectionProvider);
|
|
15
17
|
configureWorkItemTools(server, tokenProvider, connectionProvider);
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "
|
|
1
|
+
export const packageVersion = "1.0.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-devops/mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "MCP server for interacting with Azure DevOps",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Microsoft Corporation",
|
|
@@ -31,23 +31,22 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@azure/identity": "^4.10.0",
|
|
34
|
-
"@modelcontextprotocol/sdk": "1.
|
|
34
|
+
"@modelcontextprotocol/sdk": "1.13.0",
|
|
35
35
|
"azure-devops-extension-api": "^4.252.0",
|
|
36
36
|
"azure-devops-extension-sdk": "^4.0.2",
|
|
37
37
|
"azure-devops-node-api": "^15.1.0",
|
|
38
|
-
"
|
|
39
|
-
"zod": "^3.25.55",
|
|
38
|
+
"zod": "^3.25.63",
|
|
40
39
|
"zod-to-json-schema": "^3.24.5"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"@modelcontextprotocol/inspector": "^0.14.0",
|
|
44
43
|
"@types/node": "^22",
|
|
45
|
-
"@types/jest": "^
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
46
45
|
"eslint-plugin-header": "^3.1.1",
|
|
47
|
-
"jest": "^
|
|
46
|
+
"jest": "^30.0.2",
|
|
48
47
|
"jest-extended": "^6.0.0",
|
|
49
48
|
"shx": "^0.4.0",
|
|
50
|
-
"ts-jest": "^29.
|
|
49
|
+
"ts-jest": "^29.4.0",
|
|
51
50
|
"tsconfig-paths": "^4.2.0",
|
|
52
51
|
"typescript": "^5.8.3",
|
|
53
52
|
"typescript-eslint": "^8.32.1"
|