Skip to content

Cursor + Semvec Coding

Semvec's coding stack ships a FastMCP server (semvec.coding.mcp_server) that exposes the CodingEngine as MCP tools — persistent code memory, anti-resonance error checks, and compacted-context retrieval. Because Cursor speaks MCP natively, the same server that plugs into Claude Code also plugs into Cursor with one JSON file. This page walks through the full setup.

What you get

  • Persistent memory across sessions. The LiteralCache carries decisions, invariants, error patterns and code-pointer metadata between Cursor sessions. Reopening the project on Monday picks up where Friday left off.
  • Anti-resonance. Before the agent proposes a non-trivial change, Cursor can ask pss_check_anti_resonance whether a similar idea has already failed. Lets you avoid re-walking into the same trap.
  • Compacted context as a first-message system prompt. A 150–350 token block summarising prior work, fed in via pss_get_context so Cursor doesn't have to re-read the entire repo to remember what you did last.
  • Six MCP tools, all stdio-transport. Same code path as the Claude Code integration — no Cursor-specific glue.

Prerequisites

  • Cursor 0.45 or newer (older builds had a partial MCP implementation; double-check your build).
  • Python 3.10+ on your PATH. Cursor invokes the MCP server as a subprocess.
  • A Python environment with semvec[coding] installed:
pip install "semvec[coding]" sentence-transformers

The sentence-transformers package is the recommended embedder backend; semvec refuses to fall back to hash-based pseudo-embeddings, so the server hard-fails at startup if no real embedder is reachable. See Embedders for alternatives.

Use a project-local venv

On macOS and Linux a project-local virtualenv keeps Cursor's subprocess isolated from your system Python. The MCP config below shows how to point Cursor at it explicitly.

Step 1 — Configure the MCP server

Create .cursor/mcp.json in the project root (or ~/.cursor/mcp.json for a global setup):

{
  "mcpServers": {
    "semvec": {
      "command": "python",
      "args": ["-m", "semvec.coding.mcp_server"],
      "env": {
        "SEMVEC_STATE_DIR": ".semvec",
        "SEMVEC_WORKSPACE": ".",
        "SEMVEC_EMBED_MODEL": "all-MiniLM-L6-v2",
        "SEMVEC_EMBED_DEVICE": "cpu",
        "SEMVEC_TELEMETRY_QUIET": "1"
      }
    }
  }
}

If you use a project-local venv, swap command for the absolute interpreter path so Cursor does not pick up a system Python:

"command": "/abs/path/to/project/.venv/bin/python",

On Windows that becomes \\abs\\path\\to\\project\\.venv\\Scripts\\python.exe (escape the backslashes inside JSON).

Environment variables

Variable Default Purpose
SEMVEC_STATE_DIR .semvec Where the engine persists LiteralCache, code pointers, and snapshots. Commit .semvec/ to git for cross-machine continuity, or add it to .gitignore for per-clone memory.
SEMVEC_WORKSPACE (unset) Repo root — used by pss_register_code to anchor file paths. Defaults to the directory Cursor invokes the server in.
SEMVEC_EMBED_MODEL all-MiniLM-L6-v2 Sentence-transformer model name. For German / multilingual repos use paraphrase-multilingual-mpnet-base-v2 (768d).
SEMVEC_EMBED_DEVICE cpu cpu, cuda, or mps.
SEMVEC_LICENSE_KEY (unset) Pro/Enterprise JWT — bypasses the community-tier update() and calculate_* rate limits. See Licensing.
SEMVEC_TELEMETRY_QUIET (unset) Set to 1 to silence the one-off telemetry stderr notice on first import.
SEMVEC_TELEMETRY (unset) Set to 0 to disable the anonymous init ping entirely.

After saving the file, restart Cursor. Open Settings → Cursor Settings → MCP and confirm that Semvec Coding appears with six tools (pss_get_context, pss_update, pss_check_anti_resonance, pss_register_code, pss_record_error, pss_save).

