@apicircle/mcp-server 1.0.7 → 1.0.8

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.d.cts CHANGED
@@ -173,16 +173,29 @@ declare class FileBackedWorkspaceProvider implements WorkspaceProvider {
173
173
 
174
174
  declare class MultiWorkspaceProvider implements Workspaces {
175
175
  private readonly registryRoot;
176
- private active;
176
+ /** Last-known active workspace id. Refreshed every time the lazy
177
+ * provider resolves; reflects what the most recent operation saw on
178
+ * disk, not a stale boot-time snapshot. */
177
179
  private activeWorkspaceId;
180
+ /** The lazy provider tool handlers consume as `ctx.workspace`. Holds a
181
+ * reference back to this instance so each call updates
182
+ * `activeWorkspaceId` for `activeId()` callers + diagnostic logs. */
183
+ private readonly lazyProvider;
178
184
  constructor(registryRoot: string);
179
185
  /**
180
- * Hydrate the active provider from disk. Must be called once before the
181
- * MCP host boots so `ctx.workspace.read()` doesn't race the first
182
- * registry-load. Returns the registry the boot can log.
186
+ * Read the registry from disk so the host can log a boot banner. Does
187
+ * NOT cache a per-id provider — each `activeProvider()` call re-reads
188
+ * the registry, so a workspace switch in the desktop is picked up by
189
+ * the next tool call without restarting the MCP server.
183
190
  */
184
191
  init(): Promise<WorkspaceRegistry>;
185
- /** The provider tool handlers see as `ctx.workspace`. */
192
+ /**
193
+ * The provider tool handlers see as `ctx.workspace`. Returns a lazy
194
+ * provider whose `read` / `apply` / `write` calls re-read
195
+ * `registry.json` so the right active workspace is always targeted
196
+ * even if the desktop switched workspaces since this MCP process
197
+ * started.
198
+ */
186
199
  activeProvider(): WorkspaceProvider;
187
200
  list(): Promise<WorkspaceSummary[]>;
188
201
  for(workspaceId: string): WorkspaceProvider;
@@ -190,7 +203,8 @@ declare class MultiWorkspaceProvider implements Workspaces {
190
203
  setActive(workspaceId: string): Promise<void>;
191
204
  /**
192
205
  * Idempotent registry write — used by tests / tools that need to
193
- * persist registry updates that didn't go through `setActive`.
206
+ * persist registry updates that didn't go through `setActive`. The
207
+ * lazy active provider picks the new id up on its next operation.
194
208
  */
195
209
  writeRegistry(registry: WorkspaceRegistry): Promise<void>;
196
210
  }
package/dist/index.d.ts CHANGED
@@ -173,16 +173,29 @@ declare class FileBackedWorkspaceProvider implements WorkspaceProvider {
173
173
 
174
174
  declare class MultiWorkspaceProvider implements Workspaces {
175
175
  private readonly registryRoot;
176
- private active;
176
+ /** Last-known active workspace id. Refreshed every time the lazy
177
+ * provider resolves; reflects what the most recent operation saw on
178
+ * disk, not a stale boot-time snapshot. */
177
179
  private activeWorkspaceId;
180
+ /** The lazy provider tool handlers consume as `ctx.workspace`. Holds a
181
+ * reference back to this instance so each call updates
182
+ * `activeWorkspaceId` for `activeId()` callers + diagnostic logs. */
183
+ private readonly lazyProvider;
178
184
  constructor(registryRoot: string);
179
185
  /**
180
- * Hydrate the active provider from disk. Must be called once before the
181
- * MCP host boots so `ctx.workspace.read()` doesn't race the first
182
- * registry-load. Returns the registry the boot can log.
186
+ * Read the registry from disk so the host can log a boot banner. Does
187
+ * NOT cache a per-id provider — each `activeProvider()` call re-reads
188
+ * the registry, so a workspace switch in the desktop is picked up by
189
+ * the next tool call without restarting the MCP server.
183
190
  */
184
191
  init(): Promise<WorkspaceRegistry>;
185
- /** The provider tool handlers see as `ctx.workspace`. */
192
+ /**
193
+ * The provider tool handlers see as `ctx.workspace`. Returns a lazy
194
+ * provider whose `read` / `apply` / `write` calls re-read
195
+ * `registry.json` so the right active workspace is always targeted
196
+ * even if the desktop switched workspaces since this MCP process
197
+ * started.
198
+ */
186
199
  activeProvider(): WorkspaceProvider;
187
200
  list(): Promise<WorkspaceSummary[]>;
188
201
  for(workspaceId: string): WorkspaceProvider;
@@ -190,7 +203,8 @@ declare class MultiWorkspaceProvider implements Workspaces {
190
203
  setActive(workspaceId: string): Promise<void>;
191
204
  /**
192
205
  * Idempotent registry write — used by tests / tools that need to
193
- * persist registry updates that didn't go through `setActive`.
206
+ * persist registry updates that didn't go through `setActive`. The
207
+ * lazy active provider picks the new id up on its next operation.
194
208
  */
195
209
  writeRegistry(registry: WorkspaceRegistry): Promise<void>;
196
210
  }
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { z } from "zod";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@apicircle/mcp-server",
9
- version: "1.0.7",
9
+ version: "1.0.8",
10
10
  private: false,
11
11
  type: "module",
12
12
  description: "Model Context Protocol server exposing API Circle Studio's workspace as a tool catalog. Used by Claude Desktop, ChatGPT, Cursor, GitHub Copilot, and any other MCP-compatible AI client.",
@@ -2832,17 +2832,58 @@ import {
2832
2832
  setActiveWorkspace as setActiveWorkspaceOnDisk,
2833
2833
  workspaceDirFor
2834
2834
  } from "@apicircle/core/workspace/registry";
2835
+ var LazyActiveWorkspaceProvider = class {
2836
+ constructor(registryRoot, onActiveResolved) {
2837
+ this.registryRoot = registryRoot;
2838
+ this.onActiveResolved = onActiveResolved;
2839
+ }
2840
+ registryRoot;
2841
+ onActiveResolved;
2842
+ async resolveActive() {
2843
+ const registry = await loadRegistry(this.registryRoot);
2844
+ const activeId = registry?.activeWorkspaceId ?? null;
2845
+ if (!activeId) {
2846
+ throw new Error(
2847
+ "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
2848
+ );
2849
+ }
2850
+ this.onActiveResolved(activeId);
2851
+ return new FileBackedWorkspaceProvider(workspaceDirFor(this.registryRoot, activeId));
2852
+ }
2853
+ async read() {
2854
+ const provider = await this.resolveActive();
2855
+ return provider.read();
2856
+ }
2857
+ async apply(patch) {
2858
+ const provider = await this.resolveActive();
2859
+ return provider.apply(patch);
2860
+ }
2861
+ async write(next) {
2862
+ const provider = await this.resolveActive();
2863
+ return provider.write(next);
2864
+ }
2865
+ };
2835
2866
  var MultiWorkspaceProvider = class {
2836
2867
  constructor(registryRoot) {
2837
2868
  this.registryRoot = registryRoot;
2869
+ this.lazyProvider = new LazyActiveWorkspaceProvider(this.registryRoot, (id) => {
2870
+ this.activeWorkspaceId = id;
2871
+ });
2838
2872
  }
2839
2873
  registryRoot;
2840
- active = null;
2874
+ /** Last-known active workspace id. Refreshed every time the lazy
2875
+ * provider resolves; reflects what the most recent operation saw on
2876
+ * disk, not a stale boot-time snapshot. */
2841
2877
  activeWorkspaceId = null;
2878
+ /** The lazy provider tool handlers consume as `ctx.workspace`. Holds a
2879
+ * reference back to this instance so each call updates
2880
+ * `activeWorkspaceId` for `activeId()` callers + diagnostic logs. */
2881
+ lazyProvider;
2842
2882
  /**
2843
- * Hydrate the active provider from disk. Must be called once before the
2844
- * MCP host boots so `ctx.workspace.read()` doesn't race the first
2845
- * registry-load. Returns the registry the boot can log.
2883
+ * Read the registry from disk so the host can log a boot banner. Does
2884
+ * NOT cache a per-id provider — each `activeProvider()` call re-reads
2885
+ * the registry, so a workspace switch in the desktop is picked up by
2886
+ * the next tool call without restarting the MCP server.
2846
2887
  */
2847
2888
  async init() {
2848
2889
  const registry = await loadRegistry(this.registryRoot) ?? {
@@ -2850,22 +2891,18 @@ var MultiWorkspaceProvider = class {
2850
2891
  activeWorkspaceId: null,
2851
2892
  workspaces: []
2852
2893
  };
2853
- if (registry.activeWorkspaceId) {
2854
- this.activeWorkspaceId = registry.activeWorkspaceId;
2855
- this.active = new FileBackedWorkspaceProvider(
2856
- workspaceDirFor(this.registryRoot, registry.activeWorkspaceId)
2857
- );
2858
- }
2894
+ this.activeWorkspaceId = registry.activeWorkspaceId;
2859
2895
  return registry;
2860
2896
  }
2861
- /** The provider tool handlers see as `ctx.workspace`. */
2897
+ /**
2898
+ * The provider tool handlers see as `ctx.workspace`. Returns a lazy
2899
+ * provider whose `read` / `apply` / `write` calls re-read
2900
+ * `registry.json` so the right active workspace is always targeted
2901
+ * even if the desktop switched workspaces since this MCP process
2902
+ * started.
2903
+ */
2862
2904
  activeProvider() {
2863
- if (!this.active) {
2864
- throw new Error(
2865
- "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
2866
- );
2867
- }
2868
- return this.active;
2905
+ return this.lazyProvider;
2869
2906
  }
2870
2907
  // ─── Workspaces interface ──────────────────────────────────────────────────
2871
2908
  async list() {
@@ -2913,23 +2950,17 @@ var MultiWorkspaceProvider = class {
2913
2950
  if (!registry || !registry.workspaces.some((w) => w.id === workspaceId)) {
2914
2951
  throw new WorkspaceNotFoundError(workspaceId);
2915
2952
  }
2916
- const next = await setActiveWorkspaceOnDisk(this.registryRoot, workspaceId);
2917
- void next;
2953
+ await setActiveWorkspaceOnDisk(this.registryRoot, workspaceId);
2918
2954
  this.activeWorkspaceId = workspaceId;
2919
- this.active = new FileBackedWorkspaceProvider(workspaceDirFor(this.registryRoot, workspaceId));
2920
2955
  }
2921
2956
  /**
2922
2957
  * Idempotent registry write — used by tests / tools that need to
2923
- * persist registry updates that didn't go through `setActive`.
2958
+ * persist registry updates that didn't go through `setActive`. The
2959
+ * lazy active provider picks the new id up on its next operation.
2924
2960
  */
2925
2961
  async writeRegistry(registry) {
2926
2962
  await saveRegistry(this.registryRoot, registry);
2927
2963
  this.activeWorkspaceId = registry.activeWorkspaceId;
2928
- if (registry.activeWorkspaceId) {
2929
- this.active = new FileBackedWorkspaceProvider(
2930
- workspaceDirFor(this.registryRoot, registry.activeWorkspaceId)
2931
- );
2932
- }
2933
2964
  }
2934
2965
  };
2935
2966