@apicircle/mcp-server 1.0.1 → 1.0.3

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.
@@ -25,6 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/bin/mcp-server.ts
27
27
  var path = __toESM(require("path"), 1);
28
+ var import_node_fs = require("fs");
28
29
 
29
30
  // src/host/McpHost.ts
30
31
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
@@ -458,18 +459,37 @@ function renderRust(req) {
458
459
  return lines.join("\n");
459
460
  }
460
461
 
461
- // src/tools/crud.ts
462
+ // src/tools/workspaceList.ts
462
463
  var import_zod4 = require("zod");
464
+ var workspaceListTool = {
465
+ name: "workspace.list",
466
+ description: "List every workspace registered with this server, including which one is currently active. Returns id, display name, last-opened timestamp, and a per-workspace summary (request count, environment count, mock-server count, plan count). Use this BEFORE drilling into a specific workspace via other tools \u2014 pass the resulting `id` as `workspaceId` to `workspace.read` or related reads when you want to scope to a non-active workspace.",
467
+ inputSchema: import_zod4.z.object({}),
468
+ async handler(_input, ctx) {
469
+ const summaries = await ctx.workspaces.list();
470
+ return {
471
+ activeWorkspaceId: ctx.workspaces.activeId(),
472
+ workspaceCount: summaries.length,
473
+ workspaces: summaries,
474
+ // Plain-text hint the AI surfaces when telling the user. Cheap to
475
+ // generate here and saves round-trips on disambiguation prompts.
476
+ hint: summaries.length === 0 ? "No workspaces are registered yet. The user should open the desktop app once or run `apicircle workspaces create <name>` from the terminal." : summaries.length === 1 ? `Only one workspace ("${summaries[0].name}") is registered \u2014 most tools will default to it without a workspaceId.` : `Multiple workspaces are registered. Pass the desired \`id\` as \`workspaceId\` to other tools to scope reads/writes to that workspace; the active one ("${summaries.find((w) => w.isActive)?.name ?? "(none)"}") is used by default.`
477
+ };
478
+ }
479
+ };
480
+
481
+ // src/tools/crud.ts
482
+ var import_zod5 = require("zod");
463
483
  var import_shared2 = require("@apicircle/shared");
