← All MCP servers
MCP server

OpenReflex

mcp:vishnu-77/openreflex

Optional diagnostics MCP server for OpenReflex's ambient project muscle memory.

Verdict
Suspicious

Risky behavior the analysis could not rule out.

Analyzed Oct 26, 2026, 12:11 UTC at 0c5adde
3
findings
critical
highest severity
2
threat classes
110
files scanned
1,296/1,296
checks satisfied
0
package advisories

Why this verdict

mcp-server . Suspicious
  • suspicious: unverified finding mcp-server:.:src/openreflex/usage.py:339:PERSIST
plugin plugins/openreflex Suspicious
  • suspicious: finding plugin:plugins/openreflex:plugins/openreflex/runtime/launcher.cjs:180:SUPPLY
  • suspicious: unverified finding plugin:plugins/openreflex:plugins/openreflex/hooks/cursor-hooks.json:6:SUPPLY
skill plugins/openreflex/skills/openreflex No issues found
  • benign: 8 checks satisfied, no findings for skill:plugins/openreflex/skills/openreflex

Security findings 3

Persistence

criticalUnverifiedmedium confidence

The opt-in `tokens enable` path writes OTEL telemetry env entries into the user's global ~/.claude/settings.json (agent control-plane config); this is documented as optional token accounting and does not break the read-only claim, which applies to the no-argument /openreflex command.

_atomic_json(_settings_path(), settings)
vector:code

Supply chain

highContextual riskmedium confidence

At SessionStart the launcher creates a venv under ~/.openreflex and pip-installs a package from PyPI by default, or from an arbitrary spec in the OPENREFLEX_BOOTSTRAP_SPEC env var, then runs it for every hook with the full process environment and stdin.

const spec = process.env.OPENREFLEX_BOOTSTRAP_SPEC || ("openreflex==" + expectedVersion);
conceal:remotetrigger:conditionalvector:code
Counter-argument considered

The default is a pinned PyPI install, but the install spec can be overridden through the undeclared OPENREFLEX_BOOTSTRAP_SPEC env var. The override flows straight into pip install, and the resulting runtime receives every hook's stdin and environment.

Supply chain

mediumUnverifiedlow confidence

The Cursor hook config runs a bare `openreflex` resolved from PATH, contradicting the launcher's stated policy that the plugin never executes an arbitrary openreflex from PATH, so a PATH shim would receive hook data.

"command": "openreflex hook cursor sessionStart"
vector:code

Open questions 3

What the analysis could not settle from the code alone.

The README claims the no-argument /openreflex command is read-only. All observed file writes (state.json, project-memory.json, approvals.json, logs, locks) go to the local OpenReflex home or memory directory, and the AGENTS.md/CLAUDE.md/.pre-commit-config.yaml/.github/workflows literals are only read or used for role classification, never written. It is unclear whether the no-argument command path calls update_state or ensure_background_refresh, which would write local state and spawn a detached⟦… 15 more chars …⟧
Persistence
What exactly do configure_statusline and usage.enable write to user-scope Claude settings and telemetry environment, and does the receiver process persist beyond the session?
Persistence
What does the externally installed openreflex Python package do with hook payloads (prompts, tool inputs and outputs) and does it transmit them off-host?
Data exfiltration
Refuted findings 10

Candidates the review stage examined and rejected, kept for transparency.

Persistence

criticalRefutedlow confidence

install_diagnostics writes an openreflex MCP server entry (command openreflex, args mcp) into the project's .mcp.json, an opt-in agent MCP configuration write.

mcp = project / ".mcp.json"
Why it was refuted

This is the explicitly opt-in MCP diagnostics installer that writes an MCP entry into the project's .mcp.json. The detector contradiction concerns the read-only claim for the no-argument /openreflex command, not this installer.

Code execution

criticalRefutedmedium confidence

The SessionStart hook automatically runs a launcher that bootstraps a Python runtime by downloading and installing a PyPI package, which is then executed on every hook event; the package contents are not shipped for review.

node \"${CLAUDE_PLUGIN_ROOT}/runtime/launcher.cjs\" hook claude-code SessionStart --plugin-root \"${CLAUDE_PLUGIN_ROOT}\"
Why it was refuted

