JSPM

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

MCP server that proxies all agent actions, classifies risk, enforces natural-language policies, and logs to a local SQLite audit DB. View activity in the hosted dashboard at roxy-agent-web.vercel.app.

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 sub-millisecond risk classifier and an audit log. Streams to the hosted dashboard at roxy-agent-web.vercel.app.

Quick start

For the safest onboarding, run the client hardening flow instead of only pasting an MCP snippet:

npx -y @roxy-agent/agents protect --all
npx -y @roxy-agent/agents connect
npx -y @roxy-agent/agents doctor

protect installs Roxy MCP and a client-specific lockdown profile where the client supports one. doctor verifies the local protection level:

  • Advisory: MCP/rules are installed, but the client may still expose direct built-in tools.
  • Enforced: direct shell/file/network paths are denied, hooked, or sandboxed for the detected client.
  • Enterprise locked: managed config or runtime policy prevents local users from weakening the profile.
  • Bypass detected: Roxy is configured, but no audited events are appearing.

You can scope the install to one client:

npx -y @roxy-agent/agents protect --client claude-code
npx -y @roxy-agent/agents protect --client codex
npx -y @roxy-agent/agents protect --client cursor

To completely remove Roxy (MCP entries, hooks, Cursor rules, Codex profile, and dashboard pairing):

npx -y @roxy-agent/agents teardown

teardown is the inverse of setup: it runs unprotect --all, clears device pairing from ~/.agent-proxy/license.json, and removes everything protect / setup wrote. Add --purge-data to also delete the local audit DB and model cache under ~/.agent-proxy. Use --keep-pairing if you only want to remove MCP/hooks but stay linked to the hosted dashboard.

unsetup is an alias for teardown. Preview changes with --dry-run.

For a lighter uninstall (client configs only, no pairing reset):

npx -y @roxy-agent/agents unprotect --all

Cursor currently remains advisory unless you run it inside a Roxy-controlled runtime or custom mode that removes direct side-effect tools. Claude Code gets the strongest local profile today: Roxy MCP, direct-tool deny rules, Roxy MCP allowlisting, and a PreToolUse bypass hook.

Manual MCP setup

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"]
    }
  }
}

Reload MCP servers (or restart the host). On first launch npx downloads the package and:

  1. Creates an audit DB at ~/.agent-proxy/audit.db.

  2. Begins streaming each tool call (metadata only, by default) to the hosted dashboard.

  3. Prints a one-time pairing URL like:

    [agent-proxy] ──────────────────────────────────────────────
    [agent-proxy]  Sign in to view this proxy's audit log online:
    [agent-proxy]    https://roxy-agent-web.vercel.app/pair?code=W79M-5BVD
    [agent-proxy]  (Pairing code: W79M-5BVD — valid 10 minutes.)
    [agent-proxy] ──────────────────────────────────────────────

    Open it. The page asks you to sign in (or create an account), then "Link this device" attaches the running proxy to your account. The proxy detects the link within ~3 seconds and from then on uploads under your user id.

  4. Visit roxy-agent-web.vercel.app/dashboard. Once signed in, it shows events from all devices you've paired — codex on your laptop, claude on your desktop, etc.

You can also create and wait on a pairing link directly:

npx -y @roxy-agent/agents connect

Privacy / what gets uploaded

Cloud sync sends metadata only by default:

  • Tool name + action type (e.g. bash.execute)
  • Risk level, decision, reason
  • A 240-character truncation of the command / path / URL the dashboard already shows
  • Timestamps, durations, error strings (truncated)

It does not send full stdout/stderr, file contents, or response bodies unless you set AGENT_PROXY_CLOUD_PAYLOADS=full. To disable cloud sync entirely:

AGENT_PROXY_CLOUD_SYNC=0 npx -y @roxy-agent/agents

(or set it as an env value in your MCP config block).

Environment variables

Variable Default Meaning
AGENT_PROXY_DATA_DIR ~/.agent-proxy Where audit.db and license.json live
AGENT_PROXY_ML_CACHE ~/.agent-proxy/models Where the embedding model is cached
AGENT_PROXY_CLOUD_SYNC 1 Set to 0 to disable hosted-dashboard uploads
AGENT_PROXY_CLOUD_URL https://roxy-agent-web.vercel.app Override the hosted dashboard URL (self-host)
AGENT_PROXY_CLOUD_PAYLOADS metadata Set to full to send raw command/result bodies
AGENT_PROXY_CLOUD_INTERVAL_MS 5000 How often to flush the upload queue
AGENT_PROXY_PAIRING 1 Set to 0 to skip the device-pairing prompt on launch
AGENT_PROXY_SESSION_ID random UUID Override the session id for traceability

Run from source (for local development)

git clone https://github.com/dwang88/agent-proxy
cd agent-proxy
npm install
npm run build
npm run dev   # tsx src/index.ts

When working inside this repo, npx -y @roxy-agent/agents <cmd> can fail with sh: agents: command not found because npm does not create a local agents bin for the package root. Use one of these instead:

npm run teardown          # or setup, protect, connect, doctor, unprotect
node dist/index.js teardown
npm link                  # one-time; then npx works from this directory too

Then point your MCP config at node /path/to/agent-proxy/dist/index.js instead of npx -y @roxy-agent/agents.

Self-hosting the dashboard

The dashboard lives in web/ and is a Next.js app deployed to Vercel. To run it locally:

cd web
npm install
npm run dev        # starts Docker Postgres + Next.js (accounts persist)

npm run dev boots a local Postgres container (docker compose up -d db) and connects to postgres://roxy:roxy@127.0.0.1:5432/roxy by default. For production or a remote DB, set DATABASE_URL (Neon, Supabase, etc.). Use npm run dev:memory to skip Postgres and use an in-memory DB (resets on restart).

SQLite vs Postgres: the local Roxy proxy already stores audit events in SQLite (~/.agent-proxy/audit.db). The hosted dashboard uses Postgres for accounts, pairing, and multi-device sync — same schema as production. Local Postgres via Docker is the supported path; SQLite would require rewriting 100+ Postgres-specific queries.

Set AGENT_PROXY_CLOUD_URL on every machine running the local proxy to point at your deployment. The local proxy no longer serves a browser dashboard; all UI lives in the hosted app.

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 https://roxy-agent-web.vercel.app/dashboard.

Environment variables

Variable Default Purpose
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_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.
AGENT_PROXY_POLICY_SUGGEST_API_KEY unset OpenAI-compatible API key for Policies → Industry templates (LLM-generated policy suggestions).
AGENT_PROXY_POLICY_SUGGEST_BASE_URL https://api.openai.com/v1 Chat Completions API base URL (OpenRouter, Azure, etc.).
AGENT_PROXY_POLICY_SUGGEST_MODEL gpt-5.4 Model id for suggestions.
AGENT_PROXY_POLICY_SUGGEST_HEADERS unset Optional JSON object of extra HTTP headers for the completions request.

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
  cloud-sync.ts        # Hosted dashboard upload, pairing, and command polling
  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'))
);