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. Comes with a dashboard you can open in your terminal or in any browser 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 doctorprotect 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 cursorCursor 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:
Creates an audit DB at
~/.agent-proxy/audit.db.Starts a local dashboard at
localhost:4242.Begins streaming each tool call (metadata only, by default) to the hosted dashboard.
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.
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.
If you skip pairing, the proxy still uploads under an anonymous device id; you can view that device's data by clicking the local dashboard's "Open hosted view" link, which sets a per-device cookie.
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_PORT |
4242 |
Local dashboard port |
AGENT_PROXY_DISABLE_DASHBOARD |
unset | Set to 1 to skip the local web UI entirely |
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.tsThen 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 hosted dashboard lives in web/ and is a vanilla Next.js app deployed to Vercel. To run your own:
cd web
npm install
npm run bundle # copies dashboard JS/CSS from ../src/dashboard
DATABASE_URL=postgres://... npm run devSet AGENT_PROXY_CLOUD_URL on every machine running the local proxy to point at your deployment.
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. |
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
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 filesAudit 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'))
);