@ecubelabs/atlassian-mcp 1.4.0-next.1 → 1.4.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/dist/confluence-tools/add-labels-to-page.js +37 -0
- package/dist/confluence-tools/confluence-storage-format-help.js +257 -0
- package/dist/confluence-tools/create-page.js +51 -0
- package/dist/confluence-tools/delete-page.js +39 -0
- package/dist/confluence-tools/find-label-by-name.js +40 -0
- package/dist/confluence-tools/find-pages-by-label-name.js +59 -0
- package/dist/confluence-tools/find-space-by-name.js +40 -0
- package/dist/confluence-tools/get-labels.js +46 -0
- package/dist/confluence-tools/get-page-by-id.js +38 -0
- package/dist/confluence-tools/get-page-children-tree.js +57 -0
- package/dist/confluence-tools/get-page-children.js +52 -0
- package/dist/confluence-tools/get-pages-for-label.js +57 -0
- package/dist/confluence-tools/get-pages.js +67 -0
- package/dist/confluence-tools/get-recently-created-pages.js +42 -0
- package/dist/confluence-tools/get-recently-updated-pages.js +42 -0
- package/dist/confluence-tools/get-space-by-id.js +28 -0
- package/dist/confluence-tools/get-spaces.js +67 -0
- package/dist/confluence-tools/index.js +22 -0
- package/dist/confluence-tools/remove-label-from-page.js +29 -0
- package/dist/confluence-tools/update-page.js +54 -0
- package/dist/confluence-tools-register.js +23 -610
- package/dist/libs/confluence-client.js +77 -0
- package/dist/libs/confluence-storage-format-utils.js +468 -0
- package/package.json +2 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerAddLabelsToPage = (server, confluenceService) => {
|
|
3
|
+
server.tool('add-labels-to-page', 'Add one or more labels to a Confluence page', {
|
|
4
|
+
pageId: z.string().describe('ID of the page to add labels to'),
|
|
5
|
+
labels: z
|
|
6
|
+
.array(z.object({
|
|
7
|
+
name: z.string().describe('Name of the label'),
|
|
8
|
+
prefix: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Optional prefix for the label (e.g., "global", "team")'),
|
|
12
|
+
}))
|
|
13
|
+
.describe('Array of labels to add to the page'),
|
|
14
|
+
}, async ({ pageId, labels }) => {
|
|
15
|
+
try {
|
|
16
|
+
const result = await confluenceService.addLabelsToPage(pageId, labels);
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: JSON.stringify(result),
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: 'text',
|
|
31
|
+
text: `Failed to add labels to page: ${error instanceof Error ? error.message : String(error)}`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerConfluenceStorageFormatHelp = (server) => {
|
|
3
|
+
server.tool('confluence-storage-format-help', '⚠️ MANDATORY: ALWAYS use this tool BEFORE creating/updating Confluence pages. 🚫 CRITICAL: All examples use storage format XML ONLY - NEVER use wiki markup ({info}, {code}), NEVER use placeholder text ([Panel], [Code Block]), NEVER use plain text for Jira/dates/users. ✅ This tool provides ONLY valid storage format syntax that will work in Confluence. Copy examples exactly.', {
|
|
4
|
+
topic: z
|
|
5
|
+
.enum([
|
|
6
|
+
'overview',
|
|
7
|
+
'best-practices',
|
|
8
|
+
'common-patterns',
|
|
9
|
+
'headings',
|
|
10
|
+
'text-formatting',
|
|
11
|
+
'lists',
|
|
12
|
+
'tables',
|
|
13
|
+
'code-blocks',
|
|
14
|
+
'panels',
|
|
15
|
+
'links',
|
|
16
|
+
'images',
|
|
17
|
+
'macros',
|
|
18
|
+
'layout',
|
|
19
|
+
'complete-example',
|
|
20
|
+
])
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Specific topic to get help on. Use "overview" for mandatory rules, "macros" for Jira/date/user mention requirements, "best-practices" for enforcement guidelines, "common-patterns" for document templates. ALWAYS check this tool before writing Confluence content to ensure correct syntax.'),
|
|
23
|
+
}, async ({ topic = 'overview' }) => {
|
|
24
|
+
const examples = {
|
|
25
|
+
overview: `# Confluence Storage Format - Critical Rules
|
|
26
|
+
|
|
27
|
+
## ❌ FORBIDDEN Syntax
|
|
28
|
+
1. Wiki Markup: {info}, {warning}, {code}, {jira:KEY-123}
|
|
29
|
+
2. Placeholders: [Info Panel], [Code Block], [Table]
|
|
30
|
+
3. Plain text: "HA-2584", "2025-12-03", "@username"
|
|
31
|
+
|
|
32
|
+
## ✅ MANDATORY Macros
|
|
33
|
+
1. **Jira**: <ac:structured-macro ac:name="jira"><ac:parameter ac:name="key">HA-2584</ac:parameter></ac:structured-macro>
|
|
34
|
+
2. **Date**: <time datetime="2025-12-03" />
|
|
35
|
+
3. **User**: <ac:link><ri:user ri:account-id="ACCOUNT_ID" /></ac:link>
|
|
36
|
+
|
|
37
|
+
## Key Rules
|
|
38
|
+
- Use h2/h3 for headings (not h1)
|
|
39
|
+
- Put text directly in <li> tags (NO <p> wrapper)
|
|
40
|
+
- Always use storage format XML, never wiki markup
|
|
41
|
+
- Check specific topics for detailed syntax`,
|
|
42
|
+
headings: `# Headings
|
|
43
|
+
<h2>Main Section</h2>
|
|
44
|
+
<h3>Subsection</h3>
|
|
45
|
+
|
|
46
|
+
Rules: Use h2 for main sections, h3 for subsections. Don't skip levels.`,
|
|
47
|
+
'text-formatting': `# Text Formatting
|
|
48
|
+
<strong>Bold</strong>, <em>Italic</em>, <u>Underline</u>, <s>Strikethrough</s>
|
|
49
|
+
|
|
50
|
+
Use sparingly for emphasis.`,
|
|
51
|
+
lists: `# Lists
|
|
52
|
+
|
|
53
|
+
## 🚫 CRITICAL: NO <p> tags inside <li>
|
|
54
|
+
❌ WRONG: <li><p>Item</p></li>
|
|
55
|
+
✅ CORRECT: <li>Item</li>
|
|
56
|
+
|
|
57
|
+
## Syntax
|
|
58
|
+
**Simple list:**
|
|
59
|
+
<ul>
|
|
60
|
+
<li>Item 1</li>
|
|
61
|
+
<li>Item 2</li>
|
|
62
|
+
</ul>
|
|
63
|
+
|
|
64
|
+
**Nested list:**
|
|
65
|
+
<ul>
|
|
66
|
+
<li>Parent item
|
|
67
|
+
<ul>
|
|
68
|
+
<li>Child item</li>
|
|
69
|
+
</ul>
|
|
70
|
+
</li>
|
|
71
|
+
</ul>
|
|
72
|
+
|
|
73
|
+
**Ordered list:**
|
|
74
|
+
<ol>
|
|
75
|
+
<li>Step 1</li>
|
|
76
|
+
<li>Step 2</li>
|
|
77
|
+
</ol>`,
|
|
78
|
+
tables: `# Tables
|
|
79
|
+
<table>
|
|
80
|
+
<tbody>
|
|
81
|
+
<tr>
|
|
82
|
+
<th>Header 1</th>
|
|
83
|
+
<th>Header 2</th>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td>Cell 1</td>
|
|
87
|
+
<td>Cell 2</td>
|
|
88
|
+
</tr>
|
|
89
|
+
</tbody>
|
|
90
|
+
</table>`,
|
|
91
|
+
'code-blocks': `# Code Blocks
|
|
92
|
+
<ac:structured-macro ac:name="code" ac:schema-version="1">
|
|
93
|
+
<ac:parameter ac:name="language">javascript</ac:parameter>
|
|
94
|
+
<ac:plain-text-body><![CDATA[const x = 1;]]></ac:plain-text-body>
|
|
95
|
+
</ac:structured-macro>
|
|
96
|
+
|
|
97
|
+
Languages: javascript, typescript, python, bash, json, yaml, etc.`,
|
|
98
|
+
panels: `# Panels
|
|
99
|
+
|
|
100
|
+
**Info Panel:**
|
|
101
|
+
<ac:structured-macro ac:name="info" ac:schema-version="1">
|
|
102
|
+
<ac:parameter ac:name="icon">true</ac:parameter>
|
|
103
|
+
<ac:rich-text-body>
|
|
104
|
+
<p>Information message</p>
|
|
105
|
+
</ac:rich-text-body>
|
|
106
|
+
</ac:structured-macro>
|
|
107
|
+
|
|
108
|
+
**Warning Panel:**
|
|
109
|
+
<ac:structured-macro ac:name="warning" ac:schema-version="1">
|
|
110
|
+
<ac:parameter ac:name="icon">true</ac:parameter>
|
|
111
|
+
<ac:rich-text-body>
|
|
112
|
+
<p>Warning message</p>
|
|
113
|
+
</ac:rich-text-body>
|
|
114
|
+
</ac:structured-macro>
|
|
115
|
+
|
|
116
|
+
**Note Panel:**
|
|
117
|
+
<ac:structured-macro ac:name="note" ac:schema-version="1">
|
|
118
|
+
<ac:parameter ac:name="icon">true</ac:parameter>
|
|
119
|
+
<ac:rich-text-body>
|
|
120
|
+
<p>Note message</p>
|
|
121
|
+
</ac:rich-text-body>
|
|
122
|
+
</ac:structured-macro>`,
|
|
123
|
+
links: `# Links
|
|
124
|
+
|
|
125
|
+
## User Mention (MANDATORY for people)
|
|
126
|
+
❌ NEVER: "@username" or "John Doe"
|
|
127
|
+
✅ ALWAYS:
|
|
128
|
+
<ac:link>
|
|
129
|
+
<ri:user ri:account-id="ACCOUNT_ID" />
|
|
130
|
+
</ac:link>
|
|
131
|
+
|
|
132
|
+
## External Link
|
|
133
|
+
<a href="https://example.com">Link text</a>
|
|
134
|
+
|
|
135
|
+
## Page Link
|
|
136
|
+
<ac:link>
|
|
137
|
+
<ri:page ri:content-title="Page Title" />
|
|
138
|
+
</ac:link>`,
|
|
139
|
+
images: `# Images
|
|
140
|
+
<ac:image>
|
|
141
|
+
<ri:attachment ri:filename="image.png" />
|
|
142
|
+
</ac:image>
|
|
143
|
+
|
|
144
|
+
Attach images to page first, then reference by filename.`,
|
|
145
|
+
macros: `# Macros
|
|
146
|
+
|
|
147
|
+
## Table of Contents (TOC)
|
|
148
|
+
<ac:structured-macro ac:name="toc" ac:schema-version="1">
|
|
149
|
+
<ac:parameter ac:name="maxLevel">3</ac:parameter>
|
|
150
|
+
<ac:parameter ac:name="style">none</ac:parameter>
|
|
151
|
+
</ac:structured-macro>
|
|
152
|
+
|
|
153
|
+
## Jira Issue (MANDATORY for Jira keys)
|
|
154
|
+
❌ NEVER: "HA-2584" or plain text
|
|
155
|
+
✅ ALWAYS:
|
|
156
|
+
<ac:structured-macro ac:name="jira" ac:schema-version="1">
|
|
157
|
+
<ac:parameter ac:name="key">HA-2584</ac:parameter>
|
|
158
|
+
</ac:structured-macro>
|
|
159
|
+
|
|
160
|
+
## Date (MANDATORY for dates)
|
|
161
|
+
❌ NEVER: "2025-12-03" or plain text
|
|
162
|
+
✅ ALWAYS:
|
|
163
|
+
<time datetime="2025-12-03" />
|
|
164
|
+
|
|
165
|
+
## Status Chip
|
|
166
|
+
<ac:structured-macro ac:name="status" ac:schema-version="1">
|
|
167
|
+
<ac:parameter ac:name="colour">Green</ac:parameter>
|
|
168
|
+
<ac:parameter ac:name="title">완료</ac:parameter>
|
|
169
|
+
</ac:structured-macro>
|
|
170
|
+
|
|
171
|
+
Colors: Green, Yellow, Red, Blue, Grey, Purple`,
|
|
172
|
+
layout: `# Layout
|
|
173
|
+
<ac:layout>
|
|
174
|
+
<ac:layout-section ac:type="two_equal">
|
|
175
|
+
<ac:layout-cell>
|
|
176
|
+
<p>Left column</p>
|
|
177
|
+
</ac:layout-cell>
|
|
178
|
+
<ac:layout-cell>
|
|
179
|
+
<p>Right column</p>
|
|
180
|
+
</ac:layout-cell>
|
|
181
|
+
</ac:layout-section>
|
|
182
|
+
</ac:layout>
|
|
183
|
+
|
|
184
|
+
Types: two_equal, three_equal. Use sparingly.`,
|
|
185
|
+
'best-practices': `# Best Practices
|
|
186
|
+
|
|
187
|
+
## MANDATORY Rules
|
|
188
|
+
1. Jira keys → MUST use Jira macro
|
|
189
|
+
2. Dates → MUST use <time> element
|
|
190
|
+
3. People → MUST use user mention link
|
|
191
|
+
4. Lists → NEVER use <p> inside <li>
|
|
192
|
+
|
|
193
|
+
## Document Structure
|
|
194
|
+
- Start with h2 (not h1)
|
|
195
|
+
- Use panels for important info only
|
|
196
|
+
- Keep lists concise
|
|
197
|
+
- Use storage format XML only`,
|
|
198
|
+
'common-patterns': `# Common Patterns
|
|
199
|
+
|
|
200
|
+
## Meeting Notes
|
|
201
|
+
<h2>참여자</h2>
|
|
202
|
+
<ul>
|
|
203
|
+
<li><ac:link><ri:user ri:account-id="ACCOUNT_ID" /></ac:link></li>
|
|
204
|
+
</ul>
|
|
205
|
+
<h2>WIP</h2>
|
|
206
|
+
<ul><li>Task 1</li></ul>
|
|
207
|
+
<h2>ISSUE</h2>
|
|
208
|
+
|
|
209
|
+
## Specification Document
|
|
210
|
+
<ac:structured-macro ac:name="info">
|
|
211
|
+
<ac:rich-text-body>
|
|
212
|
+
<p><strong>이슈:</strong> <ac:structured-macro ac:name="jira"><ac:parameter ac:name="key">KEY-123</ac:parameter></ac:structured-macro></p>
|
|
213
|
+
<p><strong>작성일:</strong> <time datetime="2025-12-03" /></p>
|
|
214
|
+
</ac:rich-text-body>
|
|
215
|
+
</ac:structured-macro>
|
|
216
|
+
<ac:structured-macro ac:name="toc">
|
|
217
|
+
<ac:parameter ac:name="maxLevel">3</ac:parameter>
|
|
218
|
+
</ac:structured-macro>
|
|
219
|
+
<h2>배경 및 현황</h2>`,
|
|
220
|
+
'complete-example': `# Complete Example
|
|
221
|
+
|
|
222
|
+
<h2>참여자</h2>
|
|
223
|
+
<ul>
|
|
224
|
+
<li><ac:link><ri:user ri:account-id="ACCOUNT_ID" /></ac:link></li>
|
|
225
|
+
</ul>
|
|
226
|
+
|
|
227
|
+
<h2>WIP</h2>
|
|
228
|
+
<ac:structured-macro ac:name="info" ac:schema-version="1">
|
|
229
|
+
<ac:parameter ac:name="icon">true</ac:parameter>
|
|
230
|
+
<ac:rich-text-body>
|
|
231
|
+
<p>Sprint deadline: <time datetime="2025-12-15" /></p>
|
|
232
|
+
</ac:rich-text-body>
|
|
233
|
+
</ac:structured-macro>
|
|
234
|
+
|
|
235
|
+
<ul>
|
|
236
|
+
<li>Task 1</li>
|
|
237
|
+
<li>Task 2</li>
|
|
238
|
+
</ul>
|
|
239
|
+
|
|
240
|
+
<h2>ISSUE</h2>
|
|
241
|
+
<ac:structured-macro ac:name="warning" ac:schema-version="1">
|
|
242
|
+
<ac:parameter ac:name="icon">true</ac:parameter>
|
|
243
|
+
<ac:rich-text-body>
|
|
244
|
+
<p>Critical issue: <ac:structured-macro ac:name="jira" ac:schema-version="1"><ac:parameter ac:name="key">KEY-123</ac:parameter></ac:structured-macro></p>
|
|
245
|
+
</ac:rich-text-body>
|
|
246
|
+
</ac:structured-macro>`,
|
|
247
|
+
};
|
|
248
|
+
return {
|
|
249
|
+
content: [
|
|
250
|
+
{
|
|
251
|
+
type: 'text',
|
|
252
|
+
text: examples[topic] || examples.overview,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerCreatePage = (server, confluenceService) => {
|
|
3
|
+
server.tool('create-page', '⚠️ Create a new Confluence page. 🚫 CRITICAL: NEVER use wiki markup ({info}, {code}), placeholder text ([Info Panel]), or plain text for Jira/dates/users. ✅ MANDATORY: MUST use confluence-storage-format-help tool BEFORE calling this. ONLY use storage format XML: <ac:structured-macro> for macros, <time> for dates, <ac:link> for users.', {
|
|
4
|
+
spaceId: z.string().describe('ID of the space where the page will be created'),
|
|
5
|
+
title: z.string().describe('Title of the new page'),
|
|
6
|
+
bodyRepresentation: z
|
|
7
|
+
.enum(['storage', 'atlas_doc_format', 'view'])
|
|
8
|
+
.default('storage')
|
|
9
|
+
.describe('Format of the page body content (default: storage)'),
|
|
10
|
+
bodyValue: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe('🚫 NEVER use wiki markup ({info}, {code}) or placeholders ([Panel]). ✅ ONLY use storage format XML. MUST check confluence-storage-format-help tool first. Example: Panels use <ac:structured-macro ac:name="info"><ac:rich-text-body><p>text</p></ac:rich-text-body></ac:structured-macro>, Jira uses <ac:structured-macro ac:name="jira"><ac:parameter ac:name="key">KEY-123</ac:parameter></ac:structured-macro>, dates use <time datetime="YYYY-MM-DD"/>, users use <ac:link><ri:user ri:account-id="ID"/></ac:link>.'),
|
|
13
|
+
status: z
|
|
14
|
+
.enum(['current', 'draft'])
|
|
15
|
+
.optional()
|
|
16
|
+
.default('current')
|
|
17
|
+
.describe('Status of the page (default: current)'),
|
|
18
|
+
parentId: z.string().optional().describe('ID of the parent page (optional, for creating child pages)'),
|
|
19
|
+
}, async ({ spaceId, title, bodyRepresentation, bodyValue, status = 'current', parentId }) => {
|
|
20
|
+
try {
|
|
21
|
+
const result = await confluenceService.createPage({
|
|
22
|
+
spaceId,
|
|
23
|
+
title,
|
|
24
|
+
body: {
|
|
25
|
+
representation: bodyRepresentation,
|
|
26
|
+
value: bodyValue,
|
|
27
|
+
},
|
|
28
|
+
status,
|
|
29
|
+
parentId,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
content: [
|
|
33
|
+
{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify(result),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: `Failed to create page: ${error instanceof Error ? error.message : String(error)}`,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerDeletePage = (server, confluenceService) => {
|
|
3
|
+
server.tool('delete-page', 'Delete a Confluence page (moves to trash by default, or permanently deletes)', {
|
|
4
|
+
pageId: z.string().describe('ID of the page to delete'),
|
|
5
|
+
purge: z
|
|
6
|
+
.boolean()
|
|
7
|
+
.optional()
|
|
8
|
+
.default(false)
|
|
9
|
+
.describe('If true, permanently delete the page. If false, move to trash (default: false)'),
|
|
10
|
+
draft: z.boolean().optional().describe('If true, delete a draft page (default: false)'),
|
|
11
|
+
}, async ({ pageId, purge = false, draft }) => {
|
|
12
|
+
try {
|
|
13
|
+
await confluenceService.deletePage(pageId, {
|
|
14
|
+
purge,
|
|
15
|
+
draft,
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: purge
|
|
22
|
+
? `Page ${pageId} has been permanently deleted`
|
|
23
|
+
: `Page ${pageId} has been moved to trash`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: `Failed to delete page: ${error instanceof Error ? error.message : String(error)}`,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerFindLabelByName = (server, confluenceService) => {
|
|
3
|
+
server.tool('find-label-by-name', 'Find a specific label by its name', {
|
|
4
|
+
labelName: z.string().describe('Name of the label to find'),
|
|
5
|
+
}, async ({ labelName }) => {
|
|
6
|
+
try {
|
|
7
|
+
const label = await confluenceService.findLabelByName(labelName);
|
|
8
|
+
if (label) {
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: 'text',
|
|
13
|
+
text: JSON.stringify(label),
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: `Label "${labelName}" not found`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: 'text',
|
|
34
|
+
text: `Failed to find label "${labelName}": ${error instanceof Error ? error.message : String(error)}`,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerFindPagesByLabelName = (server, confluenceService) => {
|
|
3
|
+
server.tool('find-pages-by-label-name', 'Find pages by label name (convenience tool that combines label search and page retrieval)', {
|
|
4
|
+
labelName: z
|
|
5
|
+
.string()
|
|
6
|
+
.describe('Name of the label to search for (e.g., "business-kb", "api", "documentation")'),
|
|
7
|
+
bodyFormat: z
|
|
8
|
+
.array(z.enum(['storage', 'atlas_doc_format', 'view']))
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('Format of the page body to retrieve'),
|
|
11
|
+
sort: z
|
|
12
|
+
.enum([
|
|
13
|
+
'id',
|
|
14
|
+
'-id',
|
|
15
|
+
'created-date',
|
|
16
|
+
'-created-date',
|
|
17
|
+
'modified-date',
|
|
18
|
+
'-modified-date',
|
|
19
|
+
'title',
|
|
20
|
+
'-title',
|
|
21
|
+
])
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Sort order for results'),
|
|
24
|
+
cursor: z.string().optional().describe('Cursor for pagination'),
|
|
25
|
+
limit: z
|
|
26
|
+
.number()
|
|
27
|
+
.min(1)
|
|
28
|
+
.max(250)
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Maximum number of results to return (default: 25, max: 250)'),
|
|
31
|
+
}, async ({ labelName, bodyFormat, sort, cursor, limit }) => {
|
|
32
|
+
try {
|
|
33
|
+
const result = await confluenceService.getPagesByLabelName(labelName, {
|
|
34
|
+
bodyFormat,
|
|
35
|
+
sort,
|
|
36
|
+
cursor,
|
|
37
|
+
limit,
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: 'text',
|
|
43
|
+
text: JSON.stringify(result),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: `Failed to find pages for label "${labelName}": ${error instanceof Error ? error.message : String(error)}`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerFindSpaceByName = (server, confluenceService) => {
|
|
3
|
+
server.tool('find-space-by-name', 'Find a space by its name', {
|
|
4
|
+
spaceName: z.string().describe('Name of the space to find'),
|
|
5
|
+
}, async ({ spaceName }) => {
|
|
6
|
+
try {
|
|
7
|
+
const space = await confluenceService.findSpaceByName(spaceName);
|
|
8
|
+
if (space) {
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: 'text',
|
|
13
|
+
text: JSON.stringify(space),
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: `Space with name "${spaceName}" not found`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: 'text',
|
|
34
|
+
text: `Failed to find space with name "${spaceName}": ${error instanceof Error ? error.message : String(error)}`,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerGetLabels = (server, confluenceService) => {
|
|
3
|
+
server.tool('get-labels', 'Get list of labels from Confluence', {
|
|
4
|
+
ids: z.array(z.string()).optional().describe('Filter labels by IDs'),
|
|
5
|
+
prefix: z.array(z.string()).optional().describe('Filter labels by prefix'),
|
|
6
|
+
cursor: z.string().optional().describe('Cursor for pagination'),
|
|
7
|
+
limit: z
|
|
8
|
+
.number()
|
|
9
|
+
.min(1)
|
|
10
|
+
.max(250)
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Maximum number of results to return (default: 25, max: 250)'),
|
|
13
|
+
sort: z
|
|
14
|
+
.enum(['id', '-id', 'name', '-name', 'created-date', '-created-date'])
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Sort order for results'),
|
|
17
|
+
}, async ({ ids, prefix, cursor, limit, sort }) => {
|
|
18
|
+
try {
|
|
19
|
+
const result = await confluenceService.getLabels({
|
|
20
|
+
ids,
|
|
21
|
+
prefix,
|
|
22
|
+
cursor,
|
|
23
|
+
limit,
|
|
24
|
+
sort,
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{
|
|
29
|
+
type: 'text',
|
|
30
|
+
text: JSON.stringify(result),
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: `Failed to retrieve labels: ${error instanceof Error ? error.message : String(error)}`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerGetPageById = (server, confluenceService) => {
|
|
3
|
+
server.tool('get-page-by-id', 'Get a specific page by its ID', {
|
|
4
|
+
pageId: z.string().describe('ID of the page to retrieve'),
|
|
5
|
+
bodyFormat: z
|
|
6
|
+
.array(z.enum(['storage', 'atlas_doc_format', 'view']))
|
|
7
|
+
.optional()
|
|
8
|
+
.describe('Format of the page body to retrieve'),
|
|
9
|
+
getDraft: z.boolean().optional().describe('Whether to get the draft version of the page'),
|
|
10
|
+
version: z.number().optional().describe('Specific version of the page to retrieve'),
|
|
11
|
+
}, async ({ pageId, bodyFormat, getDraft, version }) => {
|
|
12
|
+
try {
|
|
13
|
+
const page = await confluenceService.getPageById(pageId, {
|
|
14
|
+
bodyFormat,
|
|
15
|
+
getDraft,
|
|
16
|
+
version,
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: JSON.stringify(page),
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: 'text',
|
|
32
|
+
text: `Failed to retrieve page: ${error instanceof Error ? error.message : String(error)}`,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerGetPageChildrenTree = (server, confluenceService) => {
|
|
3
|
+
server.tool('get-page-children-tree', 'Get the complete hierarchical tree of child pages for a specific page (recursive)', {
|
|
4
|
+
pageId: z.string().describe('ID of the root page'),
|
|
5
|
+
limit: z
|
|
6
|
+
.number()
|
|
7
|
+
.min(1)
|
|
8
|
+
.max(250)
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('Maximum number of results per level (default: 25, max: 250)'),
|
|
11
|
+
sort: z
|
|
12
|
+
.enum([
|
|
13
|
+
'id',
|
|
14
|
+
'-id',
|
|
15
|
+
'created-date',
|
|
16
|
+
'-created-date',
|
|
17
|
+
'modified-date',
|
|
18
|
+
'-modified-date',
|
|
19
|
+
'title',
|
|
20
|
+
'-title',
|
|
21
|
+
])
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Sort order for results'),
|
|
24
|
+
maxDepth: z
|
|
25
|
+
.number()
|
|
26
|
+
.min(-1)
|
|
27
|
+
.max(20)
|
|
28
|
+
.optional()
|
|
29
|
+
.default(10)
|
|
30
|
+
.describe('Maximum depth to traverse (default: 10, unlimited: -1, max: 20)'),
|
|
31
|
+
}, async ({ pageId, limit, sort, maxDepth = 10 }) => {
|
|
32
|
+
try {
|
|
33
|
+
const result = await confluenceService.getPageChildrenTree(pageId, {
|
|
34
|
+
limit,
|
|
35
|
+
sort,
|
|
36
|
+
}, maxDepth);
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: 'text',
|
|
41
|
+
text: JSON.stringify(result, null, 2),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: `Failed to retrieve page children tree: ${error instanceof Error ? error.message : String(error)}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|