464
- var HTTP_METHOD = import_zod4.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
484
+ var HTTP_METHOD = import_zod5.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
465
485
  var requestCreateTool = {
466
486
  name: "request.create",
467
487
  description: "Create a new request from explicit fields and persist it.",
468
- inputSchema: import_zod4.z.object({
469
- name: import_zod4.z.string().default("New request"),
488
+ inputSchema: import_zod5.z.object({
489
+ name: import_zod5.z.string().default("New request"),
470
490
  method: HTTP_METHOD.default("GET"),
471
- url: import_zod4.z.string().default(""),
472
- folderId: import_zod4.z.string().nullable().optional()
491
+ url: import_zod5.z.string().default(""),
492
+ folderId: import_zod5.z.string().nullable().optional()
473
493
  }),
474
494
  async handler(input, ctx) {
475
495
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -498,7 +518,7 @@ var requestCreateTool = {
498
518
  var requestReadTool = {
499
519
  name: "request.read",
500
520
  description: "Read a request by id, or list summaries (id, name, method, url) when no id is provided.",
501
- inputSchema: import_zod4.z.object({ id: import_zod4.z.string().optional() }),
521
+ inputSchema: import_zod5.z.object({ id: import_zod5.z.string().optional() }),
502
522
  async handler(input, ctx) {
503
523
  const state = await ctx.workspace.read();
504
524
  if (input.id) {
@@ -519,13 +539,13 @@ var requestReadTool = {
519
539
  var requestUpdateTool = {
520
540
  name: "request.update",
521
541
  description: "Patch fields on an existing request.",
522
- inputSchema: import_zod4.z.object({
523
- id: import_zod4.z.string(),
524
- patch: import_zod4.z.object({
525
- name: import_zod4.z.string().optional(),
542
+ inputSchema: import_zod5.z.object({
543
+ id: import_zod5.z.string(),
544
+ patch: import_zod5.z.object({
545
+ name: import_zod5.z.string().optional(),
526
546
  method: HTTP_METHOD.optional(),
527
- url: import_zod4.z.string().optional(),
528
- folderId: import_zod4.z.string().nullable().optional()
547
+ url: import_zod5.z.string().optional(),
548
+ folderId: import_zod5.z.string().nullable().optional()
529
549
  }).strict()
530
550
  }),
531
551
  async handler(input, ctx) {
@@ -540,7 +560,7 @@ var requestUpdateTool = {
540
560
  var requestDeleteTool = {
541
561
  name: "request.delete",
542
562
  description: "Delete a request by id.",
543
- inputSchema: import_zod4.z.object({ id: import_zod4.z.string() }),
563
+ inputSchema: import_zod5.z.object({ id: import_zod5.z.string() }),
544
564
  async handler(input, ctx) {
545
565
  const out = await ctx.workspace.apply({ kind: "request.delete", id: input.id });
546
566
  return { changedIds: out.changedIds };
@@ -549,9 +569,9 @@ var requestDeleteTool = {
549
569
  var folderCreateTool = {
550
570
  name: "folder.create",
551
571
  description: "Create a folder under an optional parent folder.",
552
- inputSchema: import_zod4.z.object({
553
- name: import_zod4.z.string().default("New folder"),
554
- parentId: import_zod4.z.string().nullable().optional()
572
+ inputSchema: import_zod5.z.object({
573
+ name: import_zod5.z.string().default("New folder"),
574
+ parentId: import_zod5.z.string().nullable().optional()
555
575
  }),
556
576
  async handler(input, ctx) {
557
577
  const folder = {
@@ -566,7 +586,7 @@ var folderCreateTool = {
566
586
  var folderReadTool = {
567
587
  name: "folder.read",
568
588
  description: "Read a folder by id, or list all folders when no id is provided.",
569
- inputSchema: import_zod4.z.object({ id: import_zod4.z.string().optional() }),
589
+ inputSchema: import_zod5.z.object({ id: import_zod5.z.string().optional() }),
570
590
  async handler(input, ctx) {
571
591
  const state = await ctx.workspace.read();
572
592
  if (input.id) {
@@ -582,9 +602,9 @@ var folderReadTool = {
582
602
  var folderUpdateTool = {
583
603
  name: "folder.update",
584
604
  description: "Move a folder to a new parent (or to root with parentId: null).",
585
- inputSchema: import_zod4.z.object({
586
- id: import_zod4.z.string(),
587
- parentId: import_zod4.z.string().nullable()
605
+ inputSchema: import_zod5.z.object({
606
+ id: import_zod5.z.string(),
607
+ parentId: import_zod5.z.string().nullable()
588
608
  }),
589
609
  async handler(input, ctx) {
590
610
  const out = await ctx.workspace.apply({
@@ -598,23 +618,23 @@ var folderUpdateTool = {
598
618
  var folderDeleteTool = {
599
619
  name: "folder.delete",
600
620
  description: "Delete a folder. Direct children (sub-folders + requests) are reparented to the deleted folder's parent.",
601
- inputSchema: import_zod4.z.object({ id: import_zod4.z.string() }),
621
+ inputSchema: import_zod5.z.object({ id: import_zod5.z.string() }),
602
622
  async handler(input, ctx) {
603
623
  const out = await ctx.workspace.apply({ kind: "folder.delete", id: input.id });
604
624
  return { changedIds: out.changedIds };
605
625
  }
606
626
  };
607
- var VARIABLE = import_zod4.z.object({
608
- key: import_zod4.z.string(),
609
- value: import_zod4.z.string(),
610
- encrypted: import_zod4.z.boolean().default(false)
627
+ var VARIABLE = import_zod5.z.object({
628
+ key: import_zod5.z.string(),
629
+ value: import_zod5.z.string(),
630
+ encrypted: import_zod5.z.boolean().default(false)
611
631
  });
612
632
  var environmentCreateTool = {
613
633
  name: "environment.create",
614
634
  description: "Create a new environment (or upsert one with the same name).",
615
- inputSchema: import_zod4.z.object({
616
- name: import_zod4.z.string(),
617
- variables: import_zod4.z.array(VARIABLE).default([])
635
+ inputSchema: import_zod5.z.object({
636
+ name: import_zod5.z.string(),
637
+ variables: import_zod5.z.array(VARIABLE).default([])
618
638
  }),
619
639
  async handler(input, ctx) {
620
640
  const env = { name: input.name, variables: input.variables };
@@ -625,7 +645,7 @@ var environmentCreateTool = {
625
645
  var environmentReadTool = {
626
646
  name: "environment.read",
627
647
  description: "Read environments \u2014 pass `name` for one, or omit for the full list.",
628
- inputSchema: import_zod4.z.object({ name: import_zod4.z.string().optional() }),
648
+ inputSchema: import_zod5.z.object({ name: import_zod5.z.string().optional() }),
629
649
  async handler(input, ctx) {
630
650
  const state = await ctx.workspace.read();
631
651
  if (input.name) {
@@ -642,9 +662,9 @@ var environmentReadTool = {
642
662
  var environmentUpdateTool = {
643
663
  name: "environment.update",
644
664
  description: "Replace the variables list of an environment.",
645
- inputSchema: import_zod4.z.object({
646
- name: import_zod4.z.string(),
647
- variables: import_zod4.z.array(VARIABLE)
665
+ inputSchema: import_zod5.z.object({
666
+ name: import_zod5.z.string(),
667
+ variables: import_zod5.z.array(VARIABLE)
648
668
  }),
649
669
  async handler(input, ctx) {
650
670
  const out = await ctx.workspace.apply({
@@ -657,7 +677,7 @@ var environmentUpdateTool = {
657
677
  var environmentDeleteTool = {
658
678
  name: "environment.delete",
659
679
  description: "Delete an environment by name.",
660
- inputSchema: import_zod4.z.object({ name: import_zod4.z.string() }),
680
+ inputSchema: import_zod5.z.object({ name: import_zod5.z.string() }),
661
681
  async handler(input, ctx) {
662
682
  const out = await ctx.workspace.apply({ kind: "environment.delete", name: input.name });
663
683
  return { changedIds: out.changedIds };
@@ -666,7 +686,7 @@ var environmentDeleteTool = {
666
686
  var environmentSetActiveTool = {
667
687
  name: "environment.set_active",
668
688
  description: "Set (or clear) the active environment. Pass `name: null` to deactivate the current environment.",
669
- inputSchema: import_zod4.z.object({ name: import_zod4.z.string().nullable() }),
689
+ inputSchema: import_zod5.z.object({ name: import_zod5.z.string().nullable() }),
670
690
  async handler(input, ctx) {
671
691
  const out = await ctx.workspace.apply({
672
692
  kind: "environment.setActive",
@@ -678,18 +698,18 @@ var environmentSetActiveTool = {
678
698
  var environmentSetPriorityTool = {
679
699
  name: "environment.set_priority",
680
700
  description: 'Replace the global environment priority order (highest priority first). Strings are interpreted as local env names. To target a linked env, pass `{ kind: "linked", linkedWorkspaceId, envName }` instead.',
681
- inputSchema: import_zod4.z.object({
682
- order: import_zod4.z.array(
683
- import_zod4.z.union([
684
- import_zod4.z.string(),
685
- import_zod4.z.object({
686
- kind: import_zod4.z.literal("local"),
687
- name: import_zod4.z.string()
701
+ inputSchema: import_zod5.z.object({
702
+ order: import_zod5.z.array(
703
+ import_zod5.z.union([
704
+ import_zod5.z.string(),
705
+ import_zod5.z.object({
706
+ kind: import_zod5.z.literal("local"),
707
+ name: import_zod5.z.string()
688
708
  }),
689
- import_zod4.z.object({
690
- kind: import_zod4.z.literal("linked"),
691
- linkedWorkspaceId: import_zod4.z.string(),
692
- envName: import_zod4.z.string()
709
+ import_zod5.z.object({
710
+ kind: import_zod5.z.literal("linked"),
711
+ linkedWorkspaceId: import_zod5.z.string(),
712
+ envName: import_zod5.z.string()
693
713
  })
694
714
  ])
695
715
  )
@@ -706,7 +726,7 @@ var environmentSetPriorityTool = {
706
726
  var environmentExportTool = {
707
727
  name: "environment.export",
708
728
  description: "Serialize an environment to a portable JSON string. Encrypted variables drop their value (only `secretKeyId` survives) so the export can be safely pasted elsewhere \u2014 re-attach secrets locally on the receiving side.",
709
- inputSchema: import_zod4.z.object({ name: import_zod4.z.string() }),
729
+ inputSchema: import_zod5.z.object({ name: import_zod5.z.string() }),
710
730
  async handler(input, ctx) {
711
731
  const state = await ctx.workspace.read();
712
732
  const env = state.synced.environments.items[input.name];
@@ -724,9 +744,9 @@ var environmentExportTool = {
724
744
  var environmentImportTool = {
725
745
  name: "environment.import",
726
746
  description: "Import an environment from the JSON shape produced by `environment.export`. When a target with the same name exists, pass `overwrite: true` to replace it, otherwise the import is rejected.",
727
- inputSchema: import_zod4.z.object({
728
- json: import_zod4.z.string().min(1),
729
- overwrite: import_zod4.z.boolean().default(false)
747
+ inputSchema: import_zod5.z.object({
748
+ json: import_zod5.z.string().min(1),
749
+ overwrite: import_zod5.z.boolean().default(false)
730
750
  }),
731
751
  async handler(input, ctx) {
732
752
  let parsed;
@@ -756,17 +776,17 @@ var environmentImportTool = {
756
776
  return { ok: true, name: env.name, changedIds: out.changedIds };
757
777
  }
758
778
  };
759
- var PLAN_STEP = import_zod4.z.object({
760
- requestId: import_zod4.z.string(),
761
- linkedWorkspaceId: import_zod4.z.string().optional()
779
+ var PLAN_STEP = import_zod5.z.object({
780
+ requestId: import_zod5.z.string(),
781
+ linkedWorkspaceId: import_zod5.z.string().optional()
762
782
  });
763
783
  var planCreateTool = {
764
784
  name: "plan.create",
765
785
  description: "Create a new execution plan (sequence of request steps).",
766
- inputSchema: import_zod4.z.object({
767
- name: import_zod4.z.string().default("New plan"),
768
- steps: import_zod4.z.array(PLAN_STEP).default([]),
769
- envPriorityOrder: import_zod4.z.array(import_zod4.z.string()).default([])
786
+ inputSchema: import_zod5.z.object({
787
+ name: import_zod5.z.string().default("New plan"),
788
+ steps: import_zod5.z.array(PLAN_STEP).default([]),
789
+ envPriorityOrder: import_zod5.z.array(import_zod5.z.string()).default([])
770
790
  }),
771
791
  async handler(input, ctx) {
772
792
  const id = (0, import_shared2.generateId)();
@@ -786,7 +806,7 @@ var planCreateTool = {
786
806
  var planReadTool = {
787
807
  name: "plan.read",
788
808
  description: "Read a plan by id, or list all plans when no id is provided.",
789
- inputSchema: import_zod4.z.object({ id: import_zod4.z.string().optional() }),
809
+ inputSchema: import_zod5.z.object({ id: import_zod5.z.string().optional() }),
790
810
  async handler(input, ctx) {
791
811
  const state = await ctx.workspace.read();
792
812
  if (input.id) {
@@ -802,12 +822,12 @@ var planReadTool = {
802
822
  var planUpdateTool = {
803
823
  name: "plan.update",
804
824
  description: "Patch fields on an existing plan.",
805
- inputSchema: import_zod4.z.object({
806
- id: import_zod4.z.string(),
807
- patch: import_zod4.z.object({
808
- name: import_zod4.z.string().optional(),
809
- steps: import_zod4.z.array(PLAN_STEP).optional(),
810
- envPriorityOrder: import_zod4.z.array(import_zod4.z.string()).optional()
825
+ inputSchema: import_zod5.z.object({
826
+ id: import_zod5.z.string(),
827
+ patch: import_zod5.z.object({
828
+ name: import_zod5.z.string().optional(),
829
+ steps: import_zod5.z.array(PLAN_STEP).optional(),
830
+ envPriorityOrder: import_zod5.z.array(import_zod5.z.string()).optional()
811
831
  }).strict()
812
832
  }),
813
833
  async handler(input, ctx) {
@@ -828,7 +848,7 @@ var planUpdateTool = {
828
848
  var planDeleteTool = {
829
849
  name: "plan.delete",
830
850
  description: "Delete a plan by id. Drops history rows referencing this plan.",
831
- inputSchema: import_zod4.z.object({ id: import_zod4.z.string() }),
851
+ inputSchema: import_zod5.z.object({ id: import_zod5.z.string() }),
832
852
  async handler(input, ctx) {
833
853
  const out = await ctx.workspace.apply({ kind: "plan.delete", id: input.id });
834
854
  return { changedIds: out.changedIds };
@@ -837,11 +857,11 @@ var planDeleteTool = {
837
857
  var planAddStepTool = {
838
858
  name: "plan.add_step",
839
859
  description: "Append a step to an execution plan. Optional `position` (0-based) inserts at that index instead.",
840
- inputSchema: import_zod4.z.object({
841
- planId: import_zod4.z.string(),
842
- requestId: import_zod4.z.string(),
843
- linkedWorkspaceId: import_zod4.z.string().optional(),
844
- position: import_zod4.z.number().int().nonnegative().optional()
860
+ inputSchema: import_zod5.z.object({
861
+ planId: import_zod5.z.string(),
862
+ requestId: import_zod5.z.string(),
863
+ linkedWorkspaceId: import_zod5.z.string().optional(),
864
+ position: import_zod5.z.number().int().nonnegative().optional()
845
865
  }),
846
866
  async handler(input, ctx) {
847
867
  const state = await ctx.workspace.read();
@@ -867,9 +887,9 @@ var planAddStepTool = {
867
887
  var planRemoveStepTool = {
868
888
  name: "plan.remove_step",
869
889
  description: "Remove a step from a plan by 0-based index.",
870
- inputSchema: import_zod4.z.object({
871
- planId: import_zod4.z.string(),
872
- index: import_zod4.z.number().int().nonnegative()
890
+ inputSchema: import_zod5.z.object({
891
+ planId: import_zod5.z.string(),
892
+ index: import_zod5.z.number().int().nonnegative()
873
893
  }),
874
894
  async handler(input, ctx) {
875
895
  const state = await ctx.workspace.read();
@@ -889,9 +909,9 @@ var planRemoveStepTool = {
889
909
  var planReorderStepsTool = {
890
910
  name: "plan.reorder_steps",
891
911
  description: "Replace the plan steps with a new permutation. The supplied indices must reference valid current step indices.",
892
- inputSchema: import_zod4.z.object({
893
- planId: import_zod4.z.string(),
894
- order: import_zod4.z.array(import_zod4.z.number().int().nonnegative())
912
+ inputSchema: import_zod5.z.object({
913
+ planId: import_zod5.z.string(),
914
+ order: import_zod5.z.array(import_zod5.z.number().int().nonnegative())
895
915
  }),
896
916
  async handler(input, ctx) {
897
917
  const state = await ctx.workspace.read();
@@ -913,13 +933,13 @@ var planReorderStepsTool = {
913
933
  return { ok: true, changedIds: out.changedIds };
914
934
  }
915
935
  };
916
- var PLAN_VARIABLE = import_zod4.z.object({ key: import_zod4.z.string(), value: import_zod4.z.string() });
936
+ var PLAN_VARIABLE = import_zod5.z.object({ key: import_zod5.z.string(), value: import_zod5.z.string() });
917
937
  var planSetVariablesTool = {
918
938
  name: "plan.set_variables",
919
939
  description: "Replace the plan-scoped variables. These live highest-priority during plan runs (above environment vars, below context vars).",
920
- inputSchema: import_zod4.z.object({
921
- planId: import_zod4.z.string(),
922
- variables: import_zod4.z.array(PLAN_VARIABLE)
940
+ inputSchema: import_zod5.z.object({
941
+ planId: import_zod5.z.string(),
942
+ variables: import_zod5.z.array(PLAN_VARIABLE)
923
943
  }),
924
944
  async handler(input, ctx) {
925
945
  const state = await ctx.workspace.read();
@@ -935,9 +955,9 @@ var planSetVariablesTool = {
935
955
  var planRunTool = {
936
956
  name: "plan.run",
937
957
  description: "Run a plan headlessly (server-side). Currently returns a not-implemented marker \u2014 full execution requires the Desktop or browser runtime which the MCP host does not own. The Desktop integration overrides this tool with a real runner.",
938
- inputSchema: import_zod4.z.object({
939
- id: import_zod4.z.string(),
940
- withAssertions: import_zod4.z.boolean().default(true)
958
+ inputSchema: import_zod5.z.object({
959
+ id: import_zod5.z.string(),
960
+ withAssertions: import_zod5.z.boolean().default(true)
941
961
  }),
942
962
  async handler(input, ctx) {
943
963
  const state = await ctx.workspace.read();
@@ -951,18 +971,18 @@ var planRunTool = {
951
971
  };
952
972
  }
953
973
  };
954
- var ASSERTION = import_zod4.z.object({
955
- id: import_zod4.z.string().optional(),
956
- kind: import_zod4.z.enum(["status", "header", "json-path", "duration"]),
957
- op: import_zod4.z.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
958
- target: import_zod4.z.string().optional(),
959
- expected: import_zod4.z.union([import_zod4.z.string(), import_zod4.z.number()])
974
+ var ASSERTION = import_zod5.z.object({
975
+ id: import_zod5.z.string().optional(),
976
+ kind: import_zod5.z.enum(["status", "header", "json-path", "duration"]),
977
+ op: import_zod5.z.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
978
+ target: import_zod5.z.string().optional(),
979
+ expected: import_zod5.z.union([import_zod5.z.string(), import_zod5.z.number()])
960
980
  });
961
981
  var assertionCreateTool = {
962
982
  name: "assertion.create",
963
983
  description: "Add an assertion to a request.",
964
- inputSchema: import_zod4.z.object({
965
- requestId: import_zod4.z.string(),
984
+ inputSchema: import_zod5.z.object({
985
+ requestId: import_zod5.z.string(),
966
986
  assertion: ASSERTION
967
987
  }),
968
988
  async handler(input, ctx) {
@@ -981,9 +1001,9 @@ var assertionCreateTool = {
981
1001
  var assertionReadTool = {
982
1002
  name: "assertion.read",
983
1003
  description: "List assertions for a request, or fetch a single assertion by id.",
984
- inputSchema: import_zod4.z.object({
985
- requestId: import_zod4.z.string(),
986
- assertionId: import_zod4.z.string().optional()
1004
+ inputSchema: import_zod5.z.object({
1005
+ requestId: import_zod5.z.string(),
1006
+ assertionId: import_zod5.z.string().optional()
987
1007
  }),
988
1008
  async handler(input, ctx) {
989
1009
  const state = await ctx.workspace.read();
@@ -999,8 +1019,8 @@ var assertionReadTool = {
999
1019
  var assertionUpdateTool = {
1000
1020
  name: "assertion.update",
1001
1021
  description: "Replace an existing assertion (matched by `assertion.id`).",
1002
- inputSchema: import_zod4.z.object({
1003
- requestId: import_zod4.z.string(),
1022
+ inputSchema: import_zod5.z.object({
1023
+ requestId: import_zod5.z.string(),
1004
1024
  assertion: ASSERTION.required({ id: true })
1005
1025
  }),
1006
1026
  async handler(input, ctx) {
@@ -1015,9 +1035,9 @@ var assertionUpdateTool = {
1015
1035
  var assertionDeleteTool = {
1016
1036
  name: "assertion.delete",
1017
1037
  description: "Remove an assertion from a request.",
1018
- inputSchema: import_zod4.z.object({
1019
- requestId: import_zod4.z.string(),
1020
- assertionId: import_zod4.z.string()
1038
+ inputSchema: import_zod5.z.object({
1039
+ requestId: import_zod5.z.string(),
1040
+ assertionId: import_zod5.z.string()
1021
1041
  }),
1022
1042
  async handler(input, ctx) {
1023
1043
  const out = await ctx.workspace.apply({
@@ -1030,21 +1050,53 @@ var assertionDeleteTool = {
1030
1050
  };
1031
1051
  var workspaceReadTool = {
1032
1052
  name: "workspace.read",
1033
- description: "Return the full `{ synced, local }` workspace pair. Use sparingly \u2014 entity-specific tools are more efficient for small reads.",
1034
- inputSchema: import_zod4.z.object({}),
1035
- async handler(_input, ctx) {
1036
- return await ctx.workspace.read();
1053
+ description: 'Return the full `{ synced, local }` workspace pair. Pass `workspaceId` to scope to a specific workspace when multiple are registered (call `workspace.list` first to discover ids). When omitted and multiple workspaces exist, the response is a structured "multiple workspaces found" envelope listing each summary so the AI can clarify before drilling in. Use sparingly \u2014 entity-specific tools are more efficient for small reads.',
1054
+ inputSchema: import_zod5.z.object({
1055
+ workspaceId: import_zod5.z.string().min(1).max(256).optional().describe(
1056
+ 'Optional workspace id (from `workspace.list`). Omit to read the active workspace; the tool will switch to the "multiple workspaces" envelope when ambiguous.'
1057
+ )
1058
+ }),
1059
+ async handler(input, ctx) {
1060
+ if (input.workspaceId) {
1061
+ const provider = ctx.workspaces.for(input.workspaceId);
1062
+ const state2 = await provider.read();
1063
+ return {
1064
+ kind: "single",
1065
+ workspaceId: state2.synced.workspaceId,
1066
+ synced: state2.synced,
1067
+ local: state2.local
1068
+ };
1069
+ }
1070
+ const summaries = await ctx.workspaces.list();
1071
+ if (summaries.length > 1) {
1072
+ return {
1073
+ kind: "multiple-workspaces",
1074
+ activeWorkspaceId: ctx.workspaces.activeId(),
1075
+ workspaceCount: summaries.length,
1076
+ workspaces: summaries,
1077
+ hint: `Found ${summaries.length} workspaces. Re-call \`workspace.read\` with \`workspaceId\` set to the desired entry, or call entity-specific tools (which default to the active workspace) when scoping to one workspace is acceptable.`
1078
+ };
1079
+ }
1080
+ const state = await ctx.workspace.read();
1081
+ return {
1082
+ kind: "single",
1083
+ workspaceId: state.synced.workspaceId,
1084
+ synced: state.synced,
1085
+ local: state.local
1086
+ };
1037
1087
  }
1038
1088
  };
1039
1089
  var workspaceWriteTool = {
1040
1090
  name: "workspace.write",
1041
- description: "Bulk-replace the workspace. Pass `synced` and/or `local` to overwrite either side. Mutating tools are preferred \u2014 this is for full-doc imports/exports.",
1042
- inputSchema: import_zod4.z.object({
1043
- synced: import_zod4.z.unknown().optional(),
1044
- local: import_zod4.z.unknown().optional()
1091
+ description: "Bulk-replace the workspace. Pass `synced` and/or `local` to overwrite either side. Use `workspaceId` to target a non-active workspace (omit to write the active one). Mutating tools are preferred \u2014 this is for full-doc imports/exports.",
1092
+ inputSchema: import_zod5.z.object({
1093
+ workspaceId: import_zod5.z.string().min(1).max(256).optional().describe("Optional workspace id; omit to write the active workspace."),
1094
+ synced: import_zod5.z.unknown().optional(),
1095
+ local: import_zod5.z.unknown().optional()
1045
1096
  }),
1046
1097
  async handler(input, ctx) {
1047
- const next = await ctx.workspace.write({
1098
+ const provider = input.workspaceId ? ctx.workspaces.for(input.workspaceId) : ctx.workspace;
1099
+ const next = await provider.write({
1048
1100
  synced: input.synced,
1049
1101
  local: input.local
1050
1102
  });
@@ -1053,16 +1105,16 @@ var workspaceWriteTool = {
1053
1105
  };
1054
1106
 
1055
1107
  // src/tools/history.ts
1056
- var import_zod5 = require("zod");
1108
+ var import_zod6 = require("zod");
1057
1109
  var historyListRunsTool = {
1058
1110
  name: "history.list_runs",
1059
1111
  description: "List request-run history rows in reverse-chronological order. Filter by `requestId`, `ok` (success/failure), or `since`/`until` ISO timestamps. `limit` caps the result set; default 100.",
1060
- inputSchema: import_zod5.z.object({
1061
- requestId: import_zod5.z.string().optional(),
1062
- ok: import_zod5.z.boolean().optional(),
1063
- since: import_zod5.z.string().optional(),
1064
- until: import_zod5.z.string().optional(),
1065
- limit: import_zod5.z.number().int().positive().max(500).default(100)
1112
+ inputSchema: import_zod6.z.object({
1113
+ requestId: import_zod6.z.string().optional(),
1114
+ ok: import_zod6.z.boolean().optional(),
1115
+ since: import_zod6.z.string().optional(),
1116
+ until: import_zod6.z.string().optional(),
1117
+ limit: import_zod6.z.number().int().positive().max(500).default(100)
1066
1118
  }),
1067
1119
  async handler(input, ctx) {
1068
1120
  const state = await ctx.workspace.read();
@@ -1096,7 +1148,7 @@ var historyListRunsTool = {
1096
1148
  var historyGetRunTool = {
1097
1149
  name: "history.get_run",
1098
1150
  description: "Fetch a single history row in full (headers, body preview, assertion results).",
1099
- inputSchema: import_zod5.z.object({ id: import_zod5.z.string() }),
1151
+ inputSchema: import_zod6.z.object({ id: import_zod6.z.string() }),
1100
1152
  async handler(input, ctx) {
1101
1153
  const state = await ctx.workspace.read();
1102
1154
  const run = state.local.history.requestRuns.find((r) => r.id === input.id);
@@ -1107,7 +1159,7 @@ var historyGetRunTool = {
1107
1159
  var historyDeleteRunTool = {
1108
1160
  name: "history.delete_run",
1109
1161
  description: "Delete a single request-run row by id.",
1110
- inputSchema: import_zod5.z.object({ id: import_zod5.z.string() }),
1162
+ inputSchema: import_zod6.z.object({ id: import_zod6.z.string() }),
1111
1163
  async handler(input, ctx) {
1112
1164
  const out = await ctx.workspace.apply({ kind: "history.delete_run", runId: input.id });
1113
1165
  return { deleted: out.changedIds.length, changedIds: out.changedIds };
@@ -1116,8 +1168,8 @@ var historyDeleteRunTool = {
1116
1168
  var historyPurgeTool = {
1117
1169
  name: "history.purge_by_age",
1118
1170
  description: "Drop every request-run + plan-run older than `olderThanDays` days. Pass 0 to clear all history.",
1119
- inputSchema: import_zod5.z.object({
1120
- olderThanDays: import_zod5.z.number().nonnegative()
1171
+ inputSchema: import_zod6.z.object({
1172
+ olderThanDays: import_zod6.z.number().nonnegative()
1121
1173
  }),
1122
1174
  async handler(input, ctx) {
1123
1175
  const olderThanMs = input.olderThanDays * 24 * 60 * 60 * 1e3;
@@ -1127,15 +1179,15 @@ var historyPurgeTool = {
1127
1179
  };
1128
1180
 
1129
1181
  // src/tools/codebase.ts
1130
- var import_zod6 = require("zod");
1182
+ var import_zod7 = require("zod");
1131
1183
  var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "options", "head"];
1132
1184
  var codebaseExtractCollectionTool = {
1133
1185
  name: "codebase.extract_collection",
1134
1186
  description: "Scan source code for HTTP route definitions (Express, FastAPI, NestJS, Spring) and return candidate requests for the user to confirm before import.",
1135
- inputSchema: import_zod6.z.object({
1136
- source: import_zod6.z.string().min(1),
1187
+ inputSchema: import_zod7.z.object({
1188
+ source: import_zod7.z.string().min(1),
1137
1189
  /** Hint to limit which framework patterns to apply. Empty = try all. */
1138
- frameworks: import_zod6.z.array(import_zod6.z.enum(["express", "fastapi", "nest", "spring"])).default([])
1190
+ frameworks: import_zod7.z.array(import_zod7.z.enum(["express", "fastapi", "nest", "spring"])).default([])
1139
1191
  }),
1140
1192
  async handler(input) {
1141
1193
  const enabled = new Set(
@@ -1216,18 +1268,18 @@ var codebaseExtractCollectionTool = {
1216
1268
  };
1217
1269
 
1218
1270
  // src/tools/prompt.ts
1219
- var import_zod7 = require("zod");
1271
+ var import_zod8 = require("zod");
1220
1272
  var import_shared3 = require("@apicircle/shared");
1221
1273
  var promptCreateEnvironmentTool = {
1222
1274
  name: "prompt.create_environment",
1223
1275
  description: "Create a new environment from an LLM-shaped JSON envelope. The model produces { name, variables: [{ key, value, encrypted }] }; this tool validates and persists it.",
1224
- inputSchema: import_zod7.z.object({
1225
- name: import_zod7.z.string(),
1226
- variables: import_zod7.z.array(
1227
- import_zod7.z.object({
1228
- key: import_zod7.z.string(),
1229
- value: import_zod7.z.string(),
1230
- encrypted: import_zod7.z.boolean().default(false)
1276
+ inputSchema: import_zod8.z.object({
1277
+ name: import_zod8.z.string(),
1278
+ variables: import_zod8.z.array(
1279
+ import_zod8.z.object({
1280
+ key: import_zod8.z.string(),
1281
+ value: import_zod8.z.string(),
1282
+ encrypted: import_zod8.z.boolean().default(false)
1231
1283
  })
1232
1284
  )
1233
1285
  }),
@@ -1240,13 +1292,13 @@ var promptCreateEnvironmentTool = {
1240
1292
  var promptCreateAssertionTool = {
1241
1293
  name: "prompt.create_assertion",
1242
1294
  description: 'Add an assertion to a request from an LLM-shaped JSON envelope. Useful when the user asks "assert that the response status is 200 and body.id matches".',
1243
- inputSchema: import_zod7.z.object({
1244
- requestId: import_zod7.z.string(),
1245
- assertion: import_zod7.z.object({
1246
- kind: import_zod7.z.enum(["status", "header", "json-path", "duration"]),
1247
- op: import_zod7.z.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1248
- target: import_zod7.z.string().optional(),
1249
- expected: import_zod7.z.union([import_zod7.z.string(), import_zod7.z.number()])
1295
+ inputSchema: import_zod8.z.object({
1296
+ requestId: import_zod8.z.string(),
1297
+ assertion: import_zod8.z.object({
1298
+ kind: import_zod8.z.enum(["status", "header", "json-path", "duration"]),
1299
+ op: import_zod8.z.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1300
+ target: import_zod8.z.string().optional(),
1301
+ expected: import_zod8.z.union([import_zod8.z.string(), import_zod8.z.number()])
1250
1302
  })
1251
1303
  }),
1252
1304
  async handler(input, ctx) {
@@ -1265,10 +1317,10 @@ var promptCreateAssertionTool = {
1265
1317
  var promptCreatePlanTool = {
1266
1318
  name: "prompt.create_plan",
1267
1319
  description: "Create an execution plan from an LLM-shaped JSON envelope. The model produces { name, stepRequestIds: [...] } and the tool validates that each id exists in the workspace before persisting.",
1268
- inputSchema: import_zod7.z.object({
1269
- name: import_zod7.z.string(),
1270
- stepRequestIds: import_zod7.z.array(import_zod7.z.string()).default([]),
1271
- envPriorityOrder: import_zod7.z.array(import_zod7.z.string()).default([])
1320
+ inputSchema: import_zod8.z.object({
1321
+ name: import_zod8.z.string(),
1322
+ stepRequestIds: import_zod8.z.array(import_zod8.z.string()).default([]),
1323
+ envPriorityOrder: import_zod8.z.array(import_zod8.z.string()).default([])
1272
1324
  }),
1273
1325
  async handler(input, ctx) {
1274
1326
  const state = await ctx.workspace.read();
@@ -1297,51 +1349,51 @@ var promptCreatePlanTool = {
1297
1349
  return { ok: true, id, changedIds: out.changedIds };
1298
1350
  }
1299
1351
  };
1300
- var HTTP_METHOD2 = import_zod7.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
1301
- var HEADER_OR_QUERY = import_zod7.z.object({
1302
- key: import_zod7.z.string(),
1303
- value: import_zod7.z.string(),
1304
- enabled: import_zod7.z.boolean().default(true)
1352
+ var HTTP_METHOD2 = import_zod8.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
1353
+ var HEADER_OR_QUERY = import_zod8.z.object({
1354
+ key: import_zod8.z.string(),
1355
+ value: import_zod8.z.string(),
1356
+ enabled: import_zod8.z.boolean().default(true)
1305
1357
  });
1306
- var REQUEST_BODY = import_zod7.z.object({
1307
- type: import_zod7.z.enum(["none", "json", "text", "xml", "graphql", "urlencoded"]).default("none"),
1308
- content: import_zod7.z.string().default(""),
1309
- variables: import_zod7.z.string().optional()
1358
+ var REQUEST_BODY = import_zod8.z.object({
1359
+ type: import_zod8.z.enum(["none", "json", "text", "xml", "graphql", "urlencoded"]).default("none"),
1360
+ content: import_zod8.z.string().default(""),
1361
+ variables: import_zod8.z.string().optional()
1310
1362
  });
1311
- var PROMPT_AUTH = import_zod7.z.discriminatedUnion("type", [
1312
- import_zod7.z.object({ type: import_zod7.z.literal("none") }),
1313
- import_zod7.z.object({ type: import_zod7.z.literal("inherit") }),
1314
- import_zod7.z.object({ type: import_zod7.z.literal("bearer"), token: import_zod7.z.string().default("") }),
1315
- import_zod7.z.object({
1316
- type: import_zod7.z.literal("basic"),
1317
- username: import_zod7.z.string().default(""),
1318
- password: import_zod7.z.string().default("")
1363
+ var PROMPT_AUTH = import_zod8.z.discriminatedUnion("type", [
1364
+ import_zod8.z.object({ type: import_zod8.z.literal("none") }),
1365
+ import_zod8.z.object({ type: import_zod8.z.literal("inherit") }),
1366
+ import_zod8.z.object({ type: import_zod8.z.literal("bearer"), token: import_zod8.z.string().default("") }),
1367
+ import_zod8.z.object({
1368
+ type: import_zod8.z.literal("basic"),
1369
+ username: import_zod8.z.string().default(""),
1370
+ password: import_zod8.z.string().default("")
1319
1371
  }),
1320
- import_zod7.z.object({
1321
- type: import_zod7.z.literal("api-key"),
1322
- key: import_zod7.z.string().default(""),
1323
- value: import_zod7.z.string().default(""),
1324
- addTo: import_zod7.z.enum(["header", "query", "cookie"]).default("header")
1372
+ import_zod8.z.object({
1373
+ type: import_zod8.z.literal("api-key"),
1374
+ key: import_zod8.z.string().default(""),
1375
+ value: import_zod8.z.string().default(""),
1376
+ addTo: import_zod8.z.enum(["header", "query", "cookie"]).default("header")
1325
1377
  }),
1326
- import_zod7.z.object({
1327
- type: import_zod7.z.literal("custom-header"),
1328
- key: import_zod7.z.string().default(""),
1329
- value: import_zod7.z.string().default("")
1378
+ import_zod8.z.object({
1379
+ type: import_zod8.z.literal("custom-header"),
1380
+ key: import_zod8.z.string().default(""),
1381
+ value: import_zod8.z.string().default("")
1330
1382
  })
1331
1383
  ]);
1332
- var PROMPT_ASSERTION = import_zod7.z.object({
1333
- kind: import_zod7.z.enum(["status", "header", "json-path", "duration"]),
1334
- op: import_zod7.z.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1335
- target: import_zod7.z.string().optional(),
1336
- expected: import_zod7.z.union([import_zod7.z.string(), import_zod7.z.number()])
1384
+ var PROMPT_ASSERTION = import_zod8.z.object({
1385
+ kind: import_zod8.z.enum(["status", "header", "json-path", "duration"]),
1386
+ op: import_zod8.z.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1387
+ target: import_zod8.z.string().optional(),
1388
+ expected: import_zod8.z.union([import_zod8.z.string(), import_zod8.z.number()])
1337
1389
  });
1338
- var ENDPOINT_RESPONSE = import_zod7.z.object({
1339
- status: import_zod7.z.number().int().min(100).max(599).default(200),
1340
- jsonBody: import_zod7.z.string().default("{}"),
1341
- contentType: import_zod7.z.string().default("application/json")
1390
+ var ENDPOINT_RESPONSE = import_zod8.z.object({
1391
+ status: import_zod8.z.number().int().min(100).max(599).default(200),
1392
+ jsonBody: import_zod8.z.string().default("{}"),
1393
+ contentType: import_zod8.z.string().default("application/json")
1342
1394
  });
1343
- var VALIDATION_RULE_NL = import_zod7.z.object({
1344
- kind: import_zod7.z.enum([
1395
+ var VALIDATION_RULE_NL = import_zod8.z.object({
1396
+ kind: import_zod8.z.enum([
1345
1397
  "header-required",
1346
1398
  "header-equals",
1347
1399
  "header-matches",
@@ -1352,50 +1404,50 @@ var VALIDATION_RULE_NL = import_zod7.z.object({
1352
1404
  "body-required",
1353
1405
  "content-type-equals"
1354
1406
  ]),
1355
- target: import_zod7.z.string().default(""),
1356
- expected: import_zod7.z.string().optional(),
1357
- message: import_zod7.z.string().optional(),
1358
- enabled: import_zod7.z.boolean().default(true),
1359
- failResponse: import_zod7.z.object({
1360
- status: import_zod7.z.number().int().min(100).max(599).default(400),
1361
- jsonBody: import_zod7.z.string().default('{"error":"validation failed"}')
1407
+ target: import_zod8.z.string().default(""),
1408
+ expected: import_zod8.z.string().optional(),
1409
+ message: import_zod8.z.string().optional(),
1410
+ enabled: import_zod8.z.boolean().default(true),
1411
+ failResponse: import_zod8.z.object({
1412
+ status: import_zod8.z.number().int().min(100).max(599).default(400),
1413
+ jsonBody: import_zod8.z.string().default('{"error":"validation failed"}')
1362
1414
  }).default({})
1363
1415
  });
1364
- var CONDITION_CLAUSE_NL = import_zod7.z.object({
1365
- scope: import_zod7.z.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
1366
- target: import_zod7.z.string(),
1367
- op: import_zod7.z.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
1368
- value: import_zod7.z.string().optional()
1416
+ var CONDITION_CLAUSE_NL = import_zod8.z.object({
1417
+ scope: import_zod8.z.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
1418
+ target: import_zod8.z.string(),
1419
+ op: import_zod8.z.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
1420
+ value: import_zod8.z.string().optional()
1369
1421
  });
1370
- var RESPONSE_RULE_NL = import_zod7.z.object({
1371
- name: import_zod7.z.string(),
1372
- enabled: import_zod7.z.boolean().default(true),
1373
- when: import_zod7.z.array(CONDITION_CLAUSE_NL).default([]),
1374
- response: import_zod7.z.object({
1375
- status: import_zod7.z.number().int().min(100).max(599).default(200),
1376
- jsonBody: import_zod7.z.string().default("{}")
1422
+ var RESPONSE_RULE_NL = import_zod8.z.object({
1423
+ name: import_zod8.z.string(),
1424
+ enabled: import_zod8.z.boolean().default(true),
1425
+ when: import_zod8.z.array(CONDITION_CLAUSE_NL).default([]),
1426
+ response: import_zod8.z.object({
1427
+ status: import_zod8.z.number().int().min(100).max(599).default(200),
1428
+ jsonBody: import_zod8.z.string().default("{}")
1377
1429
  }).default({})
1378
1430
  });
1379
- var MULTIPLIER_NL = import_zod7.z.object({
1380
- name: import_zod7.z.string().optional(),
1381
- source: import_zod7.z.object({
1382
- kind: import_zod7.z.enum(["query", "pathParam", "header", "body-json-path"]),
1383
- key: import_zod7.z.string()
1431
+ var MULTIPLIER_NL = import_zod8.z.object({
1432
+ name: import_zod8.z.string().optional(),
1433
+ source: import_zod8.z.object({
1434
+ kind: import_zod8.z.enum(["query", "pathParam", "header", "body-json-path"]),
1435
+ key: import_zod8.z.string()
1384
1436
  }),
1385
- targetJsonPath: import_zod7.z.string(),
1386
- defaultCount: import_zod7.z.number().int().nonnegative().default(0),
1387
- min: import_zod7.z.number().int().nonnegative().optional(),
1388
- max: import_zod7.z.number().int().nonnegative().optional()
1437
+ targetJsonPath: import_zod8.z.string(),
1438
+ defaultCount: import_zod8.z.number().int().nonnegative().default(0),
1439
+ min: import_zod8.z.number().int().nonnegative().optional(),
1440
+ max: import_zod8.z.number().int().nonnegative().optional()
1389
1441
  });
1390
- var ENDPOINT_INPUT = import_zod7.z.object({
1442
+ var ENDPOINT_INPUT = import_zod8.z.object({
1391
1443
  method: HTTP_METHOD2,
1392
- pathPattern: import_zod7.z.string().min(1),
1393
- name: import_zod7.z.string().optional(),
1394
- description: import_zod7.z.string().optional(),
1444
+ pathPattern: import_zod8.z.string().min(1),
1445
+ name: import_zod8.z.string().optional(),
1446
+ description: import_zod8.z.string().optional(),
1395
1447
  response: ENDPOINT_RESPONSE.optional(),
1396
- validationRules: import_zod7.z.array(VALIDATION_RULE_NL).default([]),
1397
- responseRules: import_zod7.z.array(RESPONSE_RULE_NL).default([]),
1398
- multipliers: import_zod7.z.array(MULTIPLIER_NL).default([])
1448
+ validationRules: import_zod8.z.array(VALIDATION_RULE_NL).default([]),
1449
+ responseRules: import_zod8.z.array(RESPONSE_RULE_NL).default([]),
1450
+ multipliers: import_zod8.z.array(MULTIPLIER_NL).default([])
1399
1451
  });
1400
1452
  function buildRequestBody(input) {
1401
1453
  if (!input) return { type: "none", content: "" };
@@ -1488,17 +1540,17 @@ function patchEndpoint(mock, endpointId, patcher) {
1488
1540
  var promptCreateRequestTool = {
1489
1541
  name: "prompt.create_request",
1490
1542
  description: "Create a fully-shaped request from an LLM-shaped JSON envelope: method, url, headers, query params, body, auth, and inline assertions. The model produces a flat object; this tool generates the request id, normalizes auth (defaults to `inherit` so folder auth wins), and persists.",
1491
- inputSchema: import_zod7.z.object({
1492
- name: import_zod7.z.string().default("New request"),
1543
+ inputSchema: import_zod8.z.object({
1544
+ name: import_zod8.z.string().default("New request"),
1493
1545
  method: HTTP_METHOD2.default("GET"),
1494
- url: import_zod7.z.string().default(""),
1495
- folderId: import_zod7.z.string().nullable().optional(),
1496
- headers: import_zod7.z.array(HEADER_OR_QUERY).default([]),
1497
- queryParams: import_zod7.z.array(HEADER_OR_QUERY).default([]),
1498
- pathParams: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).optional(),
1546
+ url: import_zod8.z.string().default(""),
1547
+ folderId: import_zod8.z.string().nullable().optional(),
1548
+ headers: import_zod8.z.array(HEADER_OR_QUERY).default([]),
1549
+ queryParams: import_zod8.z.array(HEADER_OR_QUERY).default([]),
1550
+ pathParams: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.string()).optional(),
1499
1551
  body: REQUEST_BODY.optional(),
1500
1552
  auth: PROMPT_AUTH.optional(),
1501
- assertions: import_zod7.z.array(PROMPT_ASSERTION).default([])
1553
+ assertions: import_zod8.z.array(PROMPT_ASSERTION).default([])
1502
1554
  }),
1503
1555
  async handler(input, ctx) {
1504
1556
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1528,19 +1580,19 @@ var promptCreateRequestTool = {
1528
1580
  var promptUpdateRequestTool = {
1529
1581
  name: "prompt.update_request",
1530
1582
  description: "Patch an existing request from an LLM-shaped JSON envelope. Provided fields replace the existing values; omitted fields are left untouched. Arrays (headers, queryParams, assertions) are full replacements when supplied. Returns `{ ok: false, error }` when the id does not resolve.",
1531
- inputSchema: import_zod7.z.object({
1532
- id: import_zod7.z.string(),
1533
- patch: import_zod7.z.object({
1534
- name: import_zod7.z.string().optional(),
1583
+ inputSchema: import_zod8.z.object({
1584
+ id: import_zod8.z.string(),
1585
+ patch: import_zod8.z.object({
1586
+ name: import_zod8.z.string().optional(),
1535
1587
  method: HTTP_METHOD2.optional(),
1536
- url: import_zod7.z.string().optional(),
1537
- folderId: import_zod7.z.string().nullable().optional(),
1538
- headers: import_zod7.z.array(HEADER_OR_QUERY).optional(),
1539
- queryParams: import_zod7.z.array(HEADER_OR_QUERY).optional(),
1540
- pathParams: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).optional(),
1588
+ url: import_zod8.z.string().optional(),
1589
+ folderId: import_zod8.z.string().nullable().optional(),
1590
+ headers: import_zod8.z.array(HEADER_OR_QUERY).optional(),
1591
+ queryParams: import_zod8.z.array(HEADER_OR_QUERY).optional(),
1592
+ pathParams: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.string()).optional(),
1541
1593
  body: REQUEST_BODY.optional(),
1542
1594
  auth: PROMPT_AUTH.optional(),
1543
- assertions: import_zod7.z.array(PROMPT_ASSERTION).optional()
1595
+ assertions: import_zod8.z.array(PROMPT_ASSERTION).optional()
1544
1596
  }).strict()
1545
1597
  }),
1546
1598
  async handler(input, ctx) {
@@ -1568,17 +1620,17 @@ var promptUpdateRequestTool = {
1568
1620
  return { ok: true, changedIds: out.changedIds };
1569
1621
  }
1570
1622
  };
1571
- var FOLDER_TREE_NODE = import_zod7.z.lazy(
1572
- () => import_zod7.z.object({
1573
- name: import_zod7.z.string(),
1574
- children: import_zod7.z.array(FOLDER_TREE_NODE).optional()
1623
+ var FOLDER_TREE_NODE = import_zod8.z.lazy(
1624
+ () => import_zod8.z.object({
1625
+ name: import_zod8.z.string(),
1626
+ children: import_zod8.z.array(FOLDER_TREE_NODE).optional()
1575
1627
  })
1576
1628
  );
1577
1629
  var promptCreateFolderTreeTool = {
1578
1630
  name: "prompt.create_folder_tree",
1579
1631
  description: "Create a recursive folder hierarchy from an LLM-shaped JSON envelope. The model produces `{ parentId?, tree: { name, children?: [...] } }` and this tool walks the tree, generating ids and persisting one folder per node. Returns the list of created ids in pre-order.",
1580
- inputSchema: import_zod7.z.object({
1581
- parentId: import_zod7.z.string().nullable().optional(),
1632
+ inputSchema: import_zod8.z.object({
1633
+ parentId: import_zod8.z.string().nullable().optional(),
1582
1634
  tree: FOLDER_TREE_NODE
1583
1635
  }),
1584
1636
  async handler(input, ctx) {
@@ -1604,9 +1656,9 @@ var promptCreateFolderTreeTool = {
1604
1656
  var promptAddPlanStepsTool = {
1605
1657
  name: "prompt.add_plan_steps",
1606
1658
  description: "Append one or more steps to an existing execution plan from an LLM-shaped JSON envelope. The model produces `{ planId, requestIds: [...] }`; each id is validated against the workspace before any step is appended. Order in the input list is preserved.",
1607
- inputSchema: import_zod7.z.object({
1608
- planId: import_zod7.z.string(),
1609
- requestIds: import_zod7.z.array(import_zod7.z.string()).min(1)
1659
+ inputSchema: import_zod8.z.object({
1660
+ planId: import_zod8.z.string(),
1661
+ requestIds: import_zod8.z.array(import_zod8.z.string()).min(1)
1610
1662
  }),
1611
1663
  async handler(input, ctx) {
1612
1664
  const state = await ctx.workspace.read();
@@ -1636,9 +1688,9 @@ var promptAddPlanStepsTool = {
1636
1688
  var promptSetPlanVariablesTool = {
1637
1689
  name: "prompt.set_plan_variables",
1638
1690
  description: "Replace the plan-scoped variables on an execution plan from an LLM-shaped JSON envelope. The model produces `{ planId, variables: [{ key, value }] }`. Empty array clears all plan variables.",
1639
- inputSchema: import_zod7.z.object({
1640
- planId: import_zod7.z.string(),
1641
- variables: import_zod7.z.array(import_zod7.z.object({ key: import_zod7.z.string(), value: import_zod7.z.string() }))
1691
+ inputSchema: import_zod8.z.object({
1692
+ planId: import_zod8.z.string(),
1693
+ variables: import_zod8.z.array(import_zod8.z.object({ key: import_zod8.z.string(), value: import_zod8.z.string() }))
1642
1694
  }),
1643
1695
  async handler(input, ctx) {
1644
1696
  const state = await ctx.workspace.read();
@@ -1654,10 +1706,10 @@ var promptSetPlanVariablesTool = {
1654
1706
  var promptCreateMockServerTool = {
1655
1707
  name: "prompt.create_mock_server",
1656
1708
  description: "Create a manual-mode mock server with optional inline endpoints from an LLM-shaped JSON envelope. The model produces `{ name, defaultPort?, endpoints: [{ method, pathPattern, name?, response?, validationRules?, responseRules?, multipliers? }] }`; this tool generates ids for the server and every endpoint / rule, then persists in one shot.",
1657
- inputSchema: import_zod7.z.object({
1658
- name: import_zod7.z.string().min(1),
1659
- defaultPort: import_zod7.z.number().int().positive().nullable().optional(),
1660
- endpoints: import_zod7.z.array(ENDPOINT_INPUT).default([])
1709
+ inputSchema: import_zod8.z.object({
1710
+ name: import_zod8.z.string().min(1),
1711
+ defaultPort: import_zod8.z.number().int().positive().nullable().optional(),
1712
+ endpoints: import_zod8.z.array(ENDPOINT_INPUT).default([])
1661
1713
  }),
1662
1714
  async handler(input, ctx) {
1663
1715
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1684,16 +1736,16 @@ var promptCreateMockServerTool = {
1684
1736
  var promptAddMockEndpointTool = {
1685
1737
  name: "prompt.add_mock_endpoint",
1686
1738
  description: "Append a new endpoint (with optional inline validation rules, response rules, and multipliers) to an existing mock server from an LLM-shaped JSON envelope. All ids are auto-generated; the existing endpoints stay in place.",
1687
- inputSchema: import_zod7.z.object({
1688
- mockId: import_zod7.z.string(),
1739
+ inputSchema: import_zod8.z.object({
1740
+ mockId: import_zod8.z.string(),
1689
1741
  method: HTTP_METHOD2,
1690
- pathPattern: import_zod7.z.string().min(1),
1691
- name: import_zod7.z.string().optional(),
1692
- description: import_zod7.z.string().optional(),
1742
+ pathPattern: import_zod8.z.string().min(1),
1743
+ name: import_zod8.z.string().optional(),
1744
+ description: import_zod8.z.string().optional(),
1693
1745
  response: ENDPOINT_RESPONSE.optional(),
1694
- validationRules: import_zod7.z.array(VALIDATION_RULE_NL).default([]),
1695
- responseRules: import_zod7.z.array(RESPONSE_RULE_NL).default([]),
1696
- multipliers: import_zod7.z.array(MULTIPLIER_NL).default([])
1746
+ validationRules: import_zod8.z.array(VALIDATION_RULE_NL).default([]),
1747
+ responseRules: import_zod8.z.array(RESPONSE_RULE_NL).default([]),
1748
+ multipliers: import_zod8.z.array(MULTIPLIER_NL).default([])
1697
1749
  }),
1698
1750
  async handler(input, ctx) {
1699
1751
  const state = await ctx.workspace.read();
@@ -1715,10 +1767,10 @@ var promptAddMockEndpointTool = {
1715
1767
  var promptSetEndpointValidationRulesTool = {
1716
1768
  name: "prompt.set_endpoint_validation_rules",
1717
1769
  description: "Replace an endpoint's validation rules with an LLM-shaped list. Every rule gets a fresh id; the existing rules are dropped. Empty array clears all validation rules.",
1718
- inputSchema: import_zod7.z.object({
1719
- mockId: import_zod7.z.string(),
1720
- endpointId: import_zod7.z.string(),
1721
- rules: import_zod7.z.array(VALIDATION_RULE_NL)
1770
+ inputSchema: import_zod8.z.object({
1771
+ mockId: import_zod8.z.string(),
1772
+ endpointId: import_zod8.z.string(),
1773
+ rules: import_zod8.z.array(VALIDATION_RULE_NL)
1722
1774
  }),
1723
1775
  async handler(input, ctx) {
1724
1776
  const state = await ctx.workspace.read();
@@ -1749,10 +1801,10 @@ var promptSetEndpointValidationRulesTool = {
1749
1801
  var promptSetEndpointResponseRulesTool = {
1750
1802
  name: "prompt.set_endpoint_response_rules",
1751
1803
  description: "Replace an endpoint's conditional response rules with an LLM-shaped list. Rules fire in order, first match wins. Every rule + clause gets a fresh id. Empty array falls back to defaultResponse.",
1752
- inputSchema: import_zod7.z.object({
1753
- mockId: import_zod7.z.string(),
1754
- endpointId: import_zod7.z.string(),
1755
- rules: import_zod7.z.array(RESPONSE_RULE_NL)
1804
+ inputSchema: import_zod8.z.object({
1805
+ mockId: import_zod8.z.string(),
1806
+ endpointId: import_zod8.z.string(),
1807
+ rules: import_zod8.z.array(RESPONSE_RULE_NL)
1756
1808
  }),
1757
1809
  async handler(input, ctx) {
1758
1810
  const state = await ctx.workspace.read();
@@ -1787,10 +1839,10 @@ var promptSetEndpointResponseRulesTool = {
1787
1839
  var promptSetEndpointMultipliersTool = {
1788
1840
  name: "prompt.set_endpoint_multipliers",
1789
1841
  description: "Replace the response multipliers on an endpoint's defaultResponse with an LLM-shaped list. Multipliers expand an array at `targetJsonPath` to a count derived from a request value. Every multiplier gets a fresh id. Empty array clears all multipliers.",
1790
- inputSchema: import_zod7.z.object({
1791
- mockId: import_zod7.z.string(),
1792
- endpointId: import_zod7.z.string(),
1793
- multipliers: import_zod7.z.array(MULTIPLIER_NL)
1842
+ inputSchema: import_zod8.z.object({
1843
+ mockId: import_zod8.z.string(),
1844
+ endpointId: import_zod8.z.string(),
1845
+ multipliers: import_zod8.z.array(MULTIPLIER_NL)
1794
1846
  }),
1795
1847
  async handler(input, ctx) {
1796
1848
  const state = await ctx.workspace.read();
@@ -1819,7 +1871,7 @@ var promptSetEndpointMultipliersTool = {
1819
1871
  };
1820
1872
 
1821
1873
  // src/tools/mocks.ts
1822
- var import_zod8 = require("zod");
1874
+ var import_zod9 = require("zod");
1823
1875
  var import_shared4 = require("@apicircle/shared");
1824
1876
  var import_mock_server_core2 = require("@apicircle/mock-server-core");
1825
1877
  async function ingestSource(source, name) {
@@ -1842,10 +1894,10 @@ async function ingestSource(source, name) {
1842
1894
  var mockCreateFromOpenApiTool = {
1843
1895
  name: "mock.create_from_openapi",
1844
1896
  description: "Create a mock server from an OpenAPI / Swagger spec (YAML or JSON).",
1845
- inputSchema: import_zod8.z.object({
1846
- name: import_zod8.z.string(),
1847
- spec: import_zod8.z.string().min(1),
1848
- format: import_zod8.z.enum(["json", "yaml"]).default("json")
1897
+ inputSchema: import_zod9.z.object({
1898
+ name: import_zod9.z.string(),
1899
+ spec: import_zod9.z.string().min(1),
1900
+ format: import_zod9.z.enum(["json", "yaml"]).default("json")
1849
1901
  }),
1850
1902
  async handler(input, ctx) {
1851
1903
  const { mock, warnings } = await ingestSource(
@@ -1864,7 +1916,7 @@ var mockCreateFromOpenApiTool = {
1864
1916
  var mockCreateFromPostmanTool = {
1865
1917
  name: "mock.create_from_postman",
1866
1918
  description: "Create a mock server from a Postman v2/v2.1 collection.",
1867
- inputSchema: import_zod8.z.object({ name: import_zod8.z.string(), collection: import_zod8.z.string().min(1) }),
1919
+ inputSchema: import_zod9.z.object({ name: import_zod9.z.string(), collection: import_zod9.z.string().min(1) }),
1868
1920
  async handler(input, ctx) {
1869
1921
  const { mock, warnings } = await ingestSource(
1870
1922
  { kind: "postman", collection: input.collection },
@@ -1882,7 +1934,7 @@ var mockCreateFromPostmanTool = {
1882
1934
  var mockCreateFromInsomniaTool = {
1883
1935
  name: "mock.create_from_insomnia",
1884
1936
  description: "Create a mock server from an Insomnia v4 export.",
1885
- inputSchema: import_zod8.z.object({ name: import_zod8.z.string(), export: import_zod8.z.string().min(1) }),
1937
+ inputSchema: import_zod9.z.object({ name: import_zod9.z.string(), export: import_zod9.z.string().min(1) }),
1886
1938
  async handler(input, ctx) {
1887
1939
  const { mock, warnings } = await ingestSource(
1888
1940
  { kind: "insomnia", export: input.export },
@@ -1900,7 +1952,7 @@ var mockCreateFromInsomniaTool = {
1900
1952
  var mockImportPostmanMockCollectionTool = {
1901
1953
  name: "mock.import_postman_mock_collection",
1902
1954
  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.",
1903
- inputSchema: import_zod8.z.object({ name: import_zod8.z.string(), collection: import_zod8.z.string().min(1) }),
1955
+ inputSchema: import_zod9.z.object({ name: import_zod9.z.string(), collection: import_zod9.z.string().min(1) }),
1904
1956
  async handler(input, ctx) {
1905
1957
  const { mock, warnings } = await ingestSource(
1906
1958
  { kind: "postman", collection: input.collection },
@@ -1918,7 +1970,7 @@ var mockImportPostmanMockCollectionTool = {
1918
1970
  var mockListTool = {
1919
1971
  name: "mock.list",
1920
1972
  description: "List all mock servers in the workspace plus their runtime status (running / stopped, port).",
1921
- inputSchema: import_zod8.z.object({}),
1973
+ inputSchema: import_zod9.z.object({}),
1922
1974
  async handler(_input, ctx) {
1923
1975
  const state = await ctx.workspace.read();
1924
1976
  const running = await ctx.mock.list();
@@ -1940,9 +1992,9 @@ var mockListTool = {
1940
1992
  var mockStartTool = {
1941
1993
  name: "mock.start",
1942
1994
  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.",
1943
- inputSchema: import_zod8.z.object({
1944
- id: import_zod8.z.string(),
1945
- port: import_zod8.z.number().int().positive().optional()
1995
+ inputSchema: import_zod9.z.object({
1996
+ id: import_zod9.z.string(),
1997
+ port: import_zod9.z.number().int().positive().optional()
1946
1998
  }),
1947
1999
  async handler(input, ctx) {
1948
2000
  const state = await ctx.workspace.read();
@@ -1959,7 +2011,7 @@ var mockStartTool = {
1959
2011
  var mockStopTool = {
1960
2012
  name: "mock.stop",
1961
2013
  description: "Stop a running mock server by id (no-op if not running).",
1962
- inputSchema: import_zod8.z.object({ id: import_zod8.z.string() }),
2014
+ inputSchema: import_zod9.z.object({ id: import_zod9.z.string() }),
1963
2015
  async handler(input, ctx) {
1964
2016
  try {
1965
2017
  await ctx.mock.stop(input.id);
@@ -1972,7 +2024,7 @@ var mockStopTool = {
1972
2024
  var mockDeleteTool = {
1973
2025
  name: "mock.delete",
1974
2026
  description: "Delete a mock server definition. Stops it first if it's running.",
1975
- inputSchema: import_zod8.z.object({ id: import_zod8.z.string() }),
2027
+ inputSchema: import_zod9.z.object({ id: import_zod9.z.string() }),
1976
2028
  async handler(input, ctx) {
1977
2029
  try {
1978
2030
  await ctx.mock.stop(input.id);
@@ -1982,13 +2034,13 @@ var mockDeleteTool = {
1982
2034
  return { ok: true, changedIds: out.changedIds };
1983
2035
  }
1984
2036
  };
1985
- var HTTP_METHOD3 = import_zod8.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2037
+ var HTTP_METHOD3 = import_zod9.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
1986
2038
  var mockCreateManualTool = {
1987
2039
  name: "mock.create_manual",
1988
2040
  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.",
1989
- inputSchema: import_zod8.z.object({
1990
- name: import_zod8.z.string().min(1),
1991
- defaultPort: import_zod8.z.number().int().positive().nullable().optional()
2041
+ inputSchema: import_zod9.z.object({
2042
+ name: import_zod9.z.string().min(1),
2043
+ defaultPort: import_zod9.z.number().int().positive().nullable().optional()
1992
2044
  }),
1993
2045
  async handler(input, ctx) {
1994
2046
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -2011,7 +2063,7 @@ var mockCreateManualTool = {
2011
2063
  var mockListEndpointsTool = {
2012
2064
  name: "mock.list_endpoints",
2013
2065
  description: "List endpoints for a mock server (id, method, path, name).",
2014
- inputSchema: import_zod8.z.object({ mockId: import_zod8.z.string() }),
2066
+ inputSchema: import_zod9.z.object({ mockId: import_zod9.z.string() }),
2015
2067
  async handler(input, ctx) {
2016
2068
  const state = await ctx.workspace.read();
2017
2069
  const mock = state.synced.mockServers[input.mockId];
@@ -2030,10 +2082,10 @@ var mockListEndpointsTool = {
2030
2082
  };
2031
2083
  }
2032
2084
  };
2033
- var ENDPOINT_RESPONSE2 = import_zod8.z.object({
2034
- status: import_zod8.z.number().int().min(100).max(599).default(200),
2035
- jsonBody: import_zod8.z.string().default("{}"),
2036
- contentType: import_zod8.z.string().default("application/json")
2085
+ var ENDPOINT_RESPONSE2 = import_zod9.z.object({
2086
+ status: import_zod9.z.number().int().min(100).max(599).default(200),
2087
+ jsonBody: import_zod9.z.string().default("{}"),
2088
+ contentType: import_zod9.z.string().default("application/json")
2037
2089
  });
2038
2090
  function buildDefaultEndpoint(args) {
2039
2091
  const response = args.response ?? {
@@ -2062,12 +2114,12 @@ function buildDefaultEndpoint(args) {
2062
2114
  var mockAddEndpointTool = {
2063
2115
  name: "mock.add_endpoint",
2064
2116
  description: "Append a new endpoint to a mock server. Defaults to a 200 JSON response of `{}`. Returns the new endpoint id.",
2065
- inputSchema: import_zod8.z.object({
2066
- mockId: import_zod8.z.string(),
2117
+ inputSchema: import_zod9.z.object({
2118
+ mockId: import_zod9.z.string(),
2067
2119
  method: HTTP_METHOD3,
2068
- pathPattern: import_zod8.z.string().min(1),
2069
- name: import_zod8.z.string().optional(),
2070
- description: import_zod8.z.string().optional(),
2120
+ pathPattern: import_zod9.z.string().min(1),
2121
+ name: import_zod9.z.string().optional(),
2122
+ description: import_zod9.z.string().optional(),
2071
2123
  response: ENDPOINT_RESPONSE2.optional()
2072
2124
  }),
2073
2125
  async handler(input, ctx) {
@@ -2090,13 +2142,13 @@ var mockAddEndpointTool = {
2090
2142
  var mockUpdateEndpointTool = {
2091
2143
  name: "mock.update_endpoint",
2092
2144
  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.",
2093
- inputSchema: import_zod8.z.object({
2094
- mockId: import_zod8.z.string(),
2095
- endpointId: import_zod8.z.string(),
2145
+ inputSchema: import_zod9.z.object({
2146
+ mockId: import_zod9.z.string(),
2147
+ endpointId: import_zod9.z.string(),
2096
2148
  method: HTTP_METHOD3.optional(),
2097
- pathPattern: import_zod8.z.string().optional(),
2098
- name: import_zod8.z.string().optional(),
2099
- description: import_zod8.z.string().optional(),
2149
+ pathPattern: import_zod9.z.string().optional(),
2150
+ name: import_zod9.z.string().optional(),
2151
+ description: import_zod9.z.string().optional(),
2100
2152
  response: ENDPOINT_RESPONSE2.partial().optional()
2101
2153
  }),
2102
2154
  async handler(input, ctx) {
@@ -2137,7 +2189,7 @@ var mockUpdateEndpointTool = {
2137
2189
  var mockDeleteEndpointTool = {
2138
2190
  name: "mock.delete_endpoint",
2139
2191
  description: "Remove an endpoint from a mock server.",
2140
- inputSchema: import_zod8.z.object({ mockId: import_zod8.z.string(), endpointId: import_zod8.z.string() }),
2192
+ inputSchema: import_zod9.z.object({ mockId: import_zod9.z.string(), endpointId: import_zod9.z.string() }),
2141
2193
  async handler(input, ctx) {
2142
2194
  const state = await ctx.workspace.read();
2143
2195
  const mock = state.synced.mockServers[input.mockId];
@@ -2157,9 +2209,9 @@ var mockDeleteEndpointTool = {
2157
2209
  return { ok: true, changedIds: out.changedIds };
2158
2210
  }
2159
2211
  };
2160
- var VALIDATION_RULE = import_zod8.z.object({
2161
- id: import_zod8.z.string().optional(),
2162
- kind: import_zod8.z.enum([
2212
+ var VALIDATION_RULE = import_zod9.z.object({
2213
+ id: import_zod9.z.string().optional(),
2214
+ kind: import_zod9.z.enum([
2163
2215
  "header-required",
2164
2216
  "header-equals",
2165
2217
  "header-matches",
@@ -2170,43 +2222,43 @@ var VALIDATION_RULE = import_zod8.z.object({
2170
2222
  "body-required",
2171
2223
  "content-type-equals"
2172
2224
  ]),
2173
- target: import_zod8.z.string().default(""),
2174
- expected: import_zod8.z.string().optional(),
2175
- message: import_zod8.z.string().optional(),
2176
- enabled: import_zod8.z.boolean().default(true),
2177
- failResponse: import_zod8.z.object({
2178
- status: import_zod8.z.number().int().min(100).max(599).default(400),
2179
- jsonBody: import_zod8.z.string().default('{"error":"validation failed"}')
2225
+ target: import_zod9.z.string().default(""),
2226
+ expected: import_zod9.z.string().optional(),
2227
+ message: import_zod9.z.string().optional(),
2228
+ enabled: import_zod9.z.boolean().default(true),
2229
+ failResponse: import_zod9.z.object({
2230
+ status: import_zod9.z.number().int().min(100).max(599).default(400),
2231
+ jsonBody: import_zod9.z.string().default('{"error":"validation failed"}')
2180
2232
  }).default({})
2181
2233
  });
2182
- var CONDITION_CLAUSE = import_zod8.z.object({
2183
- id: import_zod8.z.string().optional(),
2184
- scope: import_zod8.z.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
2185
- target: import_zod8.z.string(),
2186
- op: import_zod8.z.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
2187
- value: import_zod8.z.string().optional()
2234
+ var CONDITION_CLAUSE = import_zod9.z.object({
2235
+ id: import_zod9.z.string().optional(),
2236
+ scope: import_zod9.z.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
2237
+ target: import_zod9.z.string(),
2238
+ op: import_zod9.z.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
2239
+ value: import_zod9.z.string().optional()
2188
2240
  });
2189
- var RESPONSE_RULE = import_zod8.z.object({
2190
- id: import_zod8.z.string().optional(),
2191
- name: import_zod8.z.string(),
2192
- enabled: import_zod8.z.boolean().default(true),
2193
- when: import_zod8.z.array(CONDITION_CLAUSE).default([]),
2194
- response: import_zod8.z.object({
2195
- status: import_zod8.z.number().int().min(100).max(599).default(200),
2196
- jsonBody: import_zod8.z.string().default("{}")
2241
+ var RESPONSE_RULE = import_zod9.z.object({
2242
+ id: import_zod9.z.string().optional(),
2243
+ name: import_zod9.z.string(),
2244
+ enabled: import_zod9.z.boolean().default(true),
2245
+ when: import_zod9.z.array(CONDITION_CLAUSE).default([]),
2246
+ response: import_zod9.z.object({
2247
+ status: import_zod9.z.number().int().min(100).max(599).default(200),
2248
+ jsonBody: import_zod9.z.string().default("{}")
2197
2249
  }).default({})
2198
2250
  });
2199
- var MULTIPLIER = import_zod8.z.object({
2200
- id: import_zod8.z.string().optional(),
2201
- name: import_zod8.z.string().optional(),
2202
- source: import_zod8.z.object({
2203
- kind: import_zod8.z.enum(["query", "pathParam", "header", "body-json-path"]),
2204
- key: import_zod8.z.string()
2251
+ var MULTIPLIER = import_zod9.z.object({
2252
+ id: import_zod9.z.string().optional(),
2253
+ name: import_zod9.z.string().optional(),
2254
+ source: import_zod9.z.object({
2255
+ kind: import_zod9.z.enum(["query", "pathParam", "header", "body-json-path"]),
2256
+ key: import_zod9.z.string()
2205
2257
  }),
2206
- targetJsonPath: import_zod8.z.string(),
2207
- defaultCount: import_zod8.z.number().int().nonnegative().default(0),
2208
- min: import_zod8.z.number().int().nonnegative().optional(),
2209
- max: import_zod8.z.number().int().nonnegative().optional()
2258
+ targetJsonPath: import_zod9.z.string(),
2259
+ defaultCount: import_zod9.z.number().int().nonnegative().default(0),
2260
+ min: import_zod9.z.number().int().nonnegative().optional(),
2261
+ max: import_zod9.z.number().int().nonnegative().optional()
2210
2262
  });
2211
2263
  function defaultJsonResponseConfig(args) {
2212
2264
  return {
@@ -2231,10 +2283,10 @@ function patchEndpoint2(mock, endpointId, patcher) {
2231
2283
  var mockSetValidationRulesTool = {
2232
2284
  name: "mock.set_validation_rules",
2233
2285
  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.",
2234
- inputSchema: import_zod8.z.object({
2235
- mockId: import_zod8.z.string(),
2236
- endpointId: import_zod8.z.string(),
2237
- rules: import_zod8.z.array(VALIDATION_RULE)
2286
+ inputSchema: import_zod9.z.object({
2287
+ mockId: import_zod9.z.string(),
2288
+ endpointId: import_zod9.z.string(),
2289
+ rules: import_zod9.z.array(VALIDATION_RULE)
2238
2290
  }),
2239
2291
  async handler(input, ctx) {
2240
2292
  const state = await ctx.workspace.read();
@@ -2261,10 +2313,10 @@ var mockSetValidationRulesTool = {
2261
2313
  var mockSetResponseRulesTool = {
2262
2314
  name: "mock.set_response_rules",
2263
2315
  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.",
2264
- inputSchema: import_zod8.z.object({
2265
- mockId: import_zod8.z.string(),
2266
- endpointId: import_zod8.z.string(),
2267
- rules: import_zod8.z.array(RESPONSE_RULE)
2316
+ inputSchema: import_zod9.z.object({
2317
+ mockId: import_zod9.z.string(),
2318
+ endpointId: import_zod9.z.string(),
2319
+ rules: import_zod9.z.array(RESPONSE_RULE)
2268
2320
  }),
2269
2321
  async handler(input, ctx) {
2270
2322
  const state = await ctx.workspace.read();
@@ -2295,10 +2347,10 @@ var mockSetResponseRulesTool = {
2295
2347
  var mockSetMultipliersTool = {
2296
2348
  name: "mock.set_multipliers",
2297
2349
  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.",
2298
- inputSchema: import_zod8.z.object({
2299
- mockId: import_zod8.z.string(),
2300
- endpointId: import_zod8.z.string(),
2301
- multipliers: import_zod8.z.array(MULTIPLIER)
2350
+ inputSchema: import_zod9.z.object({
2351
+ mockId: import_zod9.z.string(),
2352
+ endpointId: import_zod9.z.string(),
2353
+ multipliers: import_zod9.z.array(MULTIPLIER)
2302
2354
  }),
2303
2355
  async handler(input, ctx) {
2304
2356
  const state = await ctx.workspace.read();
@@ -2334,6 +2386,7 @@ var TOOL_REGISTRY = [
2334
2386
  importInsomniaTool,
2335
2387
  importHarTool,
2336
2388
  generateCodeTool,
2389
+ workspaceListTool,
2337
2390
  workspaceReadTool,
2338
2391
  workspaceWriteTool,
2339
2392
  requestCreateTool,
@@ -2401,6 +2454,63 @@ var TOOL_REGISTRY = [
2401
2454
  mockImportPostmanMockCollectionTool
2402
2455
  ];
2403
2456
 
2457
+ // src/providers/Workspaces.ts
2458
+ var SingleWorkspaceAdapter = class {
2459
+ constructor(provider, workspaceId, displayName = "Workspace") {
2460
+ this.provider = provider;
2461
+ this.workspaceId = workspaceId;
2462
+ this.displayName = displayName;
2463
+ }
2464
+ provider;
2465
+ workspaceId;
2466
+ displayName;
2467
+ async list() {
2468
+ const state = await this.provider.read();
2469
+ const id = state.synced.workspaceId;
2470
+ this.workspaceId = id;
2471
+ return [
2472
+ {
2473
+ id,
2474
+ name: this.displayName,
2475
+ isActive: true,
2476
+ createdAt: state.synced.meta.createdAt,
2477
+ lastOpenedAt: state.synced.meta.updatedAt,
2478
+ counts: {
2479
+ requests: Object.keys(state.synced.collections.requests).length,
2480
+ folders: Object.keys(state.synced.collections.folders).length,
2481
+ environments: Object.keys(state.synced.environments.items).length,
2482
+ mockServers: Object.keys(state.synced.mockServers ?? {}).length,
2483
+ plans: Object.keys(state.synced.executionPlans ?? {}).length
2484
+ }
2485
+ }
2486
+ ];
2487
+ }
2488
+ for(workspaceId) {
2489
+ if (this.workspaceId && workspaceId !== this.workspaceId) {
2490
+ throw new WorkspaceNotFoundError(workspaceId);
2491
+ }
2492
+ return this.provider;
2493
+ }
2494
+ activeId() {
2495
+ return this.workspaceId;
2496
+ }
2497
+ setActive(workspaceId) {
2498
+ if (this.workspaceId && workspaceId !== this.workspaceId) {
2499
+ throw new WorkspaceNotFoundError(workspaceId);
2500
+ }
2501
+ return Promise.resolve();
2502
+ }
2503
+ };
2504
+ var WorkspaceNotFoundError = class extends Error {
2505
+ code = "workspace-not-found";
2506
+ workspaceId;
2507
+ constructor(workspaceId) {
2508
+ super(`No workspace with id "${workspaceId}" is available on this server.`);
2509
+ this.name = "WorkspaceNotFoundError";
2510
+ this.workspaceId = workspaceId;
2511
+ }
2512
+ };
2513
+
2404
2514
  // src/providers/InMemoryWorkspaceProvider.ts
2405
2515
  var import_core2 = require("@apicircle/core");
2406
2516
 
@@ -2440,6 +2550,109 @@ var FileBackedWorkspaceProvider = class {
2440
2550
  }
2441
2551
  };
2442
2552
 
2553
+ // src/providers/MultiWorkspaceProvider.ts
2554
+ var import_registry = require("@apicircle/core/workspace/registry");
2555
+ var MultiWorkspaceProvider = class {
2556
+ constructor(registryRoot) {
2557
+ this.registryRoot = registryRoot;
2558
+ }
2559
+ registryRoot;
2560
+ active = null;
2561
+ activeWorkspaceId = null;
2562
+ /**
2563
+ * Hydrate the active provider from disk. Must be called once before the
2564
+ * MCP host boots so `ctx.workspace.read()` doesn't race the first
2565
+ * registry-load. Returns the registry the boot can log.
2566
+ */
2567
+ async init() {
2568
+ const registry = await (0, import_registry.loadRegistry)(this.registryRoot) ?? {
2569
+ schemaVersion: 1,
2570
+ activeWorkspaceId: null,
2571
+ workspaces: []
2572
+ };
2573
+ if (registry.activeWorkspaceId) {
2574
+ this.activeWorkspaceId = registry.activeWorkspaceId;
2575
+ this.active = new FileBackedWorkspaceProvider(
2576
+ (0, import_registry.workspaceDirFor)(this.registryRoot, registry.activeWorkspaceId)
2577
+ );
2578
+ }
2579
+ return registry;
2580
+ }
2581
+ /** The provider tool handlers see as `ctx.workspace`. */
2582
+ activeProvider() {
2583
+ if (!this.active) {
2584
+ throw new Error(
2585
+ "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
2586
+ );
2587
+ }
2588
+ return this.active;
2589
+ }
2590
+ // ─── Workspaces interface ──────────────────────────────────────────────────
2591
+ async list() {
2592
+ const registry = await (0, import_registry.loadRegistry)(this.registryRoot) ?? {
2593
+ schemaVersion: 1,
2594
+ activeWorkspaceId: null,
2595
+ workspaces: []
2596
+ };
2597
+ const out = [];
2598
+ for (const entry of registry.workspaces) {
2599
+ let counts = null;
2600
+ try {
2601
+ const state = await (0, import_registry.loadWorkspaceById)(this.registryRoot, entry.id);
2602
+ if (state) {
2603
+ counts = {
2604
+ requests: Object.keys(state.synced.collections.requests).length,
2605
+ folders: Object.keys(state.synced.collections.folders).length,
2606
+ environments: Object.keys(state.synced.environments.items).length,
2607
+ mockServers: Object.keys(state.synced.mockServers ?? {}).length,
2608
+ plans: Object.keys(state.synced.executionPlans ?? {}).length
2609
+ };
2610
+ }
2611
+ } catch {
2612
+ counts = null;
2613
+ }
2614
+ out.push({
2615
+ id: entry.id,
2616
+ name: entry.name,
2617
+ isActive: entry.id === registry.activeWorkspaceId,
2618
+ createdAt: entry.createdAt,
2619
+ lastOpenedAt: entry.lastOpenedAt,
2620
+ counts
2621
+ });
2622
+ }
2623
+ return out;
2624
+ }
2625
+ for(workspaceId) {
2626
+ return new FileBackedWorkspaceProvider((0, import_registry.workspaceDirFor)(this.registryRoot, workspaceId));
2627
+ }
2628
+ activeId() {
2629
+ return this.activeWorkspaceId;
2630
+ }
2631
+ async setActive(workspaceId) {
2632
+ const registry = await (0, import_registry.loadRegistry)(this.registryRoot);
2633
+ if (!registry || !registry.workspaces.some((w) => w.id === workspaceId)) {
2634
+ throw new WorkspaceNotFoundError(workspaceId);
2635
+ }
2636
+ const next = await (0, import_registry.setActiveWorkspace)(this.registryRoot, workspaceId);
2637
+ void next;
2638
+ this.activeWorkspaceId = workspaceId;
2639
+ this.active = new FileBackedWorkspaceProvider((0, import_registry.workspaceDirFor)(this.registryRoot, workspaceId));
2640
+ }
2641
+ /**
2642
+ * Idempotent registry write — used by tests / tools that need to
2643
+ * persist registry updates that didn't go through `setActive`.
2644
+ */
2645
+ async writeRegistry(registry) {
2646
+ await (0, import_registry.saveRegistry)(this.registryRoot, registry);
2647
+ this.activeWorkspaceId = registry.activeWorkspaceId;
2648
+ if (registry.activeWorkspaceId) {
2649
+ this.active = new FileBackedWorkspaceProvider(
2650
+ (0, import_registry.workspaceDirFor)(this.registryRoot, registry.activeWorkspaceId)
2651
+ );
2652
+ }
2653
+ }
2654
+ };
2655
+
2443
2656
  // src/providers/InProcessMockController.ts
2444
2657
  var import_mock_server_core3 = require("@apicircle/mock-server-core");
2445
2658
  var InProcessMockController = class {
@@ -2480,10 +2693,19 @@ var InProcessMockController = class {
2480
2693
 
2481
2694
  // src/index.ts
2482
2695
  function createMcpServer(options) {
2696
+ const workspaces = options.workspaces ?? new SingleWorkspaceAdapter(
2697
+ options.workspace,
2698
+ null
2699
+ /* discovered on first list() */
2700
+ );
2483
2701
  return new McpHost({
2484
2702
  serverInfo: options.serverInfo,
2485
2703
  tools: options.tools ?? TOOL_REGISTRY,
2486
- context: { workspace: options.workspace, mock: options.mock }
2704
+ context: {
2705
+ workspace: options.workspace,
2706
+ workspaces,
2707
+ mock: options.mock
2708
+ }
2487
2709
  });
2488
2710
  }
2489
2711
 
@@ -2497,11 +2719,43 @@ function getWorkspaceDir() {
2497
2719
  if (env) return path.resolve(env);
2498
2720
  return path.resolve(process.cwd());
2499
2721
  }
2722
+ async function fileExists(p) {
2723
+ try {
2724
+ await import_node_fs.promises.access(p);
2725
+ return true;
2726
+ } catch {
2727
+ return false;
2728
+ }
2729
+ }
2500
2730
  async function main() {
2501
2731
  const dir = getWorkspaceDir();
2502
- const workspace = new FileBackedWorkspaceProvider(dir);
2732
+ const registryPath = path.join(dir, "registry.json");
2733
+ const isRegistryRoot = await fileExists(registryPath);
2503
2734
  const mock = new InProcessMockController();
2504
- const host = createMcpServer({ workspace, mock });
2735
+ if (isRegistryRoot) {
2736
+ const workspaces2 = new MultiWorkspaceProvider(dir);
2737
+ const registry = await workspaces2.init();
2738
+ if (!registry.activeWorkspaceId) {
2739
+ process.stderr.write(
2740
+ `apicircle-mcp: registry at ${dir} has no active workspace. Open the desktop app once, or run \`apicircle workspaces create <name>\`.
2741
+ `
2742
+ );
2743
+ process.exit(1);
2744
+ }
2745
+ const workspace2 = workspaces2.activeProvider();
2746
+ const host2 = createMcpServer({ workspace: workspace2, workspaces: workspaces2, mock });
2747
+ process.stderr.write(
2748
+ `apicircle-mcp: multi-workspace mode \xB7 ${registry.workspaces.length} workspace(s) \xB7 active=${registry.activeWorkspaceId}
2749
+ `
2750
+ );
2751
+ await host2.connect();
2752
+ return;
2753
+ }
2754
+ const workspace = new FileBackedWorkspaceProvider(dir);
2755
+ const workspaces = new SingleWorkspaceAdapter(workspace, null);
2756
+ const host = createMcpServer({ workspace, workspaces, mock });
2757
+ process.stderr.write(`apicircle-mcp: single-workspace mode \xB7 ${dir}
2758
+ `);
2505
2759
  await host.connect();
2506
2760
  }
2507
2761
  main().catch((err) => {