npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dbx-tools/appkit-mastra

v0.3.44

Published

AppKit plugin and server-side toolkit for hosting Mastra agents inside a Databricks App.

Downloads

13,152

Readme

@dbx-tools/appkit-mastra

AppKit plugin and server-side toolkit for hosting Mastra agents inside a Databricks App.

Import this package when an AppKit backend needs an agent service with Databricks on-behalf-of auth, optional Lakebase-backed memory, Databricks Genie tools, model selection, chart/data embeds, MLflow feedback, and MCP exposure. The package mounts the standard Mastra agent stream under the AppKit server, so clients can use Mastra-compatible chat transports instead of a custom protocol.

Key features:

  • AppKit plugin lifecycle integration: routes, setup, shutdown, sibling plugin access, and AppKit request context are handled inside plugin.mastra().
  • Agent composition: define one or more Mastra agents, give each one local tools, AppKit plugin toolkits, workspace skills, model defaults, and approval-gated tools.
  • Databricks execution model: tool calls run with the active AppKit OBO client where available, while storage and background work use service-principal connections.
  • Durable conversations: Lakebase-backed Mastra storage provides thread history, message persistence, and optional vector memory.
  • Rich data answers: Genie tools, statement fetches, chart preparation, and embed markers let an agent answer with text plus delayed chart/table payloads.
  • Operational surfaces: model-list routes, feedback routes, MCP exposure, scoped API gating, tracing, and MLflow feedback are bundled with the plugin.

Why Not Just AppKit Agents?

Native AppKit includes a beta Agents plugin with markdown and TypeScript agent definitions, AppKit tool-provider integration, streaming chat, thread management, cancellation, and HITL approval. Use it when you want the AppKit agent model and do not need a separate agent framework.

Use this package when you specifically want Mastra inside AppKit:

  • Mastra's larger plugin/tool ecosystem, MCP support, memory/storage model, workflow primitives, and @mastra/client-js stream shape.
  • AppKit toolkits as Mastra tools, so Analytics, Files, Genie, and other AppKit ToolProvider plugins stay available without rewriting them.
  • Genie as an agent tool that emits typed progress events, result metadata, and delayed chart/data markers into the same assistant turn.
  • A paired React client in @dbx-tools/ui-mastra with model picking, thread sidebar, approvals, feedback, exports, and inline embeds.
  • Per-request model override and fuzzy endpoint resolution through @dbx-tools/model, instead of binding every agent to a fixed endpoint name.

Quick Start

import { analytics, createApp, lakebase, server } from "@databricks/appkit";
import { agents, genie, plugin } from "@dbx-tools/appkit-mastra";
import { z } from "zod";

const analyst = agents.createAgent({
  name: "analyst",
  instructions: ["You answer questions about workspace data.", genie.GENIE_INSTRUCTIONS].join(
    "\n\n",
  ),
  tools(plugins) {
    return {
      ...plugins.analytics.toolkit(),
      ...plugins.genie?.toolkit(),
      get_weather: agents.tool({
        description: "Get a simple weather report.",
        schema: z.object({ city: z.string() }),
        execute: async ({ city }) => `Sunny in ${city}`,
      }),
    };
  },
});

await createApp({
  plugins: [
    server(),
    analytics(),
    lakebase(),
    plugin.mastra({
      agents: { analyst },
      defaultAgent: "analyst",
      genieSpaces: { sales: "01ef..." },
    }),
  ],
});

Benefits of importing the package:

  • plugin.mastra() registers a full AppKit plugin named mastra.
  • agents.createAgent() keeps agent definitions typed and applies the default Databricks workspace/skill mounts.
  • agents.tool() lets the same AppKit-shaped tool body work in this Mastra plugin.
  • genie.GENIE_INSTRUCTIONS and plugins.genie.toolkit() give agents a Databricks Genie workflow without embedding a second agent.
  • Lakebase registration automatically enables durable thread storage and vector memory unless you opt out.

Agent Registration

plugin.mastra({ agents }) accepts a single definition, an array, or a record. Records are best when clients need stable agent ids:

plugin.mastra({
  agents: {
    support: agents.createAgent({ instructions: "Answer support questions." }),
    analyst: agents.createAgent({ instructions: "Analyze workspace data." }),
  },
  defaultAgent: "support",
});

When no agents are supplied, the plugin registers a built-in default analyst so the route surface still works for smoke tests. Each agent is streamed through the Mastra agent API mounted below the plugin path, typically /api/mastra.

Use agents.createTool when you need Mastra-native fields such as outputSchema, suspendSchema, requireApproval, or MCP metadata. Use agents.tool for the smaller AppKit-compatible shape:

const approveRefund = agents.createTool({
  id: "approve_refund",
  description: "Approve a refund request.",
  inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
  requireApproval: true,
  execute: async ({ context }) => approve(context.orderId, context.amount),
});

