@forestadmin/agent (Node.js/TypeScript).
Agent Class
createAgent(options)
Create and configure a Forest agent.
Example:
agent.addDataSource(factory, options?)
Add a datasource to the agent.
Example:
agent.customizeCollection(name, callback)
Customize a specific collection with the provided callback.agent.addChart(name, definition)
Create a datasource-level API chart.agent.removeCollection(…names)
Remove collections from the exported schema (they remain usable within the agent).agent.use(plugin, options?)
Load a plugin across all collections.agent.start()
Start the agent and connect to Forest servers.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.addAi(provider)
Route Forest’s AI features (natural language search, actions, etc.) through your own agent so your data never leaves your infrastructure. Pass a provider built withcreateAiProvider (from @forestadmin/ai-proxy); openai and anthropic are supported. See Self-hosted AI for the full guide.
agent.mountAiMcpServer(options?)
Enable a Model Context Protocol (MCP) server on the agent, allowing AI assistants to interact with your data through standardized tools.
Available tool names:
'describeCollection', 'list', 'listRelated', 'create', 'update', 'delete', 'associate', 'dissociate', 'getActionForm', 'executeAction'
Example:
Scoping under a prefix:
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.FOREST_MCP_ENABLED_TOOLS environment variable (comma-separated tool names), or set the server port with MCP_SERVER_PORT (default: 3931).
agent.updateTypesOnFileSystem(typingsPath, typingsMaxDepth)
Update the TypeScript typings file generated from your datasources.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.
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.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 throughagent.customizeCollection().
Actions
collection.addAction(name, definition)
Add an action to the collection.
Execute Function:
context.collection- Collection instancecontext.filter- Filter for selected recordscontext.caller- User who triggered the actioncontext.formValues- Form values submitted
context.getRecords(fields)- Get multiple records (Bulk/Single scope)context.getRecordIds()- Get IDs of selected recordscontext.getCompositeRecordIds()- Get composite IDs of selected recordscontext.hasFieldChanged(fieldName)- Check if form field changedcontext.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.success(message?, options?)- Success responseoptions.html- Custom HTML to displayoptions.invalidated- Array of collection names to refresh
resultBuilder.error(message?, options?)- Error responseoptions.html- Custom HTML to display
resultBuilder.webhook(url, method, headers, body)- Trigger webhookresultBuilder.file(stream, filename, mimeType)- Return file downloadresultBuilder.redirectTo(path)- Redirect to URLresultBuilder.setHeader(name, value)- Add HTTP header to response
Fields
collection.addField(name, definition)
Add a computed field to the collection.
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
collection.importField(name, options)
Import a field from a related collection.collection.renameField(currentName, newName)
Rename a field in the exported schema.collection.removeField(…names)
Remove fields from the exported schema (they remain usable within the agent).collection.addFieldValidation(name, operator, value?)
Add a validation rule to a field.'Present', 'LongerThan', 'ShorterThan', 'Contains', 'Like', 'Match', 'GreaterThan', 'LessThan', 'Before', 'After'
Example:
collection.setFieldNullable(name)
Mark a field as optional (nullable).collection.replaceFieldWriting(name, definition)
Replace the write behavior of a field.collection.replaceFieldBinaryMode(name, mode)
Choose how binary data should be transported to the GUI.'datauri'- Best for file uploads, uses FilePicker widget'hex'- Best for short binary data like UUIDs
Relationships
collection.addManyToOneRelation(name, foreignCollection, options)
Add a many-to-one relationship.collection.addOneToManyRelation(name, foreignCollection, options)
Add a one-to-many relationship.collection.addOneToOneRelation(name, foreignCollection, options)
Add a one-to-one relationship.collection.addManyToManyRelation(name, foreignCollection, throughCollection, options)
Add a many-to-many relationship.collection.addExternalRelation(name, definition)
Add a virtual collection into the related data of a record.Segments
collection.addSegment(name, definition)
Add a segment (saved filter) to the collection.Hooks
collection.addHook(position, type, handler)
Add a hook to execute code before or after operations.'List', 'Create', 'Update', 'Delete', 'Aggregate'
Example - Before Hook:
Search
collection.replaceSearch(definition)
Replace the default search behavior.collection.disableSearch()
Disable search functionality on the collection.Sorting
collection.emulateFieldSorting(name)
Enable in-memory sorting on a field.collection.replaceFieldSorting(name, equivalentSort)
Replace sorting implementation for a field.collection.disableFieldSorting(name)
Disable sorting on a specific field.Filtering
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.collection.replaceFieldOperator(name, operator, replacer)
Replace the implementation of a filter operator.Charts
collection.addChart(name, definition)
Add a chart to the collection.Collection Overrides
collection.overrideCreate(handler)
Replace the default create operation.collection.overrideUpdate(handler)
Replace the default update operation.collection.overrideDelete(handler)
Replace the default delete operation.Other Methods
collection.disableCount()
Disable count in list view pagination for improved performance.collection.use(plugin, options?)
Load a plugin on a specific collection.Chart Result Builders
When creating charts withcollection.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.'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
'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:Dropdown Widget
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'
'Contains','NotContains''StartsWith','EndsWith''Like','ILike'(case-insensitive)
'Before','After''Today','Yesterday','PreviousWeek','PreviousMonth','PreviousQuarter','PreviousYear''Past','Future'
'IncludesAll','IncludesNone'
Related Types
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.flattenRelation(dataSource, collection, options)
Import fields from a relation directly into the collection.flattenJsonColumn(dataSource, collection, options)
Expand a JSON column into individual typed fields.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 acloud-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.