Skip to content

Interface: MastraPluginConfig

Configuration accepted by the Mastra AppKit plugin.

  • BasePluginConfig

[key: string]: unknown

optional agentMaxSteps?: number

Maximum LLM steps each agent gets per turn. One step = one round-trip to the underlying model (a tool call consumes a step, the final-text reply consumes one too). Applies to every agent registered through MastraPluginConfig.agents

  • per-agent overrides aren’t surfaced yet because the same ceiling has been sufficient across every workload we’ve run.

Defaults to DEFAULT_AGENT_MAX_STEPS (25), sized to fit a decomposed Genie turn (grounding + several ask_genie calls

  • prepare_chart per dataset + the final-text reply) with headroom for the model to chain a couple of follow-ups before answering. Mastra’s own agent.generate default of 5 would cut multi-step orchestration off after 2-3 tool calls, so explicitly raising the ceiling here is what lets the agent-mode loop play out.

Lower when an unusually slow or expensive model makes long turns unaffordable; raise for exploratory workloads that need to drill deep into a dataset within a single turn.


optional agents?: AnyMastraAgentDefinition | Record<string, AnyMastraAgentDefinition> | AnyMastraAgentDefinition[]

Code-defined agents. Accepts three shapes for convenience:

  • Record: { analyst: def, helper: def } - keys become the registered ids and the first key is the default.
  • Single definition: def - registered under slugify(def.name) (or "default" when name is omitted) and automatically marked as the default agent.
  • Array: [def1, def2] - each registered under slugify(def.name) (or agent_${i} when name is omitted); the first entry is the default.

Each entry becomes a Mastra Agent reachable at /api/<plugin>/route/chat/<id> (the chat route also matches :agentId). When agents is omitted entirely, the plugin registers a single built-in default analyst so the bare mastra() call still mounts a working chat endpoint.

Single-agent shorthand

mastra({
agents: createAgent({ instructions: "..." }),
});

Array

mastra({
agents: [
createAgent({ name: "analyst", instructions: "..." }),
createAgent({ name: "helper", instructions: "..." }),
],
});

Record (explicit ids)

mastra({
agents: {
analyst: createAgent({ instructions: "..." }),
helper: createAgent({ instructions: "..." }),
},
defaultAgent: "analyst",
});

optional apiAccess?: "scoped" | "full"

