@de-otio/epimethian-mcp 5.5.0 → 5.6.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/cli/index.js +64 -15
- package/dist/cli/index.js.map +2 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -35054,7 +35054,7 @@ async function getPage(pageId, includeBody) {
|
|
|
35054
35054
|
async function _rawCreatePage(spaceId, title, body, parentId, clientLabel) {
|
|
35055
35055
|
const cfg = await getConfig();
|
|
35056
35056
|
const pageBody = stripAttributionFooter(toStorageFormat(body));
|
|
35057
|
-
const epimethianTag = `Epimethian v${"5.
|
|
35057
|
+
const epimethianTag = `Epimethian v${"5.6.0"}`;
|
|
35058
35058
|
const versionMsg = cfg.attribution && clientLabel ? `Created by ${clientLabel} (via ${epimethianTag})` : `Created by ${epimethianTag}`;
|
|
35059
35059
|
const payload = {
|
|
35060
35060
|
title,
|
|
@@ -35079,7 +35079,7 @@ async function _rawCreatePage(spaceId, title, body, parentId, clientLabel) {
|
|
|
35079
35079
|
async function _rawUpdatePage(pageId, opts) {
|
|
35080
35080
|
const cfg = await getConfig();
|
|
35081
35081
|
const newVersion = opts.version + 1;
|
|
35082
|
-
const epimethianTag = `Epimethian v${"5.
|
|
35082
|
+
const epimethianTag = `Epimethian v${"5.6.0"}`;
|
|
35083
35083
|
const effectiveClient = cfg.attribution ? opts.clientLabel : void 0;
|
|
35084
35084
|
let versionMessage;
|
|
35085
35085
|
if (opts.versionMessage && effectiveClient)
|
|
@@ -46648,6 +46648,35 @@ var init_content_safety_guards = __esm({
|
|
|
46648
46648
|
});
|
|
46649
46649
|
|
|
46650
46650
|
// src/server/safe-write.ts
|
|
46651
|
+
function detectMixedInput(body) {
|
|
46652
|
+
let stripped = body.replace(
|
|
46653
|
+
/^(`{3,})[^\n]*\n[\s\S]*?^\1\s*$/gm,
|
|
46654
|
+
""
|
|
46655
|
+
);
|
|
46656
|
+
stripped = stripped.replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, "");
|
|
46657
|
+
stripped = stripped.replace(
|
|
46658
|
+
/<ac:plain-text-body>[\s\S]*?<\/ac:plain-text-body>/gi,
|
|
46659
|
+
""
|
|
46660
|
+
);
|
|
46661
|
+
if (!/<ac:|<ri:|<time[\s/>]/i.test(stripped)) {
|
|
46662
|
+
return [];
|
|
46663
|
+
}
|
|
46664
|
+
const STRUCTURAL_MD = [
|
|
46665
|
+
{ name: "ATX heading (## ...)", re: /^#{1,6}[ \t]+\S/m },
|
|
46666
|
+
{ name: "fenced code block (```...)", re: /^```/m },
|
|
46667
|
+
{ name: "GFM table separator (| --- |)", re: /^\|[\s\-:|]+\|\s*$/m },
|
|
46668
|
+
{ name: "unordered list (- ... or * ...)", re: /^[-*][ \t]+\S/m },
|
|
46669
|
+
{ name: "ordered list (1. ...)", re: /^\d+\.[ \t]+\S/m },
|
|
46670
|
+
{
|
|
46671
|
+
name: "GitHub alert (> [!NOTE])",
|
|
46672
|
+
re: /^>\s*\[!(INFO|NOTE|TIP|WARNING|CAUTION|IMPORTANT)\]/im
|
|
46673
|
+
},
|
|
46674
|
+
{ name: "YAML frontmatter delimiter (---)", re: /^---\s*$/m }
|
|
46675
|
+
];
|
|
46676
|
+
return STRUCTURAL_MD.filter(({ re }) => re.test(stripped)).map(
|
|
46677
|
+
({ name }) => name
|
|
46678
|
+
);
|
|
46679
|
+
}
|
|
46651
46680
|
function assertPostTransformBody(inputLen, outputBody) {
|
|
46652
46681
|
if (outputBody.trim().length === 0) {
|
|
46653
46682
|
throw new ConverterError(
|
|
@@ -46759,6 +46788,25 @@ async function safePrepareBody(input) {
|
|
|
46759
46788
|
READ_ONLY_MARKDOWN_ROUND_TRIP
|
|
46760
46789
|
);
|
|
46761
46790
|
}
|
|
46791
|
+
const mixedSignals = detectMixedInput(body);
|
|
46792
|
+
if (mixedSignals.length > 0) {
|
|
46793
|
+
throw new ConverterError(
|
|
46794
|
+
`Body contains BOTH Confluence storage tags (<ac:.../>, <ri:.../>) AND markdown structural patterns: ${mixedSignals.join(", ")}.
|
|
46795
|
+
|
|
46796
|
+
The format detector classifies any body containing storage tags as storage format and skips markdown\u2192storage conversion. Submitting this would store the markdown verbatim and Confluence would render it as literal text.
|
|
46797
|
+
|
|
46798
|
+
Pick one path:
|
|
46799
|
+
\u2022 TOC macro from markdown \u2014 drop the inline <ac:structured-macro ac:name="toc"/> and add YAML frontmatter at the top of the body:
|
|
46800
|
+
---
|
|
46801
|
+
toc:
|
|
46802
|
+
maxLevel: 3
|
|
46803
|
+
minLevel: 1
|
|
46804
|
+
---
|
|
46805
|
+
\u2022 Other macros from markdown \u2014 use directive syntax (e.g. ":info[content]", ":mention[Name]{accountId=...}", ":date[2026-04-23]").
|
|
46806
|
+
\u2022 Pure storage format \u2014 convert the markdown structure (## headings, lists, tables, code fences) to <h1>/<h2>/<p>/<ul>/<table>/etc. before submitting.`,
|
|
46807
|
+
MIXED_INPUT_DETECTED
|
|
46808
|
+
);
|
|
46809
|
+
}
|
|
46762
46810
|
const converterOptions = {
|
|
46763
46811
|
allowRawHtml: allowRawHtml === true,
|
|
46764
46812
|
...confluenceBaseUrl ? { confluenceBaseUrl } : {}
|
|
@@ -46951,7 +46999,7 @@ async function safeSubmitPage(input) {
|
|
|
46951
46999
|
throw err;
|
|
46952
47000
|
}
|
|
46953
47001
|
}
|
|
46954
|
-
var DELETION_ACK_MISMATCH, POST_TRANSFORM_BODY_REJECTED, READ_ONLY_MARKDOWN_ROUND_TRIP, POST_TRANSFORM_MIN_INPUT_LEN, POST_TRANSFORM_MAX_REDUCTION_RATIO;
|
|
47002
|
+
var DELETION_ACK_MISMATCH, POST_TRANSFORM_BODY_REJECTED, READ_ONLY_MARKDOWN_ROUND_TRIP, MIXED_INPUT_DETECTED, POST_TRANSFORM_MIN_INPUT_LEN, POST_TRANSFORM_MAX_REDUCTION_RATIO;
|
|
46955
47003
|
var init_safe_write = __esm({
|
|
46956
47004
|
"src/server/safe-write.ts"() {
|
|
46957
47005
|
"use strict";
|
|
@@ -46965,6 +47013,7 @@ var init_safe_write = __esm({
|
|
|
46965
47013
|
DELETION_ACK_MISMATCH = "DELETION_ACK_MISMATCH";
|
|
46966
47014
|
POST_TRANSFORM_BODY_REJECTED = "POST_TRANSFORM_BODY_REJECTED";
|
|
46967
47015
|
READ_ONLY_MARKDOWN_ROUND_TRIP = "READ_ONLY_MARKDOWN_ROUND_TRIP";
|
|
47016
|
+
MIXED_INPUT_DETECTED = "MIXED_INPUT_DETECTED";
|
|
46968
47017
|
POST_TRANSFORM_MIN_INPUT_LEN = 500;
|
|
46969
47018
|
POST_TRANSFORM_MAX_REDUCTION_RATIO = 0.9;
|
|
46970
47019
|
}
|
|
@@ -47944,7 +47993,7 @@ __export(upgrade_exports, {
|
|
|
47944
47993
|
runUpgrade: () => runUpgrade
|
|
47945
47994
|
});
|
|
47946
47995
|
async function runUpgrade() {
|
|
47947
|
-
const currentVersion = "5.
|
|
47996
|
+
const currentVersion = "5.6.0";
|
|
47948
47997
|
console.log(`epimethian-mcp upgrade: current version v${currentVersion}`);
|
|
47949
47998
|
let pending = await getPendingUpdate();
|
|
47950
47999
|
if (!pending) {
|
|
@@ -58962,7 +59011,7 @@ function registerTools(server, config3) {
|
|
|
58962
59011
|
{
|
|
58963
59012
|
description: describeWithLock(
|
|
58964
59013
|
withDestructiveWarning(
|
|
58965
|
-
"Create a new page in Confluence. Accepts either Confluence storage format (XHTML) or GFM markdown \u2014 markdown is automatically converted to storage format before submission. Use allow_raw_html: true to permit raw HTML inside markdown (disabled by default for security). Use confluence_base_url to override the base URL used by the link rewriter (defaults to the configured Confluence URL)."
|
|
59014
|
+
"Create a new page in Confluence. Accepts either Confluence storage format (XHTML) or GFM markdown \u2014 markdown is automatically converted to storage format before submission. Do NOT mix the two: a body that contains both <ac:.../> storage tags AND markdown structural patterns (## headings, lists, fenced code blocks) is rejected with MIXED_INPUT_DETECTED. To inject a TOC macro from markdown, use YAML frontmatter at the top of the body: `---\\ntoc:\\n maxLevel: 3\\n minLevel: 1\\n---`. For other macros from markdown, use directive syntax: `:info[content]`, `:mention[Name]{accountId=...}`, `:date[2026-04-23]`. Use allow_raw_html: true to permit raw HTML inside markdown (disabled by default for security). Use confluence_base_url to override the base URL used by the link rewriter (defaults to the configured Confluence URL)."
|
|
58966
59015
|
),
|
|
58967
59016
|
config3
|
|
58968
59017
|
),
|
|
@@ -58970,7 +59019,7 @@ function registerTools(server, config3) {
|
|
|
58970
59019
|
title: external_exports.string().describe("Page title"),
|
|
58971
59020
|
space_key: external_exports.string().describe("Confluence space key, e.g. 'DEV' or 'TEAM'"),
|
|
58972
59021
|
body: external_exports.string().describe(
|
|
58973
|
-
"Page content \u2014 GFM markdown or Confluence storage format (XHTML). Markdown is auto-detected and converted."
|
|
59022
|
+
"Page content \u2014 GFM markdown or Confluence storage format (XHTML). Markdown is auto-detected and converted. Do not mix the two: inlining <ac:.../> macros inside a markdown body is rejected. For a TOC use YAML frontmatter (toc: { maxLevel, minLevel }); for other macros use directive syntax (:info[...], :mention[...]{...})."
|
|
58974
59023
|
),
|
|
58975
59024
|
parent_id: external_exports.string().optional().describe("Optional parent page ID"),
|
|
58976
59025
|
allow_raw_html: external_exports.boolean().default(false).describe("Allow raw HTML passthrough inside markdown bodies (disabled by default; only enable for trusted content)."),
|
|
@@ -59099,7 +59148,7 @@ ${truncated}`);
|
|
|
59099
59148
|
{
|
|
59100
59149
|
description: describeWithLock(
|
|
59101
59150
|
withDestructiveWarning(
|
|
59102
|
-
"Update an existing Confluence page. Accepts GFM markdown or Confluence storage format \u2014 markdown is automatically converted via the token-aware write path, which preserves all existing macros and rich elements. You must provide the version number from your most recent get_page call. If the page was modified by someone else since then, this will return a conflict error \u2014 re-read the page and retry.\n\nFor narrow changes to a single section, prefer update_page_section \u2014 it leaves the rest of the page untouched and is safer for targeted edits.\n\nMarkdown update flags:\n- confirm_deletions: set to true to acknowledge removing preserved macros/elements (default false \u2014 any deletion errors until confirmed).\n- replace_body: set to true for a wholesale rewrite that skips preservation (default false).\n- confirm_shrinkage: set to true to acknowledge a >50% body size reduction (default false).\n- confirm_structure_loss: set to true to acknowledge a >50% heading count drop (default false).\n- allow_raw_html: allow raw HTML inside markdown bodies (default false).\n- confluence_base_url: override the URL used by the link rewriter.\n\nreplace_body skips all safety nets (token preservation, deletion confirmation). When delegating update_page to a subagent, ensure the agent includes the full existing body \u2014 replace_body replaces ALL content with only what you provide."
|
|
59151
|
+
"Update an existing Confluence page. Accepts GFM markdown or Confluence storage format \u2014 markdown is automatically converted via the token-aware write path, which preserves all existing macros and rich elements. Do NOT mix the two: a body that contains both <ac:.../> storage tags AND markdown structural patterns (## headings, lists, fenced code blocks) is rejected with MIXED_INPUT_DETECTED. To inject a TOC macro from markdown, use YAML frontmatter at the top of the body: `---\\ntoc:\\n maxLevel: 3\\n minLevel: 1\\n---`. For other macros from markdown, use directive syntax: `:info[content]`, `:mention[Name]{accountId=...}`, `:date[2026-04-23]`. You must provide the version number from your most recent get_page call. If the page was modified by someone else since then, this will return a conflict error \u2014 re-read the page and retry.\n\nFor narrow changes to a single section, prefer update_page_section \u2014 it leaves the rest of the page untouched and is safer for targeted edits.\n\nMarkdown update flags:\n- confirm_deletions: set to true to acknowledge removing preserved macros/elements (default false \u2014 any deletion errors until confirmed).\n- replace_body: set to true for a wholesale rewrite that skips preservation (default false).\n- confirm_shrinkage: set to true to acknowledge a >50% body size reduction (default false).\n- confirm_structure_loss: set to true to acknowledge a >50% heading count drop (default false).\n- allow_raw_html: allow raw HTML inside markdown bodies (default false).\n- confluence_base_url: override the URL used by the link rewriter.\n\nreplace_body skips all safety nets (token preservation, deletion confirmation). When delegating update_page to a subagent, ensure the agent includes the full existing body \u2014 replace_body replaces ALL content with only what you provide."
|
|
59103
59152
|
),
|
|
59104
59153
|
config3
|
|
59105
59154
|
),
|
|
@@ -59107,7 +59156,7 @@ ${truncated}`);
|
|
|
59107
59156
|
page_id: external_exports.string().describe("The Confluence page ID"),
|
|
59108
59157
|
title: external_exports.string().describe("Page title (use the title from get_page if unchanged)"),
|
|
59109
59158
|
version: external_exports.number().int().positive().describe("The page version number from your most recent get_page call"),
|
|
59110
|
-
body: external_exports.string().optional().describe("New body content \u2014 GFM markdown or Confluence storage format (XHTML). Markdown is auto-detected and converted via the token-aware write path."),
|
|
59159
|
+
body: external_exports.string().optional().describe("New body content \u2014 GFM markdown or Confluence storage format (XHTML). Markdown is auto-detected and converted via the token-aware write path. Do not mix the two: inlining <ac:.../> macros inside a markdown body is rejected. For a TOC use YAML frontmatter (toc: { maxLevel, minLevel }); for other macros use directive syntax (:info[...], :mention[...]{...})."),
|
|
59111
59160
|
version_message: external_exports.string().optional().describe("Optional version comment"),
|
|
59112
59161
|
confirm_deletions: external_exports.boolean().default(false).describe("Set to true to acknowledge that your markdown removes preserved macros or rich elements. Required when any preserved element would be deleted."),
|
|
59113
59162
|
replace_body: external_exports.boolean().default(false).describe("Set to true for a wholesale page rewrite that skips token preservation. All existing macros will be lost. Use only when intentionally replacing the full body."),
|
|
@@ -59207,7 +59256,7 @@ ${truncated}`);
|
|
|
59207
59256
|
inputSchema: {
|
|
59208
59257
|
page_id: external_exports.string().describe("The Confluence page ID"),
|
|
59209
59258
|
section: external_exports.string().describe("Heading text identifying the section to replace (case-insensitive)"),
|
|
59210
|
-
body: external_exports.string().describe("New content for this section \u2014 GFM markdown or Confluence storage format. Markdown is auto-detected and converted via the token-aware write path, which preserves existing macros and emoticons within the section. The heading itself is preserved; only content under it is replaced."),
|
|
59259
|
+
body: external_exports.string().describe("New content for this section \u2014 GFM markdown or Confluence storage format. Markdown is auto-detected and converted via the token-aware write path, which preserves existing macros and emoticons within the section. The heading itself is preserved; only content under it is replaced. Do not mix the two: inlining <ac:.../> macros inside a markdown body is rejected with MIXED_INPUT_DETECTED. For macros from markdown use directive syntax (:info[...], :mention[...]{...})."),
|
|
59211
59260
|
version: external_exports.number().int().positive().describe("The page version number from your most recent get_page call"),
|
|
59212
59261
|
version_message: external_exports.string().optional().describe("Optional version comment"),
|
|
59213
59262
|
confirm_deletions: external_exports.boolean().default(false).describe("Set to true to acknowledge that your markdown removes preserved macros, emoticons, or rich elements from this section. Required when any preserved element would be deleted.")
|
|
@@ -60369,7 +60418,7 @@ ${titleFenced}${echo2}`
|
|
|
60369
60418
|
inputSchema: {}
|
|
60370
60419
|
},
|
|
60371
60420
|
async () => {
|
|
60372
|
-
let text2 = `epimethian-mcp v${"5.
|
|
60421
|
+
let text2 = `epimethian-mcp v${"5.6.0"}`;
|
|
60373
60422
|
try {
|
|
60374
60423
|
const pending = await getPendingUpdate();
|
|
60375
60424
|
if (pending) {
|
|
@@ -60400,7 +60449,7 @@ ${label} update available: v${pending.current} \u2192 v${pending.latest}. Run \`
|
|
|
60400
60449
|
const pending = await getPendingUpdate();
|
|
60401
60450
|
if (!pending) {
|
|
60402
60451
|
return toolResult(
|
|
60403
|
-
`epimethian-mcp v${"5.
|
|
60452
|
+
`epimethian-mcp v${"5.6.0"} is already up to date.`
|
|
60404
60453
|
);
|
|
60405
60454
|
}
|
|
60406
60455
|
const output = await performUpgrade(pending.latest);
|
|
@@ -60422,7 +60471,7 @@ async function startRecoveryServer(profile) {
|
|
|
60422
60471
|
const server = new McpServer(
|
|
60423
60472
|
{
|
|
60424
60473
|
name: `confluence-${profile}-setup-needed`,
|
|
60425
|
-
version: "5.
|
|
60474
|
+
version: "5.6.0"
|
|
60426
60475
|
},
|
|
60427
60476
|
{
|
|
60428
60477
|
instructions: `The Confluence profile "${profile}" referenced by CONFLUENCE_PROFILE has no keychain entry, so no Confluence tools are available. Call the setup_profile tool for instructions to create it.`
|
|
@@ -60470,21 +60519,21 @@ async function main() {
|
|
|
60470
60519
|
const serverName = config3.profile ? `confluence-${config3.profile}` : "confluence";
|
|
60471
60520
|
const server = new McpServer({
|
|
60472
60521
|
name: serverName,
|
|
60473
|
-
version: "5.
|
|
60522
|
+
version: "5.6.0"
|
|
60474
60523
|
});
|
|
60475
60524
|
registerTools(server, config3);
|
|
60476
60525
|
const transport = new StdioServerTransport();
|
|
60477
60526
|
await server.connect(transport);
|
|
60478
60527
|
try {
|
|
60479
60528
|
const pending = await getPendingUpdate();
|
|
60480
|
-
if (pending && pending.current === "5.
|
|
60529
|
+
if (pending && pending.current === "5.6.0") {
|
|
60481
60530
|
console.error(
|
|
60482
60531
|
`epimethian-mcp: update available: v${pending.current} \u2192 v${pending.latest} (${pending.type}). Run \`epimethian-mcp upgrade\` to install.`
|
|
60483
60532
|
);
|
|
60484
60533
|
}
|
|
60485
60534
|
} catch {
|
|
60486
60535
|
}
|
|
60487
|
-
checkForUpdates("5.
|
|
60536
|
+
checkForUpdates("5.6.0").catch(() => {
|
|
60488
60537
|
});
|
|
60489
60538
|
}
|
|
60490
60539
|
|