Skip to content

App plugins ​

An AppPlugin bundles everything one app contributes to the framework. A server consumes a list of plugins; createFrameworkApp wires each one in.

Shape ​

ts
interface AppPlugin<TServer = MCPServer> {
  definition: AppDefinition
  appConfig?: Record<string, unknown>
  registerTools?: (server: TServer) => void
  registerWidgetTools?: (server: TServer, metaDefaults?: WidgetToolMetaDefaults) => void
}

interface AppDefinition {
  name: string
  steps: PipelineStepDefinition[]
  widgets: WidgetDefinition[]
}

What the framework does with it ​

  1. At boot — loadApps(plugin.definition) registers every step and widget into StepRegistry + WidgetRegistry.
  2. At boot — plugin.registerTools?.(server) registers MCP tools under the plugin's prefix.
  3. At request time — the pipeline executor's bindAppConfig hands each step its plugin's appConfig. If the config carries a callTool closure (injected by the plugin itself), the per-request userId is pre-bound as a hidden 3rd argument, so steps keep a 2-arg signature.
  4. After framework tools — plugin.registerWidgetTools?.(server, metaDefaults) registers any widget-action tools (optional). Model-visible widget tools bind a view named after the tool; createFrameworkApp collects those bindings and primes mcp-use's view registry with the shared app bundle.

Two common shapes ​

Plain module ​

Registers its own tools (wraps an API, domain logic, etc.), exposes steps that call into those tools, and ships widgets that render the results.

ts
export function createPlugin(): AppPlugin {
  return {
    definition,
    registerTools(server) {
      server.tool({ name: "invoice_create", ... }, async (args) => { ... })
    },
    registerWidgetTools(server) {
      server.tool({ name: "invoice_refresh", visibility: "app", ... }, ...)
    },
  }
}

The runnable reference is examples/modules/tasks.

Module with a typed callTool (tool-codegen) ​

Same as above, plus the plugin injects a typed callTool closure — its signature generated by tool-codegen from the module's own tool surface — into its appConfig, so steps call tools with compile-time-checked names and args.

ts
export function createPlugin(): AppPlugin {
  const store = createArticleStore()
  return {
    definition,
    appConfig: { callTool: createArticlesCallTool(store) }, // typed, in-process
    registerTools: (server) => registerArticleTools(server, store),
  }
}

See typed-call-tool-in-steps; the worked example is examples/modules/articles.

Naming ​

  • AppDefinition.name — lower-kebab-case, e.g. lexoffice, articles, tasks.
  • Step ids, widget ids, and the keys they emit all prefix with the app name: articles:article-card, articles:articleId, tasks:board. The prefix is how get-framework-manifest groups entries, and how steps find their appConfig (ref.step.split(":")[0]).

Reference ​

  • AppPlugin → packages/core/src/types/app.ts
  • AppDefinition → packages/core/src/types/app.ts
  • Registration loop → packages/core/src/tools/create-framework-app.ts

Released under the MIT License.