Skip to content

Build a first-party plugin

A Tilecast plugin is one directory below plugins/. Tilecast discovers its server code, Studio pages, Player runtime, migrations, API description, and public docs from that directory.

Plugins ship with Tilecast. Plugin API v1 does not download or run third-party code.

For the exact API contract, use the Plugin API v1 engineering reference.

  • Directoryplugins/
    • Directorytransit-alerts/
      • tilecast.plugin.json Identity, capabilities, and entry points.
      • plugin.go Go entry point.
      • plugin_test.go Conformance test.
      • Directoryserver/ Optional server implementation.
        • …
      • Directorymigrations/ Optional Goose migrations.
        • …
      • api/openapi.yaml Optional API description.
      • automation.yaml Optional automation mapping (Automation Contract v1).
      • automation.gen.json Generated from automation.yaml and the API description.
      • Directorywidgets/ Optional bundled Widgets.
        • …
      • Directorydata-sources/ Optional declarative Data Sources.
        • …
      • Directorystudio/ Optional Studio pages and translations.
        • …
      • Directoryruntime/ Optional Player runtime and styles.
        • …
      • Directorydocs/ Optional public docs pages.
        • …

The plugin ID uses underscores. The directory uses hyphens, so transit_alerts lives in plugins/transit-alerts/.

The build owns the few files that list every plugin. Run npm run plugins:generate instead of editing those files by hand.

A plugin with an HTTP API can offer its operations in the tilecast CLI and the MCP server through automation.yaml (Automation Contract v1). The file maps operation IDs from the plugin’s api/openapi.yaml to a CLI path, an MCP action, and a risk level. It doesn’t redefine any path, schema, authorization, or validation; the server stays authoritative for all of those.

plugins/countdown-bar/automation.yaml (excerpt)
apiVersion: 1
operations:
- operationId: listCountdownBarInstances
risk: read
description: List the configured Countdown Bar instances.
cli:
path: [countdown-bar, instance, list]
mcp:
action: list_instances
- operationId: createCountdownBarInstance
risk: routine
description: Create a Countdown Bar instance from a JSON document.
cli:
path: [countdown-bar, instance, create]
mcp:
action: create_instance
input: document

Once the plugin is installed, each mapped operation appears in two places:

  • CLI: below tilecast plugin, at the declared path, such as tilecast plugin countdown-bar instance list. Path parameters are positional arguments, query parameters become flags, and an operation with input: document takes its JSON body from --input or --file.
  • MCP: as a tool named after the plugin ID and the action, such as countdown_bar_list_instances. Its input schema comes from the operation’s OpenAPI parameters and request body.

Choose the risk level that matches the operation’s effect: read, routine, sensitive, high-impact, or security-critical. The CLI asks for confirmation at sensitive and above, MCP requires confirm: true for the same levels, and tilecast mcp --read-only offers only read operations. The MCP guide explains what each level means to an operator. A risk level describes the operation; it never replaces the server’s authorization.

List operations that must stay out of automation under exclusions, each with a reason, such as a browser-only security step.

The CLI and MCP server read the resolved mappings from Tilecast Server at run time. Nothing about the plugin is written into the CLI, so no CLI change ships with the plugin. npm run plugins:generate derives parameter and body metadata into the plugin’s generated automation.gen.json, and npm run plugins:check rejects a mapping that doesn’t match the OpenAPI fragment.

The schema lives in @tilecast/plugin-sdk (./automation, plus the portable schema/tilecast-automation.schema.json). Countdown Bar’s automation.yaml is the worked example. For the exact contract, see the programmable control plane reference.

A plugin can bundle ordinary Widgets and declarative Data Sources in its own directory. A bundled Widget is the same Widgets V2 module as a core Widget in widgets/; only its owner differs, and tilecast.plugin.json doesn’t change. See Bundle Widgets and Data Sources.

A plugin can also put its data on a screen without its own Widget: publish a Data Source that core Widgets such as List, Table, or Cards can display. Forms works this way.

The three bundled plugins cover different parts of the API:

  • Countdown Bar is the smallest Player-facing example. It has Studio configuration, a manifest projector, and shared-runtime surfaces.
  • Emergency Alerts shows routes, background work, network access, Takeovers, and an emergency Player surface.
  • Forms shows a larger workflow plugin with standalone Studio routes, secondary navigation, a Data Source provider, private assets, and PostgreSQL-heavy tests.

Start with the example closest to what you are building. Don’t copy a contribution interface that your plugin does not need.