Gateways
Gateways enable agents running inside AgentVisor™ to access external services through managed, policy-enforced connections.
This page covers the outbound direction. For the inbound direction — how external clients access AgentVisor agents — see API Transports.
The Problem
Agents running in AgentVisor's sandbox have no direct network access. This isolation is intentional - it prevents credential theft, data exfiltration, and policy bypass.
However, agents need to interact with external services:
- Call tools on MCP servers (GitHub, Slack, filesystem)
- Communicate with other AI agents
- Access databases and APIs
Gateways solve this by providing a controlled, policy-enforced path from agents to external services.
How Gateways Work
┌──────────────────────────────────────────────────────────────────────┐
│ External Services │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ MCP Servers │ │ A2A Agents │ │ Other APIs │ │
│ │ • GitHub │ │ • Research │ │ (via HTTP │ │
│ │ • Slack │ │ • Code │ │ proxy) │ │
│ │ • Filesystem │ │ • Partner │ │ │ │
│ └────────▲─────────┘ └────────▲─────────┘ └───────▲────────┘ │
│ │ │ │ │
├───────────┼───────────────────────┼──────────────────────┼──── ───────┤
│ Host Runtime (Gateways) │ │ │
│ │ │ │ │
│ ┌────────┴─────────┐ ┌────────┴─────────┐ ┌───────┴────────┐ │
│ │ MCP Gateway │ │ A2A Gateway │ │ HTTP Proxy │ │
│ │ │ │ │ │ │ │
│ │ • Connection │ │ • Connection │ │ • Credential │ │
│ │ pooling │ │ pooling │ │ injection │ │
│ │ • Credential │ │ • Credential │ │ • TLS term. │ │
│ │ injection │ │ injection │ │ │ │
│ │ • Policy check │ │ • Policy check │ │ │ │
│ └────────▲──── ─────┘ └────────▲─────────┘ └───────▲────────┘ │
│ │ │ │ │
│ └───────────────────────┴──────────────────────┘ │
│ │ │
│ Policy Engine │
│ │ │
├───────────────────────────────────┼──────────────────────────────────┤
│ Guest Sandbox (isolated) │ │
│ │ │
│ ┌────────────────────────────────┴─────────────────────────────┐ │
│ │ Agent Code │ │
│ │ │ │
│ │ from agentvisor.mcp import list_tools, call_tool │ │
│ │ from agentvisor.a2a import send_task, get_task │ │
│ │ │ │
│ │ # Agent calls SDK → gRPC → Gateway → External Service │ │
│ └───────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
The gateway pattern ensures:
- Agents request access through SDK functions
- Policy Engine evaluates each request against configured policies
- Gateway resolves credentials and manages connections
- External service receives authenticated request
- Response flows back through the gateway to the agent
Available Gateways
| Gateway | External Services | SDK Module | Configuration | Status |
|---|---|---|---|---|
| MCP Gateway | MCP servers (GitHub, Slack, filesystem, etc.) | agentvisor.mcp, agentvisor.langchain.mcp | mcp.servers[] | Available |
| A2A Gateway | A2A-compatible agents (research, code, partner agents) | agentvisor.a2a, agentvisor.langchain.a2a | a2a_gateway.agents[] | Available |
| HTTP Proxy | Any HTTP/HTTPS API (LLM providers, REST APIs, etc.) | Standard HTTP libraries (transparent via HTTPS_PROXY) | proxy.credentials[] | Available |
Why Gateways?
Credential Isolation
Real credentials never enter the sandbox. The gateway:
- Stores credentials on the host side
- Injects them when forwarding requests to external services
- Returns responses without exposing secrets
# Host configuration - credentials stay here
mcp:
servers:
- name: github
credentials:
type: bearer_token
source: env
env_var: GITHUB_TOKEN # Real token, never visible to agent
# Agent code - no credentials needed
tools = get_mcp_tools(server_name="github")
result = call_tool("github", "create_issue", {"title": "Bug fix"})
Policy Enforcement
Every gateway operation is evaluated by the policy engine before execution:
Agent calls: call_tool("github", "delete_repo", {...})
│
▼
Policy Engine: Can this principal delete repos?
│
┌───────┴───────┐
▼ ▼
ALLOWED DENIED
│ │
Execute call Return error
This enables fine-grained control:
resources:
# Read operations allowed
- name: github-read
selector:
- "mrn:agentvisor:mcp:github/get_.*"
- "mrn:agentvisor:mcp:github/list_.*"
group: "mrn:agentvisor:resourcegroup:allowed"
# Write operations require elevated permissions
- name: github-write
selector:
- "mrn:agentvisor:mcp:github/create_.*"
- "mrn:agentvisor:mcp:github/update_.*"
group: "mrn:agentvisor:resourcegroup:elevated"
Stdio Server Isolation
For MCP servers launched via the stdio transport, AgentVisor goes a step further: each stdio server runs in its own per-server sandbox (gVisor or Docker, matching guest.sandbox), preventing arbitrary npm/PyPI code from running as a host subprocess. See MCP Gateway: Sandboxed Execution of stdio Servers for details.
Connection Management
Gateways manage connections to external services efficiently:
- Connection pooling: Reuse connections across requests
- Per-principal isolation: Separate connections when credentials differ
- Automatic cleanup: Close idle connections to conserve resources
- Health monitoring: Detect and recover from connection failures
Audit Logging
Every MCP and A2A gateway call that policy allows emits one audit record, logged as
<gateway>.<operation> — the same name as the OTEL span for that call, so logs and
traces correlate directly. Both gateways share one record shape:
{"level":"INFO","time":"2026-08-10T18:32:04.512Z","component":"[mcp]","msg":"mcp.call_tool","gateway":"mcp","endpoint":"github","target":"create_issue","transport":"streamable_http","principal_id":"a1b2c3d4e5f6a7b8","agent_name":"research-agent","execution_id":"","status":200,"outcome":"ok","latency":"184ms","req_bytes":312,"resp_bytes":1024}
{"level":"INFO","time":"2026-08-10T18:32:07.901Z","component":"[a2a.client]","msg":"a2a.send_task","gateway":"a2a","endpoint":"research-agent","transport":"http","principal_id":"a1b2c3d4e5f6a7b8","agent_name":"research-agent","execution_id":"","status":200,"outcome":"ok","latency":"342ms","req_bytes":256,"resp_bytes":890}
Key fields: principal_id is a pseudonym for the caller, never the raw JWT subject;
target is the tool name / resource URI (MCP) or task id (A2A), present only when the
operation has one; outcome is ok or error, with error_class added only on error
(one of the coarse codes in Troubleshooting: Interpreting GET /ready).
execution_id is reserved on the shared record; both gateways always leave it
empty.
A policy denial never reaches this record — a denied call is logged separately,
at the point the decision is made, e.g. MCP tool call denied by policy with
server, tool, principal, and reason fields.
By default, the record omits the full URL and headers; set
AGENTVISOR_MCP_LOG_LEVEL=full / AGENTVISOR_A2A_GATEWAY_LOG_LEVEL=full to include a
truncated URL and sanitized headers on every call. See the
MCP Gateway and
A2A Gateway troubleshooting sections for the
full connection-diagnosis flow (probe commands, GET /ready, and debug logging).
Summary
| Outbound target | Path | What's configurable |
|---|---|---|
| External MCP server | MCP Gateway | mcp.servers[] — adding an entry grants access via the gateway; removing it revokes access entirely. There is no "direct" alternative. |
| External A2A agent | A2A Gateway | a2a_gateway.agents[] — same shape: add to grant access, remove to revoke. |
| Any HTTP/HTTPS API | HTTP Proxy | Cannot be turned off. HTTPS_PROXY is forced into the agent environment. proxy.credentials[] only controls credential substitution rules, not whether the proxy is in the path. |
Note: "Cannot be turned off" is structurally enforced in
--sandbox=gvisorand--sandbox=docker(no other network route exists). In--sandbox=none— the development-only mode, which is unconfined — the agent shares the host network and can bypass the injectedHTTPS_PROXYviaNO_PROXYor by opening raw sockets directly. See HTTP Proxy → Bypassing the Proxy.
Gateways aren't optional features that can be toggled — they're the only routes out of the sandbox. Configuration controls what destinations exist, not whether the gateway sits in front. So the table below contrasts the AgentVisor gateway pattern against the unmanaged baseline, not between two AgentVisor configurations.
| Aspect | Without Gateway | With Gateway |
|---|---|---|
| Network | Agent has network access | Agent isolated (no direct network access) |
| Credentials | In agent memory | Host-side only |
| Policy | Application-level | Policy-enforced at boundary |
| Audit | Varies | Consistent logging |
| Connection mgmt | Per-agent | Pooled, efficient |
The gateway pattern aligns with AgentVisor's security model: agents execute, policy governs.
Configuration Overview
Each gateway is a list of destinations in agentvisor.yaml — mcp.servers[] for MCP servers,
a2a_gateway.agents[] for A2A agents — plus a shared pool (connection reuse) block and, for
A2A, defaults (timeout/retry). Adding an entry grants access through the gateway; removing it
revokes access entirely — there's no separate on/off switch for an individual destination.
See the MCP Gateway and A2A Gateway configuration references for complete field lists and examples.
Related Documentation
- MCP Gateway - Detailed MCP Gateway documentation
- A2A Gateway Guide - Detailed A2A Gateway documentation
- API Transports - How external clients access agents (opposite direction)
- Credential Brokering - How credentials are protected
- Policy Enforcement - How policies control access
- HTTP Proxy - Direct HTTP access (for non-gateway APIs)
- Configuration Reference - Complete configuration options