Announcing Beacon: Self-improving memory for coding agents
← All MCP servers
MCP server

Vexor

mcp:scarletkc/vexor

A semantic search engine for files and code.

Verdict
No issues found

Nothing found that works against the person who installs it.

Analyzed Oct 29, 2026, 16:15 UTC at eae0d6d

Dispute security verdict

If you believe this verdict is incorrect, submit a dispute and we'll review it.

0
findings
—
highest severity
0
threat classes
151
files scanned
817/817
checks satisfied
0
package advisories

Why this verdict

  • benign: 817 checks satisfied, nothing works against the installer of mcp-server:. (22 notes, 1 third-party flags)

Security findings 0

No security findings

Nothing found that works against the person who installs it.

Notes 22

Capabilities, vulnerabilities and disclosed behavior worth knowing. Nothing here works against the person who installs it, so notes never change the verdict.

Destructive action

mediumNoteConfirmed on reviewhigh confidence

The config --clear-flashrank path deletes the flashrank model cache directory with shutil.rmtree when the user runs that command, which is the declared cache-clearing purpose and is user-invoked.

shutil.rmtree(cache_dir)
vector:code
Reviewer's reasoning

The rmtree of the flashrank cache runs only on the user-invoked config --clear-flashrank command and is its declared purpose.

Destructive action

mediumNoteConfirmed on reviewhigh confidence

The local --clean-up command deletes the fastembed model cache directory with shutil.rmtree when the user explicitly runs it, which is the declared cleanup purpose.

shutil.rmtree(cache_dir)
vector:code
Reviewer's reasoning

The fastembed cache rmtree runs only when the user runs local --clean-up, its declared purpose.

Code execution

mediumNoteConfirmed on reviewhigh confidence

The update --upgrade path runs pip-style upgrade commands for the artifact itself, but only after the user answers a confirmation prompt, so the user decides.

if not typer.confirm(Messages.PROMPT_UPDATE_UPGRADE_CONFIRM):
vector:code
Reviewer's reasoning

The upgrade commands run only after typer.confirm returns true, so the user decides on this self-upgrade of the artifact.

Persistence

mediumNoteConfirmed on reviewhigh confidence

The alias command appends a shell alias line to the user's shell profile after the user confirms the prompt; the write is user-approved and limited to the declared alias purpose.

profile_path.open("a", encoding="utf-8")
vector:code
Reviewer's reasoning

The profile append happens only after the user confirms the alias prompt and is limited to the declared alias purpose.

Code execution

mediumNoteConfirmed on reviewhigh confidence

The star command runs the local gh CLI to star the project repository when the user invokes it, a declared user-invoked action against the publisher's repo.

