@azure-devops/mcp 2.1.0-nightly.20250921 → 2.2.0-nightly.20250922
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 +2 -11
- package/dist/auth.js +74 -0
- package/dist/index.js +20 -26
- package/dist/org-tenants.js +73 -0
- package/dist/shared/domains.js +9 -2
- package/dist/tools/advanced-security.js +1 -1
- package/dist/tools/auth.js +2 -2
- package/dist/tools/core.js +1 -1
- package/dist/tools/pipelines.js +1 -1
- package/dist/tools/search.js +3 -3
- package/dist/tools/test-plans.js +1 -1
- package/dist/tools/wiki.js +12 -10
- package/dist/tools/work-items.js +4 -4
- package/dist/tools/work.js +1 -1
- package/dist/tools.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -152,16 +152,7 @@ For the best experience, use Visual Studio Code and GitHub Copilot. See the [get
|
|
|
152
152
|
|
|
153
153
|
1. Install [VS Code](https://code.visualstudio.com/download) or [VS Code Insiders](https://code.visualstudio.com/insiders)
|
|
154
154
|
2. Install [Node.js](https://nodejs.org/en/download) 20+
|
|
155
|
-
3.
|
|
156
|
-
4. Open VS Code in an empty folder
|
|
157
|
-
|
|
158
|
-
### Azure Login
|
|
159
|
-
|
|
160
|
-
Ensure you are logged in to Azure DevOps via the Azure CLI:
|
|
161
|
-
|
|
162
|
-
```sh
|
|
163
|
-
az login
|
|
164
|
-
```
|
|
155
|
+
3. Open VS Code in an empty folder
|
|
165
156
|
|
|
166
157
|
### Installation
|
|
167
158
|
|
|
@@ -232,7 +223,7 @@ Click "Select Tools" and choose the available tools.
|
|
|
232
223
|
|
|
233
224
|

|
|
234
225
|
|
|
235
|
-
Open GitHub Copilot Chat and try a prompt like `List ADO projects`.
|
|
226
|
+
Open GitHub Copilot Chat and try a prompt like `List ADO projects`. The first time an ADO tool is executed browser will open prompting to login with your Microsoft account. Please ensure you are using credentials matching selected Azure DevOps organization.
|
|
236
227
|
|
|
237
228
|
> 💥 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.
|
|
238
229
|
> 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.
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { AzureCliCredential, ChainedTokenCredential, DefaultAzureCredential } from "@azure/identity";
|
|
2
|
+
import { PublicClientApplication } from "@azure/msal-node";
|
|
3
|
+
import open from "open";
|
|
4
|
+
const scopes = ["499b84ac-1321-427f-aa17-267ca6975798/.default"];
|
|
5
|
+
class OAuthAuthenticator {
|
|
6
|
+
static clientId = "0d50963b-7bb9-4fe7-94c7-a99af00b5136";
|
|
7
|
+
static defaultAuthority = "https://login.microsoftonline.com/common";
|
|
8
|
+
accountId;
|
|
9
|
+
publicClientApp;
|
|
10
|
+
constructor(tenantId) {
|
|
11
|
+
this.accountId = null;
|
|
12
|
+
this.publicClientApp = new PublicClientApplication({
|
|
13
|
+
auth: {
|
|
14
|
+
clientId: OAuthAuthenticator.clientId,
|
|
15
|
+
authority: tenantId ? `https://login.microsoftonline.com/${tenantId}` : OAuthAuthenticator.defaultAuthority,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async getToken() {
|
|
20
|
+
let authResult = null;
|
|
21
|
+
if (this.accountId) {
|
|
22
|
+
try {
|
|
23
|
+
authResult = await this.publicClientApp.acquireTokenSilent({
|
|
24
|
+
scopes,
|
|
25
|
+
account: this.accountId,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
authResult = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!authResult) {
|
|
33
|
+
authResult = await this.publicClientApp.acquireTokenInteractive({
|
|
34
|
+
scopes,
|
|
35
|
+
openBrowser: async (url) => {
|
|
36
|
+
open(url);
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
this.accountId = authResult.account;
|
|
40
|
+
}
|
|
41
|
+
if (!authResult.accessToken) {
|
|
42
|
+
throw new Error("Failed to obtain Azure DevOps OAuth token.");
|
|
43
|
+
}
|
|
44
|
+
return authResult.accessToken;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function createAuthenticator(type, tenantId) {
|
|
48
|
+
switch (type) {
|
|
49
|
+
case "azcli":
|
|
50
|
+
case "env":
|
|
51
|
+
if (type !== "env") {
|
|
52
|
+
process.env.AZURE_TOKEN_CREDENTIALS = "dev";
|
|
53
|
+
}
|
|
54
|
+
let credential = new DefaultAzureCredential(); // CodeQL [SM05138] resolved by explicitly setting AZURE_TOKEN_CREDENTIALS
|
|
55
|
+
if (tenantId) {
|
|
56
|
+
// Use Azure CLI credential if tenantId is provided for multi-tenant scenarios
|
|
57
|
+
const azureCliCredential = new AzureCliCredential({ tenantId });
|
|
58
|
+
credential = new ChainedTokenCredential(azureCliCredential, credential);
|
|
59
|
+
}
|
|
60
|
+
return async () => {
|
|
61
|
+
const result = await credential.getToken(scopes);
|
|
62
|
+
if (!result) {
|
|
63
|
+
throw new Error("Failed to obtain Azure DevOps token. Ensure you have Azure CLI logged or use interactive type of authentication.");
|
|
64
|
+
}
|
|
65
|
+
return result.token;
|
|
66
|
+
};
|
|
67
|
+
default:
|
|
68
|
+
const authenticator = new OAuthAuthenticator(tenantId);
|
|
69
|
+
return () => {
|
|
70
|
+
return authenticator.getToken();
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export { createAuthenticator };
|
package/dist/index.js
CHANGED
|
@@ -4,14 +4,19 @@
|
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import * as azdev from "azure-devops-node-api";
|
|
7
|
-
import { AzureCliCredential, ChainedTokenCredential, DefaultAzureCredential } from "@azure/identity";
|
|
8
7
|
import yargs from "yargs";
|
|
9
8
|
import { hideBin } from "yargs/helpers";
|
|
9
|
+
import { createAuthenticator } from "./auth.js";
|
|
10
|
+
import { getOrgTenant } from "./org-tenants.js";
|
|
10
11
|
import { configurePrompts } from "./prompts.js";
|
|
11
12
|
import { configureAllTools } from "./tools.js";
|
|
12
13
|
import { UserAgentComposer } from "./useragent.js";
|
|
13
14
|
import { packageVersion } from "./version.js";
|
|
14
15
|
import { DomainsManager } from "./shared/domains.js";
|
|
16
|
+
function isGitHubCodespaceEnv() {
|
|
17
|
+
return process.env.CODESPACES === "true" && !!process.env.CODESPACE_NAME;
|
|
18
|
+
}
|
|
19
|
+
const defaultAuthenticationType = isGitHubCodespaceEnv() ? "azcli" : "interactive";
|
|
15
20
|
// Parse command line arguments using yargs
|
|
16
21
|
const argv = yargs(hideBin(process.argv))
|
|
17
22
|
.scriptName("mcp-server-azuredevops")
|
|
@@ -30,42 +35,29 @@ const argv = yargs(hideBin(process.argv))
|
|
|
30
35
|
type: "string",
|
|
31
36
|
array: true,
|
|
32
37
|
default: "all",
|
|
38
|
+
})
|
|
39
|
+
.option("authentication", {
|
|
40
|
+
alias: "a",
|
|
41
|
+
describe: "Type of authentication to use. Supported values are 'interactive', 'azcli' and 'env' (default: 'interactive')",
|
|
42
|
+
type: "string",
|
|
43
|
+
choices: ["interactive", "azcli", "env"],
|
|
44
|
+
default: defaultAuthenticationType,
|
|
33
45
|
})
|
|
34
46
|
.option("tenant", {
|
|
35
47
|
alias: "t",
|
|
36
|
-
describe: "Azure tenant ID (optional,
|
|
48
|
+
describe: "Azure tenant ID (optional, applied when using 'interactive' and 'azcli' type of authentication)",
|
|
37
49
|
type: "string",
|
|
38
50
|
})
|
|
39
51
|
.help()
|
|
40
52
|
.parseSync();
|
|
41
|
-
const tenantId = argv.tenant;
|
|
42
53
|
export const orgName = argv.organization;
|
|
43
54
|
const orgUrl = "https://dev.azure.com/" + orgName;
|
|
44
55
|
const domainsManager = new DomainsManager(argv.domains);
|
|
45
56
|
export const enabledDomains = domainsManager.getEnabledDomains();
|
|
46
|
-
|
|
47
|
-
if (process.env.ADO_MCP_AZURE_TOKEN_CREDENTIALS) {
|
|
48
|
-
process.env.AZURE_TOKEN_CREDENTIALS = process.env.ADO_MCP_AZURE_TOKEN_CREDENTIALS;
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
process.env.AZURE_TOKEN_CREDENTIALS = "dev";
|
|
52
|
-
}
|
|
53
|
-
let credential = new DefaultAzureCredential(); // CodeQL [SM05138] resolved by explicitly setting AZURE_TOKEN_CREDENTIALS
|
|
54
|
-
if (tenantId) {
|
|
55
|
-
// Use Azure CLI credential if tenantId is provided for multi-tenant scenarios
|
|
56
|
-
const azureCliCredential = new AzureCliCredential({ tenantId });
|
|
57
|
-
credential = new ChainedTokenCredential(azureCliCredential, credential);
|
|
58
|
-
}
|
|
59
|
-
const token = await credential.getToken("499b84ac-1321-427f-aa17-267ca6975798/.default");
|
|
60
|
-
if (!token) {
|
|
61
|
-
throw new Error("Failed to obtain Azure DevOps token. Ensure you have Azure CLI logged in or another token source setup correctly.");
|
|
62
|
-
}
|
|
63
|
-
return token;
|
|
64
|
-
}
|
|
65
|
-
function getAzureDevOpsClient(userAgentComposer) {
|
|
57
|
+
function getAzureDevOpsClient(getAzureDevOpsToken, userAgentComposer) {
|
|
66
58
|
return async () => {
|
|
67
|
-
const
|
|
68
|
-
const authHandler = azdev.getBearerHandler(
|
|
59
|
+
const accessToken = await getAzureDevOpsToken();
|
|
60
|
+
const authHandler = azdev.getBearerHandler(accessToken);
|
|
69
61
|
const connection = new azdev.WebApi(orgUrl, authHandler, undefined, {
|
|
70
62
|
productName: "AzureDevOps.MCP",
|
|
71
63
|
productVersion: packageVersion,
|
|
@@ -83,8 +75,10 @@ async function main() {
|
|
|
83
75
|
server.server.oninitialized = () => {
|
|
84
76
|
userAgentComposer.appendMcpClientInfo(server.server.getClientVersion());
|
|
85
77
|
};
|
|
78
|
+
const tenantId = (await getOrgTenant(orgName)) ?? argv.tenant;
|
|
79
|
+
const authenticator = createAuthenticator(argv.authentication, tenantId);
|
|
86
80
|
configurePrompts(server);
|
|
87
|
-
configureAllTools(server,
|
|
81
|
+
configureAllTools(server, authenticator, getAzureDevOpsClient(authenticator, userAgentComposer), () => userAgentComposer.userAgent, enabledDomains);
|
|
88
82
|
const transport = new StdioServerTransport();
|
|
89
83
|
await server.connect(transport);
|
|
90
84
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
const CACHE_FILE = path.join(os.homedir(), ".ado_orgs.cache");
|
|
5
|
+
const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 1 week in milliseconds
|
|
6
|
+
async function loadCache() {
|
|
7
|
+
try {
|
|
8
|
+
const cacheData = await fs.readFile(CACHE_FILE, "utf-8");
|
|
9
|
+
return JSON.parse(cacheData);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
// Cache file doesn't exist or is invalid, return empty cache
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function trySavingCache(cache) {
|
|
17
|
+
try {
|
|
18
|
+
await fs.writeFile(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error("Failed to save org tenants cache:", error);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function fetchTenantFromApi(orgName) {
|
|
25
|
+
const url = `https://vssps.dev.azure.com/${orgName}`;
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
28
|
+
if (response.status !== 404) {
|
|
29
|
+
throw new Error(`Expected status 404, got ${response.status}`);
|
|
30
|
+
}
|
|
31
|
+
const tenantId = response.headers.get("x-vss-resourcetenant");
|
|
32
|
+
if (!tenantId) {
|
|
33
|
+
throw new Error("x-vss-resourcetenant header not found in response");
|
|
34
|
+
}
|
|
35
|
+
return tenantId;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new Error(`Failed to fetch tenant for organization ${orgName}: ${error}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isCacheEntryExpired(entry) {
|
|
42
|
+
return Date.now() - entry.refreshedOn > CACHE_TTL_MS;
|
|
43
|
+
}
|
|
44
|
+
export async function getOrgTenant(orgName) {
|
|
45
|
+
// Load cache
|
|
46
|
+
const cache = await loadCache();
|
|
47
|
+
// Check if tenant is cached and not expired
|
|
48
|
+
const cachedEntry = cache[orgName];
|
|
49
|
+
if (cachedEntry && !isCacheEntryExpired(cachedEntry)) {
|
|
50
|
+
return cachedEntry.tenantId;
|
|
51
|
+
}
|
|
52
|
+
// Try to fetch fresh tenant from API
|
|
53
|
+
try {
|
|
54
|
+
const tenantId = await fetchTenantFromApi(orgName);
|
|
55
|
+
// Cache the result
|
|
56
|
+
cache[orgName] = {
|
|
57
|
+
tenantId,
|
|
58
|
+
refreshedOn: Date.now(),
|
|
59
|
+
};
|
|
60
|
+
await trySavingCache(cache);
|
|
61
|
+
return tenantId;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
// If we have an expired cache entry, return it as fallback
|
|
65
|
+
if (cachedEntry) {
|
|
66
|
+
console.error(`Failed to fetch fresh tenant for ADO org ${orgName}, using expired cache entry:`, error);
|
|
67
|
+
return cachedEntry.tenantId;
|
|
68
|
+
}
|
|
69
|
+
// No cache entry available, log and return empty result
|
|
70
|
+
console.error(`Failed to fetch tenant for ADO org ${orgName}:`, error);
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/shared/domains.js
CHANGED
|
@@ -106,12 +106,19 @@ export class DomainsManager {
|
|
|
106
106
|
* @returns Normalized array of domain strings
|
|
107
107
|
*/
|
|
108
108
|
static parseDomainsInput(domainsInput) {
|
|
109
|
-
if (!domainsInput) {
|
|
110
|
-
return [];
|
|
109
|
+
if (!domainsInput || this.isEmptyDomainsInput(domainsInput)) {
|
|
110
|
+
return ["all"];
|
|
111
111
|
}
|
|
112
112
|
if (typeof domainsInput === "string") {
|
|
113
113
|
return domainsInput.split(",").map((d) => d.trim().toLowerCase());
|
|
114
114
|
}
|
|
115
115
|
return domainsInput.map((d) => d.trim().toLowerCase());
|
|
116
116
|
}
|
|
117
|
+
static isEmptyDomainsInput(domainsInput) {
|
|
118
|
+
if (typeof domainsInput === "string" && domainsInput.trim() === "")
|
|
119
|
+
return true;
|
|
120
|
+
if (Array.isArray(domainsInput) && domainsInput.length === 0)
|
|
121
|
+
return true;
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
117
124
|
}
|
|
@@ -7,7 +7,7 @@ const ADVSEC_TOOLS = {
|
|
|
7
7
|
get_alerts: "advsec_get_alerts",
|
|
8
8
|
get_alert_details: "advsec_get_alert_details",
|
|
9
9
|
};
|
|
10
|
-
function configureAdvSecTools(server,
|
|
10
|
+
function configureAdvSecTools(server, _, connectionProvider) {
|
|
11
11
|
server.tool(ADVSEC_TOOLS.get_alerts, "Retrieve Advanced Security alerts for a repository.", {
|
|
12
12
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
13
13
|
repository: z.string().describe("The name or ID of the repository to get alerts for."),
|
package/dist/tools/auth.js
CHANGED
|
@@ -4,7 +4,7 @@ import { apiVersion } from "../utils.js";
|
|
|
4
4
|
async function getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider) {
|
|
5
5
|
const connection = await connectionProvider();
|
|
6
6
|
const url = `${connection.serverUrl}/_apis/connectionData`;
|
|
7
|
-
const token =
|
|
7
|
+
const token = await tokenProvider();
|
|
8
8
|
const response = await fetch(url, {
|
|
9
9
|
method: "GET",
|
|
10
10
|
headers: {
|
|
@@ -34,7 +34,7 @@ async function searchIdentities(identity, tokenProvider, connectionProvider, use
|
|
|
34
34
|
});
|
|
35
35
|
const response = await fetch(`${baseUrl}?${params}`, {
|
|
36
36
|
headers: {
|
|
37
|
-
"Authorization": `Bearer ${token
|
|
37
|
+
"Authorization": `Bearer ${token}`,
|
|
38
38
|
"Content-Type": "application/json",
|
|
39
39
|
"User-Agent": userAgentProvider(),
|
|
40
40
|
},
|
package/dist/tools/core.js
CHANGED
|
@@ -65,7 +65,7 @@ function configureCoreTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
server.tool(CORE_TOOLS.get_identity_ids, "Retrieve Azure DevOps identity IDs for a provided search filter.", {
|
|
68
|
-
searchFilter: z.string().describe("Search filter (unique
|
|
68
|
+
searchFilter: z.string().describe("Search filter (unique name, display name, email) to retrieve identity IDs for."),
|
|
69
69
|
}, async ({ searchFilter }) => {
|
|
70
70
|
try {
|
|
71
71
|
const identities = await searchIdentities(searchFilter, tokenProvider, connectionProvider, userAgentProvider);
|
package/dist/tools/pipelines.js
CHANGED
|
@@ -253,7 +253,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
253
253
|
method: "PATCH",
|
|
254
254
|
headers: {
|
|
255
255
|
"Content-Type": "application/json",
|
|
256
|
-
"Authorization": `Bearer ${token
|
|
256
|
+
"Authorization": `Bearer ${token}`,
|
|
257
257
|
"User-Agent": userAgentProvider(),
|
|
258
258
|
},
|
|
259
259
|
body: JSON.stringify(body),
|
package/dist/tools/search.js
CHANGED
|
@@ -45,7 +45,7 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
45
45
|
method: "POST",
|
|
46
46
|
headers: {
|
|
47
47
|
"Content-Type": "application/json",
|
|
48
|
-
"Authorization": `Bearer ${accessToken
|
|
48
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
49
49
|
"User-Agent": userAgentProvider(),
|
|
50
50
|
},
|
|
51
51
|
body: JSON.stringify(requestBody),
|
|
@@ -89,7 +89,7 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
89
89
|
method: "POST",
|
|
90
90
|
headers: {
|
|
91
91
|
"Content-Type": "application/json",
|
|
92
|
-
"Authorization": `Bearer ${accessToken
|
|
92
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
93
93
|
"User-Agent": userAgentProvider(),
|
|
94
94
|
},
|
|
95
95
|
body: JSON.stringify(requestBody),
|
|
@@ -139,7 +139,7 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
139
139
|
method: "POST",
|
|
140
140
|
headers: {
|
|
141
141
|
"Content-Type": "application/json",
|
|
142
|
-
"Authorization": `Bearer ${accessToken
|
|
142
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
143
143
|
"User-Agent": userAgentProvider(),
|
|
144
144
|
},
|
|
145
145
|
body: JSON.stringify(requestBody),
|
package/dist/tools/test-plans.js
CHANGED
|
@@ -9,7 +9,7 @@ const Test_Plan_Tools = {
|
|
|
9
9
|
list_test_cases: "testplan_list_test_cases",
|
|
10
10
|
list_test_plans: "testplan_list_test_plans",
|
|
11
11
|
};
|
|
12
|
-
function configureTestPlanTools(server,
|
|
12
|
+
function configureTestPlanTools(server, _, connectionProvider) {
|
|
13
13
|
/*
|
|
14
14
|
LIST OF TEST PLANS
|
|
15
15
|
get list of test plans by project
|
package/dist/tools/wiki.js
CHANGED
|
@@ -8,7 +8,7 @@ const WIKI_TOOLS = {
|
|
|
8
8
|
get_wiki_page_content: "wiki_get_page_content",
|
|
9
9
|
create_or_update_page: "wiki_create_or_update_page",
|
|
10
10
|
};
|
|
11
|
-
function configureWikiTools(server, tokenProvider, connectionProvider) {
|
|
11
|
+
function configureWikiTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
12
12
|
server.tool(WIKI_TOOLS.get_wiki, "Get the wiki by wikiIdentifier", {
|
|
13
13
|
wikiIdentifier: z.string().describe("The unique identifier of the wiki."),
|
|
14
14
|
project: z.string().optional().describe("The project name or ID where the wiki is located. If not provided, the default project will be used."),
|
|
@@ -121,15 +121,14 @@ function configureWikiTools(server, tokenProvider, connectionProvider) {
|
|
|
121
121
|
}
|
|
122
122
|
if (parsed.pageId) {
|
|
123
123
|
try {
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
accessToken = await tokenProvider();
|
|
127
|
-
}
|
|
128
|
-
catch { }
|
|
124
|
+
const accessToken = await tokenProvider();
|
|
129
125
|
const baseUrl = connection.serverUrl.replace(/\/$/, "");
|
|
130
126
|
const restUrl = `${baseUrl}/${resolvedProject}/_apis/wiki/wikis/${resolvedWiki}/pages/${parsed.pageId}?includeContent=true&api-version=7.1`;
|
|
131
127
|
const resp = await fetch(restUrl, {
|
|
132
|
-
headers:
|
|
128
|
+
headers: {
|
|
129
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
130
|
+
"User-Agent": userAgentProvider(),
|
|
131
|
+
},
|
|
133
132
|
});
|
|
134
133
|
if (resp.ok) {
|
|
135
134
|
const json = await resp.json();
|
|
@@ -193,8 +192,9 @@ function configureWikiTools(server, tokenProvider, connectionProvider) {
|
|
|
193
192
|
const createResponse = await fetch(url, {
|
|
194
193
|
method: "PUT",
|
|
195
194
|
headers: {
|
|
196
|
-
"Authorization": `Bearer ${accessToken
|
|
195
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
197
196
|
"Content-Type": "application/json",
|
|
197
|
+
"User-Agent": userAgentProvider(),
|
|
198
198
|
},
|
|
199
199
|
body: JSON.stringify({ content: content }),
|
|
200
200
|
});
|
|
@@ -218,7 +218,8 @@ function configureWikiTools(server, tokenProvider, connectionProvider) {
|
|
|
218
218
|
const getResponse = await fetch(url, {
|
|
219
219
|
method: "GET",
|
|
220
220
|
headers: {
|
|
221
|
-
Authorization: `Bearer ${accessToken
|
|
221
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
222
|
+
"User-Agent": userAgentProvider(),
|
|
222
223
|
},
|
|
223
224
|
});
|
|
224
225
|
if (getResponse.ok) {
|
|
@@ -236,8 +237,9 @@ function configureWikiTools(server, tokenProvider, connectionProvider) {
|
|
|
236
237
|
const updateResponse = await fetch(url, {
|
|
237
238
|
method: "PUT",
|
|
238
239
|
headers: {
|
|
239
|
-
"Authorization": `Bearer ${accessToken
|
|
240
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
240
241
|
"Content-Type": "application/json",
|
|
242
|
+
"User-Agent": userAgentProvider(),
|
|
241
243
|
"If-Match": currentEtag,
|
|
242
244
|
},
|
|
243
245
|
body: JSON.stringify({ content: content }),
|
package/dist/tools/work-items.js
CHANGED
|
@@ -181,7 +181,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
181
181
|
const response = await fetch(`${orgUrl}/${project}/_apis/wit/workItems/${workItemId}/comments?format=${formatParameter}&api-version=${markdownCommentsApiVersion}`, {
|
|
182
182
|
method: "POST",
|
|
183
183
|
headers: {
|
|
184
|
-
"Authorization": `Bearer ${accessToken
|
|
184
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
185
185
|
"Content-Type": "application/json",
|
|
186
186
|
"User-Agent": userAgentProvider(),
|
|
187
187
|
},
|
|
@@ -287,7 +287,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
287
287
|
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
288
288
|
method: "PATCH",
|
|
289
289
|
headers: {
|
|
290
|
-
"Authorization": `Bearer ${accessToken
|
|
290
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
291
291
|
"Content-Type": "application/json",
|
|
292
292
|
"User-Agent": userAgentProvider(),
|
|
293
293
|
},
|
|
@@ -540,7 +540,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
540
540
|
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
541
541
|
method: "PATCH",
|
|
542
542
|
headers: {
|
|
543
|
-
"Authorization": `Bearer ${accessToken
|
|
543
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
544
544
|
"Content-Type": "application/json",
|
|
545
545
|
"User-Agent": userAgentProvider(),
|
|
546
546
|
},
|
|
@@ -596,7 +596,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
596
596
|
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
597
597
|
method: "PATCH",
|
|
598
598
|
headers: {
|
|
599
|
-
"Authorization": `Bearer ${accessToken
|
|
599
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
600
600
|
"Content-Type": "application/json",
|
|
601
601
|
"User-Agent": userAgentProvider(),
|
|
602
602
|
},
|
package/dist/tools/work.js
CHANGED
|
@@ -7,7 +7,7 @@ const WORK_TOOLS = {
|
|
|
7
7
|
create_iterations: "work_create_iterations",
|
|
8
8
|
assign_iterations: "work_assign_iterations",
|
|
9
9
|
};
|
|
10
|
-
function configureWorkTools(server,
|
|
10
|
+
function configureWorkTools(server, _, connectionProvider) {
|
|
11
11
|
server.tool(WORK_TOOLS.list_team_iterations, "Retrieve a list of iterations for a specific team in a project.", {
|
|
12
12
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
13
13
|
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
package/dist/tools.js
CHANGED
|
@@ -21,7 +21,7 @@ function configureAllTools(server, tokenProvider, connectionProvider, userAgentP
|
|
|
21
21
|
configureIfDomainEnabled(Domain.PIPELINES, () => configurePipelineTools(server, tokenProvider, connectionProvider, userAgentProvider));
|
|
22
22
|
configureIfDomainEnabled(Domain.REPOSITORIES, () => configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider));
|
|
23
23
|
configureIfDomainEnabled(Domain.WORK_ITEMS, () => configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider));
|
|
24
|
-
configureIfDomainEnabled(Domain.WIKI, () => configureWikiTools(server, tokenProvider, connectionProvider));
|
|
24
|
+
configureIfDomainEnabled(Domain.WIKI, () => configureWikiTools(server, tokenProvider, connectionProvider, userAgentProvider));
|
|
25
25
|
configureIfDomainEnabled(Domain.TEST_PLANS, () => configureTestPlanTools(server, tokenProvider, connectionProvider));
|
|
26
26
|
configureIfDomainEnabled(Domain.SEARCH, () => configureSearchTools(server, tokenProvider, connectionProvider, userAgentProvider));
|
|
27
27
|
configureIfDomainEnabled(Domain.ADVANCED_SECURITY, () => configureAdvSecTools(server, tokenProvider, connectionProvider));
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "2.
|
|
1
|
+
export const packageVersion = "2.2.0-nightly.20250922";
|