Step 2 — Replace Claude Code's lifecycle hooks with Cursor Rules

Claude Code triggers SessionStart and PreCompact hooks automatically; Cursor does not have equivalent lifecycle events. The clean substitute is a Cursor Rule that instructs the agent to invoke the MCP tools at the right moments.

Create .cursor/rules/semvec.mdc:

---
description: Semvec PSS persistent memory across coding sessions
alwaysApply: true
---

This project uses Semvec PSS for persistent coding memory between sessions.

# At session start
- Before answering the first user request in a session, call
  `pss_get_context(task=<short task description>)` and treat the
  returned block as authoritative prior context (decisions, code
  pointers, known error patterns, invariants). Do not ignore an
  invariant; if a request contradicts one, surface that conflict.

# During the session
- After each substantial action call exactly one of:
  - `pss_update(text=..., update_type="code_change", file_path=..., signature=...)`
    when you write or edit a file
  - `pss_update(text=..., update_type="error")` when a runtime / test
    error surfaces
  - `pss_record_error(error_text=..., source="test_failure")` when a
    pytest / jest / cargo run produces a FAILED line
- Before proposing a non-trivial change, call
  `pss_check_anti_resonance(proposal=<the change you intend>)` and
  bail out if it matches a known-bad pattern.

# Before the session ends
- If the user signals end-of-session ("we are done", commit message,
  end of feature) call `pss_save()` exactly once.

Do not narrate these calls — invoke them silently and weave their
output into your normal answer.

Drop the rule into version control. Cursor reads *.mdc files in .cursor/rules/ and treats them as project-level system instructions — they apply to every conversation in the project.

.cursorrules (legacy)

