fix(chroma): pin onnxruntime>=1.20 + protobuf<7 to fix INVALID_PROTOBUF on macOS arm64

The shipped all-MiniLM-L6-v2 model has pytorch-2.0 IR. chroma-mcp 0.2.6
transitively depends on `chromadb>=1.0.16` which only requires
`onnxruntime>=1.14.1` — uv can therefore resolve to an onnxruntime old
enough to fail every embedding add with `[ONNXRuntimeError] : 7 :
INVALID_PROTOBUF` on macOS arm64 / Python 3.13. Semantic search silently
degraded to FTS-only and smart backfill broke (#2371).

Path B (override) was required because chroma-mcp 0.2.6 is the latest
PyPI release — no upstream bump exists.

Inject `--with onnxruntime>=1.20 --with protobuf<7` into the uvx spawn
args (both persistent and remote modes). The protobuf cap is essential:
forcing only `onnxruntime>=1.20` causes uv to re-resolve and land on
protobuf 7.x, which trips opentelemetry's `_pb2` stubs with `TypeError:
Descriptors cannot be created directly` because they were generated
with protoc <3.19. Capping below 7 lands on protobuf 6.x which
opentelemetry tolerates.

Verified end-to-end: ONNX model loads, embeddings produce a 384-dim
vector, PersistentClient init / add / query roundtrip succeeds:

    uvx --python 3.13 --with "onnxruntime>=1.20" --with "protobuf<7" \
        chroma-mcp==0.2.6 --help     # clean
    # programmatic test: onnxruntime 1.26.0, protobuf 6.33.6,
    # embedding ok 384, query ok ids=[['1']]

Fixes #2371.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-05-09 01:25:39 -07:00
parent 8cb73b8c07
commit ecd5b80297

View File

@@ -23,6 +23,26 @@ const CHROMA_SUPERVISOR_ID = 'chroma-mcp';
const CHROMA_MCP_PINNED_VERSION = '0.2.6';
// Override transitive dep resolutions for chroma-mcp 0.2.6 (issue #2371).
//
// Why onnxruntime>=1.20: the shipped all-MiniLM-L6-v2 model has pytorch-2.0
// IR. Older onnxruntime versions can't parse it and fail every embedding
// add with `[ONNXRuntimeError] : 7 : INVALID_PROTOBUF`. uv may otherwise
// resolve to a too-old onnxruntime on macOS arm64 / Python 3.13 depending
// on cache state, so we force a floor.
//
// Why protobuf<7: protobuf 7.x's stricter generated-file check rejects
// opentelemetry's _pb2 stubs (generated with protoc <3.19), throwing
// `TypeError: Descriptors cannot be created directly` at chromadb import.
// Capping below 7 lands on protobuf 6.x which opentelemetry tolerates.
//
// These pins are runtime-only (uvx --with) so we don't have to fork
// chroma-mcp upstream — they apply only to claude-mem's spawned subprocess.
const CHROMA_MCP_DEP_OVERRIDES: ReadonlyArray<string> = [
'onnxruntime>=1.20',
'protobuf<7',
];
export class ChromaMcpManager {
private static instance: ChromaMcpManager | null = null;
private client: Client | null = null;
@@ -158,6 +178,8 @@ export class ChromaMcpManager {
const chromaMode = settings.CLAUDE_MEM_CHROMA_MODE || 'local';
const pythonVersion = process.env.CLAUDE_MEM_PYTHON_VERSION || settings.CLAUDE_MEM_PYTHON_VERSION || '3.13';
const depOverrideFlags = CHROMA_MCP_DEP_OVERRIDES.flatMap(spec => ['--with', spec]);
if (chromaMode === 'remote') {
const chromaHost = settings.CLAUDE_MEM_CHROMA_HOST || '127.0.0.1';
const chromaPort = settings.CLAUDE_MEM_CHROMA_PORT || '8000';
@@ -168,6 +190,7 @@ export class ChromaMcpManager {
const args = [
'--python', pythonVersion,
...depOverrideFlags,
`chroma-mcp==${CHROMA_MCP_PINNED_VERSION}`,
'--client-type', 'http',
'--host', chromaHost,
@@ -193,6 +216,7 @@ export class ChromaMcpManager {
return [
'--python', pythonVersion,
...depOverrideFlags,
`chroma-mcp==${CHROMA_MCP_PINNED_VERSION}`,
'--client-type', 'persistent',
'--data-dir', DEFAULT_CHROMA_DATA_DIR.replace(/\\/g, '/')