subprocess.run(
vector:code
Reviewer's reasoning

The gh repo star call runs only when the user invokes the star command and targets the publisher's repo, as declared.

Code execution

mediumNoteConfirmed on reviewhigh confidence

The feedback command runs the gh CLI to open a GitHub issue page for the publisher's repository when the user invokes it.

[gh, "issue", "create", "--repo", "scarletkc/vexor", "--web"],
vector:code
Reviewer's reasoning

The feedback command opens a publisher issue page only when the user invokes it.

Code execution

mediumNoteConfirmed on reviewhigh confidence

The config editor path runs the user's configured editor on the config file when the user runs config with no options; a user-invoked step.

subprocess.run([*cmd_list, str(config_path)], check=True)
vector:code
Reviewer's reasoning

The editor launch runs only when the user runs config with no options, a user-invoked step.

Destructive action

mediumNoteConfirmed on reviewmedium confidence

clear_all_collections unlinks the collections database files; it is not wired to a CLI command in this batch and deletes only the artifact's own store, so it is a note.

candidate.unlink()
vector:code
Reviewer's reasoning

clear_all_collections is not wired to any CLI command in the shown code and only removes the artifact's own store.

Credential access

mediumNoteConfirmed on reviewlow confidence

Undeclared residue: when VEXOR_API_KEY and the Gemini/Voyage keys are absent, resolve_api_key sends the OPENAI_API_KEY to whatever base URL the user configured for the 'custom' provider; this is operator-configured and the key goes only to a user-directed endpoint, so it is a note.

if normalized in {"openai", "custom"}:
vector:code
Reviewer's reasoning

The OPENAI_API_KEY fallback goes only to the base URL the user configured for the custom provider, which is a user-chosen endpoint, not an unrelated host.

Persistence

mediumNoteConfirmed on reviewmedium confidence

Alias setup appends a shell alias line to the user's shell profile, but only after the user explicitly confirms with default no; a disclosed, user-decided rc-file write.

handle.write(alias_command + "\n")
vector:code
Reviewer's reasoning

The alias write follows an explicit user confirmation with default no and is the declared alias setup.

Persistence

mediumNoteConfirmed on reviewmedium confidence

Skill installer writes the bundled skill into agent skill directories (auto-detected or user-chosen) after the user confirms; an agent-config write the user opted into for the declared purpose.

result = install_bundled_skill(
vector:code
Reviewer's reasoning

Skill installation happens only after the user answers the install confirmation and picks a target, matching the declared purpose.

Code execution

mediumNoteConfirmed on reviewmedium confidence

Installer runs pip install of the publisher's own vexor extras through subprocess after the user confirms the prompt; a user-invoked install step that changes only the artifact itself.

completed = subprocess.run(cmd, check=False)
vector:code
Reviewer's reasoning

The pip install of the publisher's own extras runs only after the user confirms the install prompt and changes only the artifact itself.

Data exfiltration

mediumNoteConfirmed on reviewhigh confidence

With remote rerank configured, the search tool POSTs the query and workspace code snippets, plus the rerank bearer key, to the user-configured endpoint; declared feature, user-chosen destination.

request = urlrequest.Request(config.base_url, data=data, method="POST")
vector:code
Reviewer's reasoning

The POST goes only to a user-configured rerank endpoint carrying the user's own key, a declared feature with a user-chosen destination (test 5, API key for the wrapped service and model endpoint the user chooses).

Destructive action

mediumNoteConfirmed on reviewmedium confidence

With force enabled, the bundled skill installer deletes an existing destination skill directory with rmtree before copying, a destructive step the user opts into by passing force.

shutil.rmtree(destination)
vector:code
Reviewer's reasoning

The rmtree runs only when the user passes force after a FileExistsError would otherwise occur, so the destructive step is user-opted (test 4).

Persistence

mediumNoteConfirmed on reviewmedium confidence

The bundled skill is copied into the agent's skills folder (for example ~/.claude/skills), where the agent loads it; this happens only when the user runs the install command with a skills target.

shutil.copytree(source_dir, destination)
vector:code
Reviewer's reasoning

Copying the bundled skill into the agent skills folder happens only when the user runs the install command with a skills target, which is the declared purpose and user-invoked (test 4).

Data exfiltration

mediumNoteConfirmed on reviewmedium confidence

The remote rerank path sends the search query and result file paths and previews to the user-configured rerank endpoint with the user's API key, a disclosed declared feature.

remote_rerank_request(
vector:code
Reviewer's reasoning

The remote rerank call sends the query and previews to the user-configured endpoint with the user's key, a disclosed declared feature (test 5).

Code execution

mediumNoteConfirmed on reviewmedium confidence

The upgrade helper runs pip, pipx, uv, or git commands for the publisher's own package only when the user invokes the update-with-upgrade command.

completed = subprocess.run(list(command), check=False, timeout=timeout)
vector:code
Reviewer's reasoning

The helper runs pip, pipx, uv, or git only for the publisher's own package and is invoked only through the user's update-with-upgrade command, so the step is user-opted (test 4).

Supply chain

lowNoteConfirmed on reviewhigh confidence

The install command copies the bundled agent skill into user-chosen skill directories when the user runs it with --skills, which is the declared purpose and is user-invoked.

install_bundled_skill(
vector:code
Reviewer's reasoning

Skill install copies the bundled skill into directories the user chose via --skills, a declared user-invoked step.

Supply chain

lowNoteConfirmed on reviewhigh confidence

The MCP server's start-time update check contacts PyPI for the publisher's own package and caches the result, with network use unless disabled by environment or config.

latest = fetch_latest_pypi_version(package, timeout=timeout)
vector:code
Reviewer's reasoning

The start-time check queries PyPI for the publisher's own package and caches the result, which is the artifact's own update path and can be disabled by config (test 5).

Reconnaissance

infoNoteConfirmed on reviewhigh confidence

The MCP launcher reads the undeclared environment variable VEXOR_MCP_NUM_THREADS and writes it into four BLAS/thread-pool variables via setdefault; this is operator-controlled thread tuning with no data sent off-host, so it is an undeclared but harmless note.

os.environ.setdefault(name, numeric_threads)
vector:code
Reviewer's reasoning

The env var read sets BLAS thread counts for the MCP process only; it is operator-controlled tuning with no outbound data, so it stays a note.

Reconnaissance

infoNoteConfirmed on reviewmedium confidence

The MCP search and index tools accept an absolute path from the calling agent and read or return that tree (content included by default); this is the declared indexing job on the caller's chosen path.

candidate = Path(raw_path).expanduser()
vector:code
Reviewer's reasoning

The MCP tools read the caller-supplied path as their declared indexing job; the caller chooses the path, so this is the tool doing its job.

Reconnaissance

infoNoteConfirmed on reviewlow confidence

Startup launches a background check against the publisher's release endpoint with the current version; a best-effort, publisher-directed update check.

latest = check_for_update(__version__)
vector:code
Reviewer's reasoning

The startup thread calls the publisher's own PyPI version check, which is the artifact's own update path (test 5) and is best-effort and silent on failure; it does not work against the installer.

Third-party flags 1

Behavior aimed at other sites, platforms or people, such as evading rate limits or offensive-security tooling. It does not affect the person who installs it, so it never changes the verdict.

Supply chain

lowNoteConfirmed on reviewmedium confidence

On every non-skipped invocation a background thread fetches the latest version from PyPI for the publisher's own package to refresh the update cache; this is the publisher's own registry and is a note.

check_for_update(__version__)
vector:code
Reviewer's reasoning

The background update check queries PyPI for the publisher's own package, which is a publisher registry fetch and harms no one.

Open questions 2

What the analysis could not settle from the code alone.

[file request plugins/vexor/skills/vexor-cli/SKILL.md] Does the bundled vexor-cli skill file contain instructions that steer the agent beyond the search task, such as hidden directives, config writes, or data routing?
Agent hijacking
[file request plugins/vexor/skills/vexor-cli] Does the bundled vexor-cli skill that gets copied into agent skill folders contain instructions that steer the agent beyond the search task, such as hidden directives, config writes, or data routing?
Agent hijacking

Execution chain 84

What it calls, runs, installs, reads and writes, with the line that does it.

Environment variables 10

<dynamic>
GOOGLE_GENAI_API_KEY
VEXOR_CONFIG_JSON
VEXOR_MCP_NUM_THREADS
VEXOR_NO_UPDATE_CHECK
VEXOR_REMOTE_RERANK_API_KEY

Commands run 2

<dynamic>
vexor/cli.py:1654 · via:git_worktree_is_dirty
git -C {…} status --porcelain

Files read 4

<dynamic>
~/.config/fish/config.fish

Files written 1

<dynamic>

GitHub repositories 1

scarletkc/vexor

Packages 14

pypi:charset_normalizer
server.json · delegated
pypi:numpy
server.json · delegated
pypi:openai
server.json · delegated
pypi:pathspec
server.json · delegated
pypi:pypdf
server.json · delegated
pypi:rank_bm25
server.json · delegated
pypi:rich
server.json · delegated
pypi:tokenizers
server.json · delegated
pypi:tree_sitter
server.json · delegated
pypi:tree_sitter_javascript
server.json · delegated
pypi:tree_sitter_typescript
server.json · delegated
pypi:typer
server.json · delegated
pypi:vexor@0.30.0
server.json:15 · launch:server.json
pypi:watchdog
server.json · delegated

URLs 50

http://www.w3.org/2000/svg
https://api.voyageai.com/v1
https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl
https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl
https://files.pythonhosted.org/packages/00/78/f34251dadb8f3921264a1d9b8946f5e542014ee2614b285261b4e40e6775/fsspec-2026.7.0.tar.gz
https://files.pythonhosted.org/packages/00/8c/e68fa5d862ea6a27fced3535c25ea4eaa26ba1ce00dfef5841924c74b167/py_rust_stemmers-0.1.8-cp314-cp314-musllinux_1_2_x86_64.whl
https://files.pythonhosted.org/packages/01/4a/542378ab3972b0c1cf1df3df3eff9591cea0d30c58c3aa3c4ddbc244e787/websockets-16.1.1-cp311-cp311-macosx_11_0_arm64.whl
https://files.pythonhosted.org/packages/01/65/d43b714731bb2f40d4053dfa00ecfc1c5a301f8e3316c5db3a09af59fe94/charset_normalizer-3.5.1-cp37-abi3-win32.whl
https://files.pythonhosted.org/packages/01/6d/46c2f2ce6751cb26f39293e1ecbf8544cb01321397cd476c2756b98c216d/websockets-16.1.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl
https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl
https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl
https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl
https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl
https://files.pythonhosted.org/packages/01/c3/629999e78d46c1115c11886d51c6bd68c17ce4a944f1ea3e153a91316a33/protobuf-7.36.0-py3-none-any.whl
https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl
https://files.pythonhosted.org/packages/01/e8/de0accd8cd004cf11252ca53fc5c3dda59bcf1856abfc7609842ceba7363/google_genai-2.19.0-py3-none-any.whl
https://files.pythonhosted.org/packages/01/ec/00e7eeca200facf9266a83e4cbbf1bed0e67fba1d4d45031d3e5b3d81b5c/websockets-16.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl
https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz
https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
https://files.pythonhosted.org/packages/02/0b/0483078c8567445557a7015b0e5b187f6d7d4fda73464df9c4bdea7f7f3c/tree_sitter-0.26.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl
https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl
https://files.pythonhosted.org/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl
https://files.pythonhosted.org/packages/02/51/d3a6ea424652c60f05600d8c2e01a55c913755e7cdad64afabbd1aa16f44/readme_renderer-45.0.tar.gz
https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl
https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl
https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
https://files.pythonhosted.org/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl
https://files.pythonhosted.org/packages/03/3b/0cc9a26777334ab2f2e3089b948bbf4e4fe72ea70b897715ef6415043ec8/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl
https://files.pythonhosted.org/packages/03/4f/2750f7f6f025a1507cd3b7218691671eecfd0bbebebe8b39aa0fe1d360b8/pefile-2024.8.26.tar.gz
https://files.pythonhosted.org/packages/03/6d/439231dfc3ccfa6f8c06477b7da2219cbd41a2de3d49084df8ec7b5100f2/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl
https://files.pythonhosted.org/packages/03/7a/2ed9bed79925f4367c83c77f66a89e5ca7229c288d2d19ad5f36d1ca0070/coverage-7.15.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
https://files.pythonhosted.org/packages/03/82/5df3df10c528cbcbc550a8770919f15b9fc929e80ecf5a7654cf66ac265e/onnxruntime_gpu-1.29.0-cp314-cp314t-manylinux_2_28_x86_64.whl
https://files.pythonhosted.org/packages/03/c1/8b24192e89286399765155251f99ee9f070a9d637109018ac23d99b99f6f/coverage-7.15.4-cp313-cp313-musllinux_1_2_x86_64.whl
https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl
https://files.pythonhosted.org/packages/04/94/21adfa7d90a7a697137ad6de33eeff6445420ca55e433a5d4919c79bc3b5/mmh3-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl
https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl
https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl
https://files.pythonhosted.org/packages/05/49/25371987ee459a5f67c0427fb75c74f9358e65f2c71fe75bf41c1b6c5fcb/coverage-7.15.4-cp313-cp313-musllinux_1_2_ppc64le.whl
https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz
https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl
https://files.pythonhosted.org/packages/05/b3/a43012748fb861c914c5eac1c1a3bad44282e767499cd02280d4d1edf092/lxml-6.1.2-cp314-cp314-manylinux_2_28_i686.whl
https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl
https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl
https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl
https://files.pythonhosted.org/packages/06/42/89a2760cd2f2cda28ef5b9591ec775a6a5183d193e7b62ddb936b1565167/lxml-6.1.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
https://files.pythonhosted.org/packages/06/59/8211cd0f56e53f7770debd9a78de37985fb5662ae66e3b7b380f4c79888b/py_rust_stemmers-0.1.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz
https://files.pythonhosted.org/packages/06/ae/7ae8807410dfa33f8e6f1715740adeaafa8a816cc4cb33508f54b1f7c896/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl

Coverage gaps 1

Parts of the artifact the static analysis could not fully examine.

context:unanalyzable_binary binary content is only byte-scanned
assets/vexor.ico