@launchdarkly/mcp-server 0.7.0 → 0.7.1
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 +9 -9
- package/bin/mcp-server.js +145 -5
- package/bin/mcp-server.js.map +8 -7
- package/dist/commonjs/lib/config.d.ts +2 -2
- package/dist/commonjs/lib/config.js +2 -2
- package/dist/commonjs/mcp-server/cli/start/impl.d.ts.map +1 -1
- package/dist/commonjs/mcp-server/cli/start/impl.js +7 -0
- package/dist/commonjs/mcp-server/cli/start/impl.js.map +1 -1
- package/dist/commonjs/mcp-server/cli/start/tls-blocklist.d.ts +48 -0
- package/dist/commonjs/mcp-server/cli/start/tls-blocklist.d.ts.map +1 -0
- package/dist/commonjs/mcp-server/cli/start/tls-blocklist.js +206 -0
- package/dist/commonjs/mcp-server/cli/start/tls-blocklist.js.map +1 -0
- package/dist/commonjs/mcp-server/mcp-server.js +1 -1
- package/dist/commonjs/mcp-server/server.js +1 -1
- package/dist/esm/lib/config.d.ts +2 -2
- package/dist/esm/lib/config.js +2 -2
- package/dist/esm/mcp-server/cli/start/impl.d.ts.map +1 -1
- package/dist/esm/mcp-server/cli/start/impl.js +7 -0
- package/dist/esm/mcp-server/cli/start/impl.js.map +1 -1
- package/dist/esm/mcp-server/cli/start/tls-blocklist.d.ts +48 -0
- package/dist/esm/mcp-server/cli/start/tls-blocklist.d.ts.map +1 -0
- package/dist/esm/mcp-server/cli/start/tls-blocklist.js +192 -0
- package/dist/esm/mcp-server/cli/start/tls-blocklist.js.map +1 -0
- package/dist/esm/mcp-server/mcp-server.js +1 -1
- package/dist/esm/mcp-server/server.js +1 -1
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/lib/config.ts +2 -2
- package/src/mcp-server/cli/start/impl.ts +11 -0
- package/src/mcp-server/cli/start/tls-blocklist.ts +252 -0
- package/src/mcp-server/mcp-server.ts +1 -1
- package/src/mcp-server/server.ts +1 -1
- package/test/tls-blocklist.test.mjs +314 -0
package/README.md
CHANGED
|
@@ -35,6 +35,15 @@ The local [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) serve
|
|
|
35
35
|
|
|
36
36
|
This MCP server can be installed in any AI client that supports the MCP protocol. Refer to your AI client's instructions if it isn't listed here.
|
|
37
37
|
|
|
38
|
+
### Runtime requirements
|
|
39
|
+
|
|
40
|
+
> [!IMPORTANT]
|
|
41
|
+
> Some eMudhra intermediate certificate authorities were compromised in October 2026 ([advisory](https://emudhra.we-have-a-problem.com/)). Anyone on your network who holds one of their keys could intercept the server's connection to LaunchDarkly and read your API access token.
|
|
42
|
+
>
|
|
43
|
+
> - **npx and local installs:** use Node.js **22.23.4, 24.22.1, 26.12.1 or later**. These releases reject the compromised intermediates. The server also rejects them itself and prints a warning on startup when your runtime does not.
|
|
44
|
+
> - **Docker:** use image 0.7.1 or later. Those run on Node.js 24.22.1; 0.7.0 and earlier run on Node.js 24.2.0.
|
|
45
|
+
> - **Standalone binaries:** stop using the `mcp-server-bun-*` binaries attached to releases v0.6.1 and earlier. They embed a Bun runtime that does not reject the compromised intermediates, and they are no longer published. Switch to npx with a patched Node.js or to Docker.
|
|
46
|
+
|
|
38
47
|
### Cursor installation steps
|
|
39
48
|
|
|
40
49
|
Create a `.cursor/mcp.json` file in your project root with the following content:
|
|
@@ -128,15 +137,6 @@ Specify your API key as found on LaunchDarkly's Authorization page.
|
|
|
128
137
|
|
|
129
138
|
For more information, see the [GitHub Copilot CLI documentation](https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli).
|
|
130
139
|
|
|
131
|
-
### Standalone binary installation steps
|
|
132
|
-
|
|
133
|
-
You can also run the MCP server as a standalone binary with no additional dependencies. You must pull these binaries from available GitHub releases while specifying the appropriate `tag` value:
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
curl -L -o mcp-server https://github.com/launchdarkly/mcp-server/releases/download/{tag}/mcp-server-bun-darwin-arm64 && \
|
|
137
|
-
chmod +x mcp-server
|
|
138
|
-
```
|
|
139
|
-
|
|
140
140
|
### Installation steps from a local clone
|
|
141
141
|
|
|
142
142
|
You can also run the MCP server locally by cloning this repository. Once cloned, you'll need to install dependencies (`npm install`) and build the server (`npm run build`).
|
package/bin/mcp-server.js
CHANGED
|
@@ -54339,9 +54339,9 @@ var init_config = __esm(() => {
|
|
|
54339
54339
|
SDK_METADATA = {
|
|
54340
54340
|
language: "typescript",
|
|
54341
54341
|
openapiDocVersion: "2.0",
|
|
54342
|
-
sdkVersion: "0.7.
|
|
54342
|
+
sdkVersion: "0.7.1",
|
|
54343
54343
|
genVersion: "2.845.15",
|
|
54344
|
-
userAgent: "speakeasy-sdk/typescript 0.7.
|
|
54344
|
+
userAgent: "speakeasy-sdk/typescript 0.7.1 2.845.15 2.0 @launchdarkly/mcp-server"
|
|
54345
54345
|
};
|
|
54346
54346
|
});
|
|
54347
54347
|
|
|
@@ -62064,7 +62064,7 @@ var init_featureFlagsPatch2 = __esm(() => {
|
|
|
62064
62064
|
function createMCPServer(deps) {
|
|
62065
62065
|
const server = new McpServer({
|
|
62066
62066
|
name: "LaunchDarkly",
|
|
62067
|
-
version: "0.7.
|
|
62067
|
+
version: "0.7.1"
|
|
62068
62068
|
});
|
|
62069
62069
|
const client = new LaunchDarklyCore({
|
|
62070
62070
|
apiKey: deps.apiKey,
|
|
@@ -62150,12 +62150,151 @@ function requireBearerToken(token) {
|
|
|
62150
62150
|
}
|
|
62151
62151
|
var init_sse_security = () => {};
|
|
62152
62152
|
|
|
62153
|
+
// src/mcp-server/cli/start/tls-blocklist.ts
|
|
62154
|
+
import { createHash, X509Certificate } from "node:crypto";
|
|
62155
|
+
import tls from "node:tls";
|
|
62156
|
+
function normalizeName(value) {
|
|
62157
|
+
return value.normalize("NFKC").toLowerCase().replace(/\s+/g, " ").trim();
|
|
62158
|
+
}
|
|
62159
|
+
function isBlockedIssuerName(cn) {
|
|
62160
|
+
const names = Array.isArray(cn) ? cn : cn === undefined ? [] : [cn];
|
|
62161
|
+
return names.some((name) => BLOCKED_ISSUER_CN.test(normalizeName(name)));
|
|
62162
|
+
}
|
|
62163
|
+
function spkiSha256(raw) {
|
|
62164
|
+
const spki = new X509Certificate(raw).publicKey.export({
|
|
62165
|
+
type: "spki",
|
|
62166
|
+
format: "der"
|
|
62167
|
+
});
|
|
62168
|
+
return createHash("sha256").update(spki).digest("hex");
|
|
62169
|
+
}
|
|
62170
|
+
function findBlockedCertificate(cert) {
|
|
62171
|
+
const seen = new Set;
|
|
62172
|
+
for (let current = cert;current && !seen.has(current); current = current.issuerCertificate) {
|
|
62173
|
+
seen.add(current);
|
|
62174
|
+
if (isBlockedIssuerName(current.issuer?.CN)) {
|
|
62175
|
+
return `certificate issued by "${String(current.issuer?.CN)}"`;
|
|
62176
|
+
}
|
|
62177
|
+
if (current.raw && current.raw.length > 0) {
|
|
62178
|
+
const hash = spkiSha256(current.raw);
|
|
62179
|
+
if (BLOCKED_SPKI_SHA256.has(hash)) {
|
|
62180
|
+
return `certificate with blocked public key sha256/${hash}`;
|
|
62181
|
+
}
|
|
62182
|
+
}
|
|
62183
|
+
}
|
|
62184
|
+
return;
|
|
62185
|
+
}
|
|
62186
|
+
function checkServerIdentity(hostname, cert) {
|
|
62187
|
+
let blocked;
|
|
62188
|
+
try {
|
|
62189
|
+
blocked = findBlockedCertificate(cert);
|
|
62190
|
+
} catch (err) {
|
|
62191
|
+
blocked = `certificate that could not be parsed (${String(err)})`;
|
|
62192
|
+
}
|
|
62193
|
+
if (blocked) {
|
|
62194
|
+
return Object.assign(new Error(`Refusing TLS connection to ${hostname}: the chain contains a ${blocked}, ` + "which belongs to a compromised eMudhra intermediate CA."), { code: BLOCKED_CERT_ERROR_CODE });
|
|
62195
|
+
}
|
|
62196
|
+
return defaultCheckServerIdentity(hostname, cert);
|
|
62197
|
+
}
|
|
62198
|
+
function installTlsBlocklist() {
|
|
62199
|
+
if (installed)
|
|
62200
|
+
return;
|
|
62201
|
+
installed = true;
|
|
62202
|
+
if (isBun()) {
|
|
62203
|
+
const originalFetch = globalThis.fetch;
|
|
62204
|
+
const wrapped = (input, init) => {
|
|
62205
|
+
const tlsOptions = init?.tls;
|
|
62206
|
+
const callerCheck = tlsOptions?.checkServerIdentity;
|
|
62207
|
+
const combined = callerCheck ? (hostname, cert) => checkServerIdentity(hostname, cert) ?? callerCheck(hostname, cert) : checkServerIdentity;
|
|
62208
|
+
return originalFetch(input, {
|
|
62209
|
+
...init,
|
|
62210
|
+
tls: { ...tlsOptions, checkServerIdentity: combined }
|
|
62211
|
+
});
|
|
62212
|
+
};
|
|
62213
|
+
globalThis.fetch = Object.assign(wrapped, originalFetch);
|
|
62214
|
+
} else {
|
|
62215
|
+
tls.checkServerIdentity = checkServerIdentity;
|
|
62216
|
+
}
|
|
62217
|
+
}
|
|
62218
|
+
function isBun() {
|
|
62219
|
+
return typeof globalThis.Bun !== "undefined";
|
|
62220
|
+
}
|
|
62221
|
+
function parseVersion(version2) {
|
|
62222
|
+
const match2 = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version2);
|
|
62223
|
+
if (!match2)
|
|
62224
|
+
return;
|
|
62225
|
+
return [Number(match2[1]), Number(match2[2]), Number(match2[3])];
|
|
62226
|
+
}
|
|
62227
|
+
function compareVersions(a, b2) {
|
|
62228
|
+
return a[0] - b2[0] || a[1] - b2[1] || a[2] - b2[2];
|
|
62229
|
+
}
|
|
62230
|
+
function isNodeVersionPatched(version2) {
|
|
62231
|
+
const parsed = parseVersion(version2);
|
|
62232
|
+
if (!parsed)
|
|
62233
|
+
return false;
|
|
62234
|
+
const fixed = FIXED_NODE_VERSIONS[parsed[0]];
|
|
62235
|
+
if (fixed)
|
|
62236
|
+
return compareVersions(parsed, fixed) >= 0;
|
|
62237
|
+
const newestLine = Math.max(...Object.keys(FIXED_NODE_VERSIONS).map(Number));
|
|
62238
|
+
return parsed[0] > newestLine;
|
|
62239
|
+
}
|
|
62240
|
+
function isBunVersionPatched(version2) {
|
|
62241
|
+
if (FIXED_BUN_VERSION === undefined)
|
|
62242
|
+
return false;
|
|
62243
|
+
const parsed = parseVersion(version2);
|
|
62244
|
+
const fixed = parseVersion(FIXED_BUN_VERSION);
|
|
62245
|
+
return !!parsed && !!fixed && compareVersions(parsed, fixed) >= 0;
|
|
62246
|
+
}
|
|
62247
|
+
function unpatchedRuntimeWarning(versions2 = process.versions) {
|
|
62248
|
+
let runtime;
|
|
62249
|
+
let upgrade;
|
|
62250
|
+
if (versions2.bun !== undefined) {
|
|
62251
|
+
if (isBunVersionPatched(versions2.bun))
|
|
62252
|
+
return;
|
|
62253
|
+
runtime = `Bun ${versions2.bun}`;
|
|
62254
|
+
upgrade = "run the server with Node.js 22.23.4, 24.22.1, 26.12.1 or later, or use the Docker image";
|
|
62255
|
+
} else if (versions2.node !== undefined) {
|
|
62256
|
+
if (isNodeVersionPatched(versions2.node))
|
|
62257
|
+
return;
|
|
62258
|
+
runtime = `Node.js ${versions2.node}`;
|
|
62259
|
+
upgrade = "upgrade to Node.js 22.23.4, 24.22.1, 26.12.1 or later";
|
|
62260
|
+
} else {
|
|
62261
|
+
return;
|
|
62262
|
+
}
|
|
62263
|
+
return `WARNING: ${runtime} does not block the compromised eMudhra intermediate CAs. ` + "The LaunchDarkly MCP server rejects them for its own requests, but the runtime does not; " + `${upgrade}. See https://github.com/launchdarkly/mcp-server#runtime-requirements
|
|
62264
|
+
`;
|
|
62265
|
+
}
|
|
62266
|
+
var BLOCKED_SPKI_SHA256, BLOCKED_ISSUER_CN, FIXED_NODE_VERSIONS, FIXED_BUN_VERSION = undefined, BLOCKED_CERT_ERROR_CODE = "ERR_TLS_CERT_BLOCKED", defaultCheckServerIdentity, installed = false;
|
|
62267
|
+
var init_tls_blocklist = __esm(() => {
|
|
62268
|
+
BLOCKED_SPKI_SHA256 = new Set([
|
|
62269
|
+
"43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b",
|
|
62270
|
+
"49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f",
|
|
62271
|
+
"5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0",
|
|
62272
|
+
"63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8",
|
|
62273
|
+
"92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30",
|
|
62274
|
+
"e0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41",
|
|
62275
|
+
"ebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105",
|
|
62276
|
+
"ec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6"
|
|
62277
|
+
]);
|
|
62278
|
+
BLOCKED_ISSUER_CN = /^em (dv|ov|ev) (tls|cs) ca - g3[bc]$/;
|
|
62279
|
+
FIXED_NODE_VERSIONS = {
|
|
62280
|
+
22: [22, 23, 4],
|
|
62281
|
+
24: [24, 22, 1],
|
|
62282
|
+
26: [26, 12, 1]
|
|
62283
|
+
};
|
|
62284
|
+
defaultCheckServerIdentity = tls.checkServerIdentity;
|
|
62285
|
+
});
|
|
62286
|
+
|
|
62153
62287
|
// src/mcp-server/cli/start/impl.ts
|
|
62154
62288
|
var exports_impl = {};
|
|
62155
62289
|
__export(exports_impl, {
|
|
62156
62290
|
main: () => main
|
|
62157
62291
|
});
|
|
62158
62292
|
async function main(flags) {
|
|
62293
|
+
installTlsBlocklist();
|
|
62294
|
+
const runtimeWarning = unpatchedRuntimeWarning();
|
|
62295
|
+
if (runtimeWarning) {
|
|
62296
|
+
process.stderr.write(runtimeWarning);
|
|
62297
|
+
}
|
|
62159
62298
|
flags.env?.forEach(([key, value]) => {
|
|
62160
62299
|
process.env[key] = value;
|
|
62161
62300
|
});
|
|
@@ -62286,6 +62425,7 @@ var init_impl = __esm(() => {
|
|
|
62286
62425
|
init_console_logger();
|
|
62287
62426
|
init_server3();
|
|
62288
62427
|
init_sse_security();
|
|
62428
|
+
init_tls_blocklist();
|
|
62289
62429
|
});
|
|
62290
62430
|
|
|
62291
62431
|
// node_modules/@stricli/core/dist/index.js
|
|
@@ -63386,7 +63526,7 @@ var routes = an({
|
|
|
63386
63526
|
var app = He(routes, {
|
|
63387
63527
|
name: "mcp",
|
|
63388
63528
|
versionInfo: {
|
|
63389
|
-
currentVersion: "0.7.
|
|
63529
|
+
currentVersion: "0.7.1"
|
|
63390
63530
|
}
|
|
63391
63531
|
});
|
|
63392
63532
|
zt(app, process4.argv.slice(2), buildContext(process4));
|
|
@@ -63394,5 +63534,5 @@ export {
|
|
|
63394
63534
|
app
|
|
63395
63535
|
};
|
|
63396
63536
|
|
|
63397
|
-
//# debugId=
|
|
63537
|
+
//# debugId=7226CCD8FB08BAF664756E2164756E21
|
|
63398
63538
|
//# sourceMappingURL=mcp-server.js.map
|