Architecture
A Miragon MCP Toolkit server is an mcp-use MCPServer with two layers:
- App plugins — register domain tools, widget tools, pipeline steps, widgets.
- Framework tools — always-on: LLM-facing
get-framework-manifestandrender-view; app-only (iframe internal, never in LLMtools/list)refresh-view. Plus one natively registeredui://views/<tool>.htmlresource per view-bound tool, all embedding the shared widget bundle. Opt-in (only whenapp.builder === true, default off): thesave-/list-/load-/delete-dashboardCRUD quartet backed by a pluggableDashboardStore, plus the app-onlyget-builder-cataloguethat powers the in-iframe builder. Lean servers leave the builder off sorender-view+ the widget core stay the entire framework surface.
┌──────────────────────────────────────────────────────────────────────┐
│ MCP client (LLM) │
└──────────────────────────┬───────────────────────────────────────────┘
│ tools/list · tools/call
┌──────────────────────────▼───────────────────────────────────────────┐
│ MCPServer (mcp-use) │
│ ┌──────────────┐ ┌──────────────────────────────────────────────┐ │
│ │ middleware │ │ tools │ │
│ │ org-gate │ │ get-framework-manifest · render-view │ │
│ │ role-filter │ │ <plugin>_* │ │
│ └──────────────┘ │ (app-only) refresh-view │ │
│ │ (opt-in, app.builder) save-/list-/load-/ │ │
│ │ delete-dashboard · (app-only) get-builder- │ │
│ │ catalogue │ │
│ └──────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ resources: ui://views/<tool>.html (shared widget bundle) │ │
│ └──────────────────────────────────────────────────────────────┘ │
└────┬─────────────────────────────────────────────────────────────────┘
│ pipeline: step.execute(ctx, appConfig)
│ with the plugin-injected typed callTool closure
┌────▼────────────────┐
│ StepRegistry │
│ WidgetRegistry │
└─────────────────────┘Federation is a gateway concern
Every toolkit server is self-contained: its own tools, its own steps, its own widgets, its own UI bundle. Aggregating several MCP servers into one client-facing surface is the job of an external MCP gateway in front of them (e.g. agentgateway — federation / multiplexing of multiple MCP servers, virtual servers). The client host renders the UI of whichever server's tool was called. The toolkit deliberately ships no upstream-proxy or module-discovery machinery of its own.
Request flow — render-view
- Client calls
render-viewwith{ keys, steps, layout, title }. - Framework-tool handler resolves the caller with
resolvePipelineContext(ctx)—{ userId, authenticated }, the id beingctx.auth.user.id, elseuserId, else the token'ssub. executePipelineruns each step:- Looks up definition by
ref.stepinStepRegistry. - Checks all
requireskeys exist in the running context. - Builds the step's
appConfigviabindAppConfig: if the config has acallToolclosure (injected by the plugin viaAppPlugin.appConfig), wraps it so the 3rdctxarg gets pre-bound withuserId, leaving steps with a 2-arg signature. - Calls
step.execute(context, appConfig). - Merges the returned
keysinto the running context.
- Looks up definition by
- The view payload returned to the client has
structuredContentwith{ layout, context: { keys, stepIds, stepData, errors }, _refreshParams }. - The widget bundle (
mcp-app.js) renders each widget in the layout by reading itsWidgetDefinition.requiresagainstcontext.keys.
Request flow — server-internal tool call from a step
- Step calls its injected
callTool("articles_get-article", { id }). - The executor's
bindAppConfighas pre-bound the per-requestuserIdonto the closure. - The closure resolves the call however the plugin implemented it — an in-process store lookup (the examples' articles module), a REST call, or an MCP client session.
Caveat: role-filter middleware only runs on the public RPC surface. Step-internal dispatches bypass it. For defence-in-depth on step paths, add an explicit check in the step.
Where the parts live
| Concern | File |
|---|---|
| Server boot + wiring | packages/core/src/tools/create-framework-app.ts |
| Framework tools | packages/core/src/tools/register-framework-tools.ts |
| Builder catalogue | packages/core/src/tools/register-catalogue-tool.ts (app-only) |
| Catalogue helper | packages/core/src/framework/catalogue.ts |
| Dashboard CRUD tools | packages/core/src/tools/register-dashboard-tools.ts |
| Dashboard store | packages/core/src/framework/dashboard-store.ts |
| Pipeline executor | packages/core/src/engine/pipeline-executor.ts |
| View rendering | packages/core/src/framework/render-view.ts |
| Manifest | packages/core/src/framework/manifest.ts |
| REST helper | packages/core/src/rest/ (createRestClient + createRestTool) |