@apicircle/mcp-server 1.0.7 → 1.0.9

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.cjs CHANGED
@@ -41,7 +41,7 @@ var import_zod = require("zod");
41
41
  // package.json
42
42
  var package_default = {
43
43
  name: "@apicircle/mcp-server",
44
- version: "1.0.7",
44
+ version: "1.0.9",
45
45
  private: false,
46
46
  type: "module",
47
47
  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.",
@@ -2143,15 +2143,143 @@ var promptSetEndpointMultipliersTool = {
2143
2143
  }
2144
2144
  };
2145
2145
 
2146
- // src/tools/mocks.ts
2146
+ // src/tools/globalAssets.ts
2147
2147
  var import_zod10 = require("zod");
2148
2148
  var import_shared4 = require("@apicircle/shared");
2149
+ function deriveState(asset, hasPendingUpload) {
2150
+ const w = asset.workingBranchRef ?? null;
2151
+ const b = asset.baseBranchRef ?? null;
2152
+ if (w && b) {
2153
+ if (w.blobSha && b.blobSha && w.blobSha !== b.blobSha) return "diverged";
2154
+ return "merged";
2155
+ }
2156
+ if (w && !b) return "workingOnly";
2157
+ if (!w && b) return "baseOnly";
2158
+ if (hasPendingUpload) return "uploading";
2159
+ return "missing";
2160
+ }
2161
+ var globalAssetsFilesListTool = {
2162
+ name: "assets.list_files",
2163
+ description: "List every Global File Asset with its provenance state and reference count. Each entry includes id, name, filename, size, mimeType, sha256, state (uploading | workingOnly | merged | baseOnly | missing | diverged), workingBranchRef, baseBranchRef, and usage { requests, mockEndpoints, total }.",
2164
+ inputSchema: import_zod10.z.object({}).strict(),
2165
+ async handler(_input, ctx) {
2166
+ const state = await ctx.workspace.read();
2167
+ const files = state.synced.globalAssets.files ?? {};
2168
+ const pending = state.local.pendingFileUploads ?? {};
2169
+ const usage = state.local.assetUsageIndex ?? {};
2170
+ const items = Object.values(files).map((asset) => {
2171
+ const hasPending = Boolean(pending[asset.id]);
2172
+ const u = usage[asset.id] ?? { requests: [], mockEndpoints: [], total: 0 };
2173
+ return {
2174
+ id: asset.id,
2175
+ name: asset.name,
2176
+ description: asset.description ?? null,
2177
+ filename: asset.filename,
2178
+ size: asset.size,
2179
+ mimeType: asset.mimeType,
2180
+ sha256: asset.sha256 ?? null,
2181
+ state: deriveState(asset, hasPending),
2182
+ workingBranchRef: asset.workingBranchRef ?? null,
2183
+ baseBranchRef: asset.baseBranchRef ?? null,
2184
+ usage: { ...u }
2185
+ };
2186
+ });
2187
+ return { count: items.length, files: items };
2188
+ }
2189
+ };
2190
+ var globalAssetsFilesCreateTool = {
2191
+ name: "assets.create_file",
2192
+ description: 'Register a Global File Asset entry. Bytes are NOT carried \u2014 MCP returns the new asset id and the asset enters the "missing" state. The user fills the bytes from the Global Assets panel (a "Fill bytes" button surfaces on missing-state assets) which preserves the slot id and queues the bytes for the next push. Use this when an AI client wants to claim an asset slot for a file the user will provide later.',
2193
+ inputSchema: import_zod10.z.object({
2194
+ name: import_zod10.z.string().min(1, "name is required"),
2195
+ description: import_zod10.z.string().optional(),
2196
+ filename: import_zod10.z.string().min(1, "filename is required"),
2197
+ size: import_zod10.z.number().int().nonnegative(),
2198
+ mimeType: import_zod10.z.string().default("application/octet-stream"),
2199
+ sha256: import_zod10.z.string().optional()
2200
+ }).strict(),
2201
+ async handler(input, ctx) {
2202
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2203
+ const file = {
2204
+ id: (0, import_shared4.generateId)(),
2205
+ name: input.name,
2206
+ description: input.description,
2207
+ slotId: (0, import_shared4.generateId)(),
2208
+ filename: input.filename,
2209
+ size: input.size,
2210
+ mimeType: input.mimeType,
2211
+ sha256: input.sha256,
2212
+ createdAt: now,
2213
+ updatedAt: now
2214
+ };
2215
+ const out = await ctx.workspace.apply({ kind: "globalAsset.upsertFile", file });
2216
+ return { id: file.id, slotId: file.slotId, changedIds: out.changedIds };
2217
+ }
2218
+ };
2219
+ var globalAssetsFilesUpdateTool = {
2220
+ name: "assets.update_file",
2221
+ description: "Rename or re-describe a Global File Asset. Provenance refs (workingBranchRef, baseBranchRef) and binary metadata (slotId, sha256, size, mimeType) are preserved verbatim.",
2222
+ inputSchema: import_zod10.z.object({
2223
+ id: import_zod10.z.string().min(1),
2224
+ patch: import_zod10.z.object({
2225
+ name: import_zod10.z.string().optional(),
2226
+ description: import_zod10.z.string().nullable().optional()
2227
+ }).strict()
2228
+ }).strict(),
2229
+ async handler(input, ctx) {
2230
+ const state = await ctx.workspace.read();
2231
+ const existing = state.synced.globalAssets.files?.[input.id];
2232
+ if (!existing) {
2233
+ return { found: false, changedIds: [] };
2234
+ }
2235
+ const next = {
2236
+ ...existing,
2237
+ name: input.patch.name ?? existing.name,
2238
+ description: input.patch.description === null ? void 0 : input.patch.description ?? existing.description
2239
+ };
2240
+ const out = await ctx.workspace.apply({ kind: "globalAsset.upsertFile", file: next });
2241
+ return { found: true, id: next.id, changedIds: out.changedIds };
2242
+ }
2243
+ };
2244
+ var globalAssetsFilesDeleteTool = {
2245
+ name: "assets.delete_file",
2246
+ description: "Delete a Global File Asset. Cascades \u2014 every request body and mock-response body that referenced the asset is unbound in the same mutation. The result envelope includes the consumer list that was cleared so the caller can report what changed.",
2247
+ inputSchema: import_zod10.z.object({ id: import_zod10.z.string().min(1) }).strict(),
2248
+ async handler(input, ctx) {
2249
+ const before = await ctx.workspace.read();
2250
+ const usage = before.local.assetUsageIndex?.[input.id] ?? {
2251
+ requests: [],
2252
+ mockEndpoints: [],
2253
+ total: 0
2254
+ };
2255
+ const existing = before.synced.globalAssets.files?.[input.id];
2256
+ if (!existing) {
2257
+ return { found: false, changedIds: [] };
2258
+ }
2259
+ const out = await ctx.workspace.apply({ kind: "globalAsset.removeFile", id: input.id });
2260
+ return {
2261
+ found: true,
2262
+ id: input.id,
2263
+ filename: existing.filename,
2264
+ unbound: {
2265
+ requests: usage.requests,
2266
+ mockEndpoints: usage.mockEndpoints,
2267
+ total: usage.total
2268
+ },
2269
+ changedIds: out.changedIds
2270
+ };
2271
+ }
2272
+ };
2273
+
2274
+ // src/tools/mocks.ts
2275
+ var import_zod11 = require("zod");
2276
+ var import_shared5 = require("@apicircle/shared");
2149
2277
  var import_mock_server_core2 = require("@apicircle/mock-server-core");
