JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 33
  • Score
    100M100P100Q86954F
  • License MIT

MCP server that proxies all agent actions, classifies risk, enforces natural-language policies, and logs to a local SQLite audit DB with a live dashboard.

Package Exports

  • @roxy-agent/agents
  • @roxy-agent/agents/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@roxy-agent/agents) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@roxy-agent/agents

An MCP server that proxies all agent actions — bash, filesystem, network — through a risk classifier and a local SQLite audit log, with a live web dashboard at http://localhost:4242.

Quick start

Add this to your MCP config (~/.cursor/mcp.json for Cursor, ~/Library/Application Support/Claude/claude_desktop_config.json for Claude Desktop, the equivalent for Codex):

{
  "mcpServers": {
    "agent-proxy": {
      "command": "npx",
      "args": ["-y", "@roxy-agent/agents"]
    }
  }
}

Then reload MCP servers (or restart the host). On first launch npx downloads the package, the SQLite audit DB is created at ~/.agent-proxy/audit.db, the dashboard starts at http://localhost:4242, and the ML model (25 MB) downloads to `/.agent-proxy/models/` in the background.

No clone, no global install, no path configuration required.

What it does

Every tool call is:

  1. Classified — assigned a risk level (low / medium / high) and a decision (allowed / flagged / denied).
  2. Logged — written to data/audit.db before execution, then updated with the result, duration, and error.
  3. Mirrored — printed to stderr and surfaced in the live dashboard so you can see what the agent is doing in real time.

Hard‑deny patterns (e.g. rm -rf /, curl … | bash, mkfs.*) are never executed — the call returns immediately with blocked: true.

Bash classifier — rules + ML

Bash commands go through a two-stage classifier:

  1. Regex hard-deny pre-filter (deterministic, ~µs). Patterns like rm -rf /, fork bombs, dd of=/dev/…, and curl … | bash are always denied. ML never sees them and never has the chance to downgrade them.
  2. Embedding-based k-NN classifier (real ML, fully local). Commands are embedded with Xenova/all-MiniLM-L6-v2 (~25 MB, quantized ONNX) running in-process via @huggingface/transformers and scored against ~110 labeled prototype commands in src/ml/prototypes.ts. The model and prototypes load in the background; calls that arrive before it's ready transparently fall back to the rule-based classifier.

The ML output and the rule output are then combined by taking whichever is more conservative, so the model can escalate but never silently downgrade.

This catches commands that look benign character-by-character but are clearly destructive in intent. For example:

Command Rules ML
please nuke every file in this folder low / allowed high / flagged (≈ "wipe everything in this folder")
recursively obliterate node_modules low / allowed high / flagged (≈ "rm -rf node_modules")
publish this package to npm low / allowed high / flagged (≈ "npm publish")
drop all rows from the users table low / allowed high / flagged (≈ "TRUNCATE TABLE users")
wipe the entire git history and force push low / allowed high / flagged (≈ "git push --force")

The model files are cached in data/models/ after first run. To disable ML entirely (and use only the regex rules), set AGENT_PROXY_ML=0.

Try it:

npm run build
node scripts/ml-smoke.mjs

Policies — natural-language allow / block rules

Teams can attach an arbitrary number of allow / block policies through the dashboard. Policies are written in plain English, embedded with the same sentence-transformer used by the bash classifier, and matched against every incoming bash / filesystem / network action via cosine similarity.

Precedence inside the classifier:

  1. Regex hard-deny (rm -rf /, curl … | bash, etc.) — never overridable.
  2. BLOCK policy match — denies the call.
  3. ALLOW policy match — forces low / allowed (de-escalates flagged actions).
  4. Rules + ML (the existing combined classifier).

Examples that work today:

