Skip to main content
The Forest Gateway serves two things: the MCP service, for AI assistants, and the API service, a REST contract for trusted browser UIs such as the Zendesk app. On a Node.js agent, you embed it with 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.
On forest_liana, a model whose declared primary key is not unique, the workaround Rails 7.0 and earlier use for composite keys (self.primary_key = "tenant_id" on a (tenant_id, seq) table), is not supported. The agent returns the same id for several records, so the API cannot tell them apart: lists show duplicate ids, and a record cannot be found back by its id.

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

The image needs nothing else. With 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.
The routes are the embedded Gateway’s, listed on its page, plus the root /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 names forest-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.
Entries are domains, not client ids. A client is allowed only when every redirect URI it registered is on a listed domain or one of its subdomains. Every other client gets a standard OAuth 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:
What the module exports is described in Action file uploads.

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.
The Gateway needs Node.js 22.12 or later. On an older Node.js, use the image.

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-bff to ghcr.io/forestadmin/gateway, and HTTP_PORT becomes PORT.
  • If you set a public URL, point it at …/api. A BFF_PUBLIC_URL left at the root stops the boot, with an error saying it must end with /api.
  • forest-bff had no OAuth client allowlist. Setting FOREST_GATEWAY_ALLOWED_OAUTH_CLIENTS on the Gateway restricts the API too.
Do not publish a forest-bff under a path ending in /api, such as https://corp.example.com/forest/api. The Zendesk app reads a URL ending in /api as a Gateway, and sends sign-in to https://corp.example.com/forest/oauth/*?service=api, which forest-bff does not serve: nobody can sign in. Publish forest-bff under another path, or move to the Gateway.

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.