Behaviour-Runtime MCP Interface
kUML exposes its Behaviour-Runtime via the Model Context Protocol (MCP). This allows any MCP-compatible client — Claude Desktop, VS Code Copilot, or your own agent — to run kUML state machines and activity diagrams interactively, without writing a single line of integration code.
The MCP server is a standalone binary, kuml-mcp, built from the kuml-mcp module
(application plugin, mainClass = dev.kuml.mcp.MainKt). It is not a subcommand of
the kuml CLI — there is no kuml ai mcp-stdio command. Start it directly:
/path/to/kuml-mcp/bin/kuml-mcp
After ./gradlew :kuml-mcp:installDist the binary is at
kuml-mcp/build/install/kuml-mcp/bin/kuml-mcp. Once kuml-mcp is bundled into
runtimeZip (see the distribution changelog), brew install kuml puts it on PATH
alongside kuml.
Claude Desktop (and other clients) connect to this process over stdio.
The five runtime tools
| Tool | Purpose | Arguments | Return shape |
|---|---|---|---|
|
Start a new runtime session from a |
|
|
|
Send a named event to a running state-machine session. |
|
|
|
Non-destructively inspect a session: active states, variables, trace tail, step count. |
|
|
|
Patch session variables and/or teleport the state machine to a named state. |
|
|
|
Terminate the session and return the full execution trace. |
|
|
sessionId is assigned by kuml.run.start (format rs-<hex>). All subsequent calls on
the same session pass this ID. The server keeps sessions alive in memory until
kuml.run.stop is called or the session expires (30 minutes of inactivity, configurable).
Claude Desktop configuration
Add the following block to your Claude Desktop claude_desktop_config.json (typically at
~/Library/Application\ Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"kuml": {
"command": "/path/to/kuml-mcp/bin/kuml-mcp"
}
}
}
Restart Claude Desktop after editing the config. You should see kuml appear in the
toolbox icon in the composer. kUML is now available as a first-class tool for every
Claude Desktop conversation.
Full walkthrough: Pepela thermostat in Claude Desktop
This walkthrough drives the Pepela thermostat model through its full heating → idle → cooling → eco → idle → off cycle via MCP tool calls.
Step 1: Start the machine
// kuml.run.start
{
"source": "kuml-examples/src/main/kotlin/dev/kuml/examples/pepela/pepela-thermostat-stm.kuml.kts"
}
Response:
{
"sessionId": "rs-a1b2c3d4",
"kind": "stm",
"activeStates": ["Off"],
"trace": []
}
Step 2: Fire powerOn
// kuml.run.event
{
"sessionId": "rs-a1b2c3d4",
"event": "powerOn"
}
Response:
{
"fired": ["Off→Idle"],
"activeStates": ["Idle"],
"traceDelta": [
{ "type": "EventReceived", "event": { "name": "powerOn" } },
{ "type": "Transitioned", "trigger": "powerOn", "from": "Off", "to": "Idle" },
{ "type": "Entered", "vertex": "Idle" }
]
}
Step 3: Fire a tick event to trigger heating
// kuml.run.event
{
"sessionId": "rs-a1b2c3d4",
"event": "tick",
"payload": { "temperature": 16, "targetTemperature": 21 }
}
Response: activeStates: ["Heating"] — the guard event.temperature < event.targetTemperature - 1
evaluated to true (16 < 20).
Step 4: Snapshot without firing an event
// kuml.run.snapshot
{ "sessionId": "rs-a1b2c3d4" }
Response:
{
"activeStates": ["Heating"],
"variables": {},
"traceTail": [ /* last 20 entries */ ],
"stepCount": 2
}
Step 5: Complete the cycle and stop
Continue firing tick events with rising temperature to drive the machine through Idle →
Cooling → Idle → Eco → Idle → Off, then stop:
// kuml.run.stop
{ "sessionId": "rs-a1b2c3d4" }
Response:
{
"ok": true,
"totalSteps": 6,
"traceLength": 24,
"trace": [ /* full trace */ ]
}
kuml.run.stop always returns "ok": true on a valid session ID; there is no
separate terminated/non-terminated distinction in the response — inspect activeStates
in the preceding kuml.run.snapshot/kuml.run.event calls to see whether the machine
reached a final state.
|
Payload schema for guards
Guard expressions in the DSL use event.<key> to read payload fields. The MCP payload
object maps directly to these keys — what you pass in kuml.run.event is what OCL sees:
{
"sessionId": "rs-a1b2c3d4",
"event": "tick",
"payload": {
"temperature": 19,
"targetTemperature": 21,
"humidity": 55
}
}
A guard of event.temperature < event.targetTemperature - 1 receives 19 < 20 = true.
Using kuml.run.patch for guard testing
kuml.run.patch merges variables into the session’s context and/or forces the state
machine to jump to a named state. This is intended for automated tests that need to
reach a specific state without constructing a full event sequence:
// Force the overrideEco flag so the eco-mode guard fires immediately
{ "sessionId": "rs-a1b2c3d4", "variables": { "overrideEco": true } }
// Teleport the machine directly to the "Heating" state
{ "sessionId": "rs-a1b2c3d4", "forceState": "Heating" }
See also
-
State-Machine Simulation — batch simulation via
kuml simulate, trace format -
Authoring MCP —
kuml.render/validate/list_elements/describe/generate/examplestools