Building a tool
A tool contributes an action rather than an engine: a menu item on the schema tree that collects some input and runs against the selected connection or node. Schema Diff and Copy Table are tool plugins; so is Universal Backup.
Tools reference the same DataTray.Sdk assembly as providers, and get handed the live
IDbProvider for the connection they run on — so a tool can read schema and run queries on
any engine without a driver dependency of its own.
The contract: IToolPlugin
| Member | Purpose |
|---|---|
Id | Stable id. One assembly may contain several IToolPlugin classes — all are loaded — so this need not match the manifest id. |
Target | Which tree nodes offer the tool. |
Fields | Route A input declarations; the host renders a generic dialog. Empty when the tool brings its own view. |
IsDestructive | Show a destructive-action confirmation before running. |
ExecuteAsync | Runs the tool. inputs holds the collected values keyed by ToolField.Key; report progress through progress. |
Where the tool appears: ToolTarget
The tool shows on a node when the node's provider is in ProviderIds and its kind is in
NodeKinds. The connection root has no node kind, so a whole-connection tool sets
IncludeConnectionRoot rather than trying to express the root through NodeKinds.
What a run receives
IToolHost
| Member | Purpose |
|---|---|
GetPluginSetting(key) / SetPluginSetting(key, value) | Read and write the plugin's own persisted settings — enough to remember a choice between runs. |
ListConnections() / ListDatabasesAsync(id, ct) | The user's other connections, and the databases on one of them. This is how a tool offers a destination. |
OpenConnection(id, database) | A live provider + profile for another connection, so a tool can run against a second database. |
OpenQueryEditor(sql) / OpenQueryEditorOn(id, database, sql) | Hand generated SQL to a query tab — on the launched connection, or on a picked one. Schema Diff and Copy Table both end this way: the user reviews the script before anything runs. |
Route A: declared fields
ToolFieldType is Text | Password | Choice | File | Bool | ConnectionPicker |
DatabasePicker. A Password field is routed to the OS keychain and never written to disk; a
File field gets a Browse button wired to the host's picker. ConnectionPicker and
DatabasePicker are a dependent pair: pick a connection and the database dropdown fills itself
from it.
Route B: your own view
When the inputs are interdependent, or the flow deserves a designed layout, supply an Avalonia view instead of the generated form:
Values still flow through IToolUiContext.GetValue/SetValue, so ExecuteAsync is
unchanged. The context also gives the view Localizer, the same
ListConnections()/ListDatabasesAsync() pickers, and
RunAsync()/CancelRun()/CloseDialog() so the view can drive the run from
its own buttons.
Owning the whole dialog
By default the host still renders the chrome around your view: a checklist, a log, a progress bar and an
action bar. A view that implements IToolDialogLifecycle takes all of that over.
The host then only feeds the view the run's events and lets it render input, progress and completion itself.
ToolProgress carries ItemKey and ItemStatus for a stepped checklist,
Fraction for a per-step bar, and Detail for the short note beside a step.
ToolRunOutcome is Succeeded | Cancelled | Failed.
Because the returned Control is an Avalonia type shared across the ALC boundary, reference
Avalonia with ExcludeAssets="runtime" — see
Plugin capabilities.
Tool manifest
Identical to a provider's, but type is "tool" and hostApiVersion
tracks the tool contract (ToolHostApi.Version), which versions separately from the
provider contract.
Declare the newest version whose members you actually use — not simply the newest that exists. A tool
that stays on 4 keeps loading in hosts that predate 5.