Authoring MCP Interface
kUML exposes its authoring/rendering pipeline via the Model Context Protocol (MCP). This allows any MCP-compatible client — Claude Desktop, Cursor, Continue, or your own agent — to render, validate, introspect, and generate code from kUML DSL scripts, without writing a single line of integration code.
The MCP server is the standalone kuml-mcp binary (module kuml-mcp, application
plugin, mainClass = dev.kuml.mcp.MainKt). It is not a subcommand of the kuml CLI.
Build it via:
./gradlew clean :kuml-mcp:installDist
The binary is installed at:
kuml-mcp/build/install/kuml-mcp/bin/kuml-mcp
Claude Desktop (and other MCP clients) connect to this process over stdio. kUML Desktop offers an HTTP variant for the open script, see Driving kUML Desktop over MCP.
The six authoring tools
| Tool | Parameters | Return shape |
|---|---|---|
|
|
SVG: one |
|
|
One |
|
|
One |
|
|
One |
|
|
One |
|
|
|
The first five tools take the kUML DSL script inline as the script argument (not a file
path) — the server writes it to a temporary *.kuml.kts file, evaluates it through the
same KumlScriptHost / DiagramExtractor pipeline used by kuml-cli, and cleans up the
temporary file afterwards. KumlScriptGuard.validate() runs first to reject scripts that
attempt disallowed operations (this is the standard sandboxing already applied to
kuml render/kuml validate on the CLI). kuml.examples does not evaluate any script —
it only serves bundled, pre-curated example text — and needs no sandboxing.
Minimal example: kuml.render
Request:
{
"method": "tools/call",
"params": {
"name": "kuml.render",
"arguments": {
"script": "diagram(\"Demo\") { umlClass(\"User\") { attribute(\"id\", \"String\") } }",
"format": "svg"
}
}
}
Response (abridged):
{
"content": [
{ "type": "text", "text": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...>...</svg>" }
]
}
Requesting "format": "png" instead returns a single image content block with
base64-encoded PNG bytes:
{
"content": [
{ "type": "image", "data": "iVBORw0KGgoAAAANSUhEUgAA...", "mimeType": "image/png" }
]
}
MCP resources: auto-loading DSL knowledge
In addition to the six tools above, kuml-mcp exposes the resources capability
(capabilities.resources in the initialize response, protocol version 2024-11-05).
Resources are a read-only teaching channel — where the tools let an agent render and
validate scripts, resources let an MCP client auto-load reference material about the DSL
itself, without any tool call round-trip.
| URI | Content | MIME type |
|---|---|---|
|
The full DSL reference for all builder functions across UML, SysML v2, C4, and BPMN —
the four handbook reference pages ( |
|
|
Curated example scripts covering every kUML diagram type, sourced from the vault’s
example notes (same corpus that backs |
|
|
Machine-readable JSON listing every MCP tool’s |
|
Granular per-diagram-type example resources (V3.3.1)
In addition to the aggregate kuml://dsl/examples resource above (kept unchanged for
backward compatibility), kuml-mcp exposes one resource per distinct (language,
diagramType) combination in the curated example catalog — the same catalog backing the
kuml.examples tool:
kuml://dsl/examples/<language>/<diagramType>
For example kuml://dsl/examples/uml/class, kuml://dsl/examples/sysml2/bdd,
kuml://dsl/examples/c4/container, kuml://dsl/examples/bpmn/choreography, or
kuml://dsl/examples/blueprint/journey. All are listed in resources/list with
mimeType: text/x-kotlin. The content is the raw .kuml.kts script(s) for that diagram
type — concatenated with a // ── <source note> ── header per script when more than one
curated example matches (e.g. uml/component, uml/profile, bpmn/process). Requesting
an unknown (language, diagramType) combination returns a standard JSON-RPC error (code
-32602), same as the aggregate resource.
{
"method": "resources/read",
"params": { "uri": "kuml://dsl/examples/uml/class" }
}
This is the resources-capability equivalent of calling the kuml.examples tool with both
language and diagramType set — use whichever surface your MCP client supports better;
clients without tool-call support can still fetch a single diagram type via
resources/read.
Client protocol, mirroring tools/list / tools/call:
{ "method": "resources/list" }
{
"method": "resources/read",
"params": { "uri": "kuml://dsl/reference" }
}
The response shape for resources/read is { "contents": [{ "uri", "mimeType", "text" }] }
— one entry per requested URI. Requesting an unknown uri returns a standard JSON-RPC
error (code -32602), not a crash.
All resources — the three aggregate ones and the granular per-diagram-type example
resources — are static: bundled at build time from the handbook pages and the
vault-examples classpath resources (:kuml-mcp:processResources), so they behave
identically in the packaged binary and in tests — no live filesystem or vault access at
runtime. Not yet supported: resources/subscribe / live updates, and prompts capability
(prompt templates) — both out of scope for this resources wave.
Claude Desktop configuration
Add the following block to your Claude Desktop claude_desktop_config.json — on macOS
typically at ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"kuml": {
"command": "/path/to/kuml-mcp/bin/kuml-mcp"
}
}
}
Once kuml-mcp is bundled into the Homebrew runtimeZip distribution, the command can
be simplified to just "kuml-mcp" (resolved via PATH after brew install kuml).
Restart Claude Desktop after editing the config. Both authoring tools (this page) and
Behaviour-Runtime tools (Behaviour-Runtime MCP) are exposed by the
same kuml-mcp process — there is no separate binary or config entry per tool group.
Driving kUML Desktop over MCP
kUML Desktop can host its own MCP server, so an agent works on the script that is open in the
running app and sees the live preview. It is off by default: enable it under
Tools ▸ MCP server…. The dialog shows the status and port and copies a ready-made
claude mcp add command; the bearer token is never displayed.
claude mcp add --transport http kuml-desktop http://127.0.0.1:<port>/mcp \
--header "Authorization: Bearer <token>"
The server speaks Streamable HTTP (POST /mcp only, protocol version 2025-03-26) and binds to
127.0.0.1 exclusively. The port is remembered across restarts, so the command stays valid. If that port is taken at startup, the server picks a free one and rotates the token; the dialog says so and you copy the new command.
| Tool | Purpose |
|---|---|
|
Current script, its |
|
Replaces the editor buffer. Requires the |
|
Renders the open script and returns the SVG. |
|
Evaluates the script and returns diagnostics with line and column. |
|
The last render error shown in the status bar, if any. |
|
Reads or changes the preview theme. |
The four stateless authoring tools (kuml.describe, kuml.examples, kuml.generate,
kuml.list_elements) are available on the same endpoint.
-
The token is 256 bits from
SecureRandom, stored in its own owner-only file (mcp-token; POSIX permissions, best effort on Windows), never in the settings file, and compared in constant time. -
HostandOriginheaders are checked (DNS-rebinding defence); request bodies are limited to 1 MiB; requests have timeouts; only one script evaluation runs at a time. -
Anyone holding the token can execute Kotlin code in the Desktop process — script evaluation is the feature. Treat the token like a password and keep the server off when you do not need it.
Workspace mode
When an OKF workspace is open (File ▸ Open Workspace…), the script tools above answer
WORKSPACE_MODE_UNSUPPORTED (with the list of workspace tools) and the kuml_desktop_workspace_*
tools take over. They read and edit the Markdown documents of the workspace on disk, so an agent can
browse, write, validate and render a knowledge workspace while you watch it in the app. Paths are
always relative to the workspace root and use /. Without an open workspace they answer
NO_WORKSPACE_OPEN.
| Tool | Purpose |
|---|---|
|
Name, root, mode ( |
|
Paged list ( |
|
Content and |
|
Writes a document to disk through the same save gate as Ctrl+S. With |
|
Permanently deletes a document. Needs |
|
OKF findings for the documents on disk (one document or all). |
|
Renders one kuml block of a document as SVG (text) or PNG (image content); |
|
Renders every block into a temporary location and reports rendered, skipped and failed blocks; nothing is written into the workspace. |
|
Shows a document in the editor ( |
|
Re-scans the folder; |
-
A
revisionis the first 16 hex characters of the SHA-256 of the document’s UTF-8 content. Pass the revision you last read asbaseRevision; a mismatch fails withREVISION_CONFLICTand thecurrentRevision, and the file stays untouched. -
set_documentwithoutbaseRevisiononly creates: if the file exists it fails withREVISION_CONFLICTandreason:"EXISTS". -
A document with unsaved changes in the editor is never overwritten or deleted:
set_documentanddelete_documentanswerDOCUMENT_DIRTY, even with a matching revision. Save or discard in the app, or callkuml_desktop_workspace_reloadwithonDirty:"discard".open_documentlikewise refuses to replace a different dirty document. -
If a file changes on disk while you have unsaved edits, the editor shows a banner offering to reload from disk or keep your version; your edits are never overwritten silently. Changes on disk (including the agent’s) appear in the tree within a couple of seconds.
-
set_documentsaves to disk (unlikekuml_desktop_set_script, which only changes the buffer), so there is no Undo; a notice in the status area tells you what was written or deleted.
INVALID_PATH (with a reason such as PARENT_SEGMENT, ABSOLUTE_PATH, SYMLINK_ESCAPE, NOT_MARKDOWN, EXCLUDED), DOCUMENT_NOT_FOUND (with a suggestion when a similar path exists), DOCUMENT_TOO_LARGE, CONTENT_TOO_LARGE, REVISION_CONFLICT, DOCUMENT_DIRTY, SAVE_BLOCKED (with the blocking findings), CONFIRMATION_REQUIRED, WORKSPACE_NOT_TRUSTED, BLOCK_NOT_FOUND, UNKNOWN_THEME, BUSY, RENDER_TIMEOUT, SVG_TOO_LARGE/PNG_TOO_LARGE, TOO_MANY_BLOCKS, WRITE_FAILED, UI_BUSY and OPERATION_TIMEOUT (the operation may still have been applied; check with list_documents).
-
Paths:
.., absolute paths, UNC paths, drive letters and~are rejected before the file system is touched; then containment, symbolic links, hidden and excluded folders are checked again against the real file system. Only.mdfiles inside the workspace root can be read, written or deleted; error messages never contain the absolute root path. -
Writing, deleting, rendering and
evaluate:trueneed a trusted workspace (the trust you confirm when opening it). Rendering and evaluating execute the kuml blocks of the documents, exactly like the CLI; they share the single evaluation slot, so whilerender_allruns (up to 120 s) other evaluating tools may answerBUSY. -
Limits: 512 KiB per document, 4 MiB per rendered image, 500 blocks for
render_all, path length 1024. Logs contain tool names and error codes only, never document content, tokens or absolute paths. -
render_documentandrender_allrender the documents as saved on disk, not unsaved editor buffers.
See also
-
Behaviour-Runtime MCP — the other 5 tools (
kuml.run.*), for driving state machines and activity diagrams interactively -
Validation — the CLI-side
kuml validatecommand thatkuml.validatemirrors -
Code generation — the CLI-side
kuml generatecommand thatkuml.generatemirrors