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

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:
@@ -133,6 +147,14 @@ Interact with these Azure DevOps services:
133
147
  - **testplan_list_test_cases**: Get a list of test cases in the test plan.
134
148
  - **testplan_show_test_results_from_build_id**: Get a list of test results for a given project and build ID.
135
149
 
150
+ ### πŸ“– Wiki
151
+
152
+ - **wiki_list_wikis**: Retrieve a list of wikis for an organization or project.
153
+ - **wiki_get_wiki**: Get the wiki by wikiIdentifier.
154
+ - **wiki_list_pages**: Retrieve a list of wiki pages for a specific wiki and project.
155
+ - **wiki_get_page_content**: Retrieve wiki page content by wikiIdentifier and path.
156
+ - **wiki_create_or_update_page**: ✨ **Enhanced** - Create or update wiki pages with full content support using Azure DevOps REST API. Features automatic ETag handling for safe updates, immediate content visibility, and proper conflict resolution.
157
+
136
158
  ### πŸ”Ž Search
137
159
 
138
160
  - **search_code**: Get code search results for a given search text.
@@ -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,111 @@ 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
+ comment: z.string().optional().describe("Optional comment for the page update."),
119
+ etag: z.string().optional().describe("ETag for editing existing pages (optional, will be fetched if not provided)."),
120
+ }, async ({ wikiIdentifier, path, content, project, etag }) => {
121
+ try {
122
+ const connection = await connectionProvider();
123
+ const accessToken = await tokenProvider();
124
+ // Normalize the path
125
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
126
+ const encodedPath = encodeURIComponent(normalizedPath);
127
+ // Build the URL for the wiki page API
128
+ const baseUrl = connection.serverUrl;
129
+ const projectParam = project || "";
130
+ const url = `${baseUrl}/${projectParam}/_apis/wiki/wikis/${wikiIdentifier}/pages?path=${encodedPath}&api-version=7.1`;
131
+ // First, try to create a new page (PUT without ETag)
132
+ try {
133
+ const createResponse = await fetch(url, {
134
+ method: "PUT",
135
+ headers: {
136
+ "Authorization": `Bearer ${accessToken.token}`,
137
+ "Content-Type": "application/json",
138
+ },
139
+ body: JSON.stringify({ content: content }),
140
+ });
141
+ if (createResponse.ok) {
142
+ const result = await createResponse.json();
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: `Successfully created wiki page at path: ${normalizedPath}. Response: ${JSON.stringify(result, null, 2)}`,
148
+ },
149
+ ],
150
+ };
151
+ }
152
+ // If creation failed with 409 (Conflict) or 500 (Page exists), try to update it
153
+ if (createResponse.status === 409 || createResponse.status === 500) {
154
+ // Page exists, we need to get the ETag and update it
155
+ let currentEtag = etag;
156
+ if (!currentEtag) {
157
+ // Fetch current page to get ETag
158
+ const getResponse = await fetch(url, {
159
+ method: "GET",
160
+ headers: {
161
+ Authorization: `Bearer ${accessToken.token}`,
162
+ },
163
+ });
164
+ if (getResponse.ok) {
165
+ currentEtag = getResponse.headers.get("etag") || getResponse.headers.get("ETag") || undefined;
166
+ if (!currentEtag) {
167
+ const pageData = await getResponse.json();
168
+ currentEtag = pageData.eTag;
169
+ }
170
+ }
171
+ if (!currentEtag) {
172
+ throw new Error("Could not retrieve ETag for existing page");
173
+ }
174
+ }
175
+ // Now update the existing page with ETag
176
+ const updateResponse = await fetch(url, {
177
+ method: "PUT",
178
+ headers: {
179
+ "Authorization": `Bearer ${accessToken.token}`,
180
+ "Content-Type": "application/json",
181
+ "If-Match": currentEtag,
182
+ },
183
+ body: JSON.stringify({ content: content }),
184
+ });
185
+ if (updateResponse.ok) {
186
+ const result = await updateResponse.json();
187
+ return {
188
+ content: [
189
+ {
190
+ type: "text",
191
+ text: `Successfully updated wiki page at path: ${normalizedPath}. Response: ${JSON.stringify(result, null, 2)}`,
192
+ },
193
+ ],
194
+ };
195
+ }
196
+ else {
197
+ const errorText = await updateResponse.text();
198
+ throw new Error(`Failed to update page (${updateResponse.status}): ${errorText}`);
199
+ }
200
+ }
201
+ else {
202
+ const errorText = await createResponse.text();
203
+ throw new Error(`Failed to create page (${createResponse.status}): ${errorText}`);
204
+ }
205
+ }
206
+ catch (fetchError) {
207
+ throw fetchError;
208
+ }
209
+ }
210
+ catch (error) {
211
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
212
+ return {
213
+ content: [{ type: "text", text: `Error creating/updating wiki page: ${errorMessage}` }],
214
+ isError: true,
215
+ };
216
+ }
217
+ });
112
218
  }
113
219
  function streamToString(stream) {
114
220
  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.20250814";
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.20250814",
4
4
  "description": "MCP server for interacting with Azure DevOps",
5
5
  "license": "MIT",
6
6
  "author": "Microsoft Corporation",