Class: EmailPlugin
AppKit plugin that configures and verifies the SMTP transport used by
the send_email tool, and exposes sending as an AppKit agent tool.
Example
Section titled “Example”import { createApp, server } from "@databricks/appkit";import { plugin as emailPlugin } from "@dbx-tools/email";
await createApp({ plugins: [ server(), emailPlugin.email({ smtp: { host: "smtp.example.com", user: "apikey", password: process.env.SMTP_KEY }, domain: "mail.example.com", }), ],});Extends
Section titled “Extends”Plugin<EmailPluginConfig>
Implements
Section titled “Implements”ToolProvider
Constructors
Section titled “Constructors”Constructor
Section titled “Constructor”new EmailPlugin(
config):EmailPlugin
Parameters
Section titled “Parameters”config
Section titled “config”Returns
Section titled “Returns”EmailPlugin
Inherited from
Section titled “Inherited from”Plugin<EmailPluginConfig>.constructor
Properties
Section titled “Properties”
protectedapp:AppManager
Inherited from
Section titled “Inherited from”Plugin.app
protectedcache:CacheManager
Inherited from
Section titled “Inherited from”Plugin.cache
config
Section titled “config”
protectedconfig:EmailPluginConfig
Inherited from
Section titled “Inherited from”Plugin.config
context?
Section titled “context?”
protectedoptionalcontext?:PluginContext
Inherited from
Section titled “Inherited from”Plugin.context
devFileReader
Section titled “devFileReader”
protecteddevFileReader:DevFileReader
Inherited from
Section titled “Inherited from”Plugin.devFileReader
isReady
Section titled “isReady”
protectedisReady:boolean
Inherited from
Section titled “Inherited from”Plugin.isReady
name:
string
Plugin name identifier.
Inherited from
Section titled “Inherited from”Plugin.name
streamManager
Section titled “streamManager”
protectedstreamManager:StreamManager
Inherited from
Section titled “Inherited from”Plugin.streamManager
telemetry
Section titled “telemetry”
protectedtelemetry:ITelemetry
Inherited from
Section titled “Inherited from”Plugin.telemetry
manifest
Section titled “manifest”
staticmanifest:object
config
Section titled “config”config:
object
config.schema
Section titled “config.schema”schema:
JSONSchema7=EMAIL_CONFIG_SCHEMA
description
Section titled “description”description:
string
displayName
Section titled “displayName”displayName:
string="Email"
name:
"email"="email"
resources
Section titled “resources”resources:
object
resources.optional
Section titled “resources.optional”optional:
never[] =[]
resources.required
Section titled “resources.required”required:
never[] =[]
stability
Section titled “stability”stability:
"beta"="beta"
staticphase:PluginPhase
Plugin initialization phase.
- ‘core’: Initialized first (e.g., config plugins)
- ‘normal’: Initialized second (most plugins)
- ‘deferred’: Initialized last (e.g., server plugin)
Inherited from
Section titled “Inherited from”Plugin.phase
Methods
Section titled “Methods”abortActiveOperations()
Section titled “abortActiveOperations()”abortActiveOperations():
void
Abort in-flight work. AppKit’s graceful shutdown only invokes this hook - it never calls shutdown - so the SMTP pool is closed from here or it leaks at SIGTERM. The teardown is synchronous and idempotent, so the un-awaited call costs nothing.
Returns
Section titled “Returns”void
Overrides
Section titled “Overrides”Plugin.abortActiveOperations
asUser()
Section titled “asUser()”asUser(
req):this
Execute operations using the user’s identity from the request. Returns a proxy of this plugin where all method calls execute with the user’s Databricks credentials instead of the service principal.
Parameters
Section titled “Parameters”Request
The Express request containing the user token in headers
Returns
Section titled “Returns”this
A proxied plugin instance that executes as the user
Throws
Section titled “Throws”AuthenticationError if user token is not available in request headers (production only).
In development mode (NODE_ENV=development), skips user impersonation instead of throwing.
Inherited from
Section titled “Inherited from”Plugin.asUser
attachContext()
Section titled “attachContext()”attachContext(
deps?):void
Binds runtime dependencies (telemetry provider, cache, plugin context) to
this plugin. Called by AppKit._createApp after construction and before
setup(). Idempotent: safe to call if the constructor already bound them
eagerly. Kept separate so factories can eagerly construct plugin instances
without running this before TelemetryManager.initialize() /
CacheManager.getInstance() have run.
Parameters
Section titled “Parameters”context?
Section titled “context?”unknown
telemetryConfig?
Section titled “telemetryConfig?”TelemetryOptions
Returns
Section titled “Returns”void
Inherited from
Section titled “Inherited from”Plugin.attachContext
clientConfig()
Section titled “clientConfig()”clientConfig():
Record<string,unknown>
Returns startup config to expose to the client. Override this to surface server-side values that are safe to publish to the frontend, such as feature flags, resource IDs, or other app boot settings.
This runs once when the server starts, so it should not depend on request-scoped or user-specific state.
String values that match non-public environment variables are redacted
unless you intentionally expose them via a matching PUBLIC_APPKIT_ env var.
Values must be JSON-serializable plain data (no functions, Dates, classes, Maps, Sets, BigInts, or circular references). By default returns an empty object (plugin contributes nothing to client config).
On the client, read the config with the usePluginClientConfig hook
(React) or the getPluginClientConfig function (vanilla JS), both
from @databricks/appkit-ui.
Returns
Section titled “Returns”Record<string, unknown>
Example
Section titled “Example”// Server — plugin definitionclass MyPlugin extends Plugin<MyConfig> { clientConfig() { return { warehouseId: this.config.warehouseId, features: { darkMode: true }, }; }}
// Client — React componentimport { usePluginClientConfig } from "@databricks/appkit-ui/react";
interface MyPluginConfig { warehouseId: string; features: { darkMode: boolean } }
const config = usePluginClientConfig<MyPluginConfig>("myPlugin");config.warehouseId; // "abc-123"
// Client — vanilla JSimport { getPluginClientConfig } from "@databricks/appkit-ui/js";
const config = getPluginClientConfig<MyPluginConfig>("myPlugin");Inherited from
Section titled “Inherited from”Plugin.clientConfig
execute()
Section titled “execute()”
protectedexecute<T>(fn,options,userKey?):Promise<ExecutionResult<T>>
Execute a function with the plugin’s interceptor chain.
Returns an ExecutionResult discriminated union:
{ ok: true, data: T }on success{ ok: false, status: number, message: string }on failure
Errors are never thrown — the method is production-safe.
Type Parameters
Section titled “Type Parameters”T
Parameters
Section titled “Parameters”(signal?) => Promise<T>
options
Section titled “options”PluginExecutionSettings
userKey?
Section titled “userKey?”string
Returns
Section titled “Returns”Promise<ExecutionResult<T>>
Inherited from
Section titled “Inherited from”Plugin.execute
executeAgentTool()
Section titled “executeAgentTool()”executeAgentTool(
name,args,signal?):Promise<unknown>
AppKit ToolProvider: run one tool call. Arguments are validated against
the tool’s schema first, and a validation failure comes back as an
LLM-friendly string so the model can correct itself on the next turn.
Parameters
Section titled “Parameters”string
unknown
signal?
Section titled “signal?”AbortSignal
Returns
Section titled “Returns”Promise<unknown>
Implementation of
Section titled “Implementation of”ToolProvider.executeAgentTool
executeStream()
Section titled “executeStream()”
protectedexecuteStream<T>(res,fn,options,userKey?):Promise<void>
Type Parameters
Section titled “Type Parameters”T
Parameters
Section titled “Parameters”IAppResponse
StreamExecuteHandler<T>
options
Section titled “options”StreamExecutionSettings
userKey?
Section titled “userKey?”string
Returns
Section titled “Returns”Promise<void>
Inherited from
Section titled “Inherited from”Plugin.executeStream
exports()
Section titled “exports()”exports():
object
Returns the public exports for this plugin. Override this to define a custom public API. By default, returns an empty object.
The returned object becomes the plugin’s public API on the AppKit instance
(e.g. appkit.myPlugin.method()). AppKit automatically binds method context
and adds asUser(req) for user-scoped execution.
Returns
Section titled “Returns”listSenders
Section titled “listSenders”listSenders: () =>
Promise<{defaultSender?:string;restricted:boolean;senders:string[]; }>
Sender options for the current user (the GET /senders payload).
AppKit wraps this with asUser(req) for OBO scoping.
Returns
Section titled “Returns”Promise<{ defaultSender?: string; restricted: boolean; senders: string[]; }>
sendEmail
Section titled “sendEmail”sendEmail: (
message,from,signal?,options?) =>Promise<{from:string;messageId?:string;recipient:string;sent:boolean; }>
Send a message immediately from from through the shared
transport, bypassing the approval flow. For agent-driven sends
use emailTool instead.
Parameters
Section titled “Parameters”message
Section titled “message”attachments?
Section titled “attachments?”object[] = ...
string[] = ...
string = ...
string[] = ...
subject
Section titled “subject”string = ...
string[] = ...
string
signal?
Section titled “signal?”AbortSignal
options?
Section titled “options?”Returns
Section titled “Returns”Promise<{ from: string; messageId?: string; recipient: string; sent: boolean; }>
Example
Section titled “Example”class MyPlugin extends Plugin { private getData() { return []; }
exports() { return { getData: this.getData }; }}
// After registration:const appkit = await createApp({ plugins: [myPlugin()] });appkit.myPlugin.getData();Overrides
Section titled “Overrides”Plugin.exports
getAgentTools()
Section titled “getAgentTools()”getAgentTools():
AgentToolDefinition[]
AppKit ToolProvider: the tool definitions offered to an agent.
Returns
Section titled “Returns”AgentToolDefinition[]
Implementation of
Section titled “Implementation of”ToolProvider.getAgentTools
getEndpoints()
Section titled “getEndpoints()”getEndpoints():
PluginEndpointMap
Returns
Section titled “Returns”PluginEndpointMap
Inherited from
Section titled “Inherited from”Plugin.getEndpoints
getSkipBodyParsingPaths()
Section titled “getSkipBodyParsingPaths()”getSkipBodyParsingPaths():
ReadonlySet<string>
Returns
Section titled “Returns”ReadonlySet<string>
Inherited from
Section titled “Inherited from”Plugin.getSkipBodyParsingPaths
injectRoutes()
Section titled “injectRoutes()”injectRoutes(
router):void
Expose the sender-options lookup so UI compose views can populate a
From dropdown from the configured allow-list. Mounted under the
plugin base path, i.e. GET /api/email/senders. Runs in the OBO
user scope so domain wildcards resolve against the caller’s own
local part.
OBO is used only WHEN the request can support it. asUser(req) throws
AuthenticationError outside NODE_ENV=development if the request carries
no forwarded OBO token, and AppKit does not catch a rejection raised inside
a handler - so unconditionally wrapping this route takes the process down
for a caller that authenticated some other way (a @dbx-tools/tunnel
OTP session, a health probe, a local curl). The user context is only ever
an ENRICHMENT here: without it, wildcard senders simply expand against no
local part. Degrading to the service context therefore answers correctly
instead of failing, and a front-door request is unchanged.
This is the same rule @dbx-tools/appkit’s identity module applies in
"auto" mode; the header check is inlined rather than taking a dependency
on that package for one predicate.
Parameters
Section titled “Parameters”router
Section titled “router”Router
Returns
Section titled “Returns”void
Overrides
Section titled “Overrides”Plugin.injectRoutes
registerEndpoint()
Section titled “registerEndpoint()”
protectedregisterEndpoint(name,path):void
Parameters
Section titled “Parameters”string
string
Returns
Section titled “Returns”void
Inherited from
Section titled “Inherited from”Plugin.registerEndpoint
resolveUserId()
Section titled “resolveUserId()”
protectedresolveUserId(req):string
Resolve the effective user ID from a request.
Returns the x-forwarded-user header when present. In development mode
(NODE_ENV=development) falls back to the current context user ID so
that callers outside an active runInUserContext scope still get a
consistent value.
Parameters
Section titled “Parameters”Request
Returns
Section titled “Returns”string
Throws
Section titled “Throws”AuthenticationError in production when no user header is present.
Inherited from
Section titled “Inherited from”Plugin.resolveUserId
route()
Section titled “route()”
protectedroute<_TResponse>(router,config):void
Type Parameters
Section titled “Type Parameters”_TResponse
Section titled “_TResponse”_TResponse
Parameters
Section titled “Parameters”router
Section titled “router”Router
config
Section titled “config”RouteConfig
Returns
Section titled “Returns”void
Inherited from
Section titled “Inherited from”Plugin.route
setup()
Section titled “setup()”setup():
Promise<void>
Prime the shared runtime from this plugin’s config (over env), route the
tools’ sends through this plugin’s interceptor chain, and log the
effective sender policy so an active restriction is obvious at boot. In
SMTP mode, fail setup when the transport cannot be verified: a bad host
or credential is a deploy-time mistake and should stop the app rather
than wait for a user to approve a send that cannot work. With no SMTP
credentials the runtime is in file/outbox mode (only when
EMAIL_OUTBOX_MODE is set), logged loudly here so it is obvious mail
is being written to disk rather than sent.
Returns
Section titled “Returns”Promise<void>
Overrides
Section titled “Overrides”Plugin.setup
shutdown()
Section titled “shutdown()”shutdown():
Promise<void>
Close the SMTP connection pool. Idempotent.
Returns
Section titled “Returns”Promise<void>