@eventmodelers/cli 0.0.39 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +116 -69
  2. package/cli.js +98 -32
  3. package/package.json +4 -2
  4. package/shared/build-kit/lib/adapters/pocketbase-realtime-adapter.js +29 -0
  5. package/shared/build-kit/lib/adapters/realtime-adapter.js +27 -0
  6. package/shared/build-kit/lib/adapters/supabase-realtime-adapter.js +24 -0
  7. package/shared/build-kit/lib/ralph.js +30 -31
  8. package/shared/build-kit/package.json +3 -1
  9. package/shared/build-kit/ralph-claude.js +63 -5
  10. package/shared/skills/connect/SKILL.md +54 -5
  11. package/shared/skills/learn-eventmodelers-api/SKILL.md +88 -3
  12. package/shared/skills/load-slice/SKILL.md +16 -0
  13. package/shared/skills/update-slice-status/SKILL.md +15 -5
  14. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +86 -0
  15. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +29 -1
  16. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +47 -5
  17. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +73 -59
  18. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +1 -1
  19. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +100 -14
  20. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +22 -2
  21. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +1 -1
  22. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +30 -4
  23. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +72 -12
  24. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +66 -9
  25. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +1 -1
  26. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +29 -5
  27. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +9 -1
  28. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +37 -3
  29. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +114 -27
  30. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +1 -1
  31. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +15 -3
  32. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +13 -2
  33. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +45 -7
  34. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +28 -1
  35. package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +45 -19
  36. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +172 -6
  37. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +104 -25
  38. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +31 -17
  39. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +111 -12
  40. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +10 -1
  41. package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +24 -5
  42. package/stacks/modeling-kit/templates/kit/CLAUDE.md +12 -4
  43. package/stacks/node/templates/.claude/skills/build-state-view/SKILL.md +13 -7
  44. package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +13 -7
@@ -5,11 +5,11 @@
5
5
  // onTask(prompt) — called when tasks.json has entries
6
6
  // onPlannedSlice(prompt) — called when .slices/ has a "Planned" entry (omit to skip)
7
7
 
8
- import { createClient } from '@supabase/supabase-js';
9
8
  import { readFileSync, mkdirSync, writeFileSync, existsSync, readdirSync } from 'fs';
10
9
  import { join, dirname } from 'path';
11
10
  import { homedir } from 'os';
12
11
  import { randomUUID } from 'crypto';
12
+ import { createRealtimeAdapter } from './adapters/realtime-adapter.js';
13
13
 
14
14
  // ── HTTP helpers ──────────────────────────────────────────────────────────────
15
15
 
@@ -242,6 +242,20 @@ async function writeTask(payload, kitDir) {
242
242
  console.log(`[agent] Task written — slice="${payload.sliceTitle}" status="${payload.sliceStatus}"`);
243
243
  }
244
244
 
