mcp-visor
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Runtime policy enforcement and audit control plane for MCP tool execution. Deterministic, non-AI policy engine that intercepts MCP tools/call requests before execution.
MCP Visor
Deterministic authorization for MCP tool calls.
MCP Visor is a self-hosted policy enforcement proxy for AI agents. It evaluates valid JSON-RPC tools/call requests that include an id before relay and applies allow, deny, approval, redaction, chain, and session-taint rules without an LLM. Notification-form tools/call (no id), duplicate method keys, and JSON-RPC batches containing tools/call are blocked at the proxy; recognizable malformed tools/call attempts with an id fail closed without relay.
MCP Visor is not a model guardrail. It is an action boundary.
Models can request actions. MCP Visor decides whether those actions are allowed.
Model guardrails try to shape what the model says or thinks inside the context window. MCP Visor controls what the agent is allowed to do after a tool call is requested. System prompts can describe intended behavior; MCP Visor enforces policy outside the model.
Paper: MCP Visor: Deterministic Runtime Enforcement · Writing: Spec engineering · Runtime policy enforcement
Why MCP Visor exists
AI agents can now read files, call APIs, run commands, query databases, and modify infrastructure through MCP tools. Prompt-only controls are useful guidance, but they are not an execution boundary.
MCP Visor adds that boundary at the MCP tools/call layer:
AI agent → MCP Visor → policy decision → MCP server
Valid tools/call requests with IDs are evaluated before relay. Notification-form tools/call is dropped without response, including if sent in the post-initialize handshake slot. Duplicate method keys and JSON-RPC batches containing tools/call are blocked before relay. Non-tools MCP notifications and batches still forward unchanged.
Install
go install github.com/themayursinha/mcp-visor/cmd/mcp-visor@latest
Pre-built binaries and checksums are available on the Releases page.
Quick start
# Run the built-in demo proxy
mcp-visor serve --demo
# Supplemental lint. Do not combine --strict with --no-warnings.
# This is not yet a complete fail-closed policy gate; see the threat model.
mcp-visor lint --strict examples/policies/session-taint-egress.yaml
# Proxy a real MCP server through a policy boundary
mcp-visor serve -server <your-mcp-server> -policy policy.yaml
Two-minute action-boundary demo: go run ./examples/demo-runner
What it protects against
- Secret reads followed by outbound exfiltration
- Access to configured sensitive paths such as
/project/.env, SSH keys, kubeconfigs, credentials, and key files; basename-only and policy-urigaps are documented in the threat model - Unsafe shell commands and command-injection patterns
- Unknown or newly introduced tools under default-deny policy
- High-risk actions without human approval
- Tool sequences that look safe individually but become dangerous together
Session-taint egress control
MCP Visor tracks session state, not just individual calls.
file_read("/customer-secrets/tokens.csv")
→ session tainted: sensitive_file_accessed
→ http_post(...) denied by block_sensitive_egress
→ audit log records source, taint, policy rule, sink, and decision
Example policy:
taints:
- name: "sensitive_file_accessed"
source_tools: ["file_read"]
source_patterns: ["**/customer-secrets/**"]
egress_controls:
- name: "block_sensitive_egress"
when_tainted: "sensitive_file_accessed"
sink_tools: ["http_post", "slack_send_message"]
action: deny
Full example: examples/policies/session-taint-egress.yaml
Policy example
version: "1.0"
default_action: deny
servers:
- name: "filesystem"
allowed: true
tools:
- name: "file_read"
allowed: true
rules:
- type: deny_path
patterns: ["**/.env", "**/*.pem", "/etc/passwd"]
- type: allow_path
patterns: ["/home/**", "/tmp/**"]
- name: "shell_exec"
allowed: true
risk: critical
approval_required: true
rules:
- type: deny_command_pattern
patterns: ["bash\\s+-i\\s+>&", "rm\\s+-rf\\s+/"]
tool_chains:
- name: "prevent_exfiltration"
sources:
- server: "*"
tool_pattern: "file_read"
sinks:
- server: "*"
tool_pattern: "(http_post|slack_send_message)"
action: deny
within_calls: 3
More policies: examples/policies/ · Schema reference: docs/policy-model.md
Core capabilities
| Capability | Purpose |
|---|---|
| Default-deny policy | Unknown servers and tools fail closed |
| Argument rules | Restrict paths, commands, queries, recipients, sizes, and repos |
| Pattern redaction | Replace configured matches in arguments and textual Content[].Text output; this is not full structured-payload or complete private-key sanitization |
| Tool-chain detection | Block dangerous sequences such as read → exfiltrate |
| Session taints | Change later authorization decisions after sensitive context is touched |
| Human approval | Gate critical tools before execution |
| Audit log | Selected security and session events, hash-linked within one healthy logger lifetime |
| Policy linting | Validate YAML policy before deployment |
Advanced capabilities include signed decision receipts, Vault Transit signing, webhooks, and experimental remote transport, SIEM, metrics/OTLP, and local dashboard surfaces. The --trace formatters are not yet connected to runtime message paths. See docs/complexity-budget.md and docs/threat-model.md.
Security model
- Deterministic: no LLM in the allow/deny path
- Fail closed: unknown tools are denied by default
- Layered: redaction → policy → taint-aware egress → chain detection → approval → post-allow taint marking
- Observable: selected security events are recorded in JSONL; this is not yet a complete per-call ledger or a chain that survives sink failure/reopen
- Self-hosted: single Go binary; no SaaS dependency required
- Operator-controlled: optional telemetry exports to your Prometheus, OTLP, webhook, or SIEM stack
CLI
mcp-visor serve [flags] Run the proxy
mcp-visor lint [--strict] <policy> Supplemental policy validation; not a complete fail-closed gate
mcp-visor version Print version
Common flags: -server, -policy, -audit-log, -approval-dir, -approval-cli, -demo
Advanced flags: -server-url, -webhook-url, -siem-target, -vault-addr, -metrics-addr, -otel-endpoint, -dashboard, -trace
Full reference: mcp-visor serve -h
Documentation
Architecture · Policy model · Threat model · Complexity budget · Harness
Development
go build ./cmd/mcp-visor/ # build
go test ./... # test
harness/check.sh # fmt + vet + test + evidence manifest
make bench # benchmarks
Roadmap
- v1.0: Proxy, policy engine, redaction, approval, audit, chain detection
- v1.1: Identity/time policies, partial engine hot-reload, CLI approval, experimental remote transport
- v1.2: Session taints and egress controls
- v1.3: Documentation truth, security verification, interoperability evidence, and release hardening
- Future: sandboxing, richer telemetry, optional policy engines
Contributing
See CONTRIBUTING.md. Run harness/check.sh before PRs.
License
MIT — see LICENSE
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found