One-step setup script¶
scripts/setup-mcp-server.py is the fastest way to get a working local Zscaler MCP Server. One command pulls the latest Docker image, starts the container with the right entrypoint for your chosen auth mode, verifies the endpoint, and writes the correct MCP server entry into every AI agent it can detect on your machine.
If you’ve never deployed an MCP server before, start here. Compared to running raw docker run commands (see Docker), this script makes every decision interactively and never asks you to copy/paste a JSON snippet into a client config — it does that for you.
What it does¶
Prompts for an authentication mode —
jwt,zscaler,api-key,oidcproxy, ornone.Prompts for a transport —
streamable-httporstdio. Incompatible combinations (anything other thannonewithstdio) are rejected at the prompt with an explanation.Prompts for a ``.env`` file path, or collects credentials interactively if you don’t have one.
Pulls
zscaler/zscaler-mcp-server:latestfrom Docker Hub. No local build, no image modifications.Starts the container with the right entrypoint and env wiring for the chosen auth mode. (For
oidcproxy, the entrypoint is replaced with an inline Python program that constructs the OIDCProxy auth provider — still using the same upstream image.)Verifies the endpoint responds correctly for the chosen auth mode (HTTP transports only).
Auto-detects installed AI agents and offers to configure each one for you. Existing entries with the name
zscaler-mcp-serverare overwritten; nothing else in the config is touched.
Supported on macOS, Linux, and Windows.
Quick start¶
From a checkout of the repo:
python3 scripts/setup-mcp-server.py
That’s it. Follow the prompts.
The script is idempotent — re-run anytime to switch auth modes, refresh credentials, pick up a new image, or add a newly-installed AI agent to the configured set. Each re-run stops the old container, starts a fresh one, and rewrites the zscaler-mcp-server entry in every agent config.
Authentication modes¶
Mode |
What it is |
Client sends |
Image override |
|---|---|---|---|
|
OAuth 2.0 client_credentials against an external IdP (Auth0). Server validates via JWKS. |
|
None |
|
Server validates the request’s Zscaler OneAPI credentials against |
|
None |
|
Static shared secret. Simplest to set up, weakest model. |
|
None |
|
Full OAuth 2.1 + Dynamic Client Registration via FastMCP’s |
(none — handled by the client) |
Yes — entrypoint replaced with an inline Python program |
|
No authentication. Single-user local development only. |
(nothing) |
None |
Transport modes¶
Transport |
When to use |
Compatible auth modes |
|---|---|---|
|
The container runs as a long-lived HTTP server (recommended). |
All modes |
|
The AI agent spawns the container per session via |
|
stdio + any HTTP-bound auth mode is rejected at the prompt — there’s no HTTP boundary for the auth middleware to enforce when the agent is talking to the container’s stdio directly.
Common invocations¶
Fully interactive (recommended for the first run):
python3 scripts/setup-mcp-server.py
Skip prompts when you already know the mode (great for re-runs and CI):
python3 scripts/setup-mcp-server.py \
--auth-mode zscaler \
--transport streamable-http \
--env-file .env
Refresh a JWT without re-pulling the image:
python3 scripts/setup-mcp-server.py \
--auth-mode jwt \
--transport streamable-http \
--env-file .env \
--skip-pull
Test the configuration without touching any AI agent configs:
python3 scripts/setup-mcp-server.py --skip-agent-config
CLI flags¶
Flag |
Default |
Purpose |
|---|---|---|
|
(prompt) |
Skip the auth-mode prompt. |
|
(prompt) |
Skip the transport prompt. |
|
(prompt) |
Path to a |
|
|
HTTP port for the server. |
|
|
Docker container name. Change this if you want to run multiple instances on the same host. |
|
off |
Enable |
|
off |
Skip |
|
off |
Skip endpoint verification (the HTTP smoke check). |
|
off |
Don’t touch any AI agent configs. |
|
off |
Use the original |
Environment file¶
The script accepts any .env file with KEY=value lines. # and ; comments are tolerated, and so is export FOO=bar shell syntax — the script writes a sanitized copy for docker --env-file because Docker’s parser is much stricter than typical .env loaders.
If you don’t have a .env and don’t want to make one, the script prompts for each required value interactively and writes a temporary .env that’s deleted on exit.
Live reload (.env bind-mount)¶
By default the script bind-mounts the host .env at /app/.env inside the container, in addition to passing it via --env-file. The --env-file seeds os.environ at container boot; the bind-mount makes the file re-readable from inside the container at runtime, so the lifecycle subcommands below pick up edits you make on the host without needing to recreate the container.
Reconfigure a running container¶
The zscaler-mcp CLI inside the container exposes four lifecycle subcommands:
$EDITOR .env
docker exec zscaler-mcp-server zscaler-mcp reload
docker exec zscaler-mcp-server zscaler-mcp restart
docker exec zscaler-mcp-server zscaler-mcp status
docker exec zscaler-mcp-server zscaler-mcp stop
Subcommand |
Signal |
What it does |
Sessions |
|---|---|---|---|
|
SIGHUP |
Re-reads the bind-mounted |
Survive |
|
SIGUSR2 |
Re-reads |
Drop — clients reconnect |
|
(none) |
Prints PID, uptime, transport, |
Unaffected |
|
SIGTERM |
Clean shutdown, no respawn — same path |
Drop |
Important: env vars are only “live” when .env is bind-mounted¶
Env vars passed via --env-file are read once by Docker at docker run time and copied into the container’s Config.Env metadata. After that moment, PID 1’s os.environ is fixed for the life of that container — docker stop && docker start re-uses the same Config.Env, it does NOT re-read your host’s .env.
What gets written to your AI agents¶
Each detected agent gets an mcpServers entry (or servers for VS Code) called zscaler-mcp-server. Existing entries with that name are overwritten; nothing else in the config is touched.
Agent |
Config path (macOS) |
|---|---|
Claude Desktop |
|
Claude Code (CLI) |
|
Cursor |
|
Gemini CLI |
|
VS Code |
|
Windsurf |
|
GitHub Copilot CLI |
|
Windows and Linux paths follow each agent’s standard locations.
After the script finishes¶
Restart any AI agent it configured. Most agents only read MCP config at startup. For CLI-based agents (Claude Code, Gemini CLI, Copilot CLI) it’s usually enough to open a fresh shell.
Troubleshooting¶
Symptom |
Fix |
|---|---|
|
Install Docker Desktop: https://docs.docker.com/get-docker/ |
|
Check network access to Docker Hub. Use |
|
Credentials don’t match what the server expects. Re-check the |
|
Container failed to stay running. Inspect: |
Agent doesn’t see the server after restart |
Confirm the agent reads from the path the script printed. Some agents (VS Code) need an MCP-aware extension installed. |
When to use raw docker run instead¶
The setup script optimizes for “the agent should just work on the first try.” If you want to integrate the server into existing infrastructure (Compose, Kubernetes, systemd, an init container), skip the script and use the raw Docker reference instead — it shows every flag the container actually needs.