MCP Tools Reference
Port Forwarding

Port Forwarding Tools

💡

Available on every plan. Free includes one forward per agent; paid plans allow up to 10 per agent.

Your agent's identity is a real, public IPv6 address. Port forwarding publishes a port of your own machine on it, at your agent's hostname. It needs the Route6 client running on that machine — the binary or the container.

Inbound is hub-terminated: Route6 accepts the connection on your agent's own public address at the gateway and relays it to your client over the connection it already holds. Your machine opens no listening socket to the internet, and needs no inbound firewall rule, no router configuration and no public IPv4.

Which address family answers. A forward answers over IPv6 on every plan. On a paid plan the same port also answers over IPv4, at the same hostname — verified, no add-on. On Free, IPv4-only senders reach you through the port-less webhook URL.

The IPv4 address is shared between agents, so hand out the hostname, not the address, and read the public port from the reply. The Static IPv4 add-on (opens in a new tab) gives an agent an address of its own — that is what lets you demand an exact public port with public_port.

port_forward { action: "create" }

ParameterTypeRequiredDescription
portnumber✓The port your service listens on. Also the public port you would like — if it is taken, you are assigned the next free one and the reply says which.
internal_portnumber—The local port, when it differs from the public one you asked for
public_portnumber—Demand this exact public port (1024–65535) and fail rather than take another. Requires the Static IPv4 add-on.
external_portnumber—Older name for port on create (still accepted)
protocol"tcp"—TCP only today ("udp" is accepted by the schema but not implemented)
ttl_secondsnumber—Auto-expire after N seconds (60–86400). Omit for persistent.
descriptionstring—Label for your reference
scope"public" | "mesh" | "both"—Exposure — default "public"
webhookboolean—Also publish the port-less HTTPS URL — see below. Default false.
allowed_sourcesstring[]—Restrict who may connect — see below

Read the endpoint from the reply rather than assuming the port you passed:

{
  "external_port": 18080,
  "internal_port": 18080,
  "scope": "public",
  "endpoint": "claude.on.route6.me:18080",
  "endpoint_v6": "[2001:67c:3f4:801c::4:1]:18080",
  "endpoint_v4": "176.123.57.184:18080",
  "urls": {
    "ipv6": "https://claude.on.route6.me:18080/",
    "ipv4": "https://claude.on.route6.me/"
  },
  "exposure": "PUBLIC — reachable from the entire internet"
}

Exposure (scope)

  • "public" (default) — reachable from the internet.
  • "mesh" — reachable only by agents in your team mesh, at you.mesh.route6.me:<port>. There is no public listener at all. Teammates reach it through their own client's proxy — see mesh endpoints.
  • "both" — both of the above.

The reply always echoes what was actually opened ("PUBLIC — reachable from the entire internet" vs "mesh-only — reachable only by agents in your team mesh").

port_forward { action: "create", port: 8080 }

→ you.on.route6.me:8080 (or the port the reply names) reaches localhost:8080.

port_forward { action: "create", port: 8080, scope: "mesh" }

→ teammates reach http://you.mesh.route6.me:8080/; the internet cannot.

port_forward { action: "create", port: 8888, ttl_seconds: 3600 }

→ auto-expires after one hour.

Receiving webhooks and HTTPS without a port

Most webhook senders — Stripe among them — are IPv4-only and want a plain https:// URL with no port. Set webhook: true and the reply's urls.ipv4 is exactly that:

port_forward { action: "create", port: 8080, webhook: true }
"urls": {
  "ipv6": "https://you.on.route6.me:8080/",
  "ipv4": "https://you.on.route6.me/"
}

Paste the port-less URL into Stripe, GitHub, or whatever is calling you. It works on every plan including Free, with no add-on.

💡

The port-less URL answers over both IPv4 and IPv6 on the standard HTTPS port, and is matched to your forward by hostname. Route6 terminates TLS on it with the *.on.route6.me certificate, so your local service receives plain HTTP, and the path arrives untouched — https://you.on.route6.me/stripe reaches your service as /stripe.

The port URL (https://you.on.route6.me:<port>/) is relayed as raw TCP, so if you want TLS there — or the service is not HTTP — terminate it yourself.

Limits worth knowing:

  • One webhook URL per agent, on every plan — an agent has one hostname. Point several senders at the same URL with different paths and route on the path. Asking for a second answers 409 naming the port that already holds it.
  • Not combinable with scope: "mesh" — a webhook needs a public front door; asking for both is a 400. Use scope: "both" to serve the mesh and the webhook.
  • Deleting the forward withdraws the URL.

port_forward { action: "list" }

List your active forwards with their ports, scope and status. No parameters.

For any forward with a source restriction, the reply also shows allowed_sources (the rules as you wrote them), effective_sources (the exact addresses and ranges being enforced right now, with every preset expanded) and, for each preset you used, when Route6 last refreshed its ranges.

Note that a listed forward is not by itself proof of reachability — check the URL from outside if you need certainty.

port_forward { action: "delete" }

Remove a forward.

ParameterTypeRequiredDescription
external_portnumber✓Public port of the forward to remove — from the create reply or list

Restricting who may reach a forward

A public forward is reachable from the entire internet by default. You can narrow that to specific sources with allowed_sources — a list that may mix exact addresses, CIDR ranges and named presets whose ranges Route6 keeps current.

One port_forward { action: "create" } call gives you a public HTTPS webhook URL that only Stripe can reach:

{
  "action": "create",
  "port": 8080,
  "webhook": true,
  "allowed_sources": ["preset:stripe"]
}
RuleExampleNotes
Address203.0.113.7, 2001:db8::1exact match
CIDR203.0.113.0/24, 2a0a:a440::/29either family
Presetpreset:stripe, preset:githubthe vendor's published sender ranges

Combine as many as you need. Presets and literals live in the same list, and several presets can be used together — the result is the union of everything listed. This is also how you allow a provider Route6 has no preset for: name its published addresses directly alongside the presets you do use.

{
  "allowed_sources": [
    "preset:stripe",
    "preset:github",
    "35.246.21.235",
    "198.51.100.0/24"
  ]
}

port_forward_secure

Change who may reach a forward you already have — without recreating it. The forward keeps serving throughout and live connections are not dropped, so tightening or relaxing a rule is not an outage and a webhook does not miss deliveries while you edit it.

ParameterTypeRequiredDescription
external_portnumber✓The forward to change
actionenum✓set, add, remove or clear
allowed_sourcesstring[]for set/add/removeThe rules to apply
ActionEffect
setReplace the whole list. An empty list denies every source.
addAppend to the list. If the forward had no restriction, this creates one.
removeDrop the listed entries. Never creates a restriction.
clearRemove the restriction entirely — any source may connect.
{ "external_port": 8080, "action": "add", "allowed_sources": ["preset:github"] }

add and remove exist so you can grow or shrink a list without restating it, which is what you want when the list is long and you only mean to change one entry.

Two deliberate refusals, both because guessing would be dangerous:

  • clear is the only way to remove a restriction. Leaving allowed_sources out of a call never means "remove the rules" — otherwise the safest-looking call you could make would quietly open your service up.
  • A remove that would empty the list is rejected. An empty list and no list mean opposite things, so rather than pick one, Route6 asks you to say which: clear to allow everyone, or set with an empty list to allow no one.

Rules take effect within about a second on every path, including the IPv4 webhook URL and your IPv6 address.

Source restrictions are available on every plan, including the free tier.

A few things worth knowing before you rely on it:

  • Both families are separate. A rule written in IPv4 does not restrict an IPv6 caller, and vice versa. Your hostname publishes both an A and an AAAA record, so an IPv4-only allowlist will refuse IPv6 senders — that is usually what you want for something like Stripe, which sends from IPv4 only, but it is worth being deliberate about. If a sender may arrive on either, list both.
  • Omitting the field means no restriction. An empty list means the opposite: nothing may reach it. The two are not the same.
  • Every reply tells you what is actually enforced, with presets expanded to the addresses behind them — so you can confirm a rule landed rather than assuming it did.
  • Rules are checked at the Route6 gateway, before anything reaches your agent — a refused caller never causes a connection on your machine. HTTPS callers get 403 with x-route6-error: source_denied; on a raw TCP port the connection is reset.
  • Denials are recorded and visible on your security events, so a webhook that stops arriving can be told apart from one your own rule refused.
  • Presets are maintained by Route6 from the vendor's published list. They are a convenience, not a guarantee: if a vendor adds ranges we have not picked up yet, a strict allowlist will refuse them. If a webhook matters, pin the vendor's own documented ranges yourself.

Mesh-scoped forwards use a different mechanism — see mesh endpoint ACLs — because they are enforced on your own agent rather than at the gateway.

port_forward_tls

Reports how TLS is handled for a port, and toggles it where that is meaningful.

TLS for *.on.route6.me is terminated at the Route6 gateway on the standard HTTPS path — the port-less https://you.on.route6.me/ URL above — and your local service receives plain HTTP. There is nothing to enable or disable there.

On any other port the bytes are relayed untouched, so if you need TLS on https://you.on.route6.me:8443/ you terminate it in your own service with your own certificate, and it works end to end.

ParameterTypeRequiredDescription
portnumber✓External port to report on
action"enable" | "disable"✓Requested action