@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.
@@ -36,7 +36,7 @@ var init_package = __esm({
36
36
  "package.json"() {
37
37
  package_default = {
38
38
  name: "@apicircle/mcp-server",
39
- version: "1.0.7",
39
+ version: "1.0.8",
40
40
  private: false,
41
41
  type: "module",
42
42
  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.",
@@ -2964,24 +2964,65 @@ var MultiWorkspaceProvider_exports = {};
2964
2964
  __export(MultiWorkspaceProvider_exports, {
2965
2965
  MultiWorkspaceProvider: () => MultiWorkspaceProvider
2966
2966
  });
2967
- var import_registry, MultiWorkspaceProvider;
2967
+ var import_registry, LazyActiveWorkspaceProvider, MultiWorkspaceProvider;
2968
2968
  var init_MultiWorkspaceProvider = __esm({
2969
2969
  "src/providers/MultiWorkspaceProvider.ts"() {
2970
2970
  "use strict";
2971
2971
  import_registry = require("@apicircle/core/workspace/registry");
2972
2972
  init_FileBackedWorkspaceProvider();
2973
2973
  init_Workspaces();
2974
+ LazyActiveWorkspaceProvider = class {
2975
+ constructor(registryRoot, onActiveResolved) {
2976
+ this.registryRoot = registryRoot;
2977
+ this.onActiveResolved = onActiveResolved;
2978
+ }
2979
+ registryRoot;
2980
+ onActiveResolved;
2981
+ async resolveActive() {
2982
+ const registry = await (0, import_registry.loadRegistry)(this.registryRoot);
2983
+ const activeId = registry?.activeWorkspaceId ?? null;
2984
+ if (!activeId) {
2985
+ throw new Error(
2986
+ "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
2987
+ );
2988
+ }
2989
+ this.onActiveResolved(activeId);
2990
+ return new FileBackedWorkspaceProvider((0, import_registry.workspaceDirFor)(this.registryRoot, activeId));
2991
+ }
2992
+ async read() {
2993
+ const provider = await this.resolveActive();
2994
+ return provider.read();
2995
+ }
2996
+ async apply(patch) {
2997
+ const provider = await this.resolveActive();
2998
+ return provider.apply(patch);
2999
+ }
3000
+ async write(next) {
3001
+ const provider = await this.resolveActive();
3002
+ return provider.write(next);
3003
+ }
3004
+ };
2974
3005
  MultiWorkspaceProvider = class {
2975
3006
  constructor(registryRoot) {
2976
3007
  this.registryRoot = registryRoot;
3008
+ this.lazyProvider = new LazyActiveWorkspaceProvider(this.registryRoot, (id) => {
3009
+ this.activeWorkspaceId = id;
3010
+ });
2977
3011
  }
2978
3012
  registryRoot;
2979
- active = null;
3013
+ /** Last-known active workspace id. Refreshed every time the lazy
3014
+ * provider resolves; reflects what the most recent operation saw on
3015
+ * disk, not a stale boot-time snapshot. */
2980
3016
  activeWorkspaceId = null;
3017
+ /** The lazy provider tool handlers consume as `ctx.workspace`. Holds a
3018
+ * reference back to this instance so each call updates
3019
+ * `activeWorkspaceId` for `activeId()` callers + diagnostic logs. */
3020
+ lazyProvider;
2981
3021
  /**
2982
- * Hydrate the active provider from disk. Must be called once before the
2983
- * MCP host boots so `ctx.workspace.read()` doesn't race the first
2984
- * registry-load. Returns the registry the boot can log.
3022
+ * Read the registry from disk so the host can log a boot banner. Does
3023
+ * NOT cache a per-id provider — each `activeProvider()` call re-reads
3024
+ * the registry, so a workspace switch in the desktop is picked up by
3025
+ * the next tool call without restarting the MCP server.
2985
3026
  */
2986
3027
  async init() {
2987
3028
  const registry = await (0, import_registry.loadRegistry)(this.registryRoot) ?? {
@@ -2989,22 +3030,18 @@ var init_MultiWorkspaceProvider = __esm({
2989
3030
  activeWorkspaceId: null,
2990
3031
  workspaces: []
2991
3032
  };
2992
- if (registry.activeWorkspaceId) {
2993
- this.activeWorkspaceId = registry.activeWorkspaceId;
2994
- this.active = new FileBackedWorkspaceProvider(
2995
- (0, import_registry.workspaceDirFor)(this.registryRoot, registry.activeWorkspaceId)
2996
- );
2997
- }
3033
+ this.activeWorkspaceId = registry.activeWorkspaceId;
2998
3034
  return registry;
2999
3035
  }
3000
- /** The provider tool handlers see as `ctx.workspace`. */
3036
+ /**
3037
+ * The provider tool handlers see as `ctx.workspace`. Returns a lazy
3038
+ * provider whose `read` / `apply` / `write` calls re-read
3039
+ * `registry.json` so the right active workspace is always targeted
3040
+ * even if the desktop switched workspaces since this MCP process
3041
+ * started.
3042
+ */
3001
3043
  activeProvider() {
3002
- if (!this.active) {
3003
- throw new Error(
3004
- "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
3005
- );
3006
- }
3007
- return this.active;
3044
+ return this.lazyProvider;
3008
3045
  }
3009
3046
  // ─── Workspaces interface ──────────────────────────────────────────────────
3010
3047
  async list() {
@@ -3052,23 +3089,17 @@ var init_MultiWorkspaceProvider = __esm({
3052
3089
  if (!registry || !registry.workspaces.some((w) => w.id === workspaceId)) {
3053
3090
  throw new WorkspaceNotFoundError(workspaceId);
3054
3091
  }
3055
- const next = await (0, import_registry.setActiveWorkspace)(this.registryRoot, workspaceId);
3056
- void next;
3092
+ await (0, import_registry.setActiveWorkspace)(this.registryRoot, workspaceId);
3057
3093
  this.activeWorkspaceId = workspaceId;
3058
- this.active = new FileBackedWorkspaceProvider((0, import_registry.workspaceDirFor)(this.registryRoot, workspaceId));
3059
3094
  }
3060
3095
  /**
3061
3096
  * Idempotent registry write — used by tests / tools that need to
3062
- * persist registry updates that didn't go through `setActive`.
3097
+ * persist registry updates that didn't go through `setActive`. The
3098
+ * lazy active provider picks the new id up on its next operation.
3063
3099
  */
3064
3100
  async writeRegistry(registry) {
3065
3101
  await (0, import_registry.saveRegistry)(this.registryRoot, registry);
3066
3102
  this.activeWorkspaceId = registry.activeWorkspaceId;
3067
- if (registry.activeWorkspaceId) {
3068
- this.active = new FileBackedWorkspaceProvider(
3069
- (0, import_registry.workspaceDirFor)(this.registryRoot, registry.activeWorkspaceId)
3070
- );
3071
- }
3072
3103
  }
3073
3104
  };
3074
3105
  }