Atlas / MCP servers / jjang-ai / vMLX

vMLXBLOCK

mcp/jjang-ai/vmlx

vMLX - Use MLX models easily - JANGQ (GGUF for MLX) - Not dependant on mlx_vlm

Verdict
BLOCK
Grade
F
Trust score
45 /100
Exposed tools
41 25r · 15w · 1d
Transport
sse · stdio · streamable-http
License
Apache-2.0
Stars
872
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

MLX Inference Server for Apple Silicon

Self-hosted inference server for LLMs, VLMs, and image generation on Apple Silicon. OpenAI + Anthropic + Ollama compatible HTTP API. Self-hosted; no third-party API keys required. Native MTP artifact detection and family-specific cache policy gates keep speculative/cache settings explicit and model-safe.

Looking for a native Swift macOS app or Swift inference engine? See osaurus.ai.

Read from source at commit 4b985ff8753aOBSERVED · 2026-09-27
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.

claude-code
claude mcp add vmlx --env BRAVE_API_KEY=${BRAVE_API_KEY} --env DSV4_MAX_PREFILL_TOKENS=${DSV4_MAX_PREFILL_TOKENS} --env DSV4_PROMPT_SNAPSHOT_MIN_TOKENS=${DSV4_PROMPT_SNAPSHOT_MIN_TOKENS} --env HF_TOKEN=${HF_TOKEN} -- npx -y [email protected]
claude-desktop
{
  "mcpServers": {
    "vmlx": {
      "command": "npx",
      "args": [
        "-y",
        "[email protected]"
      ],
      "env": {
        "BRAVE_API_KEY": "${BRAVE_API_KEY}",
        "DSV4_MAX_PREFILL_TOKENS": "${DSV4_MAX_PREFILL_TOKENS}",
        "DSV4_PROMPT_SNAPSHOT_MIN_TOKENS": "${DSV4_PROMPT_SNAPSHOT_MIN_TOKENS}",
        "HF_TOKEN": "${HF_TOKEN}"
      }
    }
  }
}
03

Exposed tools (41)

25 read · 15 write · 1 destructive. Blast radius: 1 tool can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.

