Backstop MCP - Configuration
10 min read
Overview
All configuration is through environment variables. Every variable this service defines is listed on this page, grouped the way config.py groups them. The FASTMCP_* and OTEL_* variables read by the FastMCP and OpenTelemetry libraries are not covered and not supported.
App default is what the server uses when the variable is unset.
Chart default is what the Helm chart's
values.yamlsets underenv. A blank cell means the chart leaves it to the app.Most values are validated at startup, and a value outside its allowed range stops the server. The error names the settings class and field, for example
BackstopConfig/max_retry_attemptsforBACKSTOP_MAX_RETRY_ATTEMPTS.BACKSTOP_CUSTOM_FIELD_SCHEMA_TTL_MINUTESis clamped instead.SEARCH_*,RESOLUTION_*,PRODUCT_INVESTORS_*, andACTIVITY_HISTORY_*are read on the first tool call that uses them. A bad value there does not stop startup; it fails those tool calls.Variable names are case-insensitive, except
DATABASE_URLandLOGS_DIAGNOSTICS_DATA_POLICY, which must be upper case. Booleans accepttrue/false.
Only two variables have no usable default in production: PUBLIC_BASE_URL and BACKSTOP_MCP_ENCRYPTION_KEY. A database connection is also required.
Setting values with Helm
Kind | Where | Example |
|---|---|---|
Per-firm settings |
|
|
Tuning (caches, timeouts, page sizes, logging) |
|
|
Secrets |
|
|
Database |
|
|
Chart values
The settings that differ per firm have typed values under mcpConfig:
Chart value | Variable | Type |
|---|---|---|
|
| URL |
|
| URL |
|
| URL |
|
| List |
|
| List |
|
| List |
|
| List |
|
| Object, rendered as JSON. See Tenant guidance |
mcpConfig:
app:
publicBaseUrl: https://backstop-mcp.example.com
backstop:
baseUrl: https://<firm>.backstopsolutions.com
uiBaseUrl: https://<firm>.backstopsolutions.com
employment:
formerRelationshipTypeIds: ["1042", "1043"]A misspelt key fails
helm installagainst the chart schema. A URL must start withhttp://orhttps://, and a list item cannot contain a comma. Ids are strings: quote them (["1042"]), or the schema rejects them.A URL is rendered only when it is not empty, so an empty value keeps the app default. URLs pass through
tpl, so an overlay can build them from other values. The schema checks the raw value beforetplruns, so the scheme must be literal:https://{{ .Values.routes.hostname }}passes, while{{ printf "https://%s" .Values.routes.hostname }}is rejected.A list is rendered comma-separated whenever it is set, including
[], which reaches the app as an empty value. The chart defaultnullleaves the variable unset. This matters for the markers lists:[]replaces their defaults,nullkeeps them. See Departed-contact detection.mcpConfig.tenantGuidanceis rendered only when it is not empty.mcpConfig.enabled: falserenders none of these variables, tenant guidance included.A variable set both under
mcpConfigand underenvtakes themcpConfigvalue.envreaches the container through a ConfigMap (envFrom), and Kubernetes gives a container's ownenventries precedence over it. A deployment that still sets these variables underenvkeeps working, and can move them tomcpConfigat any time.
Required
Variable | Format | Description |
|---|---|---|
|
| The URL clients and browsers reach this server on. It is the OAuth issuer; the login form and every OAuth endpoint hang off it. With |
| Fernet key | Encrypts stored Backstop credentials. Startup fails if it is missing or invalid. See Authentication: Encryption key |
| See Database | PostgreSQL connection |
Application
Variable | App default | Chart default | Description |
|---|---|---|---|
|
|
|
|
|
|
| HTTP port. The container image also sets |
|
|
|
|
|
|
|
|
|
| Set per deployment in | See Required |
| Package version |
| Reported as the MCP server version and on metrics. Not meant to be set by hand |
Backstop API
Where to reach Backstop and how the shared HTTP client behaves. Prefix BACKSTOP_.
Variable | App default | Chart default | Description |
|---|---|---|---|
|
| Set per deployment in | Backstop REST API origin: the tenant's own host or the shared API host. Both work |
| unset | Set per deployment in | CRM UI origin used to build links to records ( |
|
| Timeout for one ordinary Backstop request | |
|
| Timeout for requests to | |
|
| Concurrent Backstop requests allowed per user, per pod. Backstop itself limits each user token to 5 | |
|
| Attempts for a request that gets a | |
|
| Longest single backoff the server will wait before retrying a | |
|
| Page size for ordinary collection walks | |
|
| Page size for | |
|
| Name of the page-size query parameter. Backstop silently ignores an unknown parameter, so only change it if Backstop changes | |
|
| Name of the page-offset query parameter. Same caution |
CRM links
Tools that return a record also return its Backstop UI url, and build_backstop_links builds tab and layout links. The origin is chosen like this:
BACKSTOP_UI_BASE_URLwhen set.Otherwise
BACKSTOP_BASE_URL, but only when its host is notapi.backstopsolutions.com. A tenant host is assumed to also serve the UI.Otherwise no links: record
urlfields are empty andbuild_backstop_linksanswersnot_configured.
Set BACKSTOP_UI_BASE_URL explicitly whenever BACKSTOP_BASE_URL is not the tenant's own host. A regional or staging API host would otherwise be used as a UI origin and produce links that do not load.
Catalog Caches
Several tools read small instance-wide catalogs (custom-field definitions, activity tags, system users, and so on). Each catalog has an on/off flag and a TTL. With the flag off, every read fetches the catalog from Backstop again. Concurrent requests for the same catalog share one fetch either way.
Catalog | Enable flag | App / chart flag | TTL variable | Default TTL |
|---|---|---|---|---|
Custom-field definitions and groups |
|
|
| 120 |
Contact sources |
|
|
| 1440 |
Contact categories |
|
|
| 1440 |
Activity tags |
|
|
| 1440 |
System users |
|
|
| 1440 |
Time zones |
|
|
| 1440 |
Opportunity stages | Always on |
| 60 | |
Entity-relationship types | Always on |
| 60 |
Every TTL is in minutes, between 1 and 1440 (24 hours). A custom-field TTL above 1440 is lowered to 1440 instead of failing startup.
A TTL is ignored while its flag is off.
The custom-field cache is on because large tenants have thousands of definitions and one walk takes several seconds; every party and product read joins against it.
list_custom_fields,list_custom_field_groups,list_activity_tags,list_contact_sources,list_contact_categories, andlist_system_usersacceptrefresh=trueto fetch again regardless of the TTL.When a cached catalog's refresh fails, the previous copy keeps being served and the TTL restarts. Opportunity stages and relationship types are the exception: a failed refresh fails the call.
To decide whether to turn a cache on, compare
catalog_get_duration_seconds_count(demand) withcatalog_fetch_duration_seconds_count(actual walks). See Architecture: Observability.
Departed-Contact Detection
get_person, get_people_for_party, and search_people report whether a person's link to an organization is current or former. A firm may record a departure as a separate relationship type ("is a former employee of") rather than an end date, so the server needs to know which relationship types mean employment and which mean it has ended.
Variable | Chart value ( | App default | Description |
|---|---|---|---|
|
| empty | Relationship type ids that mean employment. Exact match |
|
|
| Case-insensitive substrings of a type's name that mean employment |
|
| empty | Relationship type ids that mean employment has ended |
|
|
| Case-insensitive substrings that mean employment has ended |
All four are comma-separated; in Helm they are lists. Setting a markers variable replaces its defaults; setting it empty (
[]in Helm) leaves only the ids for that bucket. Leave the chart valuenullto keep the defaults.The two marker lists must not overlap. A shared substring such as
employeecannot tell a current type from a former one, which is why the departure word is the marker. This is not checked at startup: an overlap silently misclassifies links.Ids differ per Backstop instance and have no default.
GET /entity-relationship-typeslists them.An organization's own record returns the mirror type ("… (mirror)") with a different id. Markers match both directions; if you configure former ids, include the mirror type's id too.
Tool Behaviour
Variable | App default | Chart default | Description |
|---|---|---|---|
|
|
| Rows one call of |
|
|
| How long a tool waits for the user to pick between several matching records before returning the candidate list instead. The Unique chat client can cancel a tool call at 60 seconds |
|
|
| Most accounts |
|
| Page size per stream in | |
|
| Characters each activity body is cut to in |
Tenant Guidance
BACKSTOP_MCP_TENANT_GUIDANCE adds firm-specific text to what the AI reads, without changing the shipped tool descriptions. In Helm, set the mcpConfig.tenantGuidance object; the chart renders it as JSON into the variable, and omits the variable when the object is empty.
mcpConfig:
tenantGuidance:
server_instructions: >-
"Strategy" on a product is the custom field named Investment Strategy.
tools:
search_products:
description: Fund short names start with NW, for example NWGM.
parameters:
name: Short names are upper case.Field | Limit | Appended to |
|---|---|---|
| 1 to 2,500 characters | The server instructions, under the heading "This firm's Backstop setup:" |
| 1 to 800 characters | That tool's description, under the same heading |
| 1 to 300 characters | That top-level parameter's description |
An unknown tool or parameter name stops startup, and the error lists the valid names.
Unknown keys are rejected.
Empty or unset means the shipped descriptions only.
Database
Variable | App default | Description |
|---|---|---|
| unset | PostgreSQL URL. |
| unset | Used when |
| unset | Used only when neither URL is set |
|
| |
| unset | |
| unset | |
| unset |
Without a URL, DB_HOST, DB_NAME, DB_USER, and DB_PASSWORD are all required and startup names the missing ones.
TLS is read from the URL's sslmode parameter:
| Behaviour |
|---|---|
| No TLS |
| TLS without certificate verification |
| TLS, verifies the certificate chain but not the hostname |
| TLS, verifies the chain and the hostname |
verify-caandverify-fullcheck the certificate against the container's system CA bundle.sslrootcertand the other libpq certificate parameters are not supported (the driver rejects them), so a database with a privately signed certificate can only userequire.With
postgresql.connection.url, putsslmodein the URL. The chart'spostgresql.connection.sslModeand thePG_DATABASE_SSL_MODEvariable it sets are not read by this server.With
postgresql.connection.host,database,username, andpassword, the chart buildsDATABASE_URLitself withsslmode=<postgresql.connection.sslMode>, defaultverify.
Migrations (alembic upgrade head) read the same variables as the server.
Authentication and Encryption
Variable | App default | Description |
|---|---|---|
| none (required) | Fernet key. See Authentication: Encryption key |
|
| Failed logins allowed per Backstop username within the window |
|
| Throttle window |
|
| How long fully expired OAuth token families are kept as an audit trail before the sweep deletes them |
|
| How long a registered OAuth client that nothing references is kept |
|
| How often the cleanup sweep runs |
Token lifetimes (15-minute access, 30-day refresh) are fixed in code. See Authentication: Token lifetimes.
Related
Local Development: the minimum
.envfor a local run, based on the commented.env.example