@azure-devops/mcp 1.1.0 → 1.2.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 +167 -101
- package/dist/index.js +38 -9
- package/dist/tools/builds.js +14 -7
- package/dist/tools/core.js +48 -0
- package/dist/tools/releases.js +26 -7
- package/dist/tools/repos.js +41 -9
- package/dist/tools/search.js +85 -80
- package/dist/tools/workitems.js +108 -41
- package/dist/useragent.js +2 -0
- package/dist/utils.js +26 -0
- package/dist/version.js +1 -1
- package/package.json +6 -5
package/dist/tools/releases.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import { ReleaseDefinitionExpands, ReleaseDefinitionQueryOrder, ReleaseExpands, ReleaseStatus, ReleaseQueryOrder } from "azure-devops-node-api/interfaces/ReleaseInterfaces.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { getEnumKeys, safeEnumConvert } from "../utils.js";
|
|
5
6
|
const RELEASE_TOOLS = {
|
|
6
7
|
get_release_definitions: "release_get_definitions",
|
|
7
8
|
get_releases: "release_get_releases",
|
|
@@ -10,12 +11,18 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
|
|
|
10
11
|
server.tool(RELEASE_TOOLS.get_release_definitions, "Retrieves list of release definitions for a given project.", {
|
|
11
12
|
project: z.string().describe("Project ID or name to get release definitions for"),
|
|
12
13
|
searchText: z.string().optional().describe("Search text to filter release definitions"),
|
|
13
|
-
expand: z
|
|
14
|
+
expand: z
|
|
15
|
+
.enum(getEnumKeys(ReleaseDefinitionExpands))
|
|
16
|
+
.default("None")
|
|
17
|
+
.describe("Expand options for release definitions"),
|
|
14
18
|
artifactType: z.string().optional().describe("Filter by artifact type"),
|
|
15
19
|
artifactSourceId: z.string().optional().describe("Filter by artifact source ID"),
|
|
16
20
|
top: z.number().optional().describe("Number of results to return (for pagination)"),
|
|
17
21
|
continuationToken: z.string().optional().describe("Continuation token for pagination"),
|
|
18
|
-
queryOrder: z
|
|
22
|
+
queryOrder: z
|
|
23
|
+
.enum(getEnumKeys(ReleaseDefinitionQueryOrder))
|
|
24
|
+
.default("NameAscending")
|
|
25
|
+
.describe("Order of the results"),
|
|
19
26
|
path: z.string().optional().describe("Path to filter release definitions"),
|
|
20
27
|
isExactNameMatch: z.boolean().optional().default(false).describe("Whether to match the exact name of the release definition. Default is false."),
|
|
21
28
|
tagFilter: z.array(z.string()).optional().describe("Filter by tags associated with the release definitions"),
|
|
@@ -26,7 +33,7 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
|
|
|
26
33
|
}, async ({ project, searchText, expand, artifactType, artifactSourceId, top, continuationToken, queryOrder, path, isExactNameMatch, tagFilter, propertyFilters, definitionIdFilter, isDeleted, searchTextContainsFolderName, }) => {
|
|
27
34
|
const connection = await connectionProvider();
|
|
28
35
|
const releaseApi = await connection.getReleaseApi();
|
|
29
|
-
const releaseDefinitions = await releaseApi.getReleaseDefinitions(project, searchText, expand, artifactType, artifactSourceId, top, continuationToken, queryOrder, path, isExactNameMatch, tagFilter, propertyFilters, definitionIdFilter, isDeleted, searchTextContainsFolderName);
|
|
36
|
+
const releaseDefinitions = await releaseApi.getReleaseDefinitions(project, searchText, safeEnumConvert(ReleaseDefinitionExpands, expand), artifactType, artifactSourceId, top, continuationToken, safeEnumConvert(ReleaseDefinitionQueryOrder, queryOrder), path, isExactNameMatch, tagFilter, propertyFilters, definitionIdFilter, isDeleted, searchTextContainsFolderName);
|
|
30
37
|
return {
|
|
31
38
|
content: [{ type: "text", text: JSON.stringify(releaseDefinitions, null, 2) }],
|
|
32
39
|
};
|
|
@@ -37,7 +44,11 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
|
|
|
37
44
|
definitionEnvironmentId: z.number().optional().describe("ID of the definition environment to filter releases"),
|
|
38
45
|
searchText: z.string().optional().describe("Search text to filter releases"),
|
|
39
46
|
createdBy: z.string().optional().describe("User ID or name who created the release"),
|
|
40
|
-
statusFilter: z
|
|
47
|
+
statusFilter: z
|
|
48
|
+
.enum(getEnumKeys(ReleaseStatus))
|
|
49
|
+
.optional()
|
|
50
|
+
.default("Active")
|
|
51
|
+
.describe("Status of the releases to filter (default: Active)"),
|
|
41
52
|
environmentStatusFilter: z.number().optional().describe("Environment status to filter releases"),
|
|
42
53
|
minCreatedTime: z.coerce
|
|
43
54
|
.date()
|
|
@@ -53,10 +64,18 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
|
|
|
53
64
|
.optional()
|
|
54
65
|
.default(() => new Date())
|
|
55
66
|
.describe("Maximum created time for releases (default: now)"),
|
|
56
|
-
queryOrder: z
|
|
67
|
+
queryOrder: z
|
|
68
|
+
.enum(getEnumKeys(ReleaseQueryOrder))
|
|
69
|
+
.optional()
|
|
70
|
+
.default("Ascending")
|
|
71
|
+
.describe("Order in which to return releases (default: Ascending)"),
|
|
57
72
|
top: z.number().optional().describe("Number of releases to return"),
|
|
58
73
|
continuationToken: z.number().optional().describe("Continuation token for pagination"),
|
|
59
|
-
expand: z
|
|
74
|
+
expand: z
|
|
75
|
+
.enum(getEnumKeys(ReleaseExpands))
|
|
76
|
+
.optional()
|
|
77
|
+
.default("None")
|
|
78
|
+
.describe("Expand options for releases"),
|
|
60
79
|
artifactTypeId: z.string().optional().describe("Filter releases by artifact type ID"),
|
|
61
80
|
sourceId: z.string().optional().describe("Filter releases by artifact source ID"),
|
|
62
81
|
artifactVersionId: z.string().optional().describe("Filter releases by artifact version ID"),
|
|
@@ -69,7 +88,7 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
|
|
|
69
88
|
}, async ({ project, definitionId, definitionEnvironmentId, searchText, createdBy, statusFilter, environmentStatusFilter, minCreatedTime, maxCreatedTime, queryOrder, top, continuationToken, expand, artifactTypeId, sourceId, artifactVersionId, sourceBranchFilter, isDeleted, tagFilter, propertyFilters, releaseIdFilter, path, }) => {
|
|
70
89
|
const connection = await connectionProvider();
|
|
71
90
|
const releaseApi = await connection.getReleaseApi();
|
|
72
|
-
const releases = await releaseApi.getReleases(project, definitionId, definitionEnvironmentId, searchText, createdBy, statusFilter, environmentStatusFilter, minCreatedTime, maxCreatedTime, queryOrder, top, continuationToken, expand, artifactTypeId, sourceId, artifactVersionId, sourceBranchFilter, isDeleted, tagFilter, propertyFilters, releaseIdFilter, path);
|
|
91
|
+
const releases = await releaseApi.getReleases(project, definitionId, definitionEnvironmentId, searchText, createdBy, safeEnumConvert(ReleaseStatus, statusFilter), environmentStatusFilter, minCreatedTime, maxCreatedTime, safeEnumConvert(ReleaseQueryOrder, queryOrder), top, continuationToken, safeEnumConvert(ReleaseExpands, expand), artifactTypeId, sourceId, artifactVersionId, sourceBranchFilter, isDeleted, tagFilter, propertyFilters, releaseIdFilter, path);
|
|
73
92
|
return {
|
|
74
93
|
content: [{ type: "text", text: JSON.stringify(releases, null, 2) }],
|
|
75
94
|
};
|
package/dist/tools/repos.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, } from "azure-devops-node-api/interfaces/GitInterfaces.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { getCurrentUserDetails } from "./auth.js";
|
|
6
|
+
import { getEnumKeys } from "../utils.js";
|
|
6
7
|
const REPO_TOOLS = {
|
|
7
8
|
list_repos_by_project: "repo_list_repos_by_project",
|
|
8
9
|
list_pull_requests_by_repo: "repo_list_pull_requests_by_repo",
|
|
@@ -16,6 +17,7 @@ const REPO_TOOLS = {
|
|
|
16
17
|
get_pull_request_by_id: "repo_get_pull_request_by_id",
|
|
17
18
|
create_pull_request: "repo_create_pull_request",
|
|
18
19
|
update_pull_request_status: "repo_update_pull_request_status",
|
|
20
|
+
update_pull_request_reviewers: "repo_update_pull_request_reviewers",
|
|
19
21
|
reply_to_comment: "repo_reply_to_comment",
|
|
20
22
|
resolve_comment: "repo_resolve_comment",
|
|
21
23
|
search_commits: "repo_search_commits",
|
|
@@ -31,15 +33,15 @@ function branchesFilterOutIrrelevantProperties(branches, top) {
|
|
|
31
33
|
}
|
|
32
34
|
function pullRequestStatusStringToInt(status) {
|
|
33
35
|
switch (status) {
|
|
34
|
-
case "
|
|
36
|
+
case "Abandoned":
|
|
35
37
|
return PullRequestStatus.Abandoned.valueOf();
|
|
36
|
-
case "
|
|
38
|
+
case "Active":
|
|
37
39
|
return PullRequestStatus.Active.valueOf();
|
|
38
|
-
case "
|
|
40
|
+
case "All":
|
|
39
41
|
return PullRequestStatus.All.valueOf();
|
|
40
|
-
case "
|
|
42
|
+
case "Completed":
|
|
41
43
|
return PullRequestStatus.Completed.valueOf();
|
|
42
|
-
case "
|
|
44
|
+
case "NotSet":
|
|
43
45
|
return PullRequestStatus.NotSet.valueOf();
|
|
44
46
|
default:
|
|
45
47
|
throw new Error(`Unknown pull request status: ${status}`);
|
|
@@ -78,16 +80,40 @@ function configureRepoTools(server, tokenProvider, connectionProvider) {
|
|
|
78
80
|
server.tool(REPO_TOOLS.update_pull_request_status, "Update status of an existing pull request to active or abandoned.", {
|
|
79
81
|
repositoryId: z.string().describe("The ID of the repository where the pull request exists."),
|
|
80
82
|
pullRequestId: z.number().describe("The ID of the pull request to be published."),
|
|
81
|
-
status: z.enum(["
|
|
83
|
+
status: z.enum(["Active", "Abandoned"]).describe("The new status of the pull request. Can be 'Active' or 'Abandoned'."),
|
|
82
84
|
}, async ({ repositoryId, pullRequestId, status }) => {
|
|
83
85
|
const connection = await connectionProvider();
|
|
84
86
|
const gitApi = await connection.getGitApi();
|
|
85
|
-
const statusValue = status === "
|
|
87
|
+
const statusValue = status === "Active" ? PullRequestStatus.Active.valueOf() : PullRequestStatus.Abandoned.valueOf();
|
|
86
88
|
const updatedPullRequest = await gitApi.updatePullRequest({ status: statusValue }, repositoryId, pullRequestId);
|
|
87
89
|
return {
|
|
88
90
|
content: [{ type: "text", text: JSON.stringify(updatedPullRequest, null, 2) }],
|
|
89
91
|
};
|
|
90
92
|
});
|
|
93
|
+
server.tool(REPO_TOOLS.update_pull_request_reviewers, "Add or remove reviewers for an existing pull request.", {
|
|
94
|
+
repositoryId: z.string().describe("The ID of the repository where the pull request exists."),
|
|
95
|
+
pullRequestId: z.number().describe("The ID of the pull request to update."),
|
|
96
|
+
reviewerIds: z.array(z.string()).describe("List of reviewer ids to add or remove from the pull request."),
|
|
97
|
+
action: z.enum(["add", "remove"]).describe("Action to perform on the reviewers. Can be 'add' or 'remove'."),
|
|
98
|
+
}, async ({ repositoryId, pullRequestId, reviewerIds, action }) => {
|
|
99
|
+
const connection = await connectionProvider();
|
|
100
|
+
const gitApi = await connection.getGitApi();
|
|
101
|
+
let updatedPullRequest;
|
|
102
|
+
if (action === "add") {
|
|
103
|
+
updatedPullRequest = await gitApi.createPullRequestReviewers(reviewerIds.map((id) => ({ id: id })), repositoryId, pullRequestId);
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: JSON.stringify(updatedPullRequest, null, 2) }],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
for (const reviewerId of reviewerIds) {
|
|
110
|
+
await gitApi.deletePullRequestReviewer(repositoryId, pullRequestId, reviewerId);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
content: [{ type: "text", text: `Reviewers with IDs ${reviewerIds.join(", ")} removed from pull request ${pullRequestId}.` }],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
});
|
|
91
117
|
server.tool(REPO_TOOLS.list_repos_by_project, "Retrieve a list of repositories for a given project", {
|
|
92
118
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
93
119
|
top: z.number().default(100).describe("The maximum number of repositories to return."),
|
|
@@ -119,7 +145,10 @@ function configureRepoTools(server, tokenProvider, connectionProvider) {
|
|
|
119
145
|
skip: z.number().default(0).describe("The number of pull requests to skip."),
|
|
120
146
|
created_by_me: z.boolean().default(false).describe("Filter pull requests created by the current user."),
|
|
121
147
|
i_am_reviewer: z.boolean().default(false).describe("Filter pull requests where the current user is a reviewer."),
|
|
122
|
-
status: z
|
|
148
|
+
status: z
|
|
149
|
+
.enum(getEnumKeys(PullRequestStatus))
|
|
150
|
+
.default("Active")
|
|
151
|
+
.describe("Filter pull requests by status. Defaults to 'Active'."),
|
|
123
152
|
}, async ({ repositoryId, top, skip, created_by_me, i_am_reviewer, status }) => {
|
|
124
153
|
const connection = await connectionProvider();
|
|
125
154
|
const gitApi = await connection.getGitApi();
|
|
@@ -164,7 +193,10 @@ function configureRepoTools(server, tokenProvider, connectionProvider) {
|
|
|
164
193
|
skip: z.number().default(0).describe("The number of pull requests to skip."),
|
|
165
194
|
created_by_me: z.boolean().default(false).describe("Filter pull requests created by the current user."),
|
|
166
195
|
i_am_reviewer: z.boolean().default(false).describe("Filter pull requests where the current user is a reviewer."),
|
|
167
|
-
status: z
|
|
196
|
+
status: z
|
|
197
|
+
.enum(getEnumKeys(PullRequestStatus))
|
|
198
|
+
.default("Active")
|
|
199
|
+
.describe("Filter pull requests by status. Defaults to 'Active'."),
|
|
168
200
|
}, async ({ project, top, skip, created_by_me, i_am_reviewer, status }) => {
|
|
169
201
|
const connection = await connectionProvider();
|
|
170
202
|
const gitApi = await connection.getGitApi();
|
package/dist/tools/search.js
CHANGED
|
@@ -10,38 +10,37 @@ const SEARCH_TOOLS = {
|
|
|
10
10
|
search_workitem: "search_workitem",
|
|
11
11
|
};
|
|
12
12
|
function configureSearchTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
filters: z
|
|
24
|
-
.object({
|
|
25
|
-
Project: z.array(z.string()).optional().describe("Filter in these projects"),
|
|
26
|
-
Repository: z.array(z.string()).optional().describe("Filter in these repositories"),
|
|
27
|
-
Path: z.array(z.string()).optional().describe("Filter in these paths"),
|
|
28
|
-
Branch: z.array(z.string()).optional().describe("Filter in these branches"),
|
|
29
|
-
CodeElement: z.array(z.string()).optional().describe("Filter for these code elements (e.g., classes, functions, symbols)"),
|
|
30
|
-
// Note: CodeElement is optional and can be used to filter results by specific code elements.
|
|
31
|
-
// It can be a string or an array of strings.
|
|
32
|
-
// If provided, the search will only return results that match the specified code elements.
|
|
33
|
-
// This is useful for narrowing down the search to specific classes, functions, definitions, or symbols.
|
|
34
|
-
// Example: CodeElement: ["MyClass", "MyFunction"]
|
|
35
|
-
})
|
|
36
|
-
.partial()
|
|
37
|
-
.optional(),
|
|
38
|
-
includeFacets: z.boolean().optional(),
|
|
39
|
-
})
|
|
40
|
-
.strict(),
|
|
41
|
-
}, async ({ searchRequest }) => {
|
|
13
|
+
server.tool(SEARCH_TOOLS.search_code, "Search Azure DevOps Repositories for a given search text", {
|
|
14
|
+
searchText: z.string().describe("Keywords to search for in code repositories"),
|
|
15
|
+
project: z.array(z.string()).optional().describe("Filter by projects"),
|
|
16
|
+
repository: z.array(z.string()).optional().describe("Filter by repositories"),
|
|
17
|
+
path: z.array(z.string()).optional().describe("Filter by paths"),
|
|
18
|
+
branch: z.array(z.string()).optional().describe("Filter by branches"),
|
|
19
|
+
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"),
|
|
22
|
+
}, async ({ searchText, project, repository, path, branch, includeFacets, $skip, $top }) => {
|
|
42
23
|
const accessToken = await tokenProvider();
|
|
43
24
|
const connection = await connectionProvider();
|
|
44
25
|
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/codesearchresults?api-version=${apiVersion}`;
|
|
26
|
+
const requestBody = {
|
|
27
|
+
searchText,
|
|
28
|
+
includeFacets,
|
|
29
|
+
$skip,
|
|
30
|
+
$top,
|
|
31
|
+
};
|
|
32
|
+
const filters = {};
|
|
33
|
+
if (project && project.length > 0)
|
|
34
|
+
filters.Project = project;
|
|
35
|
+
if (repository && repository.length > 0)
|
|
36
|
+
filters.Repository = repository;
|
|
37
|
+
if (path && path.length > 0)
|
|
38
|
+
filters.Path = path;
|
|
39
|
+
if (branch && branch.length > 0)
|
|
40
|
+
filters.Branch = branch;
|
|
41
|
+
if (Object.keys(filters).length > 0) {
|
|
42
|
+
requestBody.filters = filters;
|
|
43
|
+
}
|
|
45
44
|
const response = await fetch(url, {
|
|
46
45
|
method: "POST",
|
|
47
46
|
headers: {
|
|
@@ -49,44 +48,43 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
49
48
|
"Authorization": `Bearer ${accessToken.token}`,
|
|
50
49
|
"User-Agent": userAgentProvider(),
|
|
51
50
|
},
|
|
52
|
-
body: JSON.stringify(
|
|
51
|
+
body: JSON.stringify(requestBody),
|
|
53
52
|
});
|
|
54
53
|
if (!response.ok) {
|
|
55
54
|
throw new Error(`Azure DevOps Code Search API error: ${response.status} ${response.statusText}`);
|
|
56
55
|
}
|
|
57
56
|
const resultText = await response.text();
|
|
58
57
|
const resultJson = JSON.parse(resultText);
|
|
59
|
-
const topResults = Array.isArray(resultJson.results) ? resultJson.results.slice(0, Math.min(searchRequest.$top, resultJson.results.length)) : [];
|
|
60
58
|
const gitApi = await connection.getGitApi();
|
|
61
|
-
const combinedResults = await fetchCombinedResults(
|
|
59
|
+
const combinedResults = await fetchCombinedResults(resultJson.results ?? [], gitApi);
|
|
62
60
|
return {
|
|
63
61
|
content: [{ type: "text", text: resultText + JSON.stringify(combinedResults) }],
|
|
64
62
|
};
|
|
65
63
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
$skip: z.number().default(0).describe("Number of results to skip (for pagination)"),
|
|
75
|
-
$top: z.number().default(10).describe("Number of results to return (for pagination)"),
|
|
76
|
-
filters: z
|
|
77
|
-
.object({
|
|
78
|
-
Project: z.array(z.string()).optional().describe("Filter in these projects"),
|
|
79
|
-
Wiki: z.array(z.string()).optional().describe("Filter in these wiki names"),
|
|
80
|
-
})
|
|
81
|
-
.partial()
|
|
82
|
-
.optional()
|
|
83
|
-
.describe("Filters to apply to the search text"),
|
|
84
|
-
includeFacets: z.boolean().optional(),
|
|
85
|
-
})
|
|
86
|
-
.strict(),
|
|
87
|
-
}, async ({ searchRequest }) => {
|
|
64
|
+
server.tool(SEARCH_TOOLS.search_wiki, "Search Azure DevOps Wiki for a given search text", {
|
|
65
|
+
searchText: z.string().describe("Keywords to search for wiki pages"),
|
|
66
|
+
project: z.array(z.string()).optional().describe("Filter by projects"),
|
|
67
|
+
wiki: z.array(z.string()).optional().describe("Filter by wiki names"),
|
|
68
|
+
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"),
|
|
71
|
+
}, async ({ searchText, project, wiki, includeFacets, $skip, $top }) => {
|
|
88
72
|
const accessToken = await tokenProvider();
|
|
89
73
|
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/wikisearchresults?api-version=${apiVersion}`;
|
|
74
|
+
const requestBody = {
|
|
75
|
+
searchText,
|
|
76
|
+
includeFacets,
|
|
77
|
+
$skip,
|
|
78
|
+
$top,
|
|
79
|
+
};
|
|
80
|
+
const filters = {};
|
|
81
|
+
if (project && project.length > 0)
|
|
82
|
+
filters.Project = project;
|
|
83
|
+
if (wiki && wiki.length > 0)
|
|
84
|
+
filters.Wiki = wiki;
|
|
85
|
+
if (Object.keys(filters).length > 0) {
|
|
86
|
+
requestBody.filters = filters;
|
|
87
|
+
}
|
|
90
88
|
const response = await fetch(url, {
|
|
91
89
|
method: "POST",
|
|
92
90
|
headers: {
|
|
@@ -94,7 +92,7 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
94
92
|
"Authorization": `Bearer ${accessToken.token}`,
|
|
95
93
|
"User-Agent": userAgentProvider(),
|
|
96
94
|
},
|
|
97
|
-
body: JSON.stringify(
|
|
95
|
+
body: JSON.stringify(requestBody),
|
|
98
96
|
});
|
|
99
97
|
if (!response.ok) {
|
|
100
98
|
throw new Error(`Azure DevOps Wiki Search API error: ${response.status} ${response.statusText}`);
|
|
@@ -104,32 +102,39 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
104
102
|
content: [{ type: "text", text: result }],
|
|
105
103
|
};
|
|
106
104
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
.object({
|
|
119
|
-
"System.TeamProject": z.array(z.string()).optional().describe("Filter by team project"),
|
|
120
|
-
"System.AreaPath": z.array(z.string()).optional().describe("Filter by area path"),
|
|
121
|
-
"System.WorkItemType": z.array(z.string()).optional().describe("Filter by work item type like Bug, Task, User Story"),
|
|
122
|
-
"System.State": z.array(z.string()).optional().describe("Filter by state"),
|
|
123
|
-
"System.AssignedTo": z.array(z.string()).optional().describe("Filter by assigned to"),
|
|
124
|
-
})
|
|
125
|
-
.partial()
|
|
126
|
-
.optional(),
|
|
127
|
-
includeFacets: z.boolean().optional(),
|
|
128
|
-
})
|
|
129
|
-
.strict(),
|
|
130
|
-
}, async ({ searchRequest }) => {
|
|
105
|
+
server.tool(SEARCH_TOOLS.search_workitem, "Get Azure DevOps Work Item search results for a given search text", {
|
|
106
|
+
searchText: z.string().describe("Search text to find in work items"),
|
|
107
|
+
project: z.array(z.string()).optional().describe("Filter by projects"),
|
|
108
|
+
areaPath: z.array(z.string()).optional().describe("Filter by area paths"),
|
|
109
|
+
workItemType: z.array(z.string()).optional().describe("Filter by work item types"),
|
|
110
|
+
state: z.array(z.string()).optional().describe("Filter by work item states"),
|
|
111
|
+
assignedTo: z.array(z.string()).optional().describe("Filter by assigned to users"),
|
|
112
|
+
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"),
|
|
115
|
+
}, async ({ searchText, project, areaPath, workItemType, state, assignedTo, includeFacets, $skip, $top }) => {
|
|
131
116
|
const accessToken = await tokenProvider();
|
|
132
117
|
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/workitemsearchresults?api-version=${apiVersion}`;
|
|
118
|
+
const requestBody = {
|
|
119
|
+
searchText,
|
|
120
|
+
includeFacets,
|
|
121
|
+
$skip,
|
|
122
|
+
$top,
|
|
123
|
+
};
|
|
124
|
+
const filters = {};
|
|
125
|
+
if (project && project.length > 0)
|
|
126
|
+
filters["System.TeamProject"] = project;
|
|
127
|
+
if (areaPath && areaPath.length > 0)
|
|
128
|
+
filters["System.AreaPath"] = areaPath;
|
|
129
|
+
if (workItemType && workItemType.length > 0)
|
|
130
|
+
filters["System.WorkItemType"] = workItemType;
|
|
131
|
+
if (state && state.length > 0)
|
|
132
|
+
filters["System.State"] = state;
|
|
133
|
+
if (assignedTo && assignedTo.length > 0)
|
|
134
|
+
filters["System.AssignedTo"] = assignedTo;
|
|
135
|
+
if (Object.keys(filters).length > 0) {
|
|
136
|
+
requestBody.filters = filters;
|
|
137
|
+
}
|
|
133
138
|
const response = await fetch(url, {
|
|
134
139
|
method: "POST",
|
|
135
140
|
headers: {
|
|
@@ -137,7 +142,7 @@ function configureSearchTools(server, tokenProvider, connectionProvider, userAge
|
|
|
137
142
|
"Authorization": `Bearer ${accessToken.token}`,
|
|
138
143
|
"User-Agent": userAgentProvider(),
|
|
139
144
|
},
|
|
140
|
-
body: JSON.stringify(
|
|
145
|
+
body: JSON.stringify(requestBody),
|
|
141
146
|
});
|
|
142
147
|
if (!response.ok) {
|
|
143
148
|
throw new Error(`Azure DevOps Work Item Search API error: ${response.status} ${response.statusText}`);
|