Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,6 @@
"vitest": "^4.0.18"
},
"engines": {
"node": ">=18.0.0"
"node": ">=18.0.0 <26.0.0"
}
}
28 changes: 28 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading