Skip to content

4.x: vendored deps/ is not isolated from user site-packages - a torch-ecosystem mismatch stops the MCP server from starting (Windows) #621

Closed

Description

@BillOTheWild

Summary

On a machine that already has a PyTorch stack in its user site-packages, Cortex 4.x
fails to start. The MCP server exits during import and the host reports
CONNECTION_CLOSED. The lifecycle hooks survive but lose semantic embeddings.

The cause is that deps/ vendors its own torch while scripts/launcher.py leaves
the user's site-packages on sys.path. torchvision and torchaudio are not
vendored, so they resolve from user site-packages — where they are built against a
different torch than the one deps/ ships.

3.x was not affected. Its deps/ vendored only psycopg_pool and
typing_extensions, so the whole torch stack came from user site-packages and stayed
internally consistent.

Environment

  • Cortex hypermnesia-mcp 4.23.1, installed via claude plugin install (upgraded from 3.24.1)
  • Windows 11 Pro 26200, Python 3.13.4
  • Backend: PostgreSQL 15.6 + pgvector (all Postgres checks pass)

Vendored in deps/:

Package Version
torch 2.13.0+cpu
transformers 5.16.1
sentence-transformers 6.0.1
torchvision absent
torchaudio absent

Present in user site-packages (a coherent CUDA set):

Package Version
torch 2.11.0+cu128
torchvision 0.26.0+cu128
torchaudio 2.11.0+cu128

Symptoms

scripts/setup.py reports:

  [ok] PostgreSQL connection
  [ok] Extensions (pgvector, pg_trgm)
  [ok] PL/pgSQL recall_memories()
  [FAIL] sentence-transformers
  [ok] FlashRank reranker

The MCP server never completes startup, so the host closes the connection.

Root cause

scripts/launcher.py puts deps/ first on the path:

for p in [plugin_root, deps_dir]:
    if p not in sys.path:
        sys.path.insert(0, p)

torch then comes from deps/ (2.13.0+cpu) but torchvision still comes from user
site-packages (0.26.0+cu128, built for torch 2.11.0). Importing them together aborts:

RuntimeError: operator torchvision::nms does not exist

transformers resolves PreTrainedModel lazily and re-raises the cause as a
ModuleNotFoundError, which hides the real error:

ModuleNotFoundError: Could not import module 'PreTrainedModel'.
Are this object's requirements defined correctly?

Reproduce with the vendored deps on the path:

PYTHONPATH="<plugin_root>/deps" python -c "import sentence_transformers"

Two fixes that do not work

Vendoring a matching torchvision. Installing torchvision==0.28.0+cpu (pip's
own resolution for torch 2.13.0) into deps/ clears the nms error. The next
package in the chain then fails:

OSError: Could not load this library:
...\AppData\Roaming\Python\Python313\site-packages\torchaudio\lib\libtorchaudio.pyd

Every torch-ecosystem package in user site-packages leaks in turn. Matching versions
one by one does not converge.

Disabling user site-packages alone (python -s, or PYTHONNOUSERSITE=1 in the
plugin's mcpServers.env). sentence-transformers then imports correctly, but the
server dies earlier:

[cortex-launcher] Failed to run mcp_server: No module named 'pywintypes'

deps/ does contain pywin32, including deps/win32/lib/pywintypes.py and
deps/pywin32_system32/pywintypes313.dll. It is reached through deps/pywin32.pth:

win32
win32\lib
pythonwin
import pywin32_bootstrap

.pth files are only processed for site directories. A directory added through
PYTHONPATH or sys.path.insert does not get them. So cutting off user
site-packages removes the only pywin32 that was actually reachable.

Fix that works

Both halves are needed: make deps/ a real site directory so its .pth files run,
then drop user site-packages. In scripts/launcher.py, right after the existing
sys.path.insert loop:

import site as _site

_site.addsitedir(deps_dir)
try:
    _user_site = _site.getusersitepackages()
except Exception:
    _user_site = None
if _user_site:
    _dropped = os.path.normcase(os.path.abspath(_user_site))
    sys.path[:] = [
        q for q in sys.path
        if os.path.normcase(os.path.abspath(q)) != _dropped
    ]

Placing it in the launcher covers the MCP server and all 11 lifecycle hooks at once,
without depending on the shell. A PYTHONNOUSERSITE entry in mcpServers.env does
not reach the hooks, which inherit the host's environment.

Verified on the environment above, after the change:

  • MCP initialize handshake returns the full capability set
  • mcp_server.hooks.session_start exits 0 and injects memory context
  • SentenceTransformer.encode() produces a real 384-dimension vector

This is a local workaround, not a design proposal. A dedicated virtualenv for deps/
would achieve the same isolation more conventionally.

Secondary: the installer blames the wrong component

When the verification above fails on sentence-transformers, install-plugin.sh
still ends with:

scripts/setup.py failed. PostgreSQL must be installed and running first

All four PostgreSQL checks had passed in the same run. The message sends the reader
to a working component. Reporting the check that actually failed would help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions