@azure-devops/mcp 0.1.0 → 1.1.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.
@@ -12,22 +12,46 @@ function configureWikiTools(server, tokenProvider, connectionProvider) {
12
12
  wikiIdentifier: z.string().describe("The unique identifier of the wiki."),
13
13
  project: z.string().optional().describe("The project name or ID where the wiki is located. If not provided, the default project will be used."),
14
14
  }, async ({ wikiIdentifier, project }) => {
15
- const connection = await connectionProvider();
16
- const wikiApi = await connection.getWikiApi();
17
- const wiki = await wikiApi.getWiki(wikiIdentifier, project);
18
- return {
19
- content: [{ type: "text", text: JSON.stringify(wiki, null, 2) }],
20
- };
15
+ try {
16
+ const connection = await connectionProvider();
17
+ const wikiApi = await connection.getWikiApi();
18
+ const wiki = await wikiApi.getWiki(wikiIdentifier, project);
19
+ if (!wiki) {
20
+ return { content: [{ type: "text", text: "No wiki found" }], isError: true };
21
+ }
22
+ return {
23
+ content: [{ type: "text", text: JSON.stringify(wiki, 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 wiki: ${errorMessage}` }],
30
+ isError: true,
31
+ };
32
+ }
21
33
  });
22
34
  server.tool(WIKI_TOOLS.list_wikis, "Retrieve a list of wikis for an organization or project.", {
23
35
  project: z.string().optional().describe("The project name or ID to filter wikis. If not provided, all wikis in the organization will be returned."),
24
36
  }, async ({ project }) => {
25
- const connection = await connectionProvider();
26
- const wikiApi = await connection.getWikiApi();
27
- const wikis = await wikiApi.getAllWikis(project);
28
- return {
29
- content: [{ type: "text", text: JSON.stringify(wikis, null, 2) }],
30
- };
37
+ try {
38
+ const connection = await connectionProvider();
39
+ const wikiApi = await connection.getWikiApi();
40
+ const wikis = await wikiApi.getAllWikis(project);
41
+ if (!wikis) {
42
+ return { content: [{ type: "text", text: "No wikis found" }], isError: true };
43
+ }
44
+ return {
45
+ content: [{ type: "text", text: JSON.stringify(wikis, null, 2) }],
46
+ };
47
+ }
48
+ catch (error) {
49
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
50
+ return {
51
+ content: [{ type: "text", text: `Error fetching wikis: ${errorMessage}` }],
52
+ isError: true,
53
+ };
54
+ }
31
55
  });
32
56
  server.tool(WIKI_TOOLS.list_wiki_pages, "Retrieve a list of wiki pages for a specific wiki and project.", {
33
57
  wikiIdentifier: z.string().describe("The unique identifier of the wiki."),
@@ -35,31 +59,55 @@ function configureWikiTools(server, tokenProvider, connectionProvider) {
35
59
  top: z.number().default(20).describe("The maximum number of pages to return. Defaults to 20."),
36
60
  continuationToken: z.string().optional().describe("Token for pagination to retrieve the next set of pages."),
37
61
  pageViewsForDays: z.number().optional().describe("Number of days to retrieve page views for. If not specified, page views are not included."),
38
- }, async ({ wikiIdentifier, project, top = 20, continuationToken, pageViewsForDays, }) => {
39
- const connection = await connectionProvider();
40
- const wikiApi = await connection.getWikiApi();
41
- const pagesBatchRequest = {
42
- top,
43
- continuationToken,
44
- pageViewsForDays,
45
- };
46
- const pages = await wikiApi.getPagesBatch(pagesBatchRequest, project, wikiIdentifier);
47
- return {
48
- content: [{ type: "text", text: JSON.stringify(pages, null, 2) }],
49
- };
62
+ }, async ({ wikiIdentifier, project, top = 20, continuationToken, pageViewsForDays }) => {
63
+ try {
64
+ const connection = await connectionProvider();
65
+ const wikiApi = await connection.getWikiApi();
66
+ const pagesBatchRequest = {
67
+ top,
68
+ continuationToken,
69
+ pageViewsForDays,
70
+ };
71
+ const pages = await wikiApi.getPagesBatch(pagesBatchRequest, project, wikiIdentifier);
72
+ if (!pages) {
73
+ return { content: [{ type: "text", text: "No wiki pages found" }], isError: true };
74
+ }
75
+ return {
76
+ content: [{ type: "text", text: JSON.stringify(pages, null, 2) }],
77
+ };
78
+ }
79
+ catch (error) {
80
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
81
+ return {
82
+ content: [{ type: "text", text: `Error fetching wiki pages: ${errorMessage}` }],
83
+ isError: true,
84
+ };
85
+ }
50
86
  });
51
87
  server.tool(WIKI_TOOLS.get_wiki_page_content, "Retrieve wiki page content by wikiIdentifier and path.", {
52
88
  wikiIdentifier: z.string().describe("The unique identifier of the wiki."),
53
89
  project: z.string().describe("The project name or ID where the wiki is located."),
54
90
  path: z.string().describe("The path of the wiki page to retrieve content for."),
55
91
  }, async ({ wikiIdentifier, project, path }) => {
56
- const connection = await connectionProvider();
57
- const wikiApi = await connection.getWikiApi();
58
- const stream = await wikiApi.getPageText(project, wikiIdentifier, path, undefined, undefined, true);
59
- const content = await streamToString(stream);
60
- return {
61
- content: [{ type: "text", text: JSON.stringify(content, null, 2) }],
62
- };
92
+ try {
93
+ const connection = await connectionProvider();
94
+ const wikiApi = await connection.getWikiApi();
95
+ const stream = await wikiApi.getPageText(project, wikiIdentifier, path, undefined, undefined, true);
96
+ if (!stream) {
97
+ return { content: [{ type: "text", text: "No wiki page content found" }], isError: true };
98
+ }
99
+ const content = await streamToString(stream);
100
+ return {
101
+ content: [{ type: "text", text: JSON.stringify(content, null, 2) }],
102
+ };
103
+ }
104
+ catch (error) {
105
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
106
+ return {
107
+ content: [{ type: "text", text: `Error fetching wiki page content: ${errorMessage}` }],
108
+ isError: true,
109
+ };
110
+ }
63
111
  });
64
112
  }
65
113
  function streamToString(stream) {
@@ -13,58 +13,102 @@ 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
- const connection = await connectionProvider();
17
- const workApi = await connection.getWorkApi();
18
- const iterations = await workApi.getTeamIterations({ project, team }, timeframe);
19
- return {
20
- content: [{ type: "text", text: JSON.stringify(iterations, null, 2) }],
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."),
25
- iterations: z.array(z.object({
37
+ iterations: z
38
+ .array(z.object({
26
39
  iterationName: z.string().describe("The name of the iteration to create."),
27
40
  startDate: z.string().optional().describe("The start date of the iteration in ISO format (e.g., '2023-01-01T00:00:00Z'). Optional."),
28
- finishDate: z.string().optional().describe("The finish date of the iteration in ISO format (e.g., '2023-01-31T23:59:59Z'). Optional.")
29
- })).describe("An array of iterations to create. Each iteration must have a name and can optionally have start and finish dates in ISO format.")
41
+ finishDate: z.string().optional().describe("The finish date of the iteration in ISO format (e.g., '2023-01-31T23:59:59Z'). Optional."),
42
+ }))
43
+ .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
44
  }, async ({ project, iterations }) => {
31
- const connection = await connectionProvider();
32
- const workItemTrackingApi = await connection.getWorkItemTrackingApi();
33
- const results = [];
34
- for (const { iterationName, startDate, finishDate } of iterations) {
35
- // Step 1: Create the iteration
36
- const iteration = await workItemTrackingApi.createOrUpdateClassificationNode({
37
- name: iterationName,
38
- attributes: {
39
- startDate: startDate ? new Date(startDate) : undefined,
40
- finishDate: finishDate ? new Date(finishDate) : undefined,
41
- },
42
- }, project, TreeStructureGroup.Iterations);
43
- results.push(iteration);
45
+ try {
46
+ const connection = await connectionProvider();
47
+ const workItemTrackingApi = await connection.getWorkItemTrackingApi();
48
+ const results = [];
49
+ for (const { iterationName, startDate, finishDate } of iterations) {
50
+ // Step 1: Create the iteration
51
+ const iteration = await workItemTrackingApi.createOrUpdateClassificationNode({
52
+ name: iterationName,
53
+ attributes: {
54
+ startDate: startDate ? new Date(startDate) : undefined,
55
+ finishDate: finishDate ? new Date(finishDate) : undefined,
56
+ },
57
+ }, project, TreeStructureGroup.Iterations);
58
+ if (iteration) {
59
+ results.push(iteration);
60
+ }
61
+ }
62
+ if (results.length === 0) {
63
+ return { content: [{ type: "text", text: "No iterations were created" }], isError: true };
64
+ }
65
+ return {
66
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
67
+ };
68
+ }
69
+ catch (error) {
70
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
71
+ return {
72
+ content: [{ type: "text", text: `Error creating iterations: ${errorMessage}` }],
73
+ isError: true,
74
+ };
44
75
  }
45
- return {
46
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
47
- };
48
76
  });
49
77
  server.tool(WORK_TOOLS.assign_iterations, "Assign existing iterations to a specific team in a project.", {
50
78
  project: z.string().describe("The name or ID of the Azure DevOps project."),
51
79
  team: z.string().describe("The name or ID of the Azure DevOps team."),
52
- iterations: z.array(z.object({
80
+ iterations: z
81
+ .array(z.object({
53
82
  identifier: z.string().describe("The identifier of the iteration to assign."),
54
- path: z.string().describe("The path of the iteration to assign, e.g., 'Project/Iteration'.")
55
- })).describe("An array of iterations to assign. Each iteration must have an identifier and a path."),
83
+ path: z.string().describe("The path of the iteration to assign, e.g., 'Project/Iteration'."),
84
+ }))
85
+ .describe("An array of iterations to assign. Each iteration must have an identifier and a path."),
56
86
  }, async ({ project, team, iterations }) => {
57
- const connection = await connectionProvider();
58
- const workApi = await connection.getWorkApi();
59
- const teamContext = { project, team };
60
- const results = [];
61
- for (const { identifier, path } of iterations) {
62
- const assignment = await workApi.postTeamIteration({ path: path, id: identifier }, teamContext);
63
- results.push(assignment);
87
+ try {
88
+ const connection = await connectionProvider();
89
+ const workApi = await connection.getWorkApi();
90
+ const teamContext = { project, team };
91
+ const results = [];
92
+ for (const { identifier, path } of iterations) {
93
+ const assignment = await workApi.postTeamIteration({ path: path, id: identifier }, teamContext);
94
+ if (assignment) {
95
+ results.push(assignment);
96
+ }
97
+ }
98
+ if (results.length === 0) {
99
+ return { content: [{ type: "text", text: "No iterations were assigned to the team" }], isError: true };
100
+ }
101
+ return {
102
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
103
+ };
104
+ }
105
+ catch (error) {
106
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
107
+ return {
108
+ content: [{ type: "text", text: `Error assigning iterations: ${errorMessage}` }],
109
+ isError: true,
110
+ };
64
111
  }
65
- return {
66
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
67
- };
68
112
  });
69
113
  }
70
114
  export { WORK_TOOLS, configureWorkTools };