monedula-sim-link docs menu
MCP tools
The four tools of the monedula-sim-link MCP server: build_playground_url, open_scenario, validate_actions and cluster_to_url, with inputs and outputs.
On this page
monedula-sim-link mcp serves four tools over the Model Context Protocol on stdio, and an MCP client shows each tool’s input schema to the agent. Use the MCP server shows how to register the server.
Every tool validates its input and returns a link only when it decodes back to the identical action list; a validation error names the offending field, separator or config path. Self-validation before printing lists the checks.
| Tool | Use it to |
|---|---|
build_playground_url |
Build a free-play session from a description of a cluster and a list of timed actions. |
open_scenario |
Get the deep link of a curated scenario. |
validate_actions |
Check what the playground would actually load from an actions string or a link. |
cluster_to_url |
Visualize a live cluster, snapshotted read-only. |
build_playground_url
A free-play session from a declarative description. Every input is optional, and an empty call opens the default cluster.
| Input | Meaning |
|---|---|
cluster |
The topology: single-dc (the default), active-passive, active-active, stretched-2-5 or stretched-3. |
shape |
The starting cluster, described below. Omit it for the topology’s default. Not supported for stretched-2-5. |
actions |
An ordered list of timed actions to replay on the starting cluster. It may be empty, to just open the cluster. |
The shape object has these fields:
| Field | Meaning |
|---|---|
brokers |
The total broker count of a single-zone topology (single-dc). |
brokersPerZone |
The broker count per zone, for multi-zone topologies, in topology order: [primary, dr] for active-passive, [west, east] for active-active, [dc-a, dc-b, dc-c] for stretched-3. |
topics |
The topics to create. A topic named orders shapes the primary topic, and every other topic becomes an extra topic, at most 6. Omit it to keep the default single orders topic. |
rackAware |
true turns on rack-aware placement (single-dc). |
Each entry of topics has name, partitions and replicationFactor, and optionally minInSyncReplicas and cleanupPolicy:
| Field | Meaning |
|---|---|
name |
Letters, digits, ., _ and -, at most 40 characters. An add_group can subscribe to a name with a ., but the actions with a topic field, such as produce and reassign_partition, cannot reference it, because their grammar reserves the dot, so prefer _ or -. |
partitions |
The partition count, at least 1, and at most the limit of the topology: 24 for single-dc, 6 for active-passive and active-active, and for stretched-3 the largest count from 1 to 6 that keeps the busiest broker within 3 partitions at the chosen replication factor. 9 brokers at RF 3 allow 6 partitions and reject 7, and 9 brokers at RF 4 allow 3 (the mapping reference). |
replicationFactor |
At least 1, and at most the broker count of the hosting zone set. |
minInSyncReplicas |
Optional min.insync.replicas, from 1 to the replication factor. Unset means the simulator’s default: 1 for an extra topic, which is Kafka’s own default, and max(1, replicationFactor-1) for orders. |
cleanupPolicy |
Optional: delete, compact or compact,delete. Unset means delete. |
Leaving minInSyncReplicas or cleanupPolicy out is safe: the tool writes the defaults into an extra topic’s segment of the size parameter. The segment for orders carries only a value that differs from its default, so minInSyncReplicas: 2 at replication factor 3, or cleanupPolicy: delete, adds nothing to its size, which stays 6x4x3 for 6 brokers and 4 partitions. Before 0.11.0 an extra topic with no minInSyncReplicas was written with an empty field, and the simulator dropped the whole topic from the session.
Diskless topics, from the KIP-1163 draft, add three inputs:
clusteralso takesdiskless-3az: one cluster whose three availability zones are racks, with rack-aware placement and a disklessclickstopic next toorders. Its default is 6 brokers and 3 partitions with replication factor 3;shape.brokerssets at most 9 brokers, and a topic has at most 6 partitions.- A
topicsentry may setdisklessEnable: true, except with acompactorcompact,deletecleanup policy or onactive-passiveandactive-active. shape.disklessTimingoverrides the diskless timing of the session withcommitIntervalMs, thediskless.append.commit.interval.ms, anduploadMs, the object-storage upload latency. Both are optional, in milliseconds from 100 to 5000. The override needs a diskless topic, fromdisklessEnableor thediskless-3azpreset; without one, the call fails.
The shape describes no producers or consumer groups, so it cannot set the client.rack that the simulator’s size format can give each initial producer and consumer group.
Every action has a kind and an at, the simulation time in milliseconds at which it fires, 0 or more. Actions are replayed sorted by at, and spacing them about 1000 ms apart lets the playground animate them one at a time. The other fields depend on the kind:
| Kind | Required fields | Optional fields |
|---|---|---|
produce |
producerId, topic |
key, and either value or tombstone: true. The two are mutually exclusive. |
kill_broker |
brokerId |
graceful: true for a graceful shutdown, otherwise a hard kill. |
restart_broker |
brokerId |
|
add_broker |
brokerId |
rack: pin the broker’s placement to a rack. |
remove_broker |
brokerId |
|
add_producer |
producerId |
topic: pin the producer to a topic. |
remove_producer |
producerId |
|
add_group |
groupId, topics (at least one; the first is the primary) |
autoOffsetReset: earliest, latest or none. Omitted, a new group starts at the high watermark. |
remove_group |
groupId |
Removes a whole consumer group with its members. |
add_consumer |
groupId, memberId |
|
add_share_group |
groupId, topic |
members: explicit member ids. |
config_change |
path, and either value or clear: true |
clear is allowed only for the paths the URL format marks as clearable. |
reassign_partition |
topic, partition, replicas (the full target list of broker ids) |
|
change_replication_factor |
topic, targetReplicationFactor |
|
set_replica_speed |
brokerId, slow |
slow: true throttles the broker’s follower replication, false restores full speed. |
tier_offload |
topic, partition, toOffset |
Offloads the local log up to that offset, exclusive, to remote storage. |
Broker ids are broker-1 to broker-N. A default free-play cluster starts with the producer p1, the consumer group c1 and the primary topic orders. A config_change path is one the URL format whitelists, such as producers.p1.config.acks or topics.orders.retentionMs.
The tool does not write the optional settings field of add_group. An add_group with a settings object fails with an error that names the field: settings only appears in the output of validate_actions, which decodes it.
Example input:
{
"shape": {
"brokers": 6,
"topics": [
{ "name": "orders", "partitions": 4, "replicationFactor": 3 },
{ "name": "payments", "partitions": 6, "replicationFactor": 3,
"minInSyncReplicas": 2, "cleanupPolicy": "delete" }
]
},
"actions": [
{ "kind": "add_group", "at": 1000, "groupId": "analytics", "topics": ["orders"] },
{ "kind": "produce", "at": 2000, "producerId": "p1", "topic": "orders", "key": "O1" },
{ "kind": "config_change", "at": 3000, "path": "producers.p1.config.acks", "value": "all" },
{ "kind": "kill_broker", "at": 8500, "brokerId": "broker-2" },
{ "kind": "restart_broker", "at": 22000, "brokerId": "broker-2" }
]
}
It returns the link and the fields below:
https://monedula.dev/kafka-simulator/playground?scenario=free&actions=...&size=6x4x3xpayments~6~3~2~d~~~~
| Output | Meaning |
|---|---|
url |
The full playground link. The actions are percent-encoded, and compressed automatically past the plain threshold. |
cluster, size |
The cluster and size parameters the link carries, if any. |
actionCount |
The number of encoded actions. |
plainLength |
The length of the plain action string. The compressed form starts past 1500 characters. |
compressed |
true when the link carries the compressed ~1 form. |
decodedActions |
The actions decoded back out of the generated link. Compare them with your input. |
notes |
Non-fatal adjustments or hints. |
open_scenario
The deep link of a curated scenario, paused on its scripted walkthrough.
| Input | Meaning |
|---|---|
scenarioId |
The scenario id in dotted form, such as 00.1.3. |
{ "scenarioId": "00.1.3" }
https://monedula.dev/kafka-simulator/playground?scenario=00.1.3
The output has url, scenarioId and an optional note.
validate_actions
What the playground would actually load from an actions string. The playground drops entries it cannot parse without saying so, and this tool reports them.
| Input | Meaning |
|---|---|
actions |
The raw actions value: the plain grammar (K:broker-2@8500,...), the compressed ~1 form, a percent-encoded value, or a full playground link, from which the actions parameter is extracted. |
{ "actions": "K:broker-2@8500,NOPE,AG:analytics:orders@1000" }
The result has 2 surviving entries, sorted by at, and one dropped entry: NOPE, with the reason missing @<at>.
| Output | Meaning |
|---|---|
entries |
The decoded actions the playground would load, sorted by at, in the action shape of build_playground_url. An add_group that carries the settings field also has a settings object, described below. |
keptCount |
The number of actions that survive, after the cap. |
droppedCount, dropped |
The entries the playground would silently drop, each with its entry text and a reason. |
truncatedCount |
Decodable actions beyond the cap that the playground would discard. The earliest 2000 win. |
compressed, plainLength |
Whether the value used the compressed form, and the length of the plain string. |
compressionThreshold |
1500: plain strings longer than this are emitted compressed. |
maxUrlActions |
2000: the action cap. |
canonicalEncoding |
The surviving actions re-encoded canonically, sorted and plain. Absent when no action survives. Also absent when the actions decode but the tool’s generator rules refuse to write them back, such as a produce to a dotted region topic like west.orders; a note then says why. |
notes |
Non-fatal hints. |
Group settings
An add_group can carry the group’s settings in an optional fourth field, which the playground URL format specifies. The playground writes it when kafka-consumer-groups --reset-offsets rebuilds a configured group, and when it pins a new group on an active-active cluster to its region. validate_actions decodes the field and echoes it as a settings object on the entry. The field is strict, like the playground’s decoder: an empty field, an unknown or repeated setting, or a value outside its domain drops the whole add_group, which then appears in dropped.
{ "actions": "AG:g1:west.orders~east.orders:e:region=west~assignor=range~principal=User:CN=west~_OU=apps@9000" }
The entry it returns:
{
"kind": "add_group",
"at": 9000,
"groupId": "g1",
"topics": ["west.orders", "east.orders"],
"autoOffsetReset": "earliest",
"settings": { "region": "west", "assignor": "range", "principal": "User:CN=west,OU=apps" }
}
The settings object has a field only for each setting the group carries, with its value unescaped, so the ~_ in the link is a , in principal. An add_group without the settings field has no settings object.
| Field | Meaning |
|---|---|
region |
The region the group is pinned to. It reads only that region’s copies of its topics. |
assignor |
partition.assignment.strategy: range, roundrobin, sticky or cooperative-sticky. |
isolationLevel |
isolation.level: read_committed or read_uncommitted. |
autoCommit |
enable.auto.commit, true or false. |
pollMs |
The poll interval in milliseconds, any integer. 0 or less stops the poll loop. |
maxPollRecords |
max.poll.records, at least 1. |
maxPollIntervalMs |
max.poll.interval.ms, at least 1. |
rack |
The group’s client.rack. |
principal |
The principal the group authenticates as. |
canonicalEncoding writes the settings back in the order of this table, whatever order the link used. The settings object is output only: build_playground_url refuses it as input and never writes the field.
cluster_to_url
The live-cluster counterpart to build_playground_url. It connects read-only to a real Kafka cluster, through the same path as connect, and returns the link that visualizes it. It sends the same read requests, plus DescribeProducers only when producers is true: the read-only guarantee lists them.
| Input | Meaning |
|---|---|
brokers |
The bootstrap brokers as host:port, at least one. Required. |
topics |
Exact topic names to include. It fails if one is missing, and it is the only way to include internal topics. Listed topics are always kept when the selection is over the limit of 6 extra topics. |
topicsRegex |
Additionally include non-internal topics that match this Go regular expression. |
tls |
Connect over TLS. Implied when caCert, clientCert or clientKey is set. |
caCert, clientCert, clientKey |
PEM file paths, resolved on the machine where the MCP server runs, not on the client’s. clientCert and clientKey go together. |
saslMechanism, username, password |
SASL: plain, scram-sha-256 or scram-sha-512, which requires username. Prefer the MONEDULA_SIM_LINK_PASSWORD environment variable on the server’s machine to password; the variable is the fallback when password is empty. |
timeoutSeconds |
The overall snapshot timeout in seconds. The default is 15. |
maxRecordsPerPartition |
The cap on synthetic trailing records per partition. The default is 8. |
clamp |
Trim a cluster over 10 brokers or 24 partitions per topic, or a topics list of more than 6 topics besides orders, deterministically, with warnings. Without it, such a selection fails with the list of violations. More topics or consumer groups than the simulator shows never fail: the most eventful are kept, with a warning. |
producers |
Also read the idempotent and transactional producers with DescribeProducers (Kafka 2.8 or newer), and add up to 4 of them to the link: the ones whose last write to a shown topic is in the last 15 minutes, ranked by the number of partitions they wrote to, each pinned to the topic it wrote to most and named pid-<producerId>. Off by default, because the request needs READ on every selected topic; with it off, no DescribeProducers request is sent. A producer that is neither idempotent nor transactional never appears, and one with an open transaction is shown as an idempotent producer. A refused or unsupported read is a warning, never a failure. Unlike the CLI, the tool has no input for the 15-minute window. |
Omit topics and topicsRegex to include every non-internal topic.
{ "brokers": ["broker1:9092", "broker2:9092"], "topics": ["orders", "payments"] }
| Output | Meaning |
|---|---|
url, cluster, size, actionCount, plainLength, compressed |
As for build_playground_url. The size parameter carries each topic’s real min.insync.replicas, cleanup.policy, retention.ms and retention.bytes. |
decodedActions |
The actions decoded back out of the link, including add_group and add_consumer for every mapped consumer group and its members, up to 4 members per group, and add_producer for every producer when producers is true. It starts with remove_producer for p1 and remove_group for c1, the template producer and group of free play. |
brokerNames, topicNames, groupNames |
The real-to-simulator tables, as lists of real and simulator pairs. A group with no committed offset on a shown topic is absent from groupNames, and so is a group outside the 4 kept. |
warnings |
Unreadable offsets, approximations, the topics and groups kept from a larger selection, any clamps applied and, with producers, an open transaction, a producer read that was refused or unsupported, and a window with no producer in it. |
notes |
Non-fatal hints. |
A selection that matches nothing is an error with an explanation.
Related
- Handle large clusters describes the free-play limits, which topics and groups are kept, and what
clamptrims. - The mapping reference explains the actions that
cluster_to_urlemits. - The
mcpcommand reference lists the flag of the server.