AppKit Toolkits

The tools(plugins) callback receives a dynamic index of registered AppKit tool-provider plugins. Each entry exposes .toolkit(opts) with AppKit-compatible prefix, only, except, and rename options.

const agent = agents.createAgent({
  instructions: "Use the narrowest tool that answers the question.",
  tools(plugins) {
    return {
      ...plugins.analytics.toolkit({ only: ["query"] }),
      ...plugins.files?.toolkit({ prefix: "files.", except: ["delete"] }),
    };
  },
});

Tool calls dispatch back through the owning AppKit plugin, preserving OBO auth and AppKit telemetry behavior. Optional plugins should be guarded with ?. when you spread their tools.

Tools Flow In, Not Out

The plugin is a tool consumer, not an AppKit ToolProvider: it deliberately implements neither getAgentTools() nor executeAgentTool(), so its built-in tools (ask_genie, get_space_description, get_space_serialized, get_statement, prepare_chart, render_data, summarize) are reachable only from a Mastra agent turn this plugin serves.

That is a property of the tools, not a gap. Each one reads the per-request Mastra execution context - the AppKit user stamped on RequestContext, the writer that streams Genie progress events to the chat, the per-call abortSignal - and refuses to run without it. An AppKit ToolProvider call carries none of that, so exposing these through one would advertise tools that cannot work. Reach for native AppKit Agents when you want your agent tools callable by other AppKit hosts.

Nothing here can be auto-inherited by another host as a side effect: with no AppKit ToolRegistry, there is no autoInheritable surface to opt in or out of. Every built-in tool is also read-only (Genie questions, statement reads, chart planning, summarization), and the ambient tools stay off the MCP server unless mcp: { tools: true } names them explicitly. Approval-gated tools you register yourself are enforced separately: boot fails if one is registered without Mastra storage to persist the suspended run.

Memory And Storage

The memory and storage config fields can be false, true, or a concrete Mastra Postgres/PgVector config.

plugin.mastra({
  agents: analyst,
  storage: true,
  memory: { id: "analytics_memory", tableName: "agent_memory" },
});

With lakebase() registered, both default to enabled:

  • storage uses a per-agent schema for durable threads and messages;
  • memory uses a shared vector index for semantic recall;
  • the service-principal pool is created outside any request so OBO user identities are not captured in background storage work.

Without lakebase(), agents are stateless unless you provide explicit storage and memory configs.

Workspace Skills

Every agents.createAgent() gets a default Mastra Workspace from workspaces.createWorkspace(). It mounts Databricks Workspace files through the current OBO user's WorkspaceClient, so Mastra can discover Assistant-style SKILL.md files at request time.

const agent = agents.createAgent({
  instructions: "Use mounted workspace skills when relevant.",
  workspace: workspaces.createWorkspace({
    assistantSkills: true,
    mounts: [
      async () => ({
        mounts: { "/reference": myFilesystem },
        skillPaths: ["/reference/skills"],
      }),
    ],
  }),
});

Production workspace mounts require a forwarded token with workspace, workspace.workspace, or all-apis scope. Development mode skips that gate for local iteration.

Genie Tools

genie.buildGenieTools() and plugins.genie.toolkit() expose tools for:

  • asking a configured Genie space;
  • reading space descriptions and serialized space metadata;
  • fetching statement rows by statement_id;
  • preparing charts from Genie result sets.

The central agent drives those tools directly. Genie events stream through the Mastra writer using the shared contract from @dbx-tools/shared-mastra, so clients can show thinking, SQL, row counts, summaries, chart markers, and data markers as the turn runs.

const agent = agents.createAgent({
  instructions: `${baseInstructions}\n\n${genie.GENIE_INSTRUCTIONS}`,
  tools(plugins) {
    return { ...plugins.genie?.toolkit({ prefix: "" }) };
  },
});

Charts And Data Embeds

chart.prepareChart() mints a chart id immediately, caches an in-progress record, resolves the data in the background, and stores a terminal chart or error. chart.fetchChart() long-polls that cache for route handlers and custom clients.

Both take a userKey: the chart cache is namespaced by the caller's identity, so a chart id lifted from another user's transcript resolves to nothing and the embed route answers 404. Use config.resolveUserKey(), which reads the AppKit user off the Mastra request context and falls back to the ambient execution context.

const userKey = config.resolveUserKey(requestContext);

const { chartId } = await chart.prepareChart({
  config: pluginConfig,
  userKey,
  title: "Revenue by region",
  description: "Compare total revenue by region.",
  resolveData: async () => ({ rows }),
});

const resolved = await chart.fetchChart(chartId, { userKey });

