diff --git a/package-lock.json b/package-lock.json index b04e4b6..f96825d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "vitest": "^4.0.18" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0 <26.0.0" } }, "node_modules/@ast-grep/lang-bash": { diff --git a/package.json b/package.json index 2cf13b1..5d7d91d 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,6 @@ "vitest": "^4.0.18" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0 <26.0.0" } } diff --git a/src/index.ts b/src/index.ts index ebfdb90..953bf85 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,34 @@ // SPDX-License-Identifier: AGPL-3.0-only // Copyright (C) 2026 Giancarlo Erra - Altaire Limited +// Pre-flight: refuse to start on Node versions known to break @qdrant/js-client-rest. +// The qdrant client pins undici ^6 and constructs an undici.Agent it passes to Node's +// built-in fetch() as a dispatcher. Node 26+ ships a stricter undici whose dispatcher +// hook validation rejects the v6 Agent's contract — surfaces as +// `UND_ERR_INVALID_ARG: invalid onError method` on the first qdrant request. +// (The imports below are evaluated before this check at runtime per ESM semantics, +// but qdrant-js's module-init is side-effect-light — only an actual request triggers +// the undici path — so exiting here is enough to spare users the opaque error later.) +// Tracked upstream: https://github.com/qdrant/qdrant-js/issues/134 +// Upstream PRs under discussion: qdrant/qdrant-js#123 (undici major upgrade) and +// qdrant/qdrant-js#128 (inject fetch into REST transport). If either lands — or any +// other fix supersedes them — raise the upper bound in package.json's `engines.node` +// and remove this check. +const nodeMajor = Number.parseInt(process.versions.node.split(".")[0], 10); +if (Number.isFinite(nodeMajor) && nodeMajor >= 26) { + // fs.writeSync(2, …) is the canonical Node idiom for "print fatal error then die": + // blocking (no truncation when stderr is piped — every MCP host pipes stderr) and + // synchronous (so process.exit(1) runs before any further top-level code). + const msg = + `socraticode: Node ${process.versions.node} is not supported.\n` + + " @qdrant/js-client-rest is incompatible with the undici bundled in Node 26+.\n" + + " Use Node 22.x (via nvm: `nvm install 22 && nvm use 22`, or `brew install node@22` on macOS).\n" + + " See https://github.com/qdrant/qdrant-js/issues/134.\n"; + writeSync(2, msg); + process.exit(1); +} + +import { writeSync } from "node:fs"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod";