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
- At boot —
loadApps(plugin.definition)registers every step and widget intoStepRegistry+WidgetRegistry. - At boot —
plugin.registerTools?.(server)registers MCP tools under the plugin's prefix. - At request time — the pipeline executor's
bindAppConfighands each step its plugin'sappConfig. If the config carries acallToolclosure (injected by the plugin itself), the per-requestuserIdis pre-bound as a hidden 3rd argument, so steps keep a 2-arg signature. - After framework tools —
plugin.registerWidgetTools?.(server, metaDefaults)registers any widget-action tools (optional). Model-visible widget tools bind a view named after the tool;createFrameworkAppcollects 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 howget-framework-manifestgroups entries, and how steps find theirappConfig(ref.step.split(":")[0]).
Reference
AppPlugin→packages/core/src/types/app.tsAppDefinition→packages/core/src/types/app.ts- Registration loop →
packages/core/src/tools/create-framework-app.ts