Skip to main content

Gateways

Gateways enable agents running inside AgentVisor™ to access external services through managed, policy-enforced connections.

Transport vs Gateway

This page covers Gateways - how agents access external services (outbound). For how external clients access AgentVisor agents (inbound), see:

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:

  1. Agents request access through SDK functions
  2. Policy Engine evaluates each request against configured policies
  3. Gateway resolves credentials and manages connections
  4. External service receives authenticated request
  5. Response flows back through the gateway to the agent

Available Gateways

GatewayExternal ServicesSDK ModuleConfigurationStatus
MCP GatewayMCP servers (GitHub, Slack, filesystem, etc.)agentvisor.mcp, agentvisor.langchain.mcpmcp.servers[]Available
A2A GatewayA2A-compatible agents (research, code, partner agents)agentvisor.a2a, agentvisor.langchain.a2aa2a_gateway.agents[]Available
HTTP ProxyAny 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:

  1. Stores credentials on the host side
  2. Injects them when forwarding requests to external services
  3. 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:

mcp.call_tool
{"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}
a2a.send_task
{"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 but not yet populated by either gateway — expect an empty string in current builds.

A policy denial never reaches this record — a denied call is logged separately, at the point the decision is made (internal/host/grpcserver/mcp_handlers.go/a2a_handlers.go), 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 targetPathWhat's configurable
External MCP serverMCP Gatewaymcp.servers[] — adding an entry grants access via the gateway; removing it revokes access entirely. There is no "direct" alternative.
External A2A agentA2A Gatewaya2a_gateway.agents[] — same shape: add to grant access, remove to revoke.
Any HTTP/HTTPS APIHTTP ProxyCannot 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=gvisor and --sandbox=docker (no other network route exists). In --sandbox=none — the development-only mode without containment guarantees — the agent shares the host network and can bypass the injected HTTPS_PROXY via NO_PROXY or 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.

AspectWithout GatewayWith Gateway
NetworkAgent has network accessAgent isolated (no direct network access)
CredentialsIn agent memoryHost-side only
PolicyApplication-levelPolicy-enforced at boundary
AuditVariesConsistent logging
Connection mgmtPer-agentPooled, efficient

The gateway pattern aligns with AgentVisor's security model: agents execute, policy governs.

Configuration Overview

Gateways are configured in the agentvisor.yaml file:

# MCP Gateway - access external MCP servers
mcp:
servers:
- name: filesystem
transport: stdio
command: ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/data"]

- name: github
transport: streamable_http
url: "https://api.githubcopilot.com/mcp/"
credentials:
type: bearer_token
source: env
env_var: GITHUB_TOKEN

pool:
max_size: 100
idle_timeout: 5m

# A2A Gateway - access external A2A agents
a2a_gateway:
agents:
- name: research-agent
url: "https://research.example.com"
credentials:
type: bearer_token
source: env
env_var: RESEARCH_AGENT_TOKEN

- name: internal-agent
url: "https://internal.example.com"
credentials:
type: principal_passthrough

pool:
max_size: 100
idle_timeout: 5m

defaults:
timeout: 60s
retry:
max_attempts: 3

See the Configuration Reference for complete options.