@ecubelabs/atlassian-mcp 1.6.0 → 1.8.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/create-folder.js +37 -0
- package/dist/confluence-tools/delete-folder.js +28 -0
- package/dist/confluence-tools/get-folder-by-id.js +28 -0
- package/dist/confluence-tools/get-folder-children.js +64 -0
- package/dist/confluence-tools/get-page-children.js +15 -3
- package/dist/confluence-tools/get-page-descendants.js +49 -0
- package/dist/confluence-tools/get-space-root-content.js +57 -0
- package/dist/confluence-tools/index.js +8 -0
- package/dist/confluence-tools/move-page.js +38 -0
- package/dist/confluence-tools-register.js +9 -1
- package/dist/libs/confluence-client.js +125 -1
- package/package.json +2 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerCreateFolder = (server, confluenceService) => {
|
|
3
|
+
server.tool('create-folder', 'Create a new folder in a Confluence space. Folders can contain pages and other folders.', {
|
|
4
|
+
spaceId: z.string().describe('ID of the space where the folder will be created'),
|
|
5
|
+
title: z.string().describe('Title of the folder'),
|
|
6
|
+
parentId: z
|
|
7
|
+
.string()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe('ID of the parent folder. If not provided, the folder will be created at the space root.'),
|
|
10
|
+
}, async ({ spaceId, title, parentId }) => {
|
|
11
|
+
try {
|
|
12
|
+
const result = await confluenceService.createFolder({
|
|
13
|
+
spaceId,
|
|
14
|
+
title,
|
|
15
|
+
parentId,
|
|
16
|
+
});
|
|
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 create folder: ${error instanceof Error ? error.message : String(error)}`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerDeleteFolder = (server, confluenceService) => {
|
|
3
|
+
server.tool('delete-folder', 'Delete a Confluence folder. The folder will be moved to trash where it can be restored later.', {
|
|
4
|
+
folderId: z.string().describe('ID of the folder to delete'),
|
|
5
|
+
}, async ({ folderId }) => {
|
|
6
|
+
try {
|
|
7
|
+
await confluenceService.deleteFolder(folderId);
|
|
8
|
+
return {
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
type: 'text',
|
|
12
|
+
text: JSON.stringify({ success: true, message: `Folder ${folderId} has been deleted (moved to trash)` }),
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: `Failed to delete folder: ${error instanceof Error ? error.message : String(error)}`,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerGetFolderById = (server, confluenceService) => {
|
|
3
|
+
server.tool('get-folder-by-id', 'Get details of a specific Confluence folder by its ID.', {
|
|
4
|
+
folderId: z.string().describe('ID of the folder to retrieve'),
|
|
5
|
+
}, async ({ folderId }) => {
|
|
6
|
+
try {
|
|
7
|
+
const result = await confluenceService.getFolderById(folderId);
|
|
8
|
+
return {
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
type: 'text',
|
|
12
|
+
text: JSON.stringify(result),
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: `Failed to get folder: ${error instanceof Error ? error.message : String(error)}`,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerGetFolderChildren = (server, confluenceService) => {
|
|
3
|
+
server.tool('get-folder-children', 'Get direct children (pages, folders, etc.) of a Confluence folder. Use type filter to retrieve only specific content types.', {
|
|
4
|
+
folderId: z.string().describe('ID of the folder'),
|
|
5
|
+
type: z
|
|
6
|
+
.enum(['page', 'folder', 'whiteboard', 'database', 'embed'])
|
|
7
|
+
.optional()
|
|
8
|
+
.describe('Filter by content type (page, folder, whiteboard, database, embed)'),
|
|
9
|
+
cursor: z.string().optional().describe('Cursor for pagination'),
|
|
10
|
+
limit: z
|
|
11
|
+
.number()
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(250)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Maximum number of results to return (default: 25, max: 250)'),
|
|
16
|
+
sort: z
|
|
17
|
+
.enum([
|
|
18
|
+
'id',
|
|
19
|
+
'-id',
|
|
20
|
+
'created-date',
|
|
21
|
+
'-created-date',
|
|
22
|
+
'modified-date',
|
|
23
|
+
'-modified-date',
|
|
24
|
+
'title',
|
|
25
|
+
'-title',
|
|
26
|
+
])
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Sort order for results'),
|
|
29
|
+
}, async ({ folderId, type, cursor, limit, sort }) => {
|
|
30
|
+
try {
|
|
31
|
+
const result = await confluenceService.getFolderChildren(folderId, {
|
|
32
|
+
cursor,
|
|
33
|
+
limit,
|
|
34
|
+
sort,
|
|
35
|
+
});
|
|
36
|
+
// Apply client-side type filter if specified
|
|
37
|
+
let filteredResults = result.results;
|
|
38
|
+
if (type) {
|
|
39
|
+
filteredResults = result.results.filter((child) => child.type === type);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: JSON.stringify({
|
|
46
|
+
...result,
|
|
47
|
+
results: filteredResults,
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: `Failed to get folder children: ${error instanceof Error ? error.message : String(error)}`,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const registerGetPageChildren = (server, confluenceService) => {
|
|
3
|
-
server.tool('get-page-children', 'Get direct
|
|
3
|
+
server.tool('get-page-children', 'Get direct children (pages, folders, etc.) of a specific page. Use type filter to retrieve only specific content types.', {
|
|
4
4
|
pageId: z.string().describe('ID of the parent page'),
|
|
5
|
+
type: z
|
|
6
|
+
.enum(['page', 'folder', 'whiteboard', 'database', 'embed'])
|
|
7
|
+
.optional()
|
|
8
|
+
.describe('Filter by content type (page, folder, whiteboard, database, embed)'),
|
|
5
9
|
cursor: z.string().optional().describe('Cursor for pagination'),
|
|
6
10
|
limit: z
|
|
7
11
|
.number()
|
|
@@ -22,18 +26,26 @@ export const registerGetPageChildren = (server, confluenceService) => {
|
|
|
22
26
|
])
|
|
23
27
|
.optional()
|
|
24
28
|
.describe('Sort order for results'),
|
|
25
|
-
}, async ({ pageId, cursor, limit, sort }) => {
|
|
29
|
+
}, async ({ pageId, type, cursor, limit, sort }) => {
|
|
26
30
|
try {
|
|
27
31
|
const result = await confluenceService.getPageChildren(pageId, {
|
|
28
32
|
cursor,
|
|
29
33
|
limit,
|
|
30
34
|
sort,
|
|
31
35
|
});
|
|
36
|
+
// Apply client-side type filter if specified
|
|
37
|
+
let filteredResults = result.results;
|
|
38
|
+
if (type) {
|
|
39
|
+
filteredResults = result.results.filter((child) => child.type === type);
|
|
40
|
+
}
|
|
32
41
|
return {
|
|
33
42
|
content: [
|
|
34
43
|
{
|
|
35
44
|
type: 'text',
|
|
36
|
-
text: JSON.stringify(
|
|
45
|
+
text: JSON.stringify({
|
|
46
|
+
...result,
|
|
47
|
+
results: filteredResults,
|
|
48
|
+
}),
|
|
37
49
|
},
|
|
38
50
|
],
|
|
39
51
|
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerGetPageDescendants = (server, confluenceService) => {
|
|
3
|
+
server.tool('get-page-descendants', 'Get all descendants (children, grandchildren, etc.) of a specific page. Useful for getting the entire hierarchy under a page.', {
|
|
4
|
+
pageId: z.string().describe('ID of the page'),
|
|
5
|
+
type: z
|
|
6
|
+
.enum(['page', 'comment', 'attachment'])
|
|
7
|
+
.optional()
|
|
8
|
+
.default('page')
|
|
9
|
+
.describe('Type of descendants to retrieve (default: page)'),
|
|
10
|
+
depth: z
|
|
11
|
+
.enum(['all', 'root'])
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Depth of descendants: "all" for entire tree, "root" for direct children only'),
|
|
14
|
+
start: z.number().min(0).optional().describe('Starting index for pagination (default: 0)'),
|
|
15
|
+
limit: z
|
|
16
|
+
.number()
|
|
17
|
+
.min(1)
|
|
18
|
+
.max(250)
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('Maximum number of results to return (default: 25, max: 250)'),
|
|
21
|
+
}, async ({ pageId, type, depth, start, limit }) => {
|
|
22
|
+
try {
|
|
23
|
+
const result = await confluenceService.getPageDescendants(pageId, {
|
|
24
|
+
type,
|
|
25
|
+
depth,
|
|
26
|
+
start,
|
|
27
|
+
limit,
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: JSON.stringify(result),
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: 'text',
|
|
43
|
+
text: `Failed to get page descendants: ${error instanceof Error ? error.message : String(error)}`,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerGetSpaceRootContent = (server, confluenceService) => {
|
|
3
|
+
server.tool('get-space-root-content', "Get root-level content (pages, folders, etc.) of a Confluence space. This retrieves the direct children of the space's homepage.", {
|
|
4
|
+
spaceId: z.string().describe('ID of the space'),
|
|
5
|
+
type: z
|
|
6
|
+
.enum(['page', 'folder', 'whiteboard', 'database', 'embed'])
|
|
7
|
+
.optional()
|
|
8
|
+
.describe('Filter by content type (page, folder, whiteboard, database, embed)'),
|
|
9
|
+
cursor: z.string().optional().describe('Cursor for pagination'),
|
|
10
|
+
limit: z
|
|
11
|
+
.number()
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(250)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Maximum number of results to return (default: 25, max: 250)'),
|
|
16
|
+
sort: z
|
|
17
|
+
.enum([
|
|
18
|
+
'id',
|
|
19
|
+
'-id',
|
|
20
|
+
'created-date',
|
|
21
|
+
'-created-date',
|
|
22
|
+
'modified-date',
|
|
23
|
+
'-modified-date',
|
|
24
|
+
'title',
|
|
25
|
+
'-title',
|
|
26
|
+
])
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Sort order for results'),
|
|
29
|
+
}, async ({ spaceId, type, cursor, limit, sort }) => {
|
|
30
|
+
try {
|
|
31
|
+
const result = await confluenceService.getSpaceRootContent(spaceId, {
|
|
32
|
+
type,
|
|
33
|
+
cursor,
|
|
34
|
+
limit,
|
|
35
|
+
sort,
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: 'text',
|
|
41
|
+
text: JSON.stringify(result),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: `Failed to get space root content: ${error instanceof Error ? error.message : String(error)}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
@@ -7,9 +7,11 @@ export { registerFindPagesByLabelName } from './find-pages-by-label-name.js';
|
|
|
7
7
|
export { registerFindLabelByName } from './find-label-by-name.js';
|
|
8
8
|
export { registerGetPageChildren } from './get-page-children.js';
|
|
9
9
|
export { registerGetPageChildrenTree } from './get-page-children-tree.js';
|
|
10
|
+
export { registerGetPageDescendants } from './get-page-descendants.js';
|
|
10
11
|
export { registerGetSpaces } from './get-spaces.js';
|
|
11
12
|
export { registerGetSpaceById } from './get-space-by-id.js';
|
|
12
13
|
export { registerFindSpaceByName } from './find-space-by-name.js';
|
|
14
|
+
export { registerGetSpaceRootContent } from './get-space-root-content.js';
|
|
13
15
|
export { registerGetRecentlyUpdatedPages } from './get-recently-updated-pages.js';
|
|
14
16
|
export { registerGetRecentlyCreatedPages } from './get-recently-created-pages.js';
|
|
15
17
|
export { registerGetTemplates } from './get-templates.js';
|
|
@@ -20,5 +22,11 @@ export { registerUpdatePage } from './update-page.js';
|
|
|
20
22
|
export { registerDeletePage } from './delete-page.js';
|
|
21
23
|
export { registerAddLabelsToPage } from './add-labels-to-page.js';
|
|
22
24
|
export { registerRemoveLabelFromPage } from './remove-label-from-page.js';
|
|
25
|
+
export { registerMovePage } from './move-page.js';
|
|
26
|
+
// Folder tools
|
|
27
|
+
export { registerCreateFolder } from './create-folder.js';
|
|
28
|
+
export { registerGetFolderById } from './get-folder-by-id.js';
|
|
29
|
+
export { registerDeleteFolder } from './delete-folder.js';
|
|
30
|
+
export { registerGetFolderChildren } from './get-folder-children.js';
|
|
23
31
|
// Help tool
|
|
24
32
|
export { registerConfluenceStorageFormatHelp } from './confluence-storage-format-help.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const registerMovePage = (server, confluenceService) => {
|
|
3
|
+
server.tool('move-page', 'Move a Confluence page to a new location. Use position "append" to move the page as a child of the target (page or folder). Use "before" or "after" to move the page as a sibling of the target.', {
|
|
4
|
+
pageId: z.string().describe('ID of the page to move'),
|
|
5
|
+
targetId: z
|
|
6
|
+
.string()
|
|
7
|
+
.describe('ID of the target page or folder. The page will be moved relative to this target.'),
|
|
8
|
+
position: z
|
|
9
|
+
.enum(['before', 'after', 'append'])
|
|
10
|
+
.default('append')
|
|
11
|
+
.describe('Position relative to targetId: "append" = move as child of target (use for moving into folders), "before" = move before target (same parent), "after" = move after target (same parent)'),
|
|
12
|
+
}, async ({ pageId, targetId, position }) => {
|
|
13
|
+
try {
|
|
14
|
+
const result = await confluenceService.movePage(pageId, {
|
|
15
|
+
targetId,
|
|
16
|
+
position,
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: JSON.stringify(result),
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: 'text',
|
|
32
|
+
text: `Failed to move page: ${error instanceof Error ? error.message : String(error)}`,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConfluenceService } from './libs/confluence-client.js';
|
|
2
|
-
import { registerGetPages, registerGetPagesForLabel, registerGetPageById, registerGetLabels, registerFindPagesByLabelName, registerFindLabelByName, registerGetPageChildren, registerGetPageChildrenTree, registerGetSpaces, registerGetSpaceById, registerFindSpaceByName, registerGetRecentlyUpdatedPages, registerGetRecentlyCreatedPages, registerGetTemplates, registerGetTemplateById, registerCreatePage, registerUpdatePage, registerDeletePage, registerAddLabelsToPage, registerRemoveLabelFromPage, registerConfluenceStorageFormatHelp, } from './confluence-tools/index.js';
|
|
2
|
+
import { registerGetPages, registerGetPagesForLabel, registerGetPageById, registerGetLabels, registerFindPagesByLabelName, registerFindLabelByName, registerGetPageChildren, registerGetPageChildrenTree, registerGetPageDescendants, registerGetSpaces, registerGetSpaceById, registerFindSpaceByName, registerGetSpaceRootContent, registerGetRecentlyUpdatedPages, registerGetRecentlyCreatedPages, registerGetTemplates, registerGetTemplateById, registerCreatePage, registerUpdatePage, registerDeletePage, registerAddLabelsToPage, registerRemoveLabelFromPage, registerMovePage, registerCreateFolder, registerGetFolderById, registerDeleteFolder, registerGetFolderChildren, registerConfluenceStorageFormatHelp, } from './confluence-tools/index.js';
|
|
3
3
|
export const registerConfluenceTools = (server) => {
|
|
4
4
|
// Initialize Confluence service
|
|
5
5
|
const confluenceService = new ConfluenceService();
|
|
@@ -12,9 +12,11 @@ export const registerConfluenceTools = (server) => {
|
|
|
12
12
|
registerFindLabelByName(server, confluenceService);
|
|
13
13
|
registerGetPageChildren(server, confluenceService);
|
|
14
14
|
registerGetPageChildrenTree(server, confluenceService);
|
|
15
|
+
registerGetPageDescendants(server, confluenceService);
|
|
15
16
|
registerGetSpaces(server, confluenceService);
|
|
16
17
|
registerGetSpaceById(server, confluenceService);
|
|
17
18
|
registerFindSpaceByName(server, confluenceService);
|
|
19
|
+
registerGetSpaceRootContent(server, confluenceService);
|
|
18
20
|
registerGetRecentlyUpdatedPages(server, confluenceService);
|
|
19
21
|
registerGetRecentlyCreatedPages(server, confluenceService);
|
|
20
22
|
registerGetTemplates(server, confluenceService);
|
|
@@ -25,6 +27,12 @@ export const registerConfluenceTools = (server) => {
|
|
|
25
27
|
registerDeletePage(server, confluenceService);
|
|
26
28
|
registerAddLabelsToPage(server, confluenceService);
|
|
27
29
|
registerRemoveLabelFromPage(server, confluenceService);
|
|
30
|
+
registerMovePage(server, confluenceService);
|
|
31
|
+
// Register folder tools
|
|
32
|
+
registerCreateFolder(server, confluenceService);
|
|
33
|
+
registerGetFolderById(server, confluenceService);
|
|
34
|
+
registerDeleteFolder(server, confluenceService);
|
|
35
|
+
registerGetFolderChildren(server, confluenceService);
|
|
28
36
|
// Register help tool
|
|
29
37
|
registerConfluenceStorageFormatHelp(server);
|
|
30
38
|
};
|
|
@@ -159,7 +159,7 @@ export class ConfluenceService extends BaseApiService {
|
|
|
159
159
|
if (sort)
|
|
160
160
|
params.sort = sort;
|
|
161
161
|
}
|
|
162
|
-
return this.makeRequest(() => this.client.get(`/pages/${pageId}/children`, { params }));
|
|
162
|
+
return this.makeRequest(() => this.client.get(`/pages/${pageId}/direct-children`, { params }));
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
165
|
* 페이지의 전체 하위 트리를 재귀적으로 가져오는 편의 함수
|
|
@@ -383,4 +383,128 @@ export class ConfluenceService extends BaseApiService {
|
|
|
383
383
|
}
|
|
384
384
|
return this.makeRequest(() => this.v1Client.get(`/template/page/${contentTemplateId}`, { params }));
|
|
385
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* 페이지를 다른 위치로 이동
|
|
388
|
+
* @see https://developer.atlassian.com/cloud/confluence/rest/v1/api-group-content---children-and-descendants/#api-wiki-rest-api-content-pageid-move-position-targetid-put
|
|
389
|
+
* 참고: Move API는 v1 API를 사용 (/wiki/rest/api/content)
|
|
390
|
+
* @param pageId 이동할 페이지 ID
|
|
391
|
+
* @param options.targetId 목표 페이지 또는 폴더 ID
|
|
392
|
+
* @param options.position 이동 위치: 'before', 'after', 'append'
|
|
393
|
+
*/
|
|
394
|
+
async movePage(pageId, options) {
|
|
395
|
+
const { targetId, position } = options;
|
|
396
|
+
return this.makeRequest(() => this.v1Client.put(`/content/${pageId}/move/${position}/${targetId}`, null, {
|
|
397
|
+
headers: {
|
|
398
|
+
'Accept': 'application/json',
|
|
399
|
+
},
|
|
400
|
+
}));
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* 폴더 생성
|
|
404
|
+
* @see https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-folder/#api-wiki-api-v2-folders-post
|
|
405
|
+
* @param options.spaceId Space ID
|
|
406
|
+
* @param options.title 폴더 제목
|
|
407
|
+
* @param options.parentId 부모 폴더 ID (없으면 Space 루트에 생성)
|
|
408
|
+
*/
|
|
409
|
+
async createFolder(options) {
|
|
410
|
+
const { spaceId, title, parentId } = options;
|
|
411
|
+
const requestBody = {
|
|
412
|
+
spaceId,
|
|
413
|
+
title,
|
|
414
|
+
};
|
|
415
|
+
if (parentId) {
|
|
416
|
+
requestBody.parentId = parentId;
|
|
417
|
+
}
|
|
418
|
+
return this.makeRequest(() => this.client.post('/folders', requestBody, {
|
|
419
|
+
headers: {
|
|
420
|
+
'Content-Type': 'application/json',
|
|
421
|
+
},
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* 특정 폴더 조회
|
|
426
|
+
* @see https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-folder/#api-wiki-api-v2-folders-id-get
|
|
427
|
+
* @param folderId 폴더 ID
|
|
428
|
+
*/
|
|
429
|
+
async getFolderById(folderId) {
|
|
430
|
+
return this.makeRequest(() => this.client.get(`/folders/${folderId}`));
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* 폴더 삭제 (휴지통으로 이동)
|
|
434
|
+
* @see https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-folder/#api-wiki-api-v2-folders-id-delete
|
|
435
|
+
* @param folderId 삭제할 폴더 ID
|
|
436
|
+
*/
|
|
437
|
+
async deleteFolder(folderId) {
|
|
438
|
+
return this.makeRequest(() => this.client.delete(`/folders/${folderId}`));
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* 폴더의 직접 자식 목록 조회
|
|
442
|
+
* @see https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-children/#api-wiki-api-v2-folders-id-direct-children-get
|
|
443
|
+
* @param folderId 폴더 ID
|
|
444
|
+
* @param options 조회 옵션
|
|
445
|
+
*/
|
|
446
|
+
async getFolderChildren(folderId, options) {
|
|
447
|
+
const params = {};
|
|
448
|
+
if (options) {
|
|
449
|
+
const { cursor, limit, sort } = options;
|
|
450
|
+
if (cursor)
|
|
451
|
+
params.cursor = cursor;
|
|
452
|
+
if (limit)
|
|
453
|
+
params.limit = limit;
|
|
454
|
+
if (sort)
|
|
455
|
+
params.sort = sort;
|
|
456
|
+
}
|
|
457
|
+
return this.makeRequest(() => this.client.get(`/folders/${folderId}/direct-children`, { params }));
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Space의 루트 콘텐츠 조회 (homepage의 직접 자식)
|
|
461
|
+
* 참고: Space의 homepage 페이지의 자식들을 조회하여 루트 콘텐츠를 반환
|
|
462
|
+
* @param spaceId Space ID
|
|
463
|
+
* @param options 조회 옵션
|
|
464
|
+
*/
|
|
465
|
+
async getSpaceRootContent(spaceId, options) {
|
|
466
|
+
// 1. Space 정보 조회하여 homepage ID 가져오기
|
|
467
|
+
const space = await this.getSpaceById(spaceId);
|
|
468
|
+
if (!space.homepageId) {
|
|
469
|
+
return {
|
|
470
|
+
results: [],
|
|
471
|
+
homepageId: undefined,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
const homepageId = space.homepageId;
|
|
475
|
+
// 2. Homepage의 자식들 조회
|
|
476
|
+
const { type, ...restOptions } = options || {};
|
|
477
|
+
const result = await this.getPageChildren(homepageId, restOptions);
|
|
478
|
+
// 3. type 필터 적용 (클라이언트 측)
|
|
479
|
+
let filteredResults = result.results;
|
|
480
|
+
if (type) {
|
|
481
|
+
filteredResults = result.results.filter((child) => child.type === type);
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
results: filteredResults,
|
|
485
|
+
cursor: result.cursor,
|
|
486
|
+
_links: result._links,
|
|
487
|
+
homepageId,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* 페이지의 모든 후손(descendants) 조회
|
|
492
|
+
* @see https://developer.atlassian.com/cloud/confluence/rest/v1/api-group-content---children-and-descendants/#api-wiki-rest-api-content-id-descendant-type-get
|
|
493
|
+
* 참고: Descendants API는 v1 API를 사용 (/wiki/rest/api/content)
|
|
494
|
+
* @param pageId 페이지 ID
|
|
495
|
+
* @param options 조회 옵션
|
|
496
|
+
*/
|
|
497
|
+
async getPageDescendants(pageId, options) {
|
|
498
|
+
const { type = 'page', depth, start, limit, expand } = options || {};
|
|
499
|
+
const params = {};
|
|
500
|
+
if (depth)
|
|
501
|
+
params.depth = depth;
|
|
502
|
+
if (start !== undefined)
|
|
503
|
+
params.start = start;
|
|
504
|
+
if (limit !== undefined)
|
|
505
|
+
params.limit = limit;
|
|
506
|
+
if (expand)
|
|
507
|
+
params.expand = expand.join(',');
|
|
508
|
+
return this.makeRequest(() => this.v1Client.get(`/content/${pageId}/descendant/${type}`, { params }));
|
|
509
|
+
}
|
|
386
510
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecubelabs/atlassian-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"bin": "./dist/index.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/Ecube-Labs/skynet.git"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"packageManager": "yarn@4.1.0",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
23
23
|
"axios": "^1.12.0",
|
|
24
24
|
"axios-retry": "^4.5.0",
|
|
25
25
|
"dotenv": "^17.2.0",
|