The hook is the plugin's own launcher. The runtime it installs is a version-pinned PyPI package matching the plugin manifest version, and the launcher verifies the installed version afterward. That falls under the pinned official-registry install carve-out.

Supply chain

criticalRefutedmedium confidence

The plugin ships only a launcher; at SessionStart it creates a venv and pip-installs the actual runtime (openreflex==version, overridable via the OPENREFLEX_BOOTSTRAP_SPEC env var) from a remote index, so the code that receives all hook input is fetched at runtime and is not part of the reviewed artifact.

["-m", "pip", "install", "--disable-pip-version-check", "--quiet", "--upgrade", spec],
Why it was refuted

The default install is a version-pinned package from the official registry, and the launcher verifies the installed version afterward. That is the accepted pinned-registry carve-out, and the remote runtime is the project's own published package.

Persistence

highRefutedlow confidence

configure_statusline writes a command-type statusLine, whose command can be taken from env OPENREFLEX_RUNTIME_COMMAND, into the user-level Claude settings.json, so that command re-executes every refresh in the user scope outside the project.

data["statusLine"] = {"type": "command", "command": desired, "padding": 0, "refreshInterval": 2}
Why it was refuted

The user-level statusLine write is the declared OpenReflex status line feature, it preserves user-owned entries, and the env-supplied command is set by the plugin runtime itself. No attacker-controlled chain is shown.

Persistence

highRefutedmedium confidence

configure_statusline writes an OpenReflex-owned statusLine command into the user-level Claude settings.json (agent control-plane write), invoked from session hooks without per-call confirmation; it is consistent with the declared Claude status line feature and preserves existing non-owned entries.

data["statusLine"] = {"type": "command", "command": desired, "padding": 0, "refreshInterval": 2}
Why it was refuted

The finding itself calls this consistent with the declared status line feature. The write is documented and preserves existing non-owned entries.

Persistence

highRefutedlow confidence

The onboard command (and install/hook paths) calls configure_statusline, which writes a managed OpenReflex statusline into the user-scope Claude settings, an agent control-plane write beyond what an MCP memory server strictly needs; it is disclosed and preserves existing user statuslines.

status = configure_statusline(project)
Why it was refuted

The onboard command is explicit user onboarding. It discloses the statusline behavior and preserves an existing user status line.

Persistence

highRefutedlow confidence

The tokens enable action modifies Claude Code telemetry settings and starts a local loopback receiver, a control-plane configuration change that is user-invoked and disclosed as counts-only.

result = enable()
Why it was refuted

This is an explicit user-invoked opt-in command whose help text discloses the loopback receiver, and it refuses to overwrite conflicting user telemetry settings. It is a declared core capability.

Persistence

highRefutedlow confidence

The installer merges hook commands (openreflex hook ...) into the project's .claude/settings.json and auto-approves the project, so these hooks run on every agent lifecycle event; this is the declared installer purpose but is an agent control-plane write.

settings = project / ".claude" / "settings.json"
Why it was refuted

Merging hooks into the project's settings.json is the declared purpose of the explicit install command, which has a dry-run option and a matching uninstall.

Persistence

highRefutedlow confidence

ensure_receiver spawns a detached background Python process (new session, stdio discarded) that serves a loopback HTTP receiver; the README discloses a loopback-only OpenReflex receiver for opt-in token accounting.

subprocess.Popen(command, **kwargs)
Why it was refuted

The detached process runs a fixed local command to serve the loopback receiver. It is part of the opt-in, disclosed token accounting feature and can be stopped with disable.

Persistence

highRefutedmedium confidence

The opt-in `tokens enable` command writes OpenTelemetry env keys into the user-level Claude settings.json and starts a detached loopback receiver, which changes Claude's telemetry configuration persistently, though only to a loopback endpoint with content logging disabled.

env.update(OWNED_ENV)
Why it was refuted

The write is an opt-in, user-invoked, disclosed token accounting feature. It sets only loopback telemetry keys it owns and aborts on conflicting user settings.

Coverage gaps 1

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

no_tools_extracted MCP server target with no statically extractable tools/prompts/resources
pyproject.toml