@azure-devops/mcp 2.5.0 → 2.6.0-nightly.20260419
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 +0 -12
- package/dist/auth.js +13 -0
- package/dist/index.js +23 -5
- package/dist/logger.js +0 -0
- package/dist/org-tenants.js +0 -0
- package/dist/prompts.js +0 -0
- package/dist/shared/content-safety.js +24 -0
- package/dist/shared/domains.js +7 -2
- package/dist/tools/advanced-security.js +2 -2
- package/dist/tools/core.js +5 -5
- package/dist/tools/mcp-apps.js +22 -0
- package/dist/tools/pipelines.js +33 -26
- package/dist/tools/repositories.js +536 -85
- package/dist/tools/search.js +10 -7
- package/dist/tools/test-plans.js +109 -25
- package/dist/tools/wiki.js +7 -6
- package/dist/tools/work-items.js +330 -90
- package/dist/tools/work.js +2 -2
- package/dist/tools.js +3 -1
- package/dist/useragent.js +0 -0
- package/dist/utils.js +15 -0
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/tools/search.js
CHANGED
|
@@ -12,13 +12,16 @@ const SEARCH_TOOLS = {
|
|
|
12
12
|
function configureSearchTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
13
13
|
server.tool(SEARCH_TOOLS.search_code, "Search Azure DevOps Repositories for a given search text", {
|
|
14
14
|
searchText: z.string().describe("Keywords to search for in code repositories"),
|
|
15
|
-
project: z
|
|
15
|
+
project: z
|
|
16
|
+
.union([z.string().transform((value) => [value]), z.array(z.string())])
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Filter by projects"),
|
|
16
19
|
repository: z.array(z.string()).optional().describe("Filter by repositories"),
|
|
17
20
|
path: z.array(z.string()).optional().describe("Filter by paths"),
|
|
18
21
|
branch: z.array(z.string()).optional().describe("Filter by branches"),
|
|
19
22
|
includeFacets: z.boolean().default(false).describe("Include facets in the search results"),
|
|
20
|
-
skip: z.number().default(0).describe("Number of results to skip"),
|
|
21
|
-
top: z.number().default(5).describe("Maximum number of results to return"),
|
|
23
|
+
skip: z.coerce.number().default(0).describe("Number of results to skip"),
|
|
24
|
+
top: z.coerce.number().default(5).describe("Maximum number of results to return"),
|
|
22
25
|
}, async ({ searchText, project, repository, path, branch, includeFacets, skip, top }) => {
|
|
23
26
|
const accessToken = await tokenProvider();
|
|
24
27
|
const connection = await connectionProvider();
|
|
@@ -66,8 +69,8 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
66
69
|
project: z.array(z.string()).optional().describe("Filter by projects"),
|
|
67
70
|
wiki: z.array(z.string()).optional().describe("Filter by wiki names"),
|
|
68
71
|
includeFacets: z.boolean().default(false).describe("Include facets in the search results"),
|
|
69
|
-
skip: z.number().default(0).describe("Number of results to skip"),
|
|
70
|
-
top: z.number().default(10).describe("Maximum number of results to return"),
|
|
72
|
+
skip: z.coerce.number().default(0).describe("Number of results to skip"),
|
|
73
|
+
top: z.coerce.number().default(10).describe("Maximum number of results to return"),
|
|
71
74
|
}, async ({ searchText, project, wiki, includeFacets, skip, top }) => {
|
|
72
75
|
const accessToken = await tokenProvider();
|
|
73
76
|
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/wikisearchresults?api-version=${apiVersion}`;
|
|
@@ -110,8 +113,8 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
110
113
|
state: z.array(z.string()).optional().describe("Filter by work item states"),
|
|
111
114
|
assignedTo: z.array(z.string()).optional().describe("Filter by assigned to users"),
|
|
112
115
|
includeFacets: z.boolean().default(false).describe("Include facets in the search results"),
|
|
113
|
-
skip: z.number().default(0).describe("Number of results to skip for pagination"),
|
|
114
|
-
top: z.number().default(10).describe("Number of results to return"),
|
|
116
|
+
skip: z.coerce.number().default(0).describe("Number of results to skip for pagination"),
|
|
117
|
+
top: z.coerce.number().default(10).describe("Number of results to return"),
|
|
115
118
|
}, async ({ searchText, project, areaPath, workItemType, state, assignedTo, includeFacets, skip, top }) => {
|
|
116
119
|
const accessToken = await tokenProvider();
|
|
117
120
|
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/workitemsearchresults?api-version=${apiVersion}`;
|
package/dist/tools/test-plans.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import { SuiteExpand } from "azure-devops-node-api/interfaces/TestPlanInterfaces.js";
|
|
4
3
|
import { z } from "zod";
|
|
4
|
+
import { apiVersion } from "../utils.js";
|
|
5
5
|
const Test_Plan_Tools = {
|
|
6
6
|
create_test_plan: "testplan_create_test_plan",
|
|
7
7
|
create_test_case: "testplan_create_test_case",
|
|
@@ -13,7 +13,7 @@ const Test_Plan_Tools = {
|
|
|
13
13
|
list_test_suites: "testplan_list_test_suites",
|
|
14
14
|
create_test_suite: "testplan_create_test_suite",
|
|
15
15
|
};
|
|
16
|
-
function configureTestPlanTools(server,
|
|
16
|
+
function configureTestPlanTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
17
17
|
server.tool(Test_Plan_Tools.list_test_plans, "Retrieve a paginated list of test plans from an Azure DevOps project. Allows filtering for active plans and toggling detailed information.", {
|
|
18
18
|
project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."),
|
|
19
19
|
filterActivePlans: z.boolean().default(true).describe("Filter to include only active test plans. Defaults to true."),
|
|
@@ -21,12 +21,42 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
21
21
|
continuationToken: z.string().optional().describe("Token to continue fetching test plans from a previous request."),
|
|
22
22
|
}, async ({ project, filterActivePlans, includePlanDetails, continuationToken }) => {
|
|
23
23
|
try {
|
|
24
|
-
const owner = ""; //making owner an empty string untill we can figure out how to get owner id
|
|
25
24
|
const connection = await connectionProvider();
|
|
26
|
-
const
|
|
27
|
-
const
|
|
25
|
+
const accessToken = await tokenProvider();
|
|
26
|
+
const params = new URLSearchParams({ "api-version": apiVersion });
|
|
27
|
+
if (filterActivePlans)
|
|
28
|
+
params.append("filterActivePlans", "true");
|
|
29
|
+
if (includePlanDetails)
|
|
30
|
+
params.append("includePlanDetails", "true");
|
|
31
|
+
if (continuationToken)
|
|
32
|
+
params.append("continuationToken", continuationToken);
|
|
33
|
+
const url = `${connection.serverUrl}/${encodeURIComponent(project)}/_apis/testplan/Plans?${params.toString()}`;
|
|
34
|
+
const headers = {
|
|
35
|
+
Authorization: `Bearer ${accessToken}`,
|
|
36
|
+
};
|
|
37
|
+
const userAgent = userAgentProvider?.();
|
|
38
|
+
if (userAgent) {
|
|
39
|
+
headers["User-Agent"] = userAgent;
|
|
40
|
+
}
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: "GET",
|
|
43
|
+
headers,
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
const errorText = await response.text();
|
|
47
|
+
throw new Error(`Failed to list test plans (${response.status}): ${errorText}`);
|
|
48
|
+
}
|
|
49
|
+
const body = await response.json();
|
|
50
|
+
const testPlans = body.value ?? [];
|
|
51
|
+
const nextToken = response.headers.get("x-ms-continuationtoken") ?? undefined;
|
|
52
|
+
const result = {
|
|
53
|
+
testPlans: testPlans,
|
|
54
|
+
};
|
|
55
|
+
if (nextToken) {
|
|
56
|
+
result.continuationToken = nextToken;
|
|
57
|
+
}
|
|
28
58
|
return {
|
|
29
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
59
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
30
60
|
};
|
|
31
61
|
}
|
|
32
62
|
catch (error) {
|
|
@@ -72,8 +102,8 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
72
102
|
});
|
|
73
103
|
server.tool(Test_Plan_Tools.create_test_suite, "Creates a new test suite in a test plan.", {
|
|
74
104
|
project: z.string().describe("Project ID or project name"),
|
|
75
|
-
planId: z.number().describe("ID of the test plan that contains the suites"),
|
|
76
|
-
parentSuiteId: z.number().describe("ID of the parent suite under which the new suite will be created, if not given by user this can be id of a root suite of the test plan"),
|
|
105
|
+
planId: z.coerce.number().min(1).describe("ID of the test plan that contains the suites"),
|
|
106
|
+
parentSuiteId: z.coerce.number().min(1).describe("ID of the parent suite under which the new suite will be created, if not given by user this can be id of a root suite of the test plan"),
|
|
77
107
|
name: z.string().describe("Name of the child test suite"),
|
|
78
108
|
}, async ({ project, planId, parentSuiteId, name }) => {
|
|
79
109
|
const maxRetries = 5;
|
|
@@ -120,8 +150,8 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
120
150
|
});
|
|
121
151
|
server.tool(Test_Plan_Tools.add_test_cases_to_suite, "Adds existing test cases to a test suite.", {
|
|
122
152
|
project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."),
|
|
123
|
-
planId: z.number().describe("The ID of the test plan."),
|
|
124
|
-
suiteId: z.number().describe("The ID of the test suite."),
|
|
153
|
+
planId: z.coerce.number().min(1).describe("The ID of the test plan."),
|
|
154
|
+
suiteId: z.coerce.number().min(1).describe("The ID of the test suite."),
|
|
125
155
|
testCaseIds: z.string().or(z.array(z.string())).describe("The ID(s) of the test case(s) to add. "),
|
|
126
156
|
}, async ({ project, planId, suiteId, testCaseIds }) => {
|
|
127
157
|
try {
|
|
@@ -149,10 +179,10 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
149
179
|
.string()
|
|
150
180
|
.optional()
|
|
151
181
|
.describe("The steps to reproduce the test case. Make sure to format each step as '1. Step one|Expected result one\n2. Step two|Expected result two. USE '|' as the delimiter between step and expected result. DO NOT use '|' in the description of the step or expected result."),
|
|
152
|
-
priority: z.number().optional().describe("The priority of the test case."),
|
|
182
|
+
priority: z.coerce.number().optional().describe("The priority of the test case."),
|
|
153
183
|
areaPath: z.string().optional().describe("The area path for the test case."),
|
|
154
184
|
iterationPath: z.string().optional().describe("The iteration path for the test case."),
|
|
155
|
-
testsWorkItemId: z.number().optional().describe("Optional work item id that will be set as a Microsoft.VSTS.Common.TestedBy-Reverse link to the test case."),
|
|
185
|
+
testsWorkItemId: z.coerce.number().min(1).optional().describe("Optional work item id that will be set as a Microsoft.VSTS.Common.TestedBy-Reverse link to the test case."),
|
|
156
186
|
}, async ({ project, title, steps, priority, areaPath, iterationPath, testsWorkItemId }) => {
|
|
157
187
|
try {
|
|
158
188
|
const connection = await connectionProvider();
|
|
@@ -220,7 +250,7 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
220
250
|
}
|
|
221
251
|
});
|
|
222
252
|
server.tool(Test_Plan_Tools.update_test_case_steps, "Update an existing test case work item.", {
|
|
223
|
-
id: z.number().describe("The ID of the test case work item to update."),
|
|
253
|
+
id: z.coerce.number().min(1).describe("The ID of the test case work item to update."),
|
|
224
254
|
steps: z
|
|
225
255
|
.string()
|
|
226
256
|
.describe("The steps to reproduce the test case. Make sure to format each step as '1. Step one|Expected result one\n2. Step two|Expected result two. USE '|' as the delimiter between step and expected result. DO NOT use '|' in the description of the step or expected result."),
|
|
@@ -256,15 +286,43 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
256
286
|
});
|
|
257
287
|
server.tool(Test_Plan_Tools.list_test_cases, "Gets a list of test cases in the test plan.", {
|
|
258
288
|
project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."),
|
|
259
|
-
planid: z.number().describe("The ID of the test plan."),
|
|
260
|
-
suiteid: z.number().describe("The ID of the test suite."),
|
|
261
|
-
|
|
289
|
+
planid: z.coerce.number().min(1).describe("The ID of the test plan."),
|
|
290
|
+
suiteid: z.coerce.number().min(1).describe("The ID of the test suite."),
|
|
291
|
+
continuationToken: z.string().optional().describe("Token to continue fetching test cases from a previous request."),
|
|
292
|
+
}, async ({ project, planid, suiteid, continuationToken }) => {
|
|
262
293
|
try {
|
|
263
294
|
const connection = await connectionProvider();
|
|
264
|
-
const
|
|
265
|
-
const
|
|
295
|
+
const accessToken = await tokenProvider();
|
|
296
|
+
const params = new URLSearchParams({ "api-version": "7.2-preview.3" });
|
|
297
|
+
if (continuationToken)
|
|
298
|
+
params.append("continuationToken", continuationToken);
|
|
299
|
+
const url = `${connection.serverUrl}/${encodeURIComponent(project)}/_apis/testplan/Plans/${planid}/Suites/${suiteid}/TestCase?${params.toString()}`;
|
|
300
|
+
const headers = {
|
|
301
|
+
Authorization: `Bearer ${accessToken}`,
|
|
302
|
+
};
|
|
303
|
+
const userAgent = userAgentProvider?.();
|
|
304
|
+
if (userAgent) {
|
|
305
|
+
headers["User-Agent"] = userAgent;
|
|
306
|
+
}
|
|
307
|
+
const response = await fetch(url, {
|
|
308
|
+
method: "GET",
|
|
309
|
+
headers,
|
|
310
|
+
});
|
|
311
|
+
if (!response.ok) {
|
|
312
|
+
const errorText = await response.text();
|
|
313
|
+
throw new Error(`Failed to list test cases (${response.status}): ${errorText}`);
|
|
314
|
+
}
|
|
315
|
+
const body = await response.json();
|
|
316
|
+
const testcases = body.value ?? [];
|
|
317
|
+
const nextToken = response.headers.get("x-ms-continuationtoken") ?? undefined;
|
|
318
|
+
const result = {
|
|
319
|
+
testCases: testcases,
|
|
320
|
+
};
|
|
321
|
+
if (nextToken) {
|
|
322
|
+
result.continuationToken = nextToken;
|
|
323
|
+
}
|
|
266
324
|
return {
|
|
267
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
325
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
268
326
|
};
|
|
269
327
|
}
|
|
270
328
|
catch (error) {
|
|
@@ -277,7 +335,7 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
277
335
|
});
|
|
278
336
|
server.tool(Test_Plan_Tools.test_results_from_build_id, "Gets a list of test results for a given project and build ID. Can filter by test outcome (e.g. Failed, Passed, Aborted). Returns test case titles, error messages, stack traces, and outcomes. Efficiently handles builds with large numbers of test runs.", {
|
|
279
337
|
project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."),
|
|
280
|
-
buildid: z.number().describe("The ID of the build."),
|
|
338
|
+
buildid: z.coerce.number().min(1).describe("The ID of the build."),
|
|
281
339
|
outcomes: z.array(z.string()).optional().describe("Filter results by test outcome, e.g. ['Failed', 'Passed', 'Aborted']."),
|
|
282
340
|
}, async ({ project, buildid, outcomes }) => {
|
|
283
341
|
try {
|
|
@@ -329,14 +387,34 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
329
387
|
});
|
|
330
388
|
server.tool(Test_Plan_Tools.list_test_suites, "Retrieve a paginated list of test suites from an Azure DevOps project and Test Plan Id.", {
|
|
331
389
|
project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."),
|
|
332
|
-
planId: z.number().describe("The ID of the test plan."),
|
|
390
|
+
planId: z.coerce.number().min(1).describe("The ID of the test plan."),
|
|
333
391
|
continuationToken: z.string().optional().describe("Token to continue fetching test plans from a previous request."),
|
|
334
392
|
}, async ({ project, planId, continuationToken }) => {
|
|
335
393
|
try {
|
|
336
394
|
const connection = await connectionProvider();
|
|
337
|
-
const
|
|
338
|
-
const
|
|
339
|
-
|
|
395
|
+
const accessToken = await tokenProvider();
|
|
396
|
+
const params = new URLSearchParams({ "api-version": apiVersion, "expand": "children" });
|
|
397
|
+
if (continuationToken)
|
|
398
|
+
params.append("continuationToken", continuationToken);
|
|
399
|
+
const url = `${connection.serverUrl}/${encodeURIComponent(project)}/_apis/testplan/Plans/${planId}/Suites?${params.toString()}`;
|
|
400
|
+
const headers = {
|
|
401
|
+
Authorization: `Bearer ${accessToken}`,
|
|
402
|
+
};
|
|
403
|
+
const userAgent = userAgentProvider?.();
|
|
404
|
+
if (userAgent) {
|
|
405
|
+
headers["User-Agent"] = userAgent;
|
|
406
|
+
}
|
|
407
|
+
const response = await fetch(url, {
|
|
408
|
+
method: "GET",
|
|
409
|
+
headers,
|
|
410
|
+
});
|
|
411
|
+
if (!response.ok) {
|
|
412
|
+
const errorText = await response.text();
|
|
413
|
+
throw new Error(`Failed to list test suites (${response.status}): ${errorText}`);
|
|
414
|
+
}
|
|
415
|
+
const body = await response.json();
|
|
416
|
+
const testSuites = body.value ?? [];
|
|
417
|
+
const nextToken = response.headers.get("x-ms-continuationtoken") ?? undefined;
|
|
340
418
|
// The API returns a flat list where the root suite is first, followed by all nested suites
|
|
341
419
|
// We need to build a proper hierarchy by creating a map and assembling the tree
|
|
342
420
|
// Create a map of all suites by ID for quick lookup
|
|
@@ -373,7 +451,13 @@ function configureTestPlanTools(server, _, connectionProvider) {
|
|
|
373
451
|
}
|
|
374
452
|
return cleaned;
|
|
375
453
|
};
|
|
376
|
-
const
|
|
454
|
+
const cleanedSuites = roots.map((root) => cleanSuite(root));
|
|
455
|
+
const result = {
|
|
456
|
+
testSuites: cleanedSuites,
|
|
457
|
+
};
|
|
458
|
+
if (nextToken) {
|
|
459
|
+
result.continuationToken = nextToken;
|
|
460
|
+
}
|
|
377
461
|
return {
|
|
378
462
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
379
463
|
};
|
package/dist/tools/wiki.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { apiVersion } from "../utils.js";
|
|
5
|
+
import { createExternalContentResponse } from "../shared/content-safety.js";
|
|
5
6
|
const WIKI_TOOLS = {
|
|
6
7
|
list_wikis: "wiki_list_wikis",
|
|
7
8
|
get_wiki: "wiki_get_wiki",
|
|
@@ -59,9 +60,9 @@ function configureWikiTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
59
60
|
server.tool(WIKI_TOOLS.list_wiki_pages, "Retrieve a list of wiki pages for a specific wiki and project.", {
|
|
60
61
|
wikiIdentifier: z.string().describe("The unique identifier of the wiki."),
|
|
61
62
|
project: z.string().describe("The project name or ID where the wiki is located."),
|
|
62
|
-
top: z.number().default(20).describe("The maximum number of pages to return. Defaults to 20."),
|
|
63
|
+
top: z.coerce.number().default(20).describe("The maximum number of pages to return. Defaults to 20."),
|
|
63
64
|
continuationToken: z.string().optional().describe("Token for pagination to retrieve the next set of pages."),
|
|
64
|
-
pageViewsForDays: z.number().optional().describe("Number of days to retrieve page views for. If not specified, page views are not included."),
|
|
65
|
+
pageViewsForDays: z.coerce.number().optional().describe("Number of days to retrieve page views for. If not specified, page views are not included."),
|
|
65
66
|
}, async ({ wikiIdentifier, project, top = 20, continuationToken, pageViewsForDays }) => {
|
|
66
67
|
try {
|
|
67
68
|
const connection = await connectionProvider();
|
|
@@ -111,7 +112,7 @@ function configureWikiTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
111
112
|
if (recursionLevel) {
|
|
112
113
|
params.append("recursionLevel", recursionLevel);
|
|
113
114
|
}
|
|
114
|
-
const url = `${baseUrl}/${project}/_apis/wiki/wikis/${wikiIdentifier}/pages?${params.toString()}`;
|
|
115
|
+
const url = `${baseUrl}/${encodeURIComponent(project)}/_apis/wiki/wikis/${encodeURIComponent(wikiIdentifier)}/pages?${params.toString()}`;
|
|
115
116
|
const response = await fetch(url, {
|
|
116
117
|
headers: {
|
|
117
118
|
"Authorization": `Bearer ${accessToken}`,
|
|
@@ -173,7 +174,7 @@ function configureWikiTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
173
174
|
try {
|
|
174
175
|
const accessToken = await tokenProvider();
|
|
175
176
|
const baseUrl = connection.serverUrl.replace(/\/$/, "");
|
|
176
|
-
const restUrl = `${baseUrl}/${resolvedProject}/_apis/wiki/wikis/${resolvedWiki}/pages/${parsed.pageId}?includeContent=true&api-version=7.1`;
|
|
177
|
+
const restUrl = `${baseUrl}/${encodeURIComponent(resolvedProject)}/_apis/wiki/wikis/${encodeURIComponent(resolvedWiki)}/pages/${parsed.pageId}?includeContent=true&api-version=7.1`;
|
|
177
178
|
const resp = await fetch(restUrl, {
|
|
178
179
|
headers: {
|
|
179
180
|
"Authorization": `Bearer ${accessToken}`,
|
|
@@ -209,7 +210,7 @@ function configureWikiTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
209
210
|
}
|
|
210
211
|
pageContent = await streamToString(stream);
|
|
211
212
|
}
|
|
212
|
-
return
|
|
213
|
+
return createExternalContentResponse(pageContent, "wiki page");
|
|
213
214
|
}
|
|
214
215
|
catch (error) {
|
|
215
216
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -236,7 +237,7 @@ function configureWikiTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
236
237
|
// Build the URL for the wiki page API with version descriptor
|
|
237
238
|
const baseUrl = connection.serverUrl;
|
|
238
239
|
const projectParam = project || "";
|
|
239
|
-
const url = `${baseUrl}/${projectParam}/_apis/wiki/wikis/${wikiIdentifier}/pages?path=${encodedPath}&versionDescriptor.versionType=branch&versionDescriptor.version=${encodeURIComponent(branch)}&api-version=7.1`;
|
|
240
|
+
const url = `${baseUrl}/${encodeURIComponent(projectParam)}/_apis/wiki/wikis/${encodeURIComponent(wikiIdentifier)}/pages?path=${encodedPath}&versionDescriptor.versionType=branch&versionDescriptor.version=${encodeURIComponent(branch)}&api-version=7.1`;
|
|
240
241
|
// First, try to create a new page (PUT without ETag)
|
|
241
242
|
try {
|
|
242
243
|
const createResponse = await fetch(url, {
|