codeloop-mcp-server 0.1.41 → 0.1.43

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/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import { loadConfig } from "./config.js";
20
20
  import { validateApiKey, isActivationRequired } from "./auth/api_key.js";
21
21
  import { identifyKeySource, buildRevokedKeyDiagnostic } from "./auth/key_source.js";
22
22
  import { warmCliCache } from "./auth/cli_cache_warmer.js";
23
- import { startUpdateCheck, getUpdateInfo, formatUpdateNotice, } from "./auth/update_check.js";
23
+ import { startUpdateCheck, getUpdateInfo, formatUpdateNotice, getRunningVersion, } from "./auth/update_check.js";
24
24
  import { applyUpdate, applyUpdateInputSchema, } from "./tools/apply_update.js";
25
25
  import { trackUsage } from "./auth/usage_tracker.js";
26
26
  import { isLocalMode } from "./auth/local_mode.js";
@@ -264,18 +264,45 @@ function withUpdateNotice(content) {
264
264
  ...content,
265
265
  ];
266
266
  }
267
+ /**
268
+ * Tiny footer appended to every tool response so the agent (and the
269
+ * user reading the chat) always know which MCP server version is
270
+ * actually serving the call. Without this, a stale long-lived child
271
+ * process from a pre-fix version can keep producing failing tool
272
+ * calls indefinitely with no visible signal of "you're running an
273
+ * old version". The startup banner only goes to stderr, which is
274
+ * hidden from the chat.
275
+ *
276
+ * Format is deliberately one short line so it doesn't drown the
277
+ * tool's actual JSON payload, and prefixed `[CodeLoop]` so an agent
278
+ * scanning the response can match on it without ambiguity.
279
+ */
280
+ function buildVersionBanner() {
281
+ const v = getRunningVersion();
282
+ if (!v)
283
+ return null;
284
+ return {
285
+ type: "text",
286
+ text: `[CodeLoop server v${v}]`,
287
+ };
288
+ }
267
289
  function withInitHint(content, dir) {
268
- // Order matters: update notice goes FIRST (most actionable signal),
269
- // then init hint (init-then-restart is a weird state but harmless),
270
- // then the original content. Critical update notices override
271
- // anything else the agent might do this turn.
290
+ // Order matters:
291
+ // 1. Update notice (most actionable signal CRITICAL stays at top).
292
+ // 2. Init hint (only when project is not initialized).
293
+ // 3. The original content.
294
+ // 4. Version banner footer (so the agent can always see what
295
+ // version it's talking to — survives across all responses).
296
+ const banner = buildVersionBanner();
272
297
  const withUpdate = withUpdateNotice(content);
273
- if (isProjectInitialized(dir || projectDir))
274
- return withUpdate;
275
- return [
276
- { type: "text", text: INIT_HINT },
277
- ...withUpdate,
278
- ];
298
+ const head = [];
299
+ if (!isProjectInitialized(dir || projectDir)) {
300
+ head.push({ type: "text", text: INIT_HINT });
301
+ }
302
+ const result = [...head, ...withUpdate];
303
+ if (banner)
304
+ result.push(banner);
305
+ return result;
279
306
  }
280
307
  const TOOL_BOOTSTRAP = "[CodeLoop] You MUST call codeloop_verify after every code change. If .codeloop/config.json is missing, call codeloop_init_project FIRST.\n\n";
281
308
  // ── Implemented Tools ────────────────────────────────────────────