Agents can return [chart:<id>] and [data:<statement_id>] markers in prose. The embed route resolves them later, which avoids forcing the language model to inline large tables or wait for chart planning before continuing its answer.

Brand The Charts

Pass a brand to the plugin to theme every generated chart with your brand's palette and font; omit it for the default Echarts look.

import { brand } from "@dbx-tools/shared-core";

plugin.mastra({ agents, storage: true, brand: brand.defaultBrandContext });

brand is the portable BrandContext shared across the UI, email, and libraries, so charts, email, and the chat UI theme from one source. The chart planner derives an Echarts theme from it: a series color cycle seeded from colors.primary / colors.accent (plus a colorblind-friendly spread so many-series charts stay legible) and a base text style from typography.sans / colors.foreground. Charts render to canvas, so this is applied server-side on the Echarts option rather than through the browser [data-brand] CSS bridge.

Model Selection

model.buildModel() adapts the generic resolver from @dbx-tools/model to Mastra. It resolves the model per request, so OBO identity and request-specific overrides stay isolated.

Model priority is:

  1. request override (X-Mastra-Model, ?model=, body model / modelId);
  2. per-agent model;
  3. plugin defaultModel;
  4. DATABRICKS_SERVING_ENDPOINT_NAME;
  5. workspace catalogue ranking and static fallback floor.
plugin.mastra({
  agents: analyst,
  defaultModel: "claude sonnet",
  modelFuzzyMatch: true,
  modelOverride: true,
});

Use serving.extractModelOverride() and serving.resolveServingConfig() when building custom routes that should behave like the plugin's /models and stream routes.

The plugin also serves GET /default-model (and /default-model/:agentId), returning { agentId, model, displayName } - the static serving-endpoint an agent falls back to when the client pins no model, plus its humanized label. model / displayName are null when the agent resolves its model dynamically at call time. This lets a model picker label its default option without waiting on the /models catalogue (so it never flashes a raw id). A :agentId that is not registered returns 404 with the registered ids, the same as the history and threads routes.

Threads, History, And Suggestions

When storage is enabled, the plugin provides route helpers and in-process functions for conversation management:

  • history.loadHistory() and history.clearHistory() read or clear one thread;
  • threads.listThreads(), threads.renameThread(), and threads.deleteThread() operate on the caller's scoped conversations;
  • genie.collectSpaceSuggestions() reads starter questions from the configured Genie space.

The plugin resolves the active thread from x-mastra-thread-id, ?threadId=, or a per-session fallback cookie. That keeps streaming, history, and clear operations aligned around the same conversation id.

Feedback And Observability

observability.buildObservability() wires Mastra tracing when OTLP export is configured. mlflow.resolveFeedbackEnabled() turns MLflow feedback on when both trace export and an MLflow experiment are configured, unless the plugin config forces a value.

plugin.mastra({
  agents: analyst,
  feedback: true,
});

mlflow.logFeedback() logs a human assessment against the active MLflow trace. The response header name and request/response schemas live in @dbx-tools/shared-mastra.

MCP Exposure

mcp.buildMcpServer() exposes registered agents as MCP tools by default. The AppKit plugin publishes clean aliases under its base path:

plugin.mastra({
  agents: analyst,
  mcp: {
    serverId: "analytics",
    name: "Analytics MCP",
    tools: false,
  },
});

Use mcp: false to disable MCP. Turn on tools: true only for ambient tools that are safe outside an in-process chat turn.

Driving A Turn From Outside The Routes

Another plugin (or a scheduled job) can run an agent turn directly, but a raw agent.generate(prompt) loses everything the HTTP middleware stamps - most visibly the AppKit user, which every user-scoped tool reads. ask_genie then fails with "invoke the tool from an agent turn served by the mastra plugin", so the turn answers "the data source is unreachable" where the chat routes answer with real data.

exports().createRequestContext() builds the missing context:

const mastra = context.getPlugins()?.get("mastra")?.exports();
const requestContext = await mastra.createRequestContext({
  threadId: conversationId,
  resourceId: userId,
});
const result = await mastra.getDefault().generate(prompt, { requestContext });

