Plugin Authoring Guide
Overview
A plugin is a Dart package that ships an ArtisanServiceProvider subclass. The
subclass declares the CLI commands and MCP tool descriptors the plugin contributes.
When a consumer calls plugin:install , artisan registers the provider in
lib/app/_plugins.g.dart, which is loaded at startup and merged into the
ArtisanRegistry. Commands appear in artisan list; MCP tool descriptors surface
through the stdio JSON-RPC server so LLM agents can invoke them by name.
Getting Started
Scaffold the plugin skeleton with the make:plugin command:
dart run fluttersdk_artisan make:plugin awesome_plugin
The generator creates the following tree inside awesome_plugin/:
awesome_plugin/
lib/
awesome_plugin.dart # barrel export
src/
awesome_artisan_provider.dart # ArtisanServiceProvider subclass
commands/ # one file per ArtisanCommand
pubspec.yaml # declares fluttersdk_artisan dependency
CHANGELOG.md
README.md
The generated provider is immediately wirable: plugin:install awesome_plugin
injects AwesomeArtisanProvider.new into the consumer's _plugins.g.dart.
ArtisanServiceProvider Contract
Every plugin must extend ArtisanServiceProvider
(lib/src/console/artisan_service_provider.dart:11-25):
abstract class ArtisanServiceProvider {
/// Human-readable provider name used in collision error messages.
/// Defaults to the runtime class name.
String get providerName => runtimeType.toString();
/// Returns the commands this provider contributes to the application.
List commands();
/// Returns the MCP tool descriptors this provider contributes.
///
/// Defaults to an empty list so existing providers compile without
/// modification. Override to expose VM-Service-backed tools to the MCP
/// server.
List mcpTools() => const [];
}
Key points:
commands()is abstract: return everyArtisanCommandsubclass your plugin ships. Duplicate command names throwArtisanCommandCollisionException.mcpTools()defaults to an empty list. Override it to expose VM Service extension tools to the MCP server.providerNamedefaults toruntimeType.toString(). Override it with a stable identifier (e.g.'fluttersdk_dusk') so collision messages survive class renames.- There is no
register()lifecycle hook onArtisanServiceProvider. Initialization belongs in command constructors or the host app's boot phase.
Minimal scaffold provider (assets/stubs/make_plugin/generic/provider.dart.stub):
import 'package:fluttersdk_artisan/artisan.dart';
final class AwesomeArtisanProvider extends ArtisanServiceProvider {
@override
List commands() => [
// Commands added via `make:command` land here automatically.
];
// To expose MCP tools, override mcpTools() returning your descriptor list.
}
Writing Commands
Each entry in commands() is an ArtisanCommand subclass. Declare the command
surface via the Signature DSL or configure(ArgParser):
- Signature DSL (preferred):
String get signature => 'cmd:name {arg} {--flag}'. See Signature DSL reference for the full syntax. configure(ArgParser): explicit fallback for cases the DSL cannot express (subcommands, allowed values with validation, etc.).
Scaffold a new command inside the plugin directory:
dart run fluttersdk_artisan make:command MyVerb
The generator writes lib/src/commands/my_verb_command.dart and auto-registers
it in the provider's commands() list. See
make:command for the full scaffold walkthrough.
Adding MCP Tools
Override mcpTools() and return a List. Each descriptor
(lib/src/mcp/mcp_tool_descriptor.dart:24-82) is a const-constructible value
with four required fields:
| Field | Type | Purpose |
|---|---|---|
name |
String |
Snake-case, service-prefixed tool name (e.g. awesome_start). Must be globally unique across all loaded providers. |
description |
String |
Action-oriented, LLM-targeted description. Imperative opening sentence, usage bullets, constraint-forward language. |
inputSchema |
Map |
JSON Schema draft-7 object describing accepted input. Pass {'type': 'object', 'properties': {}} when the tool takes no arguments. |
extensionMethod |
String |
VM Service extension registered in the running Flutter isolate (e.g. ext.awesome.start). Internal routing key; not included in the MCP wire shape. |
Example provider with commands and MCP tools:
import 'package:fluttersdk_artisan/artisan.dart';
final class AwesomePluginArtisanProvider extends ArtisanServiceProvider {
@override
List commands() => [
AwesomeStartCommand(),
AwesomeStopCommand(),
];
@override
List mcpTools() => [
McpToolDescriptor(
name: 'awesome_start',
description: 'Start the awesome service',
inputSchema: {'type': 'object', 'properties': {}},
),
];
}
The example above omits extensionMethod for brevity. In production all four
fields are required; extensionMethod must match the registerExtension call
in the running Flutter isolate (e.g. 'ext.awesome.start').
The MCP server collects descriptors from all loaded providers when
runArtisan(collectMcpTools: true) fires (bin/mcp.dart sets this
automatically). Tool names must be unique; a collision throws
ArtisanMcpToolCollisionException at startup.
Naming convention: prefix every tool name with a short service identifier
followed by an underscore (for example my_plugin_greet). The prefix should
match the package's providerName (or a recognizable abbreviation thereof).
Publishing
-
Bump the version in
pubspec.yamlfollowing SemVer (patch for fixes, minor for new commands/tools, major for breaking provider API changes). -
Declare the dependency on
fluttersdk_artisanwith a caret constraint, notpath::dependencies: fluttersdk_artisan: ^0.0.1 -
Validate the archive before tagging:
dart pub publish --dry-run -
Add a CHANGELOG entry, then publish:
dart pub publish
Linking from Consumer
The consumer installs the plugin with:
dart run fluttersdk_artisan plugin:install awesome_plugin
This registers AwesomeArtisanProvider.new in lib/app/_plugins.g.dart and
re-generates the barrel; the provider is discovered at the next artisan start.
See plugin:install for the three routing modes
(manifest, canonical scaffold, legacy).
Related
- install-yaml -
install.yamlmanifest schema for declaring install steps declaratively. - installer-dsl -
PluginInstallerfluent DSL for authoring programmatic install logic. - make:plugin - scaffold command reference with all flags and the generated file tree.