Skip to main content
Complete API reference for @forestadmin/agent (Node.js/TypeScript).

Agent Class

createAgent(options)

Create and configure a Forest agent.
Parameters:

Required

Connection and routing

Changing prefix changes the URL Forest calls, so update the environment’s URL in Forest to match. Two agents on one host need different prefixes — see Running two agents at once.

Schema and typings

Logging and errors

Permissions and caching

skipRelationReadPermissions widens what your users can read. The agent logs a warning at startup when it is enabled.

HTTP and payloads

Only enable useUnsafeActionEndpoint if you specifically need index-free Smart Action routes.
It removes the positional index (/_actions/{collection}/{slug} instead of /_actions/{collection}/{index}/{slug}), which can make two action names collide on the same route.
When enabled, the agent refuses to start if two actions on the same collection resolve to the same slug.

Integrations

Example:

agent.addDataSource(factory, options?)

Add a datasource to the agent.
Parameters: Example:

agent.customizeCollection(name, callback)

Customize a specific collection with the provided callback.
Example:

agent.addChart(name, definition)

Create a datasource-level API chart.
Example:

agent.removeCollection(…names)

Remove collections from the exported schema (they remain usable within the agent).
Example:

agent.use(plugin, options?)

Load a plugin across all collections.
Example:

agent.start()

Start the agent and connect to Forest servers.
Example:

agent.stop()

Stop the agent and close all connections.

agent.restart()

Reconstruct routing and remount routes at runtime. Called when customizations are refreshed externally (e.g. in cloud environments).

agent.addGateway(options?)