Older Cursor installations expect a single root-level .cursorrules file instead of .cursor/rules/*.mdc. The same body works there; copy the markdown content (without the YAML frontmatter) into .cursorrules.

Step 3 — Sanity-check

Open a fresh Cursor chat in the project and run:

List the MCP tools you have available.

You should see the six pss_* tools. Then:

Use pss_get_context to summarise prior work in this repo.

On a fresh state directory the response is a header-only block (Phase: initialization · Turn 0 · 0 memories). After a few real coding turns the same call returns a populated context.

Tool reference

The same six tools are exposed in Claude Code; Cursor calls them through stdio transport.

Tool Purpose
pss_get_context(task, invariants?, test_summary?) Token-budgeted summary of prior work — phase, top-K relevant memories, code pointers, anti-resonance warnings, plus your current invariants and the latest test summary.
pss_update(text, update_type, file_path?, signature?) Fold one observation into PSS state. update_typeconversation, code_change, error, test_failure.
pss_check_anti_resonance(proposal) Look the proposal up against past error patterns; returns a warning string when it resembles something that already failed.
pss_register_code(file_path, intent, signature) Record a code pointer ({file_path, intent, signature}) so future pss_get_context calls can surface it.
pss_record_error(error_text, source) Write an error pattern. sourcetest_failure, runtime_error, user_correction.
pss_save() Persist the engine state to SEMVEC_STATE_DIR. The CodingEngine also auto-persists at strategic moments; this is the explicit flush.

For the full Python API behind the tools see semvec.coding.

Differences from Claude Code

Capability Claude Code Cursor
MCP tool registration .claude/settings.json .cursor/mcp.json (per-project) or ~/.cursor/mcp.json (global)
SessionStart hook Auto-fires; loads state and pre-computes a context block Not available — replicated through the Cursor Rule above (pss_get_context on first request)
PreCompact hook Auto-fires before Cursor's compaction would discard context Not available — Cursor manages its own context window opaquely. Auto-save lives in pss_save(), called either by the rule on session-end or by the user-side flow (commit hook, pre-push hook).
@semvec mention Direct MCP-tool call from chat Same — Cursor lets you reference an MCP server by name
State location .semvec/ in project root (default) .semvec/ in project root (default)

In practice the rule covers ~80 % of the Claude Code lifecycle behaviour. The biggest delta is that auto-save in Cursor depends on the agent acting on your end-of-session signal; in Claude Code the PreCompact hook fires deterministically.

Optional — git hook for guaranteed persistence

If your team relies on pss_save() running before every push, install a git pre-push hook that triggers a one-shot save explicitly:

#!/usr/bin/env bash
# .git/hooks/pre-push
python -m semvec.coding.mcp_server <<EOF
{"method":"tools/call","params":{"name":"pss_save","arguments":{}}}
EOF

This is independent of Cursor and runs whenever anyone on the team pushes — a safety net for the rare case where the agent forgets to call pss_save().

End-to-end example session

User: I'm picking up the auth-flow refactor. Where did we leave off?
Cursor: <calls pss_get_context(task="auth-flow refactor")>
        Last session you renamed `authenticate()` to `verify_credentials()`
        in `src/auth/service.py`. Open invariants: never log plaintext
        passwords. Known error pattern: dropping JWT subject claim breaks
        the cluster auth tests (CP3).

User: Add rate limiting to the login endpoint.
Cursor: <calls pss_check_anti_resonance(proposal="rate limit login endpoint")>
        No prior error pattern matches. Proposing slowapi-based limiter…
        <writes src/auth/routes.py>
        <calls pss_update(text="added rate limit on POST /login (10/min/IP)
                          via slowapi", update_type="code_change",
                          file_path="src/auth/routes.py",
                          signature="rate_limited(10/min/IP)")>
        Done — 10 requests/min/IP via slowapi, returns 429 with
        Retry-After header.

User: Run the auth tests.
Cursor: <runs pytest, sees 1 failure>
        <calls pss_record_error(
            error_text="test_concurrent_logins fails: rate limit fires
                        before fixture's parallel runner finishes",
            source="test_failure")>
        Test failed because the limiter doesn't differentiate the
        fixture's parallel workers from real concurrent users…

User: Commit and stop here.
Cursor: <calls pss_save()>
        State persisted to .semvec/. Commit ready.

The next session starts with the failure already remembered — pss_check_anti_resonance will flag any future "rate limit the login endpoint" proposal that does not handle parallel workers.

Troubleshooting

Cursor MCP panel shows "Semvec Coding" red. Open the panel's logs. Common causes:

  • ModuleNotFoundError: No module named 'semvec' — Cursor is calling the wrong Python. Switch "command" in .cursor/mcp.json to the absolute interpreter path of the venv where you ran pip install "semvec[coding]".
  • RuntimeError: sentence-transformers is required — install it (pip install sentence-transformers). Semvec deliberately has no hash-fallback embedder.
  • OSError: [Errno 2] No such file or directory: '.semvec' — set SEMVEC_STATE_DIR to an absolute path or pre-create the folder once.

Tools never get called. The agent treats Cursor Rules as hints, not commands. If pss_get_context is silently skipped, strengthen the rule body — e.g. start with You MUST call pss_get_context as your first action in every session. Do not answer the user before that call returns. The rule is plain markdown; iterate as you would on any system prompt.

Conflicts with Cursor's own memory features. Cursor 0.45+ has built-in workspace memory. Semvec PSS is complementary, not a replacement: PSS gives you structured cross-session memory (decisions, invariants, error patterns) the agent can reason about explicitly; Cursor's built-in memory is opaque embedding-similarity recall. Run both. If you only want one, disable Cursor's built-in memory in Settings → Beta → Workspace Memory.

Multi-machine team. Commit .semvec/ to git. Each developer pulls the same LiteralCache and CodingEngine state. Conflicts on .semvec/state.json resolve like any other JSON merge — semvec's checksum check rejects tampered snapshots, so a bad merge surfaces as a ValueError on next load rather than as silent state drift.

Where to next

  • semvec.coding API reference — full tool semantics and the underlying CodingEngine class.
  • Embedders — pick the right model for your repo language and domain.
  • Licensing — Pro/Enterprise tier bypasses the community rate limits, useful for batch ingestion of large monorepos.