@atlassian-dc-mcp/confluence 0.12.2 → 0.13.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/CHANGELOG.md +12 -0
- package/README.md +41 -2
- package/build/__tests__/config.test.d.ts +2 -0
- package/build/__tests__/config.test.d.ts.map +1 -0
- package/build/__tests__/config.test.js +49 -0
- package/build/__tests__/config.test.js.map +1 -0
- package/build/__tests__/confluence-response-mapper.test.d.ts +2 -0
- package/build/__tests__/confluence-response-mapper.test.d.ts.map +1 -0
- package/build/__tests__/confluence-response-mapper.test.js +24 -0
- package/build/__tests__/confluence-response-mapper.test.js.map +1 -0
- package/build/__tests__/confluence-service.test.js +125 -5
- package/build/__tests__/confluence-service.test.js.map +1 -1
- package/build/config.d.ts +4 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +11 -0
- package/build/config.js.map +1 -0
- package/build/confluence-response-mapper.d.ts +13 -0
- package/build/confluence-response-mapper.d.ts.map +1 -0
- package/build/confluence-response-mapper.js +87 -0
- package/build/confluence-response-mapper.js.map +1 -0
- package/build/confluence-service.d.ts +18 -4
- package/build/confluence-service.d.ts.map +1 -1
- package/build/confluence-service.js +42 -22
- package/build/confluence-service.js.map +1 -1
- package/build/index.js +27 -14
- package/build/index.js.map +1 -1
- package/package.json +3 -3
- package/server.json +12 -5
- package/src/__tests__/config.test.ts +64 -0
- package/src/__tests__/confluence-response-mapper.test.ts +26 -0
- package/src/__tests__/confluence-service.test.ts +161 -5
- package/src/config.ts +13 -0
- package/src/confluence-response-mapper.ts +104 -0
- package/src/confluence-service.ts +69 -26
- package/src/index.ts +31 -16
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export type ConfluenceBodyMode = 'storage' | 'text' | 'none';
|
|
2
|
+
export type ConfluenceMutationOutputMode = 'ack' | 'full';
|
|
3
|
+
|
|
4
|
+
const ENTITY_MAP: Record<string, string> = {
|
|
5
|
+
' ': ' ',
|
|
6
|
+
'&': '&',
|
|
7
|
+
'<': '<',
|
|
8
|
+
'>': '>',
|
|
9
|
+
'"': '"',
|
|
10
|
+
''': "'",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function decodeHtmlEntities(value: string): string {
|
|
14
|
+
return value
|
|
15
|
+
.replace(/&(nbsp|amp|lt|gt|quot);|'/g, match => ENTITY_MAP[match] ?? match)
|
|
16
|
+
.replace(/&#(\d+);/g, (_, code) => {
|
|
17
|
+
const parsed = Number.parseInt(code, 10);
|
|
18
|
+
return Number.isNaN(parsed) ? '' : String.fromCodePoint(parsed);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function truncateText(value: string, maxBodyChars?: number): { value: string; truncated?: boolean; originalLength?: number } {
|
|
23
|
+
if (maxBodyChars === undefined || maxBodyChars < 1 || value.length <= maxBodyChars) {
|
|
24
|
+
return { value };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
value: value.slice(0, maxBodyChars).trimEnd(),
|
|
29
|
+
truncated: true,
|
|
30
|
+
originalLength: value.length,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function confluenceStorageToText(storageValue: string): string {
|
|
35
|
+
const withLineBreaks = storageValue
|
|
36
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
37
|
+
.replace(/<li\b[^>]*>/gi, '\n- ')
|
|
38
|
+
.replace(/<\/(?:p|div|h[1-6]|li|tr|td|th|blockquote|pre|ul|ol|table|section|article)\s*>/gi, '\n')
|
|
39
|
+
.replace(/<[^>]+>/g, ' ');
|
|
40
|
+
|
|
41
|
+
return decodeHtmlEntities(withLineBreaks)
|
|
42
|
+
.replace(/\r\n/g, '\n')
|
|
43
|
+
.replace(/[ \t]+\n/g, '\n')
|
|
44
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
45
|
+
.replace(/[ \t]{2,}/g, ' ')
|
|
46
|
+
.trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getContentUrl(content: any): string | undefined {
|
|
50
|
+
const self = content?._links?.self;
|
|
51
|
+
if (typeof self === 'string') {
|
|
52
|
+
return self;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const base = content?._links?.base;
|
|
56
|
+
const webui = content?._links?.webui;
|
|
57
|
+
if (typeof base === 'string' && typeof webui === 'string') {
|
|
58
|
+
return `${base}${webui}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function shapeConfluenceContent(content: any, bodyMode: ConfluenceBodyMode = 'storage', maxBodyChars?: number) {
|
|
65
|
+
if (!content || typeof content !== 'object' || bodyMode === 'storage') {
|
|
66
|
+
return content;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { body, ...rest } = content as Record<string, any>;
|
|
70
|
+
|
|
71
|
+
if (bodyMode === 'none') {
|
|
72
|
+
return rest;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const storageValue = typeof body?.storage?.value === 'string' ? body.storage.value : undefined;
|
|
76
|
+
if (storageValue === undefined) {
|
|
77
|
+
return rest;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const textBody = truncateText(confluenceStorageToText(storageValue), maxBodyChars);
|
|
81
|
+
return {
|
|
82
|
+
...rest,
|
|
83
|
+
body: {
|
|
84
|
+
text: {
|
|
85
|
+
value: textBody.value,
|
|
86
|
+
representation: 'text',
|
|
87
|
+
...(textBody.truncated ? { truncated: true, originalLength: textBody.originalLength } : {}),
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function shapeConfluenceMutationAck(content: any) {
|
|
94
|
+
const url = getContentUrl(content);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
...(content?.id !== undefined ? { id: content.id } : {}),
|
|
98
|
+
...(typeof content?.type === 'string' ? { type: content.type } : {}),
|
|
99
|
+
...(typeof content?.title === 'string' ? { title: content.title } : {}),
|
|
100
|
+
...(typeof content?.space?.key === 'string' ? { spaceKey: content.space.key } : {}),
|
|
101
|
+
...(content?.version?.number !== undefined ? { version: content.version.number } : {}),
|
|
102
|
+
...(url ? { url } : {}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ContentResourceService, OpenAPI, SearchService } from './confluence-client/index.js';
|
|
3
3
|
import { handleApiOperation } from '@atlassian-dc-mcp/common';
|
|
4
|
+
import { getDefaultPageSize, getMissingConfig } from './config.js';
|
|
5
|
+
import { ConfluenceBodyMode, shapeConfluenceContent } from './confluence-response-mapper.js';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Escapes user input for safe use inside a CQL quoted string.
|
|
@@ -31,14 +33,31 @@ export interface ConfluenceContent {
|
|
|
31
33
|
ancestors?: Array<{ id: string }>;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
function resolveToken(token: string | (() => string | undefined), missingTokenMessage: string) {
|
|
37
|
+
return async () => {
|
|
38
|
+
const resolvedToken = typeof token === 'function' ? token() : token;
|
|
39
|
+
if (!resolvedToken) {
|
|
40
|
+
throw new Error(missingTokenMessage);
|
|
41
|
+
}
|
|
42
|
+
return resolvedToken;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
export class ConfluenceService {
|
|
47
|
+
private readonly getPageSize: () => number;
|
|
48
|
+
|
|
35
49
|
/**
|
|
36
50
|
* Creates a new ConfluenceService instance
|
|
37
51
|
* @param host The hostname of the Confluence server (e.g., "host.com")
|
|
38
52
|
* @param token The API token for authentication
|
|
39
53
|
* @param fullApiUrl Optional full API URL (e.g., "https://host.com/wiki/"). If provided, host and apiBasePath are ignored.
|
|
40
54
|
*/
|
|
41
|
-
constructor(
|
|
55
|
+
constructor(
|
|
56
|
+
host: string | undefined,
|
|
57
|
+
token: string | (() => string | undefined),
|
|
58
|
+
fullApiUrl?: string,
|
|
59
|
+
getPageSize: () => number = getDefaultPageSize,
|
|
60
|
+
) {
|
|
42
61
|
if (fullApiUrl) {
|
|
43
62
|
OpenAPI.BASE = fullApiUrl;
|
|
44
63
|
} else if (host) {
|
|
@@ -46,15 +65,16 @@ export class ConfluenceService {
|
|
|
46
65
|
} else {
|
|
47
66
|
throw new Error('Either host or fullApiUrl must be provided');
|
|
48
67
|
}
|
|
49
|
-
OpenAPI.TOKEN = token;
|
|
68
|
+
OpenAPI.TOKEN = resolveToken(token, 'Missing required environment variable: CONFLUENCE_API_TOKEN');
|
|
50
69
|
OpenAPI.VERSION = '1.0';
|
|
70
|
+
this.getPageSize = getPageSize;
|
|
51
71
|
}
|
|
52
72
|
/**
|
|
53
73
|
* Get a Confluence page by ID
|
|
54
74
|
* @param contentId The ID of the page to retrieve
|
|
55
75
|
* @param expand Optional comma-separated list of properties to expand
|
|
56
76
|
*/
|
|
57
|
-
async
|
|
77
|
+
async getContentRaw(contentId: string, expand?: string) {
|
|
58
78
|
const expandValue = expand || 'body.storage';
|
|
59
79
|
const finalExpand = expand && !expand.includes('body.storage')
|
|
60
80
|
? `${expand},body.storage`
|
|
@@ -62,6 +82,18 @@ export class ConfluenceService {
|
|
|
62
82
|
return handleApiOperation(() => ContentResourceService.getContentById(contentId, finalExpand), 'Error getting content');
|
|
63
83
|
}
|
|
64
84
|
|
|
85
|
+
async getContent(contentId: string, expand?: string, bodyMode: ConfluenceBodyMode = 'storage', maxBodyChars?: number) {
|
|
86
|
+
const result = await this.getContentRaw(contentId, expand);
|
|
87
|
+
if (result.success && result.data) {
|
|
88
|
+
return {
|
|
89
|
+
...result,
|
|
90
|
+
data: shapeConfluenceContent(result.data, bodyMode, maxBodyChars),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
65
97
|
/**
|
|
66
98
|
* Search for content in Confluence using CQL
|
|
67
99
|
* @param cql Confluence Query Language string
|
|
@@ -69,8 +101,19 @@ export class ConfluenceService {
|
|
|
69
101
|
* @param start Start index for pagination
|
|
70
102
|
* @param expand Optional comma-separated list of properties to expand
|
|
71
103
|
*/
|
|
72
|
-
async searchContent(cql: string, limit?: number, start?: number, expand?: string) {
|
|
73
|
-
return handleApiOperation(
|
|
104
|
+
async searchContent(cql: string, limit?: number, start?: number, expand?: string, excerpt: 'none' | 'highlight' = 'none') {
|
|
105
|
+
return handleApiOperation(
|
|
106
|
+
() => SearchService.search1(
|
|
107
|
+
undefined,
|
|
108
|
+
expand,
|
|
109
|
+
undefined,
|
|
110
|
+
(limit ?? this.getPageSize()).toString(),
|
|
111
|
+
start?.toString(),
|
|
112
|
+
excerpt,
|
|
113
|
+
cql
|
|
114
|
+
),
|
|
115
|
+
'Error searching for content'
|
|
116
|
+
);
|
|
74
117
|
}
|
|
75
118
|
|
|
76
119
|
/**
|
|
@@ -97,7 +140,13 @@ export class ConfluenceService {
|
|
|
97
140
|
* @param start Start index for pagination
|
|
98
141
|
* @param expand Optional comma-separated list of properties to expand
|
|
99
142
|
*/
|
|
100
|
-
async searchSpaces(
|
|
143
|
+
async searchSpaces(
|
|
144
|
+
searchText: string,
|
|
145
|
+
limit?: number,
|
|
146
|
+
start?: number,
|
|
147
|
+
expand?: string,
|
|
148
|
+
excerpt: 'none' | 'highlight' = 'none'
|
|
149
|
+
) {
|
|
101
150
|
// Create a CQL query that searches for spaces
|
|
102
151
|
// The correct syntax for space search is: type=space AND title ~ "searchText"
|
|
103
152
|
const escapedSearchText = escapeSearchTextForCql(searchText);
|
|
@@ -107,59 +156,53 @@ export class ConfluenceService {
|
|
|
107
156
|
undefined,
|
|
108
157
|
expand,
|
|
109
158
|
undefined,
|
|
110
|
-
limit
|
|
159
|
+
(limit ?? this.getPageSize()).toString(),
|
|
111
160
|
start?.toString(),
|
|
112
|
-
|
|
161
|
+
excerpt,
|
|
113
162
|
cql
|
|
114
163
|
), 'Error searching for spaces');
|
|
115
164
|
}
|
|
116
165
|
|
|
117
166
|
static validateConfig(): string[] {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// API token is always required
|
|
121
|
-
if (!process.env.CONFLUENCE_API_TOKEN) {
|
|
122
|
-
missingVars.push('CONFLUENCE_API_TOKEN');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Either CONFLUENCE_HOST or CONFLUENCE_API_BASE_PATH must be set
|
|
126
|
-
if (!process.env.CONFLUENCE_HOST && !process.env.CONFLUENCE_API_BASE_PATH) {
|
|
127
|
-
missingVars.push('CONFLUENCE_HOST or CONFLUENCE_API_BASE_PATH');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return missingVars;
|
|
167
|
+
return getMissingConfig();
|
|
131
168
|
}
|
|
132
169
|
}
|
|
133
170
|
|
|
134
171
|
export const confluenceToolSchemas = {
|
|
135
172
|
getContent: {
|
|
136
173
|
contentId: z.string().describe("Confluence Data Center content ID"),
|
|
137
|
-
expand: z.string().optional().describe("Comma-separated list of properties to expand")
|
|
174
|
+
expand: z.string().optional().describe("Comma-separated list of properties to expand"),
|
|
175
|
+
bodyMode: z.enum(['storage', 'text', 'none']).optional().describe("How to return the page body. Defaults to storage for backward compatibility."),
|
|
176
|
+
maxBodyChars: z.number().optional().describe("Maximum number of characters to keep when bodyMode is text")
|
|
138
177
|
},
|
|
139
178
|
searchContent: {
|
|
140
179
|
cql: z.string().describe("Confluence Query Language (CQL) search string for Confluence Data Center"),
|
|
141
180
|
limit: z.number().optional().describe("Maximum number of results to return"),
|
|
142
181
|
start: z.number().optional().describe("Start index for pagination"),
|
|
143
|
-
expand: z.string().optional().describe("Comma-separated list of properties to expand")
|
|
182
|
+
expand: z.string().optional().describe("Comma-separated list of properties to expand"),
|
|
183
|
+
excerpt: z.enum(['none', 'highlight']).optional().describe("Excerpt mode for search results. Defaults to none.")
|
|
144
184
|
},
|
|
145
185
|
createContent: {
|
|
146
186
|
title: z.string().describe("Title of the content"),
|
|
147
187
|
spaceKey: z.string().describe("Space key where content will be created"),
|
|
148
188
|
type: z.string().default("page").describe("Content type (page, blogpost, etc)"),
|
|
149
189
|
content: z.string().describe("Content body in Confluence Data Center \"storage\" format (confluence XML)"),
|
|
150
|
-
parentId: z.string().optional().describe("ID of the parent page (if creating a child page)")
|
|
190
|
+
parentId: z.string().optional().describe("ID of the parent page (if creating a child page)"),
|
|
191
|
+
output: z.enum(['ack', 'full']).optional().describe("Return a compact acknowledgement or the full API response. Defaults to ack.")
|
|
151
192
|
},
|
|
152
193
|
updateContent: {
|
|
153
194
|
contentId: z.string().describe("ID of the content to update"),
|
|
154
195
|
title: z.string().optional().describe("New title of the content"),
|
|
155
196
|
content: z.string().optional().describe("New content body in Confluence Data Center storage format (XML-based)"),
|
|
156
197
|
version: z.number().describe("New version number (must be incremented)"),
|
|
157
|
-
versionComment: z.string().optional().describe("Comment for this version")
|
|
198
|
+
versionComment: z.string().optional().describe("Comment for this version"),
|
|
199
|
+
output: z.enum(['ack', 'full']).optional().describe("Return a compact acknowledgement or the full API response. Defaults to ack.")
|
|
158
200
|
},
|
|
159
201
|
searchSpaces: {
|
|
160
202
|
searchText: z.string().describe("Text to search for in Confluence Data Center space names or descriptions. Quotes and backslashes are escaped for CQL; pass the literal search phrase only (do not pre-escape)."),
|
|
161
203
|
limit: z.number().optional().describe("Maximum number of results to return"),
|
|
162
204
|
start: z.number().optional().describe("Start index for pagination"),
|
|
163
|
-
expand: z.string().optional().describe("Comma-separated list of properties to expand")
|
|
205
|
+
expand: z.string().optional().describe("Comma-separated list of properties to expand"),
|
|
206
|
+
excerpt: z.enum(['none', 'highlight']).optional().describe("Excerpt mode for search results. Defaults to none.")
|
|
164
207
|
}
|
|
165
208
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { connectServer, createMcpServer, formatToolResponse } from '@atlassian-dc-mcp/common';
|
|
1
|
+
import { connectServer, createMcpServer, formatToolResponse, initializeRuntimeConfig } from '@atlassian-dc-mcp/common';
|
|
2
2
|
import { ConfluenceService, ConfluenceContent, confluenceToolSchemas } from './confluence-service.js';
|
|
3
|
-
import
|
|
3
|
+
import { shapeConfluenceMutationAck } from './confluence-response-mapper.js';
|
|
4
|
+
import { getConfluenceRuntimeConfig, getDefaultPageSize } from './config.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
dotenv.config();
|
|
6
|
+
initializeRuntimeConfig();
|
|
7
7
|
|
|
8
8
|
// Validate required environment variables
|
|
9
9
|
const missingEnvVars = ConfluenceService.validateConfig();
|
|
@@ -12,10 +12,12 @@ if (missingEnvVars.length > 0) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
// Initialize Confluence service
|
|
15
|
+
const confluenceConfig = getConfluenceRuntimeConfig();
|
|
15
16
|
const confluenceService = new ConfluenceService(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
confluenceConfig.host,
|
|
18
|
+
() => getConfluenceRuntimeConfig().token,
|
|
19
|
+
confluenceConfig.apiBasePath,
|
|
20
|
+
getDefaultPageSize
|
|
19
21
|
);
|
|
20
22
|
|
|
21
23
|
// Define Confluence instance type
|
|
@@ -32,8 +34,8 @@ server.tool(
|
|
|
32
34
|
"confluence_getContent",
|
|
33
35
|
`Get Confluence content by ID from the ${confluenceInstanceType}`,
|
|
34
36
|
confluenceToolSchemas.getContent,
|
|
35
|
-
async ({ contentId, expand }) => {
|
|
36
|
-
const result = await confluenceService.getContent(contentId, expand);
|
|
37
|
+
async ({ contentId, expand, bodyMode, maxBodyChars }) => {
|
|
38
|
+
const result = await confluenceService.getContent(contentId, expand, bodyMode, maxBodyChars);
|
|
37
39
|
return formatToolResponse(result);
|
|
38
40
|
}
|
|
39
41
|
);
|
|
@@ -42,8 +44,8 @@ server.tool(
|
|
|
42
44
|
"confluence_searchContent",
|
|
43
45
|
`Search for content in ${confluenceInstanceType} using CQL`,
|
|
44
46
|
confluenceToolSchemas.searchContent,
|
|
45
|
-
async ({ cql, limit, start, expand }) => {
|
|
46
|
-
const result = await confluenceService.searchContent(cql, limit, start, expand);
|
|
47
|
+
async ({ cql, limit, start, expand, excerpt }) => {
|
|
48
|
+
const result = await confluenceService.searchContent(cql, limit, start, expand, excerpt);
|
|
47
49
|
return formatToolResponse(result);
|
|
48
50
|
}
|
|
49
51
|
);
|
|
@@ -52,7 +54,7 @@ server.tool(
|
|
|
52
54
|
"confluence_createContent",
|
|
53
55
|
`Create new content in ${confluenceInstanceType}`,
|
|
54
56
|
confluenceToolSchemas.createContent,
|
|
55
|
-
async ({ title, spaceKey, type, content, parentId }) => {
|
|
57
|
+
async ({ title, spaceKey, type, content, parentId, output }) => {
|
|
56
58
|
const contentObj: ConfluenceContent = {
|
|
57
59
|
type: type || 'page',
|
|
58
60
|
title,
|
|
@@ -71,6 +73,12 @@ server.tool(
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
const result = await confluenceService.createContent(contentObj);
|
|
76
|
+
if (result.success && result.data && output !== 'full') {
|
|
77
|
+
return formatToolResponse({
|
|
78
|
+
...result,
|
|
79
|
+
data: shapeConfluenceMutationAck(result.data),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
74
82
|
return formatToolResponse(result);
|
|
75
83
|
}
|
|
76
84
|
);
|
|
@@ -79,9 +87,9 @@ server.tool(
|
|
|
79
87
|
"confluence_updateContent",
|
|
80
88
|
`Update existing content in ${confluenceInstanceType}`,
|
|
81
89
|
confluenceToolSchemas.updateContent,
|
|
82
|
-
async ({ contentId, title, content, version, versionComment }) => {
|
|
90
|
+
async ({ contentId, title, content, version, versionComment, output }) => {
|
|
83
91
|
// First get the current content to build upon
|
|
84
|
-
const currentContent = await confluenceService.
|
|
92
|
+
const currentContent = await confluenceService.getContentRaw(contentId);
|
|
85
93
|
|
|
86
94
|
if (!currentContent.success || !currentContent.data) {
|
|
87
95
|
return formatToolResponse({
|
|
@@ -119,6 +127,12 @@ server.tool(
|
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
const result = await confluenceService.updateContent(contentId, updateObj);
|
|
130
|
+
if (result.success && result.data && output !== 'full') {
|
|
131
|
+
return formatToolResponse({
|
|
132
|
+
...result,
|
|
133
|
+
data: shapeConfluenceMutationAck(result.data),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
122
136
|
return formatToolResponse(result);
|
|
123
137
|
}
|
|
124
138
|
);
|
|
@@ -130,9 +144,10 @@ server.tool('confluence_searchSpace',
|
|
|
130
144
|
searchText,
|
|
131
145
|
limit,
|
|
132
146
|
start,
|
|
133
|
-
expand
|
|
147
|
+
expand,
|
|
148
|
+
excerpt
|
|
134
149
|
}) => {
|
|
135
|
-
const result = await confluenceService.searchSpaces(searchText, limit, start, expand);
|
|
150
|
+
const result = await confluenceService.searchSpaces(searchText, limit, start, expand, excerpt);
|
|
136
151
|
return formatToolResponse(result);
|
|
137
152
|
});
|
|
138
153
|
|