@radholm/azure-devops-mcp 1.0.0-beta.2 → 1.0.0-beta.3
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 +33 -19
- package/dist/auth.js +5 -5
- package/dist/index.js +30 -12
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -124,13 +124,13 @@ In your project, add a `.vscode\mcp.json` file with the following content:
|
|
|
124
124
|
"ado": {
|
|
125
125
|
"type": "stdio",
|
|
126
126
|
"command": "npx",
|
|
127
|
-
"args": ["-y", "@azure-devops
|
|
127
|
+
"args": ["-y", "@radholm/azure-devops-mcp", "${input:ado_org}"]
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
🔥 To stay up to date with the latest features, you can use our nightly builds. Simply update your `mcp.json` configuration to use `@azure-devops
|
|
133
|
+
🔥 To stay up to date with the latest features, you can use our nightly builds. Simply update your `mcp.json` configuration to use `@radholm/azure-devops-mcp@next`. Here is an updated example:
|
|
134
134
|
|
|
135
135
|
```json
|
|
136
136
|
{
|
|
@@ -145,7 +145,7 @@ In your project, add a `.vscode\mcp.json` file with the following content:
|
|
|
145
145
|
"ado": {
|
|
146
146
|
"type": "stdio",
|
|
147
147
|
"command": "npx",
|
|
148
|
-
"args": ["-y", "@azure-devops
|
|
148
|
+
"args": ["-y", "@radholm/azure-devops-mcp@next", "${input:ado_org}"]
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
}
|
|
@@ -187,7 +187,7 @@ For example, use `"-d", "core", "work", "work-items"` to load only Work Item rel
|
|
|
187
187
|
"ado_with_filtered_domains": {
|
|
188
188
|
"type": "stdio",
|
|
189
189
|
"command": "npx",
|
|
190
|
-
"args": ["-y", "@azure-devops
|
|
190
|
+
"args": ["-y", "@radholm/azure-devops-mcp", "${input:ado_org}", "-d", "core", "work", "work-items"]
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
}
|
|
@@ -211,7 +211,7 @@ You can also configure default Azure DevOps project and team values from `.vscod
|
|
|
211
211
|
"ado": {
|
|
212
212
|
"type": "stdio",
|
|
213
213
|
"command": "npx",
|
|
214
|
-
"args": ["-y", "@azure-devops
|
|
214
|
+
"args": ["-y", "@radholm/azure-devops-mcp", "myorg", "--authentication", "azcli"],
|
|
215
215
|
"env": {
|
|
216
216
|
"ado_mcp_project": "Contoso",
|
|
217
217
|
"ado_mcp_team": "Fabrikam Team"
|
|
@@ -231,35 +231,49 @@ The local MCP server supports connecting to on-premises Azure DevOps Server (TFS
|
|
|
231
231
|
|
|
232
232
|
### Configuration
|
|
233
233
|
|
|
234
|
-
Use the `--base-url` (or `-b`) option to point to your on-prem server, or set the `AZURE_DEVOPS_BASE_URL` environment variable. The `organization` positional argument is
|
|
234
|
+
Use the `--base-url` (or `-b`) option to point to your on-prem server, or set the `AZURE_DEVOPS_BASE_URL` environment variable. The `organization` positional argument is **optional** when `--base-url` is provided — the server will automatically derive the organization/collection name from the last path segment of the URL.
|
|
235
235
|
|
|
236
236
|
> **Note:** Only `pat` and `envvar` authentication are supported for on-premises servers.
|
|
237
237
|
|
|
238
|
+
### Authentication
|
|
239
|
+
|
|
240
|
+
Set `PERSONAL_ACCESS_TOKEN` to your **raw** Personal Access Token (the token string as generated by Azure DevOps). The server handles encoding internally.
|
|
241
|
+
|
|
238
242
|
### Example `.vscode/mcp.json`
|
|
239
243
|
|
|
240
244
|
```json
|
|
241
245
|
{
|
|
242
|
-
"
|
|
243
|
-
{
|
|
244
|
-
"
|
|
245
|
-
"
|
|
246
|
-
"
|
|
246
|
+
"servers": {
|
|
247
|
+
"ado-onprem": {
|
|
248
|
+
"type": "stdio",
|
|
249
|
+
"command": "npx",
|
|
250
|
+
"args": ["-y", "@radholm/azure-devops-mcp", "--base-url", "https://azuredo.example.com/tfs/MyCollection", "--authentication", "pat"],
|
|
251
|
+
"env": {
|
|
252
|
+
"PERSONAL_ACCESS_TOKEN": "<your raw PAT>"
|
|
253
|
+
}
|
|
247
254
|
}
|
|
248
|
-
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
You can still provide the organization explicitly if needed:
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
249
263
|
"servers": {
|
|
250
264
|
"ado-onprem": {
|
|
251
265
|
"type": "stdio",
|
|
252
266
|
"command": "npx",
|
|
253
|
-
"args": ["-y", "@azure-devops
|
|
267
|
+
"args": ["-y", "@radholm/azure-devops-mcp", "MyCollection", "--base-url", "https://azuredo.example.com/tfs/MyCollection", "--authentication", "pat"],
|
|
254
268
|
"env": {
|
|
255
|
-
"PERSONAL_ACCESS_TOKEN": "<
|
|
269
|
+
"PERSONAL_ACCESS_TOKEN": "<your raw PAT>"
|
|
256
270
|
}
|
|
257
271
|
}
|
|
258
272
|
}
|
|
259
273
|
}
|
|
260
274
|
```
|
|
261
275
|
|
|
262
|
-
Alternatively, use the `AZURE_DEVOPS_BASE_URL` environment variable
|
|
276
|
+
Alternatively, use the `AZURE_DEVOPS_BASE_URL` environment variable instead of `--base-url`:
|
|
263
277
|
|
|
264
278
|
```json
|
|
265
279
|
{
|
|
@@ -267,10 +281,10 @@ Alternatively, use the `AZURE_DEVOPS_BASE_URL` environment variable:
|
|
|
267
281
|
"ado-onprem": {
|
|
268
282
|
"type": "stdio",
|
|
269
283
|
"command": "npx",
|
|
270
|
-
"args": ["-y", "@azure-devops
|
|
284
|
+
"args": ["-y", "@radholm/azure-devops-mcp", "--authentication", "pat"],
|
|
271
285
|
"env": {
|
|
272
286
|
"AZURE_DEVOPS_BASE_URL": "https://azuredo.example.com/tfs/MyCollection",
|
|
273
|
-
"PERSONAL_ACCESS_TOKEN": "<
|
|
287
|
+
"PERSONAL_ACCESS_TOKEN": "<your raw PAT>"
|
|
274
288
|
}
|
|
275
289
|
}
|
|
276
290
|
}
|
|
@@ -299,9 +313,9 @@ Example:
|
|
|
299
313
|
"ado-onprem": {
|
|
300
314
|
"type": "stdio",
|
|
301
315
|
"command": "npx",
|
|
302
|
-
"args": ["-y", "@azure-devops
|
|
316
|
+
"args": ["-y", "@radholm/azure-devops-mcp", "--base-url", "https://azuredo.example.com/tfs/MyCollection", "--authentication", "pat"],
|
|
303
317
|
"env": {
|
|
304
|
-
"PERSONAL_ACCESS_TOKEN": "<
|
|
318
|
+
"PERSONAL_ACCESS_TOKEN": "<your raw PAT>",
|
|
305
319
|
"AZURE_DEVOPS_API_VERSION": "7.1"
|
|
306
320
|
}
|
|
307
321
|
}
|
package/dist/auth.js
CHANGED
|
@@ -75,14 +75,14 @@ function createAuthenticator(type, tenantId) {
|
|
|
75
75
|
logger.debug(`Authenticator: Using PAT authentication (PERSONAL_ACCESS_TOKEN)`);
|
|
76
76
|
return async () => {
|
|
77
77
|
logger.debug(`${type}: Reading token from PERSONAL_ACCESS_TOKEN environment variable`);
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
78
|
+
const pat = process.env["PERSONAL_ACCESS_TOKEN"];
|
|
79
|
+
if (!pat) {
|
|
80
80
|
logger.error(`${type}: PERSONAL_ACCESS_TOKEN environment variable is not set or empty`);
|
|
81
|
-
throw new Error("Environment variable 'PERSONAL_ACCESS_TOKEN' is not set or empty. Please set it with a valid
|
|
81
|
+
throw new Error("Environment variable 'PERSONAL_ACCESS_TOKEN' is not set or empty. Please set it with a valid Azure DevOps Personal Access Token.");
|
|
82
82
|
}
|
|
83
|
-
// Return
|
|
83
|
+
// Return the raw PAT value as-is. The caller handles encoding for Basic auth.
|
|
84
84
|
logger.debug(`${type}: Successfully retrieved PAT from environment variable`);
|
|
85
|
-
return
|
|
85
|
+
return pat;
|
|
86
86
|
};
|
|
87
87
|
case "envvar":
|
|
88
88
|
logger.debug(`Authenticator: Using environment variable authentication (ADO_MCP_AUTH_TOKEN)`);
|
package/dist/index.js
CHANGED
|
@@ -18,16 +18,28 @@ function isGitHubCodespaceEnv() {
|
|
|
18
18
|
return process.env.CODESPACES === "true" && !!process.env.CODESPACE_NAME;
|
|
19
19
|
}
|
|
20
20
|
const defaultAuthenticationType = isGitHubCodespaceEnv() ? "azcli" : "interactive";
|
|
21
|
+
/**
|
|
22
|
+
* Extract the organization/collection name from a base URL.
|
|
23
|
+
* E.g. "https://azuredo.lfnet.se/tfs/Lansforsakringar" -> "Lansforsakringar"
|
|
24
|
+
* "https://dev.azure.com/MyOrg" -> "MyOrg"
|
|
25
|
+
*/
|
|
26
|
+
function extractOrgFromUrl(url) {
|
|
27
|
+
const pathname = new URL(url).pathname.replace(/\/+$/, "");
|
|
28
|
+
const lastSegment = pathname.split("/").pop();
|
|
29
|
+
if (!lastSegment) {
|
|
30
|
+
throw new Error(`Cannot derive organization name from base URL '${url}'. Please provide the organization positional argument.`);
|
|
31
|
+
}
|
|
32
|
+
return lastSegment;
|
|
33
|
+
}
|
|
21
34
|
// Parse command line arguments using yargs
|
|
22
35
|
const argv = yargs(hideBin(process.argv))
|
|
23
|
-
.scriptName("
|
|
24
|
-
.usage("Usage: $0
|
|
36
|
+
.scriptName("azure-devops-mcp")
|
|
37
|
+
.usage("Usage: $0 [organization] [options]")
|
|
25
38
|
.version(packageVersion)
|
|
26
|
-
.command("$0
|
|
39
|
+
.command("$0 [organization] [options]", "Azure DevOps MCP Server", (yargs) => {
|
|
27
40
|
yargs.positional("organization", {
|
|
28
|
-
describe: "Azure DevOps organization name",
|
|
41
|
+
describe: "Azure DevOps organization name (optional if --base-url is provided)",
|
|
29
42
|
type: "string",
|
|
30
|
-
demandOption: true,
|
|
31
43
|
});
|
|
32
44
|
})
|
|
33
45
|
.option("domains", {
|
|
@@ -53,11 +65,17 @@ const argv = yargs(hideBin(process.argv))
|
|
|
53
65
|
alias: "b",
|
|
54
66
|
describe: "Base URL for Azure DevOps Server (on-prem). E.g. 'https://azuredo.example.com/tfs/MyCollection'. Defaults to 'https://dev.azure.com/{organization}'.",
|
|
55
67
|
type: "string",
|
|
68
|
+
})
|
|
69
|
+
.check((argv) => {
|
|
70
|
+
if (!argv.organization && !argv.baseUrl && !process.env.AZURE_DEVOPS_BASE_URL) {
|
|
71
|
+
throw new Error("Either <organization> or --base-url (or AZURE_DEVOPS_BASE_URL env var) must be provided.");
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
56
74
|
})
|
|
57
75
|
.help()
|
|
58
76
|
.parseSync();
|
|
59
|
-
export const
|
|
60
|
-
export const
|
|
77
|
+
export const orgUrl = argv.baseUrl ?? process.env.AZURE_DEVOPS_BASE_URL ?? "https://dev.azure.com/" + argv.organization;
|
|
78
|
+
export const orgName = argv.organization ?? extractOrgFromUrl(orgUrl);
|
|
61
79
|
/**
|
|
62
80
|
* Whether the server is connecting to an on-premises Azure DevOps Server instance
|
|
63
81
|
* (i.e., not the cloud-hosted dev.azure.com service).
|
|
@@ -68,9 +86,8 @@ export const enabledDomains = domainsManager.getEnabledDomains();
|
|
|
68
86
|
function getAzureDevOpsClient(getAzureDevOpsToken, userAgentComposer, authType) {
|
|
69
87
|
return async () => {
|
|
70
88
|
const accessToken = await getAzureDevOpsToken();
|
|
71
|
-
// For pat, accessToken is
|
|
72
|
-
|
|
73
|
-
const authHandler = authType === "pat" ? getPersonalAccessTokenHandler(Buffer.from(accessToken, "base64").toString("utf8").split(":").slice(1).join(":")) : getBearerHandler(accessToken);
|
|
89
|
+
// For pat, accessToken is the raw PAT token
|
|
90
|
+
const authHandler = authType === "pat" ? getPersonalAccessTokenHandler(accessToken) : getBearerHandler(accessToken);
|
|
74
91
|
const connection = new WebApi(orgUrl, authHandler, undefined, {
|
|
75
92
|
productName: "AzureDevOps.MCP",
|
|
76
93
|
productVersion: packageVersion,
|
|
@@ -112,8 +129,9 @@ async function main() {
|
|
|
112
129
|
const tenantId = isOnPrem ? argv.tenant : ((await getOrgTenant(orgName)) ?? argv.tenant);
|
|
113
130
|
const authenticator = createAuthenticator(argv.authentication, tenantId);
|
|
114
131
|
if (argv.authentication === "pat") {
|
|
115
|
-
const
|
|
116
|
-
//
|
|
132
|
+
const rawPat = await authenticator();
|
|
133
|
+
// Build the Basic auth value from the raw PAT: base64(":token")
|
|
134
|
+
const basicValue = Buffer.from(`:${rawPat}`).toString("base64");
|
|
117
135
|
const _originalFetch = globalThis.fetch;
|
|
118
136
|
globalThis.fetch = async (input, init) => {
|
|
119
137
|
if (init?.headers) {
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "1.0.0-beta.
|
|
1
|
+
export const packageVersion = "1.0.0-beta.3";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radholm/azure-devops-mcp",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"description": "MCP server for interacting with Azure DevOps",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Fredrik Radholm",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"type": "module",
|
|
14
14
|
"bin": {
|
|
15
|
-
"
|
|
15
|
+
"azure-devops-mcp": "dist/index.js"
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist"
|