@eventmodelers/cli 1.0.2 → 1.0.4

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.
package/cli.js CHANGED
@@ -932,6 +932,7 @@ async function installStack(stackKey, stackCfg, options = {}) {
932
932
  // even without a token yet — the file only ever holds the env-var
933
933
  // placeholder, never the literal secret (see connect/SKILL.md's Security notes).
934
934
  ensureMcpRegistered(targetDir, config.baseUrl || DEFAULT_BASE_URL);
935
+ ensureEnvToken(targetDir, config.token);
935
936
 
936
937
  // --- 6. Install manifest (drives precise `uninstall` later) ---
937
938
  // Only the footprint listed here is ever removed by `uninstall` — the root
@@ -1155,6 +1156,57 @@ function ensureMcpRegistered(projectDir, baseUrl) {
1155
1156
  writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
1156
1157
  }
1157
1158
 
1159
+ // Companion to ensureMcpRegistered: that function deliberately never writes the
1160
+ // token itself, on the assumption the caller sets EVENTMODELERS_TOKEN in whatever
1161
+ // process spawns `claude` (true for `run --modeling`'s own spawn). It is NOT true
1162
+ // for an interactive session opened directly in the project right after
1163
+ // `init`/`init-config` — that `claude` process inherits the user's shell env,
1164
+ // which never had a reason to already have this var set.
1165
+ //
1166
+ // A plain `.env` file does NOT fix this — Claude Code never sources one; per its
1167
+ // own docs, an unresolved `.mcp.json` placeholder is left as the literal
1168
+ // `${EVENTMODELERS_TOKEN}` text, which fails auth and falls back to an OAuth
1169
+ // flow the eventmodelers server can't actually satisfy for this client. The
1170
+ // only things Claude Code itself resolves `.mcp.json` placeholders against are
1171
+ // the inherited shell env and its own settings files' `env` block. `.claude/
1172
+ // settings.local.json` is the documented per-user, gitignored-by-convention
1173
+ // scope for exactly this — same idea as `.eventmodelers/config.json` already
1174
+ // holding the raw token, just in the one file Claude Code's own process env
1175
+ // actually consults before expanding `.mcp.json`.
1176
+ function ensureEnvToken(targetDir, token) {
1177
+ if (!token) return;
1178
+ const claudeDir = join(targetDir, '.claude');
1179
+ mkdirSync(claudeDir, { recursive: true });
1180
+ const settingsPath = join(claudeDir, 'settings.local.json');
1181
+
1182
+ let settings = {};
1183
+ if (existsSync(settingsPath)) {
1184
+ try {
1185
+ settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
1186
+ } catch {
1187
+ settings = {};
1188
+ }
1189
+ }
1190
+
1191
+ settings.env = settings.env || {};
1192
+ settings.env.EVENTMODELERS_TOKEN = token;
1193
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
1194
+
1195
+ // settings.local.json is gitignored by Claude Code's own convention, but make
1196
+ // sure nothing here relies on that silently — it now holds a live secret.
1197
+ const gitignorePath = join(targetDir, '.gitignore');
1198
+ const entry = '.claude/settings.local.json';
1199
+ if (existsSync(gitignorePath)) {
1200
+ const content = readFileSync(gitignorePath, 'utf-8');
1201
+ if (!content.split('\n').map((l) => l.trim()).includes(entry)) {
1202
+ appendFileSync(gitignorePath, `${content === '' || content.endsWith('\n') ? '' : '\n'}${entry}\n`);
1203
+ }
1204
+ } else {
1205
+ writeFileSync(gitignorePath, `${entry}\n`);
1206
+ }
1207
+ console.log(' ✓ Wrote EVENTMODELERS_TOKEN to .claude/settings.local.json (gitignored)');
1208
+ }
1209
+
1158
1210
  // `run --modeling`: modeling-kit's one and only runtime mode — there is no
1159
1211
  // cold-spawn/tasks.json loop for this kit (that's a build-kit concept; see the
1160
1212
  // `run` command's build-kit-vs-modeling-kit gate above). It keeps ONE Claude
@@ -1650,6 +1702,7 @@ credentialFlags(program
1650
1702
  // stale MCP registration pointing at the wrong host is worse than none
1651
1703
  // (see the beta-api protected-resource-metadata incident this fixed).
1652
1704
  ensureMcpRegistered(targetDir, cfg.baseUrl || DEFAULT_BASE_URL);
1705
+ ensureEnvToken(targetDir, cfg.token);
1653
1706
  }
1654
1707
  });
1655
1708
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, Cratis, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -150,7 +150,7 @@ Always run this step (not only when Step 3 ran) — it's idempotent and safe to
150
150
 
151
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
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:
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 set the actual secret in `.claude/settings.local.json`'s `env` block:
154
154
 
155
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
156
  2. Add or update the `eventmodelers` entry:
@@ -165,8 +165,14 @@ The eventmodelers backend exposes the same board capabilities as an MCP server a
165
165
  }
166
166
  }
167
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.
168
+ 3. Read the existing `.claude/settings.local.json` if present and merge in — never clobber `permissions`/`enabledMcpjsonServers` or anything else already there. Ensure it has `EVENTMODELERS_TOKEN` set under `env` (create the file with just this key if it doesn't exist yet):
169
+ ```json
170
+ {
171
+ "env": { "EVENTMODELERS_TOKEN": "<TOKEN>" }
172
+ }
173
+ ```
174
+ A plain `.env` file does **not** work here — Claude Code never sources one, so a `${EVENTMODELERS_TOKEN}` placeholder in `.mcp.json` would be left unexpanded (sent as the literal `${EVENTMODELERS_TOKEN}` text), which fails auth and pushes the client into an OAuth flow the eventmodelers server can't satisfy for this client. `.claude/settings.local.json`'s `env` block — alongside the inherited shell environment — is the only thing Claude Code actually resolves `.mcp.json` placeholders against.
175
+ 4. Ensure `.claude/settings.local.json` is listed in `.gitignore` (same check-then-append pattern as Step 3 uses for `.eventmodelers/config.json`) — it now holds the same secret and must never be committed. (Gitignored by Claude Code's own convention already, but don't rely on that silently.)
170
176
 
171
177
  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
178
 
@@ -226,7 +232,7 @@ The `token` field is a secret. It is never logged or shown after initial confirm
226
232
 
227
233
  ## Security notes
228
234
 
229
- - The config file (`.eventmodelers/config.json`) and the `.env` file holding `EVENTMODELERS_TOKEN` are both workspace-local and gitignored — never commit either.
235
+ - The config file (`.eventmodelers/config.json`) and `.claude/settings.local.json` (holding `EVENTMODELERS_TOKEN` under `env`) are both workspace-local and gitignored — never commit either.
230
236
  - `.mcp.json` itself is safe to commit — it only ever contains the `${EVENTMODELERS_TOKEN}` placeholder, never the literal token.
231
237
  - The token grants write access to all boards in its organization — treat it like a password.
232
238
  - 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.