@dbx-tools/appkit-mastra 0.1.112 → 0.3.1
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 +309 -344
- package/index.ts +46 -0
- package/package.json +50 -28
- package/src/agents.ts +858 -0
- package/src/chart.ts +695 -0
- package/src/config.ts +443 -0
- package/src/filesystems.ts +1090 -0
- package/src/genie.ts +1091 -0
- package/src/history.ts +297 -0
- package/src/mcp.ts +105 -0
- package/src/memory.ts +300 -0
- package/src/mlflow.ts +149 -0
- package/src/model.ts +163 -0
- package/src/observability.ts +144 -0
- package/src/pagination.ts +34 -0
- package/src/plugin.ts +804 -0
- package/src/processors.ts +168 -0
- package/src/rest.ts +67 -0
- package/src/server.ts +343 -0
- package/src/serving-sanitize.ts +167 -0
- package/src/serving.ts +97 -0
- 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 -1676
- package/dist/index.js +0 -5337
package/README.md
CHANGED
|
@@ -1,44 +1,70 @@
|
|
|
1
1
|
# @dbx-tools/appkit-mastra
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Databricks App
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
and
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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/model`](../model), instead of binding every agent to a fixed
|
|
48
|
+
endpoint name.
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
28
51
|
|
|
29
52
|
```ts
|
|
30
|
-
import { analytics, createApp,
|
|
31
|
-
import {
|
|
53
|
+
import { analytics, createApp, lakebase, server } from "@databricks/appkit";
|
|
54
|
+
import { agents, genie, plugin } from "@dbx-tools/appkit-mastra";
|
|
32
55
|
import { z } from "zod";
|
|
33
56
|
|
|
34
|
-
const
|
|
35
|
-
|
|
57
|
+
const analyst = agents.createAgent({
|
|
58
|
+
name: "analyst",
|
|
59
|
+
instructions: ["You answer questions about workspace data.", genie.GENIE_INSTRUCTIONS].join(
|
|
60
|
+
"\n\n",
|
|
61
|
+
),
|
|
36
62
|
tools(plugins) {
|
|
37
63
|
return {
|
|
38
|
-
...plugins.analytics.toolkit(),
|
|
39
|
-
...plugins.
|
|
40
|
-
get_weather: tool({
|
|
41
|
-
description: "
|
|
64
|
+
...plugins.analytics.toolkit(),
|
|
65
|
+
...plugins.genie?.toolkit(),
|
|
66
|
+
get_weather: agents.tool({
|
|
67
|
+
description: "Get a simple weather report.",
|
|
42
68
|
schema: z.object({ city: z.string() }),
|
|
43
69
|
execute: async ({ city }) => `Sunny in ${city}`,
|
|
44
70
|
}),
|
|
@@ -50,380 +76,319 @@ await createApp({
|
|
|
50
76
|
plugins: [
|
|
51
77
|
server(),
|
|
52
78
|
analytics(),
|
|
53
|
-
files(),
|
|
54
|
-
// Drop `lakebase()` in and `mastra` auto-enables per-agent thread
|
|
55
|
-
// storage plus shared semantic recall. Skip it for a stateless agent.
|
|
56
79
|
lakebase(),
|
|
57
|
-
mastra({
|
|
80
|
+
plugin.mastra({
|
|
81
|
+
agents: { analyst },
|
|
82
|
+
defaultAgent: "analyst",
|
|
83
|
+
genie: { spaces: { sales: "01ef..." } },
|
|
84
|
+
}),
|
|
58
85
|
],
|
|
59
86
|
});
|
|
60
87
|
```
|
|
61
88
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
Benefits of importing the package:
|
|
90
|
+
|
|
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.
|
|
67
100
|
|
|
68
|
-
|
|
69
|
-
`[@dbx-tools/appkit-mastra-ui](../appkit-mastra-ui)`; it wires itself
|
|
70
|
-
from the plugin's published client config and streams over
|
|
71
|
-
`@mastra/client-js`, so there's no transport code to write:
|
|
101
|
+
## Agent Registration
|
|
72
102
|
|
|
73
|
-
|
|
74
|
-
|
|
103
|
+
`plugin.mastra({ agents })` accepts a single definition, an array, or a record.
|
|
104
|
+
Records are best when clients need stable agent ids:
|
|
75
105
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
106
|
+
```ts
|
|
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
|
+
});
|
|
79
114
|
```
|
|
80
115
|
|
|
81
|
-
|
|
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`.
|
|
82
119
|
|
|
83
|
-
|
|
84
|
-
`
|
|
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:
|
|
85
123
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
```ts
|
|
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
|
+
});
|
|
132
|
+
```
|
|
91
133
|
|
|
92
|
-
|
|
93
|
-
`false` (opt out), `true` (explicit on), or a config object (dedicated
|
|
94
|
-
index / schema). The fields are typed in `[config.ts](src/config.ts)`;
|
|
95
|
-
the wiring is in `[memory.ts](src/memory.ts)`.
|
|
134
|
+
## AppKit Toolkits
|
|
96
135
|
|
|
97
|
-
|
|
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.
|
|
98
139
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
140
|
+
```ts
|
|
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
|
+
};
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
```
|
|
103
151
|
|
|
104
|
-
|
|
105
|
-
|
|
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.
|
|
106
155
|
|
|
107
|
-
|
|
108
|
-
| ---------------------------------- | ------------------------ |
|
|
109
|
-
| `/Workspace/.assistant/skills` | `/workspace_skills` |
|
|
110
|
-
| `/Users/<email>/.assistant/skills` | `/workspace_user_skills` |
|
|
156
|
+
## Memory And Storage
|
|
111
157
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`MASTRA_SCOPES_KEY` using `tokenUtils` from `@dbx-tools/shared`.
|
|
115
|
-
`NODE_ENV=development` skips the scope gate.
|
|
158
|
+
The `memory` and `storage` config fields can be `false`, `true`, or a concrete
|
|
159
|
+
Mastra Postgres/PgVector config.
|
|
116
160
|
|
|
117
161
|
```ts
|
|
118
|
-
|
|
162
|
+
plugin.mastra({
|
|
163
|
+
agents: analyst,
|
|
164
|
+
storage: true,
|
|
165
|
+
memory: { id: "analytics_memory", tableName: "agent_memory" },
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
With `lakebase()` registered, both default to enabled:
|
|
170
|
+
|
|
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.
|
|
175
|
+
|
|
176
|
+
Without `lakebase()`, agents are stateless unless you provide explicit storage
|
|
177
|
+
and memory configs.
|
|
178
|
+
|
|
179
|
+
## Workspace Skills
|
|
119
180
|
|
|
120
|
-
|
|
121
|
-
|
|
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.
|
|
122
185
|
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
instructions: "
|
|
126
|
-
workspace: createWorkspace({
|
|
186
|
+
```ts
|
|
187
|
+
const agent = agents.createAgent({
|
|
188
|
+
instructions: "Use mounted workspace skills when relevant.",
|
|
189
|
+
workspace: workspaces.createWorkspace({
|
|
190
|
+
assistantSkills: true,
|
|
127
191
|
mounts: [
|
|
128
192
|
async () => ({
|
|
129
|
-
mounts: { "/
|
|
130
|
-
skillPaths: [],
|
|
193
|
+
mounts: { "/reference": myFilesystem },
|
|
194
|
+
skillPaths: ["/reference/skills"],
|
|
131
195
|
}),
|
|
132
196
|
],
|
|
133
197
|
}),
|
|
134
198
|
});
|
|
135
|
-
|
|
136
|
-
// Disable built-in skill mounts.
|
|
137
|
-
const bare = createAgent({
|
|
138
|
-
instructions: "...",
|
|
139
|
-
workspace: createWorkspace({ assistantSkills: false }),
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Exported for direct use: `createWorkspace`, `DatabricksWorkspaceFilesystem`,
|
|
144
|
-
`emptyFilesystem`, and path helpers (`normalizeDatabricksBasePath`,
|
|
145
|
-
`isDbfsPath`, `resolveDatabricksAbsolutePath`, ...).
|
|
146
|
-
|
|
147
|
-
## Conversations (threads)
|
|
148
|
-
|
|
149
|
-
When storage is on, a user owns many conversation threads. The plugin
|
|
150
|
-
resolves the thread a request targets from `RequestContext`, in order:
|
|
151
|
-
|
|
152
|
-
1. A **client-selected thread id** - the thread-selection header
|
|
153
|
-
(`x-mastra-thread-id`) or `?threadId=` query. This is how the chat UI
|
|
154
|
-
references a specific conversation: it picks an id from the threads
|
|
155
|
-
listing (or mints one for a new chat) and stamps it on every call, so
|
|
156
|
-
streaming, history, and clear all target that thread.
|
|
157
|
-
2. The **per-session cookie** (`appkit_<plugin-name>_session_id`), minted
|
|
158
|
-
on first contact, as the single-thread fallback for clients that
|
|
159
|
-
don't manage threads explicitly.
|
|
160
|
-
|
|
161
|
-
The thread id pins both `agent.stream()` persistence and the history /
|
|
162
|
-
threads routes, so client and server can never disagree on which
|
|
163
|
-
conversation is active. Two custom routes back the UI's conversation
|
|
164
|
-
management (registered alongside `/route/history`):
|
|
165
|
-
|
|
166
|
-
- `GET /route/threads` (`/route/threads/:agentId`) - one page of the
|
|
167
|
-
caller's threads, newest first, **scoped to the caller's resource** so
|
|
168
|
-
a user only ever sees their own conversations.
|
|
169
|
-
- `DELETE /route/threads` - delete the targeted thread (id from the
|
|
170
|
-
thread-selection header). Ownership is enforced server-side: a thread
|
|
171
|
-
is only removed when it belongs to the caller.
|
|
172
|
-
- `PATCH /route/threads` - rename the targeted thread (id from the
|
|
173
|
-
thread-selection header) to the `{ title }` in the JSON body. Same
|
|
174
|
-
ownership enforcement; existing thread metadata is preserved.
|
|
175
|
-
|
|
176
|
-
Each thread is auto-titled from its opening turn (Mastra's
|
|
177
|
-
`generateTitle`), but titling runs on the **small / fast chat tier**
|
|
178
|
-
(`ModelClass.ChatFast`) via the dedicated summarizer in
|
|
179
|
-
`[summarize.ts](src/summarize.ts)` - not the agent's primary model - so
|
|
180
|
-
naming a conversation never spends the heavyweight model. The route
|
|
181
|
-
handlers live in `[threads.ts](src/threads.ts)`; the thread-id
|
|
182
|
-
resolution is in `[server.ts](src/server.ts)`. The drop-in `MastraChat`
|
|
183
|
-
from `@dbx-tools/appkit-mastra-ui` renders the whole conversation sidebar
|
|
184
|
-
over these routes with no extra wiring.
|
|
185
|
-
|
|
186
|
-
### Summarization (`summarize` tool)
|
|
187
|
-
|
|
188
|
-
Every agent gets a system-default ambient `summarize` tool that condenses
|
|
189
|
-
arbitrary text (long content, notes, transcripts, bulky tool results) on
|
|
190
|
-
the small / fast chat tier instead of the agent's primary model. It takes
|
|
191
|
-
`text` plus optional `instructions` (e.g. "one sentence", "list the action
|
|
192
|
-
items") and a `maxWords` soft cap, and returns `{ summary }`. The same
|
|
193
|
-
small-tier summarizer backs conversation titling above. Shadow it by
|
|
194
|
-
defining a same-named tool in `config.tools` (or a per-agent `tools`), and
|
|
195
|
-
reuse it programmatically via the exported `summarizeText(config, text, opts)` / `buildSummarizeTool(config)`.
|
|
196
|
-
|
|
197
|
-
## The `tools(plugins)` callback
|
|
198
|
-
|
|
199
|
-
Each agent supplies either a static `tools: { ... }` record or a
|
|
200
|
-
`tools(plugins)` callback. The returned record accepts any tool shape
|
|
201
|
-
Mastra understands - Mastra `createTool` tools, AppKit-shaped `tool()`
|
|
202
|
-
tools, Vercel AI SDK tools, provider-defined tools, and toolkits from
|
|
203
|
-
`plugins.<name>.toolkit(...)`.
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
tools(plugins) {
|
|
207
|
-
return {
|
|
208
|
-
...plugins.analytics.toolkit(), // every analytics tool
|
|
209
|
-
...plugins.files.toolkit({ only: ["uploads.read"] }), // filtered subset
|
|
210
|
-
get_weather: tool({
|
|
211
|
-
description: "Weather",
|
|
212
|
-
schema: z.object({ city: z.string() }),
|
|
213
|
-
execute: async ({ city }) => `Sunny in ${city}`,
|
|
214
|
-
}),
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
199
|
```
|
|
218
200
|
|
|
219
|
-
|
|
220
|
-
`
|
|
221
|
-
|
|
222
|
-
Tool calls dispatch through the plugin's `executeAgentTool`, so OBO auth
|
|
223
|
-
and telemetry spans stay intact. Plugins that aren't registered resolve
|
|
224
|
-
to `undefined`, so guard optional backings with `?.` / `?? {}`.
|
|
225
|
-
|
|
226
|
-
`toolkit(opts)` takes the same `ToolkitOptions` AppKit exposes
|
|
227
|
-
(`prefix`, `only`, `except`, `rename`), passed through verbatim.
|
|
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.
|
|
228
204
|
|
|
229
|
-
|
|
205
|
+
## Genie Tools
|
|
230
206
|
|
|
231
|
-
`
|
|
232
|
-
portable between the AppKit `agents` plugin and this one. Reach for
|
|
233
|
-
Mastra's `createTool` when you need Mastra-only fields (`outputSchema`,
|
|
234
|
-
`suspendSchema`, `requireApproval`, `mcp`, ...). An omitted `tool()` `id`
|
|
235
|
-
is auto-derived from the description (slug + short hash) so traces stay
|
|
236
|
-
stable across runs.
|
|
207
|
+
`genie.buildGenieTools()` and `plugins.genie.toolkit()` expose tools for:
|
|
237
208
|
|
|
238
|
-
|
|
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.
|
|
239
213
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
The orchestration prompt ships as the exported `GENIE_INSTRUCTIONS`
|
|
246
|
-
string; compose it into your agent's `instructions` to get the canonical
|
|
247
|
-
behavior:
|
|
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.
|
|
248
218
|
|
|
249
219
|
```ts
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const support = createAgent({
|
|
253
|
-
instructions: `${baseInstructions}\n\n${GENIE_INSTRUCTIONS}`,
|
|
220
|
+
const agent = agents.createAgent({
|
|
221
|
+
instructions: `${baseInstructions}\n\n${genie.GENIE_INSTRUCTIONS}`,
|
|
254
222
|
tools(plugins) {
|
|
255
|
-
return { ...plugins.genie?.toolkit() };
|
|
223
|
+
return { ...plugins.genie?.toolkit({ prefix: "" }) };
|
|
256
224
|
},
|
|
257
225
|
});
|
|
258
226
|
```
|
|
259
227
|
|
|
260
|
-
|
|
261
|
-
(and the matching `app.yaml` resources); the tools talk to Genie
|
|
262
|
-
directly via `@dbx-tools/genie` (`genieEventChat`) and the workspace
|
|
263
|
-
`statementExecution` API. Each Genie turn forwards its wire
|
|
264
|
-
`GenieChatEvent`s through `ctx.writer` for live UI progress. The exact
|
|
265
|
-
tool set, ids, and writer-event contract live in `[genie.ts](src/genie.ts)`
|
|
266
|
-
and the `GenieWriterEvent` docs in
|
|
267
|
-
`[@dbx-tools/appkit-mastra-shared](../appkit-mastra-shared)`.
|
|
268
|
-
|
|
269
|
-
## Embeds
|
|
270
|
-
|
|
271
|
-
Charts and data tables ride out-of-band via inline markers so the model
|
|
272
|
-
never blocks on or round-trips large payloads:
|
|
273
|
-
|
|
274
|
-
- `render_data` (a system-default ambient tool) and the Genie
|
|
275
|
-
`prepare_chart` tool both mint a `chartId` synchronously, kick chart
|
|
276
|
-
planning into the background, and return `{ chartId }` immediately. The
|
|
277
|
-
model embeds `[chart:<chartId>]` on its own line where the chart should
|
|
278
|
-
appear.
|
|
279
|
-
- `[data:<statement_id>]` markers resolve to inline table data.
|
|
280
|
-
|
|
281
|
-
The host UI splits the assistant text on these markers and long-polls
|
|
282
|
-
the plugin's generic `${basePath}/embed/:type/:id` route until each
|
|
283
|
-
entry settles. Cache entries carry a 1h TTL; after expiry the route
|
|
284
|
-
404s and the marker falls through harmlessly. Implementation and the
|
|
285
|
-
placement contract are in `[chart.ts](src/chart.ts)`; the wire shapes
|
|
286
|
-
are in `[@dbx-tools/appkit-mastra-shared](../appkit-mastra-shared)`.
|
|
287
|
-
|
|
288
|
-
## Feedback (MLflow)
|
|
289
|
-
|
|
290
|
-
When MLflow tracing is wired for the deployment, the plugin lets users
|
|
291
|
-
rate an assistant turn (thumbs up / down) and leave a comment, logged as
|
|
292
|
-
a HUMAN [assessment](https://mlflow.org/docs/latest/genai/assessments/)
|
|
293
|
-
on that turn's trace and attributed to the signed-in (OBO) user.
|
|
294
|
-
|
|
295
|
-
Enablement is auto-detected: it turns on when an OTLP exporter endpoint
|
|
296
|
-
(`OTEL_EXPORTER_OTLP_ENDPOINT` / `..._TRACES_ENDPOINT`) **and** an MLflow
|
|
297
|
-
experiment (`MLFLOW_EXPERIMENT_ID` / `MLFLOW_EXPERIMENT_NAME`) are
|
|
298
|
-
configured - the two signals that traces actually materialize in MLflow.
|
|
299
|
-
Set `config.feedback` to `true` / `false` to force it on or off
|
|
300
|
-
regardless of the env.
|
|
301
|
-
|
|
302
|
-
```ts
|
|
303
|
-
mastra({ agents: support, feedback: true }); // force-enable
|
|
304
|
-
```
|
|
228
|
+
## Charts And Data Embeds
|
|
305
229
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
Databricks MLflow REST API. Trace export is asynchronous, so a
|
|
311
|
-
just-finished trace may not exist yet - the log call retries briefly on
|
|
312
|
-
"not found" and otherwise fails softly (the chat stays usable). The
|
|
313
|
-
current state is published to the client as
|
|
314
|
-
`clientConfig().feedbackEnabled`; see `[mlflow.ts](src/mlflow.ts)` and
|
|
315
|
-
the UI wiring in `[@dbx-tools/appkit-mastra-ui](../appkit-mastra-ui)`.
|
|
316
|
-
|
|
317
|
-
## Model resolution
|
|
318
|
-
|
|
319
|
-
Each agent call resolves a model lazily (so concurrent requests keep
|
|
320
|
-
distinct user identities), delegating to
|
|
321
|
-
`[@dbx-tools/model](../model)` for the actual workspace-aware selection
|
|
322
|
-
(fuzzy name matching, score-based class classification, offline
|
|
323
|
-
fallbacks). Its exports are re-exported here, so `ModelClass` /
|
|
324
|
-
`modelForClass` and friends are available straight off
|
|
325
|
-
`@dbx-tools/appkit-mastra`.
|
|
326
|
-
|
|
327
|
-
The plugin layers on the config sources for _which_ model an agent asks
|
|
328
|
-
for, in priority order: a per-request override, the per-agent `model`,
|
|
329
|
-
the plugin `defaultModel`, then `DATABRICKS_SERVING_ENDPOINT_NAME`. With
|
|
330
|
-
none set, resolution falls through to the dynamic class picker.
|
|
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.
|
|
331
234
|
|
|
332
235
|
```ts
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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,
|
|
338
245
|
});
|
|
246
|
+
|
|
247
|
+
const resolved = await chart.fetchChart(chartId);
|
|
339
248
|
```
|
|
340
249
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
exposed at `GET ${basePath}/models` for model pickers; the same data is
|
|
345
|
-
available in-process from a sibling plugin via
|
|
346
|
-
`appkitUtils.require(this.context, mastra).exports()`.
|
|
347
|
-
|
|
348
|
-
## MCP server
|
|
349
|
-
|
|
350
|
-
The plugin's agents are exposed as a Mastra
|
|
351
|
-
`[MCPServer](https://mastra.ai/docs/tools-mcp/mcp-overview)` **by
|
|
352
|
-
default** so external MCP clients - Claude Desktop, Cursor, the Mastra
|
|
353
|
-
playground, or another agent - can call them over the standard MCP
|
|
354
|
-
transports. The server is registered on the Mastra instance via
|
|
355
|
-
`mcpServers`, so `@mastra/express` serves the stock MCP routes under the
|
|
356
|
-
plugin mount; no bespoke route is added. It's on out of the box because
|
|
357
|
-
wrapping the already-registered agents is free - only the ambient tools
|
|
358
|
-
(which assume an in-process chat turn) stay off unless opted in.
|
|
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.
|
|
359
253
|
|
|
360
|
-
|
|
361
|
-
import { mastra } from "@dbx-tools/appkit-mastra";
|
|
254
|
+
## Model Selection
|
|
362
255
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
256
|
+
`model.buildModel()` adapts the generic resolver from
|
|
257
|
+
[`@dbx-tools/model`](../model) to Mastra. It resolves the model per request,
|
|
258
|
+
so OBO identity and request-specific overrides stay isolated.
|
|
366
259
|
|
|
367
|
-
|
|
368
|
-
mastra({ mcp: false });
|
|
260
|
+
Model priority is:
|
|
369
261
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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.
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
plugin.mastra({
|
|
270
|
+
agents: analyst,
|
|
271
|
+
defaultModel: "claude sonnet",
|
|
272
|
+
modelFuzzyMatch: true,
|
|
273
|
+
modelOverride: true,
|
|
378
274
|
});
|
|
379
275
|
```
|
|
380
276
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
- Streamable HTTP: `POST /api/<plugin>/mcp`
|
|
385
|
-
- SSE (legacy): `GET /api/<plugin>/sse` + `POST /api/<plugin>/messages`
|
|
277
|
+
Use `serving.extractModelOverride()` and `serving.resolveServingConfig()` when
|
|
278
|
+
building custom routes that should behave like the plugin's `/models` and stream
|
|
279
|
+
routes.
|
|
386
280
|
|
|
387
|
-
|
|
388
|
-
internally - the plugin rewrites the clean alias to that route, so a
|
|
389
|
-
client never sees the doubled `/mcp/<serverId>/mcp` segment.)
|
|
281
|
+
## Threads, History, And Suggestions
|
|
390
282
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
resolves its model and tools as the calling user. The resolved endpoint
|
|
394
|
-
paths are available in-process via
|
|
395
|
-
`appkitUtils.require(this.context, mastra).exports().getMcp()`.
|
|
283
|
+
When storage is enabled, the plugin provides route helpers and in-process
|
|
284
|
+
functions for conversation management:
|
|
396
285
|
|
|
397
|
-
|
|
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.
|
|
398
291
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
logs, scores). AppKit authenticates every request as the OBO user, but
|
|
403
|
-
does not restrict _which_ of those a browser client may call.
|
|
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.
|
|
404
295
|
|
|
405
|
-
|
|
296
|
+
## Feedback And Observability
|
|
406
297
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
`/route/*` routes (history / threads / feedback), and, when `mcp` is
|
|
412
|
-
enabled, the MCP transport. Everything else is refused with `403`
|
|
413
|
-
before it reaches Mastra.
|
|
414
|
-
- `"full"`: dispatch the entire stock Mastra API. Use only for a trusted
|
|
415
|
-
first-party console that genuinely needs the management surface.
|
|
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.
|
|
416
302
|
|
|
417
303
|
```ts
|
|
418
|
-
mastra({
|
|
419
|
-
|
|
304
|
+
plugin.mastra({
|
|
305
|
+
agents: analyst,
|
|
306
|
+
feedback: true,
|
|
307
|
+
});
|
|
420
308
|
```
|
|
421
309
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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).
|
|
313
|
+
|
|
314
|
+
## MCP Exposure
|
|
315
|
+
|
|
316
|
+
`mcp.buildMcpServer()` exposes registered agents as MCP tools by default. The
|
|
317
|
+
AppKit plugin publishes clean aliases under its base path:
|
|
426
318
|
|
|
427
|
-
|
|
319
|
+
```ts
|
|
320
|
+
plugin.mastra({
|
|
321
|
+
agents: analyst,
|
|
322
|
+
mcp: {
|
|
323
|
+
serverId: "analytics",
|
|
324
|
+
name: "Analytics MCP",
|
|
325
|
+
tools: false,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
```
|
|
428
329
|
|
|
429
|
-
|
|
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).
|