@azure-devops/mcp 1.3.1-nightly.20250813 β†’ 1.3.1-nightly.20250815

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 CHANGED
@@ -33,11 +33,25 @@ The Azure DevOps MCP Server brings Azure DevOps context to your agents. Try prom
33
33
  - "List iterations for project 'Contoso'"
34
34
  - "List my work items for project 'Contoso'"
35
35
  - "List work items in current iteration for 'Contoso' project and 'Contoso Team'"
36
+ - "List all wikis in the 'Contoso' project"
37
+ - "Create a wiki page '/Architecture/Overview' with content about system design"
38
+ - "Update the wiki page '/Getting Started' with new onboarding instructions"
39
+ - "Get the content of the wiki page '/API/Authentication' from the Documentation wiki"
36
40
 
37
41
  ## πŸ† Expectations
38
42
 
39
43
  The Azure DevOps MCP Server is built from tools that are concise, simple, focused, and easy to useβ€”each designed for a specific scenario. We intentionally avoid complex tools that try to do too much. The goal is to provide a thin abstraction layer over the REST APIs, making data access straightforward and letting the language model handle complex reasoning.
40
44
 
45
+ ## ✨ Recent Enhancements
46
+
47
+ ### πŸ“– **Enhanced Wiki Support**
48
+
49
+ - **Full Content Management**: Create and update wiki pages with complete content using the native Azure DevOps REST API
50
+ - **Automatic ETag Handling**: Safe updates with built-in conflict resolution for concurrent edits
51
+ - **Immediate Visibility**: Pages appear instantly in the Azure DevOps wiki interface
52
+ - **Hierarchical Structure**: Support for organized page structures within existing folder hierarchies
53
+ - **Robust Error Handling**: Comprehensive error management for various HTTP status codes and edge cases
54
+
41
55
  ## βš™οΈ Supported Tools
42
56
 
43
57
  Interact with these Azure DevOps services:
@@ -74,11 +88,7 @@ Interact with these Azure DevOps services:
74
88
  - **wit_update_work_items_batch**: Update work items in batch.
75
89
  - **wit_work_items_link**: Link work items together in batch.
76
90
  - **wit_work_item_unlink**: Unlink one or many links from a work item.
77
-
78
- #### Deprecated Tools
79
-
80
- - **wit_add_child_work_item**: Replaced by `wit_add_child_work_items` to allow creating one or more child items per call.
81
- - **wit_close_and_link_workitem_duplicates**: This tool is no longer needed. Finding and marking duplicates can be done with other tools.
91
+ - **wit_add_artifact_link**: Link to artifacts like branch, pull request, commit, and build.
82
92
 
83
93
  ### πŸ“ Repositories
84
94
 
@@ -133,6 +143,14 @@ Interact with these Azure DevOps services:
133
143
  - **testplan_list_test_cases**: Get a list of test cases in the test plan.
134
144
  - **testplan_show_test_results_from_build_id**: Get a list of test results for a given project and build ID.
135
145
 
146
+ ### πŸ“– Wiki
147
+
148
+ - **wiki_list_wikis**: Retrieve a list of wikis for an organization or project.
149
+ - **wiki_get_wiki**: Get the wiki by wikiIdentifier.
150
+ - **wiki_list_pages**: Retrieve a list of wiki pages for a specific wiki and project.
151
+ - **wiki_get_page_content**: Retrieve wiki page content by wikiIdentifier and path.
152
+ - **wiki_create_or_update_page**: Create or update wiki pages with full content support.
153
+
136
154
  ### πŸ”Ž Search
137
155
 
138
156
  - **search_code**: Get code search results for a given search text.
package/dist/prompts.js CHANGED
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  import { CORE_TOOLS } from "./tools/core.js";
5
5
  import { WORKITEM_TOOLS } from "./tools/workitems.js";
