Architecture
The platform is a single Node.js MCP server that exposes Camunda 7 / CIB Seven operations and Prometheus-backed analytics to any MCP host (Claude, ChatGPT, …). A Kotlin plugin in the engine emits process metrics via Micrometer, pushed over OTLP; the OTEL Collector exports them to Prometheus so the analytics module can query them.
At a glance
Modules
| Module | Role |
|---|---|
MCP Server (apps/mcp-server-camunda7/) | Hosts the HTTP transport on port 8400, loads modules from MCP_ACTIVE_MODULES, and serves the Vite-built React widget bundle (see widgets). |
camunda7 (packages/connectors/camunda/camunda7-connector/) | Wraps the Camunda 7 / CIB Seven REST API via an OpenAPI-generated client. Exposes process, task, incident, deployment, and history tools plus widgets. |
analytics (packages/connectors/analytics/analytics-connector/) | Queries Prometheus via PromQL for performance, failure, bottleneck, version/cluster comparison, and the cross-engine landscape. Tools + widgets. |
engine-plugins (engine-plugins/) | Kotlin Micrometer plugins for CIB Seven: a process-metrics emitter. Independent build (Java 21, Gradle). No engine-side database. |
widgets (apps/mcp-server-camunda7/dist/mcp-app.js + .css) | One Vite-built ES module + stylesheet containing React, Tailwind, and every widget. mcp-use serves it behind one ui://views/<tool>.html resource per widget tool; the host renders the bound view when a tool returns { widget, data }. |
Composition rules
A small set of layering rules keeps modules independently usable and a future second engine dialect cheap:
- App = one product per engine dialect.
apps/mcp-server-camunda7is the thin composition root of the Camunda-7-dialect product (camunda7 + analytics). Engine vendors of that dialect (CIB Seven, Operaton, Camunda 7) are per-engine runtime configuration (flavoron the engine entry, resolved to anEngineProvidercarrying only the real differences: cockpit routes, branding, client hook) — never separate apps. A different dialect (e.g. Flowable, with its own REST API) would be its own module, client, and app. - Modules are peers.
mcp-*packages never import each other. Cross-module capabilities are injected by the app — e.g. the camunda7 module's BPMN-XML lookup feeding the analytics heatmap (SharedResources.fetchBpmnXml). - Modules are self-contained. Each exports a module definition — config schema, env mapping, known env vars, boot warnings, plugin factory (
packages/*/src/module.ts). The app only selects modules (MCP_ACTIVE_MODULES) and wires shared resources against its own port (apps/mcp-server-camunda7/src/module-contract.ts). The module packages are published to npm, so external composition roots can mount them next to their own modules —templates/composed-server/is the reference. - Apps own no domain UI. Widgets and their catalogues live in packages:
widget-shellcarries the generic primitives plus theshell:*widgets; each module carries its own. Cross-module UI has three sanctioned tiers — genericshell:*widgets fed viaprops.dataKey, tool-name string references with graceful degradation, and hard-composed views in a dedicated package (created with the first real view). - Extract on the second consumer, never speculatively. A composed-views package appears with the first cross-module view, a shared domain-widget package with a second dialect, a host-kit with a second app.
User settings
Preferences live in one profile record per user, shared by every module and persisted in Postgres (DATABASE_URL), on disk (MCP_PROFILE_DIR), or in memory — in that order of precedence. The record is keyed by the authenticated user when the server runs with MCP_OAUTH, otherwise by the MCP session id.
| Part | Owner | Contents |
|---|---|---|
| Core record + store | widget-shell | Language, theme, record metadata, the slice transport, the stores |
modules.<module> slice | that module | Whatever only it understands — e.g. camunda7's engine picks |
Each module owns its slice end to end: its own schema, its own save tool, and its own section on the settings page. Nothing central knows what a slice contains — the record only transports it, and a save merges per module key, so one module can never overwrite another's settings. Reads are fail-soft throughout: an unreadable slice or an unreachable store yields defaults rather than an error, so a profile-store hiccup can't break a call that only needs the engine or Prometheus.
The settings page composes one section widget per module and assembles itself from the widgets the server actually bundles — every widget id ending in :settings becomes a row. A module that isn't mounted simply has no section instead of producing a broken tab, and a custom module in a composed server contributes its section by that convention alone.
External systems
| System | Purpose | Default endpoint |
|---|---|---|
| Camunda 7 / CIB Seven | The BPM engine itself — process definitions, instances, tasks, incidents. | http://localhost:8410/engine-rest |
| OpenTelemetry collector | Receives OTLP from the engine; exports metrics to Prometheus. | :8431 (OTLP HTTP) |
| Prometheus | Time-series store for the process metrics; the analytics module's data source. | http://localhost:8460 |
| Grafana | Provisioned process-analytics dashboards over Prometheus. | http://localhost:8470 |
Data flow
- The MCP host calls a tool on the server (e.g.
analytics_element_bottleneck). - The server delegates to the matching module's plugin.
- The plugin calls the relevant system (REST for the engine, PromQL for Prometheus) and returns structured content.
- Widget tools also return a
widgetkey — the host renders the corresponding React component from the shared bundle and feeds it thedata.
Process metrics originate in the engine: the Kotlin plugin maps history events to Micrometer counters/histograms (100 % coverage, model-bounded labels), the Collector receives them over OTLP, and Prometheus stores them. Per-instance drill-down (search) is served by the engine REST history API, not the metrics.
Repository layout
| Path | Description |
|---|---|
apps/mcp-server-camunda7/ | The MCP server entry point and the widget bundle. |
packages/ | Reusable libraries — clients, MCP plugins, widget-shell. |
engine-plugins/ | Kotlin Micrometer plugins (process metrics). |
playground/ | Demo env: showcases, Compose stack, Fly.io deployment. |
For deeper detail, the root README.md keeps the full module table and tool list.