Troubleshooting Guide

This guide covers common issues and solutions when using the Zscaler Integrations MCP Server.

Tip

For the most up-to-date troubleshooting information, see the Troubleshooting Guide on GitHub.

Installation Issues

“Command not found: zscaler-mcp”

Install uv and use uvx to run the server:

curl -LsSf https://astral.sh/uv/install.sh | sh
uvx zscaler-mcp

Or install directly:

uv tool install zscaler-mcp

If installed but not found, update your shell PATH.

“.env file not found”

  • Use absolute paths, not relative paths, when specifying the .env file location

  • Verify the file exists: ls -la /path/to/.env

  • Check file permissions (should be readable)

Zscaler API Authentication

“Authentication failed” / 401 Unauthorized

  1. Verify all 4 required OneAPI variables are set:

    • ZSCALER_CLIENT_ID

    • ZSCALER_CLIENT_SECRET

    • ZSCALER_CUSTOMER_ID

    • ZSCALER_VANITY_DOMAIN

  2. Check that credentials are not expired

  3. Ensure the API client has the necessary scopes in the ZIdentity console

“ZSCALER_CUSTOMER_ID required”

ZSCALER_CUSTOMER_ID is required for ZPA operations. If you only use ZIA/ZDX/ZCC, this can be omitted.

MCP Client Authentication

Client receives 401 when connecting over HTTP

  1. Check ZSCALER_MCP_AUTH_ENABLED is true

  2. Verify ZSCALER_MCP_AUTH_MODE matches the client’s authentication method

  3. For api-key mode: Client must send Authorization: Bearer <key> with the correct key

  4. For jwt mode: Verify the JWKS URI is reachable, issuer/audience match, and the token is not expired

  5. For zscaler mode: Client must send valid Basic Auth or X-Zscaler-Client-ID/X-Zscaler-Client-Secret headers

JWT token validation fails

  • Ensure ZSCALER_MCP_AUTH_JWKS_URI points to a reachable JWKS endpoint

  • Verify ZSCALER_MCP_AUTH_ISSUER exactly matches the iss claim in your tokens

  • Verify ZSCALER_MCP_AUTH_AUDIENCE exactly matches the aud claim

  • Check that ZSCALER_MCP_AUTH_ALGORITHMS includes the algorithm used by your IdP (default: RS256)

Generate a test token:

zscaler-mcp --generate-auth-token

Network Security

Connection refused on non-localhost

By default, the server blocks plaintext HTTP on non-localhost interfaces. Either:

  • Configure TLS: Set ZSCALER_MCP_TLS_CERT_FILE and ZSCALER_MCP_TLS_KEY_FILE

  • Or allow HTTP explicitly: Set ZSCALER_MCP_ALLOW_HTTP=true (only if TLS is handled upstream)

421 Misdirected Request / “Invalid Host header”

The server validates Host headers to prevent DNS rebinding attacks. Fix:

  • Add your hostname to ZSCALER_MCP_ALLOWED_HOSTS: export ZSCALER_MCP_ALLOWED_HOSTS="myserver.example.com"

  • Or disable validation (not recommended): export ZSCALER_MCP_DISABLE_HOST_VALIDATION=true

  • Localhost variants (localhost, 127.0.0.1, ::1) are always allowed

Client IP rejected / 403 Forbidden

If ZSCALER_MCP_ALLOWED_SOURCE_IPS is configured:

  • Ensure the client’s IP is in the allowed list

  • Use CIDR notation for ranges: 10.0.0.0/8,192.168.1.0/24

  • Check if the client is behind a proxy (the server sees the proxy’s IP)

Write Operations

Write tools not appearing

  1. Is ZSCALER_MCP_WRITE_ENABLED=true set?

  2. Is ZSCALER_MCP_WRITE_TOOLS provided? This is mandatory — if empty, 0 write tools are registered

  3. Do patterns match tool names? Use zscaler-mcp --list-tools to see all registered tools

  4. Wildcards are supported: zpa_create_*, zia_*, zpa_delete_application_segment

Delete operations require confirmation

All 33 delete operations require double confirmation:

  1. AI agent permission dialog (destructiveHint)

  2. Server-side confirmation via hidden kwargs parameter

To skip confirmations (advanced/CI use only): Set ZSCALER_MCP_SKIP_CONFIRMATIONS with an HMAC-SHA256 token. The confirmation window is controlled by ZSCALER_MCP_CONFIRMATION_TTL (default: 300 seconds).

Tool Filtering

These checks apply on every transport (stdio, sse, streamable-http).

A specific tool isn’t loaded

Walk down this list in order — each item rules out one filter the server applies at startup.

  1. Was the service explicitly disabled? Check --disabled-services / ZSCALER_MCP_DISABLED_SERVICES. A service in this list contributes zero tools.

  2. Was the tool’s pattern explicitly disabled? Check --disabled-tools / ZSCALER_MCP_DISABLED_TOOLS. This list supports wildcards (e.g. zcc_*) and wins over every other filter.

  3. Is it a write tool with no allowlist? Write tools (*_create_*, *_update_*, *_delete_*) require both --enable-write-tools AND a matching pattern in --write-tools. With write mode disabled (the default), no write tool is registered.

  4. Was the tool’s toolset filtered out? If you set --toolsets (or ZSCALER_MCP_TOOLSETS), only tools that belong to a listed toolset are registered. Call zscaler_list_toolsets from your client to see what’s currently active and what’s available.

  5. Did the OneAPI entitlement filter drop the tool’s product? Look for a startup log line of the form entitlement filter applied: entitled services=[...], kept N toolset(s), removed M toolset(s). If the product (e.g. ZDX) isn’t in the entitled list, every toolset for that product was filtered out. Either grant the OneAPI client the missing product entitlement in ZIdentity, or — for a quick diagnostic — restart the server with --no-entitlement-filter to confirm the filter is the cause.

If the agent reports a tool that genuinely doesn’t exist (typo, hallucination), zscaler_list_toolsets and zscaler_get_available_services are the right introspection tools to call. Use name_contains / description_contains on zscaler_list_toolsets to scope the search to a single area before drilling in with zscaler_get_toolset_tools.

How do I see what the entitlement filter dropped?

The OneAPI entitlement filter logs one INFO line at startup with the result, for example:

entitlement filter applied: entitled services=['zia', 'zpa'], kept 12 toolset(s), removed 17 toolset(s)

To see which specific toolsets were removed, compare two startup runs:

  1. Start the server normally and call zscaler_list_toolsets — note which toolsets show as currently enabled.

  2. Restart with --no-entitlement-filter (or ZSCALER_MCP_DISABLE_ENTITLEMENT_FILTER=true), call zscaler_list_toolsets again — anything new is what the filter was hiding.

If you see a WARN entitlement filter skipped (...) line instead, the filter never ran — the parenthesized reason tells you why (missing OneAPI credentials, the ZIdentity token endpoint was unreachable, the token didn’t decode, or the token had no recognizable product entitlements). Your tool list in that case is whatever your other filters produced.

Agent/Editor Issues

Claude Desktop: “MCP server not found”

  • Verify the .env file path is absolute and correct

  • Check Claude Desktop logs: Help → View Logs

  • Restart Claude Desktop completely (quit and reopen)

Claude Desktop: Extension not working on Windows

The one-click extension bundles macOS/Linux binaries. Use manual configuration with uvx instead:

{
  "mcpServers": {
    "zscaler-mcp-server": {
      "command": "uvx",
      "args": ["--env-file", "C:\\Users\\You\\.env", "zscaler-mcp"]
    }
  }
}

Windows: “‘C:\Program’ is not recognized” (npx path issue)

Paths with spaces break npx when called directly. Use cmd /c:

{
  "mcpServers": {
    "zscaler-mcp-server": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "mcp-remote", "http://server:8000/mcp"]
    }
  }
}

“Non-HTTPS URLs are only allowed for localhost” (mcp-remote)

mcp-remote enforces HTTPS for non-localhost URLs. Add --allow-http to the args:

{
  "args": ["-y", "mcp-remote", "http://server:8000/mcp", "--allow-http", "--header", "Authorization: Bearer sk-key"]
}

Self-signed certificate rejected (mcp-remote)

When using self-signed certificates, add to the MCP server config:

{
  "env": { "NODE_TLS_REJECT_UNAUTHORIZED": "0" }
}

Cursor: Tools not appearing

  • Check Cursor’s MCP logs: View → Output → MCP

  • Verify the .env file path is absolute

  • Config file location: ~/.cursor/mcp.json (macOS/Linux) or %USERPROFILE%\.cursor\mcp.json (Windows)

Docker Issues

“Server connection timeout” with Docker

When using HTTP transports in Docker, always bind to 0.0.0.0:

docker run --rm -p 8000:8000 --env-file /path/to/.env \
  zscaler/zscaler-mcp-server:latest --transport streamable-http --host 0.0.0.0

Port conflicts

Change the port with --port:

docker run --rm -p 8001:8001 --env-file /path/to/.env \
  zscaler/zscaler-mcp-server:latest --transport streamable-http --host 0.0.0.0 --port 8001

Debugging

Enable debug logging:

export ZSCALER_MCP_DEBUG="true"
zscaler-mcp

List all registered tools:

zscaler-mcp --list-tools

Test the server manually:

# Start server with HTTP transport
zscaler-mcp --transport streamable-http --host 127.0.0.1 --port 8000

# Test health (in another terminal)
curl http://127.0.0.1:8000/mcp

Getting Help