@de-otio/epimethian-mcp 2.0.1 → 3.0.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 +824 -383
- package/dist/cli/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
#!/usr/bin/env node
|
|
3
2
|
"use strict";
|
|
4
3
|
var __create = Object.create;
|
|
5
4
|
var __defProp = Object.defineProperty;
|
|
@@ -6804,20 +6803,28 @@ var require_dist = __commonJS({
|
|
|
6804
6803
|
});
|
|
6805
6804
|
|
|
6806
6805
|
// src/shared/keychain.ts
|
|
6806
|
+
function accountForProfile(profile) {
|
|
6807
|
+
if (!PROFILE_NAME_RE.test(profile)) {
|
|
6808
|
+
throw new Error(
|
|
6809
|
+
`Invalid profile name: "${profile}". Use lowercase alphanumeric and hyphens only (1-63 chars).`
|
|
6810
|
+
);
|
|
6811
|
+
}
|
|
6812
|
+
return `${LEGACY_ACCOUNT}/${profile}`;
|
|
6813
|
+
}
|
|
6807
6814
|
function exec(cmd, args) {
|
|
6808
6815
|
return new Promise((resolve2, reject) => {
|
|
6809
|
-
(0, import_node_child_process.execFile)(cmd, args, { timeout: 5e3 }, (err,
|
|
6816
|
+
(0, import_node_child_process.execFile)(cmd, args, { timeout: 5e3 }, (err, stdout3, stderr) => {
|
|
6810
6817
|
if (err) {
|
|
6811
6818
|
reject(new Error(stderr || err.message));
|
|
6812
6819
|
} else {
|
|
6813
|
-
resolve2(
|
|
6820
|
+
resolve2(stdout3);
|
|
6814
6821
|
}
|
|
6815
6822
|
});
|
|
6816
6823
|
});
|
|
6817
6824
|
}
|
|
6818
|
-
async function writeMacOS(password) {
|
|
6825
|
+
async function writeMacOS(account, password) {
|
|
6819
6826
|
try {
|
|
6820
|
-
await exec("security", ["delete-generic-password", "-s", SERVICE, "-a",
|
|
6827
|
+
await exec("security", ["delete-generic-password", "-s", SERVICE, "-a", account]);
|
|
6821
6828
|
} catch {
|
|
6822
6829
|
}
|
|
6823
6830
|
await exec("security", [
|
|
@@ -6825,23 +6832,26 @@ async function writeMacOS(password) {
|
|
|
6825
6832
|
"-s",
|
|
6826
6833
|
SERVICE,
|
|
6827
6834
|
"-a",
|
|
6828
|
-
|
|
6835
|
+
account,
|
|
6829
6836
|
"-w",
|
|
6830
6837
|
password,
|
|
6831
6838
|
"-U"
|
|
6832
6839
|
]);
|
|
6833
6840
|
}
|
|
6834
|
-
async function readMacOS() {
|
|
6841
|
+
async function readMacOS(account) {
|
|
6835
6842
|
return (await exec("security", [
|
|
6836
6843
|
"find-generic-password",
|
|
6837
6844
|
"-s",
|
|
6838
6845
|
SERVICE,
|
|
6839
6846
|
"-a",
|
|
6840
|
-
|
|
6847
|
+
account,
|
|
6841
6848
|
"-w"
|
|
6842
6849
|
])).trim();
|
|
6843
6850
|
}
|
|
6844
|
-
async function
|
|
6851
|
+
async function deleteMacOS(account) {
|
|
6852
|
+
await exec("security", ["delete-generic-password", "-s", SERVICE, "-a", account]);
|
|
6853
|
+
}
|
|
6854
|
+
async function writeLinux(account, password) {
|
|
6845
6855
|
return new Promise((resolve2, reject) => {
|
|
6846
6856
|
const proc = (0, import_node_child_process.spawn)("secret-tool", [
|
|
6847
6857
|
"store",
|
|
@@ -6850,7 +6860,7 @@ async function writeLinux(password) {
|
|
|
6850
6860
|
"service",
|
|
6851
6861
|
SERVICE,
|
|
6852
6862
|
"account",
|
|
6853
|
-
|
|
6863
|
+
account
|
|
6854
6864
|
]);
|
|
6855
6865
|
proc.stdin.write(password);
|
|
6856
6866
|
proc.stdin.end();
|
|
@@ -6861,55 +6871,122 @@ async function writeLinux(password) {
|
|
|
6861
6871
|
proc.on("error", reject);
|
|
6862
6872
|
});
|
|
6863
6873
|
}
|
|
6864
|
-
async function readLinux() {
|
|
6874
|
+
async function readLinux(account) {
|
|
6865
6875
|
return (await exec("secret-tool", [
|
|
6866
6876
|
"lookup",
|
|
6867
6877
|
"service",
|
|
6868
6878
|
SERVICE,
|
|
6869
6879
|
"account",
|
|
6870
|
-
|
|
6880
|
+
account
|
|
6871
6881
|
])).trim();
|
|
6872
6882
|
}
|
|
6873
|
-
async function
|
|
6883
|
+
async function deleteLinux(account) {
|
|
6884
|
+
await exec("secret-tool", [
|
|
6885
|
+
"clear",
|
|
6886
|
+
"service",
|
|
6887
|
+
SERVICE,
|
|
6888
|
+
"account",
|
|
6889
|
+
account
|
|
6890
|
+
]);
|
|
6891
|
+
}
|
|
6892
|
+
function resolveAccount(profile) {
|
|
6893
|
+
if (profile !== void 0) {
|
|
6894
|
+
return accountForProfile(profile);
|
|
6895
|
+
}
|
|
6896
|
+
return LEGACY_ACCOUNT;
|
|
6897
|
+
}
|
|
6898
|
+
async function saveToKeychain(creds, profile) {
|
|
6899
|
+
const account = resolveAccount(profile);
|
|
6874
6900
|
const json = JSON.stringify(creds);
|
|
6875
6901
|
if (process.platform === "darwin") {
|
|
6876
|
-
await writeMacOS(json);
|
|
6902
|
+
await writeMacOS(account, json);
|
|
6877
6903
|
} else if (process.platform === "linux") {
|
|
6878
|
-
await writeLinux(json);
|
|
6904
|
+
await writeLinux(account, json);
|
|
6879
6905
|
} else {
|
|
6880
6906
|
throw new Error(`Keychain not supported on ${process.platform}`);
|
|
6881
6907
|
}
|
|
6882
6908
|
}
|
|
6883
|
-
async function readFromKeychain() {
|
|
6909
|
+
async function readFromKeychain(profile) {
|
|
6910
|
+
const account = resolveAccount(profile);
|
|
6911
|
+
let raw;
|
|
6884
6912
|
try {
|
|
6885
|
-
let raw;
|
|
6886
6913
|
if (process.platform === "darwin") {
|
|
6887
|
-
raw = await readMacOS();
|
|
6914
|
+
raw = await readMacOS(account);
|
|
6888
6915
|
} else if (process.platform === "linux") {
|
|
6889
|
-
raw = await readLinux();
|
|
6916
|
+
raw = await readLinux(account);
|
|
6890
6917
|
} else {
|
|
6891
6918
|
return null;
|
|
6892
6919
|
}
|
|
6893
|
-
const parsed = JSON.parse(raw);
|
|
6894
|
-
if (parsed && typeof parsed.apiToken === "string") {
|
|
6895
|
-
return parsed;
|
|
6896
|
-
}
|
|
6897
|
-
return null;
|
|
6898
6920
|
} catch {
|
|
6899
6921
|
return null;
|
|
6900
6922
|
}
|
|
6923
|
+
let parsed;
|
|
6924
|
+
try {
|
|
6925
|
+
parsed = JSON.parse(raw);
|
|
6926
|
+
} catch {
|
|
6927
|
+
const label = profile ? `profile "${profile}"` : "legacy keychain entry";
|
|
6928
|
+
throw new Error(`Corrupted keychain entry for ${label}: invalid JSON.`);
|
|
6929
|
+
}
|
|
6930
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.apiToken !== "string" || typeof parsed.url !== "string" || typeof parsed.email !== "string") {
|
|
6931
|
+
const label = profile ? `profile "${profile}"` : "legacy keychain entry";
|
|
6932
|
+
throw new Error(
|
|
6933
|
+
`Corrupted keychain entry for ${label}: missing required fields (url, email, apiToken).`
|
|
6934
|
+
);
|
|
6935
|
+
}
|
|
6936
|
+
return parsed;
|
|
6937
|
+
}
|
|
6938
|
+
async function deleteFromKeychain(profile) {
|
|
6939
|
+
const account = resolveAccount(profile);
|
|
6940
|
+
try {
|
|
6941
|
+
if (process.platform === "darwin") {
|
|
6942
|
+
await deleteMacOS(account);
|
|
6943
|
+
} else if (process.platform === "linux") {
|
|
6944
|
+
await deleteLinux(account);
|
|
6945
|
+
}
|
|
6946
|
+
} catch {
|
|
6947
|
+
}
|
|
6901
6948
|
}
|
|
6902
|
-
var import_node_child_process, SERVICE,
|
|
6949
|
+
var import_node_child_process, SERVICE, LEGACY_ACCOUNT, PROFILE_NAME_RE;
|
|
6903
6950
|
var init_keychain = __esm({
|
|
6904
6951
|
"src/shared/keychain.ts"() {
|
|
6905
6952
|
"use strict";
|
|
6906
6953
|
import_node_child_process = require("node:child_process");
|
|
6907
6954
|
SERVICE = "epimethian-mcp";
|
|
6908
|
-
|
|
6955
|
+
LEGACY_ACCOUNT = "confluence-credentials";
|
|
6956
|
+
PROFILE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
6909
6957
|
}
|
|
6910
6958
|
});
|
|
6911
6959
|
|
|
6912
6960
|
// src/shared/test-connection.ts
|
|
6961
|
+
async function verifyTenantIdentity(url, email2, apiToken) {
|
|
6962
|
+
const endpoint = `${url.replace(/\/+$/, "")}/wiki/rest/api/user/current`;
|
|
6963
|
+
const auth = Buffer.from(`${email2}:${apiToken}`).toString("base64");
|
|
6964
|
+
try {
|
|
6965
|
+
const response = await fetch(endpoint, {
|
|
6966
|
+
method: "GET",
|
|
6967
|
+
headers: {
|
|
6968
|
+
Authorization: `Basic ${auth}`,
|
|
6969
|
+
Accept: "application/json"
|
|
6970
|
+
}
|
|
6971
|
+
});
|
|
6972
|
+
if (!response.ok) {
|
|
6973
|
+
return { ok: false, message: `HTTP ${response.status}: ${response.statusText}` };
|
|
6974
|
+
}
|
|
6975
|
+
const body = await response.json();
|
|
6976
|
+
const authenticatedEmail = body.email ?? "";
|
|
6977
|
+
if (authenticatedEmail.toLowerCase() !== email2.toLowerCase()) {
|
|
6978
|
+
return {
|
|
6979
|
+
ok: false,
|
|
6980
|
+
authenticatedEmail,
|
|
6981
|
+
message: `Tenant identity mismatch. Expected: ${email2}, authenticated as: ${authenticatedEmail}. This may indicate a DNS or configuration issue.`
|
|
6982
|
+
};
|
|
6983
|
+
}
|
|
6984
|
+
return { ok: true, authenticatedEmail, message: `Verified identity: ${authenticatedEmail}` };
|
|
6985
|
+
} catch (err) {
|
|
6986
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6987
|
+
return { ok: false, message: `Identity verification failed: ${message}` };
|
|
6988
|
+
}
|
|
6989
|
+
}
|
|
6913
6990
|
async function testConnection(url, email2, apiToken) {
|
|
6914
6991
|
const endpoint = `${url.replace(/\/+$/, "")}/wiki/api/v2/spaces?limit=1`;
|
|
6915
6992
|
const auth = Buffer.from(`${email2}:${apiToken}`).toString("base64");
|
|
@@ -6947,6 +7024,76 @@ var init_test_connection = __esm({
|
|
|
6947
7024
|
}
|
|
6948
7025
|
});
|
|
6949
7026
|
|
|
7027
|
+
// src/shared/profiles.ts
|
|
7028
|
+
async function ensureConfigDir() {
|
|
7029
|
+
await (0, import_promises2.mkdir)(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
7030
|
+
}
|
|
7031
|
+
async function readProfileRegistry() {
|
|
7032
|
+
try {
|
|
7033
|
+
const raw = await (0, import_promises2.readFile)(REGISTRY_FILE, "utf-8");
|
|
7034
|
+
const parsed = JSON.parse(raw);
|
|
7035
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.profiles) && parsed.profiles.every((p) => typeof p === "string")) {
|
|
7036
|
+
return parsed.profiles;
|
|
7037
|
+
}
|
|
7038
|
+
console.error(
|
|
7039
|
+
"Warning: Profile registry has unexpected format. Treating as empty."
|
|
7040
|
+
);
|
|
7041
|
+
return [];
|
|
7042
|
+
} catch (err) {
|
|
7043
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
7044
|
+
return [];
|
|
7045
|
+
}
|
|
7046
|
+
console.error("Warning: Could not read profile registry. Treating as empty.");
|
|
7047
|
+
return [];
|
|
7048
|
+
}
|
|
7049
|
+
}
|
|
7050
|
+
async function addToProfileRegistry(name) {
|
|
7051
|
+
await ensureConfigDir();
|
|
7052
|
+
const profiles = await readProfileRegistry();
|
|
7053
|
+
if (profiles.includes(name)) return;
|
|
7054
|
+
profiles.push(name);
|
|
7055
|
+
const data = JSON.stringify({ profiles }, null, 2) + "\n";
|
|
7056
|
+
const tmpFile = (0, import_node_path2.join)(
|
|
7057
|
+
CONFIG_DIR,
|
|
7058
|
+
`.profiles.${(0, import_node_crypto.randomBytes)(4).toString("hex")}.tmp`
|
|
7059
|
+
);
|
|
7060
|
+
await (0, import_promises2.writeFile)(tmpFile, data, { mode: 384 });
|
|
7061
|
+
await (0, import_promises2.rename)(tmpFile, REGISTRY_FILE);
|
|
7062
|
+
}
|
|
7063
|
+
async function removeFromProfileRegistry(name) {
|
|
7064
|
+
const profiles = await readProfileRegistry();
|
|
7065
|
+
const filtered = profiles.filter((p) => p !== name);
|
|
7066
|
+
if (filtered.length === profiles.length) return;
|
|
7067
|
+
await ensureConfigDir();
|
|
7068
|
+
const data = JSON.stringify({ profiles: filtered }, null, 2) + "\n";
|
|
7069
|
+
const tmpFile = (0, import_node_path2.join)(
|
|
7070
|
+
CONFIG_DIR,
|
|
7071
|
+
`.profiles.${(0, import_node_crypto.randomBytes)(4).toString("hex")}.tmp`
|
|
7072
|
+
);
|
|
7073
|
+
await (0, import_promises2.writeFile)(tmpFile, data, { mode: 384 });
|
|
7074
|
+
await (0, import_promises2.rename)(tmpFile, REGISTRY_FILE);
|
|
7075
|
+
}
|
|
7076
|
+
async function appendAuditLog(message) {
|
|
7077
|
+
await ensureConfigDir();
|
|
7078
|
+
const entry = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
7079
|
+
`;
|
|
7080
|
+
const { appendFile } = await import("node:fs/promises");
|
|
7081
|
+
await appendFile(AUDIT_LOG, entry, { mode: 384 });
|
|
7082
|
+
}
|
|
7083
|
+
var import_promises2, import_node_path2, import_node_os2, import_node_crypto, CONFIG_DIR, REGISTRY_FILE, AUDIT_LOG;
|
|
7084
|
+
var init_profiles = __esm({
|
|
7085
|
+
"src/shared/profiles.ts"() {
|
|
7086
|
+
"use strict";
|
|
7087
|
+
import_promises2 = require("node:fs/promises");
|
|
7088
|
+
import_node_path2 = require("node:path");
|
|
7089
|
+
import_node_os2 = require("node:os");
|
|
7090
|
+
import_node_crypto = require("node:crypto");
|
|
7091
|
+
CONFIG_DIR = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".config", "epimethian-mcp");
|
|
7092
|
+
REGISTRY_FILE = (0, import_node_path2.join)(CONFIG_DIR, "profiles.json");
|
|
7093
|
+
AUDIT_LOG = (0, import_node_path2.join)(CONFIG_DIR, "audit.log");
|
|
7094
|
+
}
|
|
7095
|
+
});
|
|
7096
|
+
|
|
6950
7097
|
// src/cli/setup.ts
|
|
6951
7098
|
var setup_exports = {};
|
|
6952
7099
|
__export(setup_exports, {
|
|
@@ -6982,15 +7129,22 @@ function readPassword(prompt) {
|
|
|
6982
7129
|
import_node_process2.stdin.on("data", onData);
|
|
6983
7130
|
});
|
|
6984
7131
|
}
|
|
6985
|
-
async function runSetup() {
|
|
7132
|
+
async function runSetup(profile) {
|
|
6986
7133
|
if (!import_node_process2.stdin.isTTY) {
|
|
6987
7134
|
console.error(
|
|
6988
7135
|
"Error: setup requires an interactive terminal.\nFor non-interactive environments, set CONFLUENCE_URL, CONFLUENCE_EMAIL, and CONFLUENCE_API_TOKEN as environment variables."
|
|
6989
7136
|
);
|
|
6990
7137
|
process.exit(1);
|
|
6991
7138
|
}
|
|
6992
|
-
|
|
6993
|
-
|
|
7139
|
+
if (profile !== void 0 && !PROFILE_NAME_RE.test(profile)) {
|
|
7140
|
+
console.error(
|
|
7141
|
+
`Error: Invalid profile name "${profile}". Use lowercase alphanumeric and hyphens only (1-63 chars).`
|
|
7142
|
+
);
|
|
7143
|
+
process.exit(1);
|
|
7144
|
+
}
|
|
7145
|
+
const banner = profile ? `Epimethian MCP - Credential setup for profile "${profile}"` : "Epimethian MCP - Confluence credential setup";
|
|
7146
|
+
console.log(banner + "\n");
|
|
7147
|
+
const existing = await readFromKeychain(profile);
|
|
6994
7148
|
const rl = readline.createInterface({ input: import_node_process2.stdin, output: import_node_process2.stdout });
|
|
6995
7149
|
try {
|
|
6996
7150
|
const defaultUrl = existing?.url ?? "";
|
|
@@ -7006,6 +7160,21 @@ async function runSetup() {
|
|
|
7006
7160
|
console.error("Error: URL must start with https://");
|
|
7007
7161
|
process.exit(1);
|
|
7008
7162
|
}
|
|
7163
|
+
try {
|
|
7164
|
+
const parsed = new URL(url);
|
|
7165
|
+
if (parsed.username || parsed.password || /[\n\r]/.test(url)) {
|
|
7166
|
+
console.error("Error: URL contains invalid characters.");
|
|
7167
|
+
process.exit(1);
|
|
7168
|
+
}
|
|
7169
|
+
if (!parsed.hostname.endsWith(".atlassian.net")) {
|
|
7170
|
+
console.error(
|
|
7171
|
+
`Warning: URL does not match *.atlassian.net. Ensure this is the correct Confluence instance.`
|
|
7172
|
+
);
|
|
7173
|
+
}
|
|
7174
|
+
} catch {
|
|
7175
|
+
console.error("Error: Invalid URL format.");
|
|
7176
|
+
process.exit(1);
|
|
7177
|
+
}
|
|
7009
7178
|
const defaultEmail = existing?.email ?? "";
|
|
7010
7179
|
const emailPrompt = defaultEmail ? `Email [${defaultEmail}]: ` : "Email: ";
|
|
7011
7180
|
let email2 = (await rl.question(emailPrompt)).trim();
|
|
@@ -7030,8 +7199,17 @@ async function runSetup() {
|
|
|
7030
7199
|
}
|
|
7031
7200
|
console.log(`${result.message}
|
|
7032
7201
|
`);
|
|
7033
|
-
await saveToKeychain({ url, email: email2, apiToken });
|
|
7034
|
-
|
|
7202
|
+
await saveToKeychain({ url, email: email2, apiToken }, profile);
|
|
7203
|
+
if (profile) {
|
|
7204
|
+
await addToProfileRegistry(profile);
|
|
7205
|
+
console.log(`Credentials saved to OS keychain (profile: ${profile}).
|
|
7206
|
+
`);
|
|
7207
|
+
} else {
|
|
7208
|
+
console.log("Credentials saved to OS keychain.\n");
|
|
7209
|
+
console.log(
|
|
7210
|
+
"Tip: Use --profile <name> for multi-tenant support.\n"
|
|
7211
|
+
);
|
|
7212
|
+
}
|
|
7035
7213
|
console.log(`Available tools (${TOOLS.length}):`);
|
|
7036
7214
|
console.log(` ${TOOLS.join(", ")}`);
|
|
7037
7215
|
console.log(
|
|
@@ -7049,6 +7227,7 @@ var init_setup = __esm({
|
|
|
7049
7227
|
import_node_process2 = require("node:process");
|
|
7050
7228
|
init_test_connection();
|
|
7051
7229
|
init_keychain();
|
|
7230
|
+
init_profiles();
|
|
7052
7231
|
TOOLS = [
|
|
7053
7232
|
"create_page",
|
|
7054
7233
|
"get_page",
|
|
@@ -7066,6 +7245,164 @@ var init_setup = __esm({
|
|
|
7066
7245
|
}
|
|
7067
7246
|
});
|
|
7068
7247
|
|
|
7248
|
+
// src/cli/profiles.ts
|
|
7249
|
+
var profiles_exports = {};
|
|
7250
|
+
__export(profiles_exports, {
|
|
7251
|
+
runProfiles: () => runProfiles
|
|
7252
|
+
});
|
|
7253
|
+
async function runProfiles() {
|
|
7254
|
+
const args = process.argv.slice(3);
|
|
7255
|
+
const removeIdx = args.indexOf("--remove");
|
|
7256
|
+
if (removeIdx > -1) {
|
|
7257
|
+
const name = args[removeIdx + 1];
|
|
7258
|
+
if (!name || !PROFILE_NAME_RE.test(name)) {
|
|
7259
|
+
console.error(
|
|
7260
|
+
"Error: --remove requires a valid profile name."
|
|
7261
|
+
);
|
|
7262
|
+
process.exit(1);
|
|
7263
|
+
}
|
|
7264
|
+
await removeProfile(name, args.includes("--force"));
|
|
7265
|
+
return;
|
|
7266
|
+
}
|
|
7267
|
+
const verbose = args.includes("--verbose");
|
|
7268
|
+
const profiles = await readProfileRegistry();
|
|
7269
|
+
if (profiles.length === 0) {
|
|
7270
|
+
console.log("No profiles configured. Run `epimethian-mcp setup --profile <name>` to create one.");
|
|
7271
|
+
return;
|
|
7272
|
+
}
|
|
7273
|
+
if (verbose) {
|
|
7274
|
+
console.log(
|
|
7275
|
+
` ${"Profile".padEnd(20)} ${"URL".padEnd(40)} Email`
|
|
7276
|
+
);
|
|
7277
|
+
console.log(
|
|
7278
|
+
` ${"\u2500".repeat(20)} ${"\u2500".repeat(40)} ${"\u2500".repeat(30)}`
|
|
7279
|
+
);
|
|
7280
|
+
for (const name of profiles) {
|
|
7281
|
+
try {
|
|
7282
|
+
const creds = await readFromKeychain(name);
|
|
7283
|
+
if (creds) {
|
|
7284
|
+
console.log(
|
|
7285
|
+
` ${name.padEnd(20)} ${creds.url.padEnd(40)} ${creds.email}`
|
|
7286
|
+
);
|
|
7287
|
+
} else {
|
|
7288
|
+
console.log(
|
|
7289
|
+
` ${name.padEnd(20)} (credentials missing)`
|
|
7290
|
+
);
|
|
7291
|
+
}
|
|
7292
|
+
} catch {
|
|
7293
|
+
console.log(
|
|
7294
|
+
` ${name.padEnd(20)} (credentials corrupted)`
|
|
7295
|
+
);
|
|
7296
|
+
}
|
|
7297
|
+
}
|
|
7298
|
+
} else {
|
|
7299
|
+
console.log("Configured profiles:");
|
|
7300
|
+
for (const name of profiles) {
|
|
7301
|
+
console.log(` ${name}`);
|
|
7302
|
+
}
|
|
7303
|
+
console.log("\nUse --verbose to show URLs and emails.");
|
|
7304
|
+
}
|
|
7305
|
+
}
|
|
7306
|
+
async function removeProfile(name, force) {
|
|
7307
|
+
if (!force || import_node_process3.stdin.isTTY) {
|
|
7308
|
+
if (!import_node_process3.stdin.isTTY) {
|
|
7309
|
+
console.error(
|
|
7310
|
+
"Error: Removing a profile requires an interactive terminal or --force in non-TTY mode."
|
|
7311
|
+
);
|
|
7312
|
+
process.exit(1);
|
|
7313
|
+
}
|
|
7314
|
+
const rl = readline2.createInterface({ input: import_node_process3.stdin, output: import_node_process3.stdout });
|
|
7315
|
+
try {
|
|
7316
|
+
const answer = await rl.question(
|
|
7317
|
+
`Remove profile "${name}" and delete its credentials? [y/N] `
|
|
7318
|
+
);
|
|
7319
|
+
if (answer.trim().toLowerCase() !== "y") {
|
|
7320
|
+
console.log("Cancelled.");
|
|
7321
|
+
return;
|
|
7322
|
+
}
|
|
7323
|
+
} finally {
|
|
7324
|
+
rl.close();
|
|
7325
|
+
}
|
|
7326
|
+
}
|
|
7327
|
+
await deleteFromKeychain(name);
|
|
7328
|
+
await removeFromProfileRegistry(name);
|
|
7329
|
+
await appendAuditLog(`Removed profile "${name}"`);
|
|
7330
|
+
console.log(`Profile "${name}" removed.`);
|
|
7331
|
+
}
|
|
7332
|
+
var import_node_process3, readline2;
|
|
7333
|
+
var init_profiles2 = __esm({
|
|
7334
|
+
"src/cli/profiles.ts"() {
|
|
7335
|
+
"use strict";
|
|
7336
|
+
import_node_process3 = require("node:process");
|
|
7337
|
+
readline2 = __toESM(require("node:readline/promises"));
|
|
7338
|
+
init_keychain();
|
|
7339
|
+
init_profiles();
|
|
7340
|
+
}
|
|
7341
|
+
});
|
|
7342
|
+
|
|
7343
|
+
// src/cli/status.ts
|
|
7344
|
+
var status_exports = {};
|
|
7345
|
+
__export(status_exports, {
|
|
7346
|
+
runStatus: () => runStatus
|
|
7347
|
+
});
|
|
7348
|
+
async function runStatus() {
|
|
7349
|
+
const profile = process.env.CONFLUENCE_PROFILE || "";
|
|
7350
|
+
const urlEnv = process.env.CONFLUENCE_URL || "";
|
|
7351
|
+
const emailEnv = process.env.CONFLUENCE_EMAIL || "";
|
|
7352
|
+
const tokenEnv = process.env.CONFLUENCE_API_TOKEN || "";
|
|
7353
|
+
let url;
|
|
7354
|
+
let email2;
|
|
7355
|
+
let apiToken;
|
|
7356
|
+
let mode;
|
|
7357
|
+
if (profile) {
|
|
7358
|
+
if (!PROFILE_NAME_RE.test(profile)) {
|
|
7359
|
+
console.error(`Invalid CONFLUENCE_PROFILE: "${profile}".`);
|
|
7360
|
+
process.exit(1);
|
|
7361
|
+
}
|
|
7362
|
+
const creds = await readFromKeychain(profile);
|
|
7363
|
+
if (!creds) {
|
|
7364
|
+
console.error(
|
|
7365
|
+
`No credentials found for profile "${profile}". Run \`epimethian-mcp setup --profile ${profile}\` to configure.`
|
|
7366
|
+
);
|
|
7367
|
+
process.exit(1);
|
|
7368
|
+
}
|
|
7369
|
+
url = creds.url;
|
|
7370
|
+
email2 = creds.email;
|
|
7371
|
+
apiToken = creds.apiToken;
|
|
7372
|
+
mode = `profile: ${profile}`;
|
|
7373
|
+
} else if (urlEnv && emailEnv && tokenEnv) {
|
|
7374
|
+
url = urlEnv;
|
|
7375
|
+
email2 = emailEnv;
|
|
7376
|
+
apiToken = tokenEnv;
|
|
7377
|
+
mode = "env-var mode";
|
|
7378
|
+
} else {
|
|
7379
|
+
const legacy = await readFromKeychain();
|
|
7380
|
+
if (!legacy) {
|
|
7381
|
+
console.error(
|
|
7382
|
+
"No credentials configured. Run `epimethian-mcp setup --profile <name>` to get started."
|
|
7383
|
+
);
|
|
7384
|
+
process.exit(1);
|
|
7385
|
+
}
|
|
7386
|
+
url = legacy.url;
|
|
7387
|
+
email2 = legacy.email;
|
|
7388
|
+
apiToken = legacy.apiToken;
|
|
7389
|
+
mode = "legacy keychain (no profile)";
|
|
7390
|
+
}
|
|
7391
|
+
console.log(`Profile: ${mode}`);
|
|
7392
|
+
console.log(`URL: ${url}`);
|
|
7393
|
+
console.log(`Email: ${email2}`);
|
|
7394
|
+
console.log("Testing connection...");
|
|
7395
|
+
const result = await testConnection(url, email2, apiToken);
|
|
7396
|
+
console.log(`Status: ${result.ok ? "Connected" : "Failed"} - ${result.message}`);
|
|
7397
|
+
}
|
|
7398
|
+
var init_status = __esm({
|
|
7399
|
+
"src/cli/status.ts"() {
|
|
7400
|
+
"use strict";
|
|
7401
|
+
init_test_connection();
|
|
7402
|
+
init_keychain();
|
|
7403
|
+
}
|
|
7404
|
+
});
|
|
7405
|
+
|
|
7069
7406
|
// node_modules/zod/v3/external.js
|
|
7070
7407
|
var external_exports = {};
|
|
7071
7408
|
__export(external_exports, {
|
|
@@ -21286,29 +21623,64 @@ var import_node_path = require("node:path");
|
|
|
21286
21623
|
|
|
21287
21624
|
// src/server/confluence-client.ts
|
|
21288
21625
|
init_keychain();
|
|
21626
|
+
init_test_connection();
|
|
21289
21627
|
var _config = null;
|
|
21290
|
-
async function
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
if (
|
|
21296
|
-
|
|
21297
|
-
|
|
21298
|
-
|
|
21299
|
-
|
|
21300
|
-
|
|
21301
|
-
}
|
|
21302
|
-
|
|
21303
|
-
|
|
21628
|
+
async function resolveCredentials() {
|
|
21629
|
+
const profileEnv = process.env.CONFLUENCE_PROFILE || "";
|
|
21630
|
+
const urlEnv = process.env.CONFLUENCE_URL?.replace(/\/$/, "") || "";
|
|
21631
|
+
const emailEnv = process.env.CONFLUENCE_EMAIL || "";
|
|
21632
|
+
const tokenEnv = process.env.CONFLUENCE_API_TOKEN || "";
|
|
21633
|
+
if (profileEnv) {
|
|
21634
|
+
if (!PROFILE_NAME_RE.test(profileEnv)) {
|
|
21635
|
+
console.error(
|
|
21636
|
+
`Invalid CONFLUENCE_PROFILE: "${profileEnv}". Use lowercase alphanumeric and hyphens only (1-63 chars).`
|
|
21637
|
+
);
|
|
21638
|
+
process.exit(1);
|
|
21639
|
+
}
|
|
21640
|
+
const creds = await readFromKeychain(profileEnv);
|
|
21641
|
+
if (!creds) {
|
|
21642
|
+
console.error(
|
|
21643
|
+
`No credentials found for profile "${profileEnv}". Run \`epimethian-mcp setup --profile ${profileEnv}\` to configure.`
|
|
21644
|
+
);
|
|
21645
|
+
process.exit(1);
|
|
21646
|
+
}
|
|
21647
|
+
return {
|
|
21648
|
+
url: creds.url.replace(/\/$/, ""),
|
|
21649
|
+
email: creds.email,
|
|
21650
|
+
apiToken: creds.apiToken,
|
|
21651
|
+
profile: profileEnv
|
|
21652
|
+
};
|
|
21653
|
+
}
|
|
21654
|
+
if (urlEnv && emailEnv && tokenEnv) {
|
|
21655
|
+
delete process.env.CONFLUENCE_API_TOKEN;
|
|
21656
|
+
return { url: urlEnv, email: emailEnv, apiToken: tokenEnv, profile: null };
|
|
21657
|
+
}
|
|
21658
|
+
const setVars = [
|
|
21659
|
+
urlEnv && "CONFLUENCE_URL",
|
|
21660
|
+
emailEnv && "CONFLUENCE_EMAIL",
|
|
21661
|
+
tokenEnv && "CONFLUENCE_API_TOKEN"
|
|
21662
|
+
].filter(Boolean);
|
|
21663
|
+
if (setVars.length > 0) {
|
|
21304
21664
|
console.error(
|
|
21305
|
-
|
|
21665
|
+
`Error: Partial credentials detected (${setVars.join(", ")} set, but not all three).
|
|
21666
|
+
Either set CONFLUENCE_PROFILE or provide all three environment variables (CONFLUENCE_URL, CONFLUENCE_EMAIL, CONFLUENCE_API_TOKEN).
|
|
21667
|
+
Run \`epimethian-mcp setup --profile <name>\` for guided setup.`
|
|
21306
21668
|
);
|
|
21307
21669
|
process.exit(1);
|
|
21308
21670
|
}
|
|
21671
|
+
console.error(
|
|
21672
|
+
"Missing Confluence credentials. Set CONFLUENCE_PROFILE environment variable, or run `epimethian-mcp setup --profile <name>` to configure."
|
|
21673
|
+
);
|
|
21674
|
+
process.exit(1);
|
|
21675
|
+
}
|
|
21676
|
+
async function getConfig() {
|
|
21677
|
+
if (_config) return _config;
|
|
21678
|
+
const { url, email: email2, apiToken, profile } = await resolveCredentials();
|
|
21309
21679
|
const authHeader = "Basic " + Buffer.from(`${email2}:${apiToken}`).toString("base64");
|
|
21310
|
-
_config = {
|
|
21680
|
+
_config = Object.freeze({
|
|
21311
21681
|
url,
|
|
21682
|
+
email: email2,
|
|
21683
|
+
profile,
|
|
21312
21684
|
apiV2: `${url}/wiki/api/v2`,
|
|
21313
21685
|
apiV1: `${url}/wiki/rest/api`,
|
|
21314
21686
|
authHeader,
|
|
@@ -21316,9 +21688,43 @@ async function getConfig() {
|
|
|
21316
21688
|
Authorization: authHeader,
|
|
21317
21689
|
"Content-Type": "application/json"
|
|
21318
21690
|
}
|
|
21319
|
-
};
|
|
21691
|
+
});
|
|
21320
21692
|
return _config;
|
|
21321
21693
|
}
|
|
21694
|
+
async function validateStartup(config2) {
|
|
21695
|
+
const { url, email: email2, profile } = config2;
|
|
21696
|
+
const decoded = Buffer.from(
|
|
21697
|
+
config2.authHeader.replace("Basic ", ""),
|
|
21698
|
+
"base64"
|
|
21699
|
+
).toString();
|
|
21700
|
+
const colonIndex = decoded.indexOf(":");
|
|
21701
|
+
const apiToken = decoded.slice(colonIndex + 1);
|
|
21702
|
+
const connResult = await testConnection(url, email2, apiToken);
|
|
21703
|
+
if (!connResult.ok) {
|
|
21704
|
+
const profileHint = profile ? `Run \`epimethian-mcp setup --profile ${profile}\` to update credentials.` : "Check your CONFLUENCE_URL, CONFLUENCE_EMAIL, and CONFLUENCE_API_TOKEN.";
|
|
21705
|
+
console.error(
|
|
21706
|
+
`Error: Confluence credentials rejected by ${url}
|
|
21707
|
+
${connResult.message}
|
|
21708
|
+
${profileHint}`
|
|
21709
|
+
);
|
|
21710
|
+
process.exit(1);
|
|
21711
|
+
}
|
|
21712
|
+
const identityResult = await verifyTenantIdentity(url, email2, apiToken);
|
|
21713
|
+
if (!identityResult.ok) {
|
|
21714
|
+
const profileHint = profile ? `Run \`epimethian-mcp setup --profile ${profile}\` to reconfigure.` : "Check your credential configuration.";
|
|
21715
|
+
console.error(
|
|
21716
|
+
`Error: Tenant identity mismatch for ${profile ? `profile "${profile}"` : "configured credentials"}.
|
|
21717
|
+
Expected user: ${email2}
|
|
21718
|
+
` + (identityResult.authenticatedEmail ? `Authenticated as: ${identityResult.authenticatedEmail}
|
|
21719
|
+
` : "") + `This may indicate a DNS or configuration issue. ${profileHint}`
|
|
21720
|
+
);
|
|
21721
|
+
process.exit(1);
|
|
21722
|
+
}
|
|
21723
|
+
const profileLabel = profile ? `profile: ${profile}` : "env-var mode";
|
|
21724
|
+
console.error(
|
|
21725
|
+
`epimethian-mcp: connected to ${url} as ${email2} (${profileLabel})`
|
|
21726
|
+
);
|
|
21727
|
+
}
|
|
21322
21728
|
var PageSchema = external_exports.object({
|
|
21323
21729
|
id: external_exports.string(),
|
|
21324
21730
|
title: external_exports.string(),
|
|
@@ -21367,12 +21773,22 @@ var UploadResultSchema = external_exports.object({
|
|
|
21367
21773
|
})
|
|
21368
21774
|
).default([])
|
|
21369
21775
|
});
|
|
21776
|
+
function sanitizeError(message) {
|
|
21777
|
+
let safe = message.slice(0, 500);
|
|
21778
|
+
safe = safe.replace(/Basic [A-Za-z0-9+/=]{20,}/g, "Basic [REDACTED]");
|
|
21779
|
+
safe = safe.replace(/Authorization:\s*\S+/gi, "Authorization: [REDACTED]");
|
|
21780
|
+
safe = safe.replace(/Bearer [A-Za-z0-9._-]{20,}/g, "Bearer [REDACTED]");
|
|
21781
|
+
return safe;
|
|
21782
|
+
}
|
|
21370
21783
|
async function confluenceRequest(url, options = {}) {
|
|
21371
21784
|
const cfg = await getConfig();
|
|
21372
21785
|
const res = await fetch(url, { headers: cfg.jsonHeaders, ...options });
|
|
21373
21786
|
if (!res.ok) {
|
|
21374
21787
|
const body = await res.text();
|
|
21375
|
-
|
|
21788
|
+
console.error(`Confluence API error (${res.status}): ${body}`);
|
|
21789
|
+
throw new Error(
|
|
21790
|
+
`Confluence API error (${res.status}): ${sanitizeError(body)}`
|
|
21791
|
+
);
|
|
21376
21792
|
}
|
|
21377
21793
|
return res;
|
|
21378
21794
|
}
|
|
@@ -21531,7 +21947,10 @@ async function uploadAttachment(pageId, fileData, filename, comment) {
|
|
|
21531
21947
|
});
|
|
21532
21948
|
if (!res.ok) {
|
|
21533
21949
|
const body = await res.text();
|
|
21534
|
-
|
|
21950
|
+
console.error(`Confluence API error (${res.status}): ${body}`);
|
|
21951
|
+
throw new Error(
|
|
21952
|
+
`Confluence API error (${res.status}): ${sanitizeError(body)}`
|
|
21953
|
+
);
|
|
21535
21954
|
}
|
|
21536
21955
|
const data = UploadResultSchema.parse(await res.json());
|
|
21537
21956
|
const att = data.results[0];
|
|
@@ -21593,360 +22012,374 @@ function toolResult(text) {
|
|
|
21593
22012
|
return { content: [{ type: "text", text }] };
|
|
21594
22013
|
}
|
|
21595
22014
|
function toolError(err) {
|
|
21596
|
-
const
|
|
22015
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
22016
|
+
const message = sanitizeError(raw);
|
|
21597
22017
|
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
21598
22018
|
}
|
|
21599
|
-
|
|
21600
|
-
|
|
21601
|
-
|
|
21602
|
-
|
|
21603
|
-
|
|
21604
|
-
|
|
21605
|
-
|
|
21606
|
-
|
|
21607
|
-
|
|
21608
|
-
|
|
21609
|
-
|
|
21610
|
-
|
|
21611
|
-
|
|
21612
|
-
|
|
21613
|
-
|
|
22019
|
+
function tenantEcho(config2) {
|
|
22020
|
+
const host = new URL(config2.url).hostname;
|
|
22021
|
+
const mode = config2.profile ? `profile: ${config2.profile}` : "env-var mode";
|
|
22022
|
+
return `
|
|
22023
|
+
Tenant: ${host} (${mode})`;
|
|
22024
|
+
}
|
|
22025
|
+
function registerTools(server, config2) {
|
|
22026
|
+
const echo = tenantEcho(config2);
|
|
22027
|
+
server.registerTool(
|
|
22028
|
+
"create_page",
|
|
22029
|
+
{
|
|
22030
|
+
description: "Create a new page in Confluence",
|
|
22031
|
+
inputSchema: {
|
|
22032
|
+
title: external_exports.string().describe("Page title"),
|
|
22033
|
+
space_key: external_exports.string().describe("Confluence space key, e.g. 'DEV' or 'TEAM'"),
|
|
22034
|
+
body: external_exports.string().describe(
|
|
22035
|
+
"Page content \u2013 plain text or Confluence storage format (HTML)"
|
|
22036
|
+
),
|
|
22037
|
+
parent_id: external_exports.string().optional().describe("Optional parent page ID")
|
|
22038
|
+
},
|
|
22039
|
+
annotations: { destructiveHint: false, idempotentHint: false }
|
|
21614
22040
|
},
|
|
21615
|
-
|
|
21616
|
-
|
|
21617
|
-
|
|
21618
|
-
|
|
21619
|
-
|
|
21620
|
-
|
|
21621
|
-
|
|
21622
|
-
|
|
21623
|
-
|
|
21624
|
-
|
|
21625
|
-
|
|
21626
|
-
|
|
21627
|
-
|
|
21628
|
-
|
|
21629
|
-
|
|
21630
|
-
|
|
21631
|
-
|
|
21632
|
-
|
|
21633
|
-
|
|
22041
|
+
async ({ title, space_key, body, parent_id }) => {
|
|
22042
|
+
try {
|
|
22043
|
+
const spaceId = await resolveSpaceId(space_key);
|
|
22044
|
+
const page = await createPage(spaceId, title, body, parent_id);
|
|
22045
|
+
return toolResult(await formatPage(page, false) + echo);
|
|
22046
|
+
} catch (err) {
|
|
22047
|
+
return toolError(err);
|
|
22048
|
+
}
|
|
22049
|
+
}
|
|
22050
|
+
);
|
|
22051
|
+
server.registerTool(
|
|
22052
|
+
"get_page",
|
|
22053
|
+
{
|
|
22054
|
+
description: "Read a Confluence page by ID",
|
|
22055
|
+
inputSchema: {
|
|
22056
|
+
page_id: external_exports.string().describe("The Confluence page ID"),
|
|
22057
|
+
include_body: external_exports.boolean().default(true).describe("Whether to include the page body content")
|
|
22058
|
+
},
|
|
22059
|
+
annotations: { readOnlyHint: true }
|
|
21634
22060
|
},
|
|
21635
|
-
|
|
21636
|
-
|
|
21637
|
-
|
|
21638
|
-
|
|
21639
|
-
|
|
21640
|
-
|
|
21641
|
-
|
|
21642
|
-
|
|
21643
|
-
|
|
21644
|
-
|
|
21645
|
-
|
|
21646
|
-
|
|
21647
|
-
|
|
21648
|
-
|
|
21649
|
-
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
22061
|
+
async ({ page_id, include_body }) => {
|
|
22062
|
+
try {
|
|
22063
|
+
const page = await getPage(page_id, include_body);
|
|
22064
|
+
return toolResult(await formatPage(page, include_body));
|
|
22065
|
+
} catch (err) {
|
|
22066
|
+
return toolError(err);
|
|
22067
|
+
}
|
|
22068
|
+
}
|
|
22069
|
+
);
|
|
22070
|
+
server.registerTool(
|
|
22071
|
+
"update_page",
|
|
22072
|
+
{
|
|
22073
|
+
description: "Update an existing Confluence page. Auto-increments version number.",
|
|
22074
|
+
inputSchema: {
|
|
22075
|
+
page_id: external_exports.string().describe("The Confluence page ID"),
|
|
22076
|
+
title: external_exports.string().optional().describe("New title (omit to keep current)"),
|
|
22077
|
+
body: external_exports.string().optional().describe("New body content in plain text or storage format"),
|
|
22078
|
+
version_message: external_exports.string().optional().describe("Optional version comment")
|
|
22079
|
+
},
|
|
22080
|
+
annotations: { destructiveHint: false, idempotentHint: true }
|
|
21655
22081
|
},
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
|
|
21662
|
-
|
|
21663
|
-
|
|
21664
|
-
|
|
21665
|
-
|
|
21666
|
-
|
|
21667
|
-
|
|
21668
|
-
|
|
21669
|
-
return toolError(err);
|
|
22082
|
+
async ({ page_id, title, body, version_message }) => {
|
|
22083
|
+
try {
|
|
22084
|
+
const { page, newVersion } = await updatePage(page_id, {
|
|
22085
|
+
title,
|
|
22086
|
+
body,
|
|
22087
|
+
versionMessage: version_message
|
|
22088
|
+
});
|
|
22089
|
+
return toolResult(
|
|
22090
|
+
`Updated: ${page.title} (ID: ${page.id}, version: ${newVersion})` + echo
|
|
22091
|
+
);
|
|
22092
|
+
} catch (err) {
|
|
22093
|
+
return toolError(err);
|
|
22094
|
+
}
|
|
21670
22095
|
}
|
|
21671
|
-
|
|
21672
|
-
|
|
21673
|
-
|
|
21674
|
-
|
|
21675
|
-
|
|
21676
|
-
|
|
21677
|
-
|
|
21678
|
-
|
|
22096
|
+
);
|
|
22097
|
+
server.registerTool(
|
|
22098
|
+
"delete_page",
|
|
22099
|
+
{
|
|
22100
|
+
description: "Delete a Confluence page by ID",
|
|
22101
|
+
inputSchema: {
|
|
22102
|
+
page_id: external_exports.string().describe("The Confluence page ID to delete")
|
|
22103
|
+
},
|
|
22104
|
+
annotations: { destructiveHint: true, idempotentHint: true }
|
|
21679
22105
|
},
|
|
21680
|
-
|
|
21681
|
-
|
|
21682
|
-
|
|
21683
|
-
|
|
21684
|
-
|
|
21685
|
-
|
|
21686
|
-
|
|
21687
|
-
|
|
21688
|
-
|
|
21689
|
-
|
|
21690
|
-
|
|
21691
|
-
|
|
21692
|
-
|
|
21693
|
-
|
|
21694
|
-
|
|
21695
|
-
|
|
21696
|
-
|
|
21697
|
-
|
|
21698
|
-
|
|
21699
|
-
|
|
22106
|
+
async ({ page_id }) => {
|
|
22107
|
+
try {
|
|
22108
|
+
await deletePage(page_id);
|
|
22109
|
+
return toolResult(`Deleted page ${page_id}` + echo);
|
|
22110
|
+
} catch (err) {
|
|
22111
|
+
return toolError(err);
|
|
22112
|
+
}
|
|
22113
|
+
}
|
|
22114
|
+
);
|
|
22115
|
+
server.registerTool(
|
|
22116
|
+
"search_pages",
|
|
22117
|
+
{
|
|
22118
|
+
description: "Search Confluence pages using CQL (Confluence Query Language)",
|
|
22119
|
+
inputSchema: {
|
|
22120
|
+
cql: external_exports.string().describe(
|
|
22121
|
+
`CQL query string (e.g., 'space = "DEV" AND title ~ "architecture"')`
|
|
22122
|
+
),
|
|
22123
|
+
limit: external_exports.number().default(25).describe("Maximum results to return (default: 25)")
|
|
22124
|
+
},
|
|
22125
|
+
annotations: { readOnlyHint: true }
|
|
21700
22126
|
},
|
|
21701
|
-
|
|
21702
|
-
|
|
21703
|
-
|
|
21704
|
-
|
|
21705
|
-
|
|
21706
|
-
|
|
21707
|
-
|
|
21708
|
-
|
|
21709
|
-
|
|
21710
|
-
|
|
21711
|
-
|
|
21712
|
-
lines.
|
|
21713
|
-
}
|
|
21714
|
-
|
|
21715
|
-
|
|
21716
|
-
|
|
21717
|
-
|
|
21718
|
-
|
|
21719
|
-
|
|
21720
|
-
|
|
21721
|
-
|
|
21722
|
-
|
|
21723
|
-
|
|
21724
|
-
|
|
21725
|
-
|
|
21726
|
-
|
|
21727
|
-
|
|
22127
|
+
async ({ cql, limit }) => {
|
|
22128
|
+
try {
|
|
22129
|
+
const results = await searchPages(cql, limit);
|
|
22130
|
+
if (results.length === 0) {
|
|
22131
|
+
return toolResult("No pages found matching the query.");
|
|
22132
|
+
}
|
|
22133
|
+
const lines = [`Found ${results.length} page(s):`, ""];
|
|
22134
|
+
for (const p of results) {
|
|
22135
|
+
const spaceKey = p.spaceId ?? p.space?.key ?? "N/A";
|
|
22136
|
+
lines.push(`- ${p.title} (ID: ${p.id}, space: ${spaceKey})`);
|
|
22137
|
+
}
|
|
22138
|
+
return toolResult(lines.join("\n"));
|
|
22139
|
+
} catch (err) {
|
|
22140
|
+
return toolError(err);
|
|
22141
|
+
}
|
|
22142
|
+
}
|
|
22143
|
+
);
|
|
22144
|
+
server.registerTool(
|
|
22145
|
+
"list_pages",
|
|
22146
|
+
{
|
|
22147
|
+
description: "List pages in a Confluence space",
|
|
22148
|
+
inputSchema: {
|
|
22149
|
+
space_key: external_exports.string().describe("Confluence space key (e.g., 'DEV')"),
|
|
22150
|
+
limit: external_exports.number().default(25).describe("Maximum results (default: 25)"),
|
|
22151
|
+
status: external_exports.string().default("current").describe("Page status filter (default: 'current')")
|
|
22152
|
+
},
|
|
22153
|
+
annotations: { readOnlyHint: true }
|
|
21728
22154
|
},
|
|
21729
|
-
|
|
21730
|
-
|
|
21731
|
-
|
|
21732
|
-
|
|
21733
|
-
|
|
21734
|
-
|
|
21735
|
-
|
|
21736
|
-
|
|
21737
|
-
|
|
21738
|
-
|
|
21739
|
-
|
|
21740
|
-
lines.
|
|
21741
|
-
}
|
|
21742
|
-
|
|
21743
|
-
|
|
21744
|
-
|
|
21745
|
-
|
|
21746
|
-
|
|
21747
|
-
|
|
21748
|
-
|
|
21749
|
-
|
|
21750
|
-
|
|
21751
|
-
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
|
|
22155
|
+
async ({ space_key, limit, status }) => {
|
|
22156
|
+
try {
|
|
22157
|
+
const spaceId = await resolveSpaceId(space_key);
|
|
22158
|
+
const pages = await listPages(spaceId, limit, status);
|
|
22159
|
+
if (pages.length === 0) {
|
|
22160
|
+
return toolResult(`No pages found in space ${space_key}.`);
|
|
22161
|
+
}
|
|
22162
|
+
const lines = [`Pages in ${space_key} (${pages.length}):`, ""];
|
|
22163
|
+
for (const p of pages) {
|
|
22164
|
+
lines.push(`- ${p.title} (ID: ${p.id})`);
|
|
22165
|
+
}
|
|
22166
|
+
return toolResult(lines.join("\n"));
|
|
22167
|
+
} catch (err) {
|
|
22168
|
+
return toolError(err);
|
|
22169
|
+
}
|
|
22170
|
+
}
|
|
22171
|
+
);
|
|
22172
|
+
server.registerTool(
|
|
22173
|
+
"get_page_children",
|
|
22174
|
+
{
|
|
22175
|
+
description: "Get child pages of a given Confluence page",
|
|
22176
|
+
inputSchema: {
|
|
22177
|
+
page_id: external_exports.string().describe("Parent page ID"),
|
|
22178
|
+
limit: external_exports.number().default(25).describe("Maximum results (default: 25)")
|
|
22179
|
+
},
|
|
22180
|
+
annotations: { readOnlyHint: true }
|
|
21755
22181
|
},
|
|
21756
|
-
|
|
21757
|
-
|
|
21758
|
-
|
|
21759
|
-
|
|
21760
|
-
|
|
21761
|
-
|
|
21762
|
-
|
|
21763
|
-
|
|
21764
|
-
|
|
21765
|
-
|
|
21766
|
-
lines.
|
|
21767
|
-
}
|
|
21768
|
-
|
|
21769
|
-
|
|
21770
|
-
|
|
21771
|
-
|
|
21772
|
-
|
|
21773
|
-
|
|
21774
|
-
|
|
21775
|
-
|
|
21776
|
-
|
|
21777
|
-
|
|
21778
|
-
|
|
21779
|
-
|
|
21780
|
-
|
|
22182
|
+
async ({ page_id, limit }) => {
|
|
22183
|
+
try {
|
|
22184
|
+
const children = await getPageChildren(page_id, limit);
|
|
22185
|
+
if (children.length === 0) {
|
|
22186
|
+
return toolResult(`No child pages found for page ${page_id}.`);
|
|
22187
|
+
}
|
|
22188
|
+
const lines = [`Child pages (${children.length}):`, ""];
|
|
22189
|
+
for (const p of children) {
|
|
22190
|
+
lines.push(`- ${p.title} (ID: ${p.id})`);
|
|
22191
|
+
}
|
|
22192
|
+
return toolResult(lines.join("\n"));
|
|
22193
|
+
} catch (err) {
|
|
22194
|
+
return toolError(err);
|
|
22195
|
+
}
|
|
22196
|
+
}
|
|
22197
|
+
);
|
|
22198
|
+
server.registerTool(
|
|
22199
|
+
"get_spaces",
|
|
22200
|
+
{
|
|
22201
|
+
description: "List available Confluence spaces",
|
|
22202
|
+
inputSchema: {
|
|
22203
|
+
limit: external_exports.number().default(25).describe("Maximum results (default: 25)"),
|
|
22204
|
+
type: external_exports.string().optional().describe("Filter by space type (e.g., 'global', 'personal')")
|
|
22205
|
+
},
|
|
22206
|
+
annotations: { readOnlyHint: true }
|
|
21781
22207
|
},
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
|
|
21786
|
-
|
|
21787
|
-
|
|
21788
|
-
|
|
21789
|
-
|
|
21790
|
-
|
|
21791
|
-
|
|
21792
|
-
lines.
|
|
21793
|
-
}
|
|
21794
|
-
|
|
21795
|
-
|
|
21796
|
-
|
|
21797
|
-
|
|
21798
|
-
|
|
21799
|
-
|
|
21800
|
-
|
|
21801
|
-
|
|
21802
|
-
|
|
21803
|
-
|
|
21804
|
-
|
|
21805
|
-
|
|
21806
|
-
|
|
21807
|
-
|
|
22208
|
+
async ({ limit, type }) => {
|
|
22209
|
+
try {
|
|
22210
|
+
const spaces = await getSpaces(limit, type);
|
|
22211
|
+
if (spaces.length === 0) {
|
|
22212
|
+
return toolResult("No spaces found.");
|
|
22213
|
+
}
|
|
22214
|
+
const lines = [`Found ${spaces.length} space(s):`, ""];
|
|
22215
|
+
for (const s of spaces) {
|
|
22216
|
+
lines.push(`- ${s.name} (key: ${s.key}, type: ${s.type})`);
|
|
22217
|
+
}
|
|
22218
|
+
return toolResult(lines.join("\n"));
|
|
22219
|
+
} catch (err) {
|
|
22220
|
+
return toolError(err);
|
|
22221
|
+
}
|
|
22222
|
+
}
|
|
22223
|
+
);
|
|
22224
|
+
server.registerTool(
|
|
22225
|
+
"get_page_by_title",
|
|
22226
|
+
{
|
|
22227
|
+
description: "Look up a Confluence page by its title within a space",
|
|
22228
|
+
inputSchema: {
|
|
22229
|
+
title: external_exports.string().describe("Page title to search for"),
|
|
22230
|
+
space_key: external_exports.string().describe("Confluence space key (e.g., 'DEV')"),
|
|
22231
|
+
include_body: external_exports.boolean().default(false).describe("Whether to include the page body content")
|
|
22232
|
+
},
|
|
22233
|
+
annotations: { readOnlyHint: true }
|
|
21808
22234
|
},
|
|
21809
|
-
|
|
21810
|
-
|
|
21811
|
-
|
|
21812
|
-
|
|
21813
|
-
|
|
21814
|
-
|
|
21815
|
-
|
|
22235
|
+
async ({ title, space_key, include_body }) => {
|
|
22236
|
+
try {
|
|
22237
|
+
const spaceId = await resolveSpaceId(space_key);
|
|
22238
|
+
const page = await getPageByTitle(spaceId, title, include_body);
|
|
22239
|
+
if (!page) {
|
|
22240
|
+
return toolResult(
|
|
22241
|
+
`No page found with title "${title}" in space ${space_key}.`
|
|
22242
|
+
);
|
|
22243
|
+
}
|
|
22244
|
+
return toolResult(await formatPage(page, include_body));
|
|
22245
|
+
} catch (err) {
|
|
22246
|
+
return toolError(err);
|
|
22247
|
+
}
|
|
22248
|
+
}
|
|
22249
|
+
);
|
|
22250
|
+
server.registerTool(
|
|
22251
|
+
"add_attachment",
|
|
22252
|
+
{
|
|
22253
|
+
description: "Upload a file as an attachment to a Confluence page. The file_path must be an absolute path under the current working directory.",
|
|
22254
|
+
inputSchema: {
|
|
22255
|
+
page_id: external_exports.string().describe("The Confluence page ID to attach the file to"),
|
|
22256
|
+
file_path: external_exports.string().describe("Absolute path to the file on the local filesystem"),
|
|
22257
|
+
filename: external_exports.string().optional().describe(
|
|
22258
|
+
"Filename to use in Confluence (defaults to the basename of file_path)"
|
|
22259
|
+
),
|
|
22260
|
+
comment: external_exports.string().optional().describe("Optional comment for the attachment")
|
|
22261
|
+
},
|
|
22262
|
+
annotations: { destructiveHint: false, idempotentHint: false }
|
|
22263
|
+
},
|
|
22264
|
+
async ({ page_id, file_path, filename, comment }) => {
|
|
22265
|
+
try {
|
|
22266
|
+
const resolved = await (0, import_promises.realpath)((0, import_node_path.resolve)(file_path));
|
|
22267
|
+
const cwd = await (0, import_promises.realpath)(process.cwd());
|
|
22268
|
+
if (!resolved.startsWith(cwd + "/") && resolved !== cwd) {
|
|
22269
|
+
return toolError(
|
|
22270
|
+
new Error(
|
|
22271
|
+
`File path must be under the working directory (${cwd}). Got: ${resolved}`
|
|
22272
|
+
)
|
|
22273
|
+
);
|
|
22274
|
+
}
|
|
22275
|
+
const fileData = await (0, import_promises.readFile)(resolved);
|
|
22276
|
+
const name = filename ?? resolved.split("/").pop() ?? "attachment";
|
|
22277
|
+
const att = await uploadAttachment(page_id, fileData, name, comment);
|
|
21816
22278
|
return toolResult(
|
|
21817
|
-
`
|
|
22279
|
+
`Attached: ${att.title} (ID: ${att.id}, size: ${att.fileSize ?? "unknown"} bytes) to page ${page_id}` + echo
|
|
21818
22280
|
);
|
|
21819
|
-
}
|
|
21820
|
-
|
|
21821
|
-
|
|
21822
|
-
|
|
21823
|
-
|
|
21824
|
-
|
|
21825
|
-
|
|
21826
|
-
|
|
21827
|
-
|
|
21828
|
-
|
|
21829
|
-
|
|
21830
|
-
|
|
21831
|
-
|
|
21832
|
-
|
|
21833
|
-
|
|
21834
|
-
|
|
21835
|
-
|
|
21836
|
-
|
|
22281
|
+
} catch (err) {
|
|
22282
|
+
return toolError(err);
|
|
22283
|
+
}
|
|
22284
|
+
}
|
|
22285
|
+
);
|
|
22286
|
+
server.registerTool(
|
|
22287
|
+
"add_drawio_diagram",
|
|
22288
|
+
{
|
|
22289
|
+
description: "Add a draw.io diagram to a Confluence page. Uploads the diagram as an attachment and embeds it using the draw.io macro. Requires the draw.io app on the Confluence instance.",
|
|
22290
|
+
inputSchema: {
|
|
22291
|
+
page_id: external_exports.string().describe("The Confluence page ID to add the diagram to"),
|
|
22292
|
+
diagram_xml: external_exports.string().describe(
|
|
22293
|
+
"The draw.io diagram content in mxGraph XML format (the full XML starting with <mxfile>)"
|
|
22294
|
+
),
|
|
22295
|
+
diagram_name: external_exports.string().regex(
|
|
22296
|
+
/^[a-zA-Z0-9_\-. ]+$/,
|
|
22297
|
+
"Diagram name may only contain letters, numbers, spaces, hyphens, underscores, and dots"
|
|
22298
|
+
).describe(
|
|
22299
|
+
"Name for the diagram file (e.g., 'architecture.drawio'). Will have .drawio appended if not present."
|
|
22300
|
+
),
|
|
22301
|
+
append: external_exports.boolean().default(true).describe(
|
|
22302
|
+
"If true, appends the diagram to existing page content. If false, replaces the page body."
|
|
22303
|
+
)
|
|
22304
|
+
},
|
|
22305
|
+
annotations: { destructiveHint: false, idempotentHint: false }
|
|
21837
22306
|
},
|
|
21838
|
-
|
|
21839
|
-
|
|
21840
|
-
|
|
21841
|
-
|
|
21842
|
-
|
|
21843
|
-
|
|
21844
|
-
|
|
21845
|
-
|
|
21846
|
-
|
|
21847
|
-
|
|
21848
|
-
)
|
|
22307
|
+
async ({ page_id, diagram_xml, diagram_name, append }) => {
|
|
22308
|
+
try {
|
|
22309
|
+
const filename = diagram_name.endsWith(".drawio") ? diagram_name : `${diagram_name}.drawio`;
|
|
22310
|
+
const tmpDir = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "drawio-"));
|
|
22311
|
+
try {
|
|
22312
|
+
const tmpPath = (0, import_node_path.join)(tmpDir, filename);
|
|
22313
|
+
await (0, import_promises.writeFile)(tmpPath, diagram_xml, "utf-8");
|
|
22314
|
+
const fileData = await (0, import_promises.readFile)(tmpPath);
|
|
22315
|
+
await uploadAttachment(page_id, fileData, filename);
|
|
22316
|
+
} finally {
|
|
22317
|
+
await (0, import_promises.rm)(tmpDir, { recursive: true, force: true });
|
|
22318
|
+
}
|
|
22319
|
+
const macro = [
|
|
22320
|
+
`<ac:structured-macro ac:name="drawio" ac:schema-version="1">`,
|
|
22321
|
+
` <ac:parameter ac:name="diagramName">${escapeXml(filename)}</ac:parameter>`,
|
|
22322
|
+
` <ac:parameter ac:name="attachment">${escapeXml(filename)}</ac:parameter>`,
|
|
22323
|
+
`</ac:structured-macro>`
|
|
22324
|
+
].join("\n");
|
|
22325
|
+
const current = await getPage(page_id, true);
|
|
22326
|
+
const newVersion = (current.version?.number ?? 0) + 1;
|
|
22327
|
+
const existingBody = current.body?.storage?.value ?? current.body?.value ?? "";
|
|
22328
|
+
const newBody = append ? `${existingBody}
|
|
22329
|
+
${macro}` : macro;
|
|
22330
|
+
const { page } = await updatePage(page_id, {
|
|
22331
|
+
body: newBody,
|
|
22332
|
+
versionMessage: `Added diagram: ${filename}`
|
|
22333
|
+
});
|
|
22334
|
+
return toolResult(
|
|
22335
|
+
`Diagram "${filename}" added to page ${page.title} (ID: ${page.id}, version: ${newVersion})` + echo
|
|
21849
22336
|
);
|
|
22337
|
+
} catch (err) {
|
|
22338
|
+
return toolError(err);
|
|
21850
22339
|
}
|
|
21851
|
-
|
|
21852
|
-
|
|
21853
|
-
|
|
21854
|
-
|
|
21855
|
-
|
|
21856
|
-
|
|
21857
|
-
|
|
21858
|
-
|
|
21859
|
-
|
|
21860
|
-
|
|
21861
|
-
|
|
21862
|
-
server.registerTool(
|
|
21863
|
-
"add_drawio_diagram",
|
|
21864
|
-
{
|
|
21865
|
-
description: "Add a draw.io diagram to a Confluence page. Uploads the diagram as an attachment and embeds it using the draw.io macro. Requires the draw.io app on the Confluence instance.",
|
|
21866
|
-
inputSchema: {
|
|
21867
|
-
page_id: external_exports.string().describe("The Confluence page ID to add the diagram to"),
|
|
21868
|
-
diagram_xml: external_exports.string().describe(
|
|
21869
|
-
"The draw.io diagram content in mxGraph XML format (the full XML starting with <mxfile>)"
|
|
21870
|
-
),
|
|
21871
|
-
diagram_name: external_exports.string().regex(
|
|
21872
|
-
/^[a-zA-Z0-9_\-. ]+$/,
|
|
21873
|
-
"Diagram name may only contain letters, numbers, spaces, hyphens, underscores, and dots"
|
|
21874
|
-
).describe(
|
|
21875
|
-
"Name for the diagram file (e.g., 'architecture.drawio'). Will have .drawio appended if not present."
|
|
21876
|
-
),
|
|
21877
|
-
append: external_exports.boolean().default(true).describe(
|
|
21878
|
-
"If true, appends the diagram to existing page content. If false, replaces the page body."
|
|
21879
|
-
)
|
|
22340
|
+
}
|
|
22341
|
+
);
|
|
22342
|
+
server.registerTool(
|
|
22343
|
+
"get_attachments",
|
|
22344
|
+
{
|
|
22345
|
+
description: "List attachments on a Confluence page",
|
|
22346
|
+
inputSchema: {
|
|
22347
|
+
page_id: external_exports.string().describe("The Confluence page ID"),
|
|
22348
|
+
limit: external_exports.number().default(25).describe("Maximum results (default: 25)")
|
|
22349
|
+
},
|
|
22350
|
+
annotations: { readOnlyHint: true }
|
|
21880
22351
|
},
|
|
21881
|
-
|
|
21882
|
-
},
|
|
21883
|
-
async ({ page_id, diagram_xml, diagram_name, append }) => {
|
|
21884
|
-
try {
|
|
21885
|
-
const filename = diagram_name.endsWith(".drawio") ? diagram_name : `${diagram_name}.drawio`;
|
|
21886
|
-
const tmpDir = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "drawio-"));
|
|
22352
|
+
async ({ page_id, limit }) => {
|
|
21887
22353
|
try {
|
|
21888
|
-
const
|
|
21889
|
-
|
|
21890
|
-
|
|
21891
|
-
|
|
21892
|
-
|
|
21893
|
-
|
|
21894
|
-
|
|
21895
|
-
|
|
21896
|
-
|
|
21897
|
-
|
|
21898
|
-
|
|
21899
|
-
|
|
21900
|
-
|
|
21901
|
-
|
|
21902
|
-
|
|
21903
|
-
|
|
21904
|
-
const newBody = append ? `${existingBody}
|
|
21905
|
-
${macro}` : macro;
|
|
21906
|
-
const { page } = await updatePage(page_id, {
|
|
21907
|
-
body: newBody,
|
|
21908
|
-
versionMessage: `Added diagram: ${filename}`
|
|
21909
|
-
});
|
|
21910
|
-
return toolResult(
|
|
21911
|
-
`Diagram "${filename}" added to page ${page.title} (ID: ${page.id}, version: ${newVersion})`
|
|
21912
|
-
);
|
|
21913
|
-
} catch (err) {
|
|
21914
|
-
return toolError(err);
|
|
21915
|
-
}
|
|
21916
|
-
}
|
|
21917
|
-
);
|
|
21918
|
-
server.registerTool(
|
|
21919
|
-
"get_attachments",
|
|
21920
|
-
{
|
|
21921
|
-
description: "List attachments on a Confluence page",
|
|
21922
|
-
inputSchema: {
|
|
21923
|
-
page_id: external_exports.string().describe("The Confluence page ID"),
|
|
21924
|
-
limit: external_exports.number().default(25).describe("Maximum results (default: 25)")
|
|
21925
|
-
},
|
|
21926
|
-
annotations: { readOnlyHint: true }
|
|
21927
|
-
},
|
|
21928
|
-
async ({ page_id, limit }) => {
|
|
21929
|
-
try {
|
|
21930
|
-
const attachments = await getAttachments(page_id, limit);
|
|
21931
|
-
if (attachments.length === 0) {
|
|
21932
|
-
return toolResult(`No attachments found on page ${page_id}.`);
|
|
21933
|
-
}
|
|
21934
|
-
const lines = [
|
|
21935
|
-
`Attachments on page ${page_id} (${attachments.length}):`,
|
|
21936
|
-
""
|
|
21937
|
-
];
|
|
21938
|
-
for (const a of attachments) {
|
|
21939
|
-
const size = a.extensions?.fileSize ? `${Math.round(a.extensions.fileSize / 1024)}KB` : "unknown size";
|
|
21940
|
-
const mediaType = a.extensions?.mediaType ?? "unknown type";
|
|
21941
|
-
lines.push(`- ${a.title} (ID: ${a.id}, ${mediaType}, ${size})`);
|
|
22354
|
+
const attachments = await getAttachments(page_id, limit);
|
|
22355
|
+
if (attachments.length === 0) {
|
|
22356
|
+
return toolResult(`No attachments found on page ${page_id}.`);
|
|
22357
|
+
}
|
|
22358
|
+
const lines = [
|
|
22359
|
+
`Attachments on page ${page_id} (${attachments.length}):`,
|
|
22360
|
+
""
|
|
22361
|
+
];
|
|
22362
|
+
for (const a of attachments) {
|
|
22363
|
+
const size = a.extensions?.fileSize ? `${Math.round(a.extensions.fileSize / 1024)}KB` : "unknown size";
|
|
22364
|
+
const mediaType = a.extensions?.mediaType ?? "unknown type";
|
|
22365
|
+
lines.push(`- ${a.title} (ID: ${a.id}, ${mediaType}, ${size})`);
|
|
22366
|
+
}
|
|
22367
|
+
return toolResult(lines.join("\n"));
|
|
22368
|
+
} catch (err) {
|
|
22369
|
+
return toolError(err);
|
|
21942
22370
|
}
|
|
21943
|
-
return toolResult(lines.join("\n"));
|
|
21944
|
-
} catch (err) {
|
|
21945
|
-
return toolError(err);
|
|
21946
22371
|
}
|
|
21947
|
-
|
|
21948
|
-
|
|
22372
|
+
);
|
|
22373
|
+
}
|
|
21949
22374
|
async function main() {
|
|
22375
|
+
const config2 = await getConfig();
|
|
22376
|
+
await validateStartup(config2);
|
|
22377
|
+
const serverName = config2.profile ? `confluence-${config2.profile}` : "confluence";
|
|
22378
|
+
const server = new McpServer({
|
|
22379
|
+
name: serverName,
|
|
22380
|
+
version: "1.0.0"
|
|
22381
|
+
});
|
|
22382
|
+
registerTools(server, config2);
|
|
21950
22383
|
const transport = new StdioServerTransport();
|
|
21951
22384
|
await server.connect(transport);
|
|
21952
22385
|
}
|
|
@@ -21955,8 +22388,16 @@ async function main() {
|
|
|
21955
22388
|
async function run() {
|
|
21956
22389
|
const command = process.argv[2];
|
|
21957
22390
|
if (command === "setup") {
|
|
22391
|
+
const idx = process.argv.indexOf("--profile");
|
|
22392
|
+
const profile = idx > -1 ? process.argv[idx + 1] : void 0;
|
|
21958
22393
|
const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
21959
|
-
await runSetup2();
|
|
22394
|
+
await runSetup2(profile);
|
|
22395
|
+
} else if (command === "profiles") {
|
|
22396
|
+
const { runProfiles: runProfiles2 } = await Promise.resolve().then(() => (init_profiles2(), profiles_exports));
|
|
22397
|
+
await runProfiles2();
|
|
22398
|
+
} else if (command === "status") {
|
|
22399
|
+
const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
22400
|
+
await runStatus2();
|
|
21960
22401
|
} else {
|
|
21961
22402
|
await main();
|
|
21962
22403
|
}
|