6
6
  function configurePrompts(server) {
7
- server.prompt("listProjects", "Lists all projects in the Azure DevOps organization.", {}, () => ({
7
+ server.prompt("Projects", "Lists all projects in the Azure DevOps organization.", {}, () => ({
8
8
  messages: [
9
9
  {
10
10
  role: "user",
@@ -12,13 +12,13 @@ function configurePrompts(server) {
12
12
  type: "text",
13
13
  text: String.raw `
14
14
  # Task
15
- Use the '${CORE_TOOLS.list_projects}' tool to retrieve all projects in the current Azure DevOps organization.
16
- Present the results in a table with the following columns: Project ID, Name, and Description.`,
15
+ Use the '${CORE_TOOLS.list_projects}' tool to retrieve all 'wellFormed' projects in the current Azure DevOps organization.
16
+ Present the results in alphabetical order in a table with the following columns: Name and ID.`,
17
17
  },
18
18
  },
19
19
  ],
20
20
  }));
21
- server.prompt("listTeams", "Retrieves all teams for a given Azure DevOps project.", { project: z.string() }, ({ project }) => ({
21
+ server.prompt("Teams", "Retrieves all teams for a given Azure DevOps project.", { project: z.string() }, ({ project }) => ({
22
22
  messages: [
23
23
  {
24
24
  role: "user",
@@ -27,7 +27,7 @@ Present the results in a table with the following columns: Project ID, Name, and
27
27
  text: String.raw `
28
28
  # Task
29
29
  Use the '${CORE_TOOLS.list_project_teams}' tool to retrieve all teams for the project '${project}'.
30
- Present the results in a table with the following columns: Team ID, and Name`,
30
+ Present the results in alphabetical order in a table with the following columns: Name and Id`,
31
31
  },
32
32
  },
33
33
  ],
@@ -6,6 +6,7 @@ const WIKI_TOOLS = {
6
6
  get_wiki: "wiki_get_wiki",
7
7
  list_wiki_pages: "wiki_list_pages",
8
8
  get_wiki_page_content: "wiki_get_page_content",
9
+ create_or_update_page: "wiki_create_or_update_page",
9
10
  };
10
11
  function configureWikiTools(server, tokenProvider, connectionProvider) {
11
12
  server.tool(WIKI_TOOLS.get_wiki, "Get the wiki by wikiIdentifier", {
@@ -109,6 +110,110 @@ function configureWikiTools(server, tokenProvider, connectionProvider) {
109
110
  };
110
111
  }
111
112
  });
113
+ server.tool(WIKI_TOOLS.create_or_update_page, "Create or update a wiki page with content.", {
114
+ wikiIdentifier: z.string().describe("The unique identifier or name of the wiki."),
115
+ path: z.string().describe("The path of the wiki page (e.g., '/Home' or '/Documentation/Setup')."),
116
+ content: z.string().describe("The content of the wiki page in markdown format."),
117
+ project: z.string().optional().describe("The project name or ID where the wiki is located. If not provided, the default project will be used."),
118
+ etag: z.string().optional().describe("ETag for editing existing pages (optional, will be fetched if not provided)."),
119
+ }, async ({ wikiIdentifier, path, content, project, etag }) => {
120
+ try {
121
+ const connection = await connectionProvider();
122
+ const accessToken = await tokenProvider();
123
+ // Normalize the path
124
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
125
+ const encodedPath = encodeURIComponent(normalizedPath);
126
+ // Build the URL for the wiki page API
127
+ const baseUrl = connection.serverUrl;
128
+ const projectParam = project || "";
129
+ const url = `${baseUrl}/${projectParam}/_apis/wiki/wikis/${wikiIdentifier}/pages?path=${encodedPath}&api-version=7.1`;
130
+ // First, try to create a new page (PUT without ETag)
131
+ try {
132
+ const createResponse = await fetch(url, {
133
+ method: "PUT",
134
+ headers: {
135
+ "Authorization": `Bearer ${accessToken.token}`,
136
+ "Content-Type": "application/json",
137
+ },
138
+ body: JSON.stringify({ content: content }),
139
+ });
140
+ if (createResponse.ok) {
141
+ const result = await createResponse.json();
142
+ return {
143
+ content: [
144
+ {
145
+ type: "text",
146
+ text: `Successfully created wiki page at path: ${normalizedPath}. Response: ${JSON.stringify(result, null, 2)}`,
147
+ },
148
+ ],
149
+ };
150
+ }
151
+ // If creation failed with 409 (Conflict) or 500 (Page exists), try to update it
152
+ if (createResponse.status === 409 || createResponse.status === 500) {
153
+ // Page exists, we need to get the ETag and update it
154
+ let currentEtag = etag;
155
+ if (!currentEtag) {
156
+ // Fetch current page to get ETag
157
+ const getResponse = await fetch(url, {
158
+ method: "GET",
159
+ headers: {
160
+ Authorization: `Bearer ${accessToken.token}`,
161
+ },
162
+ });
163
+ if (getResponse.ok) {
164
+ currentEtag = getResponse.headers.get("etag") || getResponse.headers.get("ETag") || undefined;
165
+ if (!currentEtag) {
166
+ const pageData = await getResponse.json();
167
+ currentEtag = pageData.eTag;
168
+ }
169
+ }
170
+ if (!currentEtag) {
171
+ throw new Error("Could not retrieve ETag for existing page");
172
+ }
173
+ }
174
+ // Now update the existing page with ETag
175
+ const updateResponse = await fetch(url, {
176
+ method: "PUT",
177
+ headers: {
178
+ "Authorization": `Bearer ${accessToken.token}`,
179
+ "Content-Type": "application/json",
180
+ "If-Match": currentEtag,
181
+ },
182
+ body: JSON.stringify({ content: content }),
183
+ });
184
+ if (updateResponse.ok) {
185
+ const result = await updateResponse.json();
186
+ return {
187
+ content: [
188
+ {
189
+ type: "text",
190
+ text: `Successfully updated wiki page at path: ${normalizedPath}. Response: ${JSON.stringify(result, null, 2)}`,
191
+ },
192
+ ],
193
+ };
194
+ }
195
+ else {
196
+ const errorText = await updateResponse.text();
197
+ throw new Error(`Failed to update page (${updateResponse.status}): ${errorText}`);
198
+ }
199
+ }
200
+ else {
201
+ const errorText = await createResponse.text();
202
+ throw new Error(`Failed to create page (${createResponse.status}): ${errorText}`);
203
+ }
204
+ }
205
+ catch (fetchError) {
206
+ throw fetchError;
207
+ }
208
+ }
209
+ catch (error) {
210
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
211
+ return {
212
+ content: [{ type: "text", text: `Error creating/updating wiki page: ${errorMessage}` }],
213
+ isError: true,
214
+ };
215
+ }
216
+ });
112
217
  }
113
218
  function streamToString(stream) {
114
219
  return new Promise((resolve, reject) => {
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const packageVersion = "1.3.1-nightly.20250813";
1
+ export const packageVersion = "1.3.1-nightly.20250815";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-devops/mcp",
3
- "version": "1.3.1-nightly.20250813",
3
+ "version": "1.3.1-nightly.20250815",
4
4
  "description": "MCP server for interacting with Azure DevOps",
5
5
  "license": "MIT",
6
6
  "author": "Microsoft Corporation",