@simplysf/simply-gitlab-mcp 0.6.2 → 0.7.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 +4 -0
- package/docs/guides/mcp-server.md +34 -0
- package/lib/commands/gitlab/mcp/install.d.ts +33 -0
- package/lib/commands/gitlab/mcp/install.js +123 -0
- package/lib/commands/gitlab/mcp/install.js.map +1 -0
- package/lib/mcpClients/apply.d.ts +10 -0
- package/lib/mcpClients/apply.js +209 -0
- package/lib/mcpClients/apply.js.map +1 -0
- package/lib/mcpClients/clients.d.ts +36 -0
- package/lib/mcpClients/clients.js +111 -0
- package/lib/mcpClients/clients.js.map +1 -0
- package/lib/mcpClients/existingEntry.d.ts +16 -0
- package/lib/mcpClients/existingEntry.js +140 -0
- package/lib/mcpClients/existingEntry.js.map +1 -0
- package/lib/mcpClients/index.d.ts +8 -0
- package/lib/mcpClients/index.js +29 -0
- package/lib/mcpClients/index.js.map +1 -0
- package/lib/mcpClients/install.d.ts +14 -0
- package/lib/mcpClients/install.js +55 -0
- package/lib/mcpClients/install.js.map +1 -0
- package/lib/mcpClients/jsonEdit.d.ts +12 -0
- package/lib/mcpClients/jsonEdit.js +123 -0
- package/lib/mcpClients/jsonEdit.js.map +1 -0
- package/lib/mcpClients/launch.d.ts +36 -0
- package/lib/mcpClients/launch.js +75 -0
- package/lib/mcpClients/launch.js.map +1 -0
- package/lib/mcpClients/plan.d.ts +14 -0
- package/lib/mcpClients/plan.js +95 -0
- package/lib/mcpClients/plan.js.map +1 -0
- package/lib/mcpClients/report.d.ts +12 -0
- package/lib/mcpClients/report.js +63 -0
- package/lib/mcpClients/report.js.map +1 -0
- package/lib/mcpClients/runner.d.ts +9 -0
- package/lib/mcpClients/runner.js +38 -0
- package/lib/mcpClients/runner.js.map +1 -0
- package/lib/mcpClients/types.d.ts +98 -0
- package/lib/mcpClients/types.js +18 -0
- package/lib/mcpClients/types.js.map +1 -0
- package/oclif.manifest.json +124 -1
- package/package.json +6 -3
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
17
|
+
import { parse } from 'jsonc-parser';
|
|
18
|
+
/** @returns How `launch` compares with the user-scope `mcpServers` entry in Claude Code's `~/.claude.json`. */
|
|
19
|
+
export function compareClaudeEntry(text, name, launch) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const root = parse(text, errors, { allowTrailingComma: true });
|
|
22
|
+
// An unreadable file can't show an entry; `claude` itself reports whatever is wrong with it.
|
|
23
|
+
if (errors.length > 0 || !isRecord(root) || !isRecord(root.mcpServers))
|
|
24
|
+
return 'absent';
|
|
25
|
+
const entry = root.mcpServers[name];
|
|
26
|
+
if (entry === undefined)
|
|
27
|
+
return 'absent';
|
|
28
|
+
if (!isRecord(entry))
|
|
29
|
+
return 'differs';
|
|
30
|
+
const { type = 'stdio', command, args = [], env = {}, ...rest } = entry;
|
|
31
|
+
const same = type === 'stdio' &&
|
|
32
|
+
command === launch.command &&
|
|
33
|
+
isDeepStrictEqual(args, launch.args) &&
|
|
34
|
+
isDeepStrictEqual(env, {}) &&
|
|
35
|
+
Object.keys(rest).length === 0;
|
|
36
|
+
return same ? 'same' : 'differs';
|
|
37
|
+
}
|
|
38
|
+
/** @returns How `launch` compares with the `[mcp_servers.<name>]` table in Codex's `config.toml`. */
|
|
39
|
+
export function compareCodexEntry(text, name, launch) {
|
|
40
|
+
const lines = text.split(/\r?\n/);
|
|
41
|
+
const header = lines.findIndex((line) => tableName(line) === `mcp_servers.${name}`);
|
|
42
|
+
if (header === -1)
|
|
43
|
+
return 'absent';
|
|
44
|
+
const body = [];
|
|
45
|
+
for (const line of lines.slice(header + 1)) {
|
|
46
|
+
const table = tableName(line);
|
|
47
|
+
if (table !== undefined) {
|
|
48
|
+
// A sub-table such as `[mcp_servers.<name>.env]` is more than a bare launch.
|
|
49
|
+
if (table.startsWith(`mcp_servers.${name}.`))
|
|
50
|
+
return 'differs';
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
body.push(line);
|
|
54
|
+
}
|
|
55
|
+
const values = parseKeyValues(body.join('\n'));
|
|
56
|
+
if (!values)
|
|
57
|
+
return 'differs';
|
|
58
|
+
const { command, args = [], ...rest } = values;
|
|
59
|
+
const same = command === launch.command && isDeepStrictEqual(args, launch.args) && Object.keys(rest).length === 0;
|
|
60
|
+
return same ? 'same' : 'differs';
|
|
61
|
+
}
|
|
62
|
+
function isRecord(value) {
|
|
63
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
/** @returns The dotted name of a `[table]` header line, with quoted keys unquoted, or undefined. */
|
|
66
|
+
function tableName(line) {
|
|
67
|
+
const match = /^\s*\[([^[\]]+)\]\s*(#.*)?$/.exec(line);
|
|
68
|
+
if (!match)
|
|
69
|
+
return undefined;
|
|
70
|
+
return match[1]
|
|
71
|
+
.split('.')
|
|
72
|
+
.map((part) => part.trim().replace(/^"(.*)"$|^'(.*)'$/, '$1$2'))
|
|
73
|
+
.join('.');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Reads `key = "string"` and `key = ["string", …]` pairs, which is all a launch-only server table
|
|
77
|
+
* holds. Anything else makes the whole table unreadable, so the caller treats it as different.
|
|
78
|
+
*
|
|
79
|
+
* @returns The pairs, or undefined if the text holds anything else.
|
|
80
|
+
*/
|
|
81
|
+
function parseKeyValues(text) {
|
|
82
|
+
const tokens = tokenize(text);
|
|
83
|
+
if (!tokens)
|
|
84
|
+
return undefined;
|
|
85
|
+
const values = {};
|
|
86
|
+
let i = 0;
|
|
87
|
+
while (i < tokens.length) {
|
|
88
|
+
const key = tokens[i];
|
|
89
|
+
if (key.kind !== 'key' || tokens[i + 1]?.kind !== '=')
|
|
90
|
+
return undefined;
|
|
91
|
+
i += 2;
|
|
92
|
+
const value = tokens[i];
|
|
93
|
+
if (value?.kind === 'string') {
|
|
94
|
+
values[key.value] = value.value;
|
|
95
|
+
i += 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (value?.kind !== '[')
|
|
99
|
+
return undefined;
|
|
100
|
+
i += 1;
|
|
101
|
+
const items = [];
|
|
102
|
+
while (tokens[i]?.kind === 'string') {
|
|
103
|
+
items.push(tokens[i].value);
|
|
104
|
+
i += 1;
|
|
105
|
+
if (tokens[i]?.kind === ',')
|
|
106
|
+
i += 1;
|
|
107
|
+
}
|
|
108
|
+
if (tokens[i]?.kind !== ']')
|
|
109
|
+
return undefined;
|
|
110
|
+
values[key.value] = items;
|
|
111
|
+
i += 1;
|
|
112
|
+
}
|
|
113
|
+
return values;
|
|
114
|
+
}
|
|
115
|
+
function tokenize(text) {
|
|
116
|
+
const tokens = [];
|
|
117
|
+
const pattern = /\s+|#[^\n]*|([A-Za-z0-9_-]+)|("(?:[^"\\\n]|\\.)*")|'([^'\n]*)'|([=[\],])|(.)/gy;
|
|
118
|
+
for (const match of text.matchAll(pattern)) {
|
|
119
|
+
const [, key, basic, literal, punctuation, unexpected] = match;
|
|
120
|
+
if (unexpected !== undefined)
|
|
121
|
+
return undefined;
|
|
122
|
+
if (key !== undefined)
|
|
123
|
+
tokens.push({ kind: 'key', value: key });
|
|
124
|
+
else if (literal !== undefined)
|
|
125
|
+
tokens.push({ kind: 'string', value: literal });
|
|
126
|
+
else if (punctuation !== undefined)
|
|
127
|
+
tokens.push({ kind: punctuation, value: '' });
|
|
128
|
+
else if (basic !== undefined) {
|
|
129
|
+
try {
|
|
130
|
+
// TOML basic strings use JSON's escapes for everything a launch line needs.
|
|
131
|
+
tokens.push({ kind: 'string', value: JSON.parse(basic) });
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return tokens;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=existingEntry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"existingEntry.js","sourceRoot":"","sources":["../../src/mcpClients/existingEntry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAmB,MAAM,cAAc,CAAC;AAYtD,+GAA+G;AAC/G,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,IAAY,EAAE,MAAc;IAC3E,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,MAAM,IAAI,GAAY,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,6FAA6F;IAC7F,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,QAAQ,CAAC;IAExF,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACzC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEvC,MAAM,EAAE,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IACxE,MAAM,IAAI,GACR,IAAI,KAAK,OAAO;QAChB,OAAO,KAAK,MAAM,CAAC,OAAO;QAC1B,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;QACpC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACnC,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,IAAY,EAAE,MAAc;IAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,eAAe,IAAI,EAAE,CAAC,CAAC;IACpF,IAAI,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEnC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,6EAA6E;YAC7E,IAAI,KAAK,CAAC,UAAU,CAAC,eAAe,IAAI,GAAG,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC/D,MAAM;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAC/C,MAAM,IAAI,GAAG,OAAO,KAAK,MAAM,CAAC,OAAO,IAAI,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;IAClH,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACnC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,oGAAoG;AACpG,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,KAAK,CAAC,CAAC,CAAC;SACZ,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;SAC/D,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAID;;;;;GAKG;AACH,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,MAAM,MAAM,GAA8B,EAAE,CAAC;IAC7C,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QACxE,CAAC,IAAI,CAAC,CAAC;QACP,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,KAAK,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;YAChC,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,KAAK,EAAE,IAAI,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QAC1C,CAAC,IAAI,CAAC,CAAC;QACP,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC,IAAI,CAAC,CAAC;YACP,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG;gBAAE,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QAC9C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QAC1B,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAID,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,gFAAgF,CAAC;IACjG,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,GAAG,KAAK,CAAC;QAC/D,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC/C,IAAI,GAAG,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;aAC3D,IAAI,OAAO,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;aAC3E,IAAI,WAAW,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAoC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;aACtG,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,4EAA4E;gBAC5E,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAW,EAAE,CAAC,CAAC;YACtE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { CLIENT_IDS, type ApplyOptions, type ClientId, type CliInstallPlan, type FileInstallPlan, type InstallEnvironment, type InstallOutcome, type InstallPlan, type InstallResult, type InstallScope, type Launcher, type ProcessRunner, type ServerSpec, type ServersKey, type UnsupportedInstallPlan, } from './types.js';
|
|
2
|
+
export { CLIENTS } from './clients.js';
|
|
3
|
+
export { planInstall, isValidServerName } from './plan.js';
|
|
4
|
+
export { applyPlan } from './apply.js';
|
|
5
|
+
export { installServer, exitCodeFor, detectInstalledClients, isClientId } from './install.js';
|
|
6
|
+
export { buildLaunch, currentEnvironment, defaultLauncher, resolveEnvFile, SIMPLY_CLI_PACKAGE } from './launch.js';
|
|
7
|
+
export { formatInstallReport, formatClientChoices } from './report.js';
|
|
8
|
+
export { formatCommandLine, runProcess } from './runner.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
// The knowledge of MCP clients — where each keeps its configuration, what shape it wants, and how to
|
|
17
|
+
// edit it without destroying the rest — behind `gitlab mcp install`. See
|
|
18
|
+
// docs/design/0010-mcp-install.md. simply-atlassian keeps an identical copy for `atlassian mcp install`:
|
|
19
|
+
// change both together. Like the core package, nothing here writes to stdout or stderr or reads
|
|
20
|
+
// process.argv: it plans, applies, and returns text for the caller to print.
|
|
21
|
+
export { CLIENT_IDS, } from './types.js';
|
|
22
|
+
export { CLIENTS } from './clients.js';
|
|
23
|
+
export { planInstall, isValidServerName } from './plan.js';
|
|
24
|
+
export { applyPlan } from './apply.js';
|
|
25
|
+
export { installServer, exitCodeFor, detectInstalledClients, isClientId } from './install.js';
|
|
26
|
+
export { buildLaunch, currentEnvironment, defaultLauncher, resolveEnvFile, SIMPLY_CLI_PACKAGE } from './launch.js';
|
|
27
|
+
export { formatInstallReport, formatClientChoices } from './report.js';
|
|
28
|
+
export { formatCommandLine, runProcess } from './runner.js';
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcpClients/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,qGAAqG;AACrG,yEAAyE;AACzE,yGAAyG;AACzG,gGAAgG;AAChG,6EAA6E;AAE7E,OAAO,EACL,UAAU,GAeX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ApplyOptions, type ClientId, type InstallEnvironment, type InstallResult, type InstallScope, type ServerSpec } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Installs one server into each named client, in order. Every client is attempted whatever happened
|
|
4
|
+
* to the ones before it.
|
|
5
|
+
*
|
|
6
|
+
* @returns One result per client, in the order given.
|
|
7
|
+
*/
|
|
8
|
+
export declare function installServer(spec: ServerSpec, clients: readonly ClientId[], scope: InstallScope, environment: InstallEnvironment, options?: ApplyOptions): Promise<InstallResult[]>;
|
|
9
|
+
/** @returns The exit code for a set of results: the highest any one of them contributes. */
|
|
10
|
+
export declare function exitCodeFor(results: readonly InstallResult[]): 0 | 1 | 2;
|
|
11
|
+
/** @returns The clients whose configuration directory exists for this user. */
|
|
12
|
+
export declare function detectInstalledClients(environment: InstallEnvironment): Promise<ClientId[]>;
|
|
13
|
+
/** @returns Whether `value` is a client id this module knows. */
|
|
14
|
+
export declare function isClientId(value: string): value is ClientId;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
import { applyPlan } from './apply.js';
|
|
18
|
+
import { clientMarker } from './clients.js';
|
|
19
|
+
import { planInstall } from './plan.js';
|
|
20
|
+
import { CLIENT_IDS, } from './types.js';
|
|
21
|
+
/**
|
|
22
|
+
* Installs one server into each named client, in order. Every client is attempted whatever happened
|
|
23
|
+
* to the ones before it.
|
|
24
|
+
*
|
|
25
|
+
* @returns One result per client, in the order given.
|
|
26
|
+
*/
|
|
27
|
+
export async function installServer(spec, clients, scope, environment, options = {}) {
|
|
28
|
+
const results = [];
|
|
29
|
+
for (const client of new Set(clients)) {
|
|
30
|
+
// eslint-disable-next-line no-await-in-loop -- one client at a time keeps CLI output and file writes in a predictable order
|
|
31
|
+
results.push(await applyPlan(planInstall(spec, client, scope, environment), options));
|
|
32
|
+
}
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
/** @returns The exit code for a set of results: the highest any one of them contributes. */
|
|
36
|
+
export function exitCodeFor(results) {
|
|
37
|
+
return results.reduce((highest, result) => (result.exitCode > highest ? result.exitCode : highest), 0);
|
|
38
|
+
}
|
|
39
|
+
/** @returns The clients whose configuration directory exists for this user. */
|
|
40
|
+
export async function detectInstalledClients(environment) {
|
|
41
|
+
const found = await Promise.all(CLIENT_IDS.map(async (client) => {
|
|
42
|
+
try {
|
|
43
|
+
return (await fs.stat(clientMarker(client, environment))).isDirectory();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}));
|
|
49
|
+
return CLIENT_IDS.filter((_, index) => found[index]);
|
|
50
|
+
}
|
|
51
|
+
/** @returns Whether `value` is a client id this module knows. */
|
|
52
|
+
export function isClientId(value) {
|
|
53
|
+
return CLIENT_IDS.includes(value);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/mcpClients/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACL,UAAU,GAOX,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAgB,EAChB,OAA4B,EAC5B,KAAmB,EACnB,WAA+B,EAC/B,UAAwB,EAAE;IAE1B,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,MAAM,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,4HAA4H;QAC5H,OAAO,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAAC,OAAiC;IAC3D,OAAO,OAAO,CAAC,MAAM,CAAY,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;AACpH,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,WAA+B;IAC1E,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IACF,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAQ,UAAgC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets `root[key][name] = value` in JSON or JSONC text, changing no byte outside that entry.
|
|
3
|
+
*
|
|
4
|
+
* Not `jsonc-parser`'s `modify`: given formatting options, it reformats the neighbouring entry and
|
|
5
|
+
* can move a trailing comment onto the new line, and without them it writes the value on one line.
|
|
6
|
+
* Here the new text is the only text: it takes the file's own indentation and line endings, and a
|
|
7
|
+
* comment after the previous entry stays on that entry's line.
|
|
8
|
+
*
|
|
9
|
+
* @param text - The file, already known to parse to an object whose `key`, if present, is an object.
|
|
10
|
+
* @returns The new text.
|
|
11
|
+
*/
|
|
12
|
+
export declare function setNestedEntry(text: string, key: string, name: string, value: unknown): string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { findNodeAtLocation, parseTree } from 'jsonc-parser';
|
|
17
|
+
/**
|
|
18
|
+
* Sets `root[key][name] = value` in JSON or JSONC text, changing no byte outside that entry.
|
|
19
|
+
*
|
|
20
|
+
* Not `jsonc-parser`'s `modify`: given formatting options, it reformats the neighbouring entry and
|
|
21
|
+
* can move a trailing comment onto the new line, and without them it writes the value on one line.
|
|
22
|
+
* Here the new text is the only text: it takes the file's own indentation and line endings, and a
|
|
23
|
+
* comment after the previous entry stays on that entry's line.
|
|
24
|
+
*
|
|
25
|
+
* @param text - The file, already known to parse to an object whose `key`, if present, is an object.
|
|
26
|
+
* @returns The new text.
|
|
27
|
+
*/
|
|
28
|
+
export function setNestedEntry(text, key, name, value) {
|
|
29
|
+
const eol = text.includes('\r\n') ? '\r\n' : '\n';
|
|
30
|
+
const unit = indentUnit(text);
|
|
31
|
+
if (text.trim() === '') {
|
|
32
|
+
return [`{`, `${unit}${property(key, { [name]: value }, unit, unit, eol)}`, `}`, ''].join(eol);
|
|
33
|
+
}
|
|
34
|
+
const root = parseTree(text, [], { allowTrailingComma: true });
|
|
35
|
+
const existing = findNodeAtLocation(root, [key, name]);
|
|
36
|
+
if (existing) {
|
|
37
|
+
const indent = lineIndent(text, existing.parent.offset);
|
|
38
|
+
return applyEdit(text, {
|
|
39
|
+
offset: existing.offset,
|
|
40
|
+
length: existing.length,
|
|
41
|
+
content: formatValue(value, indent, unit, eol),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const servers = findNodeAtLocation(root, [key]);
|
|
45
|
+
return servers
|
|
46
|
+
? insertProperty(text, servers, name, value, unit, eol)
|
|
47
|
+
: insertProperty(text, root, key, { [name]: value }, unit, eol);
|
|
48
|
+
}
|
|
49
|
+
function insertProperty(text, object, name, value, unit, eol) {
|
|
50
|
+
const properties = object.children ?? [];
|
|
51
|
+
const parentIndent = lineIndent(text, object.offset);
|
|
52
|
+
if (properties.length === 0) {
|
|
53
|
+
const indent = parentIndent + unit;
|
|
54
|
+
const open = object.offset + 1;
|
|
55
|
+
const close = object.offset + object.length - 1;
|
|
56
|
+
const inner = text.slice(open, close);
|
|
57
|
+
const content = `${eol}${indent}${property(name, value, indent, unit, eol)}`;
|
|
58
|
+
// `{}` becomes a block; anything inside an otherwise empty object (a comment) is kept after it.
|
|
59
|
+
return inner.trim() === ''
|
|
60
|
+
? applyEdit(text, { offset: open, length: close - open, content: `${content}${eol}${parentIndent}` })
|
|
61
|
+
: applyEdit(text, { offset: open, length: 0, content: `${content},` });
|
|
62
|
+
}
|
|
63
|
+
const last = properties.at(-1);
|
|
64
|
+
const first = properties[0];
|
|
65
|
+
const indent = startsLine(text, first.offset) ? lineIndent(text, first.offset) : parentIndent + unit;
|
|
66
|
+
const valueEnd = last.offset + last.length;
|
|
67
|
+
// A trailing comma already separates the last entry from whatever follows it.
|
|
68
|
+
const afterValue = /^\s*,/.exec(text.slice(valueEnd));
|
|
69
|
+
const commaEnd = afterValue ? valueEnd + afterValue[0].length : valueEnd;
|
|
70
|
+
// The new entry goes after anything else on the last entry's line (a `// comment`), unless that
|
|
71
|
+
// line also closes the object, as in `{ "a": 1 }`.
|
|
72
|
+
const lineEnd = endOfLine(text, commaEnd);
|
|
73
|
+
const restOfLine = text.slice(commaEnd, lineEnd);
|
|
74
|
+
const at = /^[ \t]*(\/\/.*|\/\*.*?\*\/[ \t]*)?$/.test(restOfLine) ? lineEnd : commaEnd;
|
|
75
|
+
const entry = `${eol}${indent}${property(name, value, indent, unit, eol)}`;
|
|
76
|
+
if (afterValue)
|
|
77
|
+
return applyEdit(text, { offset: at, length: 0, content: entry });
|
|
78
|
+
if (at === valueEnd)
|
|
79
|
+
return applyEdit(text, { offset: at, length: 0, content: `,${entry}` });
|
|
80
|
+
return applyEdits(text, [
|
|
81
|
+
{ offset: valueEnd, length: 0, content: ',' },
|
|
82
|
+
{ offset: at, length: 0, content: entry },
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
function property(name, value, indent, unit, eol) {
|
|
86
|
+
return `${JSON.stringify(name)}: ${formatValue(value, indent, unit, eol)}`;
|
|
87
|
+
}
|
|
88
|
+
/** @returns `value` as JSON, its continuation lines indented from `indent`. */
|
|
89
|
+
function formatValue(value, indent, unit, eol) {
|
|
90
|
+
return JSON.stringify(value, null, unit).split('\n').join(`${eol}${indent}`);
|
|
91
|
+
}
|
|
92
|
+
/** @returns The file's indentation step: its first indented line's leading tab or spaces, else two spaces. */
|
|
93
|
+
function indentUnit(text) {
|
|
94
|
+
const indent = /^([ \t]+)\S/m.exec(text)?.[1];
|
|
95
|
+
if (!indent)
|
|
96
|
+
return ' ';
|
|
97
|
+
return indent.startsWith('\t') ? '\t' : indent;
|
|
98
|
+
}
|
|
99
|
+
function lineStart(text, offset) {
|
|
100
|
+
return text.lastIndexOf('\n', offset - 1) + 1;
|
|
101
|
+
}
|
|
102
|
+
function lineIndent(text, offset) {
|
|
103
|
+
return /^[ \t]*/.exec(text.slice(lineStart(text, offset)))?.[0] ?? '';
|
|
104
|
+
}
|
|
105
|
+
function startsLine(text, offset) {
|
|
106
|
+
return text.slice(lineStart(text, offset), offset).trim() === '';
|
|
107
|
+
}
|
|
108
|
+
function endOfLine(text, offset) {
|
|
109
|
+
const newline = text.indexOf('\n', offset);
|
|
110
|
+
if (newline === -1)
|
|
111
|
+
return text.length;
|
|
112
|
+
return text[newline - 1] === '\r' ? newline - 1 : newline;
|
|
113
|
+
}
|
|
114
|
+
function applyEdit(text, edit) {
|
|
115
|
+
return applyEdits(text, [edit]);
|
|
116
|
+
}
|
|
117
|
+
/** Applies non-overlapping edits, last first, so earlier offsets stay valid. */
|
|
118
|
+
function applyEdits(text, edits) {
|
|
119
|
+
return [...edits]
|
|
120
|
+
.sort((a, b) => b.offset - a.offset)
|
|
121
|
+
.reduce((result, edit) => result.slice(0, edit.offset) + edit.content + result.slice(edit.offset + edit.length), text);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=jsonEdit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonEdit.js","sourceRoot":"","sources":["../../src/mcpClients/jsonEdit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { InstallEnvironment, InstallScope, Launcher } from './types.js';
|
|
2
|
+
/** The package `npx` fetches for the `npx` launcher. */
|
|
3
|
+
export declare const SIMPLY_CLI_PACKAGE = "@simplysf/simply-cli";
|
|
4
|
+
/** @returns The environment of the running process, for `planInstall` and friends. */
|
|
5
|
+
export declare function currentEnvironment(): InstallEnvironment;
|
|
6
|
+
/**
|
|
7
|
+
* `absolute` for personal configuration, where the path is right for the only person who reads it;
|
|
8
|
+
* `simply` for a committed project file, where an absolute path is wrong for everyone else.
|
|
9
|
+
*
|
|
10
|
+
* @returns The launcher to use when the caller didn't name one.
|
|
11
|
+
*/
|
|
12
|
+
export declare function defaultLauncher(scope: InstallScope): Launcher;
|
|
13
|
+
/**
|
|
14
|
+
* Builds the process an entry launches.
|
|
15
|
+
*
|
|
16
|
+
* @param launcher - How to start `simply`.
|
|
17
|
+
* @param simplyArgs - The `simply` arguments, e.g. `['atlassian', 'mcp', '--allow-writes']`.
|
|
18
|
+
* @param host - For `absolute`: the Node binary (`process.execPath`) and the host's `bin/run.js`.
|
|
19
|
+
* @returns The entry's `command` and `args`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildLaunch(launcher: Launcher, simplyArgs: readonly string[], host: {
|
|
22
|
+
nodePath: string;
|
|
23
|
+
runScript: string;
|
|
24
|
+
}): {
|
|
25
|
+
command: string;
|
|
26
|
+
args: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Makes an `--env-file` path absolute and checks it exists, here, at install time. A client starts
|
|
30
|
+
* the server from a working directory of its own, so `~` and relative paths written into its
|
|
31
|
+
* configuration would resolve somewhere else, or nowhere.
|
|
32
|
+
*
|
|
33
|
+
* @returns The absolute path.
|
|
34
|
+
* @throws When the file doesn't exist or isn't a file.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveEnvFile(file: string, environment: InstallEnvironment): Promise<string>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
import { pathFor } from './clients.js';
|
|
19
|
+
/** The package `npx` fetches for the `npx` launcher. */
|
|
20
|
+
export const SIMPLY_CLI_PACKAGE = '@simplysf/simply-cli';
|
|
21
|
+
/** @returns The environment of the running process, for `planInstall` and friends. */
|
|
22
|
+
export function currentEnvironment() {
|
|
23
|
+
return { platform: process.platform, homedir: os.homedir(), cwd: process.cwd(), env: process.env };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* `absolute` for personal configuration, where the path is right for the only person who reads it;
|
|
27
|
+
* `simply` for a committed project file, where an absolute path is wrong for everyone else.
|
|
28
|
+
*
|
|
29
|
+
* @returns The launcher to use when the caller didn't name one.
|
|
30
|
+
*/
|
|
31
|
+
export function defaultLauncher(scope) {
|
|
32
|
+
return scope === 'project' ? 'simply' : 'absolute';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Builds the process an entry launches.
|
|
36
|
+
*
|
|
37
|
+
* @param launcher - How to start `simply`.
|
|
38
|
+
* @param simplyArgs - The `simply` arguments, e.g. `['atlassian', 'mcp', '--allow-writes']`.
|
|
39
|
+
* @param host - For `absolute`: the Node binary (`process.execPath`) and the host's `bin/run.js`.
|
|
40
|
+
* @returns The entry's `command` and `args`.
|
|
41
|
+
*/
|
|
42
|
+
export function buildLaunch(launcher, simplyArgs, host) {
|
|
43
|
+
switch (launcher) {
|
|
44
|
+
case 'absolute':
|
|
45
|
+
return { command: host.nodePath, args: [host.runScript, ...simplyArgs] };
|
|
46
|
+
case 'npx':
|
|
47
|
+
return { command: 'npx', args: ['-y', SIMPLY_CLI_PACKAGE, ...simplyArgs] };
|
|
48
|
+
case 'simply':
|
|
49
|
+
return { command: 'simply', args: [...simplyArgs] };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Makes an `--env-file` path absolute and checks it exists, here, at install time. A client starts
|
|
54
|
+
* the server from a working directory of its own, so `~` and relative paths written into its
|
|
55
|
+
* configuration would resolve somewhere else, or nowhere.
|
|
56
|
+
*
|
|
57
|
+
* @returns The absolute path.
|
|
58
|
+
* @throws When the file doesn't exist or isn't a file.
|
|
59
|
+
*/
|
|
60
|
+
export async function resolveEnvFile(file, environment) {
|
|
61
|
+
const p = pathFor(environment);
|
|
62
|
+
const expanded = /^~(?=$|[/\\])/.test(file) ? p.join(environment.homedir, file.slice(1)) : file;
|
|
63
|
+
const absolute = p.resolve(environment.cwd, expanded);
|
|
64
|
+
let isFile = false;
|
|
65
|
+
try {
|
|
66
|
+
isFile = (await fs.stat(absolute)).isFile();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Reported below, with the resolved path, which is the useful part.
|
|
70
|
+
}
|
|
71
|
+
if (!isFile)
|
|
72
|
+
throw new Error(`--env-file ${absolute} does not exist or is not a file.`);
|
|
73
|
+
return absolute;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/mcpClients/launch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,wDAAwD;AACxD,MAAM,CAAC,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAEzD,sFAAsF;AACtF,MAAM,UAAU,kBAAkB;IAChC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;AACrG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAmB;IACjD,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,QAAkB,EAClB,UAA6B,EAC7B,IAA6C;IAE7C,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,UAAU;YACb,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC;QAC3E,KAAK,KAAK;YACR,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC;QAC7E,KAAK,QAAQ;YACX,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC;IACxD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,WAA+B;IAChF,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChG,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACtD,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;IACtE,CAAC;IACD,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,QAAQ,mCAAmC,CAAC,CAAC;IACxF,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ClientId, InstallEnvironment, InstallPlan, InstallScope, ServerSpec } from './types.js';
|
|
2
|
+
/** @returns Whether every client accepts `name` as an entry name. */
|
|
3
|
+
export declare function isValidServerName(name: string): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Works out what installing `spec` into one client means: which file, or which CLI command. Reads
|
|
6
|
+
* nothing and writes nothing; `applyPlan` does the rest.
|
|
7
|
+
*
|
|
8
|
+
* @param spec - The entry name and the process to launch.
|
|
9
|
+
* @param client - The client to install into.
|
|
10
|
+
* @param scope - The client's personal configuration, or its repo-level file under `environment.cwd`.
|
|
11
|
+
* @param environment - Platform, home directory, working directory and environment variables.
|
|
12
|
+
* @returns The plan. An unsupported client/scope pair, or an invalid name, plans as `unsupported`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function planInstall(spec: ServerSpec, client: ClientId, scope: InstallScope, environment: InstallEnvironment): InstallPlan;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { CLIENTS, pathFor } from './clients.js';
|
|
17
|
+
/** Entry names every client accepts: Codex, the strictest, takes nothing else as a TOML bare key. */
|
|
18
|
+
const SERVER_NAME = /^[A-Za-z0-9_-]+$/;
|
|
19
|
+
/** @returns Whether every client accepts `name` as an entry name. */
|
|
20
|
+
export function isValidServerName(name) {
|
|
21
|
+
return SERVER_NAME.test(name);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Works out what installing `spec` into one client means: which file, or which CLI command. Reads
|
|
25
|
+
* nothing and writes nothing; `applyPlan` does the rest.
|
|
26
|
+
*
|
|
27
|
+
* @param spec - The entry name and the process to launch.
|
|
28
|
+
* @param client - The client to install into.
|
|
29
|
+
* @param scope - The client's personal configuration, or its repo-level file under `environment.cwd`.
|
|
30
|
+
* @param environment - Platform, home directory, working directory and environment variables.
|
|
31
|
+
* @returns The plan. An unsupported client/scope pair, or an invalid name, plans as `unsupported`.
|
|
32
|
+
*/
|
|
33
|
+
export function planInstall(spec, client, scope, environment) {
|
|
34
|
+
const base = { client, scope, name: spec.name };
|
|
35
|
+
const definition = CLIENTS[client];
|
|
36
|
+
if (!isValidServerName(spec.name)) {
|
|
37
|
+
return {
|
|
38
|
+
...base,
|
|
39
|
+
kind: 'unsupported',
|
|
40
|
+
reason: `"${spec.name}" is not a valid server name. Use only letters, digits, "-" and "_".`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const launch = { command: spec.command, args: [...spec.args] };
|
|
44
|
+
if (scope === 'project') {
|
|
45
|
+
if (!definition.project) {
|
|
46
|
+
return {
|
|
47
|
+
...base,
|
|
48
|
+
kind: 'unsupported',
|
|
49
|
+
reason: `${definition.displayName} has no project-level configuration. Use --scope user.`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const { file, key, typed } = definition.project;
|
|
53
|
+
return {
|
|
54
|
+
...base,
|
|
55
|
+
kind: 'file',
|
|
56
|
+
file: pathFor(environment).resolve(environment.cwd, file),
|
|
57
|
+
key,
|
|
58
|
+
entry: typed ? { type: 'stdio', ...launch } : launch,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const location = definition.user(environment);
|
|
62
|
+
if ('cli' in location) {
|
|
63
|
+
if (location.cli === 'claude') {
|
|
64
|
+
return {
|
|
65
|
+
...base,
|
|
66
|
+
kind: 'cli',
|
|
67
|
+
executable: 'claude',
|
|
68
|
+
addArgs: ['mcp', 'add-json', spec.name, JSON.stringify({ type: 'stdio', ...launch }), '--scope', 'user'],
|
|
69
|
+
removeArgs: ['mcp', 'remove', spec.name, '--scope', 'user'],
|
|
70
|
+
launch,
|
|
71
|
+
configFile: location.configFile,
|
|
72
|
+
configFormat: 'claude-json',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
...base,
|
|
77
|
+
kind: 'cli',
|
|
78
|
+
executable: 'codex',
|
|
79
|
+
addArgs: ['mcp', 'add', spec.name, '--', launch.command, ...launch.args],
|
|
80
|
+
removeArgs: ['mcp', 'remove', spec.name],
|
|
81
|
+
launch,
|
|
82
|
+
configFile: location.configFile,
|
|
83
|
+
configFormat: 'codex-toml',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
...base,
|
|
88
|
+
kind: 'file',
|
|
89
|
+
file: location.file,
|
|
90
|
+
key: location.key,
|
|
91
|
+
entry: location.typed ? { type: 'stdio', ...launch } : launch,
|
|
92
|
+
clientDir: location.clientDir,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=plan.js.map
|