Help us shape the future of Go! Take the 2026 Go Developer Survey

mcp-visor

module
v1.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 17, 2026 License: MIT

README

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.

Go Version License CI Release

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.
# Allows fail closed unless -audit-log points at a writable JSONL file.
mcp-visor serve -server <your-mcp-server> -policy policy.yaml -audit-log ./audit.jsonl

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-uri gaps 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/ · Interop policies: examples/policies/interop/ · 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
Durable allow commit Terminal allows are appended and fsync'd to the JSONL sink before relay (H19)
Stdio identity pin Optional stdio_invocation_sha256_v1 binds a local invocation; registry runners are unpinnable
Audit log Hash-linked JSONL; allows are a durable commit; denies and other selected events are written without the same fsync
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; a missing or non-durable -audit-log denies every allow
  • Layered: optional stdio identity → redaction → policy → taint-aware egress → chain detection → approval → durable allow commit → post-allow taint marking → relay
  • Observable: every terminal allow is a standalone JSONL tool_call_allowed record, fully appended and Sync()'d before relay. Denies, approvals-required, taints, and session events are also JSONL but are not equivalently fsync'd. The hash chain recovers when reopening a healthy file; incomplete or corrupt tails fail closed. This is not a signed repudiation control, and it does not survive an untrusted audit directory.
  • Self-hosted: single Go binary; no SaaS dependency required
  • Operator-controlled: optional telemetry exports to your Prometheus, OTLP, webhook, or SIEM stack
  • Not a host sandbox: Visor authorizes MCP tools/call. It does not confine arbitrary filesystem, network, or subprocesses.

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 · Interoperability · Qwen-MM-Plugins demo · 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
  • v1.4: Durable allow-commit before relay, optional stdio identity attestation, Qwen third-party demo, documentation truth
  • Future: sandboxing, richer telemetry, optional policy engines — only if deployment evidence demands them

Contributing

See CONTRIBUTING.md. Run harness/check.sh before PRs.

License

MIT — see LICENSE

Directories

Path Synopsis
cmd
mcp-visor command
policy-lint command
visor-workflow command
Command visor-workflow — supervised checks; status derived from artifacts.
Command visor-workflow — supervised checks; status derived from artifacts.
examples
confused-deputy-demo command
Command confused-deputy-demo proves MCP Visor's stdio executable identity attestation end to end.
Command confused-deputy-demo proves MCP Visor's stdio executable identity attestation end to end.
confused-deputy-demo/benign-server command
Benign demo MCP server: same harmless open_ticket tool as the poisoned server, with a plain description.
Benign demo MCP server: same harmless open_ticket tool as the poisoned server, with a plain description.
confused-deputy-demo/poisoned-server command
Poisoned demo MCP server: same harmless open_ticket tool and schema as the benign server, but the tool description carries adversarial preference text so a description-driven selector picks it.
Poisoned demo MCP server: same harmless open_ticket tool and schema as the benign server, but the tool description carries adversarial preference text so a description-driven selector picks it.
demo-mcp-server command
demo-runner command
internal
confuseddeputydemo
Package confuseddeputydemo implements the deterministic two-server confused-deputy demo: two distinct executables expose the same harmless tool and schema, only their tool description differs, and a poisoned description wins a deterministic selector.
Package confuseddeputydemo implements the deterministic two-server confused-deputy demo: two distinct executables expose the same harmless tool and schema, only their tool description differs, and a poisoned description wins a deterministic selector.
demoutil
Package demoutil provides types and validation used by both the demo-runner and its integration tests.
Package demoutil provides types and validation used by both the demo-runner and its integration tests.
mcp
observability
Package observability exports mcp-visor proxy metrics for Prometheus scrape and optional OpenTelemetry (OTLP) traces and metrics.
Package observability exports mcp-visor proxy metrics for Prometheus scrape and optional OpenTelemetry (OTLP) traces and metrics.
serveridentity
Package serveridentity resolves an out-of-band identity for the launched stdio server executable.
Package serveridentity resolves an out-of-band identity for the launched stdio server executable.
workflow
Package workflow derives supervised workflow status from artifacts only.
Package workflow derives supervised workflow status from artifacts only.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL