Skip to content
SZ-MCP
Get Support

Controller requirements

SZ-MCP calls your controller from a Cloudflare Worker, over the public internet. That imposes three requirements, and the first one is the one that stops most setups. A controller that cannot meet the first two — on a private network, or with a self-signed certificate — can be reached through the controller tunnel instead.

Your controller must present a certificate issued by a public CA on the port you expose. SmartZone ships with a self-signed certificate, which will not work.

Cloudflare Workers validate certificates on every outbound fetch and provide no skip-verify option — there is no setting in SZ-MCP that relaxes this for a direct connection, because there is no setting in the platform to expose. A controller reached directly with a self-signed certificate is unreachable, not degraded. The controller tunnel is the exception: see below.

Install a certificate from a public CA through the SmartZone admin UI. Let’s Encrypt with a DNS-01 challenge is usually the simplest path: it validates your control of the DNS name rather than requiring inbound HTTP, so the controller does not need port 80 open to the internet.

The certificate must be valid for the exact hostname you enter on the dashboard. A certificate for sz.example.com will not satisfy a connection made to an IP address.

Save & detect fails even though the credentials are right

Section titled “Save & detect fails even though the credentials are right”

This is almost always the certificate. The TLS handshake fails before SmartZone ever sees your username, so Save & detect reports invalid_host, a connection-layer error, rather than “wrong password”. It is easy to misread as a credentials problem. If you are confident the username and password are right, suspect the certificate first, then the address and port below.

SZ-MCP refuses to call hosts that are not publicly routable. This is checked when you save credentials and again on every call, and it applies to both hostnames and IP literals.

Refused outright:

CategoryExamples
Loopback127.0.0.0/8, ::1, localhost
Private (RFC 1918)10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
Link-local169.254.0.0/16 (including cloud metadata endpoints), fe80::/10
Carrier-grade NAT100.64.0.0/10
Unique local IPv6fc00::/7
Multicast and reserved224.0.0.0/4 and above, ff00::/8, 0.0.0.0/8
Internal-looking namesany host ending .local, .internal, .localhost; broadcasthost

Other reserved IPv4 ranges (192.0.0.0/24, 192.88.99.0/24, 198.18.0.0/15) are refused too, as is every IPv6 address outside global unicast (2000::/3), and IPv6 forms that carry a private IPv4 address.

Non-canonical IP literals are refused as a class — integer (2130706433), octal (0177.0.0.1), hexadecimal (0x7f.0.0.1) and short (127.1) forms all expand to real addresses at resolve time, so anything whose labels are entirely numeric is rejected rather than normalised. A trailing dot does not help: 127.0.0.1. is treated as 127.0.0.1.

Attempting to save one of these returns forbidden_host with a message naming the reason, for example IPv4 10.1.2.3 is in a reserved/private range.

The port is 8443 (the default, SmartZone’s API port) or 443 (for a controller behind NAT, a reverse proxy or the controller tunnel). No other port is accepted: saving one returns forbidden_port with port <n> is not allowed; use 8443 or 443. Whichever you choose must be open to Cloudflare’s egress and serving the certificate from requirement 1.

SZ-MCP always speaks HTTPS to the controller. There is no plaintext option.

MomentWhat is verified
Save & detectPort is 8443 or 443; host is publicly routable and its name resolves only to public addresses; TLS handshake succeeds; the host answers apiInfo as a SmartZone; credentials are accepted; API version detected
Test connectionThe same checks, re-run against the saved credentials
Every call from ClaudePort and host checks, including the name’s resolved addresses; TLS is validated by the platform on each request

The host check runs on every call, not only at save time, so a stored credential cannot become a route into a private range later.

A hostname is resolved (over DNS-over-HTTPS) and refused with forbidden_host if any of its addresses is private, for example "sz.example.com" resolves to 10.1.2.3, a reserved/private address. On save and test, a name that does not resolve is refused too; on a call, a resolver outage lets the request through.

What if my controller is private or self-signed?

Section titled “What if my controller is private or self-signed?”

Use the controller tunnel. SZ-MCP creates a Cloudflare Tunnel for your organisation with its own hostname; you run cloudflared (or its Docker image) on a machine on the controller’s network, which needs only outbound HTTPS. Only the SmartZone WSG and SwitchM API paths pass the tunnel, and only SZ-MCP can use it. The controller’s own certificate is not checked on that last hop, so a self-signed controller works.

StepWhere
Create the tunnelConfigure › Controller › Private controller (tunnel): enter the controller’s address on your network and its port, then Create tunnel
Run the connectorCopy the cloudflared or Docker command the card shows. The token in it is the tunnel’s credential: keep it like a password
Point the login at itUse for the login fills in the tunnel hostname and port 443; enter the username and password, then Save & detect

Controller tunnels are in limited rollout and must be enabled per account: contact support to have them enabled. Until then the card reads “Controller tunnels are in limited rollout; ask an sz-mcp admin to enable them for your account.” Only organisation admins can set one up.

Once the login uses the tunnel, SZ-MCP can also issue a Let’s Encrypt certificate for the controller itself and renew it: the Controller certificate (Let’s Encrypt) card on the same page, Get a certificate.

Full setup: Reach a private or self-signed controller.