Skip to main content

Plugin Development Overview

ForgePortal plugins extend the portal with custom UI (entity tabs, cards, routes), backend logic (actions, API routes, catalog providers), or both. This page introduces the three plugin types, when to use each, and how to scaffold a new plugin with the CLI.

Three plugin types

TypeWhat you addEntry pointRuns in
uiEntity tabs, entity cards, top-level routesregisterPlugin(sdk: ForgePluginSDK)Browser (React app)
backendAction providers, Fastify routes, catalog providersregisterBackendPlugin(sdk: ForgeBackendPluginSDK)API server (Node)
fullstackBoth UI and backend capabilitiesTwo entry points (UI + backend)UI in browser, backend in API
  • Entity tab — A tab on the entity detail page (e.g. "PagerDuty", "Deployments"). You get the current entity and can call APIs or show custom content.
  • Entity card — A card on the entity Overview tab. Same entity context as a tab.
  • Route — A standalone page (e.g. /pagerduty) with its own URL and optional nav label.
  • Action provider — A new action (e.g. myplugin.notify@v1) that templates and the action runner can invoke. You receive an ActionContext (config, logger, SCM, DB, locks).
  • Backend route — Fastify routes under /api/v1/plugins/{pluginId}/.... Useful for serving data to your UI or webhooks.
  • Catalog provider — An async iterator that yields entity drafts; the worker can ingest them into the catalog periodically.
When to use which type
  • UI only — You only need to display something on the entity page or a custom page (e.g. link to external dashboard). Use ui.
  • Backend only — You add actions or API routes, no custom React. Use backend.
  • UI + backend — Your tab/card calls your own API (e.g. /api/v1/plugins/myplugin/data). Use fullstack.

Scaffold with the CLI

The fastest way to start is the create-forge-plugin CLI. It generates a package with the correct structure, manifest, and dependencies.

npx create-forge-plugin my-plugin --type ui
  • Interactive mode — Run npx create-forge-plugin with no arguments; the CLI will prompt for name, type, and optional org.
  • Non-interactive--type ui | backend | fullstack, and optionally --org @myorg, --out-dir ./my-plugin, --yes to skip prompts.

After generation you get a directory (e.g. forge-plugin-my-plugin/) with:

File / dirPurpose
forgeportal-plugin.jsonManifest: name, version, type, capabilities, config schema.
package.jsonDependencies (e.g. @forgeportal/plugin-sdk as peer), build script.
tsconfig.jsonTypeScript config.
src/index.tsEntry: calls sdk.registerEntityTab (ui), or sdk.registerActionProvider / registerBackendRoute (backend), or both (fullstack).
src/MyPluginTab.tsx (ui)Sample entity tab component.
src/actions/ (backend)Sample action provider.
src/routes.ts (backend)Sample Fastify route registration.

Run pnpm install and pnpm build inside the generated folder to verify it compiles. Next steps: add your logic, then register the plugin in the ForgePortal repo (UI and/or config).

Plugin ID

The plugin ID is derived from the package name: strip scope and the forge-plugin- prefix. Examples:

  • @myorg/forge-plugin-pagerdutypagerduty
  • forge-plugin-slack-notifyslack-notify

Use this ID in forgeportal.yaml under plugins.<id> and in the UI registration (registerPluginById('pagerduty', registerPagerDuty)). It must match between API and UI so that config and capabilities line up.

Next steps