ToolRiskDescription
addwriteAdd two integers.
apply_regexwriteApply a regex find-and-replace across one or more files. Returns the number of replacements made per file.
ask_userreadAsk the user a question and wait for their response. Use when you need clarification, confirmation, or user input to proceed with a task.
batch_editwriteApply multiple find-and-replace edits to a file in a single call. More efficient than multiple edit_file calls. Each edit is applied sequentially.
clipboard_readreadRead the current contents of the system clipboard.
clipboard_writewriteWrite text to the system clipboard.
copy_filereadCopy a file to a new location.
count_tokensreadEstimate the token count of a text string using character and word heuristics.
create_directorywriteCreate a directory and any necessary parent directories.
delete_filedestructiveDelete a file or empty directory. Use with caution — this cannot be undone.
diff_filesreadShow a unified diff between two files, or between a file and its git HEAD version (if path_b is omitted).
echoreadReturn the provided text.
edit_filewriteEdit a file by finding and replacing text. The search_text must match exactly (including indentation). Use read_file first to see the current content. Set replace_all=true to replace ALL occurrences (useful for renaming variables/functions).
fetch_urlreadFetch a URL and return its content as text. HTML is automatically converted to readable text. Useful for reading documentation, API responses, or web pages.
file_inforeadGet metadata about a file or directory: size, type (file/directory/symlink), last modified time, permissions.
find_filesreadFind files by name pattern. Returns matching file paths. Useful for finding files when you don\
get_current_datetimereadGet the current date, time, and timezone. Use this when you need to know the current date or time.
get_diagnosticswriteRun diagnostics on a file or project: type checking (TypeScript), linting, or syntax validation. Returns errors and warnings with file locations.
get_process_outputreadRead stdout/stderr from a previously spawned background process. Returns latest output and whether the process is still running.
get_treereadGet a project directory tree respecting .gitignore rules. Shows the hierarchical file and directory structure.
gitwriteRun git commands in the working directory. Supports status, diff, log, blame, add, commit, branch, checkout, stash, show, and more. Blocks destructive operations (push --force, reset --hard).
insert_textwriteInsert text at a specific line number in a file. The new text is inserted BEFORE the specified line. Use read_file first to see line numbers.
list_directoryreadList files and directories at a path. Shows file types and sizes.
move_filewriteMove or rename a file or directory.
patch_filewriteApply a unified diff patch to a file. Useful for making multiple related edits at once. The patch should be in standard unified diff format (--- a/file, +++ b/file, @@ hunks).
read_filereadRead the contents of a file with line numbers. Returns up to 2000 lines by default. Use offset/limit for large files.
read_imagereadRead an image file and return its base64-encoded data with MIME type. Supports png, jpg, gif, webp, svg. Max 10MB.
read_videoreadRead a local video file and attach it for VL model analysis. Supports mp4, mov, m4v, webm, mkv. Max 50MB.
record_gemma_labelreadRecord a label for the Gemma API stress test.
record_gemma_stream_labelreadRecord a Gemma streaming label.
record_gemma_stream_response_labelreadRecord a Gemma Responses streaming label.
record_mm3_labelreadRecord a label for the MM3 API stress test.
record_mm3_stream_labelreadRecord an MM3 streaming label.
record_mm3_stream_response_labelreadRecord an MM3 Responses streaming label.
replace_linesreadReplace a range of lines in a file with new content. Use read_file first to see line numbers.
run_applescriptwriteExecute AppleScript on this Mac with /usr/bin/osascript and return its output. Use this for macOS app automation and system scripting.
run_commandwriteExecute a shell command in the working directory. Has a 60 second timeout. Returns stdout, stderr, and exit code.
search_filesreadSearch file contents for a pattern using ripgrep. Returns matching lines with file paths and line numbers.
spawn_processwriteStart a long-running background process (e.g., dev server, watcher). Returns a process ID for checking output later. Auto-kills after 5 minutes.
web_searchreadSearch the web using Brave Search. Returns titles, URLs, and descriptions of matching pages.
write_filewriteWrite content to a file. Creates the file and parent directories if they don\
04

Trust audit

BLOCKgrade F · trust 45/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfaceWARN
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (17 observation(s))
Network
declared (10 observation(s))
Shell
declared (10 observation(s))
Dependencies
not all pinned
Secrets in source
found

Findings (25)

HIGHCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
vmlx_engine/utils/ssm_companion_disk_store.py:878
opaque_decoded[k] = pickle.loads(base64.b16decode(hexed))
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
panel/src/main/engine-manager.ts:345
const result = await exec(`"${path}" --version 2>&1`)
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
panel/src/main/engine-manager.ts:426
const result = await exec(`"${pipPath}" --version 2>&1`)
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
panel/scripts/scoped-release-preflight-20.py:7796
basename in {".pypirc", ".npmrc", ".netrc"}
Why it matters. touches a credential store
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
panel/scripts/verify-bundled-python.sh:588
__import__(mod)
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
panel/scripts/verify-bundled-python.sh:763
_mod = importlib.import_module(_mod_name)
MEDIUMInformation disclosure · disclose.log_secret · CWE-209, CWE-532
bench/profile_minimax_layers.py:199
print(f"  {'(sum sublayers/token)':<28} {'':>18} {sub_total_token_ms:>17.2f} ms")
MEDIUMInformation disclosure · disclose.log_secret · CWE-209, CWE-532
vmlx_engine/distributed/cli.py:118
logger.info("  Auth: cluster secret set (%d bytes)", len(secret))
MEDIUMInformation disclosure · disclose.log_secret · CWE-209, CWE-532
vmlx_engine/mllm_batch_generator.py:8325
print(f"Request {resp.request_id}: token={resp.token}")
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
bench/all_local_model_smoke.py:1803
base_url = f"http://127.0.0.1:{port}"
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
bench/all_local_model_smoke.py:1898
base_url = f"http://127.0.0.1:{port}"
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
bench/api_kwargs_parity_probe.py:227
base_url = f"http://127.0.0.1:{args.port}"
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
bench/dsv4_all_surfaces_cache_parity.py:26
BASE = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:8000"
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
panel/scripts/live-real-ui-model-proof.mjs:4672
token: 'REAL_UI_LIVE_TOOL_ONE',
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
panel/scripts/live-real-ui-model-proof.mjs:4677
token: 'REAL_UI_LIVE_TOOL_TWO',
MEDIUMFilesystem / path · mcp.destructive_tools · CWE-22, CWE-59
delete_file
Why it matters. 1 tool(s) can delete or overwrite
Fix. prefer a read-only mode or scoped tokens; the page states the blast radius
LOWCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
tests/test_release_automation.py:221
path.name: (path.read_text(), yaml.load(path.read_text(), Loader=yaml.BaseLoader))
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
LOWCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
tests/test_release_automation.py:298
workflow = yaml.load((ROOT / ".github/workflows/publish-release.yml").read_text(), Loader=yaml.BaseLoader)
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
LOWCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
tests/test_release_automation.py:318
workflow = yaml.load((ROOT / ".github/workflows/publish-release.yml").read_text(), Loader=yaml.BaseLoader)
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
LOWCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
tests/test_cross_matrix_output_counts.py:59
module = importlib.import_module(f"tests.cross_matrix.{name}")
LOWCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
tests/test_mlx_vlm_patch_targets_still_exist.py:54
module = importlib.import_module(module_path)
LOWCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
tests/test_reasoning_parser_whitespace_fidelity.py:51
module = importlib.import_module(f"vmlx_engine.reasoning.{mod.name}")
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
panel/tests/chat-edit-replay.test.ts:38
const handler = new Function(...Object.keys(dependencies), code)(...Object.values(dependencies))
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
panel/tests/chat-error-history.test.ts:16
await new Function(...Object.keys(context), `return (async () => {${source.slice(start, end)}})()`)(...Object.values(context))
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
panel/tests/chat-session-start.test.ts:46
const run = new Function('window', 'sessionId', 'activeSessionId', 'showToast', 't', code)(
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table

Gates applied: no_behavioural_pass.

Audited 2026-09-27 · audit v0.4.1 · source sha 4b985ff8753afull audit observations/trust-audit/mcp-server/jjang-ai__vmlx.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-09-274b985ff8753aBLOCKF45first audit
06

Questions

What is the vMLX MCP server?

vMLX - Use MLX models easily - JANGQ (GGUF for MLX) - Not dependant on mlx_vlm

What tools does vMLX expose?

41 in total: 25 read-only, 15 that write, and 1 that can delete or overwrite (delete_file). Every one is listed on this page with its risk.

Is vMLX safe to connect to an agent?

No — not without reading the findings first. The audit graded it F (45/100) and found 4 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 1 of its tools can destroy data, so scope the token you give it to what you actually need.

What credentials does vMLX need?

It reads BRAVE_API_KEY, DSV4_MAX_PREFILL_TOKENS, DSV4_PROMPT_SNAPSHOT_MIN_TOKENS, HF_TOKEN, TOKENIZERS_PARALLELISM, VLLM_API_KEY, VMLINUX_API_KEY, VMLINUX_CACHE_SELECTION_HOT_ADVANTAGE_TOKENS, VMLINUX_DSV4_PROMPT_SNAPSHOT_MIN_TOKENS, VMLINUX_MIMO_TEXT_PREFILL_REJECT_TOKENS, VMLINUX_MIMO_TEXT_PREFILL_TOTAL_TOKENS and VMLINUX_MIMO_V2_TOKEN_TRACE from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.

How does vMLX run?

It speaks sse, stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as vmlx at 1.6.67.

How current is this page?

The grade is for one exact copy of the source (4b985ff8753a), read on 2026-09-27. The repository is watched and re-audited when it changes.

Advertisement