The AppKit user, the memory thread / resource pair, and a request id (so the turn's spans join up in traces) are stamped exactly as the request middleware stamps them. Call it inside an asUser(req) scope to inherit the caller's OBO identity; outside one it resolves to the service principal. @dbx-tools/teams uses this so a Teams card turn has the same tool reach - and therefore the same answer - as a chat turn.

API Gate

The stock @mastra/express app has broad management routes. The plugin's default apiAccess: "scoped" allows only the chat, read-only metadata, plugin-owned /route/*, embed, model, suggestion, and MCP surfaces that the client needs. Use apiAccess: "full" only for a trusted first-party console.

server.isMastraRequestAllowed() is exported for tests and custom dispatch logic that need the same allowlist.

Routes

Mounted under the plugin base path, which is /api/mastra unless you override name. Every route below is registered through AppKit's route() helper, so it appears in the plugin's endpoint map and forwards handler errors to AppKit.

| Method | Path | Purpose | | -------------------------- | --------------------------- | ------------------------------------------------------------------------------------------- | | GET | /models | Serving-endpoint catalogue for a model picker. | | GET | /default-model[/:agentId] | Static default model an agent falls back to, with its humanized label. 404 on unknown id. | | GET | /suggestions[/:agentId] | Starter questions from the configured Genie spaces. Degrades to []. | | GET | /embed/chart/:id | Long-polls a [chart:<id>] marker's cached spec. ?timeoutMs= up to 5 minutes. | | GET | /embed/data/:id | Rows behind a [data:<statement_id>] marker. ?limit= clamped server-side. | | GET / DELETE | /route/history[/:agentId] | Load or clear the caller's thread messages. | | GET / DELETE / PATCH | /route/threads[/:agentId] | List, delete, or rename the caller's conversations. | | POST | /route/feedback | Log a thumbs / comment assessment to the turn's MLflow trace. 404 when feedback is off. | | POST / GET | /mcp, /sse, /messages | MCP transports, when mcp is enabled. |

Agent inference itself rides the stock Mastra routes (/agents/:id/stream), so @mastra/client-js and @dbx-tools/ui-mastra work without a bespoke protocol.

Environment Variables

Every value can also be set through plugin config, which wins. These are the fallbacks, so a deployment that already follows AppKit's Databricks env naming needs no extra wiring.

| Variable | Effect | | ------------------------------------------------------------------- | ------------------------------------------------------------------------- | | DATABRICKS_SERVING_ENDPOINT_NAME | Model used when neither the agent nor defaultModel names one. | | DATABRICKS_GENIE_SPACE_ID | Genie space registered under the default alias. | | OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | Presence of either turns Mastra tracing on when observability is unset. | | MLFLOW_EXPERIMENT_ID, MLFLOW_EXPERIMENT_NAME | With an OTLP endpoint, turns MLflow feedback on when feedback is unset. |

Configuration Reference

The plugin config is intentionally centered on the AppKit lifecycle instead of requiring callers to assemble a Mastra server by hand.

  • agents registers a single agent, an array, or a record keyed by stable agent ids. Records are best for UIs because the ids become route-visible.
  • defaultAgent controls which registered agent handles requests that do not name an agent explicitly.
  • storage and memory accept true, false, or concrete Mastra Postgres / PgVector options. true resolves from lakebase() when present.
  • genieSpaces maps aliases to Genie Space IDs (or to { spaceId, hint } objects). Those aliases flow into tool names, suggestions, and chart/data workflows. An alias present with no space id is a wiring contradiction and fails at construction rather than silently registering no Genie tools.
  • defaultModel, modelOverride, and modelFuzzyMatch control how loose model names are resolved through Databricks Model Serving.
  • feedback controls whether MLflow feedback routes are exposed. The automatic mode enables feedback when tracing and an MLflow experiment are configured.
  • mcp controls whether agents are exposed as MCP tools and how that server is named.
  • apiAccess chooses the route allowlist. Keep the default scoped mode for deployed apps.

Use this package when you want an AppKit-native agent runtime. Use the shared schemas in @dbx-tools/shared-mastra when building a client that talks to these routes.

Modules

  • plugin - MastraPlugin and mastra() AppKit plugin factory.
  • agents - createAgent, tool, createTool, agent build helpers, fallback defaults, and approval-gated tool inspection.
  • config - plugin config types and RequestContext key constants.
  • model / serving / servingSanitize - Mastra model config, request overrides, serving-endpoint config, and the on-the-wire request/response cleanup that keeps provider-specific payload quirks (Claude's replayed thinking blocks, Gemini's content-parts responses) from failing a turn.
  • genie - Genie prompt, space normalization, Genie toolkits, and suggestions.
  • chart / statement / writer - chart cache, statement row fetches, and safe writer events.
  • history / threads / pagination / validation - conversation persistence helpers, route handlers, and request-body validation.
  • defaults - cache / retry / timeout settings for the plugin's own outbound calls, one constant per call site with its reasoning.
  • memory / storageSchema - Lakebase-backed Mastra store/vector setup.
  • workspaces / filesystems - Mastra workspace creation and Databricks Workspace file adapters.
  • mcp - MCP server construction.
  • observability / mlflow - tracing and feedback.
  • server / rest / processors - Express dispatch, Databricks REST helpers, stream/result processors.

Browser-facing wire types are in @dbx-tools/shared-mastra. Genie event contracts are in @dbx-tools/shared-genie. Model request/result contracts are in @dbx-tools/shared-model. The matching React chat surface is @dbx-tools/ui-mastra.