Skip to content

Gateway CLI Reference

The Freesail Gateway is the central bridge between AI agents and browser-based frontends. It is started via the freesail CLI.

Terminal window
freesail run gateway [options]

You can also invoke the gateway binary directly if installed:

Terminal window
freesail-gateway [options]

OptionDefaultDescription
--config <file>freesail-gateway.config.jsonPath to JSON config file in CWD
--http-port <port>3001Port for the A2UI HTTP/SSE server
--http-host <host>0.0.0.0Bind address for the A2UI server
--mcp-mode <mode>httpMCP transport: http (standalone) or stdio (child process)
--mcp-port <port>3000Port for MCP Streamable HTTP server (http mode only)
--mcp-host <host>127.0.0.1Bind address for MCP server (http mode only)
--session-timeout <s>1800Session idle timeout in seconds
--reconnect-grace-period <s>180Session resumption window in seconds
--log-file <file>Write logs to file (in addition to console)
--log-level <level>infoMinimum log level: fatal | error | warn | info | debug
--log-filter <f>Per-subsystem level override, e.g. express:debug. Repeatable.
--helpShow help

CLI flags always take precedence over config file values.


Terminal window
# Default — HTTP MCP on port 3000, A2UI on port 3001
freesail run gateway
# Custom ports
freesail run gateway --http-port 8080 --mcp-port 4000
# Stdio MCP mode (agent spawns gateway as a child process)
freesail run gateway --mcp-mode stdio
# With a config file
freesail run gateway --config /etc/freesail/gateway.json
# Suppress MCP noise, verbose session events
freesail run gateway --log-level warn --log-filter session:debug
# Log to file, suppress MCP noise
freesail run gateway --log-file gateway.log --log-level info --log-filter mcp:warn
# Verbose agent-surface events only
freesail run gateway --log-filter session.agent-surface:debug

All settings can be provided via a JSON config file. By default the gateway looks for freesail-gateway.config.json in the current working directory. Override the path with --config.

A sample config is included in the package:

Terminal window
cp node_modules/@freesail/gateway/freesail-gateway.config.sample.json freesail-gateway.config.json
{
"httpPort": 3001,
"httpHost": "0.0.0.0",
"mcpMode": "http",
"mcpPort": 3000,
"mcpHost": "127.0.0.1",
"webhookUrl": "http://localhost:3002/action",
"sessionTimeout": 3600,
"reconnectGracePeriod": 180,
"catalogLogDir": "/var/log/freesail/catalogs",
"bodyLimit": "5mb",
"log": {
"level": "info",
"file": "/var/log/freesail/gateway.log",
"filters": {
"express": "info",
"mcp": "warn",
"session": "info",
"session.agent-surface": "debug",
"session.client-surface": "warn"
}
}
}

All fields are optional; omit any you don’t need and the defaults will apply.

Note: sessionTimeout and reconnectGracePeriod are specified in seconds in the config file, not milliseconds.


The gateway exposes two independent network interfaces:

InterfaceDefault PortProtocolPurpose
Agent-facing3000MCP Streamable HTTPExposes tools, resources, and prompts to AI agents
UI-facing3001HTTP SSE + POSTStreams A2UI updates to frontends; receives user actions

By default the MCP server binds to 127.0.0.1 (localhost only) for network isolation. The A2UI server binds to 0.0.0.0 so browsers can connect.


Idle sessions are removed after 30 minutes by default. Override in the config file:

{ "sessionTimeout": 3600 }

When a browser tab closes (or page refreshes), the gateway suspends the session for a grace period rather than removing it immediately. If the client reconnects within this window, the session is resumed seamlessly — including all surface state. The default is 180 seconds.

{ "reconnectGracePeriod": 300 }

The client stores the session ID in sessionStorage (tab-scoped) and sends it as the X-Freesail-Session header on reconnect. The gateway matches this to the suspended session and resumes it. If two browser tabs share the same sessionStorage value (e.g. after duplicating a tab), the gateway detects the collision and creates a fresh session for the duplicate.