@@ -1929,6 +1956,10 @@ Wait 1-2 seconds between interactions so video frames capture state changes.`, {
1929
1956
  .describe("For win_ui_automate"),
1930
1957
  app_name: z.string().optional().describe("App name to bring to front before interaction. Auto-detected from active recording if omitted. Also used for launch_app, win_ui_inspect, win_ui_automate."),
1931
1958
  package_id: z.string().optional().describe("Package/bundle ID for mobile actions"),
1959
+ intent: z.string().optional().describe("Semantic label for what this interaction is doing — short verb-phrase like 'edit product row', 'confirm delete dialog', 'save form', 'create new range'. STRONGLY RECOMMENDED for desktop coordinate-based clicks (target_type='desktop' with only x/y) because the CRUD classifier in user_journey_evidence can't infer the meaning of a raw (x, y) pair the way it can from a Playwright selector or DOM aria_label. Without `intent`, a sequence of coordinate clicks that delete a record will score `delete_actions: 0` and fail the gate. The classifier reads `intent` (plus `description` / `purpose` / `step` as aliases) into the same target-text bucket as selectors and aria labels, so the SAME keywords that work for browsers (edit / delete / save / submit / create / new) also work here. Example: when clicking the 'Yes' button of a Windows MessageBox at (2640, 820), pass intent='confirm delete'."),
1960
+ description: z.string().optional().describe("[Alias for intent] Same semantics."),
1961
+ purpose: z.string().optional().describe("[Alias for intent] Same semantics."),
1962
+ step: z.string().optional().describe("Plan-step name when this interaction is driving a codeloop_plan_user_journey arc (e.g. 'edit', 'delete', 'create', 'save', 'verify'). Logged alongside `intent` and read by the CRUD classifier."),
1932
1963
  project_dir: z.string().optional().describe("Absolute path to project root."),
1933
1964
  workspace_root: z.string().optional().describe("[Alias for project_dir] Pass either; they're equivalent."),
1934
1965
  }, async (params) => {
@@ -2537,6 +2568,24 @@ Wait 1-2 seconds between interactions so video frames capture state changes.`, {
2537
2568
  inputArgs.automation_action = params.automation_action;
2538
2569
  if (params.app_name)
2539
2570
  inputArgs.app_name = params.app_name;
2571
+ // Semantic-intent fields. These are the ONLY hook coordinate-based
2572
+ // desktop clicks (target_type='desktop' with only x/y) have to
2573
+ // tell the CRUD classifier what they MEAN. Without one of these,
2574
+ // a sequence of (x, y) clicks that deletes a record scores
2575
+ // delete_actions: 0 because the classifier has nothing to match
2576
+ // DELETE_HINT against. Persisted under the same keys we accepted
2577
+ // them under (no normalisation) so users can grep the log for
2578
+ // their original tags. The classifier in interaction_coverage.ts
2579
+ // reads each of these fields into targetText() alongside
2580
+ // selector / aria_label / etc.
2581
+ if (params.intent)
2582
+ inputArgs.intent = params.intent;
2583
+ if (params.description)
2584
+ inputArgs.description = params.description;
2585
+ if (params.purpose)
2586
+ inputArgs.purpose = params.purpose;
2587
+ if (params.step)
2588
+ inputArgs.step = params.step;
2540
2589
  // Post-action verification readback. Persisted alongside the
2541
2590
  // interaction so a downstream consumer (depth gate, dev report,
2542
2591
  // the agent on the next turn) can confirm the action actually
@@ -2660,6 +2709,78 @@ Returns: counts for attempted / succeeded / requeued events and the queue locati
2660
2709
  ]),
2661
2710
  };
2662
2711
  });