How much of the stock @mastra/express management API is reachable through the plugin mount. @mastra/express registers its full route table (agent inference plus admin / mutating routes: direct tool execution, workflow control, raw memory read/write, telemetry, logs, scores). AppKit already authenticates every request as the OBO user, but nothing there restricts which of those operations the browser client may invoke.

  • "scoped" (default): only the routes the chat client legitimately needs are dispatched to Mastra - agent inference (stream / generate / network), read-only agent metadata, this plugin’s own OBO- and resource-scoped /route/* routes (history / threads), and, when mcp is enabled, the MCP transport. Everything else (tool execution, workflow control, raw memory, telemetry, logs, scores, and other mutations) is rejected with 403 before it reaches Mastra.
  • "full": dispatch the entire stock Mastra API. Use only for a trusted first-party console that genuinely needs the management surface.

optional backgroundTurns?: boolean

Run turns through Mastra’s evented durable agent so execution continues after a browser disconnect and can be observed again by run id. Defaults to true.


optional brand?: object

Optional brand context applied to charts produced by the built-in render_data / prepare_chart tools. When set, the chart planner’s Echarts output is themed with the brand’s palette (series colors derived from colors.primary / colors.accent) and sans font (typography.sans) instead of Echarts’ defaults. Omit for the default Echarts look.

Pass the portable BrandContext shared across the UI and libraries (e.g. brand.defaultBrandContext from @dbx-tools/shared-core, or a customer brand) - the same object the email add-on and the UI BrandProvider consume, so a host themes charts, email, and UI from one source.

assets: object

favicon: string

icon: object

optional dark?: string

light: string

logo: object

optional dark?: string

light: string

colors: object = BrandColorsSchema

accent: string

background: string

border: string

foreground: string

muted: string

primary: string

primaryHover: string

surface: string

description: string

extensions: Record<string, unknown>

links: object

optional documentation?: string

optional repository?: string

website: string

name: string

schemaVersion: "1"

shortName: string

tagline: string

typography: object

mono: string

sans: string

voice: object = BrandVoiceSchema

audience: string[]

avoid: string[]

principles: string[]

tone: string[]


optional codeMode?: boolean | MastraCodeModeConfig

Expose executable tools available to the current request through native Mastra Code Mode. Defaults on whenever the request has executable tools. The plugin selects Mastra’s isolated-vm transport once during startup and falls back to Mastra QuickJS when the native transport is unavailable. These transports do not execute through the workspace command sandbox; SSH is used only when an exposed SSH-backed tool is called. Set false to opt out.


optional defaultAgent?: string

Agent id used when the client doesn’t specify one (the bare, un-suffixed history / suggestions routes resolve to it). Defaults to the first key in agents (or "default" when agents is omitted). Must match an id in agents when both are set; a mismatch throws at setup with the available candidates.


optional defaultModel?: string | LanguageModelV1 | LanguageModelV2 | LanguageModelV3 | LanguageModelV4 | string & object | { api?: "chat" | "responses"; apiKey?: string; headers?: Record<string, string>; id: `${string}/${string}`; url?: string; } | { api?: "chat" | "responses"; apiKey?: string; headers?: Record<string, string>; modelId: string; providerId: string; url?: string; } | MastraLanguageModelV2 | MastraLanguageModelV3 | MastraLanguageModelV4 | ModelWithRetries[] | ((__namedParameters) => MastraModelConfig | ModelWithRetries[] | Promise<MastraModelConfig | ModelWithRetries[]>)

Plugin-level default model applied to every agent that omits its own model. Mirrors AppKit’s agents({ defaultModel }).

  • string: shorthand for “use the OBO auto-resolver but swap the modelId” (e.g. "databricks-claude-sonnet-4-6").
  • Any other Mastra DynamicArgument<MastraModelConfig>: passed through verbatim. Use this when you need full control over auth or providerId.

Resolution order per agent: def.model → defaultModel → DATABRICKS_SERVING_ENDPOINT_NAME → built-in /serving-endpoints resolver.


optional defaultModelFallbacks?: readonly string[]

Priority-ordered list of endpoint names tried first when no agent / plugin / env / request-override model id is set, ahead of the dynamic score-classified catalogue. The resolver picks the first id that is actually present in the workspace’s /serving-endpoints listing.

When unset, resolution is driven by the live Foundation Model API quality / speed / cost scores: endpoints are classified into chat classes (classifyEndpoints) and walked best-first (ChatThinking -> ChatBalanced -> ChatFast). The automatic default is live-only and fails when no matching endpoint is currently available. Set this to pin a regulated workspace to an approved subset, or to put custom endpoints in front of the auto-classified catalogue.


optional feedback?: boolean

Log user feedback (thumbs up/down + freeform comments) to MLflow as trace assessments, and surface the feedback controls in the chat UI.

  • undefined (default, auto): enabled only when MLflow tracing is wired - an OTLP exporter endpoint is set and an MLflow experiment is named (the same signals the observability pipeline needs to ship traces to MLflow). Otherwise off, since there’d be no trace to attach feedback to.
  • true: force on. Requires MLFLOW_EXPERIMENT_ID or MLFLOW_EXPERIMENT_NAME; app startup fails when neither is configured. Use this when trace export is wired in a way the auto-probe does not recognize.
  • false: force off. No trace-id header, no feedback route, no UI.

Feedback attaches to a turn’s MLflow trace via the OpenTelemetry trace id the server stamps on each response; see mlflow.ts.


optional genieAgentMode?: boolean

Use the streaming Genie Agent Mode API for ask_genie. Defaults to true. Set false to force the legacy Conversation API polling driver. A pre-stream feature-disabled or preview-toggle response falls back automatically.


optional genieIdentity?: IdentityMode

Which Databricks identity the agents’ workspace calls run as: the serving-endpoint catalogue behind the model picker, Genie suggestions, ask_genie, and the Statement Execution fetch behind a [data:<id>] embed. Falls back to MASTRA_GENIE_IDENTITY, then "user".

On-behalf-of (OBO) auth requires the caller to be a member of the WORKSPACE, not just of the Databricks account. An app shared with an account-level group can be opened by someone whose token is valid but whose every workspace call fails with Unauthorized access to Org: <id> - and granting workspace membership is not always possible, since a workspace caps membership well below the size of a large account’s user group. The app’s service principal already holds the grants the app was deployed with.

  • "user" (default): always OBO. Per-user attribution, and Genie / Unity Catalog row filters apply per user. Correct when every caller is a workspace member.
  • "service-principal": always the app service principal. Needs no OBO scopes and works for any caller who can open the app, at the cost of per-user attribution in Genie / Unity Catalog.
  • "auto": OBO when the request carries an OBO token, the service principal when it does not. For an app serving BOTH the platform front door and a door that has no token to forward - a @dbx-tools/tunnel gate, a Teams channel - where AppKit’s asUser would otherwise throw AuthenticationError outside NODE_ENV=development.

The service-principal path changes only the Databricks CREDENTIAL. Memory threads, the per-user cache namespace, and trace metadata still key off the forwarded user, so callers sharing the service principal’s data access keep separate conversations and cannot read each other’s threads or charts.

An app any account user can open

mastra({ genieIdentity: "service-principal", genieSpaces: { default: spaceId } });

One app behind both the front door and a public tunnel

mastra({ genieIdentity: "auto", genieSpaces: { default: spaceId } });

optional genieSpaceCacheTtlMs?: number

TTL for the in-memory Genie space metadata cache, in milliseconds. Defaults to 5 minutes. The Genie agent calls client.genie.getSpace(...) on every cold-start to get the title / description / warehouse id; cached responses skip the round-trip and concurrent callers coalesce on a single in-flight fetch. Drop to a smaller value when analysts are actively editing space metadata and you want changes visible within seconds; raise it to amortise the round-trip when space metadata is effectively frozen.

Backed by AppKit’s CacheManager, so the cache participates in telemetry spans (cache.getOrExecute) and benefits from Lakebase persistence when the lakebase plugin is wired up.


optional genieSpaces?: GenieSpacesConfig

Genie spaces this plugin’s agents can delegate to. One Mastra tool is registered per alias (genie for the well-known default alias, genie_<alias> otherwise). Each tool spins up a per-question Genie sub-agent that runs Databricks “agent mode” against the space, broadcasts wire events to the UI, fetches statement rows for non-empty results, and returns a (string | data | chart)[] summary the host UI renders inline.

Entries accept either a full GenieSpaceConfig object or a bare space_id string when no extras are needed:

mastra({
genieSpaces: {
default: "01ef0d3c0e1b1f4a8d2c3e4f5a6b7c8d",
forecasts: { spaceId: "01ef...", hint: "weekly demand forecasts" },
},
});

Reach the spaces from an agent’s tools(plugins) callback via plugins.genie?.toolkit(); the resulting tools accept { content, conversationId? } and return a hydrated summary.

Fallback discovery (highest precedence first): if this field is omitted, the Genie agent also picks up spaces from (1) the AppKit genie({ spaces: { ... } }) plugin instance when registered, and (2) the DATABRICKS_GENIE_SPACE_ID env var (registered under the default alias). This keeps existing AppKit deployments working without restating the spaces config in two places.


optional host?: string

BasePluginConfig.host


optional localSkillMounts?: LocalSkillMount[]

Read-only local skill trees mounted into every default agent workspace.


optional mcp?: boolean | MastraMcpConfig

Expose the plugin’s agents (and optionally tools) as a Mastra MCP server so external MCP clients - Claude Desktop, Cursor, the Mastra playground, or another agent - can call them over the standard MCP transports. Enabled by default (agents only): wrapping the already-registered agents costs nothing extra, so the endpoint is on out of the box; only the ambient tools (which assume an in-process chat turn) stay off unless explicitly opted in.

  • undefined (default) / true: expose every registered agent as an ask_<agentId> MCP tool under a server whose id is the plugin name.
  • false: no MCP endpoints.
  • MastraMcpConfig: fine-grained control over the server id, advertised metadata, and which agents / tools are exposed.

When enabled, the stock Mastra MCP routes mount under the plugin’s base path (no bespoke route is added - the server is handed to the Mastra instance via mcpServers, which @mastra/express serves):

  • Streamable HTTP: POST /api/<plugin>/mcp/<serverId>/mcp
  • SSE (legacy): GET /api/<plugin>/mcp/<serverId>/sse POST /api/<plugin>/mcp/<serverId>/messages

Requests run under the same AppKit OBO scope as the chat routes, so an agent invoked over MCP resolves its model and tools as the calling user.


optional memory?: boolean | MastraMemoryConfig

PgVector store for Mastra memory recall. true captures the lakebase plugin’s existing service-principal pool; an object opens a separately configured store.


optional modelCacheTtlMs?: number

TTL for the in-memory serving-endpoints list cache, in milliseconds. Defaults to 5 minutes; no env fallback.

The cache is per workspace host and shared across users; concurrent callers coalesce on a single in-flight fetch.


optional modelFuzzyMatch?: boolean

Fuzzy-match loose model names ("claude sonnet") against the workspace’s Model Serving endpoints. Defaults to true; no env fallback.

Set false to require exact endpoint names everywhere.


optional modelFuzzyThreshold?: number

Fuse.js score threshold for the fuzzy matcher, 0 (exact) to 1 (anything). Defaults to 0.4; no env fallback.

Lower values reject loose matches; raise it if you have a sprawling endpoint catalogue with similar-looking names.


optional modelOverride?: boolean

Let clients pick the backing endpoint per request. Defaults to true; no env fallback.

Reads the X-Mastra-Model header, the ?model= query string, or a model body field, in that order. Disable when running multi-tenant where untrusted clients shouldn’t choose the endpoint.


optional name?: string

BasePluginConfig.name


optional observability?: boolean

Wire Mastra spans into AppKit’s global OTel pipeline via @mastra/otel-bridge.

  • undefined (default, auto): on only when OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set. When unset, the bridge is skipped so Mastra does not log [OtelBridge] No OTEL span found on the noop tracer.
  • true: force on even without an OTLP endpoint.
  • false: force off.

optional providerId?: string

AI SDK provider-options namespace. Defaults to "openai"; no env fallback.


optional remoteSkills?: RemoteSkillsOption

Remote Agent-Skill sources materialized once at app startup. A single source, a list, or a { sources, failOnError?, ... } bag.

Each source is a GitHub owner/repo, a git / GitLab URL, or a direct SKILL.md / archive download URL. Resolution prefers the OPTIONAL skills npm CLI (a peer dep) which is copied into a temp dir and then persisted; without it, a URL source is fetched directly. A source that resolves through neither fails startup unless failOnError: false.

AI Tools is cached daily with @dbx-tools/cache and served to every auto-created workspace from the shared /.databricks/skills memory mount. Other sources are written to the Databricks user’s Assistant skills folder when writable, with a read-only local-memory fallback.

mastra({ remoteSkills: "vercel-labs/agent-skills" });
mastra({
remoteSkills: {
sources: ["vercel-labs/agent-skills", "https://example.com/skill.md"],
failOnError: false,
},
});

optional sandbox?: boolean | WorkspaceSandbox | "monty" | WorkspaceSandboxResolver

Sandbox for auto-created agent workspaces. Defaults to Monty. false disables command execution; true or "monty" selects Monty; an explicit Mastra sandbox or resolver supplies a remote transport. An agent with an explicit custom workspace keeps that workspace and its sandbox.


optional storage?: boolean | PostgresStoreConfig

PostgresStore for Mastra threads/messages. true captures the lakebase plugin’s existing service-principal pool; an object opens a separately configured store.


optional streamConfig?: StreamConfig

SSE stream configuration for this plugin’s executeStream() calls (buffer sizes, maxEventSize, TTL, heartbeat). Sets the plugin’s StreamManager defaults; a per-call stream config still overrides these. Use it to raise maxEventSize above the 5 MiB default when a stream emits larger events.

BasePluginConfig.streamConfig


optional stripStaleCharts?: boolean

When true (default), every agent gets a built-in input processor that strips chartId fields from prior assistant tool-invocation results before they reach the model. This prevents the model from reusing turn-scoped chartIds it sees in memory recall (which would leave [chart:<id>] markers pointing at writer events that no longer exist).

Set to false to opt out - useful if a non-default agent needs full visibility into prior chartIds (e.g. an audit agent reasoning about chart lineage).


optional styleInstructions?: string | false

Style guardrails appended to every agent’s instructions to curb common LLM-isms (em dashes, emojis, sycophantic openers, throwaway closers, excessive hedging).

  • undefined (default): use the built-in DEFAULT_STYLE_INSTRUCTIONS from agents.ts.
  • string: replace the default with the supplied block.
  • false: disable entirely (agents see only their bespoke instructions).

Appended (not prepended) so the agent’s role and rules come first and the style block leans on the model’s recency bias.


optional telemetry?: TelemetryOptions

BasePluginConfig.telemetry


optional tools?: ToolsInput

Ambient tools spread into every registered agent’s tools record; per-agent tools win on key collision. Use for a small shared library; for per-agent tools set agents[id].tools instead.


optional workspaceFallbackToTmp?: boolean

Add user-scoped /tmp only when no configured workspace path mounts writable.


optional workspaceSkills?: boolean | MastraWorkspaceSkillsConfig

Use Mastra’s native workspace skill manager. Defaults to true. Pass an object to tune on-demand discovery and loaded-skill state.


optional workspaceTools?: WorkspaceToolsConfig

Native Mastra workspace tool availability, approval, and hook configuration.