Skip to main content

tunnel

Manage tunnel connections for exposing local services publicly. Perfect for mobile device testing and sharing development builds.

Synopsis

tunnel {action: "<action>", ...params}

Actions

ActionDescription
startStart a tunnel to expose a local port
stopStop a running tunnel
statusGet tunnel status and public URL
listList all active tunnels

Supported Providers

ProviderBinaryNative SupportDescription
cloudflarecloudflaredYesFree quick tunnels via trycloudflare.com
ngrokngrokYesPopular tunneling service with stable URLs
TailscaletailscaleManualPersistent URLs for Tailscale users

Choosing a Provider

Use CaseRecommended Provider
Quick testing, no signupCloudflare
Stable URLs for webhooksngrok (paid)
Team already on TailscaleTailscale Funnel
Maximum privacyTailscale Funnel

start

Start a tunnel to expose a local port publicly.

tunnel {
action: "start",
id: "app",
provider: "cloudflare",
local_port: 8080
}

Parameters:

ParameterTypeRequiredDefaultDescription
idstringYes-Unique tunnel identifier
providerstringYes-Tunnel provider: cloudflare or ngrok
local_portintegerYes-Local port to tunnel
local_hoststringNolocalhostLocal host to tunnel
binary_pathstringNo(from PATH)Path to tunnel binary
proxy_idstringNo-Proxy ID to auto-configure with public URL

Response:

{
"id": "app",
"provider": "cloudflare",
"state": "connected",
"public_url": "https://random-words-here.trycloudflare.com",
"local_addr": "localhost:8080"
}

Auto-Configure Proxy

When proxy_id is specified, the tunnel automatically updates the proxy's public_url once the tunnel URL is discovered:

// Start proxy first
proxy {action: "start", id: "app", target_url: "http://localhost:3000", bind_address: "0.0.0.0"}
// Response: {listen_addr: "0.0.0.0:45849"}

// Start tunnel with proxy auto-configuration
tunnel {
action: "start",
id: "app",
provider: "cloudflare",
local_port: 45849,
proxy_id: "app"
}

The proxy will now correctly rewrite URLs to use the tunnel's HTTPS scheme.

stop

Stop a running tunnel.

tunnel {action: "stop", id: "app"}

Response:

{
"success": true,
"id": "app",
"message": "Tunnel stopped"
}

status

Get tunnel status and public URL.

tunnel {action: "status", id: "app"}

Response:

{
"id": "app",
"provider": "cloudflare",
"state": "connected",
"public_url": "https://random-words-here.trycloudflare.com",
"local_addr": "localhost:8080"
}

Tunnel States

StateDescription
idleNot started
startingStarting up, waiting for URL
connectedRunning with public URL available
failedFailed to start or crashed
stoppedStopped by user

list

List all active tunnels.

tunnel {action: "list"}

Response:

{
"count": 2,
"tunnels": [
{
"id": "frontend",
"provider": "cloudflare",
"state": "connected",
"public_url": "https://abc.trycloudflare.com",
"local_addr": "localhost:8080"
},
{
"id": "api",
"provider": "ngrok",
"state": "connected",
"public_url": "https://xyz.ngrok-free.app",
"local_addr": "localhost:4000"
}
]
}

Installation Requirements

Cloudflare (cloudflared)

# macOS
brew install cloudflare/cloudflare/cloudflared

# Linux
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb

# Windows
winget install --id Cloudflare.cloudflared

ngrok

# macOS
brew install ngrok/ngrok/ngrok

# Linux/Windows
# Download from https://ngrok.com/download

# Configure auth token (required)
ngrok config add-authtoken <your-token>

Tailscale Funnel (Manual Setup)

Tailscale Funnel provides persistent, memorable URLs for teams already using Tailscale. While not natively integrated, it works well with agnt's proxy.

# Install Tailscale (if not already installed)
# macOS
brew install tailscale

# Linux
curl -fsSL https://tailscale.com/install.sh | sh

# Windows
winget install --id Tailscale.Tailscale

Setup workflow:

# 1. Start agnt proxy on all interfaces
proxy {action: "start", id: "app", target_url: "http://localhost:3000", bind_address: "0.0.0.0"}
# Note the port from listen_addr (e.g., 45849)

# 2. In a separate terminal, start Tailscale Funnel
tailscale funnel 45849
# Output: https://your-machine.tailnet-name.ts.net

# 3. Configure proxy with the public URL (for proper URL rewriting)
proxy {action: "start", id: "app", target_url: "http://localhost:3000", bind_address: "0.0.0.0", public_url: "https://your-machine.tailnet-name.ts.net"}

Requirements:

  • Tailscale installed and authenticated (tailscale up)
  • Funnel enabled on your tailnet (requires tailnet admin to enable)
  • HTTPS certificates are automatically managed by Tailscale

Advantages over other providers:

  • Persistent URL (based on your machine name)
  • No third-party accounts if you already use Tailscale
  • End-to-end encrypted
  • Works with Tailscale ACLs for access control

Real-World Patterns

Mobile Device Testing

// 1. Start your dev server
run {script_name: "dev"}

// 2. Wait for it to be ready
proc {action: "output", process_id: "dev", grep: "ready"}

// 3. Start proxy on all interfaces
proxy {
action: "start",
id: "app",
target_url: "http://localhost:3000",
bind_address: "0.0.0.0"
}
// Note the listen_addr port from the response

// 4. Start tunnel with auto-configuration
tunnel {
action: "start",
id: "app",
provider: "cloudflare",
local_port: 45849,
proxy_id: "app"
}
// Share the public_url with your mobile device

Multiple Environments

// Frontend tunnel
tunnel {action: "start", id: "frontend", provider: "cloudflare", local_port: 3000}

// API tunnel
tunnel {action: "start", id: "api", provider: "cloudflare", local_port: 4000}

// List all
tunnel {action: "list"}

BrowserStack Integration

Use agnt tunnels with BrowserStack's MCP server for automated mobile testing:

// claude_desktop_config.json
{
"mcpServers": {
"agnt": {
"command": "agnt",
"args": ["mcp"]
},
"browserstack": {
"command": "npx",
"args": ["@anthropic-ai/browserstack-mcp"],
"env": {
"BROWSERSTACK_USERNAME": "your_username",
"BROWSERSTACK_ACCESS_KEY": "your_key"
}
}
}
}

Then use both tools together:

  1. Start your proxy and tunnel with agnt
  2. Use BrowserStack MCP to run tests on the tunneled URL
  3. Capture errors and screenshots through the instrumented proxy

Error Responses

Tunnel Not Found

{
"error": "tunnel not found",
"id": "nonexistent"
}

Binary Not Found

{
"error": "cloudflared not found in PATH"
}

Tunnel Already Exists

{
"error": "tunnel already exists",
"id": "app"
}

See Also