2712
+ server.tool("codeloop_version", `Return the running MCP server version, the latest published version, and whether an update is pending.
2713
+
2714
+ Use this tool when:
2715
+ - The agent or the user is unsure whether the CodeLoop MCP server has picked up a recent fix.
2716
+ - A previous CodeLoop tool call returned an error that looks like it should have been fixed (e.g., a Zod schema rejection that you know was made optional in a later release).
2717
+ - The user explicitly asks "what CodeLoop version am I running?".
2718
+
2719
+ Use this tool FIRST in any new chat session if you have reason to suspect the MCP child process is stale — Cursor / Claude Code keep one long-lived MCP server per workspace, so a session opened days ago may still be serving from an old version even though npm has shipped fixes since.
2720
+
2721
+ Returns:
2722
+ - \`running_version\`: the version of THIS process. This is the source of truth — the agent should compare its expectations against this value, not against the npm package metadata.
2723
+ - \`latest_published_version\`: from the background update poller (may be null if the poll has not completed yet).
2724
+ - \`is_outdated\`: whether running < latest.
2725
+ - \`is_critical\`: whether the running version falls below any entry in the critical-fix floor list.
2726
+ - \`recommended_action\`: a one-line instruction for what to do next (call codeloop_apply_update, restart IDE, or no action).
2727
+ - \`how_to_update\`: human-readable steps to get to the latest version, tailored to the situation.
2728
+
2729
+ No project_dir / workspace_root required — this tool is workspace-independent.`, {}, async () => {
2730
+ const running = getRunningVersion();
2731
+ const info = getUpdateInfo();
2732
+ const isOutdated = info?.is_outdated ?? false;
2733
+ const isCritical = info?.is_critical ?? false;
2734
+ let recommendedAction;
2735
+ let howToUpdate;
2736
+ if (!running) {
2737
+ recommendedAction = "diagnose_only";
2738
+ howToUpdate = [
2739
+ "Could not read the running MCP server's package.json — this is unusual. Check that the codeloop-mcp-server package files are intact.",
2740
+ ];
2741
+ }
2742
+ else if (!info) {
2743
+ recommendedAction = "no_action";
2744
+ howToUpdate = [
2745
+ `Currently running v${running}.`,
2746
+ "The background update check has not completed yet (or has been disabled via CODELOOP_SKIP_UPDATE_CHECK=1). Try this tool again in a few seconds, or run `npm view codeloop-mcp-server version` in the user's terminal to compare manually.",
2747
+ ];
2748
+ }
2749
+ else if (!isOutdated) {
2750
+ recommendedAction = "no_action";
2751
+ howToUpdate = [`Already on the latest version (v${running}). No action needed.`];
2752
+ }
2753
+ else if (isCritical) {
2754
+ recommendedAction = "call_codeloop_apply_update_now";
2755
+ howToUpdate = [
2756
+ `Running v${running} is below a critical-fix floor. Latest is v${info.latest}.`,
2757
+ `Reasons: ${info.critical_reasons.join("; ")}.`,
2758
+ "Call `codeloop_apply_update` NOW. It returns the terminal commands to pre-warm the npx cache, schedules the MCP server to exit gracefully, and the IDE respawns it with the latest version automatically. Your next CodeLoop tool call will be served by the new version — no IDE restart required.",
2759
+ ];
2760
+ }
2761
+ else {
2762
+ recommendedAction = "call_codeloop_apply_update_when_convenient";
2763
+ howToUpdate = [
2764
+ `Running v${running}, latest is v${info.latest}.`,
2765
+ "Not critical, but recommended. Call `codeloop_apply_update` to pick up the new version in-session, OR let the next IDE restart pick it up via `npx -y codeloop-mcp-server@latest`.",
2766
+ ];
2767
+ }
2768
+ const result = {
2769
+ running_version: running,
2770
+ latest_published_version: info?.latest ?? null,
2771
+ is_outdated: isOutdated,
2772
+ is_critical: isCritical,
2773
+ critical_reasons: info?.critical_reasons ?? [],
2774
+ last_checked_at: info?.checked_at ?? null,
2775
+ recommended_action: recommendedAction,
2776
+ how_to_update: howToUpdate,
2777
+ };
2778
+ return {
2779
+ content: withInitHint([
2780
+ { type: "text", text: JSON.stringify(result, null, 2) },
2781
+ ]),
2782
+ };
2783
+ });
2663
2784
  server.tool("codeloop_apply_update", TOOL_BOOTSTRAP + `Apply a pending CodeLoop MCP server update to the current chat session — without asking the user to restart their IDE.
2664
2785
 
2665
2786
  Use this tool when: