addGateway(). On any other back-end, or when the API must scale apart from the agent, run it standalone: one process, one port, either or both services.
The standalone Gateway ships as the @forestadmin/gateway npm package, whose command is forest-gateway, and as the ghcr.io/forestadmin/gateway Docker image.
Supported back-ends
The MCP service supports the same back-ends as the standalone MCP server it replaces. The API service calls your back-end directly, over HTTP. It has been verified against these agents:
What was verified: listing and counting records, with filters, sort, search and projection; listing and counting related records; loading an action form. On a Node.js agent, prefer the embedded Gateway.
Deploying the Gateway
1
Choose the services
FOREST_GATEWAY_SERVICES lists what the Gateway serves: mcp, api, or mcp,api. It is required: without it, or with any other value, the Gateway refuses to start.2
Start it
npx, the Gateway needs Node.js 22.12 or later.The Gateway listens on 3931 whenever the MCP service is on, and on 3450 when it serves the API alone. PORT overrides both.Follow this guide to retrieve your AUTH and ENV secrets.
FOREST_GATEWAY_API_TOKEN_ENCRYPTION_KEY is a Base64-encoded 32-byte key, generated with openssl rand -base64 32.3
Check it
/health answers 200 when every service it serves is configured, and 503 otherwise. A service that is off reads "disabled". It always sits at the root, whatever FOREST_GATEWAY_BASE_PATH says, and the image’s HEALTHCHECK probes it.4
Give out the URLs
With the default empty base path:
- AI assistants connect to
https://gateway.example.com/mcp. - Trusted UIs, the Zendesk app included, call
https://gateway.example.com/api.
/health.
Environment variables
The API service reads these too:
The MCP service reads the same
FOREST_MCP_* variables as before, under the same names: enabled tools, token lifetimes and file uploads. They are listed on the MCP server page.
When a value is wrong. A malformed value (a bad URL, a bad port, an API public URL that does not end with {basePath}/api) stops the boot with an error naming the variable. A required variable that is merely missing does not: the service that needs it starts degraded, answers 503, and a boot warning names the variable. FOREST_GATEWAY_API_ALLOWED_ORIGINS is the one exception to the first rule: an entry that cannot be parsed is dropped, and a boot warning names the key.
Legacy names
The Gateway also reads the namesforest-mcp-server and forest-bff used, so a migrated configuration boots unchanged:
The Gateway name wins when it is set. Each legacy name in use logs one boot warning naming its replacement. Two legacy names that disagree with no Gateway name set, such as
HTTP_PORT=8080 and MCP_SERVER_PORT=3931, stop the boot. These names have no removal date.
Ingress and network rules
An ingress or WAF that allow-lists only{basePath}/api/* breaks sign-in. Browser sign-in runs on {basePath}/oauth/*, so that namespace must pass too. With the MCP service on and a non-empty base path, the two discovery paths /.well-known/oauth-authorization-server{basePath} and /.well-known/oauth-protected-resource{basePath}/mcp must also reach the Gateway, even though they sit at the origin root, outside {basePath}.
Restricting which OAuth clients can connect
FOREST_GATEWAY_ALLOWED_OAUTH_CLIENTS restricts both services at once, MCP and API. Without it, or with an empty value, any OAuth client registered at Forest can connect. A value that lists no domain, such as spaces or a lone comma, stops the boot instead of opening the Gateway to every client.
invalid_client error. Keep forestadmin.com in the list as long as you use Forest’s Zendesk app: its sign-in redirects through app.forestadmin.com. The details are on the Gateway page.
Action file uploads with the image
FOREST_MCP_UPLOAD_STORAGE_MODULE points to a module on the Gateway’s filesystem. With the image, mount that module into the container and point to it there. The path resolves from /app, the image’s working directory. The module’s own dependencies are not in the image, so mount a directory that carries its node_modules:
Exporting the OpenAPI document
forest-gateway openapi writes the API’s OpenAPI document to stdout without starting the server, with its paths under {basePath}/api. --output [file] writes it to a file instead.
Moving from forest-mcp-server or forest-bff
Staying is fine. forest-mcp-server and forest-bff keep working as they do today, with the same variables and the same URLs. Both are deprecated: they log one warning at boot pointing here. Moving to the Gateway gets you both services in one process, and the new variable names.
Moving is explicit: nothing switches on its own. Replace the command or the image, add FOREST_GATEWAY_SERVICES, and keep your variables. The legacy names still work and each logs one warning until you rename it.
From forest-mcp-server
Your MCP URLs do not change if you keep an empty base path: the Gateway serves the MCP on /mcp and listens on 3931 whenever the MCP service is on, as before.
From forest-bff
The API moves from the root of your URL to /api: https://your-bff.example.com/agent/v1/... becomes https://your-bff.example.com/api/agent/v1/.... Update every application that calls it. In the Zendesk app, set the URL to https://your-bff.example.com/api. Users sign in again.
- The image changes from
ghcr.io/forestadmin/agent-bfftoghcr.io/forestadmin/gateway, andHTTP_PORTbecomesPORT. - If you set a public URL, point it at
…/api. ABFF_PUBLIC_URLleft at the root stops the boot, with an error saying it must end with/api. forest-bffhad no OAuth client allowlist. SettingFOREST_GATEWAY_ALLOWED_OAUTH_CLIENTSon the Gateway restricts the API too.
What’s next
Forest Gateway →
Routes, OAuth, CORS, timeouts and errors, shared by both deployments.
Forest MCP Server →
The MCP service’s tools, restrictions, token lifetimes and uploads.