245
+ async function handleSliceChanged(payload, cfg, kitDir, queueAllStatuses) {
246
+ console.log(`[agent] slice:changed — slice="${payload.sliceTitle}" status="${payload.sliceStatus}"`);
247
+ await retryOn401('fetchAndPersistSlices', () => fetchAndPersistSlices(cfg, kitDir)).catch((err) =>
248
+ console.error('[agent] Slice persist error:', err),
249
+ );
250
+ // Planned slices are handled by onPlannedSlice directly — no task needed.
251
+ // queueAllStatuses opts out of that split entirely (e.g. bridge has no
252
+ // onPlannedSlice consumer, so a lingering Planned slice would otherwise
253
+ // never naturally clear its own trigger — see lib/ralph.js callers).
254
+ if (queueAllStatuses || (payload.sliceStatus || '').toLowerCase() !== 'planned') {
255
+ await writeTask(payload, kitDir).catch((err) => console.error('[agent] writeTask error:', err));
256
+ }
257
+ }
258
+
245
259
  async function startRealtimeAgent(cfg, kitDir, { agentType = 'BUILD', queueAllStatuses = false } = {}) {
246
260
  let realtimeToken = await retryOn401('getRealtimeToken', () => getRealtimeToken(cfg));
247
261
 
@@ -249,41 +263,26 @@ async function startRealtimeAgent(cfg, kitDir, { agentType = 'BUILD', queueAllSt
249
263
  console.error('[agent] Initial slice fetch error:', err),
250
264
  );
251
265
 
252
- const supabase = createClient(cfg.supabaseUrl, cfg.supabaseAnonKey, {
253
- realtime: { params: { apikey: cfg.supabaseAnonKey } },
254
- });
255
- await supabase.realtime.setAuth(realtimeToken);
256
-
257
266
  const channelName = `board:${cfg.boardId}-slicechanged`;
258
-
259
- supabase
260
- .channel(channelName, { config: { private: true } })
261
- .on('broadcast', { event: 'message' }, (msg) => {
262
- if (msg.payload === 'Exit') {
263
- console.log('[agent] Received "Exit" — shutting down');
264
- process.exit(0);
265
- }
266
- })
267
- .on('broadcast', { event: 'slice:changed' }, async (msg) => {
268
- const payload = msg.payload;
269
- console.log(`[agent] slice:changed — slice="${payload.sliceTitle}" status="${payload.sliceStatus}"`);
270
- await retryOn401('fetchAndPersistSlices', () => fetchAndPersistSlices(cfg, kitDir)).catch((err) =>
271
- console.error('[agent] Slice persist error:', err),
272
- );
273
- // Planned slices are handled by onPlannedSlice directly — no task needed.
274
- // queueAllStatuses opts out of that split entirely (e.g. bridge has no
275
- // onPlannedSlice consumer, so a lingering Planned slice would otherwise
276
- // never naturally clear its own trigger — see lib/ralph.js callers).
277
- if (queueAllStatuses || (payload.sliceStatus || '').toLowerCase() !== 'planned') {
278
- await writeTask(payload, kitDir).catch((err) => console.error('[agent] writeTask error:', err));
279
- }
280
- })
281
- .subscribe((status) => console.log(`[agent] Channel "${channelName}": ${status}`));
267
+ const realtime = await createRealtimeAdapter(cfg, realtimeToken);
268
+ realtime.subscribe(
269
+ channelName,
270
+ {
271
+ message: (payload) => {
272
+ if (payload === 'Exit') {
273
+ console.log('[agent] Received "Exit" — shutting down');
274
+ process.exit(0);
275
+ }
276
+ },
277
+ 'slice:changed': (payload) => handleSliceChanged(payload, cfg, kitDir, queueAllStatuses),
278
+ },
279
+ (status) => console.log(`[agent] Channel "${channelName}": ${status}`),
280
+ );
282
281
 
283
282
  setInterval(async () => {
284
283
  try {
285
284
  realtimeToken = await retryOn401('getRealtimeToken (refresh)', () => getRealtimeToken(cfg));
286
- supabase.realtime.setAuth(realtimeToken);
285
+ await realtime.setAuth(realtimeToken);
287
286
  console.log('[agent] Token refreshed');
288
287
  } catch (err) {
289
288
  console.error('[agent] Token refresh failed:', err);
@@ -6,6 +6,8 @@
6
6
  "start": "node ralph.js"
7
7
  },
8
8
  "dependencies": {
9
- "@supabase/supabase-js": "^2.0.0"
9
+ "@supabase/supabase-js": "^2.0.0",
10
+ "eventsource": "^3.0.7",
11
+ "pocketbase": "^0.27.0"
10
12
  }
11
13
  }
@@ -15,19 +15,77 @@ const inlineHeader = cfg.boardId
15
15
  ? `board=${cfg.boardId} token=${cfg.token} org=${cfg.organizationId} baseUrl=${cfg.baseUrl}\n\n`
16
16
  : '';
17
17
 
18
- const claudeArgs = ['--dangerously-skip-permissions'];
18
+ // --verbose here (set via `eventmodelers run --verbose`, passed down as RALPH_VERBOSE)
19
+ // logs full tool input and assistant reasoning text; the default (condensed) mode logs
20
+ // only the high-level step — a skill name, or a bare tool name — mirroring `run --modeling`'s
21
+ // own two-tier logging in cli.js.
22
+ const verbose = process.env.RALPH_VERBOSE === '1';
23
+
24
+ const claudeArgs = ['--dangerously-skip-permissions', '--output-format', 'stream-json', '--verbose'];
19
25
  if (cfg.model) claudeArgs.push('--model', cfg.model);
20
- const claudeEnv = cfg.anthropicBaseUrl
21
- ? { ...process.env, ANTHROPIC_BASE_URL: cfg.anthropicBaseUrl }
22
- : process.env;
26
+ const claudeEnv = {
27
+ ...process.env,
28
+ ...(cfg.anthropicBaseUrl ? { ANTHROPIC_BASE_URL: cfg.anthropicBaseUrl } : {}),
29
+ ...(cfg.token ? { EVENTMODELERS_TOKEN: cfg.token } : {}),
30
+ };
31
+
32
+ // Collapses whitespace/newlines to a single line and truncates past `max` chars — a long
33
+ // multi-line curl command wrapped across many terminal lines is just as unreadable as no
34
+ // detail at all. Keeps one tool call to one log line.
35
+ function oneLine(s, max) {
36
+ const collapsed = String(s ?? '').replace(/\s+/g, ' ').trim();
37
+ return collapsed.length > max ? `${collapsed.slice(0, max)}…` : collapsed;
38
+ }
39
+
40
+ function describeToolUse(block) {
41
+ const input = block.input ?? {};
42
+ switch (block.name) {
43
+ case 'Bash': return `Bash: ${oneLine(input.command, 100)}`;
44
+ case 'Skill': return `Skill: ${input.skill}${input.args ? ` ${oneLine(input.args, 60)}` : ''}`;
45
+ case 'Read': return `Read: ${input.file_path}`;
46
+ case 'Edit': return `Edit: ${input.file_path}`;
47
+ case 'Write': return `Write: ${input.file_path}`;
48
+ case 'Grep': return `Grep: ${oneLine(input.pattern, 60)}`;
49
+ case 'Glob': return `Glob: ${input.pattern}`;
50
+ case 'WebFetch': return `WebFetch: ${input.url}`;
51
+ case 'Agent': return `Agent: ${oneLine(input.description ?? input.subagent_type ?? '', 60)}`;
52
+ default: return block.name;
53
+ }
54
+ }
23
55
 
24
56
  function runClaude(prompt) {
25
57
  return new Promise((resolve, reject) => {
26
58
  const proc = spawn('claude', [...claudeArgs, '-p', inlineHeader + prompt], {
27
59
  cwd: projectDir,
28
- stdio: 'inherit',
60
+ stdio: ['inherit', 'pipe', 'inherit'],
29
61
  env: claudeEnv,
30
62
  });
63
+
64
+ let buffer = '';
65
+ proc.stdout.on('data', (chunk) => {
66
+ buffer += chunk.toString();
67
+ const lines = buffer.split('\n');
68
+ buffer = lines.pop();
69
+ for (const line of lines) {
70
+ if (!line.trim()) continue;
71
+ let msg;
72
+ try { msg = JSON.parse(line); } catch { continue; }
73
+
74
+ if (msg.type === 'assistant') {
75
+ for (const block of msg.message?.content ?? []) {
76
+ if (block.type === 'text' && block.text && verbose) console.log(block.text);
77
+ if (block.type === 'tool_use') {
78
+ if (verbose) console.log(`→ ${describeToolUse(block)}`);
79
+ else if (block.name === 'Skill') console.log(`→ Skill: ${block.input?.skill ?? ''}`);
80
+ else console.log(`→ ${block.name}`);
81
+ }
82
+ }
83
+ } else if (msg.type === 'result') {
84
+ console.log(`done (${msg.duration_ms}ms${msg.total_cost_usd ? `, $${msg.total_cost_usd.toFixed(4)}` : ''})`);
85
+ }
86
+ }
87
+ });
88
+
31
89
  proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Claude exited ${code}`))));
32
90
  proc.on('error', reject);
33
91
  });
@@ -7,6 +7,10 @@ description: Resolve eventmodelers connection config (token, boardId, baseUrl) f
7
7
 
8
8
  **Every other skill invokes this skill first** before making any API calls. Do not proceed past this skill until all four values (`TOKEN`, `BOARD_ID`, `ORG_ID`, `BASE_URL`) are resolved.
9
9
 
10
+ **This should happen once per session, not once per skill.** If `TOKEN`/`BOARD_ID`/`ORG_ID`/`BASE_URL` are already resolved and verified from earlier in the current session — including earlier in the *same turn*, e.g. one skill internally invoking a second skill (`add-next-slice` → `html-screen`) — every subsequent "invoke `connect`" instruction is satisfied immediately by reusing those values. Do not re-run Steps 0–4 below. Only re-run this skill from scratch when a value actually needs to change: a fresh `401`/`403`/access-denied response from some other call, a different `board_id` on this turn, or a new inline param that overrides what's already resolved.
11
+
12
+ This skill also registers the **eventmodelers MCP server** for the project (Step 3.5) so other skills can call MCP tools (`mcp__eventmodelers__*`) instead of raw curl. MCP is the preferred transport; curl remains a fallback for hosts without MCP support, or for the one or two endpoints (documented in `learn-eventmodelers-api`) the MCP server doesn't expose.
13
+
10
14
  ---
11
15
 
12
16
  ## What this skill produces
@@ -20,7 +24,7 @@ After running, the following variables are available for the rest of the session
20
24
  | `ORG_ID` | — | Organization UUID (used in all board-scoped URLs) |
21
25
  | `BASE_URL` | — | Base URL, e.g. `http://localhost:3000` |
22
26
 
23
- Every API call in every skill must include these headers:
27
+ Every curl-fallback call in every skill must include these headers:
24
28
  ```
25
29
  x-token: <TOKEN>
26
30
  x-board-id: <BOARD_ID>
@@ -29,6 +33,8 @@ x-user-id: <skill-name> ← set by each skill individually
29
33
 
30
34
  All board-scoped URLs follow the pattern: `<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/...`
31
35
 
36
+ When calling MCP tools instead, no `x-*` headers are needed — the MCP server resolves `ORG_ID` from `TOKEN` itself and every tool takes `boardId` as an explicit argument. See `learn-eventmodelers-api` for the full tool catalog.
37
+
32
38
  ---
33
39
 
34
40
  ## Step 0 — Check for inline parameters
@@ -138,9 +144,51 @@ Tell the user: `"Config saved to .eventmodelers/config.json and added to .gitign
138
144
 
139
145
  ---
140
146
 
147
+ ## Step 3.5 — Register the MCP server
148
+
149
+ Always run this step (not only when Step 3 ran) — it's idempotent and safe to repeat every time `connect` is invoked.
150
+
151
+ The eventmodelers backend exposes the same board capabilities as an MCP server at `<BASE_URL>/mcp`, authenticated with the same `TOKEN` via an `x-token` header. Register it in the project's `.mcp.json` so Claude Code (or any other MCP-aware host) can connect and expose tools as `mcp__eventmodelers__<tool_name>`.
152
+
153
+ **Do not put the raw token in `.mcp.json`** — that file is typically committed to share server config with the team. Instead reference an environment variable and keep the actual secret in a gitignored `.env` file:
154
+
155
+ 1. Read the existing `.mcp.json` at the project root if present (it may already list other MCP servers, e.g. a browser-automation server used by `discover-storyboard` — merge into `mcpServers`, never replace the whole file). If absent, start from `{"mcpServers": {}}`.
156
+ 2. Add or update the `eventmodelers` entry:
157
+ ```json
158
+ {
159
+ "mcpServers": {
160
+ "eventmodelers": {
161
+ "type": "http",
162
+ "url": "<BASE_URL>/mcp",
163
+ "headers": { "x-token": "${EVENTMODELERS_TOKEN}" }
164
+ }
165
+ }
166
+ }
167
+ ```
168
+ 3. Ensure a project-root `.env` file contains `EVENTMODELERS_TOKEN=<TOKEN>` (append/update the line; create the file if missing).
169
+ 4. Ensure `.env` is listed in `.gitignore` (same check-then-append pattern as Step 3 uses for `.eventmodelers/config.json`) — it holds the same secret and must never be committed.
170
+
171
+ MCP tools only become visible to the current agent session after the host (re)connects to the server — a brand-new `.mcp.json` entry written mid-session may need the user to approve the new server or reconnect (e.g. Claude Code's `/mcp` command) before `mcp__eventmodelers__*` tools appear in the tool list. That's expected and not an error: tell the user once, then let every other skill fall back to curl automatically until the tools show up.
172
+
173
+ ---
174
+
141
175
  ## Step 4 — Verify
142
176
 
143
- Confirm the token and board are valid with a lightweight call:
177
+ Prefer verifying through MCP if `mcp__eventmodelers__*` tools are already visible in this session (e.g. from a `.mcp.json` set up in an earlier turn or a previous session):
178
+
179
+ ```
180
+ mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
181
+ ```
182
+
183
+ A successful result (even an empty array) confirms the token and board are valid. An error mentioning "not found or access denied" means the token/board pairing is wrong — treat it like the `403`/`404` curl cases below.
184
+
185
+ If the MCP server itself shows as needing authentication (e.g. the host lists it as "needs authentication" rather than connected), ask the user once:
186
+
187
+ > "The eventmodelers MCP server needs to be re-authenticated — please run `/mcp` and authenticate the `eventmodelers` server, then let me know when that's done."
188
+
189
+ Wait for their reply. If they say it's done, retry the MCP verify call above. If they skip it, or it still isn't connected, or it's unreachable for some other reason entirely (not an auth prompt), don't keep blocking on it — fall back to the equivalent curl call for this and every subsequent skill in the session, same as if the tools were never visible.
190
+
191
+ Otherwise (no MCP tools visible yet this session), fall back to the equivalent curl call:
144
192
 
145
193
  ```bash
146
194
  curl -s -o /dev/null -w "%{http_code}" \
@@ -152,7 +200,7 @@ curl -s -o /dev/null -w "%{http_code}" \
152
200
 
153
201
  | Response | Action |
154
202
  |----------|--------|
155
- | `200` | Config is valid. Print one line: `"Connected — board <BOARD_ID>"` and return. |
203
+ | `200` | Config is valid. Print one line: `"Connected — board <BOARD_ID>"` (note if MCP tools aren't active yet, curl fallback is in use) and return. |
156
204
  | `401` | Token is invalid or missing. Tell the user and re-run from Step 2, clearing `token`. |
157
205
  | `403` | Token organization does not match board. Tell the user to check that the token was issued for the correct workspace. Re-run from Step 2 for both fields. |
158
206
  | `404` | Board not found. Tell the user and re-run from Step 2, clearing `boardId`. |
@@ -178,6 +226,7 @@ The `token` field is a secret. It is never logged or shown after initial confirm
178
226
 
179
227
  ## Security notes
180
228
 
181
- - The config file is workspace-local and gitignored — never commit it.
229
+ - The config file (`.eventmodelers/config.json`) and the `.env` file holding `EVENTMODELERS_TOKEN` are both workspace-local and gitignored — never commit either.
230
+ - `.mcp.json` itself is safe to commit — it only ever contains the `${EVENTMODELERS_TOKEN}` placeholder, never the literal token.
182
231
  - The token grants write access to all boards in its organization — treat it like a password.
183
- - If a skill receives a `401` or `403` mid-session, re-invoke this skill to refresh the config before retrying.
232
+ - If a skill receives a `401`/`403` (curl) or an access-denied tool error (MCP) mid-session, re-invoke this skill to refresh the config before retrying.
@@ -5,7 +5,65 @@ description: Teaches an agent everything about the eventmodelers platform API
5
5
 
6
6
  # Eventmodelers Platform API Reference
7
7
 
8
- You now have complete knowledge of the eventmodelers platform API. Use this reference whenever you need to call, implement, or reason about any endpoint.
8
+ You now have complete knowledge of the eventmodelers platform API. This is a reference for *how a skill talks to the platform once you're already executing one* — it is not a license to call the API directly instead of invoking the skill that matches the user's intent (see the Skill Selection table in `CLAUDE.md`). If a prompt matches a row in that table, invoke that skill first and let it decide which endpoint/tool to call; only reach for this reference directly when no skill matches the intent at all, or when you're implementing/debugging a skill itself.
9
+
10
+ **Load this once per session, on demand — not as a mandatory preamble.** Every other skill already documents the exact API calls it needs inline; none of them require this full reference to be loaded before they can run. Reach for this skill only when you hit a specific endpoint, field, or element type that a skill's own instructions don't cover, and don't reload it again later in the same session once you have.
11
+
12
+ **Two transports exist for board operations: MCP tools (preferred) and raw REST/curl (fallback).** The `connect` skill registers the MCP server in `.mcp.json`. Once `mcp__eventmodelers__*` tools are visible in your tool list, use them — they need no `x-token`/`x-board-id`/`x-user-id` headers (auth and org resolution happen server-side from the registered token) and return the same data as the REST endpoints below. Fall back to the numbered REST sections only when MCP tools aren't connected yet, or for the handful of endpoints (prompts lifecycle, snapshots, user management, board/extension CRUD) the MCP server intentionally doesn't expose — it only covers board-content operations (nodes, timelines, slices, comments, screens). This preference is about *which transport a skill's own instructions should use*, never about whether to invoke the skill in the first place.
13
+
14
+ ---
15
+
16
+ ## MCP Tool Catalog (Preferred)
17
+
18
+ Server name: `eventmodelers`. Every tool takes `boardId` explicitly; none need `orgId` (resolved from the token) or `x-user-id` (the server attributes writes to the authenticated principal). REST section numbers below give the underlying implementation for tools that wrap a single endpoint 1:1.
19
+
20
+ | Tool | Args | Purpose | REST equivalent |
21
+ |---|---|---|---|
22
+ | `list_boards` | — | List boards for the org | §1 `GET /api/boards` (org-scoped) |
23
+ | `get_nodes` | `boardId`, `type?`, `name?` | List nodes, optionally by type and/or a partial case-insensitive title match | §3 `GET .../nodes` |
24
+ | `get_node` | `boardId`, `nodeId` | Get one node | §3 `GET .../nodes/:nodeId` |
25
+ | `get_node_comments` | `boardId`, `nodeId` | List comments on a node | §1 `GET .../nodes/:nodeId/comments` |
26
+ | `get_board_events` | `boardId` | All board events, in sequence | §1 `GET .../events` |
27
+ | `search_board_events` | `boardId`, `name` | Search events by node name | §1 `GET .../events/search` |
28
+ | `submit_node_events` | `boardId`, `events[]` | Create/update nodes (raw `NodeChangeEvent`/edge events) | §3 `POST .../nodes/events` |
29
+ | `delete_node` | `boardId`, `nodeId` | Delete a node | (via `node:deleted` event, §3) |
30
+ | `create_drawing` | `boardId`, `kind`, `x`, `y`, `width`, `height`, ... | Freehand canvas annotation (path/rect/text) — never placed in a cell | — (no REST equivalent; MCP-only) |
31
+ | `find_nodes_in_drawing` | `boardId`, `drawingId` | Nodes fully contained inside a drawing's bounding box | — (no REST equivalent; MCP-only) |
32
+ | `create_chapter` | `boardId`, `x?`, `y?` | Create a timeline | §2 `POST .../chapters` |
33
+ | `add_column` | `boardId`, `timelineId`, `index?` | Add a column | §2 `POST .../timelines/:id/columns` |
34
+ | `delete_column` | `boardId`, `timelineId`, `columnId` | Delete a column | §2 `DELETE .../columns/:columnId` |
35
+ | `add_lane` | `boardId`, `timelineId`, `type`, `label?`, `index?` | Add a lane/row | §2 `POST .../timelines/:id/lanes` |
36
+ | `remove_lane` | `boardId`, `timelineId`, `rowId` | Remove a lane | — (extends §2; no direct REST route) |
37
+ | `move_node_in_timeline` | `boardId`, `timelineId`, `movedNodeId`, `toCellId` | Move a placed node to another cell | — (MCP-only convenience) |
38
+ | `move_timeline_structure` | `boardId`, `timelineId`, `kind` (`'column'\|'lane'`), `id`, `toIndex` | Reorder a column or lane (row) — `kind` picks which `id` refers to | — (MCP-only convenience) |
39
+ | `move_timeline_position` | `boardId`, `timelineId`, `x`, `y` | Move a chapter node on canvas | — (MCP-only convenience) |
40
+ | `drop_node_to_cell` | `boardId`, `timelineId`, `cellId`, `nodeId`, `nodeType` | Place an existing node into a cell | §2 `POST .../cells/:cellId/drop` |
41
+ | `clear_cell` | `boardId`, `timelineId`, `cellId` | Remove+delete the node in a cell | — (MCP-only convenience) |
42
+ | `create_slice` | `boardId`, `timelineId`, `type`, `index?` | Create a full slice (column + nodes + SLICE_BORDER) | §5 `POST .../slices` |
43
+ | `create_slice_definition` | `boardId`, `timelineId`, `columnId`, `title`, `data?`, `meta?` | Create a SLICE_BORDER over an existing column | §5 `POST .../slice-definitions` |
44
+ | `place_element` | `boardId`, `timelineId`, `elementType`, `title`, `columnIndex?` | Find/create an empty cell in the right lane and place a COMMAND/READMODEL/EVENT | — (MCP-only convenience; composes §2+§3) |
45
+ | `list_slices` | `boardId` | List slices (id, title, status) | §8 `GET .../slicedata/slices` |
46
+ | `update_slice_status` | `boardId`, `sliceId`, `newStatus` | Change a SLICE_BORDER's `sliceStatus` | — (via `node:changed` event, §3) |
47
+ | `get_slice_data` | `boardId`, `contextName?`, `contextId?`, `sliceId?` | Full element graph for slices in a context | §8 `GET /slicedata` |
48
+ | `get_spec_info` | `boardId`, `timelineId` | EVENT/COMMAND/READMODEL nodes valid in GWT steps | §6 `GET .../spec-info` |
49
+ | `add_scenario` | `boardId`, `timelineId`, `columnId`, `scenarios[]` | Append GWT scenario(s) to a column's spec node | §6 `POST .../scenarios` |
50
+ | `set_connection` | `boardId`, `source`, `target`, `action` (`'connect'\|'remove'`) | Add or remove a type-checked directed edge | — (via `edges` on §3 events) |
51
+ | `auto_connect_node` | `boardId`, `nodeId` | Re-run auto-connect for a node | §3 `POST .../nodes/:nodeId/auto-connect` |
52
+ | `add_comment` | `boardId`, `nodeId`, `text`, `type?` (`'COMMENT'\|'TASK'\|'QUESTION'`), `author?` | Add a comment — `QUESTION` flags gaps/edge cases during review | — (via comment events) |
53
+ | `update_comment` | `boardId`, `nodeId`, `commentId`, `action` (`'resolve'\|'delete'`) | Resolve or delete a comment | — (via comment events) |
54
+ | `create_screen` | `boardId`, `contentType` (`'image'\|'sketch'\|'html'`), `nodeId?`, `chapterId`, `cellId?`/`cellName?`, plus content fields (`imageBase64`/`mimeType`, `elements[]`, or `pages[]`/`backgroundColor`), `description?` | Create + place a new screen node (SCREEN or HTML_SCREEN) atomically, in one call | §4 `POST .../images/:id/sketch` + `image-nodes` |
55
+ | `render_screen` | `boardId`, `nodeId`, `elements[]?` (SCREEN) or `pages[]?`+`backgroundColor?` (HTML_SCREEN), `description?` | Update an existing screen's content — exactly one of `elements`/`pages` | §4 `POST .../images/:id/sketch` + `image-nodes` |
56
+ | `add_field_examples` | `boardId`, `nodeId?`, `name?`, `cellName?`, `timelineId?` | Fill empty field examples using linked-node context | — (MCP-only convenience) |
57
+ | `get_attribute_chain` | `boardId`, `timelineId`, `targetCellName`, `sourceCellName` | Resolve every node between two cells, ordered target→source | — (MCP-only convenience) |
58
+ | `verify_screen` | `boardId`, `nodeId` | Check a screen node exists and has rendered content — works for both SCREEN and HTML_SCREEN, dispatching on the node's actual type | — (MCP-only convenience) |
59
+ | `get_image_snapshot_description` | `boardId`, `nodeId` | Load the `{elements:[...]}` sketch description from storage | — (reads what §4 sketch endpoints write) |
60
+ | `validate_slice_data` | `sliceData` | Offline validation of a `SliceDataOutput` payload — no board access | — (MCP-only, pure function) |
61
+ | `commit_board_to_git` | `boardId` | Force a git-extension commit/push, bypassing the autoCommit gate | — (MCP-only; git extension) |
62
+ | `update_prompt_status` | `promptId`, `newStatus`, `comment?` | Update a prompt's lifecycle status (`ADDED`/`CLAIMED`/`IN_PROGRESS`/`DONE`), optionally with a progress comment. Not board-scoped — no `boardId` arg; the prompt's board is resolved server-side. | §14 `POST .../prompts/:id/status` |
63
+
64
+ **Not exposed via MCP at all** — always use REST/curl for these: §7 Config Import, §10 Snapshots, §11–12 User Management, §13 Utility (`/api/user`, swagger), and the rest of §14 Prompts (submission, claiming, deletion, realtime-token) — only the status-update endpoint has an MCP tool (`update_prompt_status`, used by the `update-prompt-status` skill); everything else in Prompts is an intentionally separate lifecycle the board-content MCP server doesn't otherwise own.
65
+
66
+ **Capabilities with no direct MCP filter** — e.g. REST's `GET .../nodes?cellId=<id>` (§3) has no `cellId` param on `get_nodes`. Get the same answer by calling `get_node` on the CHAPTER and reading `meta.timelineData.cells` (sparse array; a cell absent from it is empty) instead of asking the server to filter by cell.
9
67
 
10
68
  ---
11
69
 
@@ -54,6 +112,29 @@ SLICE_BORDER // Slice boundary marker
54
112
 
55
113
  ---
56
114
 
115
+ ## Field Types
116
+
117
+ Every field on a `COMMAND`, `EVENT`, `READMODEL`, `SCREEN`, or `TABLE` element (`meta.fields[]`) has a `type` from this exact set — the canonical source is the [event-modeling-spec schema](https://github.com/dilgerma/event-modeling-spec/blob/main/eventmodeling.schema.json) (`$defs.Field.properties.type`):
118
+
119
+ ```typescript
120
+ String // text
121
+ Boolean // true / false
122
+ Int // 32-bit integer
123
+ Long // 64-bit integer
124
+ Double // floating-point number
125
+ Decimal // precise fixed-point number — prefer this over Double for money/currency
126
+ Date // calendar date only, no time component (e.g. "2026-06-01")
127
+ DateTime // date + time, ISO 8601 (e.g. "2026-06-01T09:00:00Z")
128
+ UUID // universally unique identifier
129
+ Custom // structured/nested value — use with `subfields` or `schema`
130
+ ```
131
+
132
+ Other `Field` properties: `name`, `example`, `subfields[]` (nested `Field`s), `mapping`, `optional`, `technicalAttribute`, `generated`, `idAttribute`, `pii`, `schema`, `cardinality` (`"List"` | `"Single"`).
133
+
134
+ Use exactly these type names (case-sensitive) — not lowercase (`string`), synonyms (`Number`, `Text`, `Integer`), or types outside this set.
135
+
136
+ ---
137
+
57
138
  ## Standard HTTP Status Codes
58
139
 
59
140
  | Code | Meaning |
@@ -354,10 +435,12 @@ Create a complete slice (1 column + 3 nodes automatically placed).
354
435
  ```
355
436
 
356
437
  **Slice node mapping**:
357
- - `state-change` → SCREEN (actor) + COMMAND (interaction) + EVENT (swimlane)
358
- - `state-view` → SCREEN (actor) + READMODEL (interaction) + EVENT (swimlane)
438
+ - `state-change` → HTML_SCREEN (actor) + COMMAND (interaction) + EVENT (swimlane)
439
+ - `state-view` → HTML_SCREEN (actor) + READMODEL (interaction) + EVENT (swimlane)
359
440
  - `automation` → AUTOMATION (actor) + COMMAND (interaction) + EVENT (swimlane)
360
441
 
442
+ The actor HTML_SCREEN is created as a **stub** — a single visibly-placeholder page ("Untitled screen — design pending") unless `nodes.actor.pages` is passed explicitly. Whoever calls this (the `add-next-slice` skill — the one that creates a brand-new slice from scratch, as opposed to `eventmodeling-slicing-event-models`, which only makes existing elements explicit) is responsible for immediately replacing that stub via the `html-screen` skill — including gathering the board's existing screens first so the new one matches their established style, since `html-screen` itself has no visibility into other screens.
443
+
361
444
  **Response**: `200` — slice data
362
445
 
363
446
  ### POST `/api/org/:orgId/boards/:boardId/timelines/:timelineId/slice-definitions`
@@ -635,6 +718,8 @@ Claim the next pending (`ADDED`) prompt for a board — atomically flips it to `
635
718
  ### POST `/api/org/:orgId/prompts/:id/status`
636
719
  Set a prompt's status, optionally attaching a progress comment. Auth: `x-token` only (bot token — no user JWT needed, this is meant to be called directly by the agent working the prompt).
637
720
 
721
+ **Prefer MCP**: `mcp__eventmodelers__update_prompt_status { "promptId": "<id>", "newStatus": "IN_PROGRESS", "comment": "..." }` — no `orgId`/`x-token` needed, same validation and response shape. Fall back to the curl below only when MCP tools aren't connected.
722
+
638
723
  **Request body**:
639
724
  ```typescript
640
725
  {
@@ -24,6 +24,22 @@ If neither is provided, load and persist all slices without filtering.
24
24
 
25
25
  ## Step 2 — Fetch all slices from the slicedata API
26
26
 
27
+ Prefer the MCP tool when `mcp__eventmodelers__*` tools are visible in this session:
28
+
29
+ ```
30
+ mcp__eventmodelers__list_slices { "boardId": "<BOARD_ID>" }
31
+ ```
32
+
33
+ This returns `{ "slices": [ { "id": "...", "title": "...", "status": "..." } ] }` — lighter than the full slicedata payload (no `contextName`/`contextId`/`comments`). If Step 3/4 below need those richer fields for a specific slice, follow up with:
34
+
35
+ ```
36
+ mcp__eventmodelers__get_slice_data { "boardId": "<BOARD_ID>", "contextName": "<name>" }
37
+ ```
38
+
39
+ (`get_slice_data` requires a `contextName` or `contextId` — call `list_slices` first, then resolve context per slice via `mcp__eventmodelers__get_node` on each `SLICE_BORDER` id if the context isn't already known.)
40
+
41
+ **Fallback (no MCP):**
42
+
27
43
  ```bash
28
44
  curl -s \
29
45
  -H "x-token: <TOKEN>" \
@@ -37,7 +37,13 @@ If `newStatus` is not one of these exact values, stop and tell the user the vali
37
37
 
38
38
  ## Step 2 — List all slices
39
39
 
40
- Fetch all slices on the board:
40
+ Prefer the MCP tool when `mcp__eventmodelers__*` tools are visible in this session:
41
+
42
+ ```
43
+ mcp__eventmodelers__list_slices { "boardId": "<BOARD_ID>" }
44
+ ```
45
+
46
+ **Fallback (no MCP):**
41
47
 
42
48
  ```bash
43
49
  curl -s \
@@ -47,9 +53,7 @@ curl -s \
47
53
  "<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/slicedata/slices"
48
54
  ```
49
55
 
50
- Response: `{ "slices": [{ "id": "<nodeId>", "title": "<title>", "status": "<status>" }] }`
51
-
52
- The `id` here is the `SLICE_BORDER` node ID — use it directly in Step 3.
56
+ Either way, the response is `{ "slices": [{ "id": "<nodeId>", "title": "<title>", "status": "<status>" }] }`. The `id` here is the `SLICE_BORDER` node ID — use it directly in Step 3.
53
57
 
54
58
  Find the slice whose `title` matches `sliceName` (case-insensitive). If no match is found, stop and list the available slice titles so the user can pick one.
55
59
 
@@ -61,7 +65,13 @@ Save the matched slice as:
61
65
 
62
66
  ## Step 3 — Update the slice status
63
67
 
64
- Send a `node:changed` event to update the `sliceStatus` field in the SLICE_BORDER node's meta:
68
+ Prefer the MCP tool it does the same `node:changed`/`sliceStatus` update in one call, no event envelope to hand-assemble:
69
+
70
+ ```
71
+ mcp__eventmodelers__update_slice_status { "boardId": "<BOARD_ID>", "sliceId": "<SLICE_NODE_ID>", "newStatus": "<newStatus>" }
72
+ ```
73
+
74
+ **Fallback (no MCP)** — send a `node:changed` event to update the `sliceStatus` field in the SLICE_BORDER node's meta directly:
65
75
 
66
76
  ```bash
67
77
  curl -s -X POST "<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/events" \
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: add-next-slice
3
+ description: Decide on and create a genuinely new slice from scratch when there's no existing COMMAND/READMODEL/AUTOMATION left to slice — the model doesn't yet suggest an obvious next capability, or every existing element already has a Done slice. Use when eventmodeling-slicing-event-models finds nothing left to make explicit. Do not use for: making an already-modelled element's slice explicit (use eventmodeling-slicing-event-models — that skill only slices existing elements, it never invents new ones).
4
+ ---
5
+
6
+ # Add Next Slice
7
+
8
+ > **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Every API call this skill needs (`create_slice`, `get_nodes`/`get_node`) is already given in full below — do not additionally load `learn-eventmodelers-api`; only reach for it if you hit an error this file doesn't cover.
9
+
10
+ **Purpose**: Given a timeline where every existing COMMAND/READMODEL/AUTOMATION already has a slice (or there's no model content at all yet), decide on a plausible next capability and create it as a brand-new slice — screen, command/read model, and event all at once.
11
+
12
+ This is the counterpart to `eventmodeling-slicing-event-models`, which only ever makes *existing* elements explicit as slices and correctly refuses to invent new ones. Something has to own deciding what comes next — that's this skill; `eventmodeling-slicing-event-models` points here whenever it finds nothing left to slice.
13
+
14
+ ---
15
+
16
+ ## Core Concept: A Slice Is One Command, One Read Model, or One Automation — Never Combined
17
+
18
+ Same rules as `eventmodeling-slicing-event-models` — the slice you create here must obey them too, not just slices made from pre-existing elements.
19
+
20
+ A **Feature Slice** is the thinnest possible vertical cut through the model — exactly one decision or one query:
21
+
22
+ ```
23
+ state-change slice = SCREEN/Processor → COMMAND → EVENT(s)
24
+ state-view slice = EVENT(s) → READMODEL → SCREEN/Processor
25
+ automation slice = EVENT(s) → AUTOMATION → COMMAND → EVENT(s)
26
+ ```
27
+
28
+ A slice never mixes a COMMAND and a READMODEL — the platform models these as two distinct slice types (`state-change` and `state-view`). If the capability you decide on in Step 1 needs both a command and a read model (e.g. "place an order" needs the `PlaceOrder` command *and* an `OrderDetailView` read model), that's **two slices** — create them one at a time, each its own `create_slice` call.
29
+
30
+ **Key characteristics**:
31
+ - Exactly one COMMAND (state-change), exactly one READMODEL (state-view), or one AUTOMATION's command — never combined
32
+ - Named after that command, read model, or automation
33
+ - Independently deployable
34
+ - Communicates with other slices via events only
35
+
36
+ ---
37
+
38
+ ## This is never the ambiguous/no-default case the per-turn "Questioning rule" allows you to stop on
39
+
40
+ "I don't know what the next capability is" is not the same as "any guess risks doing the wrong thing." A plausible next slice — the next lifecycle stage, an unaddressed affordance on an existing screen, a natural CRUD/notification gap — always exists for a working domain, and a wrong guess here costs nothing: it's just another slice on the board, easy to rename or discard later.
41
+
42
+ **Posting a comment and closing the prompt with no board mutation is not an acceptable outcome of this skill.** That only defers the same empty decision to the next identical prompt, forever. If you already posted a `QUESTION`/`TASK` comment about this exact ambiguity on a previous turn, that does not make it acceptable to do so again instead of creating something — the comment already served its purpose (flagging the assumption for a human to correct later); this turn should still create the slice.
43
+
44
+ ---
45
+
46
+ ## Step 1: Decide on the next capability
47
+
48
+ **If the prompt gives no specific instruction about what the next slice should be** (e.g. a bare "add the next slice"), don't ask for one — look at the previous slices already on the timeline and derive the next one yourself:
49
+
50
+ - Read the existing COMMAND/READMODEL/AUTOMATION titles and their fields in narrative order (left to right on the timeline) — they tell a story (e.g. Reserve Table → Cancel Reservation → Check-In → ...).
51
+ - Identify what the story is missing next: the natural following lifecycle stage, an unaddressed affordance implied by an existing screen (a button/link with nothing behind it yet), or a CRUD/notification gap the existing entity clearly has (created but never updated/cancelled/queried in detail, an action with no confirmation view, etc.).
52
+ - Prefer the option that most directly continues the existing narrative over one that starts an unrelated new thread — the goal is the slice that makes the most sense as *next*, not just *any* plausible slice.
53
+
54
+ If a specific instruction *is* given (the prompt names a capability, or references a comment/discussion that does), use that instead of inferring one.
55
+
56
+ If, after looking at the existing slices, it's genuinely unclear which of several equally-reasonable next steps to pick, post a `QUESTION` comment (via `handle-comment`) noting the assumption you're about to make — then make it and create the slice in the same turn, every time. Never stop at just the comment.
57
+
58
+ ## Step 2: Create the slice
59
+
60
+ ```
61
+ mcp__eventmodelers__create_slice { "boardId": "<BOARD_ID>", "timelineId": "<TL>", "type": "state-change", "nodes": {"interaction": {"title": "CancelReservation"}} }
62
+ ```
63
+
64
+ Pick `type` based on what you decided in Step 1 — `state-change` for a new command, `state-view` for a new read model, `automation` for a new automation. Always pass `nodes.interaction.title` as the command/read model/automation name you decided on in Step 1 — per the Core Concept above, the slice is *named after that element*, and the backend only derives the slice title from this field; omitting it produces a useless generic "State Change"/"State View"/"Automation" label instead. This also creates the slice's `SLICE_BORDER` automatically — no separate `create_slice_definition` call needed.
65
+
66
+ **Fallback (no MCP):**
67
+ ```bash
68
+ curl -X POST $BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/slices \
69
+ -H "x-token: $TOKEN" -H "Content-Type: application/json" \
70
+ -d '{"type":"state-change","nodes":{"interaction":{"title":"CancelReservation"}}}'
71
+ ```
72
+
73
+ ## Step 3: Replace the placeholder screen — matching the board's existing style
74
+
75
+ For `state-change`/`state-view`, the actor node this creates is an `HTML_SCREEN` — but only as a **stub**: a single visibly-placeholder page ("Untitled screen — design pending"), never real content. Never leave it at that:
76
+
77
+ 1. Look up the board's other screens on this timeline (`mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "HTML_SCREEN" }`, or the equivalent already-loaded slice data) and note their established visual style — layout conventions, color/tone, recurring components (nav bar, card style, button treatment).
78
+ 2. Invoke the `html-screen` skill on the new stub node, passing a `description` that covers **both** what this screen should contain (derived from the new command/read model's fields) **and** the style to match (derived from what you just observed in step 1). `html-screen` has no visibility into other screens on the board — gathering that context and folding it into the description is this skill's job, not something to expect `html-screen` to do on its own.
79
+ 3. Default to `html-screen` here, never `storyboard-screen` (wireframe sketch) — a sketch is only for an explicit user request for one.
80
+
81
+ ## Step 4: Report back
82
+
83
+ Tell the user (or, in an autonomous modeling session, note it in the turn's progress line):
84
+ - The capability you decided on and why
85
+ - The slice type and node IDs created
86
+ - Whether the placeholder screen was replaced with a real design, and what style it matched
@@ -5,7 +5,9 @@ description: Analyze the existing event model on a board — summarizes contexts
5
5
 
6
6
  # Analyze Existing Model
7
7
 
8
- > **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until the connect skill has completed.
8
+ > **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until the connect skill has completed.
9
+
10
+ Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
9
11
 
10
12
  Read the full event model from a board and produce a structured analysis: what contexts exist, how many slices are in each state, which slices have GWT specs, and where the visible gaps are. This skill is read-only — it never posts comments or modifies the board.
11
13
 
@@ -26,6 +28,12 @@ If `boardId` is explicitly passed it overrides `BOARD_ID` from `connect`.
26
28
 
27
29
  ## Step 2 — List all slices
28
30
 
31
+ **Prefer MCP:**
32
+ ```
33
+ mcp__eventmodelers__list_slices { "boardId": "$BOARD_ID" }
34
+ ```
35
+
36
+ **Fallback (no MCP):**
29
37
  ```bash
30
38
  curl -s \
31
39
  -H "x-token: $TOKEN" \
@@ -48,6 +56,12 @@ Save the full slice list. Count total slices and group by status:
48
56
 
49
57
  Fetch all `MODEL_CONTEXT` nodes to identify bounded contexts on the board:
50
58
 
59
+ **Prefer MCP:**
60
+ ```
61
+ mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT" }
62
+ ```
63
+
64
+ **Fallback (no MCP):**
51
65
  ```bash
52
66
  curl -s \
53
67
  -H "x-token: $TOKEN" \
@@ -66,6 +80,12 @@ curl -s \
66
80
 
67
81
  For each resolved context, fetch the full element graph:
68
82
 
83
+ **Prefer MCP:**
84
+ ```
85
+ mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>" }
86
+ ```
87
+
88
+ **Fallback (no MCP):**
69
89
  ```bash
70
90
  curl -s \
71
91
  -H "x-token: $TOKEN" \
@@ -189,6 +209,14 @@ If a context was specified but not found, tell the user clearly and list the con
189
209
 
190
210
  ## Example — full board analysis
191
211
 
212
+ **Prefer MCP:**
213
+ ```
214
+ mcp__eventmodelers__list_slices { "boardId": "$BOARD_ID" }
215
+ mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT" }
216
+ mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "Ordering" }
217
+ ```
218
+
219
+ **Fallback (no MCP):**
192
220
  ```bash
193
221
  # 1. List slices
194
222
  curl -s \