@neverprepared/mcp-markdown-to-confluence 1.3.0 → 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/index.js +60 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -202,6 +202,30 @@ async function scanDirectoryTree(rootPath, spaceKey, currentPath = rootPath, dep
|
|
|
202
202
|
return { nodes, skipped };
|
|
203
203
|
}
|
|
204
204
|
// ---------------------------------------------------------------------------
|
|
205
|
+
// Wiki link resolution
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Matches [[Page Name]] and [[Page Name#Heading]] and [[Page Name|Display Text]]
|
|
208
|
+
const WIKI_LINK_RE = /\[\[([^\]|#]+)(?:#([^\]|]+))?(?:\|([^\]]+))?\]\]/g;
|
|
209
|
+
function hasWikiLinks(markdown) {
|
|
210
|
+
return WIKI_LINK_RE.test(markdown);
|
|
211
|
+
}
|
|
212
|
+
function resolveWikiLinks(markdown, titleToUrl) {
|
|
213
|
+
return markdown.replace(WIKI_LINK_RE, (_match, pageName, heading, displayText) => {
|
|
214
|
+
const trimmedName = pageName.trim();
|
|
215
|
+
const url = titleToUrl.get(trimmedName);
|
|
216
|
+
if (!url) {
|
|
217
|
+
// No matching page found — leave as plain text
|
|
218
|
+
return displayText?.trim() || trimmedName;
|
|
219
|
+
}
|
|
220
|
+
const label = displayText?.trim() || trimmedName;
|
|
221
|
+
const anchor = heading?.trim();
|
|
222
|
+
const anchorSuffix = anchor
|
|
223
|
+
? '#' + anchor.replace(/\s+/g, '-')
|
|
224
|
+
: '';
|
|
225
|
+
return `[${label}](${url}${anchorSuffix})`;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
205
229
|
// Core publish logic
|
|
206
230
|
// ---------------------------------------------------------------------------
|
|
207
231
|
async function publishMarkdown(markdown, title, spaceKey, pageId, parentId, skipPreview = false) {
|
|
@@ -659,6 +683,40 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
659
683
|
})));
|
|
660
684
|
allResults.push(...levelResults);
|
|
661
685
|
}
|
|
686
|
+
// Second pass: resolve wiki links [[Page Name]] and [[Page Name#Heading]]
|
|
687
|
+
// Build title → URL map from all successfully published pages
|
|
688
|
+
const titleToUrl = new Map();
|
|
689
|
+
for (const r of allResults) {
|
|
690
|
+
if (r.success && r.url) {
|
|
691
|
+
titleToUrl.set(r.title, r.url);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// Find nodes with wiki links that need re-publishing
|
|
695
|
+
const nodesWithLinks = nodes.filter((n) => n.markdownFile && hasWikiLinks(n.markdownFile.content) && n.resolvedPageId);
|
|
696
|
+
if (nodesWithLinks.length > 0 && titleToUrl.size > 0) {
|
|
697
|
+
const linkResults = await Promise.all(nodesWithLinks.map((node) => limit(async () => {
|
|
698
|
+
try {
|
|
699
|
+
const resolvedMarkdown = resolveWikiLinks(node.markdownFile.content, titleToUrl);
|
|
700
|
+
const result = await publishMarkdown(resolvedMarkdown, node.title, input.spaceKey, node.resolvedPageId, undefined, // don't reparent on second pass
|
|
701
|
+
true);
|
|
702
|
+
return { relativePath: node.relativePath, title: node.title, success: true, version: result.version };
|
|
703
|
+
}
|
|
704
|
+
catch {
|
|
705
|
+
return { relativePath: node.relativePath, title: node.title, success: false };
|
|
706
|
+
}
|
|
707
|
+
})));
|
|
708
|
+
const linkedCount = linkResults.filter((r) => r.success).length;
|
|
709
|
+
if (linkedCount > 0) {
|
|
710
|
+
// Update versions in allResults
|
|
711
|
+
for (const lr of linkResults) {
|
|
712
|
+
if (lr.success && lr.version) {
|
|
713
|
+
const existing = allResults.find((r) => r.relativePath === lr.relativePath);
|
|
714
|
+
if (existing)
|
|
715
|
+
existing.version = lr.version;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
662
720
|
// Build summary
|
|
663
721
|
const succeeded = allResults.filter((r) => r.success);
|
|
664
722
|
const failed = allResults.filter((r) => !r.success);
|
|
@@ -666,7 +724,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
666
724
|
`=== DIRECTORY PUBLISH RESULTS ===`,
|
|
667
725
|
`Directory: ${input.directoryPath}`,
|
|
668
726
|
`Space: ${input.spaceKey}`,
|
|
669
|
-
`Succeeded: ${succeeded.length} | Failed: ${failed.length} | Skipped: ${skipped.length}
|
|
727
|
+
`Succeeded: ${succeeded.length} | Failed: ${failed.length} | Skipped: ${skipped.length}` +
|
|
728
|
+
(nodesWithLinks.length > 0 ? ` | Wiki links resolved: ${nodesWithLinks.length} page(s)` : ''),
|
|
670
729
|
'',
|
|
671
730
|
];
|
|
672
731
|
if (succeeded.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neverprepared/mcp-markdown-to-confluence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "MCP server for converting markdown to Confluence ADF and publishing pages with diagram support via Kroki",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|