Serve the Forest Gateway inside the agent process: one mount exposing the MCP service (mcp), the API service (api, the “Gateway API”), or both. Available from @forestadmin/agent 1.109.0. The API service also requires the @forestadmin/agent-bff package, installed at the exact version the agent declares; MCP alone installs nothing extra.
Parameters: api options: The secrets, the Forest URLs and the logger are inherited from the agent and cannot be overridden. These options are the only configuration channel: embedded, the API never reads the environment, so FOREST_GATEWAY_API_RATE_LIMIT_MAX_REQUESTS, FOREST_GATEWAY_API_RATE_LIMIT_WINDOW_MS, FOREST_GATEWAY_API_PUBLIC_URL and PORT are ignored silently and their defaults stand. The feature switches default to off, so what you leave out stays off, and GET {basePath}/api/health reports which surfaces are on under configured. Routes: with an empty basePath, POST /mcp, /mcp/uploads, /oauth/authorize · /oauth/token (?service=api dispatches to the API service), /api/agent/v1/*, GET /api/health, and GET /api/docs when api.openapiEnabled is set; the two /.well-known discovery documents sit at the origin root. The full route table and the ingress rules live on the Gateway page.
addGateway() can be called once, before start(): a second call throws addGateway can only be called once, so merge the options you need into a single call rather than stacking calls. It must enable at least one service (addGateway({ mcp: true }), addGateway({ api: {} }), or both), checked when the agent starts; an argument-less addGateway() is a TypeError on the spot. It cannot be combined with the deprecated mountAiMcpServer() or addBff() aliases: whichever is called second throws, and the agent logs the reason at Error level first. Nesting a top-level option under a service (addGateway({ mcp: { basePath } })) throws too, with the correct placement named.
Mount the agent before any body parser of your own, on every framework. A host parser that runs first consumes the request body, so every API POST on the data routes fails with 500 stream.not.readable on Express and a bare 500 Internal Server Error on Koa; the OAuth routes answer the host’s bare 500 on every framework. See Mount before your body parser.
Example:

agent.mountAiMcpServer(options?)

Deprecated. Use addGateway({ mcp }) instead. mountAiMcpServer() keeps its current paths and semantics (the host root, or its own basePath), and the agent logs a deprecation warning at startup. It cannot be combined with addGateway(). See The deprecated MCP alias.
Enable a Model Context Protocol (MCP) server on the agent, allowing AI assistants to interact with your data through standardized tools.
Parameters: Available tool names: 'describeCollection', 'list', 'listRelated', 'create', 'update', 'delete', 'associate', 'dissociate', 'getActionForm', 'executeAction', 'requestActionFileUpload', 'listWorkflows', 'triggerWorkflow', 'getWorkflowRun' Example:
By default, the MCP server registers its endpoints at the host root:
When mounted inside an existing application, the MCP server intercepts the entire /oauth/* namespace plus /mcp at the host root, and claims the two .well-known discovery documents (/.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource/mcp). Any request in those namespaces is captured by the MCP server — if it doesn’t serve that exact route (or your app already does), the request gets a 404/405 instead of reaching your app. For example, your own /oauth/callback would break. Every other /.well-known/* request (e.g. acme-challenge/... or security.txt) reaches your app. Set basePath to narrow the MCP server to a dedicated prefix (and move the .well-known claim to two prefix-suffixed paths) so your routes are left untouched.
Scoping under a prefix:
With basePath: '/ai', the OAuth and protocol routes move under the prefix. The .well-known discovery documents stay at the host root (as required by OAuth discovery, RFC 8414/9728) but are served at prefix-suffixed paths, so they no longer collide with your application’s own root .well-known routes: MCP clients discover these endpoints automatically from the metadata, so no client-side configuration is needed.
The prefix applies to all routes, including the protocol endpoint — so basePath: '/mcp' yields /mcp/mcp. Use a distinct prefix such as /ai to avoid the repetition.
Because the .well-known discovery documents must stay at the origin root, basePath requires the agent to be served at the domain root. If your agent’s URL already includes a path (e.g. https://host/api), setting basePath throws at startup. And root /.well-known/* requests must still reach the agent — a reverse proxy that forwards only /<prefix>/* will break discovery.
When the MCP service runs in the standalone Gateway, the same options are set through environment variables: FOREST_MCP_ENABLED_TOOLS (comma-separated tool names), FOREST_GATEWAY_ALLOWED_OAUTH_CLIENTS (comma-separated domains), FOREST_MCP_ACCESS_TOKEN_TTL_SECONDS and FOREST_MCP_REFRESH_TOKEN_TTL_SECONDS (seconds), and PORT (default: 3931). A mounted server does not read these variables — pass the options to mountAiMcpServer instead.

Standalone Gateway environment variables

When the agent cannot host the Gateway, the standalone Gateway serves the same routes from its own process, configured through environment variables. Their reference lives on its page.

agent.updateTypesOnFileSystem(typingsPath, typingsMaxDepth)

Update the TypeScript typings file generated from your datasources.
Example:

agent.generateSchemaOnly()

Build the schema (.forestadmin-schema.json) and the TypeScript typings and write them to disk without starting the agent or sending the schema to Forest. Useful for generating the schema at build time in a CI/CD pipeline, see Generate the schema at build time. Available since @forestadmin/agent 1.83.0.
It takes no arguments: it reads schemaPath, typingsPath and typingsMaxDepth from the options you passed to createAgent. It always writes the schema to schemaPath, and also writes the TypeScript typings to typingsPath in the same pass whenever that option is set. Unlike agent.start(), it always rebuilds the schema, even when isProduction is true, and it never contacts Forest, with one exception: if you enable experimental no-code customizations, it still fetches their configuration from the Forest API, so connectivity is required in that case.
Despite the name, Only means it only generates the files without starting the agent or sending the schema to Forest, not “the schema only”. If you want to regenerate only the typings, use agent.updateTypesOnFileSystem() instead.
Example:
This does not close your data source connections. In a one-shot script, close your data source (the SQL/Sequelize/Mongo client you passed in) or call process.exit() once it resolves, otherwise an open connection pool can keep the process alive.

Collection Customizer

Methods available when customizing a collection through agent.customizeCollection().

Actions

collection.addAction(name, definition)

Add an action to the collection.
Definition Properties: Execute Function:
ActionContext Properties:
  • context.collection - Collection instance
  • context.filter - Filter for selected records
  • context.caller - User who triggered the action
  • context.formValues - Form values submitted
ActionContext Methods:
  • context.getRecords(fields) - Get multiple records (Bulk/Single scope)
  • context.getRecordIds() - Get IDs of selected records
  • context.getCompositeRecordIds() - Get composite IDs of selected records
  • context.hasFieldChanged(fieldName) - Check if form field changed
  • context.getRecord(fields) - Get single record (Single scope only)
  • context.getRecordId() - Get single record ID (Single scope only)
  • context.getCompositeRecordId() - Get composite ID (Single scope only)
  • context.getField(fieldName) - Get single field value (Single scope only)
ResultBuilder Methods:
  • resultBuilder.success(message?, options?) - Success response
    • options.html - Custom HTML to display
    • options.invalidated - Array of collection names to refresh
  • resultBuilder.error(message?, options?) - Error response
    • options.html - Custom HTML to display
  • resultBuilder.webhook(url, method, headers, body) - Trigger webhook
  • resultBuilder.file(stream, filename, mimeType) - Return file download
  • resultBuilder.redirectTo(path) - Redirect to URL
  • resultBuilder.setHeader(name, value) - Add HTTP header to response
Example - Simple Action:
Example - Action with Form:
Example - File Generation:

Fields

collection.addField(name, definition)

Add a computed field to the collection.
Definition Properties: Column Types:
  • 'String' - Text
  • 'Number' - Numeric value
  • 'Boolean' - True/false
  • 'Date' - Date with time
  • 'Dateonly' - Date without time
  • 'Time' - Time only
  • 'Enum' - Enumeration
  • 'Json' - JSON object
  • 'Uuid' - UUID
  • 'Point' - Geographic point
  • 'File' - File reference
Example - Simple Computed Field:
Example - Async Computed Field:

collection.importField(name, options)

Import a field from a related collection.
Example:

collection.renameField(currentName, newName)

Rename a field in the exported schema.
Example:

collection.removeField(…names)

Remove fields from the exported schema (they remain usable within the agent).
Example:

collection.addFieldValidation(name, operator, value?)

Add a validation rule to a field.
Operators: 'Present', 'LongerThan', 'ShorterThan', 'Contains', 'Like', 'Match', 'GreaterThan', 'LessThan', 'Before', 'After' Example:

collection.setFieldNullable(name)

Mark a field as optional (nullable).
Your database might still refuse empty values if it requires one.
Example:

collection.replaceFieldWriting(name, definition)

Replace the write behavior of a field.
Example:

collection.replaceFieldBinaryMode(name, mode)

Choose how binary data should be transported to the GUI.
Modes:
  • 'datauri' - Best for file uploads, uses FilePicker widget
  • 'hex' - Best for short binary data like UUIDs
Example:

Relationships

collection.addManyToOneRelation(name, foreignCollection, options)

Add a many-to-one relationship.
Example:

collection.addOneToManyRelation(name, foreignCollection, options)

Add a one-to-many relationship.
Example:

collection.addOneToOneRelation(name, foreignCollection, options)

Add a one-to-one relationship.
Example:

collection.addManyToManyRelation(name, foreignCollection, throughCollection, options)

Add a many-to-many relationship.
Example:

collection.addExternalRelation(name, definition)

Add a virtual collection into the related data of a record.
Example:

Segments

collection.addSegment(name, definition)

Add a segment (saved filter) to the collection.
Example - Static Segment:
Example - Dynamic Segment:

Hooks

collection.addHook(position, type, handler)

Add a hook to execute code before or after operations.
Hook Types: 'List', 'Create', 'Update', 'Delete', 'Aggregate' Example - Before Hook:
Example - After Hook:

collection.replaceSearch(definition)

Replace the default search behavior, with a field selection or a handler.
Field selection. Narrows the fields the default search reads. The agent knows which columns the search reads, so it checks them against the caller’s read permission and extended search keeps working. Requires @forestadmin/datasource-customizer 1.71.3. On a collection whose datasource searches natively — one calling enableSearch(), which no Forest-maintained datasource does — a field selection does not narrow that native search: it replaces it with the agent’s own per-column search restricted to the selection, so matching semantics change with it. Paths cross relations with a colon, at any depth. ManyToOne, OneToOne and OneToMany segments resolve; a ManyToMany segment does not, and a path crossing one is dropped from the selection without an error. extended is absent on purpose: the caller owns that flag, and the agent forwards it from the request.
Handler. Receives (searchString, extended, context) and returns a condition tree. Use it when the filter depends on the search string, an external service, or anything a field list cannot express.
From @forestadmin/agent 1.97.3 on, a handler makes extended search return 403 on the collection: the agent cannot enumerate the fields a handler reads, so it cannot check them against the caller’s read permission. Normal search is unaffected. See Search Configuration.

collection.disableSearch()

Disable search functionality on the collection.

Sorting

collection.emulateFieldSorting(name)

Enable in-memory sorting on a field.
Example:

collection.replaceFieldSorting(name, equivalentSort)

Replace sorting implementation for a field.
Example:

collection.disableFieldSorting(name)

Disable sorting on a specific field.

Filtering

collection.disableFieldFiltering(name)

Disable filtering for end users on a column, or on a many-to-one or one-to-one relationship. Needs @forestadmin/agent 1.107.0 or later. See Disabling filtering.
Example:

collection.emulateFieldFiltering(name)

Enable in-memory filtering on all operators for a field.

collection.emulateFieldOperator(name, operator)

Enable in-memory filtering for a specific operator on a field.
Example:

collection.replaceFieldOperator(name, operator, replacer)

Replace the implementation of a filter operator.
Example:

Charts

collection.addChart(name, definition)

Add a chart to the collection.
Example - Value Chart:
Example - Distribution Chart:
Example - Time-based Chart:

Collection Overrides

collection.overrideCreate(handler)

Replace the default create operation.
Example:

collection.overrideUpdate(handler)

Replace the default update operation.
Example:

collection.overrideDelete(handler)

Replace the default delete operation.
Example:

Other Methods

collection.disableCount()

Disable count in list view pagination for improved performance.

collection.use(plugin, options?)

Load a plugin on a specific collection.
Example:

Chart Result Builders

When creating charts with collection.addChart() or agent.addChart(), the result builder provides methods to format chart data.

resultBuilder.value(value, previousValue?)

Create a Value chart (single number).

resultBuilder.distribution(obj)

Create a Distribution/Pie chart.

resultBuilder.timeBased(timeRange, values)

Create a Time-based/Line chart.
Time Ranges: 'Day', 'Week', 'Month', 'Quarter', 'Year'

resultBuilder.multipleTimeBased(timeRange, dates, lines)

Create a Multi-line Time-based chart.

resultBuilder.percentage(value)

Create a Percentage chart.

resultBuilder.objective(value, objective)

Create an Objective chart (progress toward goal).

resultBuilder.leaderboard(obj)

Create a Leaderboard chart (sorted distribution).

resultBuilder.smart(data)

Create a Smart chart (custom format).

Form Field Types

Action forms support various field types with different widgets.

Basic Field Types

Available Types:
  • 'String' - Text input
  • 'Number' - Numeric input
  • 'Boolean' - Checkbox
  • 'Date' - Date picker
  • 'Dateonly' - Date without time
  • 'Time' - Time picker
  • 'Enum' - Single selection
  • 'EnumList' - Multiple selection
  • 'File' - File upload
  • 'FileList' - Multiple file upload
  • 'Json' - JSON editor
  • 'Collection' - Record picker
  • 'NumberList' - Array of numbers
  • 'StringList' - Array of strings

Enum Fields


Collection Fields

Pick a record from another collection:


Dynamic Dropdown


Conditional Fields

Show/hide fields based on other field values:

Dynamic Field Values


Read-Only Fields


ConditionTree Utilities

Build complex filter conditions programmatically.

ConditionTreeLeaf

Simple condition on a single field:

ConditionTreeBranch

Combine multiple conditions with AND/OR:

ConditionTree Factory


Available Operators

Comparison:
  • 'Equal', 'NotEqual'
  • 'GreaterThan', 'LessThan'
  • 'In', 'NotIn'
  • 'Present', 'Blank'
String:
  • 'Contains', 'NotContains'
  • 'StartsWith', 'EndsWith'
  • 'Like', 'ILike' (case-insensitive)
Date:
  • 'Before', 'After'
  • 'Today', 'Yesterday', 'PreviousWeek', 'PreviousMonth', 'PreviousQuarter', 'PreviousYear'
  • 'Past', 'Future'
Array:
  • 'IncludesAll', 'IncludesNone'

Caller

User information available in all contexts:

Filter


Projection

Specify which fields to retrieve:

Plugin: Flattener

@forestadmin/plugin-flattener flattens nested data structures (composite columns, relations, JSON columns) into individual top-level fields.

flattenColumn(dataSource, collection, options)

Decompose a column with a composite type into individual fields.
Options:

flattenRelation(dataSource, collection, options)

Import fields from a relation directly into the collection.
Options:

flattenJsonColumn(dataSource, collection, options)

Expand a JSON column into individual typed fields.
Options:

Package: Forest Cloud

@forestadmin/forest-cloud is a dev-only package that provides CLI tooling for cloud-hosted customization projects.

Installation

CLI Commands

bootstrap

Initialize a cloud customization project. Authenticates with Forest, creates a cloud-customizer directory, configures credentials, and generates type definitions.

update-typings

Regenerate TypeScript type definitions based on your current database structure and customization code.

login

Refresh your Forest authentication token.

Exported Types


Package: Agent Testing

@forestadmin/agent-testing provides utilities to test agent customizations locally without connecting to Forest servers.

createForestServerSandbox(port)

Start a local sandbox that mimics Forest servers.

createAgentTestClient(options)

Connect a test client to a running agent to simulate frontend requests.
Parameters:

Typical test workflow