@dbx-tools/appkit-mastra 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +304 -645
- package/index.ts +46 -38
- package/package.json +58 -45
- package/src/agents.ts +224 -66
- package/src/chart.ts +531 -429
- package/src/config.ts +270 -19
- package/src/filesystems.ts +1090 -0
- package/src/genie.ts +1000 -660
- package/src/history.ts +166 -79
- package/src/mcp.ts +105 -0
- package/src/memory.ts +94 -92
- package/src/mlflow.ts +149 -0
- package/src/model.ts +75 -408
- package/src/observability.ts +121 -69
- package/src/pagination.ts +34 -0
- package/src/plugin.ts +552 -67
- package/src/processors.ts +168 -0
- package/src/rest.ts +67 -0
- package/src/server.ts +232 -45
- package/src/serving-sanitize.ts +167 -0
- package/src/serving.ts +27 -243
- package/src/statement.ts +89 -0
- package/src/storage-schema.ts +41 -0
- package/src/summarize.ts +176 -0
- package/src/threads.ts +338 -0
- package/src/workspaces.ts +346 -0
- package/src/writer.ts +44 -0
- package/tsconfig.json +41 -0
- package/dist/index.d.ts +0 -20
- package/dist/index.js +0 -20
- package/dist/src/agents.d.ts +0 -306
- package/dist/src/agents.js +0 -403
- package/dist/src/chart.d.ts +0 -170
- package/dist/src/chart.js +0 -491
- package/dist/src/config.d.ts +0 -183
- package/dist/src/config.js +0 -12
- package/dist/src/genie.d.ts +0 -131
- package/dist/src/genie.js +0 -630
- package/dist/src/history.d.ts +0 -67
- package/dist/src/history.js +0 -172
- package/dist/src/memory.d.ts +0 -100
- package/dist/src/memory.js +0 -242
- package/dist/src/model.d.ts +0 -159
- package/dist/src/model.js +0 -427
- package/dist/src/observability.d.ts +0 -33
- package/dist/src/observability.js +0 -71
- package/dist/src/plugin.d.ts +0 -130
- package/dist/src/plugin.js +0 -283
- package/dist/src/processors/strip-stale-charts.d.ts +0 -29
- package/dist/src/processors/strip-stale-charts.js +0 -96
- package/dist/src/server.d.ts +0 -46
- package/dist/src/server.js +0 -123
- package/dist/src/serving.d.ts +0 -156
- package/dist/src/serving.js +0 -231
- package/dist/src/tools/email.d.ts +0 -74
- package/dist/src/tools/email.js +0 -122
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/src/processors/strip-stale-charts.ts +0 -105
- package/src/tools/email.ts +0 -147
package/README.md
CHANGED
|
@@ -1,36 +1,70 @@
|
|
|
1
|
-
# @dbx-tools/appkit-mastra
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Databricks App
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
AppKit
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
# @dbx-tools/node-appkit-mastra
|
|
2
|
+
|
|
3
|
+
AppKit plugin and server-side toolkit for hosting Mastra agents inside a
|
|
4
|
+
Databricks App.
|
|
5
|
+
|
|
6
|
+
Import this package when an AppKit backend needs an agent service with
|
|
7
|
+
Databricks on-behalf-of auth, optional Lakebase-backed memory, Databricks Genie
|
|
8
|
+
tools, model selection, chart/data embeds, MLflow feedback, and MCP exposure.
|
|
9
|
+
The package mounts the standard Mastra agent stream under the AppKit server, so
|
|
10
|
+
clients can use Mastra-compatible chat transports instead of a custom protocol.
|
|
11
|
+
|
|
12
|
+
Key features:
|
|
13
|
+
|
|
14
|
+
- AppKit plugin lifecycle integration: routes, setup, shutdown, sibling plugin
|
|
15
|
+
access, and AppKit request context are handled inside `plugin.mastra()`.
|
|
16
|
+
- Agent composition: define one or more Mastra agents, give each one local tools,
|
|
17
|
+
AppKit plugin toolkits, workspace skills, model defaults, and approval-gated
|
|
18
|
+
tools.
|
|
19
|
+
- Databricks execution model: tool calls run with the active AppKit OBO client
|
|
20
|
+
where available, while storage and background work use service-principal
|
|
21
|
+
connections.
|
|
22
|
+
- Durable conversations: Lakebase-backed Mastra storage provides thread
|
|
23
|
+
history, message persistence, and optional vector memory.
|
|
24
|
+
- Rich data answers: Genie tools, statement fetches, chart preparation, and
|
|
25
|
+
embed markers let an agent answer with text plus delayed chart/table payloads.
|
|
26
|
+
- Operational surfaces: model-list routes, feedback routes, MCP exposure,
|
|
27
|
+
scoped API gating, tracing, and MLflow feedback are bundled with the plugin.
|
|
28
|
+
|
|
29
|
+
## Why Not Just AppKit Agents?
|
|
30
|
+
|
|
31
|
+
Native AppKit includes a beta Agents plugin with markdown and TypeScript agent
|
|
32
|
+
definitions, AppKit tool-provider integration, streaming chat, thread
|
|
33
|
+
management, cancellation, and HITL approval. Use it when you want the AppKit
|
|
34
|
+
agent model and do not need a separate agent framework.
|
|
35
|
+
|
|
36
|
+
Use this package when you specifically want Mastra inside AppKit:
|
|
37
|
+
|
|
38
|
+
- Mastra's larger plugin/tool ecosystem, MCP support, memory/storage model,
|
|
39
|
+
workflow primitives, and `@mastra/client-js` stream shape.
|
|
40
|
+
- AppKit toolkits as Mastra tools, so Analytics, Files, Genie, and other AppKit
|
|
41
|
+
ToolProvider plugins stay available without rewriting them.
|
|
42
|
+
- Genie as an agent tool that emits typed progress events, result metadata, and
|
|
43
|
+
delayed chart/data markers into the same assistant turn.
|
|
44
|
+
- A paired React client in [`@dbx-tools/ui-mastra`](../../ui/mastra) with model
|
|
45
|
+
picking, thread sidebar, approvals, feedback, exports, and inline embeds.
|
|
46
|
+
- Per-request model override and fuzzy endpoint resolution through
|
|
47
|
+
[`@dbx-tools/node-model`](../model), instead of binding every agent to a fixed
|
|
48
|
+
endpoint name.
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
20
51
|
|
|
21
52
|
```ts
|
|
22
|
-
import { analytics, createApp,
|
|
23
|
-
import {
|
|
53
|
+
import { analytics, createApp, lakebase, server } from "@databricks/appkit";
|
|
54
|
+
import { agents, genie, plugin } from "@dbx-tools/node-appkit-mastra";
|
|
24
55
|
import { z } from "zod";
|
|
25
56
|
|
|
26
|
-
const
|
|
27
|
-
|
|
57
|
+
const analyst = agents.createAgent({
|
|
58
|
+
name: "analyst",
|
|
59
|
+
instructions: ["You answer questions about workspace data.", genie.GENIE_INSTRUCTIONS].join(
|
|
60
|
+
"\n\n",
|
|
61
|
+
),
|
|
28
62
|
tools(plugins) {
|
|
29
63
|
return {
|
|
30
|
-
...plugins.analytics.toolkit(),
|
|
31
|
-
...plugins.
|
|
32
|
-
get_weather: tool({
|
|
33
|
-
description: "
|
|
64
|
+
...plugins.analytics.toolkit(),
|
|
65
|
+
...plugins.genie?.toolkit(),
|
|
66
|
+
get_weather: agents.tool({
|
|
67
|
+
description: "Get a simple weather report.",
|
|
34
68
|
schema: z.object({ city: z.string() }),
|
|
35
69
|
execute: async ({ city }) => `Sunny in ${city}`,
|
|
36
70
|
}),
|
|
@@ -42,694 +76,319 @@ await createApp({
|
|
|
42
76
|
plugins: [
|
|
43
77
|
server(),
|
|
44
78
|
analytics(),
|
|
45
|
-
files(),
|
|
46
|
-
// Drop `lakebase()` in and `mastra` auto-enables per-agent thread
|
|
47
|
-
// storage (`schemaName: "mastra_<agentId>"`) plus a shared
|
|
48
|
-
// semantic-recall vector index. Skip it for a stateless agent.
|
|
49
79
|
lakebase(),
|
|
50
|
-
mastra({
|
|
80
|
+
plugin.mastra({
|
|
81
|
+
agents: { analyst },
|
|
82
|
+
defaultAgent: "analyst",
|
|
83
|
+
genie: { spaces: { sales: "01ef..." } },
|
|
84
|
+
}),
|
|
51
85
|
],
|
|
52
86
|
});
|
|
53
87
|
```
|
|
54
88
|
|
|
55
|
-
|
|
56
|
-
`tool` is the AppKit-shaped factory (`{ description, schema, execute }`)
|
|
57
|
-
that auto-adapts to Mastra's `createTool` under the hood.
|
|
58
|
-
|
|
59
|
-
Memory + storage cascades:
|
|
60
|
-
|
|
61
|
-
- **No `lakebase()` registered** ▸ agent is fully stateless. No threads,
|
|
62
|
-
no recall. Same as `mastra()` alone.
|
|
63
|
-
- `**lakebase()` registered, no `storage` / `memory` config** ▸ both
|
|
64
|
-
auto-turn on. Each agent gets its own `PostgresStore` schema; every
|
|
65
|
-
agent shares one `PgVector` recall index.
|
|
66
|
-
- **Per-agent opt-out** ▸ `createAgent({ ..., memory: false, storage: false })`
|
|
67
|
-
for routing / one-shot agents that don't need history.
|
|
68
|
-
- **Per-agent override** ▸ pass a `PgVectorConfig` / `PostgresStoreConfig` object on the agent for a private index or a shared external schema.
|
|
69
|
-
|
|
70
|
-
See [Memory + storage](#memory--storage) for the full cascade and worked
|
|
71
|
-
examples.
|
|
72
|
-
|
|
73
|
-
On the React side, never hardcode `/api/mastra/...`. Pull the published
|
|
74
|
-
paths from `usePluginClientConfig` and use the `chatUrl` helper. Import
|
|
75
|
-
them from the dependency-free `@dbx-tools/appkit-mastra-shared` package
|
|
76
|
-
so your browser bundle doesn't pull in `pg`, `fastembed`, or Mastra:
|
|
77
|
-
|
|
78
|
-
```tsx
|
|
79
|
-
import { usePluginClientConfig } from "@databricks/appkit-ui/react";
|
|
80
|
-
import { chatUrl, type MastraClientConfig } from "@dbx-tools/appkit-mastra-shared";
|
|
81
|
-
import { useChat } from "@ai-sdk/react";
|
|
82
|
-
import { DefaultChatTransport } from "ai";
|
|
83
|
-
import { useMemo } from "react";
|
|
84
|
-
|
|
85
|
-
function Chat() {
|
|
86
|
-
const config = usePluginClientConfig<MastraClientConfig>("mastra");
|
|
87
|
-
const transport = useMemo(
|
|
88
|
-
() => new DefaultChatTransport({ api: chatUrl(config) }),
|
|
89
|
-
[config],
|
|
90
|
-
);
|
|
91
|
-
const { messages, sendMessage } = useChat({ transport });
|
|
92
|
-
// ...
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
See [Client wiring](#client-wiring) for the full `MastraClientConfig`
|
|
97
|
-
shape and per-agent selection.
|
|
98
|
-
|
|
99
|
-
## Sensible defaults
|
|
100
|
-
|
|
101
|
-
The plugin is opinionated about what "no config" should mean. Everything
|
|
102
|
-
below can be overridden, but the bare path works:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
| Scenario | What you get |
|
|
106
|
-
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
107
|
-
| `mastra()` | One built-in `default` analyst agent, model from `/serving-endpoints`; memory + storage auto-on if the `lakebase` plugin is registered |
|
|
108
|
-
| `mastra({ agents: def })` | Single-agent shorthand - `def` is registered and marked as default |
|
|
109
|
-
| `mastra({ agents: [def1, def2] })` | Array shorthand - keys come from each `def.name` (or `agent_<i>`); first one is default |
|
|
110
|
-
| `mastra({ agents: { x: def, y: def }})` | Record - keys are the registered ids; first key is the default |
|
|
111
|
-
| No `defaultAgent` set | First registered agent wins |
|
|
112
|
-
| No `model` on an agent | Falls back to `config.defaultModel`, then `DATABRICKS_SERVING_ENDPOINT_NAME`, then walks `defaultModelFallbacks` (Thinking → Balanced → Fast tiers, Claude ↔ GPT ↔ Gemini interleaved within each, then open-weights) and picks the first endpoint actually present in the workspace |
|
|
113
|
-
| No `name` on a definition | Uses the registry key as `Agent.name` |
|
|
114
|
-
| No `tools` on an agent | Inherits plugin-level `config.tools` ambient set (if any) |
|
|
115
|
-
| No `storage` / `memory` and `lakebase()` registered | Both auto-default to `true`. Pass `false` (or a custom config object) on the plugin or an agent to opt out / override. |
|
|
116
|
-
| `storage` / `memory` on an agent | Cascades from plugin: storage namespaces **per-agent** (`schemaName: "mastra_<agentId>"`), vector recall is **shared** across agents on one `PgVector` singleton. |
|
|
117
|
-
|
|
89
|
+
Benefits of importing the package:
|
|
118
90
|
|
|
119
|
-
|
|
91
|
+
- `plugin.mastra()` registers a full AppKit plugin named `mastra`.
|
|
92
|
+
- `agents.createAgent()` keeps agent definitions typed and applies the default
|
|
93
|
+
Databricks workspace/skill mounts.
|
|
94
|
+
- `agents.tool()` lets the same AppKit-shaped tool body work in this Mastra
|
|
95
|
+
plugin.
|
|
96
|
+
- `genie.GENIE_INSTRUCTIONS` and `plugins.genie.toolkit()` give agents a
|
|
97
|
+
Databricks Genie workflow without embedding a second agent.
|
|
98
|
+
- Lakebase registration automatically enables durable thread storage and vector
|
|
99
|
+
memory unless you opt out.
|
|
120
100
|
|
|
101
|
+
## Agent Registration
|
|
121
102
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
| `name` | Display name. Defaults to the registry key. |
|
|
125
|
-
| `description` | Long-form description, surfaced as `Agent.description`. |
|
|
126
|
-
| `instructions` | System prompt body. Required. |
|
|
127
|
-
| `model` | Per-agent model override. String = `modelId` sugar; otherwise a Mastra `DynamicArgument`. |
|
|
128
|
-
| `tools` | Plain record OR `(plugins) => tools` callback (see below). |
|
|
129
|
-
| `memory` | `false`, `true`, or a `PgVectorConfig`. Cascades from `config.memory`. **Default: shared singleton `PgVector` across every agent** - object override switches to a dedicated index. |
|
|
130
|
-
| `storage` | `false`, `true`, or a `PostgresStoreConfig`. Cascades from `config.storage`. **Default: per-agent namespace** via `schemaName: "mastra_<agentId>"` so threads stay isolated. |
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Plugin-level fields:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
| Field | Description |
|
|
137
|
-
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
138
|
-
| `agents` | The registry. Omit for a built-in `default` analyst. |
|
|
139
|
-
| `defaultAgent` | Id that `chatRoute` binds to when no `:agentId` is supplied. Defaults to first registered. |
|
|
140
|
-
| `defaultModel` | Fallback for any agent that omits `model`. Same shape (string sugar or `DynamicArgument`). |
|
|
141
|
-
| `defaultModelFallbacks` | Priority-ordered list walked when no `model` / env / override is set. First entry whose endpoint exists in the workspace wins. Default chains the three `ModelTier`s (Thinking → Balanced → Fast); within each tier providers are interleaved Claude ↔ GPT ↔ Gemini with open-weights appended. Compose your own with `modelsForTier(ModelTier.Fast)` or read straight from `MODEL_CATALOG`. |
|
|
142
|
-
| `tools` | Ambient tools merged into every agent (per-agent tools win on collisions). |
|
|
143
|
-
| `storage` | `undefined` (default) auto-enables when `lakebase()` is registered; `true` does the same explicitly; `false` opts out; an object opens a dedicated `PostgresStore`. Per-agent default is `schemaName: "mastra_<agentId>"`. |
|
|
144
|
-
| `memory` | `undefined` (default) auto-enables when `lakebase()` is registered; `true` does the same explicitly; `false` opts out; an object opens a dedicated `PgVector`. Default behavior: one shared `PgVector` singleton across every agent. |
|
|
145
|
-
| `modelFuzzyMatch` | `false` to disable fuzzy snapping of model ids against the workspace's Model Serving catalogue. Defaults to `true`. |
|
|
146
|
-
| `modelFuzzyThreshold` | Fuse.js score threshold (`0` exact, `1` anything). Defaults to `0.4`. |
|
|
147
|
-
| `modelCacheTtlMs` | TTL for the cached endpoint list, per workspace host. Defaults to 5 minutes. Concurrent callers share one in-flight fetch. |
|
|
148
|
-
| `modelOverride` | `false` to disable per-request `X-Mastra-Model` / `?model=` / body overrides. Defaults to `true`. |
|
|
149
|
-
| `styleInstructions` | Style guardrails appended to every agent's `instructions` to curb LLM-isms (em dashes, emojis, sycophantic openers, throwaway closers). `undefined` (default) uses the built-in `DEFAULT_STYLE_INSTRUCTIONS`; a string replaces it; `false` disables. |
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
## The `tools(plugins)` callback
|
|
153
|
-
|
|
154
|
-
Each registered agent can supply either a static `tools: { ... }` record
|
|
155
|
-
or a `tools(plugins)` callback. The returned record accepts **any**
|
|
156
|
-
tool shape Mastra understands:
|
|
157
|
-
|
|
158
|
-
- Mastra tools built with `createTool` or `new Tool(...)`
|
|
159
|
-
- AppKit-shaped tools built with the `tool()` wrapper (below)
|
|
160
|
-
- Vercel AI SDK tools (`tool({...})` from `ai`)
|
|
161
|
-
- Provider-defined tools (e.g. `openai.tools.webSearch(...)`)
|
|
162
|
-
- Toolkits returned from `plugins.<name>.toolkit(...)`
|
|
103
|
+
`plugin.mastra({ agents })` accepts a single definition, an array, or a record.
|
|
104
|
+
Records are best when clients need stable agent ids:
|
|
163
105
|
|
|
164
106
|
```ts
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
get_weather: tool({
|
|
173
|
-
description: "Weather",
|
|
174
|
-
schema: z.object({ city: z.string() }),
|
|
175
|
-
execute: async ({ city }) => `Sunny in ${city}`,
|
|
176
|
-
}),
|
|
177
|
-
|
|
178
|
-
// Existing Mastra tool dropped in unchanged.
|
|
179
|
-
save_doc: existingMastraTool,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
`plugins` is a typed `Record<string, { toolkit(opts?): tools }>` matching
|
|
185
|
-
AppKit's `Plugins` type. It's backed by a runtime Proxy that
|
|
186
|
-
**auto-discovers any registered AppKit `ToolProvider` plugin** -
|
|
187
|
-
`analytics`, `files`, `lakebase`, `genie`, plus any third-party plugin
|
|
188
|
-
that implements the standard `getAgentTools()` + `executeAgentTool()` +
|
|
189
|
-
`toolkit()` interface. Tool calls dispatch through the plugin's
|
|
190
|
-
`executeAgentTool`, so OBO auth (`asUser`) and telemetry spans stay
|
|
191
|
-
intact.
|
|
192
|
-
|
|
193
|
-
`plugins.genie` is special-cased: it swaps the generic AppKit toolkit
|
|
194
|
-
(which only emits a single final result chunk per call) for a
|
|
195
|
-
streaming-aware tools record built on top of the plugin's
|
|
196
|
-
`exports().sendMessage` AsyncGenerator. Each Genie wire event
|
|
197
|
-
(`FETCHING_METADATA`, `ASKING_AI`, `EXECUTING_QUERY`, attached SQL,
|
|
198
|
-
errors) is normalised into a `GenieProgress` payload and pushed
|
|
199
|
-
mid-flight through Mastra's `ctx.writer`, surfacing as
|
|
200
|
-
`tool-output` chunks the React client can render as inline status
|
|
201
|
-
pills and SQL blocks while the LLM is still waiting on the final
|
|
202
|
-
`tool-result`. Tool ids are stable: `genie` for the default alias,
|
|
203
|
-
`genie-<alias>` for additional aliases, and one shared
|
|
204
|
-
`genie_get_conversation`.
|
|
205
|
-
|
|
206
|
-
Genie tool-result shape (LLM-bound):
|
|
207
|
-
|
|
208
|
-
```
|
|
209
|
-
{
|
|
210
|
-
conversationId?: string, // pass back to continue the same Genie thread
|
|
211
|
-
genieAnswer?: string, // Genie's prose answer; pass through verbatim
|
|
212
|
-
datasets?: [{ // metadata only, one per executed SQL statement
|
|
213
|
-
chartId: string, // embed [[chart:<chartId>]] to render inline
|
|
214
|
-
title?: string,
|
|
215
|
-
description?: string,
|
|
216
|
-
columns: string[],
|
|
217
|
-
rowCount: number,
|
|
218
|
-
sql?: string,
|
|
219
|
-
}],
|
|
220
|
-
suggestedFollowUps?: string[], // UI shows as buttons; don't list in reply
|
|
221
|
-
error?: string,
|
|
222
|
-
}
|
|
107
|
+
plugin.mastra({
|
|
108
|
+
agents: {
|
|
109
|
+
support: agents.createAgent({ instructions: "Answer support questions." }),
|
|
110
|
+
analyst: agents.createAgent({ instructions: "Analyze workspace data." }),
|
|
111
|
+
},
|
|
112
|
+
defaultAgent: "support",
|
|
113
|
+
});
|
|
223
114
|
```
|
|
224
115
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
regardless of dataset size). The model references each dataset by
|
|
229
|
-
its `chartId` via the `[[chart:<chartId>]]` marker to display the
|
|
230
|
-
chart inline; see the `render_data` section below for how the
|
|
231
|
-
client resolves those markers.
|
|
232
|
-
|
|
233
|
-
Genie data flow:
|
|
234
|
-
|
|
235
|
-
- The writer emits `kind: "started" | "status" | "sql" |
|
|
236
|
-
"suggested" | "error"` events for the live loading pill. SQL
|
|
237
|
-
text is shown via a small Shiki-highlighted block.
|
|
238
|
-
- The writer **also** emits two `kind: "chart"` events per
|
|
239
|
-
executed SQL statement, sharing the same `chartId`: the first
|
|
240
|
-
carries `{chartId, title, description?, data}` (rows converted
|
|
241
|
-
to objects keyed by column name with best-effort numeric
|
|
242
|
-
coercion); the second, when the chart-planner agent finishes,
|
|
243
|
-
carries `{chartId, option}` (the resolved Echarts spec). The
|
|
244
|
-
chat client's `<ChartSlot>` merges them by `chartId` and
|
|
245
|
-
renders inline at the matching `[[chart:<chartId>]]` marker.
|
|
246
|
-
This is the exact same wire format as the `render_data` tool,
|
|
247
|
-
so Genie and hand-built charts are indistinguishable on the
|
|
248
|
-
client.
|
|
249
|
-
- After a hard reload, `synthesizeToolEventsFromHistory` rebuilds
|
|
250
|
-
`suggested` events from the persisted tool-result; the SQL pill
|
|
251
|
-
and chart events are live-only and don't replay.
|
|
252
|
-
|
|
253
|
-
### `render_data` (system-default ambient tool)
|
|
254
|
-
|
|
255
|
-
`buildAgents` registers a system-level `render_data` tool on
|
|
256
|
-
every agent so the model can submit any tabular dataset for
|
|
257
|
-
inline charting. Users can shadow it by including a same-named
|
|
258
|
-
tool in `config.tools` or in a per-agent `tools` map; otherwise
|
|
259
|
-
it's just there.
|
|
260
|
-
|
|
261
|
-
The tool is generic - not coupled to Genie or any particular
|
|
262
|
-
upstream. Input is `{ title, description?, data: Row[] }` where
|
|
263
|
-
`data` is an array of objects keyed by column name (a SQL row
|
|
264
|
-
set, an API response, a hand-built array, etc.).
|
|
265
|
-
|
|
266
|
-
#### How it works (shared pipeline with Genie)
|
|
267
|
-
|
|
268
|
-
Both `render_data` and Genie's `drainGenieStream` route through
|
|
269
|
-
one helper, `emitChartWithPlanning`, so the wire format and
|
|
270
|
-
trace shape are consistent everywhere:
|
|
271
|
-
|
|
272
|
-
1. Mint a short `chartId` (8 hex chars).
|
|
273
|
-
2. Push a `{ kind: "chart", chartId, title, description?, data }`
|
|
274
|
-
event to `ctx.writer` immediately. The chat client mounts a
|
|
275
|
-
`<ChartSlot>` showing a "Rendering chart" skeleton.
|
|
276
|
-
3. Kick off the chart-planner agent
|
|
277
|
-
(`modelForTier(ModelTier.Fast)`) with the dataset in the
|
|
278
|
-
background. The agent's `structuredOutput` schema is a compact
|
|
279
|
-
"chart plan" (chart type + axis labels + categories +
|
|
280
|
-
series); the helper expands the plan into an Echarts
|
|
281
|
-
`EChartsOption` JSON.
|
|
282
|
-
4. When the planner resolves, push a follow-up
|
|
283
|
-
`{ kind: "chart", chartId, option }` event. The client's
|
|
284
|
-
`<ChartSlot>` merges it with the dataset event (last write
|
|
285
|
-
wins per field) and swaps in `<ReactECharts>`.
|
|
286
|
-
5. If the planner fails, no follow-up event fires. Once the
|
|
287
|
-
parent tool finishes, the slot transitions to a "couldn't
|
|
288
|
-
render chart" fallback frame.
|
|
289
|
-
|
|
290
|
-
The parent tool (`render_data` or Genie) `await`s the planner
|
|
291
|
-
promise(s) before its `execute` returns, so chart latency shows
|
|
292
|
-
up under the tool's trace span. The dataset event fires
|
|
293
|
-
**immediately**, though, so the calling LLM gets back the
|
|
294
|
-
`chartId` synchronously and the chat client has a layout slot
|
|
295
|
-
ready before the planner resolves.
|
|
296
|
-
|
|
297
|
-
The LLM-bound payload is just `{ chartId }` (for `render_data`)
|
|
298
|
-
or `datasets[]` metadata (for Genie); row data and option JSON
|
|
299
|
-
never reach the LLM, so token cost stays flat regardless of
|
|
300
|
-
dataset size.
|
|
301
|
-
|
|
302
|
-
#### Inline placement contract
|
|
303
|
-
|
|
304
|
-
The model embeds `[[chart:<chartId>]]` on its own line in its
|
|
305
|
-
markdown reply at the position where the chart should appear:
|
|
306
|
-
|
|
307
|
-
```markdown
|
|
308
|
-
## Audit Score
|
|
309
|
-
|
|
310
|
-
Audit Score is stable at ~94%, hovering between 93.5 and 95.0.
|
|
311
|
-
|
|
312
|
-
[[chart:a3f9c1d2]]
|
|
313
|
-
|
|
314
|
-
## Service Time
|
|
315
|
-
|
|
316
|
-
Service time is the outlier at 162.5s, up from a target of 150s.
|
|
317
|
-
|
|
318
|
-
[[chart:b7e2d4f1]]
|
|
319
|
-
```
|
|
116
|
+
When no agents are supplied, the plugin registers a built-in `default` analyst so
|
|
117
|
+
the route surface still works for smoke tests. Each agent is streamed through the
|
|
118
|
+
Mastra agent API mounted below the plugin path, typically `/api/mastra`.
|
|
320
119
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
a "Queueing chart" skeleton; a chart whose marker the model
|
|
325
|
-
forgot to place falls through to the end of the reply as a
|
|
326
|
-
fallback. All three states (queueing, rendering, rendered) share
|
|
327
|
-
the same fixed-height frame so the layout doesn't jump as
|
|
328
|
-
charts resolve.
|
|
329
|
-
|
|
330
|
-
#### Trade-offs
|
|
331
|
-
|
|
332
|
-
- Both events ride the writer, not the persisted tool-result, so
|
|
333
|
-
charts don't re-render after a hard reload. The model can call
|
|
334
|
-
`render_data` again (or re-ask Genie) on the next turn if the
|
|
335
|
-
user wants the chart back.
|
|
336
|
-
- The chart-planner is a separate model call per dataset (fast
|
|
337
|
-
tier, but still ~1-3s each). For an N-chart turn, latency is
|
|
338
|
-
`Genie + max(planners)` since the planners run concurrently
|
|
339
|
-
with the rest of Genie's stream and with each other.
|
|
340
|
-
|
|
341
|
-
Plugins that aren't registered (or don't implement the toolkit
|
|
342
|
-
interface) resolve to `undefined` at runtime, so guard with `?.` /
|
|
343
|
-
`?? {}` when a backing plugin is optional in some environments:
|
|
120
|
+
Use `agents.createTool` when you need Mastra-native fields such as
|
|
121
|
+
`outputSchema`, `suspendSchema`, `requireApproval`, or MCP metadata. Use
|
|
122
|
+
`agents.tool` for the smaller AppKit-compatible shape:
|
|
344
123
|
|
|
345
124
|
```ts
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
125
|
+
const approveRefund = agents.createTool({
|
|
126
|
+
id: "approve_refund",
|
|
127
|
+
description: "Approve a refund request.",
|
|
128
|
+
inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
|
|
129
|
+
requireApproval: true,
|
|
130
|
+
execute: async ({ context }) => approve(context.orderId, context.amount),
|
|
131
|
+
});
|
|
352
132
|
```
|
|
353
133
|
|
|
354
|
-
|
|
355
|
-
AppKit's own toolkits expose (passed through verbatim):
|
|
134
|
+
## AppKit Toolkits
|
|
356
135
|
|
|
357
|
-
|
|
358
|
-
-
|
|
359
|
-
|
|
360
|
-
- `rename?: Record<string, string>` - remap individual keys
|
|
361
|
-
|
|
362
|
-
### `tool()` vs `createTool()`
|
|
363
|
-
|
|
364
|
-
The `tool()` factory mirrors `@databricks/appkit/beta`'s shape so
|
|
365
|
-
sharing tool code between the AppKit `agents` plugin and this one is a
|
|
366
|
-
single-line import change:
|
|
136
|
+
The `tools(plugins)` callback receives a dynamic index of registered AppKit
|
|
137
|
+
tool-provider plugins. Each entry exposes `.toolkit(opts)` with AppKit-compatible
|
|
138
|
+
`prefix`, `only`, `except`, and `rename` options.
|
|
367
139
|
|
|
368
140
|
```ts
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
// Full Mastra `createTool` (id required, inputSchema, advanced fields).
|
|
379
|
-
const saveDoc = createTool({
|
|
380
|
-
id: "save-doc",
|
|
381
|
-
description: "Persist a document",
|
|
382
|
-
inputSchema: z.object({ key: z.string(), value: z.any() }),
|
|
383
|
-
outputSchema: z.object({ saved: z.boolean() }),
|
|
384
|
-
requireApproval: true,
|
|
385
|
-
execute: async (input, ctx) => {
|
|
386
|
-
await ctx.mastra?.getStorage()?.set(input.key, input.value);
|
|
387
|
-
return { saved: true };
|
|
141
|
+
const agent = agents.createAgent({
|
|
142
|
+
instructions: "Use the narrowest tool that answers the question.",
|
|
143
|
+
tools(plugins) {
|
|
144
|
+
return {
|
|
145
|
+
...plugins.analytics.toolkit({ only: ["query"] }),
|
|
146
|
+
...plugins.files?.toolkit({ prefix: "files.", except: ["delete"] }),
|
|
147
|
+
};
|
|
388
148
|
},
|
|
389
149
|
});
|
|
390
150
|
```
|
|
391
151
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
one.
|
|
396
|
-
|
|
397
|
-
Reach for `createTool` when you need Mastra-only fields (`outputSchema`,
|
|
398
|
-
`suspendSchema`, `requireApproval`, `mcp`, etc.).
|
|
399
|
-
|
|
400
|
-
## Model resolution
|
|
401
|
-
|
|
402
|
-
Each agent call resolves a `MastraModelConfig` lazily so concurrent
|
|
403
|
-
requests get distinct user identities. There are two paths through
|
|
404
|
-
the resolver depending on whether the caller asked for a specific
|
|
405
|
-
model.
|
|
406
|
-
|
|
407
|
-
### Explicit ask (override / agent / plugin / env)
|
|
408
|
-
|
|
409
|
-
When any of these is set the resolver fuzzy-matches that single id
|
|
410
|
-
against the live `/serving-endpoints` list, in priority order:
|
|
152
|
+
Tool calls dispatch back through the owning AppKit plugin, preserving OBO auth
|
|
153
|
+
and AppKit telemetry behavior. Optional plugins should be guarded with `?.` when
|
|
154
|
+
you spread their tools.
|
|
411
155
|
|
|
412
|
-
|
|
413
|
-
or `model` / `modelId` body field; see below)
|
|
414
|
-
2. Per-agent `def.model` (string sugar or `DynamicArgument`)
|
|
415
|
-
3. Plugin-level `config.defaultModel`
|
|
416
|
-
4. `DATABRICKS_SERVING_ENDPOINT_NAME` env var
|
|
156
|
+
## Memory And Storage
|
|
417
157
|
|
|
418
|
-
The
|
|
419
|
-
|
|
420
|
-
loose tokens like `"claude sonnet"` snap to
|
|
421
|
-
`databricks-claude-sonnet-4-6`, `"llama 70b"` to
|
|
422
|
-
`databricks-meta-llama-3-3-70b-instruct`, `"DBRX"` to
|
|
423
|
-
`databricks-dbrx-instruct`, and so on. If no candidate scores below
|
|
424
|
-
`modelFuzzyThreshold` (default `0.4`) the input is returned verbatim
|
|
425
|
-
and Databricks surfaces the canonical 404.
|
|
158
|
+
The `memory` and `storage` config fields can be `false`, `true`, or a concrete
|
|
159
|
+
Mastra Postgres/PgVector config.
|
|
426
160
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
variant is wired up.
|
|
435
|
-
|
|
436
|
-
The catalogue is grouped two ways:
|
|
437
|
-
|
|
438
|
-
- By **capability tier** via the `ModelTier` enum:
|
|
439
|
-
`ModelTier.Thinking` (deepest reasoning), `ModelTier.Balanced`
|
|
440
|
-
(cost/latency sweet spot), `ModelTier.Fast` (cheap & quick for
|
|
441
|
-
classification / routing / simple summarisation).
|
|
442
|
-
- By **provider** within each tier: `claude`, `gpt`, `gemini`,
|
|
443
|
-
`openSource`.
|
|
444
|
-
|
|
445
|
-
Both views live on `MODEL_CATALOG[tier][provider]`. The walked
|
|
446
|
-
`FALLBACK_MODEL_IDS` chains the three tiers in descending power
|
|
447
|
-
(Thinking → Balanced → Fast); within each tier providers are
|
|
448
|
-
round-robin-zipped (Claude ↔ GPT ↔ Gemini) before the open-weights
|
|
449
|
-
tail is appended as the universal floor.
|
|
161
|
+
```ts
|
|
162
|
+
plugin.mastra({
|
|
163
|
+
agents: analyst,
|
|
164
|
+
storage: true,
|
|
165
|
+
memory: { id: "analytics_memory", tableName: "agent_memory" },
|
|
166
|
+
});
|
|
167
|
+
```
|
|
450
168
|
|
|
169
|
+
With `lakebase()` registered, both default to enabled:
|
|
451
170
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
| `ModelTier.Fast` | Haiku 4.5 | 5.4 mini → 5.4 nano → 5 mini → 5 nano | 3.1 Flash Lite | GPT-OSS 20B, Gemma 3 12B, Llama 3.1 8B |
|
|
171
|
+
- storage uses a per-agent schema for durable threads and messages;
|
|
172
|
+
- memory uses a shared vector index for semantic recall;
|
|
173
|
+
- the service-principal pool is created outside any request so OBO user
|
|
174
|
+
identities are not captured in background storage work.
|
|
457
175
|
|
|
176
|
+
Without `lakebase()`, agents are stateless unless you provide explicit storage
|
|
177
|
+
and memory configs.
|
|
458
178
|
|
|
459
|
-
|
|
179
|
+
## Workspace Skills
|
|
460
180
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
181
|
+
Every `agents.createAgent()` gets a default Mastra `Workspace` from
|
|
182
|
+
`workspaces.createWorkspace()`. It mounts Databricks Workspace files through the
|
|
183
|
+
current OBO user's `WorkspaceClient`, so Mastra can discover Assistant-style
|
|
184
|
+
`SKILL.md` files at request time.
|
|
464
185
|
|
|
465
186
|
```ts
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
187
|
+
const agent = agents.createAgent({
|
|
188
|
+
instructions: "Use mounted workspace skills when relevant.",
|
|
189
|
+
workspace: workspaces.createWorkspace({
|
|
190
|
+
assistantSkills: true,
|
|
191
|
+
mounts: [
|
|
192
|
+
async () => ({
|
|
193
|
+
mounts: { "/reference": myFilesystem },
|
|
194
|
+
skillPaths: ["/reference/skills"],
|
|
195
|
+
}),
|
|
196
|
+
],
|
|
197
|
+
}),
|
|
476
198
|
});
|
|
477
199
|
```
|
|
478
200
|
|
|
479
|
-
|
|
201
|
+
Production workspace mounts require a forwarded token with `workspace`,
|
|
202
|
+
`workspace.workspace`, or `all-apis` scope. Development mode skips that gate for
|
|
203
|
+
local iteration.
|
|
480
204
|
|
|
481
|
-
|
|
482
|
-
pass it to `defaultModelFallbacks` to scope the auto-resolver:
|
|
205
|
+
## Genie Tools
|
|
483
206
|
|
|
484
|
-
|
|
485
|
-
import { mastra, ModelTier, modelsForTier } from "@dbx-tools/appkit-mastra";
|
|
207
|
+
`genie.buildGenieTools()` and `plugins.genie.toolkit()` expose tools for:
|
|
486
208
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
#### Pin a custom approved subset
|
|
209
|
+
- asking a configured Genie space;
|
|
210
|
+
- reading space descriptions and serialized space metadata;
|
|
211
|
+
- fetching statement rows by `statement_id`;
|
|
212
|
+
- preparing charts from Genie result sets.
|
|
494
213
|
|
|
495
|
-
|
|
496
|
-
|
|
214
|
+
The central agent drives those tools directly. Genie events stream through the
|
|
215
|
+
Mastra writer using the shared contract from
|
|
216
|
+
[`@dbx-tools/shared-mastra`](../../shared/mastra), so clients can show thinking,
|
|
217
|
+
SQL, row counts, summaries, chart markers, and data markers as the turn runs.
|
|
497
218
|
|
|
498
219
|
```ts
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
"
|
|
503
|
-
|
|
220
|
+
const agent = agents.createAgent({
|
|
221
|
+
instructions: `${baseInstructions}\n\n${genie.GENIE_INSTRUCTIONS}`,
|
|
222
|
+
tools(plugins) {
|
|
223
|
+
return { ...plugins.genie?.toolkit({ prefix: "" }) };
|
|
224
|
+
},
|
|
504
225
|
});
|
|
505
226
|
```
|
|
506
227
|
|
|
507
|
-
|
|
508
|
-
returned and Databricks surfaces the canonical error.
|
|
509
|
-
|
|
510
|
-
The endpoint list is cached per workspace host through AppKit's
|
|
511
|
-
built-in `CacheManager` (`CacheManager.getInstanceSync().getOrExecute`),
|
|
512
|
-
which is the TypeScript counterpart of Python's `cachetools.TTLCache`
|
|
513
|
-
plus `cachetools-async` rolled into one: per-entry TTL (default 5
|
|
514
|
-
minutes via `modelCacheTtlMs`), bounded size, in-flight request
|
|
515
|
-
coalescing (the manager's internal `inFlightRequests` map shares one
|
|
516
|
-
fetch across every concurrent caller), telemetry spans, and optional
|
|
517
|
-
Lakebase persistence when the `lakebase` plugin is wired up. No extra
|
|
518
|
-
dependency lives in this package; the catalogue piggybacks on whatever
|
|
519
|
-
storage backend AppKit picked at boot.
|
|
520
|
-
|
|
521
|
-
String values (`"databricks-claude-sonnet-4-6"`) are `modelId` sugar
|
|
522
|
-
layered on top of the auto-resolver - workspace URL, provider, and OBO
|
|
523
|
-
auth stay default. Pass a `DynamicArgument<MastraModelConfig>` on
|
|
524
|
-
`def.model` / `config.defaultModel` when you need full control over
|
|
525
|
-
auth, provider, or URL; that path bypasses the fuzzy matcher and
|
|
526
|
-
per-request override.
|
|
527
|
-
|
|
528
|
-
### `GET /api/mastra/models`
|
|
529
|
-
|
|
530
|
-
The plugin exposes the cached endpoint catalogue at `/models` (mounted
|
|
531
|
-
under the plugin prefix, default `/api/mastra`) so clients can populate
|
|
532
|
-
model pickers and validate `?model=` choices without a separate
|
|
533
|
-
Databricks SDK round-trip:
|
|
534
|
-
|
|
535
|
-
```bash
|
|
536
|
-
curl -s http://localhost:8000/api/mastra/models | jq
|
|
537
|
-
# {
|
|
538
|
-
# "endpoints": [
|
|
539
|
-
# { "name": "databricks-claude-sonnet-4-6", "task": "llm/v1/chat", "state": "READY", ... },
|
|
540
|
-
# { "name": "databricks-meta-llama-3-3-70b-instruct", ... },
|
|
541
|
-
# ...
|
|
542
|
-
# ]
|
|
543
|
-
# }
|
|
544
|
-
```
|
|
228
|
+
## Charts And Data Embeds
|
|
545
229
|
|
|
546
|
-
|
|
230
|
+
`chart.prepareChart()` mints a chart id immediately, caches an in-progress
|
|
231
|
+
record, resolves the data in the background, and stores a terminal chart or
|
|
232
|
+
error. `chart.fetchChart()` long-polls that cache for route handlers and custom
|
|
233
|
+
clients.
|
|
547
234
|
|
|
548
235
|
```ts
|
|
549
|
-
|
|
550
|
-
|
|
236
|
+
const { chartId } = await chart.prepareChart({
|
|
237
|
+
config,
|
|
238
|
+
request: {
|
|
239
|
+
title: "Revenue by region",
|
|
240
|
+
chartType: "bar",
|
|
241
|
+
instructions: "Compare total revenue by region.",
|
|
242
|
+
data: rows,
|
|
243
|
+
},
|
|
244
|
+
resolveData: async () => rows,
|
|
245
|
+
});
|
|
551
246
|
|
|
552
|
-
const
|
|
553
|
-
const endpoints = await m.asUser(req).listModels(); // user-scoped
|
|
554
|
-
m.clearModelsCache(); // force the next call to re-fetch
|
|
247
|
+
const resolved = await chart.fetchChart(chartId);
|
|
555
248
|
```
|
|
556
249
|
|
|
557
|
-
|
|
250
|
+
Agents can return `[chart:<id>]` and `[data:<statement_id>]` markers in prose.
|
|
251
|
+
The embed route resolves them later, which avoids forcing the language model to
|
|
252
|
+
inline large tables or wait for chart planning before continuing its answer.
|
|
558
253
|
|
|
559
|
-
|
|
560
|
-
redeploying. Sources, checked in priority order:
|
|
254
|
+
## Model Selection
|
|
561
255
|
|
|
256
|
+
`model.buildModel()` adapts the generic resolver from
|
|
257
|
+
[`@dbx-tools/node-model`](../model) to Mastra. It resolves the model per request,
|
|
258
|
+
so OBO identity and request-specific overrides stay isolated.
|
|
562
259
|
|
|
563
|
-
|
|
564
|
-
| ------------------------- | ------------------------------------------------ |
|
|
565
|
-
| `X-Mastra-Model` header | `curl -H 'X-Mastra-Model: claude-haiku' ...` |
|
|
566
|
-
| `?model=` query parameter | `POST /api/mastra/route/chat?model=llama-70b` |
|
|
567
|
-
| Body `model` or `modelId` | `{ "messages": [...], "model": "claude-haiku" }` |
|
|
260
|
+
Model priority is:
|
|
568
261
|
|
|
262
|
+
1. request override (`X-Mastra-Model`, `?model=`, body `model` / `modelId`);
|
|
263
|
+
2. per-agent `model`;
|
|
264
|
+
3. plugin `defaultModel`;
|
|
265
|
+
4. `DATABRICKS_SERVING_ENDPOINT_NAME`;
|
|
266
|
+
5. workspace catalogue ranking and static fallback floor.
|
|
569
267
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
268
|
+
```ts
|
|
269
|
+
plugin.mastra({
|
|
270
|
+
agents: analyst,
|
|
271
|
+
defaultModel: "claude sonnet",
|
|
272
|
+
modelFuzzyMatch: true,
|
|
273
|
+
modelOverride: true,
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Use `serving.extractModelOverride()` and `serving.resolveServingConfig()` when
|
|
278
|
+
building custom routes that should behave like the plugin's `/models` and stream
|
|
279
|
+
routes.
|
|
576
280
|
|
|
577
|
-
##
|
|
281
|
+
## Threads, History, And Suggestions
|
|
578
282
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
`lakebase()` already gets you per-agent threads + shared semantic recall;
|
|
582
|
-
zero extra config required.
|
|
283
|
+
When storage is enabled, the plugin provides route helpers and in-process
|
|
284
|
+
functions for conversation management:
|
|
583
285
|
|
|
286
|
+
- `history.loadHistory()` and `history.clearHistory()` read or clear one thread;
|
|
287
|
+
- `threads.listThreads()`, `threads.renameThread()`, and
|
|
288
|
+
`threads.deleteThread()` operate on the caller's scoped conversations;
|
|
289
|
+
- `genie.collectSpaceSuggestions()` reads starter questions from the configured
|
|
290
|
+
Genie space.
|
|
584
291
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
| `memory` | **Shared singleton** `PgVector` across every agent (cross-agent semantic recall on one index). | RAG-style recall over past messages via FastEmbed vectors. |
|
|
292
|
+
The plugin resolves the active thread from `x-mastra-thread-id`, `?threadId=`,
|
|
293
|
+
or a per-session fallback cookie. That keeps streaming, history, and clear
|
|
294
|
+
operations aligned around the same conversation id.
|
|
589
295
|
|
|
296
|
+
## Feedback And Observability
|
|
590
297
|
|
|
591
|
-
|
|
592
|
-
|
|
298
|
+
`observability.buildObservability()` wires Mastra tracing when OTLP export is
|
|
299
|
+
configured. `mlflow.resolveFeedbackEnabled()` turns MLflow feedback on when both
|
|
300
|
+
trace export and an MLflow experiment are configured, unless the plugin config
|
|
301
|
+
forces a value.
|
|
593
302
|
|
|
594
303
|
```ts
|
|
595
|
-
mastra({
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
304
|
+
plugin.mastra({
|
|
305
|
+
agents: analyst,
|
|
306
|
+
feedback: true,
|
|
307
|
+
});
|
|
308
|
+
```
|
|
600
309
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
// No overrides: inherits the auto-on defaults above.
|
|
605
|
-
// - threads stored under schema "mastra_analyst"
|
|
606
|
-
// - recalls from the shared vector index
|
|
607
|
-
}),
|
|
310
|
+
`mlflow.logFeedback()` logs a human assessment against the active MLflow trace.
|
|
311
|
+
The response header name and request/response schemas live in
|
|
312
|
+
[`@dbx-tools/shared-mastra`](../../shared/mastra).
|
|
608
313
|
|
|
609
|
-
|
|
610
|
-
instructions: "Stateless routing agent.",
|
|
611
|
-
// Opt out of both for a fully stateless agent.
|
|
612
|
-
storage: false,
|
|
613
|
-
memory: false,
|
|
614
|
-
}),
|
|
314
|
+
## MCP Exposure
|
|
615
315
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
// Private vector index so legal's recall doesn't bleed into
|
|
619
|
-
// analyst's. Threads still get their own per-agent schema.
|
|
620
|
-
memory: { connectionString: process.env.LEGAL_PG_URL!, /* ... */ },
|
|
621
|
-
}),
|
|
316
|
+
`mcp.buildMcpServer()` exposes registered agents as MCP tools by default. The
|
|
317
|
+
AppKit plugin publishes clean aliases under its base path:
|
|
622
318
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
}),
|
|
319
|
+
```ts
|
|
320
|
+
plugin.mastra({
|
|
321
|
+
agents: analyst,
|
|
322
|
+
mcp: {
|
|
323
|
+
serverId: "analytics",
|
|
324
|
+
name: "Analytics MCP",
|
|
325
|
+
tools: false,
|
|
631
326
|
},
|
|
632
327
|
});
|
|
633
328
|
```
|
|
634
329
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
`
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
<>
|
|
701
|
-
<select onChange={(e) => setSelected(e.target.value)}>
|
|
702
|
-
{config.agents.map((id) => (
|
|
703
|
-
<option key={id} value={id}>
|
|
704
|
-
{id}
|
|
705
|
-
</option>
|
|
706
|
-
))}
|
|
707
|
-
</select>
|
|
708
|
-
{/* render messages, etc. */}
|
|
709
|
-
</>
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
`MastraClientConfig` fields (all derived from the server-side plugin
|
|
715
|
-
mount, so a custom `mastra({ name: "myMastra" })` rewrites every path):
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
| Field | Example | Description |
|
|
719
|
-
| ------------------ | ----------------------------------- | -------------------------------------------------------- |
|
|
720
|
-
| `basePath` | `"/api/mastra"` | Plugin mount path. |
|
|
721
|
-
| `chatPath` | `"/api/mastra/route/chat"` | Default-agent chat URL. Use `chatUrl(config)` to get it. |
|
|
722
|
-
| `chatPathTemplate` | `"/api/mastra/route/chat/:agentId"` | OpenAPI-style template for tools / docs. |
|
|
723
|
-
| `modelsPath` | `"/api/mastra/models"` | `GET` cached endpoint catalogue. |
|
|
724
|
-
| `defaultAgent` | `"analyst"` | Agent id `chatRoute` binds to when none is supplied. |
|
|
725
|
-
| `agents` | `["analyst", "helper"]` | Every registered agent id in order. |
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
`chatUrl(config, agentId?)` returns `config.chatPath` for the default
|
|
729
|
-
agent (the registered `chatRoute` mount that omits `:agentId`), and
|
|
730
|
-
`${config.chatPath}/${encodeURIComponent(agentId)}` otherwise. Pure
|
|
731
|
-
function: no React, no hooks, safe in service workers and SSR.
|
|
732
|
-
|
|
733
|
-
## License
|
|
734
|
-
|
|
735
|
-
Apache-2.0
|
|
330
|
+
Use `mcp: false` to disable MCP. Turn on `tools: true` only for ambient tools
|
|
331
|
+
that are safe outside an in-process chat turn.
|
|
332
|
+
|
|
333
|
+
## API Gate
|
|
334
|
+
|
|
335
|
+
The stock `@mastra/express` app has broad management routes. The plugin's
|
|
336
|
+
default `apiAccess: "scoped"` allows only the chat, read-only metadata,
|
|
337
|
+
plugin-owned `/route/*`, embed, model, suggestion, and MCP surfaces that the
|
|
338
|
+
client needs. Use `apiAccess: "full"` only for a trusted first-party console.
|
|
339
|
+
|
|
340
|
+
`server.isMastraRequestAllowed()` is exported for tests and custom dispatch
|
|
341
|
+
logic that need the same allowlist.
|
|
342
|
+
|
|
343
|
+
## Configuration Reference
|
|
344
|
+
|
|
345
|
+
The plugin config is intentionally centered on the AppKit lifecycle instead of
|
|
346
|
+
requiring callers to assemble a Mastra server by hand.
|
|
347
|
+
|
|
348
|
+
- `agents` registers a single agent, an array, or a record keyed by stable agent
|
|
349
|
+
ids. Records are best for UIs because the ids become route-visible.
|
|
350
|
+
- `defaultAgent` controls which registered agent handles requests that do not
|
|
351
|
+
name an agent explicitly.
|
|
352
|
+
- `storage` and `memory` accept `true`, `false`, or concrete Mastra Postgres /
|
|
353
|
+
PgVector options. `true` resolves from `lakebase()` when present.
|
|
354
|
+
- `genie.spaces` maps aliases to Genie Space IDs. Those aliases flow into tools,
|
|
355
|
+
suggestions, and chart/data workflows.
|
|
356
|
+
- `defaultModel`, `modelOverride`, and `modelFuzzyMatch` control how loose model
|
|
357
|
+
names are resolved through Databricks Model Serving.
|
|
358
|
+
- `feedback` controls whether MLflow feedback routes are exposed. The automatic
|
|
359
|
+
mode enables feedback when tracing and an MLflow experiment are configured.
|
|
360
|
+
- `mcp` controls whether agents are exposed as MCP tools and how that server is
|
|
361
|
+
named.
|
|
362
|
+
- `apiAccess` chooses the route allowlist. Keep the default scoped mode for
|
|
363
|
+
deployed apps.
|
|
364
|
+
|
|
365
|
+
Use this package when you want an AppKit-native agent runtime. Use the shared
|
|
366
|
+
schemas in [`@dbx-tools/shared-mastra`](../../shared/mastra) when building a
|
|
367
|
+
client that talks to these routes.
|
|
368
|
+
|
|
369
|
+
## Modules
|
|
370
|
+
|
|
371
|
+
- `plugin` - `MastraPlugin` and `mastra()` AppKit plugin factory.
|
|
372
|
+
- `agents` - `createAgent`, `tool`, `createTool`, agent build helpers, fallback
|
|
373
|
+
defaults, and approval-gated tool inspection.
|
|
374
|
+
- `config` - plugin config types and RequestContext key constants.
|
|
375
|
+
- `model` / `serving` / `servingSanitize` - Mastra model config, request
|
|
376
|
+
overrides, serving-endpoint config, and request-body cleanup.
|
|
377
|
+
- `genie` - Genie prompt, space normalization, Genie toolkits, and suggestions.
|
|
378
|
+
- `chart` / `statement` / `writer` - chart cache, statement row fetches, and
|
|
379
|
+
safe writer events.
|
|
380
|
+
- `history` / `threads` / `pagination` - conversation persistence helpers and
|
|
381
|
+
route handlers.
|
|
382
|
+
- `memory` / `storageSchema` - Lakebase-backed Mastra store/vector setup.
|
|
383
|
+
- `workspaces` / `filesystems` - Mastra workspace creation and Databricks
|
|
384
|
+
Workspace file adapters.
|
|
385
|
+
- `mcp` - MCP server construction.
|
|
386
|
+
- `observability` / `mlflow` - tracing and feedback.
|
|
387
|
+
- `server` / `rest` / `processors` - Express dispatch, Databricks REST helpers,
|
|
388
|
+
stream/result processors.
|
|
389
|
+
|
|
390
|
+
Browser-facing wire types are in
|
|
391
|
+
[`@dbx-tools/shared-mastra`](../../shared/mastra). Genie event contracts are in
|
|
392
|
+
[`@dbx-tools/shared-genie`](../../shared/genie). Model request/result contracts
|
|
393
|
+
are in [`@dbx-tools/shared-model`](../../shared/model). The matching React chat
|
|
394
|
+
surface is [`@dbx-tools/ui-mastra`](../../ui/mastra).
|