@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.
Files changed (2) hide show
  1. package/dist/index.js +60 -1
  2. 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.0",
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",