2150
2278
  async function ingestSource(source, name) {
2151
2279
  const { endpoints, warnings } = await (0, import_mock_server_core2.parseSourceToEndpoints)(source);
2152
2280
  const now = (/* @__PURE__ */ new Date()).toISOString();
2153
2281
  const mock = {
2154
- id: (0, import_shared4.generateId)(),
2282
+ id: (0, import_shared5.generateId)(),
2155
2283
  name,
2156
2284
  source,
2157
2285
  endpoints,
@@ -2167,10 +2295,10 @@ async function ingestSource(source, name) {
2167
2295
  var mockCreateFromOpenApiTool = {
2168
2296
  name: "mock.create_from_openapi",
2169
2297
  description: "Create a mock server from an OpenAPI / Swagger spec (YAML or JSON).",
2170
- inputSchema: import_zod10.z.object({
2171
- name: import_zod10.z.string(),
2172
- spec: import_zod10.z.string().min(1),
2173
- format: import_zod10.z.enum(["json", "yaml"]).default("json")
2298
+ inputSchema: import_zod11.z.object({
2299
+ name: import_zod11.z.string(),
2300
+ spec: import_zod11.z.string().min(1),
2301
+ format: import_zod11.z.enum(["json", "yaml"]).default("json")
2174
2302
  }),
2175
2303
  async handler(input, ctx) {
2176
2304
  const { mock, warnings } = await ingestSource(
@@ -2189,7 +2317,7 @@ var mockCreateFromOpenApiTool = {
2189
2317
  var mockCreateFromPostmanTool = {
2190
2318
  name: "mock.create_from_postman",
2191
2319
  description: "Create a mock server from a Postman v2/v2.1 collection.",
2192
- inputSchema: import_zod10.z.object({ name: import_zod10.z.string(), collection: import_zod10.z.string().min(1) }),
2320
+ inputSchema: import_zod11.z.object({ name: import_zod11.z.string(), collection: import_zod11.z.string().min(1) }),
2193
2321
  async handler(input, ctx) {
2194
2322
  const { mock, warnings } = await ingestSource(
2195
2323
  { kind: "postman", collection: input.collection },
@@ -2207,7 +2335,7 @@ var mockCreateFromPostmanTool = {
2207
2335
  var mockCreateFromInsomniaTool = {
2208
2336
  name: "mock.create_from_insomnia",
2209
2337
  description: "Create a mock server from an Insomnia v4 export.",
2210
- inputSchema: import_zod10.z.object({ name: import_zod10.z.string(), export: import_zod10.z.string().min(1) }),
2338
+ inputSchema: import_zod11.z.object({ name: import_zod11.z.string(), export: import_zod11.z.string().min(1) }),
2211
2339
  async handler(input, ctx) {
2212
2340
  const { mock, warnings } = await ingestSource(
2213
2341
  { kind: "insomnia", export: input.export },
@@ -2225,7 +2353,7 @@ var mockCreateFromInsomniaTool = {
2225
2353
  var mockImportPostmanMockCollectionTool = {
2226
2354
  name: "mock.import_postman_mock_collection",
2227
2355
  description: "Import a Postman Mock Collection (collections previously hosted on Postman's mock service). Same parser as a regular Postman collection but marked as a mock import.",
2228
- inputSchema: import_zod10.z.object({ name: import_zod10.z.string(), collection: import_zod10.z.string().min(1) }),
2356
+ inputSchema: import_zod11.z.object({ name: import_zod11.z.string(), collection: import_zod11.z.string().min(1) }),
2229
2357
  async handler(input, ctx) {
2230
2358
  const { mock, warnings } = await ingestSource(
2231
2359
  { kind: "postman", collection: input.collection },
@@ -2243,7 +2371,7 @@ var mockImportPostmanMockCollectionTool = {
2243
2371
  var mockListTool = {
2244
2372
  name: "mock.list",
2245
2373
  description: "List all mock servers in the workspace plus their runtime status (running / stopped, port).",
2246
- inputSchema: import_zod10.z.object({}),
2374
+ inputSchema: import_zod11.z.object({}),
2247
2375
  async handler(_input, ctx) {
2248
2376
  const state = await ctx.workspace.read();
2249
2377
  const running = await ctx.mock.list();
@@ -2265,9 +2393,9 @@ var mockListTool = {
2265
2393
  var mockStartTool = {
2266
2394
  name: "mock.start",
2267
2395
  description: "Start a mock server by id. Returns the bound port. Errors if the mock is already running or the requested port is in use.",
2268
- inputSchema: import_zod10.z.object({
2269
- id: import_zod10.z.string(),
2270
- port: import_zod10.z.number().int().positive().optional()
2396
+ inputSchema: import_zod11.z.object({
2397
+ id: import_zod11.z.string(),
2398
+ port: import_zod11.z.number().int().positive().optional()
2271
2399
  }),
2272
2400
  async handler(input, ctx) {
2273
2401
  const state = await ctx.workspace.read();
@@ -2284,7 +2412,7 @@ var mockStartTool = {
2284
2412
  var mockStopTool = {
2285
2413
  name: "mock.stop",
2286
2414
  description: "Stop a running mock server by id (no-op if not running).",
2287
- inputSchema: import_zod10.z.object({ id: import_zod10.z.string() }),
2415
+ inputSchema: import_zod11.z.object({ id: import_zod11.z.string() }),
2288
2416
  async handler(input, ctx) {
2289
2417
  try {
2290
2418
  await ctx.mock.stop(input.id);
@@ -2297,7 +2425,7 @@ var mockStopTool = {
2297
2425
  var mockDeleteTool = {
2298
2426
  name: "mock.delete",
2299
2427
  description: "Delete a mock server definition. Stops it first if it's running.",
2300
- inputSchema: import_zod10.z.object({ id: import_zod10.z.string() }),
2428
+ inputSchema: import_zod11.z.object({ id: import_zod11.z.string() }),
2301
2429
  async handler(input, ctx) {
2302
2430
  try {
2303
2431
  await ctx.mock.stop(input.id);
@@ -2307,18 +2435,18 @@ var mockDeleteTool = {
2307
2435
  return { ok: true, changedIds: out.changedIds };
2308
2436
  }
2309
2437
  };
2310
- var HTTP_METHOD3 = import_zod10.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2438
+ var HTTP_METHOD3 = import_zod11.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2311
2439
  var mockCreateManualTool = {
2312
2440
  name: "mock.create_manual",
2313
2441
  description: "Create an empty manual-mode mock server. Use `mock.add_endpoint` afterward to populate it. CORS defaults to off (same-origin only); enable + list explicit origins via `mock.update_cors` if cross-origin access is needed.",
2314
- inputSchema: import_zod10.z.object({
2315
- name: import_zod10.z.string().min(1),
2316
- defaultPort: import_zod10.z.number().int().positive().nullable().optional()
2442
+ inputSchema: import_zod11.z.object({
2443
+ name: import_zod11.z.string().min(1),
2444
+ defaultPort: import_zod11.z.number().int().positive().nullable().optional()
2317
2445
  }),
2318
2446
  async handler(input, ctx) {
2319
2447
  const now = (/* @__PURE__ */ new Date()).toISOString();
2320
2448
  const mock = {
2321
- id: (0, import_shared4.generateId)(),
2449
+ id: (0, import_shared5.generateId)(),
2322
2450
  name: input.name,
2323
2451
  source: { kind: "manual", endpoints: [] },
2324
2452
  endpoints: [],
@@ -2336,7 +2464,7 @@ var mockCreateManualTool = {
2336
2464
  var mockListEndpointsTool = {
2337
2465
  name: "mock.list_endpoints",
2338
2466
  description: "List endpoints for a mock server (id, method, path, name).",
2339
- inputSchema: import_zod10.z.object({ mockId: import_zod10.z.string() }),
2467
+ inputSchema: import_zod11.z.object({ mockId: import_zod11.z.string() }),
2340
2468
  async handler(input, ctx) {
2341
2469
  const state = await ctx.workspace.read();
2342
2470
  const mock = state.synced.mockServers[input.mockId];
@@ -2355,10 +2483,10 @@ var mockListEndpointsTool = {
2355
2483
  };
2356
2484
  }
2357
2485
  };
2358
- var ENDPOINT_RESPONSE2 = import_zod10.z.object({
2359
- status: import_zod10.z.number().int().min(100).max(599).default(200),
2360
- jsonBody: import_zod10.z.string().default("{}"),
2361
- contentType: import_zod10.z.string().default("application/json")
2486
+ var ENDPOINT_RESPONSE2 = import_zod11.z.object({
2487
+ status: import_zod11.z.number().int().min(100).max(599).default(200),
2488
+ jsonBody: import_zod11.z.string().default("{}"),
2489
+ contentType: import_zod11.z.string().default("application/json")
2362
2490
  });
2363
2491
  function buildDefaultEndpoint(args) {
2364
2492
  const response = args.response ?? {
@@ -2368,16 +2496,16 @@ function buildDefaultEndpoint(args) {
2368
2496
  };
2369
2497
  const headers = [{ key: "Content-Type", value: response.contentType, enabled: true }];
2370
2498
  return {
2371
- id: (0, import_shared4.generateId)(),
2499
+ id: (0, import_shared5.generateId)(),
2372
2500
  name: args.name ?? `${args.method} ${args.pathPattern}`,
2373
2501
  method: args.method,
2374
2502
  pathPattern: args.pathPattern,
2375
2503
  description: args.description,
2376
- requestSchema: (0, import_shared4.makeDefaultRequestSchema)(),
2504
+ requestSchema: (0, import_shared5.makeDefaultRequestSchema)(),
2377
2505
  requestValidation: [],
2378
2506
  responseRules: [],
2379
2507
  defaultResponse: {
2380
- ...(0, import_shared4.makeDefaultMockResponse)(),
2508
+ ...(0, import_shared5.makeDefaultMockResponse)(),
2381
2509
  status: response.status,
2382
2510
  headers,
2383
2511
  body: { type: "json", content: response.jsonBody }
@@ -2387,12 +2515,12 @@ function buildDefaultEndpoint(args) {
2387
2515
  var mockAddEndpointTool = {
2388
2516
  name: "mock.add_endpoint",
2389
2517
  description: "Append a new endpoint to a mock server. Defaults to a 200 JSON response of `{}`. Returns the new endpoint id.",
2390
- inputSchema: import_zod10.z.object({
2391
- mockId: import_zod10.z.string(),
2518
+ inputSchema: import_zod11.z.object({
2519
+ mockId: import_zod11.z.string(),
2392
2520
  method: HTTP_METHOD3,
2393
- pathPattern: import_zod10.z.string().min(1),
2394
- name: import_zod10.z.string().optional(),
2395
- description: import_zod10.z.string().optional(),
2521
+ pathPattern: import_zod11.z.string().min(1),
2522
+ name: import_zod11.z.string().optional(),
2523
+ description: import_zod11.z.string().optional(),
2396
2524
  response: ENDPOINT_RESPONSE2.optional()
2397
2525
  }),
2398
2526
  async handler(input, ctx) {
@@ -2415,13 +2543,13 @@ var mockAddEndpointTool = {
2415
2543
  var mockUpdateEndpointTool = {
2416
2544
  name: "mock.update_endpoint",
2417
2545
  description: "Patch fields on a single mock endpoint (method, pathPattern, name, description, defaultResponse status / contentType / json body). Pass only the fields you want to change.",
2418
- inputSchema: import_zod10.z.object({
2419
- mockId: import_zod10.z.string(),
2420
- endpointId: import_zod10.z.string(),
2546
+ inputSchema: import_zod11.z.object({
2547
+ mockId: import_zod11.z.string(),
2548
+ endpointId: import_zod11.z.string(),
2421
2549
  method: HTTP_METHOD3.optional(),
2422
- pathPattern: import_zod10.z.string().optional(),
2423
- name: import_zod10.z.string().optional(),
2424
- description: import_zod10.z.string().optional(),
2550
+ pathPattern: import_zod11.z.string().optional(),
2551
+ name: import_zod11.z.string().optional(),
2552
+ description: import_zod11.z.string().optional(),
2425
2553
  response: ENDPOINT_RESPONSE2.partial().optional()
2426
2554
  }),
2427
2555
  async handler(input, ctx) {
@@ -2462,7 +2590,7 @@ var mockUpdateEndpointTool = {
2462
2590
  var mockDeleteEndpointTool = {
2463
2591
  name: "mock.delete_endpoint",
2464
2592
  description: "Remove an endpoint from a mock server.",
2465
- inputSchema: import_zod10.z.object({ mockId: import_zod10.z.string(), endpointId: import_zod10.z.string() }),
2593
+ inputSchema: import_zod11.z.object({ mockId: import_zod11.z.string(), endpointId: import_zod11.z.string() }),
2466
2594
  async handler(input, ctx) {
2467
2595
  const state = await ctx.workspace.read();
2468
2596
  const mock = state.synced.mockServers[input.mockId];
@@ -2482,9 +2610,9 @@ var mockDeleteEndpointTool = {
2482
2610
  return { ok: true, changedIds: out.changedIds };
2483
2611
  }
2484
2612
  };
2485
- var VALIDATION_RULE = import_zod10.z.object({
2486
- id: import_zod10.z.string().optional(),
2487
- kind: import_zod10.z.enum([
2613
+ var VALIDATION_RULE = import_zod11.z.object({
2614
+ id: import_zod11.z.string().optional(),
2615
+ kind: import_zod11.z.enum([
2488
2616
  "header-required",
2489
2617
  "header-equals",
2490
2618
  "header-matches",
@@ -2495,43 +2623,43 @@ var VALIDATION_RULE = import_zod10.z.object({
2495
2623
  "body-required",
2496
2624
  "content-type-equals"
2497
2625
  ]),
2498
- target: import_zod10.z.string().default(""),
2499
- expected: import_zod10.z.string().optional(),
2500
- message: import_zod10.z.string().optional(),
2501
- enabled: import_zod10.z.boolean().default(true),
2502
- failResponse: import_zod10.z.object({
2503
- status: import_zod10.z.number().int().min(100).max(599).default(400),
2504
- jsonBody: import_zod10.z.string().default('{"error":"validation failed"}')
2626
+ target: import_zod11.z.string().default(""),
2627
+ expected: import_zod11.z.string().optional(),
2628
+ message: import_zod11.z.string().optional(),
2629
+ enabled: import_zod11.z.boolean().default(true),
2630
+ failResponse: import_zod11.z.object({
2631
+ status: import_zod11.z.number().int().min(100).max(599).default(400),
2632
+ jsonBody: import_zod11.z.string().default('{"error":"validation failed"}')
2505
2633
  }).default({})
2506
2634
  });
2507
- var CONDITION_CLAUSE = import_zod10.z.object({
2508
- id: import_zod10.z.string().optional(),
2509
- scope: import_zod10.z.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
2510
- target: import_zod10.z.string(),
2511
- op: import_zod10.z.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
2512
- value: import_zod10.z.string().optional()
2635
+ var CONDITION_CLAUSE = import_zod11.z.object({
2636
+ id: import_zod11.z.string().optional(),
2637
+ scope: import_zod11.z.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
2638
+ target: import_zod11.z.string(),
2639
+ op: import_zod11.z.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
2640
+ value: import_zod11.z.string().optional()
2513
2641
  });
2514
- var RESPONSE_RULE = import_zod10.z.object({
2515
- id: import_zod10.z.string().optional(),
2516
- name: import_zod10.z.string(),
2517
- enabled: import_zod10.z.boolean().default(true),
2518
- when: import_zod10.z.array(CONDITION_CLAUSE).default([]),
2519
- response: import_zod10.z.object({
2520
- status: import_zod10.z.number().int().min(100).max(599).default(200),
2521
- jsonBody: import_zod10.z.string().default("{}")
2642
+ var RESPONSE_RULE = import_zod11.z.object({
2643
+ id: import_zod11.z.string().optional(),
2644
+ name: import_zod11.z.string(),
2645
+ enabled: import_zod11.z.boolean().default(true),
2646
+ when: import_zod11.z.array(CONDITION_CLAUSE).default([]),
2647
+ response: import_zod11.z.object({
2648
+ status: import_zod11.z.number().int().min(100).max(599).default(200),
2649
+ jsonBody: import_zod11.z.string().default("{}")
2522
2650
  }).default({})
2523
2651
  });
2524
- var MULTIPLIER = import_zod10.z.object({
2525
- id: import_zod10.z.string().optional(),
2526
- name: import_zod10.z.string().optional(),
2527
- source: import_zod10.z.object({
2528
- kind: import_zod10.z.enum(["query", "pathParam", "header", "body-json-path"]),
2529
- key: import_zod10.z.string()
2652
+ var MULTIPLIER = import_zod11.z.object({
2653
+ id: import_zod11.z.string().optional(),
2654
+ name: import_zod11.z.string().optional(),
2655
+ source: import_zod11.z.object({
2656
+ kind: import_zod11.z.enum(["query", "pathParam", "header", "body-json-path"]),
2657
+ key: import_zod11.z.string()
2530
2658
  }),
2531
- targetJsonPath: import_zod10.z.string(),
2532
- defaultCount: import_zod10.z.number().int().nonnegative().default(0),
2533
- min: import_zod10.z.number().int().nonnegative().optional(),
2534
- max: import_zod10.z.number().int().nonnegative().optional()
2659
+ targetJsonPath: import_zod11.z.string(),
2660
+ defaultCount: import_zod11.z.number().int().nonnegative().default(0),
2661
+ min: import_zod11.z.number().int().nonnegative().optional(),
2662
+ max: import_zod11.z.number().int().nonnegative().optional()
2535
2663
  });
2536
2664
  function defaultJsonResponseConfig(args) {
2537
2665
  return {
@@ -2556,10 +2684,10 @@ function patchEndpoint2(mock, endpointId, patcher) {
2556
2684
  var mockSetValidationRulesTool = {
2557
2685
  name: "mock.set_validation_rules",
2558
2686
  description: "Replace an endpoint's validation rules. Rules without an `id` get a fresh one; existing rules can keep theirs to preserve client-side selection state. Empty array clears all rules.",
2559
- inputSchema: import_zod10.z.object({
2560
- mockId: import_zod10.z.string(),
2561
- endpointId: import_zod10.z.string(),
2562
- rules: import_zod10.z.array(VALIDATION_RULE)
2687
+ inputSchema: import_zod11.z.object({
2688
+ mockId: import_zod11.z.string(),
2689
+ endpointId: import_zod11.z.string(),
2690
+ rules: import_zod11.z.array(VALIDATION_RULE)
2563
2691
  }),
2564
2692
  async handler(input, ctx) {
2565
2693
  const state = await ctx.workspace.read();
@@ -2569,7 +2697,7 @@ var mockSetValidationRulesTool = {
2569
2697
  const next = patchEndpoint2(mock, input.endpointId, (e) => ({
2570
2698
  ...e,
2571
2699
  requestValidation: rules.map((r) => ({
2572
- id: r.id ?? (0, import_shared4.generateId)(),
2700
+ id: r.id ?? (0, import_shared5.generateId)(),
2573
2701
  kind: r.kind,
2574
2702
  target: r.target,
2575
2703
  expected: r.expected,
@@ -2586,10 +2714,10 @@ var mockSetValidationRulesTool = {
2586
2714
  var mockSetResponseRulesTool = {
2587
2715
  name: "mock.set_response_rules",
2588
2716
  description: "Replace an endpoint's conditional response rules. Rules fire in order; the first whose every clause matches wins. Disabled rules are skipped. Empty array falls back to defaultResponse.",
2589
- inputSchema: import_zod10.z.object({
2590
- mockId: import_zod10.z.string(),
2591
- endpointId: import_zod10.z.string(),
2592
- rules: import_zod10.z.array(RESPONSE_RULE)
2717
+ inputSchema: import_zod11.z.object({
2718
+ mockId: import_zod11.z.string(),
2719
+ endpointId: import_zod11.z.string(),
2720
+ rules: import_zod11.z.array(RESPONSE_RULE)
2593
2721
  }),
2594
2722
  async handler(input, ctx) {
2595
2723
  const state = await ctx.workspace.read();
@@ -2599,11 +2727,11 @@ var mockSetResponseRulesTool = {
2599
2727
  const next = patchEndpoint2(mock, input.endpointId, (e) => ({
2600
2728
  ...e,
2601
2729
  responseRules: rules.map((r) => ({
2602
- id: r.id ?? (0, import_shared4.generateId)(),
2730
+ id: r.id ?? (0, import_shared5.generateId)(),
2603
2731
  name: r.name,
2604
2732
  enabled: r.enabled,
2605
2733
  when: r.when.map((c) => ({
2606
- id: c.id ?? (0, import_shared4.generateId)(),
2734
+ id: c.id ?? (0, import_shared5.generateId)(),
2607
2735
  scope: c.scope,
2608
2736
  target: c.target,
2609
2737
  op: c.op,
@@ -2620,10 +2748,10 @@ var mockSetResponseRulesTool = {
2620
2748
  var mockSetMultipliersTool = {
2621
2749
  name: "mock.set_multipliers",
2622
2750
  description: "Replace the response multipliers on an endpoint's defaultResponse. Multipliers expand an array at `targetJsonPath` to a count derived from a request value. Empty array clears all multipliers.",
2623
- inputSchema: import_zod10.z.object({
2624
- mockId: import_zod10.z.string(),
2625
- endpointId: import_zod10.z.string(),
2626
- multipliers: import_zod10.z.array(MULTIPLIER)
2751
+ inputSchema: import_zod11.z.object({
2752
+ mockId: import_zod11.z.string(),
2753
+ endpointId: import_zod11.z.string(),
2754
+ multipliers: import_zod11.z.array(MULTIPLIER)
2627
2755
  }),
2628
2756
  async handler(input, ctx) {
2629
2757
  const state = await ctx.workspace.read();
@@ -2635,7 +2763,7 @@ var mockSetMultipliersTool = {
2635
2763
  defaultResponse: {
2636
2764
  ...e.defaultResponse,
2637
2765
  multipliers: multipliers.length === 0 ? void 0 : multipliers.map((m) => ({
2638
- id: m.id ?? (0, import_shared4.generateId)(),
2766
+ id: m.id ?? (0, import_shared5.generateId)(),
2639
2767
  name: m.name,
2640
2768
  source: { kind: m.source.kind, key: m.source.key },
2641
2769
  targetJsonPath: m.targetJsonPath,
@@ -2711,6 +2839,10 @@ var TOOL_REGISTRY = [
2711
2839
  promptSetEndpointValidationRulesTool,
2712
2840
  promptSetEndpointResponseRulesTool,
2713
2841
  promptSetEndpointMultipliersTool,
2842
+ globalAssetsFilesListTool,
2843
+ globalAssetsFilesCreateTool,
2844
+ globalAssetsFilesUpdateTool,
2845
+ globalAssetsFilesDeleteTool,
2714
2846
  mockCreateFromOpenApiTool,
2715
2847
  mockCreateFromPostmanTool,
2716
2848
  mockCreateFromInsomniaTool,
@@ -2851,17 +2983,58 @@ var FileBackedWorkspaceProvider = class {
2851
2983
 
2852
2984
  // src/providers/MultiWorkspaceProvider.ts
2853
2985
  var import_registry = require("@apicircle/core/workspace/registry");
2986
+ var LazyActiveWorkspaceProvider = class {
2987
+ constructor(registryRoot, onActiveResolved) {
2988
+ this.registryRoot = registryRoot;
2989
+ this.onActiveResolved = onActiveResolved;
2990
+ }
2991
+ registryRoot;
2992
+ onActiveResolved;
2993
+ async resolveActive() {
2994
+ const registry = await (0, import_registry.loadRegistry)(this.registryRoot);
2995
+ const activeId = registry?.activeWorkspaceId ?? null;
2996
+ if (!activeId) {
2997
+ throw new Error(
2998
+ "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
2999
+ );
3000
+ }
3001
+ this.onActiveResolved(activeId);
3002
+ return new FileBackedWorkspaceProvider((0, import_registry.workspaceDirFor)(this.registryRoot, activeId));
3003
+ }
3004
+ async read() {
3005
+ const provider = await this.resolveActive();
3006
+ return provider.read();
3007
+ }
3008
+ async apply(patch) {
3009
+ const provider = await this.resolveActive();
3010
+ return provider.apply(patch);
3011
+ }
3012
+ async write(next) {
3013
+ const provider = await this.resolveActive();
3014
+ return provider.write(next);
3015
+ }
3016
+ };
2854
3017
  var MultiWorkspaceProvider = class {
2855
3018
  constructor(registryRoot) {
2856
3019
  this.registryRoot = registryRoot;
3020
+ this.lazyProvider = new LazyActiveWorkspaceProvider(this.registryRoot, (id) => {
3021
+ this.activeWorkspaceId = id;
3022
+ });
2857
3023
  }
2858
3024
  registryRoot;
2859
- active = null;
3025
+ /** Last-known active workspace id. Refreshed every time the lazy
3026
+ * provider resolves; reflects what the most recent operation saw on
3027
+ * disk, not a stale boot-time snapshot. */
2860
3028
  activeWorkspaceId = null;
3029
+ /** The lazy provider tool handlers consume as `ctx.workspace`. Holds a
3030
+ * reference back to this instance so each call updates
3031
+ * `activeWorkspaceId` for `activeId()` callers + diagnostic logs. */
3032
+ lazyProvider;
2861
3033
  /**
2862
- * Hydrate the active provider from disk. Must be called once before the
2863
- * MCP host boots so `ctx.workspace.read()` doesn't race the first
2864
- * registry-load. Returns the registry the boot can log.
3034
+ * Read the registry from disk so the host can log a boot banner. Does
3035
+ * NOT cache a per-id provider — each `activeProvider()` call re-reads
3036
+ * the registry, so a workspace switch in the desktop is picked up by
3037
+ * the next tool call without restarting the MCP server.
2865
3038
  */
2866
3039
  async init() {
2867
3040
  const registry = await (0, import_registry.loadRegistry)(this.registryRoot) ?? {
@@ -2869,22 +3042,18 @@ var MultiWorkspaceProvider = class {
2869
3042
  activeWorkspaceId: null,
2870
3043
  workspaces: []
2871
3044
  };
2872
- if (registry.activeWorkspaceId) {
2873
- this.activeWorkspaceId = registry.activeWorkspaceId;
2874
- this.active = new FileBackedWorkspaceProvider(
2875
- (0, import_registry.workspaceDirFor)(this.registryRoot, registry.activeWorkspaceId)
2876
- );
2877
- }
3045
+ this.activeWorkspaceId = registry.activeWorkspaceId;
2878
3046
  return registry;
2879
3047
  }
2880
- /** The provider tool handlers see as `ctx.workspace`. */
3048
+ /**
3049
+ * The provider tool handlers see as `ctx.workspace`. Returns a lazy
3050
+ * provider whose `read` / `apply` / `write` calls re-read
3051
+ * `registry.json` so the right active workspace is always targeted
3052
+ * even if the desktop switched workspaces since this MCP process
3053
+ * started.
3054
+ */
2881
3055
  activeProvider() {
2882
- if (!this.active) {
2883
- throw new Error(
2884
- "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
2885
- );
2886
- }
2887
- return this.active;
3056
+ return this.lazyProvider;
2888
3057
  }
2889
3058
  // ─── Workspaces interface ──────────────────────────────────────────────────
2890
3059
  async list() {
@@ -2932,23 +3101,17 @@ var MultiWorkspaceProvider = class {
2932
3101
  if (!registry || !registry.workspaces.some((w) => w.id === workspaceId)) {
2933
3102
  throw new WorkspaceNotFoundError(workspaceId);
2934
3103
  }
2935
- const next = await (0, import_registry.setActiveWorkspace)(this.registryRoot, workspaceId);
2936
- void next;
3104
+ await (0, import_registry.setActiveWorkspace)(this.registryRoot, workspaceId);
2937
3105
  this.activeWorkspaceId = workspaceId;
2938
- this.active = new FileBackedWorkspaceProvider((0, import_registry.workspaceDirFor)(this.registryRoot, workspaceId));
2939
3106
  }
2940
3107
  /**
2941
3108
  * Idempotent registry write — used by tests / tools that need to
2942
- * persist registry updates that didn't go through `setActive`.
3109
+ * persist registry updates that didn't go through `setActive`. The
3110
+ * lazy active provider picks the new id up on its next operation.
2943
3111
  */
2944
3112
  async writeRegistry(registry) {
2945
3113
  await (0, import_registry.saveRegistry)(this.registryRoot, registry);
2946
3114
  this.activeWorkspaceId = registry.activeWorkspaceId;
2947
- if (registry.activeWorkspaceId) {
2948
- this.active = new FileBackedWorkspaceProvider(
2949
- (0, import_registry.workspaceDirFor)(this.registryRoot, registry.activeWorkspaceId)
2950
- );
2951
- }
2952
3115
  }
2953
3116
  };
2954
3117