@de-otio/epimethian-mcp 5.2.0 → 5.3.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/README.md +2 -2
- package/dist/cli/index.js +263 -32
- package/dist/cli/index.js.map +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ The official [Atlassian MCP server](https://github.com/atlassian/atlassian-mcp-s
|
|
|
14
14
|
- **Multi-tenant profile isolation** — Each Atlassian tenant gets its own named profile with fully separate credentials and keychain entries. No risk of cross-tenant writes when switching between clients.
|
|
15
15
|
- **Tenant-aware write safety** — Write operations echo the target tenant so the AI agent (and you) always see where changes are going before they land.
|
|
16
16
|
- **draw.io diagram support** — Create and embed draw.io diagrams directly in Confluence pages, something the official server doesn't expose.
|
|
17
|
-
- **Attribution tracking** —
|
|
17
|
+
- **Attribution tracking** — Edited pages are labelled `epimethian-edited` for easy discovery. Confluence version messages include the MCP client name (e.g. "Updated by Claude Code (via Epimethian v5.2.0)") so you can trace which AI-assisted edits touched which content.
|
|
18
18
|
|
|
19
19
|
If you don't need any of the above, the official Atlassian server is a fine choice.
|
|
20
20
|
|
|
@@ -113,7 +113,7 @@ Confluence pages are verbose — storage format HTML with macro markup can easil
|
|
|
113
113
|
| `create_page` | Create a new page |
|
|
114
114
|
| `get_page` | Read a page by ID (`headings_only`, `section`, `max_length`, `format`) |
|
|
115
115
|
| `get_page_by_title` | Look up a page by title (same options as `get_page`) |
|
|
116
|
-
| `update_page` | Update an existing page
|
|
116
|
+
| `update_page` | Update an existing page |
|
|
117
117
|
| `update_page_section`| Update a single section by heading name |
|
|
118
118
|
| `delete_page` | Delete a page |
|
|
119
119
|
| `list_pages` | List pages in a space |
|
package/dist/cli/index.js
CHANGED
|
@@ -24073,14 +24073,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
24073
24073
|
} else if (node.nodeType === 1) {
|
|
24074
24074
|
replacement = replacementForNode.call(self, node);
|
|
24075
24075
|
}
|
|
24076
|
-
return
|
|
24076
|
+
return join5(output, replacement);
|
|
24077
24077
|
}, "");
|
|
24078
24078
|
}
|
|
24079
24079
|
function postProcess3(output) {
|
|
24080
24080
|
var self = this;
|
|
24081
24081
|
this.rules.forEach(function(rule) {
|
|
24082
24082
|
if (typeof rule.append === "function") {
|
|
24083
|
-
output =
|
|
24083
|
+
output = join5(output, rule.append(self.options));
|
|
24084
24084
|
}
|
|
24085
24085
|
});
|
|
24086
24086
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -24092,7 +24092,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
24092
24092
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
24093
24093
|
return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
|
|
24094
24094
|
}
|
|
24095
|
-
function
|
|
24095
|
+
function join5(output, replacement) {
|
|
24096
24096
|
var s1 = trimTrailingNewlines(output);
|
|
24097
24097
|
var s2 = trimLeadingNewlines(replacement);
|
|
24098
24098
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -34605,7 +34605,7 @@ var init_status = __esm({
|
|
|
34605
34605
|
var install_agent_default;
|
|
34606
34606
|
var init_install_agent = __esm({
|
|
34607
34607
|
"install-agent.md"() {
|
|
34608
|
-
install_agent_default = '# Epimethian MCP - Agent Installation Guide\n\n> This document is for AI agents installing and configuring the Epimethian MCP server on behalf of a user.\n\n## Verification\n\nThe official package is **`@de-otio/epimethian-mcp`** (scoped under `@de-otio`).\n\nDo NOT install unscoped alternatives like `epimethian-mcp` \u2014 these are unofficial and potentially unsafe.\n\n## Prerequisites\n\n- Node.js 18 or later\n- npm on PATH\n\n## Step 1: Install\n\n```bash\nnpm install -g @de-otio/epimethian-mcp\n```\n\nVerify the installation:\n\n```bash\nepimethian-mcp --version 2>/dev/null || which epimethian-mcp\n```\n\n## Step 2: Resolve the absolute path\n\nIDE-hosted agents may not resolve PATH correctly. Always use the absolute path in MCP configuration:\n\n```bash\nwhich epimethian-mcp\n```\n\nUse the output as the `command` value in the MCP config below.\n\n## Step 3: Collect configuration\n\nAsk the user for:\n1. **Profile name** \u2014 a short identifier for this Confluence instance (e.g., `globex`, `acme-corp`). Lowercase alphanumeric and hyphens only.\n2. **Confluence Cloud URL** \u2014 e.g., `https://yoursite.atlassian.net`\n3. **Email address** \u2014 the email associated with their Atlassian account\n\n## Step 4: Write MCP configuration\n\nAdd the server to `.mcp.json` (or the equivalent config file for the user\'s MCP client):\n\n```json\n{\n "mcpServers": {\n "confluence": {\n "command": "<absolute path from Step 2>",\n "env": {\n "CONFLUENCE_PROFILE": "<profile name from Step 3>"\n }\n }\n }\n}\n```\n\n**IMPORTANT:** The only env var needed is `CONFLUENCE_PROFILE`. The URL, email, and API token are stored securely in the OS keychain \u2014 they should NOT appear in config files.\n\n## Step 5: Credential setup\n\nTell the user to run this command in their terminal:\n\n```\nepimethian-mcp setup --profile <profile name from Step 3>\n```\n\nThis interactive command will:\n1. Prompt for the Confluence URL, email, and API token (masked input)\n2. Test the connection\n3. Store all credentials securely in the OS keychain under the named profile\n\nThe API token is generated at: https://id.atlassian.com/manage-profile/security/api-tokens\n\n**Do NOT ask the user for the API token yourself.** The token must go directly from the user into the interactive setup command to avoid appearing in conversation logs.\n\n## Step 6: User must restart the MCP client\n\n**IMPORTANT:** The user must restart their MCP client (e.g., restart Claude Code, reload VS Code, restart Claude Desktop) for the new server configuration to take effect. The MCP client reads `.mcp.json` at startup and does not detect changes while running.\n\nTell the user:\n> Please restart your MCP client now to activate the Confluence tools.\n\n## Step 7: Validation\n\nAfter the user restarts, verify the server is working by listing available Confluence tools or running a simple operation like listing spaces.\n\n## Adding Additional Tenants\n\nTo add a second Confluence instance (e.g., for a different customer):\n\n1. Run `epimethian-mcp setup --profile <new-profile-name>` with the new credentials\n2. In the project that uses the new tenant, update `.mcp.json` to set `CONFLUENCE_PROFILE` to the new profile name\n3. Restart the MCP client\n\nEach VS Code window / Claude Code session uses the profile specified in its `.mcp.json`. Profiles are fully isolated \u2014 different OS keychain entries, different Confluence instances.\n\n## Managing Profiles\n\n- List all profiles: `epimethian-mcp profiles`\n- Show details: `epimethian-mcp profiles --verbose`\n- Check connection: `CONFLUENCE_PROFILE=<name> epimethian-mcp status`\n- Set read-only: `epimethian-mcp profiles --set-read-only <name>`\n- Set read-write: `epimethian-mcp profiles --set-read-write <name>`\n\n### Read-Only Mode\n\nNew profiles default to **read-only**. When read-only, all write tools are blocked and return an error. To enable writes for a profile:\n\n```bash\nepimethian-mcp profiles --set-read-write <name>\n```\n\nOr during setup: `epimethian-mcp setup --profile <name> --read-write`\n\n**Important:** Restart any running MCP servers after changing the read-only flag.\n\n### Removing a Profile\n\nTo delete a profile and its credentials, run:\n\n```bash\nepimethian-mcp profiles --remove <name> --force\n```\n\n**Agents must pass `--force`** because the command normally prompts for interactive confirmation (`Remove profile "<name>" and delete its credentials? [y/N]`), which will fail in non-TTY environments like agent shell sessions. The `--force` flag skips the confirmation prompt when stdin is not a TTY.\n\nThis command:\n1. Deletes the credential entry (URL, email, API token) from the OS keychain\n2. Removes the profile from the registry at `~/.config/epimethian-mcp/profiles.json`\n3. Writes an entry to the audit log at `~/.config/epimethian-mcp/audit.log`\n\nAfter removing a profile, also remove or update any `.mcp.json` files that reference it \u2014 otherwise the MCP server will fail to start with a missing-profile error.\n\n**Errors:**\n- If the profile name is invalid (not matching lowercase alphanumeric/hyphens, 1\u201363 chars), the command exits with code 1\n- If the profile does not exist in the keychain, the keychain deletion is silently skipped \u2014 the registry entry is still removed\n\n## Accessing This Guide Post-Install\n\nOnce installed, this guide is available locally via:\n\n```bash\nepimethian-mcp agent-guide\n```\n\nThis prints the full agent guide to stdout \u2014 no web fetch required.\n\n## Uninstallation\n\nWhen a user asks to uninstall Epimethian MCP, follow these steps:\n\n### Step 1: Check for existing profiles\n\n```bash\nepimethian-mcp profiles\n```\n\n### Step 2: Ask the user about credential cleanup\n\nIf profiles exist, ask the user:\n\n> You have Epimethian profiles configured: [list the profile names]. Would you like to delete all stored credentials before uninstalling? (This removes API tokens from your OS keychain.)\n\n### Step 3: Delete credentials (if the user agrees)\n\nFor each profile the user wants removed:\n\n```bash\nepimethian-mcp profiles --remove <name> --force\n```\n\nOr to remove all profiles:\n\n```bash\nfor name in $(epimethian-mcp profiles | grep \'^ \'); do epimethian-mcp profiles --remove "$name" --force; done\n```\n\n### Step 4: Remove MCP configuration\n\nDelete the `confluence` entry (or the tenant-specific entry like `confluence-globex`) from the project\'s `.mcp.json`.\n\n### Step 5: Uninstall the package\n\n```bash\nnpm uninstall -g @de-otio/epimethian-mcp\n```\n\n### Step 6: Restart the MCP client\n\nTell the user to restart their MCP client so it stops trying to launch the removed server.\n\n## CI/CD (No Keychain)\n\nFor environments where the OS keychain is unavailable (Docker, CI), set all three env vars directly:\n\n```json\n{\n "mcpServers": {\n "confluence": {\n "command": "<absolute path>",\n "env": {\n "CONFLUENCE_URL": "<url>",\n "CONFLUENCE_EMAIL": "<email>",\n "CONFLUENCE_API_TOKEN": "<token>"\n }\n }\n }\n}\n```\n\n**Warning:** This exposes the API token in the process environment. Use profile-based auth whenever possible.\n\n## Troubleshooting\n\nIf **npm install fails**:\n- Verify Node.js 18+ is installed: `node --version`\n- Verify npm is on PATH: `npm --version`\n- If permission errors occur, the user may need to fix their npm prefix or use a Node version manager (nvm, fnm)\n\nIf **`epimethian-mcp setup` fails**:\n- "Connection failed": Verify the Confluence URL is correct and accessible\n- "Token is invalid or expired": The user needs to generate a new API token at https://id.atlassian.com/manage-profile/security/api-tokens\n- Keychain errors on Linux: The user may need to install `libsecret` / `gnome-keyring` (`apt install libsecret-tools` or equivalent)\n\nIf **the server doesn\'t appear after restart**:\n- Verify the `.mcp.json` path is correct for the user\'s MCP client\n- Verify the `command` value is an absolute path (run `which epimethian-mcp` to confirm)\n- Check that `.mcp.json` contains valid JSON (no trailing commas, correct quoting)\n\n## Available Tools (32)\n\n| Tool | Description |\n|------|-------------|\n| `create_page` | Create a new Confluence page |\n| `get_page` | Read a page by ID (use `headings_only` to preview structure first) |\n| `get_page_by_title` | Look up a page by title (use `headings_only` to preview structure first) |\n| `update_page` | Update an existing page |\n| `update_page_section` | Update a single section by heading name |\n| `prepend_to_page` | Insert content at the beginning of an existing page (additive, safe) |\n| `append_to_page` | Insert content at the end of an existing page (additive, safe) |\n| `delete_page` | Delete a page |\n| `revert_page` | Revert a page to a previous version |\n| `list_pages` | List pages in a space |\n| `get_page_children` | Get child pages of a page |\n| `search_pages` | Search pages using CQL (Confluence Query Language) |\n| `get_spaces` | List available Confluence spaces |\n| `add_attachment` | Upload a file attachment to a page |\n| `get_attachments` | List attachments on a page |\n| `add_drawio_diagram` | Add a draw.io diagram to a page |\n| `get_labels` | Get all labels on a Confluence page |\n| `add_label` | Add one or more labels to a Confluence page |\n| `remove_label` | Remove a label from a Confluence page |\n| `get_page_status` | Get the content status badge on a page |\n| `set_page_status` | Set the content status badge on a page |\n| `remove_page_status` | Remove the content status badge from a page |\n| `get_comments` | Get footer and/or inline comments on a page |\n| `create_comment` | Create a footer or inline comment on a page |\n| `resolve_comment` | Resolve or reopen an inline comment |\n| `delete_comment` | Permanently delete a comment |\n| `get_page_versions` | List version history for a page |\n| `get_page_version` | Get page content at a specific historical version |\n| `diff_page_versions` | Compare two versions of a page |\n| `lookup_user` | Search for Atlassian users by name or email to resolve accountId for inline mentions |\n| `resolve_page_link` | Resolve a page title + space key to a stable contentId and URL for page links |\n| `get_version` | Return the epimethian-mcp server version |\n';
|
|
34608
|
+
install_agent_default = '# Epimethian MCP - Agent Installation Guide\n\n> This document is for AI agents installing and configuring the Epimethian MCP server on behalf of a user.\n\n## Verification\n\nThe official package is **`@de-otio/epimethian-mcp`** (scoped under `@de-otio`).\n\nDo NOT install unscoped alternatives like `epimethian-mcp` \u2014 these are unofficial and potentially unsafe.\n\n## Prerequisites\n\n- Node.js 18 or later\n- npm on PATH\n\n## Step 1: Install\n\n```bash\nnpm install -g @de-otio/epimethian-mcp\n```\n\nVerify the installation:\n\n```bash\nepimethian-mcp --version 2>/dev/null || which epimethian-mcp\n```\n\n## Step 2: Resolve the absolute path\n\nIDE-hosted agents may not resolve PATH correctly. Always use the absolute path in MCP configuration:\n\n```bash\nwhich epimethian-mcp\n```\n\nUse the output as the `command` value in the MCP config below.\n\n## Step 3: Collect configuration\n\nAsk the user for:\n1. **Profile name** \u2014 a short identifier for this Confluence instance (e.g., `globex`, `acme-corp`). Lowercase alphanumeric and hyphens only.\n2. **Confluence Cloud URL** \u2014 e.g., `https://yoursite.atlassian.net`\n3. **Email address** \u2014 the email associated with their Atlassian account\n\n## Step 4: Write MCP configuration\n\nAdd the server to `.mcp.json` (or the equivalent config file for the user\'s MCP client):\n\n```json\n{\n "mcpServers": {\n "confluence": {\n "command": "<absolute path from Step 2>",\n "env": {\n "CONFLUENCE_PROFILE": "<profile name from Step 3>"\n }\n }\n }\n}\n```\n\n**IMPORTANT:** The only env var needed is `CONFLUENCE_PROFILE`. The URL, email, and API token are stored securely in the OS keychain \u2014 they should NOT appear in config files.\n\n## Step 5: Credential setup\n\nTell the user to run this command in their terminal:\n\n```\nepimethian-mcp setup --profile <profile name from Step 3>\n```\n\nThis interactive command will:\n1. Prompt for the Confluence URL, email, and API token (masked input)\n2. Test the connection\n3. Store all credentials securely in the OS keychain under the named profile\n\nThe API token is generated at: https://id.atlassian.com/manage-profile/security/api-tokens\n\n**Do NOT ask the user for the API token yourself.** The token must go directly from the user into the interactive setup command to avoid appearing in conversation logs.\n\n## Step 6: User must restart the MCP client\n\n**IMPORTANT:** The user must restart their MCP client (e.g., restart Claude Code, reload VS Code, restart Claude Desktop) for the new server configuration to take effect. The MCP client reads `.mcp.json` at startup and does not detect changes while running.\n\nTell the user:\n> Please restart your MCP client now to activate the Confluence tools.\n\n## Step 7: Validation\n\nAfter the user restarts, verify the server is working by listing available Confluence tools or running a simple operation like listing spaces.\n\n## Adding Additional Tenants\n\nTo add a second Confluence instance (e.g., for a different customer):\n\n1. Run `epimethian-mcp setup --profile <new-profile-name>` with the new credentials\n2. In the project that uses the new tenant, update `.mcp.json` to set `CONFLUENCE_PROFILE` to the new profile name\n3. Restart the MCP client\n\nEach VS Code window / Claude Code session uses the profile specified in its `.mcp.json`. Profiles are fully isolated \u2014 different OS keychain entries, different Confluence instances.\n\n## Managing Profiles\n\n- List all profiles: `epimethian-mcp profiles`\n- Show details: `epimethian-mcp profiles --verbose`\n- Check connection: `CONFLUENCE_PROFILE=<name> epimethian-mcp status`\n- Set read-only: `epimethian-mcp profiles --set-read-only <name>`\n- Set read-write: `epimethian-mcp profiles --set-read-write <name>`\n\n### Read-Only Mode\n\nNew profiles default to **read-only**. When read-only, all write tools are blocked and return an error. To enable writes for a profile:\n\n```bash\nepimethian-mcp profiles --set-read-write <name>\n```\n\nOr during setup: `epimethian-mcp setup --profile <name> --read-write`\n\n**Important:** Restart any running MCP servers after changing the read-only flag.\n\n### Removing a Profile\n\nTo delete a profile and its credentials, run:\n\n```bash\nepimethian-mcp profiles --remove <name> --force\n```\n\n**Agents must pass `--force`** because the command normally prompts for interactive confirmation (`Remove profile "<name>" and delete its credentials? [y/N]`), which will fail in non-TTY environments like agent shell sessions. The `--force` flag skips the confirmation prompt when stdin is not a TTY.\n\nThis command:\n1. Deletes the credential entry (URL, email, API token) from the OS keychain\n2. Removes the profile from the registry at `~/.config/epimethian-mcp/profiles.json`\n3. Writes an entry to the audit log at `~/.config/epimethian-mcp/audit.log`\n\nAfter removing a profile, also remove or update any `.mcp.json` files that reference it \u2014 otherwise the MCP server will fail to start with a missing-profile error.\n\n**Errors:**\n- If the profile name is invalid (not matching lowercase alphanumeric/hyphens, 1\u201363 chars), the command exits with code 1\n- If the profile does not exist in the keychain, the keychain deletion is silently skipped \u2014 the registry entry is still removed\n\n## Accessing This Guide Post-Install\n\nOnce installed, this guide is available locally via:\n\n```bash\nepimethian-mcp agent-guide\n```\n\nThis prints the full agent guide to stdout \u2014 no web fetch required.\n\n## Uninstallation\n\nWhen a user asks to uninstall Epimethian MCP, follow these steps:\n\n### Step 1: Check for existing profiles\n\n```bash\nepimethian-mcp profiles\n```\n\n### Step 2: Ask the user about credential cleanup\n\nIf profiles exist, ask the user:\n\n> You have Epimethian profiles configured: [list the profile names]. Would you like to delete all stored credentials before uninstalling? (This removes API tokens from your OS keychain.)\n\n### Step 3: Delete credentials (if the user agrees)\n\nFor each profile the user wants removed:\n\n```bash\nepimethian-mcp profiles --remove <name> --force\n```\n\nOr to remove all profiles:\n\n```bash\nfor name in $(epimethian-mcp profiles | grep \'^ \'); do epimethian-mcp profiles --remove "$name" --force; done\n```\n\n### Step 4: Remove MCP configuration\n\nDelete the `confluence` entry (or the tenant-specific entry like `confluence-globex`) from the project\'s `.mcp.json`.\n\n### Step 5: Uninstall the package\n\n```bash\nnpm uninstall -g @de-otio/epimethian-mcp\n```\n\n### Step 6: Restart the MCP client\n\nTell the user to restart their MCP client so it stops trying to launch the removed server.\n\n## CI/CD (No Keychain)\n\nFor environments where the OS keychain is unavailable (Docker, CI), set all three env vars directly:\n\n```json\n{\n "mcpServers": {\n "confluence": {\n "command": "<absolute path>",\n "env": {\n "CONFLUENCE_URL": "<url>",\n "CONFLUENCE_EMAIL": "<email>",\n "CONFLUENCE_API_TOKEN": "<token>"\n }\n }\n }\n}\n```\n\n**Warning:** This exposes the API token in the process environment. Use profile-based auth whenever possible.\n\n## Troubleshooting\n\nIf **npm install fails**:\n- Verify Node.js 18+ is installed: `node --version`\n- Verify npm is on PATH: `npm --version`\n- If permission errors occur, the user may need to fix their npm prefix or use a Node version manager (nvm, fnm)\n\nIf **`epimethian-mcp setup` fails**:\n- "Connection failed": Verify the Confluence URL is correct and accessible\n- "Token is invalid or expired": The user needs to generate a new API token at https://id.atlassian.com/manage-profile/security/api-tokens\n- Keychain errors on Linux: The user may need to install `libsecret` / `gnome-keyring` (`apt install libsecret-tools` or equivalent)\n\nIf **the server doesn\'t appear after restart**:\n- Verify the `.mcp.json` path is correct for the user\'s MCP client\n- Verify the `command` value is an absolute path (run `which epimethian-mcp` to confirm)\n- Check that `.mcp.json` contains valid JSON (no trailing commas, correct quoting)\n\n## Available Tools (33)\n\n| Tool | Description |\n|------|-------------|\n| `create_page` | Create a new Confluence page |\n| `get_page` | Read a page by ID (use `headings_only` to preview structure first) |\n| `get_page_by_title` | Look up a page by title (use `headings_only` to preview structure first) |\n| `update_page` | Update an existing page |\n| `update_page_section` | Update a single section by heading name |\n| `prepend_to_page` | Insert content at the beginning of an existing page (additive, safe) |\n| `append_to_page` | Insert content at the end of an existing page (additive, safe) |\n| `delete_page` | Delete a page |\n| `revert_page` | Revert a page to a previous version |\n| `list_pages` | List pages in a space |\n| `get_page_children` | Get child pages of a page |\n| `search_pages` | Search pages using CQL (Confluence Query Language) |\n| `get_spaces` | List available Confluence spaces |\n| `add_attachment` | Upload a file attachment to a page |\n| `get_attachments` | List attachments on a page |\n| `add_drawio_diagram` | Add a draw.io diagram to a page |\n| `get_labels` | Get all labels on a Confluence page |\n| `add_label` | Add one or more labels to a Confluence page |\n| `remove_label` | Remove a label from a Confluence page |\n| `get_page_status` | Get the content status badge on a page |\n| `set_page_status` | Set the content status badge on a page |\n| `remove_page_status` | Remove the content status badge from a page |\n| `get_comments` | Get footer and/or inline comments on a page |\n| `create_comment` | Create a footer or inline comment on a page |\n| `resolve_comment` | Resolve or reopen an inline comment |\n| `delete_comment` | Permanently delete a comment |\n| `get_page_versions` | List version history for a page |\n| `get_page_version` | Get page content at a specific historical version |\n| `diff_page_versions` | Compare two versions of a page |\n| `lookup_user` | Search for Atlassian users by name or email to resolve accountId for inline mentions |\n| `resolve_page_link` | Resolve a page title + space key to a stable contentId and URL for page links |\n| `get_version` | Return the epimethian-mcp server version and report available updates |\n| `upgrade` | Upgrade epimethian-mcp to the latest available version (restart required after) |\n';
|
|
34609
34609
|
}
|
|
34610
34610
|
});
|
|
34611
34611
|
|
|
@@ -48840,9 +48840,9 @@ var StdioServerTransport = class {
|
|
|
48840
48840
|
};
|
|
48841
48841
|
|
|
48842
48842
|
// src/server/index.ts
|
|
48843
|
-
var
|
|
48844
|
-
var
|
|
48845
|
-
var
|
|
48843
|
+
var import_promises3 = require("node:fs/promises");
|
|
48844
|
+
var import_node_os3 = require("node:os");
|
|
48845
|
+
var import_node_path4 = require("node:path");
|
|
48846
48846
|
|
|
48847
48847
|
// src/server/confluence-client.ts
|
|
48848
48848
|
var import_turndown = __toESM(require_turndown_cjs());
|
|
@@ -49324,7 +49324,7 @@ async function getPage(pageId, includeBody) {
|
|
|
49324
49324
|
async function createPage(spaceId, title, body, parentId, clientLabel) {
|
|
49325
49325
|
const cfg = await getConfig();
|
|
49326
49326
|
const pageBody = stripAttributionFooter(toStorageFormat(body));
|
|
49327
|
-
const epimethianTag = `Epimethian v${"5.
|
|
49327
|
+
const epimethianTag = `Epimethian v${"5.3.0"}`;
|
|
49328
49328
|
const versionMsg = cfg.attribution && clientLabel ? `Created by ${clientLabel} (via ${epimethianTag})` : `Created by ${epimethianTag}`;
|
|
49329
49329
|
const payload = {
|
|
49330
49330
|
title,
|
|
@@ -49349,7 +49349,7 @@ async function createPage(spaceId, title, body, parentId, clientLabel) {
|
|
|
49349
49349
|
async function updatePage(pageId, opts) {
|
|
49350
49350
|
const cfg = await getConfig();
|
|
49351
49351
|
const newVersion = opts.version + 1;
|
|
49352
|
-
const epimethianTag = `Epimethian v${"5.
|
|
49352
|
+
const epimethianTag = `Epimethian v${"5.3.0"}`;
|
|
49353
49353
|
const effectiveClient = cfg.attribution ? opts.clientLabel : void 0;
|
|
49354
49354
|
let versionMessage;
|
|
49355
49355
|
if (opts.versionMessage && effectiveClient)
|
|
@@ -56883,6 +56883,152 @@ function errorRecord(operation, pageId, err, extra) {
|
|
|
56883
56883
|
};
|
|
56884
56884
|
}
|
|
56885
56885
|
|
|
56886
|
+
// src/shared/update-check.ts
|
|
56887
|
+
var import_promises2 = require("node:fs/promises");
|
|
56888
|
+
var import_node_path3 = require("node:path");
|
|
56889
|
+
var import_node_os2 = require("node:os");
|
|
56890
|
+
var import_node_crypto2 = require("node:crypto");
|
|
56891
|
+
var import_node_child_process2 = require("node:child_process");
|
|
56892
|
+
var import_node_util = require("node:util");
|
|
56893
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process2.execFile);
|
|
56894
|
+
var CONFIG_DIR2 = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".config", "epimethian-mcp");
|
|
56895
|
+
var UPDATE_CHECK_FILE = (0, import_node_path3.join)(CONFIG_DIR2, "update-check.json");
|
|
56896
|
+
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
56897
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@de-otio/epimethian-mcp/latest";
|
|
56898
|
+
var PACKAGE_NAME = "@de-otio/epimethian-mcp";
|
|
56899
|
+
function parseSemVer(version2) {
|
|
56900
|
+
const match2 = version2.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
56901
|
+
if (!match2) return null;
|
|
56902
|
+
return {
|
|
56903
|
+
major: parseInt(match2[1], 10),
|
|
56904
|
+
minor: parseInt(match2[2], 10),
|
|
56905
|
+
patch: parseInt(match2[3], 10)
|
|
56906
|
+
};
|
|
56907
|
+
}
|
|
56908
|
+
function classifyUpdate(current, latest) {
|
|
56909
|
+
if (latest.major > current.major) return "major";
|
|
56910
|
+
if (latest.major === current.major && latest.minor > current.minor)
|
|
56911
|
+
return "minor";
|
|
56912
|
+
if (latest.major === current.major && latest.minor === current.minor && latest.patch > current.patch)
|
|
56913
|
+
return "patch";
|
|
56914
|
+
return null;
|
|
56915
|
+
}
|
|
56916
|
+
async function readCheckState() {
|
|
56917
|
+
try {
|
|
56918
|
+
const raw = await (0, import_promises2.readFile)(UPDATE_CHECK_FILE, "utf-8");
|
|
56919
|
+
const parsed = JSON.parse(raw);
|
|
56920
|
+
if (parsed && typeof parsed === "object" && typeof parsed.lastCheck === "string") {
|
|
56921
|
+
return parsed;
|
|
56922
|
+
}
|
|
56923
|
+
return null;
|
|
56924
|
+
} catch {
|
|
56925
|
+
return null;
|
|
56926
|
+
}
|
|
56927
|
+
}
|
|
56928
|
+
async function writeCheckState(state) {
|
|
56929
|
+
await (0, import_promises2.mkdir)(CONFIG_DIR2, { recursive: true, mode: 448 });
|
|
56930
|
+
const data = JSON.stringify(state, null, 2) + "\n";
|
|
56931
|
+
const tmpFile = (0, import_node_path3.join)(
|
|
56932
|
+
CONFIG_DIR2,
|
|
56933
|
+
`.update-check.${(0, import_node_crypto2.randomBytes)(4).toString("hex")}.tmp`
|
|
56934
|
+
);
|
|
56935
|
+
await (0, import_promises2.writeFile)(tmpFile, data, { mode: 384 });
|
|
56936
|
+
await (0, import_promises2.rename)(tmpFile, UPDATE_CHECK_FILE);
|
|
56937
|
+
}
|
|
56938
|
+
async function fetchLatestVersion() {
|
|
56939
|
+
try {
|
|
56940
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
56941
|
+
headers: { Accept: "application/json" },
|
|
56942
|
+
signal: AbortSignal.timeout(1e4)
|
|
56943
|
+
});
|
|
56944
|
+
if (!response.ok) return null;
|
|
56945
|
+
const data = await response.json();
|
|
56946
|
+
return typeof data.version === "string" ? data.version : null;
|
|
56947
|
+
} catch {
|
|
56948
|
+
return null;
|
|
56949
|
+
}
|
|
56950
|
+
}
|
|
56951
|
+
async function getPendingUpdate() {
|
|
56952
|
+
const state = await readCheckState();
|
|
56953
|
+
return state?.pendingUpdate ?? null;
|
|
56954
|
+
}
|
|
56955
|
+
async function clearPendingUpdate() {
|
|
56956
|
+
const state = await readCheckState();
|
|
56957
|
+
if (state) {
|
|
56958
|
+
delete state.pendingUpdate;
|
|
56959
|
+
await writeCheckState(state);
|
|
56960
|
+
}
|
|
56961
|
+
}
|
|
56962
|
+
async function performUpgrade(version2) {
|
|
56963
|
+
const { stdout: stdout3, stderr } = await execFileAsync(
|
|
56964
|
+
"npm",
|
|
56965
|
+
["install", "-g", `${PACKAGE_NAME}@${version2}`],
|
|
56966
|
+
{ timeout: 12e4 }
|
|
56967
|
+
);
|
|
56968
|
+
return (stdout3 + stderr).trim();
|
|
56969
|
+
}
|
|
56970
|
+
async function checkForUpdates(currentVersion) {
|
|
56971
|
+
if (process.env.EPIMETHIAN_NO_UPDATE_CHECK === "true") return null;
|
|
56972
|
+
try {
|
|
56973
|
+
const state = await readCheckState();
|
|
56974
|
+
if (state?.lastCheck) {
|
|
56975
|
+
const elapsed = Date.now() - new Date(state.lastCheck).getTime();
|
|
56976
|
+
if (elapsed < ONE_DAY_MS) {
|
|
56977
|
+
return state.pendingUpdate ?? null;
|
|
56978
|
+
}
|
|
56979
|
+
}
|
|
56980
|
+
const latestStr = await fetchLatestVersion();
|
|
56981
|
+
if (!latestStr) {
|
|
56982
|
+
return state?.pendingUpdate ?? null;
|
|
56983
|
+
}
|
|
56984
|
+
const current = parseSemVer(currentVersion);
|
|
56985
|
+
const latest = parseSemVer(latestStr);
|
|
56986
|
+
if (!current || !latest) return null;
|
|
56987
|
+
const type = classifyUpdate(current, latest);
|
|
56988
|
+
const newState = {
|
|
56989
|
+
lastCheck: (/* @__PURE__ */ new Date()).toISOString()
|
|
56990
|
+
};
|
|
56991
|
+
if (!type) {
|
|
56992
|
+
await writeCheckState(newState);
|
|
56993
|
+
return null;
|
|
56994
|
+
}
|
|
56995
|
+
const info = {
|
|
56996
|
+
current: currentVersion,
|
|
56997
|
+
latest: latestStr,
|
|
56998
|
+
type
|
|
56999
|
+
};
|
|
57000
|
+
if (type === "patch") {
|
|
57001
|
+
try {
|
|
57002
|
+
await performUpgrade(latestStr);
|
|
57003
|
+
info.autoInstalled = true;
|
|
57004
|
+
newState.pendingUpdate = info;
|
|
57005
|
+
await writeCheckState(newState);
|
|
57006
|
+
console.error(
|
|
57007
|
+
`[epimethian-mcp] Bugfix v${latestStr} installed automatically. Restart the MCP server to apply.`
|
|
57008
|
+
);
|
|
57009
|
+
} catch (err) {
|
|
57010
|
+
newState.pendingUpdate = info;
|
|
57011
|
+
await writeCheckState(newState);
|
|
57012
|
+
console.error(
|
|
57013
|
+
`[epimethian-mcp] Auto-update to v${latestStr} failed: ${err instanceof Error ? err.message : err}`
|
|
57014
|
+
);
|
|
57015
|
+
}
|
|
57016
|
+
return info;
|
|
57017
|
+
}
|
|
57018
|
+
newState.pendingUpdate = info;
|
|
57019
|
+
await writeCheckState(newState);
|
|
57020
|
+
console.error(
|
|
57021
|
+
`[epimethian-mcp] ${type === "major" ? "Major" : "Minor"} update available: v${currentVersion} \u2192 v${latestStr}. Use the upgrade tool to install.`
|
|
57022
|
+
);
|
|
57023
|
+
return info;
|
|
57024
|
+
} catch (err) {
|
|
57025
|
+
console.error(
|
|
57026
|
+
`[epimethian-mcp] Update check failed: ${err instanceof Error ? err.message : err}`
|
|
57027
|
+
);
|
|
57028
|
+
return null;
|
|
57029
|
+
}
|
|
57030
|
+
}
|
|
57031
|
+
|
|
56886
57032
|
// src/server/index.ts
|
|
56887
57033
|
function getClientLabel(server) {
|
|
56888
57034
|
const client = server.server.getClientVersion();
|
|
@@ -56892,6 +57038,7 @@ function getClientLabel(server) {
|
|
|
56892
57038
|
function escapeXml(s) {
|
|
56893
57039
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
56894
57040
|
}
|
|
57041
|
+
var READ_ONLY_MARKDOWN_MARKER = "<!-- epimethian:read-only-markdown \u2014 do not pass this content to update_page -->";
|
|
56895
57042
|
function formatMarkdownWithTokens(markdown, sidecar, header) {
|
|
56896
57043
|
const tokenCount = Object.keys(sidecar).length;
|
|
56897
57044
|
let body = markdown;
|
|
@@ -56904,13 +57051,19 @@ function formatMarkdownWithTokens(markdown, sidecar, header) {
|
|
|
56904
57051
|
const name = m && m[2] ? ` ac:name="${m[2]}"` : "";
|
|
56905
57052
|
return `- [[epi:${id}]]: <${tag}${name}>`;
|
|
56906
57053
|
}).join("\n");
|
|
56907
|
-
body =
|
|
57054
|
+
body = `${READ_ONLY_MARKDOWN_MARKER}
|
|
57055
|
+
|
|
57056
|
+
<!-- ${tokenCount} Confluence macro${tokenCount === 1 ? "" : "s"} preserved as tokens; remove a token to delete that macro on the next update_page -->
|
|
56908
57057
|
|
|
56909
57058
|
${markdown}
|
|
56910
57059
|
|
|
56911
57060
|
---
|
|
56912
57061
|
Tokens:
|
|
56913
57062
|
${table2}`;
|
|
57063
|
+
} else {
|
|
57064
|
+
body = `${READ_ONLY_MARKDOWN_MARKER}
|
|
57065
|
+
|
|
57066
|
+
${markdown}`;
|
|
56914
57067
|
}
|
|
56915
57068
|
return `${header}
|
|
56916
57069
|
|
|
@@ -56945,6 +57098,7 @@ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
56945
57098
|
"get_page_version",
|
|
56946
57099
|
"diff_page_versions",
|
|
56947
57100
|
"get_version",
|
|
57101
|
+
"upgrade",
|
|
56948
57102
|
"lookup_user",
|
|
56949
57103
|
"resolve_page_link"
|
|
56950
57104
|
]);
|
|
@@ -57021,6 +57175,12 @@ function registerTools(server, config3) {
|
|
|
57021
57175
|
);
|
|
57022
57176
|
const pageIdSchema = external_exports.string().regex(/^\d+$/, "Page ID must be numeric");
|
|
57023
57177
|
async function concatPageContent(page_id, version2, newContent, position, opts = {}) {
|
|
57178
|
+
if (newContent.includes("epimethian:read-only-markdown")) {
|
|
57179
|
+
throw new ConverterError(
|
|
57180
|
+
"The content contains output produced by get_page with format: 'markdown', which is a read-only rendering not suitable for prepend/append operations. Compose new content from scratch instead.",
|
|
57181
|
+
"READ_ONLY_MARKDOWN_ROUND_TRIP"
|
|
57182
|
+
);
|
|
57183
|
+
}
|
|
57024
57184
|
const currentPage = await getPage(page_id, true);
|
|
57025
57185
|
const currentStorage = currentPage.body?.storage?.value ?? currentPage.body?.value ?? "";
|
|
57026
57186
|
const isMarkdown = looksLikeMarkdown(newContent);
|
|
@@ -57076,6 +57236,12 @@ function registerTools(server, config3) {
|
|
|
57076
57236
|
const blocked = writeGuard("create_page", config3);
|
|
57077
57237
|
if (blocked) return blocked;
|
|
57078
57238
|
try {
|
|
57239
|
+
if (body.includes("epimethian:read-only-markdown")) {
|
|
57240
|
+
throw new ConverterError(
|
|
57241
|
+
"The body contains content produced by get_page with format: 'markdown', which is a read-only rendering not suitable for creating pages (tables, macros, and rich elements may be lost). Compose new markdown from scratch instead.",
|
|
57242
|
+
"READ_ONLY_MARKDOWN_ROUND_TRIP"
|
|
57243
|
+
);
|
|
57244
|
+
}
|
|
57079
57245
|
let finalBody = body;
|
|
57080
57246
|
if (looksLikeMarkdown(body)) {
|
|
57081
57247
|
const cfg = await getConfig();
|
|
@@ -57225,7 +57391,15 @@ ${truncated}`);
|
|
|
57225
57391
|
const currentStorage = currentPage.body?.storage?.value ?? currentPage.body?.value ?? "";
|
|
57226
57392
|
let finalStorage;
|
|
57227
57393
|
let effectiveVersionMessage = version_message;
|
|
57228
|
-
if (body &&
|
|
57394
|
+
if (body && body.includes("epimethian:read-only-markdown")) {
|
|
57395
|
+
throw new ConverterError(
|
|
57396
|
+
"The body contains content produced by get_page with format: 'markdown', which is a read-only rendering not suitable for round-trip updates (tables, macros, and rich elements may be lost). To update this page, either: (1) read with format: 'storage' and edit the storage XML, (2) use update_page_section for targeted edits, or (3) compose new markdown from scratch (do not copy from format: 'markdown' output).",
|
|
57397
|
+
"READ_ONLY_MARKDOWN_ROUND_TRIP"
|
|
57398
|
+
);
|
|
57399
|
+
}
|
|
57400
|
+
if (body === void 0 || body === null) {
|
|
57401
|
+
finalStorage = void 0;
|
|
57402
|
+
} else if (looksLikeMarkdown(body)) {
|
|
57229
57403
|
const plan = planUpdate({
|
|
57230
57404
|
currentStorage,
|
|
57231
57405
|
callerMarkdown: body,
|
|
@@ -57239,14 +57413,16 @@ ${truncated}`);
|
|
|
57239
57413
|
finalStorage = plan.newStorage;
|
|
57240
57414
|
effectiveVersionMessage = plan.versionMessage && version_message ? `${version_message}; ${plan.versionMessage}` : plan.versionMessage ?? version_message;
|
|
57241
57415
|
} else {
|
|
57242
|
-
finalStorage = body
|
|
57416
|
+
finalStorage = body;
|
|
57417
|
+
}
|
|
57418
|
+
if (finalStorage !== void 0) {
|
|
57419
|
+
enforceContentSafetyGuards({
|
|
57420
|
+
oldStorage: currentStorage,
|
|
57421
|
+
newStorage: finalStorage,
|
|
57422
|
+
confirmShrinkage: confirm_shrinkage,
|
|
57423
|
+
confirmStructureLoss: confirm_structure_loss
|
|
57424
|
+
});
|
|
57243
57425
|
}
|
|
57244
|
-
enforceContentSafetyGuards({
|
|
57245
|
-
oldStorage: currentStorage,
|
|
57246
|
-
newStorage: finalStorage,
|
|
57247
|
-
confirmShrinkage: confirm_shrinkage,
|
|
57248
|
-
confirmStructureLoss: confirm_structure_loss
|
|
57249
|
-
});
|
|
57250
57426
|
const { page, newVersion } = await updatePage(page_id, {
|
|
57251
57427
|
title,
|
|
57252
57428
|
body: finalStorage,
|
|
@@ -57262,11 +57438,12 @@ ${truncated}`);
|
|
|
57262
57438
|
oldVersion: version2,
|
|
57263
57439
|
newVersion,
|
|
57264
57440
|
oldBodyLen: currentStorage.length,
|
|
57265
|
-
newBodyLen: finalStorage.length,
|
|
57441
|
+
newBodyLen: finalStorage?.length ?? currentStorage.length,
|
|
57266
57442
|
replaceBody: replace_body || void 0
|
|
57267
57443
|
});
|
|
57444
|
+
const bodyReport = finalStorage !== void 0 ? `body: ${currentStorage.length}\u2192${finalStorage.length} chars` : `title only, body unchanged`;
|
|
57268
57445
|
return toolResult(
|
|
57269
|
-
`Updated: ${page.title} (ID: ${page.id}, version: ${newVersion},
|
|
57446
|
+
`Updated: ${page.title} (ID: ${page.id}, version: ${newVersion}, ${bodyReport})` + echo
|
|
57270
57447
|
);
|
|
57271
57448
|
} catch (err) {
|
|
57272
57449
|
logMutation(errorRecord("update_page", page_id, err, {
|
|
@@ -57323,6 +57500,12 @@ ${truncated}`);
|
|
|
57323
57500
|
const blocked = writeGuard("update_page_section", config3);
|
|
57324
57501
|
if (blocked) return blocked;
|
|
57325
57502
|
try {
|
|
57503
|
+
if (body.includes("epimethian:read-only-markdown")) {
|
|
57504
|
+
throw new ConverterError(
|
|
57505
|
+
"The body contains content produced by get_page with format: 'markdown', which is a read-only rendering not suitable for section updates. Use format: 'storage' to read the section, then edit the storage XML.",
|
|
57506
|
+
"READ_ONLY_MARKDOWN_ROUND_TRIP"
|
|
57507
|
+
);
|
|
57508
|
+
}
|
|
57326
57509
|
const page = await getPage(page_id, true);
|
|
57327
57510
|
const fullBody = page.body?.storage?.value ?? page.body?.value ?? "";
|
|
57328
57511
|
const newFullBody = replaceSection(fullBody, section, body);
|
|
@@ -57659,8 +57842,8 @@ ${truncated}`);
|
|
|
57659
57842
|
const blocked = writeGuard("add_attachment", config3);
|
|
57660
57843
|
if (blocked) return blocked;
|
|
57661
57844
|
try {
|
|
57662
|
-
const resolved = await (0,
|
|
57663
|
-
const cwd = await (0,
|
|
57845
|
+
const resolved = await (0, import_promises3.realpath)((0, import_node_path4.resolve)(file_path));
|
|
57846
|
+
const cwd = await (0, import_promises3.realpath)(process.cwd());
|
|
57664
57847
|
if (!resolved.startsWith(cwd + "/") && resolved !== cwd) {
|
|
57665
57848
|
return toolError(
|
|
57666
57849
|
new Error(
|
|
@@ -57668,7 +57851,7 @@ ${truncated}`);
|
|
|
57668
57851
|
)
|
|
57669
57852
|
);
|
|
57670
57853
|
}
|
|
57671
|
-
const fileData = await (0,
|
|
57854
|
+
const fileData = await (0, import_promises3.readFile)(resolved);
|
|
57672
57855
|
const name = filename ?? resolved.split("/").pop() ?? "attachment";
|
|
57673
57856
|
const att = await uploadAttachment(page_id, fileData, name, comment2);
|
|
57674
57857
|
return toolResult(
|
|
@@ -57708,14 +57891,14 @@ ${truncated}`);
|
|
|
57708
57891
|
if (blocked) return blocked;
|
|
57709
57892
|
try {
|
|
57710
57893
|
const filename = diagram_name.endsWith(".drawio") ? diagram_name : `${diagram_name}.drawio`;
|
|
57711
|
-
const tmpDir = await (0,
|
|
57894
|
+
const tmpDir = await (0, import_promises3.mkdtemp)((0, import_node_path4.join)((0, import_node_os3.tmpdir)(), "drawio-"));
|
|
57712
57895
|
try {
|
|
57713
|
-
const tmpPath = (0,
|
|
57714
|
-
await (0,
|
|
57715
|
-
const fileData = await (0,
|
|
57896
|
+
const tmpPath = (0, import_node_path4.join)(tmpDir, filename);
|
|
57897
|
+
await (0, import_promises3.writeFile)(tmpPath, diagram_xml, "utf-8");
|
|
57898
|
+
const fileData = await (0, import_promises3.readFile)(tmpPath);
|
|
57716
57899
|
await uploadAttachment(page_id, fileData, filename);
|
|
57717
57900
|
} finally {
|
|
57718
|
-
await (0,
|
|
57901
|
+
await (0, import_promises3.rm)(tmpDir, { recursive: true, force: true });
|
|
57719
57902
|
}
|
|
57720
57903
|
const macroId = crypto.randomUUID();
|
|
57721
57904
|
const localId = crypto.randomUUID();
|
|
@@ -58350,27 +58533,75 @@ ${lines.join("\n")}${echo2}`
|
|
|
58350
58533
|
server.registerTool(
|
|
58351
58534
|
"get_version",
|
|
58352
58535
|
{
|
|
58353
|
-
description: "Return the epimethian-mcp server version.",
|
|
58536
|
+
description: "Return the epimethian-mcp server version. Also reports available updates, if any.",
|
|
58354
58537
|
inputSchema: {}
|
|
58355
58538
|
},
|
|
58356
|
-
async () =>
|
|
58539
|
+
async () => {
|
|
58540
|
+
let text2 = `epimethian-mcp v${"5.3.0"}`;
|
|
58541
|
+
try {
|
|
58542
|
+
const pending = await getPendingUpdate();
|
|
58543
|
+
if (pending) {
|
|
58544
|
+
if (pending.autoInstalled) {
|
|
58545
|
+
text2 += `
|
|
58546
|
+
|
|
58547
|
+
Bugfix v${pending.latest} has been installed automatically. Restart the MCP server to apply.`;
|
|
58548
|
+
} else {
|
|
58549
|
+
text2 += `
|
|
58550
|
+
|
|
58551
|
+
${pending.type === "major" ? "Major" : "Minor"} update available: v${pending.current} \u2192 v${pending.latest}. Call the upgrade tool to install.`;
|
|
58552
|
+
}
|
|
58553
|
+
}
|
|
58554
|
+
} catch {
|
|
58555
|
+
}
|
|
58556
|
+
return toolResult(text2);
|
|
58557
|
+
}
|
|
58558
|
+
);
|
|
58559
|
+
server.registerTool(
|
|
58560
|
+
"upgrade",
|
|
58561
|
+
{
|
|
58562
|
+
description: "Upgrade epimethian-mcp to the latest available version. After a successful upgrade the user must restart the MCP server (reload the VS Code window or restart Claude).",
|
|
58563
|
+
inputSchema: {}
|
|
58564
|
+
},
|
|
58565
|
+
async () => {
|
|
58566
|
+
try {
|
|
58567
|
+
const pending = await getPendingUpdate();
|
|
58568
|
+
if (!pending) {
|
|
58569
|
+
return toolResult(
|
|
58570
|
+
`epimethian-mcp v${"5.3.0"} is already up to date.`
|
|
58571
|
+
);
|
|
58572
|
+
}
|
|
58573
|
+
const output = await performUpgrade(pending.latest);
|
|
58574
|
+
await clearPendingUpdate();
|
|
58575
|
+
return toolResult(
|
|
58576
|
+
`Upgraded epimethian-mcp from v${pending.current} to v${pending.latest}.
|
|
58577
|
+
|
|
58578
|
+
\u26A0 Restart required: reload the VS Code window (or restart Claude) so the new version takes effect.
|
|
58579
|
+
|
|
58580
|
+
` + output
|
|
58581
|
+
);
|
|
58582
|
+
} catch (err) {
|
|
58583
|
+
return toolError(err);
|
|
58584
|
+
}
|
|
58585
|
+
}
|
|
58357
58586
|
);
|
|
58358
58587
|
}
|
|
58359
58588
|
async function main() {
|
|
58360
58589
|
const config3 = await getConfig();
|
|
58361
58590
|
await validateStartup(config3);
|
|
58362
58591
|
if (process.env.EPIMETHIAN_MUTATION_LOG === "true") {
|
|
58363
|
-
const logDir = (0,
|
|
58592
|
+
const logDir = (0, import_node_path4.join)((0, import_node_os3.homedir)(), ".epimethian", "logs");
|
|
58364
58593
|
initMutationLog(logDir);
|
|
58365
58594
|
}
|
|
58366
58595
|
const serverName = config3.profile ? `confluence-${config3.profile}` : "confluence";
|
|
58367
58596
|
const server = new McpServer({
|
|
58368
58597
|
name: serverName,
|
|
58369
|
-
version: "5.
|
|
58598
|
+
version: "5.3.0"
|
|
58370
58599
|
});
|
|
58371
58600
|
registerTools(server, config3);
|
|
58372
58601
|
const transport = new StdioServerTransport();
|
|
58373
58602
|
await server.connect(transport);
|
|
58603
|
+
checkForUpdates("5.3.0").catch(() => {
|
|
58604
|
+
});
|
|
58374
58605
|
}
|
|
58375
58606
|
|
|
58376
58607
|
// src/cli/index.ts
|