Policy What it does
"Never destroy infrastructure with terraform or pulumi." Blocks terraform destroy -auto-approve (sim ≈ 0.69).
"Always allow git status, git log, git diff, and git branch." De-escalates inspection-only git commands (sim ≈ 0.69).
"Block any kubectl command." Blocks kubectl get pods -n production (sim ≈ 0.49).
"It's fine to read any file inside the project's data folder." Allows reads of data/**.

Tips for writing good policies:

  • Be focused. "Block all AWS commands" matches aws s3 ls better than "Never run any AWS or kubectl commands against production." — fewer concepts per policy keeps the embedding tight.
  • Use the dashboard tester. Type a candidate command and watch the live similarity scores update. Anything ≥ 0.40 by default will fire.
  • Tune the threshold. Set AGENT_PROXY_POLICY_THRESHOLD=0.35 to be more permissive, or 0.55 to require very tight matches.

REST API (also used by the dashboard):

GET    /api/policies              # list all
POST   /api/policies               # { kind, description, applies_to?, scope? }
PATCH  /api/policies/:id           # partial update — re-embeds if description changes
DELETE /api/policies/:id
POST   /api/policies/test          # { text, tool } → top-N similarities (no side effects)

To disable policy matching entirely set AGENT_PROXY_POLICIES=0. To scope policies (e.g. one Cursor session is "team:eng" while another is "team:sec"), set AGENT_PROXY_SCOPES=team:eng,global — only policies whose scope is in that set will be evaluated.

Tools exposed over MCP

Side-effect tools — every call is classified and audited:

Tool Purpose
bash Run a shell command. Classified per command.
read_file Read a UTF‑8 file. Sensitive paths flagged.
write_file Write/append a file. Writes to sensitive paths denied.
delete_file Delete a file. Always flagged.
list_directory List a directory.
fetch_url Make an HTTP(S) request. External hosts flagged, executable script downloads denied.

Introspection tools — read-only, no audit entry, designed for the agent to plan and self-audit:

Tool Purpose
classify Pre-flight risk check. Returns the decision (allowed/flagged/denied) the proxy would return for a hypothetical bash command, filesystem path, or URL — without executing it. Use this before committing to a destructive op.
recent_events Read the audit log. Filter by tool, decision, or session. Pass session_id: "current" to summarize what the agent has done so far.
proxy_stats Heartbeat / aggregate counters (totals, ML readiness, current session, dashboard URL).

Policy management tools — persist user-stated guardrails across sessions:

Tool Purpose
add_policy Create a natural-language allow or block rule. Use when the user says "never push to main" or "always allow npm install in this repo" — the description is embedded and matched semantically against future calls.
list_policies List every persisted policy. With against: "<text>" it scores all policies against a hypothetical action and returns them sorted by similarity, so the agent can see which rule will fire and at what threshold.
update_policy Modify an existing policy by id (toggle enabled, retighten the description, scope it to a tool, etc.).
delete_policy Delete a policy by id. Prefer disabling via update_policy so it can be re-enabled later.

The agent is encouraged to use classify proactively whenever it's about to run a command that might be destructive or surprising. Pre-flight is much cheaper than rolling back a denied tool call mid-task.

Run from source (for local development)

git clone https://github.com/<you>/agent-proxy.git
cd agent-proxy
npm install
npm run build
npm start

Or for development with auto-reload:

npm run dev

The dashboard starts at http://localhost:4242 (override with AGENT_PROXY_PORT). When run from a source checkout, the audit DB and model cache stay in <repo>/data/ to keep your dev install separate from the user-level install at ~/.agent-proxy/.

The dashboard is a four-tab single-page app:

  • Overview — today's KPIs, 24h sparkline, recent activity, top firing policies
  • Activity — full event log with search, tool/decision filters, time range, and a drawer that shows the full payload, classifier reasoning, and adjacent session events
  • Policies — manage natural-language allow/block rules; inline tester scores any command against every policy with a similarity bar
  • Insights — stacked area chart of allowed/flagged/denied per hour, tool & decision distributions, ML model stats, top denied/flagged actions, recent sessions

Set in Geist Sans + Geist Mono + Geist Pixel, self-hosted from the geist npm package — no Google Fonts dependency, works offline. Includes a ⌘K command palette and g o/g a/g p/g i keyboard navigation.

MCP config — alternatives

The recommended config is npx -y @roxy-agent/agents (see Quick start above). You can also point your MCP host at a local checkout:

Built from source:

{
  "mcpServers": {
    "agent-proxy": {
      "command": "node",
      "args": ["/absolute/path/to/agent-proxy/dist/index.js"]
    }
  }
}

Source mode (no build step), useful while iterating:

{
  "mcpServers": {
    "agent-proxy": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/agent-proxy/src/index.ts"]
    }
  }
}

Pin a specific version (recommended for teams to avoid surprise updates):

{
  "mcpServers": {
    "agent-proxy": {
      "command": "npx",
      "args": ["-y", "@roxy-agent/agents@0.1.0"]
    }
  }
}

Project rules snippet

Drop this into a project's AGENTS.md or .cursor/rules/ to nudge the agent toward the proxy:

You have access to an `agent-proxy` MCP server. Use it for all side effects:

- Use the `bash` tool from agent-proxy instead of running shell commands directly.
- Use `read_file`, `write_file`, `delete_file`, `list_directory` instead of direct file ops.
- Use `fetch_url` for any HTTP requests.

All actions are logged. The audit dashboard is at http://localhost:4242.

Environment variables

Variable Default Purpose
AGENT_PROXY_PORT 4242 Dashboard HTTP port.
AGENT_PROXY_DATA_DIR ~/.agent-proxy Where the audit DB, license, and model cache live. Falls back to <repo>/data/ if you launched from a source checkout that already has one.
AGENT_PROXY_SESSION_ID random UUID Override the session id (handy for per‑task traceability).
AGENT_PROXY_DISABLE_DASHBOARD unset Set to 1 to skip starting the dashboard.
AGENT_PROXY_ML enabled Set to 0 to disable the embedding-based bash classifier and use rules only.
AGENT_PROXY_ML_MODEL Xenova/all-MiniLM-L6-v2 Hugging Face model id used for the bash classifier embedder.
AGENT_PROXY_ML_CACHE ~/.agent-proxy/models Where transformers.js caches model files.
AGENT_PROXY_POLICIES enabled Set to 0 to disable natural-language policies.
AGENT_PROXY_POLICY_THRESHOLD 0.40 Minimum cosine similarity for a policy to match.
AGENT_PROXY_SCOPES global Comma-separated list of scopes whose policies are active in this session.

Layout

src/
  index.ts             # MCP server entry point
  db.ts                # SQLite + queries
  classifier.ts        # rule-based + ML + policy combined classifier
  policies.ts          # natural-language allow/block policies (CRUD + matching)
  logger.ts            # logEvent + updateEventResult, mirrors to stderr
  dashboard.ts         # Express dashboard (HTML + JSON API)
  tools/
    bash.ts
    filesystem.ts
    network.ts
  ml/
    embedder.ts        # transformers.js feature-extraction pipeline
    prototypes.ts      # labeled bash command prototypes
    bash-classifier.ts # k-NN classifier over embedded prototypes
data/
  audit.db             # auto-created (events + policies tables)
  models/              # cached ONNX model files

Audit DB schema

CREATE TABLE events (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  timestamp   TEXT NOT NULL,
  session_id  TEXT NOT NULL,
  tool        TEXT NOT NULL,
  action_type TEXT NOT NULL,
  payload     TEXT NOT NULL,    -- JSON
  risk_level  TEXT NOT NULL,    -- low|medium|high
  decision    TEXT NOT NULL,    -- allowed|denied|flagged
  result      TEXT,             -- summary string
  duration_ms INTEGER,
  error       TEXT,
  reason      TEXT              -- classifier reason
);

CREATE TABLE policies (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  kind        TEXT NOT NULL,    -- allow|block
  description TEXT NOT NULL,    -- natural language
  scope       TEXT NOT NULL DEFAULT 'global',
  applies_to  TEXT NOT NULL DEFAULT '*',  -- bash|filesystem|network|*
  enabled     INTEGER NOT NULL DEFAULT 1,
  match_count INTEGER NOT NULL DEFAULT 0,
  embedding   BLOB,              -- 384-dim float32, lazily filled
  created_at  TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at  TEXT NOT NULL DEFAULT (datetime('now'))
);