The gateway sets an HttpOnly; SameSite=Strict cookie (freesail-gateway-token) on every SSE connection. All write endpoints (/message, /register-surface, /register-catalogs) validate the presence of this cookie. SameSite=Strict prevents cross-site requests from including it, blocking CSRF attacks.

Session identity is carried via the X-Freesail-Session header (set by the transport from sessionStorage), not the CSRF cookie. This ensures each browser tab maintains its own independent session.

When deployed behind a reverse proxy such as nginx, the gateway reads the X-User-Context header on every SSE connection and stores the parsed JSON as userContext on the session. Agents receive this via list_sessions.

location /sse {
proxy_set_header X-User-Context '{"userId":"$jwt_sub","orgId":"$jwt_org_id"}';
proxy_pass http://127.0.0.1:3001;
}

The header must be a valid JSON object. The gateway trusts it as-is — it must only be set by a trusted reverse proxy, never by the browser.


fatal | error | warn | info (default) | debug

Use --log-filter <subsystem>:<level> (repeatable) or log.filters in the config file to override the log level for individual subsystems:

SubsystemCovers
expressSSE connections, incoming actions, catalog registration
mcpAgent MCP tool calls, session handshake
sessionSurface creates/updates, data-model writes, stale-session cleanup
session.agent-surfaceDownstream messages sent to agent-created surfaces
session.client-surfaceDownstream messages sent to client-managed surfaces (__ prefix)

Dot-notation maps to nested log categories (e.g. session.agent-surface['freesail', 'session', 'agent-surface']).


The gateway exposes MCP resources that agents subscribe to for push-based delivery — no polling required.

URIDescription
mcp://freesail.dev/sessionsList of active sessions. Fires ResourceUpdated on every connect/disconnect.
mcp://freesail.dev/sessions/{sessionId}Pending action queue for a specific session. Fires ResourceUpdated on every new action. Reading the resource drains the queue.

Agent runtimes using @freesail/agent-runtime subscribe to these automatically. See the Agent Runtime reference for details.


The gateway exposes the following MCP tools to connected agents:

ToolDescription
create_surfaceInitialize a new UI surface for a client session
update_componentsUpdate the component tree of a surface
update_data_modelUpdate the data model without changing component structure
get_data_modelRetrieve the current client-side data model for a surface on demand
get_component_treeRetrieve the current component tree for a surface from the client
delete_surfaceRemove a surface from the UI
get_catalogsGet the catalogs supported by a client session (index + type defs)
get_component_detailsGet full property details for one or more components
get_function_detailsGet full usage details for one or more functions
get_pending_actionsDrain pending user actions from a specific session (polling fallback)
get_all_pending_actionsDrain pending actions across all sessions owned by this agent
list_sessionsList sessions owned by this agent with surface and action details
claim_sessionClaim a client session for this agent
release_sessionRelease a claim on a session

Agents must call get_catalogs(sessionId) at the start of a session before calling create_surface. It returns a slim index of available catalogs — component and function names, descriptions, and type definitions — plus the catalogId needed for create_surface.

Call get_component_details or get_function_details before using any component or function to get the full property reference.

Requests the current client-side data model for a surface. Sends a getDataModel message downstream; the client responds with the full model state. This works regardless of whether sendDataModel was enabled on the surface — useful for on-demand inspection without the overhead of sending the data model with every action.

Requests the current component tree for a surface from the client. Sends a getComponentTree message downstream; the client responds with the flat list of components and the root component ID. Returns { success, surfaceId, components, rootId }.

This is useful after reconnects or agent restarts to verify the current rendered state, or when the agent needs to understand the current layout before deciding whether to update or replace it. Both get_data_model and get_component_tree are Freesail extensions to the A2UI protocol — they provide a reverse path from client to agent that the spec does not define.

TypePatternAgent permissions
Agent-managedAlphanumeric + underscores (e.g. workspace)Full control
Client-managedStarts with __ (e.g. __chat)update_data_model and update_components only

The gateway runs plain HTTP by default. For production, place NGINX in front to handle TLS termination for both ports. See the Developer Guide for a sample NGINX configuration.


VariableDescription
CATALOG_LOG_DIRDirectory to write catalog prompt logs to. Overridden by catalogLogDir in config.