@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.
Files changed (33) hide show
  1. package/README.md +9 -9
  2. package/bin/mcp-server.js +145 -5
  3. package/bin/mcp-server.js.map +8 -7
  4. package/dist/commonjs/lib/config.d.ts +2 -2
  5. package/dist/commonjs/lib/config.js +2 -2
  6. package/dist/commonjs/mcp-server/cli/start/impl.d.ts.map +1 -1
  7. package/dist/commonjs/mcp-server/cli/start/impl.js +7 -0
  8. package/dist/commonjs/mcp-server/cli/start/impl.js.map +1 -1
  9. package/dist/commonjs/mcp-server/cli/start/tls-blocklist.d.ts +48 -0
  10. package/dist/commonjs/mcp-server/cli/start/tls-blocklist.d.ts.map +1 -0
  11. package/dist/commonjs/mcp-server/cli/start/tls-blocklist.js +206 -0
  12. package/dist/commonjs/mcp-server/cli/start/tls-blocklist.js.map +1 -0
  13. package/dist/commonjs/mcp-server/mcp-server.js +1 -1
  14. package/dist/commonjs/mcp-server/server.js +1 -1
  15. package/dist/esm/lib/config.d.ts +2 -2
  16. package/dist/esm/lib/config.js +2 -2
  17. package/dist/esm/mcp-server/cli/start/impl.d.ts.map +1 -1
  18. package/dist/esm/mcp-server/cli/start/impl.js +7 -0
  19. package/dist/esm/mcp-server/cli/start/impl.js.map +1 -1
  20. package/dist/esm/mcp-server/cli/start/tls-blocklist.d.ts +48 -0
  21. package/dist/esm/mcp-server/cli/start/tls-blocklist.d.ts.map +1 -0
  22. package/dist/esm/mcp-server/cli/start/tls-blocklist.js +192 -0
  23. package/dist/esm/mcp-server/cli/start/tls-blocklist.js.map +1 -0
  24. package/dist/esm/mcp-server/mcp-server.js +1 -1
  25. package/dist/esm/mcp-server/server.js +1 -1
  26. package/jsr.json +1 -1
  27. package/package.json +2 -1
  28. package/src/lib/config.ts +2 -2
  29. package/src/mcp-server/cli/start/impl.ts +11 -0
  30. package/src/mcp-server/cli/start/tls-blocklist.ts +252 -0
  31. package/src/mcp-server/mcp-server.ts +1 -1
  32. package/src/mcp-server/server.ts +1 -1
  33. 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.0",
54342
+ sdkVersion: "0.7.1",
54343
54343
  genVersion: "2.845.15",
54344
- userAgent: "speakeasy-sdk/typescript 0.7.0 2.845.15 2.0 @launchdarkly/mcp-server"
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.0"
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.0"
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=1BB1876EEFF45EEC64756E2164756E21
63537
+ //# debugId=7226CCD8FB08BAF664756E2164756E21
63398
63538
  //# sourceMappingURL=mcp-server.js.map