diff --git a/packages/mcp/build/lib/docmost-schema.js b/packages/mcp/build/lib/docmost-schema.js index 6b6c221d..123d58d5 100644 --- a/packages/mcp/build/lib/docmost-schema.js +++ b/packages/mcp/build/lib/docmost-schema.js @@ -1070,7 +1070,24 @@ export const docmostExtensions = [ heading: {}, link: { openOnClick: false }, }), - Image.configure({ inline: false }), + // Stock @tiptap/extension-image has no caption attribute, so a round-trip + // through this schema would drop the data-caption the client TiptapImage + // emits. Mirror editor-ext image.ts: add a caption attribute that parses + // data-caption and re-renders it only when set (caption-less images stay + // clean), keeping the MCP markdown round-trip lossless. + Image.extend({ + addAttributes() { + const parent = this.parent?.() ?? {}; + return { + ...parent, + caption: { + default: undefined, + parseHTML: (el) => el.getAttribute("data-caption") || undefined, + renderHTML: (attrs) => attrs.caption ? { "data-caption": attrs.caption } : {}, + }, + }; + }, + }).configure({ inline: false }), TaskList, TaskItem.configure({ nested: true }), // Highlight stores its color unescaped and Docmost interpolates it into diff --git a/packages/mcp/src/lib/docmost-schema.ts b/packages/mcp/src/lib/docmost-schema.ts index 546b9844..b26a45ea 100644 --- a/packages/mcp/src/lib/docmost-schema.ts +++ b/packages/mcp/src/lib/docmost-schema.ts @@ -1164,7 +1164,26 @@ export const docmostExtensions = [ heading: {}, link: { openOnClick: false }, }), - Image.configure({ inline: false }), + // Stock @tiptap/extension-image has no caption attribute, so a round-trip + // through this schema would drop the data-caption the client TiptapImage + // emits. Mirror editor-ext image.ts: add a caption attribute that parses + // data-caption and re-renders it only when set (caption-less images stay + // clean), keeping the MCP markdown round-trip lossless. + Image.extend({ + addAttributes() { + const parent = this.parent?.() ?? {}; + return { + ...parent, + caption: { + default: undefined, + parseHTML: (el: HTMLElement) => + el.getAttribute("data-caption") || undefined, + renderHTML: (attrs: Record) => + attrs.caption ? { "data-caption": attrs.caption } : {}, + }, + }; + }, + }).configure({ inline: false }), TaskList, TaskItem.configure({ nested: true }), // Highlight stores its color unescaped and Docmost interpolates it into