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?: MastraAgentDefinition | Record<string, MastraAgentDefinition> | MastraAgentDefinition[]

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 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

optional 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 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 | { apiKey?: string; headers?: Record<string, string>; id: `${string}/${string}`; url?: string; } | { 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.modeldefaultModelDATABRICKS_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), with the small built-in FALLBACK_MODEL_IDS list as the floor when the catalogue can’t be read. 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. Feedback controls show and writes are attempted regardless of env detection (use when the env is configured in a way the auto-probe doesn’t 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 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 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 reuses the lakebase plugin’s pool; an object opens a dedicated 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

Mastra OpenAI-compatible provider id. Defaults to "databricks"; 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.

Provisioned skills are written to the Databricks user’s Assistant skills folder (/Users/<email>/.assistant/skills, the “save this as a skill” target) so they persist and are picked up by the built-in Assistant-skills mount. With no writable workspace, they go to a local temp dir instead.

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

optional storage?: boolean | PostgresStoreConfig

PostgresStore for Mastra threads/messages. true reuses the lakebase plugin’s pool; an object opens a dedicated store.


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.