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

kuml.render

script (required, string — inline *.kuml.kts content)
format (optional, svg|png, default svg)
width (optional, integer, PNG only, default 1024)
theme (optional, string, default kuml — the kUML house theme; any other registered theme name, e.g. plain, elegant, playful, can be requested; an unknown name throws a structured error listing the registered themes)

SVG: one text content block with the SVG markup.
PNG: one image content block, base64-encoded, mimeType: image/png.

kuml.validate

script (required, string — inline script with constraint() declarations)

One text content block containing the JSON-serialized KumlValidationResult ({"violations": […​]}, one entry per failed OCL constraint).

kuml.list_elements

script (required, string)

One text content block: a human-readable summary listing every classifier, relationship, and other element with its stable id.

kuml.describe

script (required, string)
elementId (required, string — an ID from kuml.list_elements)

One text content block: the structured description of the element (attributes, operations, relationships, constraints depending on element type).

kuml.generate

script (required, string — must define a class diagram)
plugin (optional, string, default kotlin — codegen plugin id, e.g. kotlin, java, sql)
package (optional, string — target Kotlin/Java package name)

One text content block per generated file, prefixed with a // <filename> comment.

kuml.examples

language (required, string enum — uml|c4|sysml2|bpmn|blueprint)
diagramType (optional, string — per-language kebab-case token, e.g. class, sequence, composite-structure, bdd, container, service-blueprint, journey; omit to discover the diagram types available for language)

language only: one text content block, JSON {"language", "diagramTypes": [{"diagramType", "examples": [{"sourceNote", "description"}]}]} — no scripts, for cheap discovery.
language + diagramType: one text content block, JSON {"language", "diagramType", "examples": [{"sourceNote", "description", "script"}]} — the curated .kuml.kts example script(s) for that combination. Unknown language/diagramType throws a structured JSON error ({"error": {"code", "message", …​}}, surfaced as isError: true).

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

kuml://dsl/reference

The full DSL reference for all builder functions across UML, SysML v2, C4, and BPMN — the four handbook reference pages (uml-dsl.adoc, sysml2.adoc, c4-dsl.adoc, bpmn-dsl.adoc), concatenated.

text/asciidoc

kuml://dsl/examples

Curated example scripts covering every kUML diagram type, sourced from the vault’s example notes (same corpus that backs kuml-vault-examples-tests).

text/markdown

kuml://dsl/schema

Machine-readable JSON listing every MCP tool’s inputSchema, for structured autocompletion in MCP clients. MVP: derived directly from the same descriptors returned by tools/list, not a full reflective DSL-builder schema.

application/json

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.

Table 1. Live tools (operate on the open script)
Tool Purpose

kuml_desktop_get_script

Current script, its revision, whether it has unsaved changes and the file path.

kuml_desktop_set_script

Replaces the editor buffer. Requires the baseRevision the agent last read; on a mismatch the call fails with CONFLICT and the current revision, so a concurrent edit by you is never overwritten. The change is in-memory and undoable (one Undo step); it never saves a file.

kuml_desktop_render

Renders the open script and returns the SVG.

kuml_desktop_validate

Evaluates the script and returns diagnostics with line and column.

kuml_desktop_get_last_error

The last render error shown in the status bar, if any.

kuml_desktop_get_theme / kuml_desktop_set_theme

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.

Security
  • 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.

  • Host and Origin headers 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.

Table 2. Workspace tools
Tool Purpose

kuml_desktop_workspace_info

Name, root, mode (knowledge/engineering), whether the workspace is trusted, document counts by type, the open document and whether it has unsaved changes. Works in both modes.

kuml_desktop_workspace_list_documents

Paged list (offset, limit up to 2000, prefix) with path, type, title, number of kuml blocks, revision, dirty and size. In engineering mode it lists the .kuml.kts scripts.

kuml_desktop_workspace_get_document

Content and revision of one document (max 512 KiB). If the document is open with unsaved changes, you get that unsaved buffer plus dirty:true and the diskRevision.

kuml_desktop_workspace_set_document

Writes a document to disk through the same save gate as Ctrl+S. With baseRevision it updates an existing document, without it creates a new one (missing folders are created).

kuml_desktop_workspace_delete_document

Permanently deletes a document. Needs confirm:true; there is no trash.

kuml_desktop_workspace_validate

OKF findings for the documents on disk (one document or all). evaluate:true additionally runs the kuml blocks.

kuml_desktop_workspace_render_document

Renders one kuml block of a document as SVG (text) or PNG (image content); width 64-8192, optional theme.

kuml_desktop_workspace_render_all

Renders every block into a temporary location and reports rendered, skipped and failed blocks; nothing is written into the workspace.

kuml_desktop_workspace_open_document

Shows a document in the editor (edit:true for the raw editor).

kuml_desktop_workspace_reload

Re-scans the folder; onDirty = fail (default), keep or discard decides what happens to unsaved editor changes.

Revisions and unsaved changes
  • A revision is the first 16 hex characters of the SHA-256 of the document’s UTF-8 content. Pass the revision you last read as baseRevision; a mismatch fails with REVISION_CONFLICT and the currentRevision, and the file stays untouched.

  • set_document without baseRevision only creates: if the file exists it fails with REVISION_CONFLICT and reason:"EXISTS".

  • A document with unsaved changes in the editor is never overwritten or deleted: set_document and delete_document answer DOCUMENT_DIRTY, even with a matching revision. Save or discard in the app, or call kuml_desktop_workspace_reload with onDirty:"discard". open_document likewise 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_document saves to disk (unlike kuml_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.

Errors of the workspace tools

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).

Security of workspace mode
  • 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 .md files inside the workspace root can be read, written or deleted; error messages never contain the absolute root path.

  • Writing, deleting, rendering and evaluate:true need 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 while render_all runs (up to 120 s) other evaluating tools may answer BUSY.

  • 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_document and render_all render 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 validate command that kuml.validate mirrors

  • Code generation — the CLI-side kuml generate command that kuml.generate mirrors