Skip to main content
/

Implementing a Simple CLI Plugin in Litestar

Jacob Coffee··700 words·6 min read·

The litestar CLI is a click Group you can attach your own commands to. Subclass CLIPlugin, implement on_cli_init, and your command ships with your app.

The litestar CLI is a click Group, and plugins can attach commands to it. Subclass CLIPlugin, implement on_cli_init, register the plugin on your app. No separate console script, no argparse mess, and your command ships with the app it operates on. Just a litestar $yourCoolCommand and you're ready to go!

Everything below is against Litestar 2.24.0, the current release. Here's the whole thing. Click through the tabs, or jump straight to :

~/litestar-cli-plugin3 files
from litestar import Litestar, getfrom cli_plugin import GreetingCLIPlugin@get("/")async def index() -> dict[str, str]:    return {"hello": "world"}app = Litestar(route_handlers=[index], plugins=[GreetingCLIPlugin()])

The plugin

CLIPlugin asks for one method. on_cli_init hands you the root Group behind the litestar command, and anything you attach to it shows up in litestar --help next to the built-ins. In that's a single greet command with one option.

Two deliberate choices in there. click is imported inside on_cli_init, not at module top level, because the CLI machinery only exists when someone is actually running the CLI. Importing your app for ASGI serving never pays for it. The Group type import lives under TYPE_CHECKING for the same reason: it's annotation-only.

CLIPlugin, not CLIPluginProtocol

Most examples you'll find (including the current CLI docs) subclass CLIPluginProtocol. Use CLIPlugin instead. We started moving plugin protocols to plain base classes in 2.15, where InitPlugin and SerializationPlugin replaced their *Protocol names, which now carry deprecation notices. On the main branch, CLIPluginProtocol is gone from litestar.plugins entirely. CLIPlugin is the name that survives into 3.0.

On 2.x you lose nothing by switching. CLIPlugin subclasses the protocol, and it also gives you server_lifespan(), a context manager that wraps litestar run if you need setup and teardown around the dev server.

Wiring it up

Registration is the plugins=[GreetingCLIPlugin()] line in , identical to any other Litestar plugin. The route handler in exists to prove this is a servable app. The CLI plugin goes right along side it (not replacing it.)

CLI plugins are one corner of a much bigger extension surface. By the way if you want to learn a little deeper about the various plugin options Litestar has you can check out my talk on the topic presented at EuroPython 2026. This includes the lifecycle hooks with on_app_init, teaching Litestar to serialize your own types, custom DI providers, and contributing to the OpenAPI schema.

Run it

Copy the command from the workspace footer above. It downloads the project and invokes the new command through uv:

litestar greet --name pythonista
# Hello, pythonista!

Because greet is registered on the real CLI group, litestar greet --help comes for free, and litestar run still serves the app.