@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.
package/dist/index.js CHANGED
@@ -434,18 +434,37 @@ function renderRust(req) {
434
434
  return lines.join("\n");
435
435
  }
436
436
 
437
- // src/tools/crud.ts
437
+ // src/tools/workspaceList.ts
438
438
  import { z as z4 } from "zod";
439
+ var workspaceListTool = {
440
+ name: "workspace.list",
441
+ 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.",
442
+ inputSchema: z4.object({}),
443
+ async handler(_input, ctx) {
444
+ const summaries = await ctx.workspaces.list();
445
+ return {
446
+ activeWorkspaceId: ctx.workspaces.activeId(),
447
+ workspaceCount: summaries.length,
448
+ workspaces: summaries,
449
+ // Plain-text hint the AI surfaces when telling the user. Cheap to
450
+ // generate here and saves round-trips on disambiguation prompts.
451
+ 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.`
452
+ };
453
+ }
454
+ };
455
+
456
+ // src/tools/crud.ts
457
+ import { z as z5 } from "zod";
439
458
  import { generateId as generateId2 } from "@apicircle/shared";
440
- var HTTP_METHOD = z4.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
459
+ var HTTP_METHOD = z5.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
441
460
  var requestCreateTool = {
442
461
  name: "request.create",
443
462
  description: "Create a new request from explicit fields and persist it.",
444
- inputSchema: z4.object({
445
- name: z4.string().default("New request"),
463
+ inputSchema: z5.object({
464
+ name: z5.string().default("New request"),
446
465
  method: HTTP_METHOD.default("GET"),
447
- url: z4.string().default(""),
448
- folderId: z4.string().nullable().optional()
466
+ url: z5.string().default(""),
467
+ folderId: z5.string().nullable().optional()
449
468
  }),
450
469
  async handler(input, ctx) {
451
470
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -474,7 +493,7 @@ var requestCreateTool = {
474
493
  var requestReadTool = {
475
494
  name: "request.read",
476
495
  description: "Read a request by id, or list summaries (id, name, method, url) when no id is provided.",
477
- inputSchema: z4.object({ id: z4.string().optional() }),
496
+ inputSchema: z5.object({ id: z5.string().optional() }),
478
497
  async handler(input, ctx) {
479
498
  const state = await ctx.workspace.read();
480
499
  if (input.id) {
@@ -495,13 +514,13 @@ var requestReadTool = {
495
514
  var requestUpdateTool = {
496
515
  name: "request.update",
497
516
  description: "Patch fields on an existing request.",
498
- inputSchema: z4.object({
499
- id: z4.string(),
500
- patch: z4.object({
501
- name: z4.string().optional(),
517
+ inputSchema: z5.object({
518
+ id: z5.string(),
519
+ patch: z5.object({
520
+ name: z5.string().optional(),
502
521
  method: HTTP_METHOD.optional(),
503
- url: z4.string().optional(),
504
- folderId: z4.string().nullable().optional()
522
+ url: z5.string().optional(),
523
+ folderId: z5.string().nullable().optional()
505
524
  }).strict()
506
525
  }),
507
526
  async handler(input, ctx) {
@@ -516,7 +535,7 @@ var requestUpdateTool = {
516
535
  var requestDeleteTool = {
517
536
  name: "request.delete",
518
537
  description: "Delete a request by id.",
519
- inputSchema: z4.object({ id: z4.string() }),
538
+ inputSchema: z5.object({ id: z5.string() }),
520
539
  async handler(input, ctx) {
521
540
  const out = await ctx.workspace.apply({ kind: "request.delete", id: input.id });
522
541
  return { changedIds: out.changedIds };
@@ -525,9 +544,9 @@ var requestDeleteTool = {
525
544
  var folderCreateTool = {
526
545
  name: "folder.create",
527
546
  description: "Create a folder under an optional parent folder.",
528
- inputSchema: z4.object({
529
- name: z4.string().default("New folder"),
530
- parentId: z4.string().nullable().optional()
547
+ inputSchema: z5.object({
548
+ name: z5.string().default("New folder"),
549
+ parentId: z5.string().nullable().optional()
531
550
  }),
532
551
  async handler(input, ctx) {
533
552
  const folder = {
@@ -542,7 +561,7 @@ var folderCreateTool = {
542
561
  var folderReadTool = {
543
562
  name: "folder.read",
544
563
  description: "Read a folder by id, or list all folders when no id is provided.",
545
- inputSchema: z4.object({ id: z4.string().optional() }),
564
+ inputSchema: z5.object({ id: z5.string().optional() }),
546
565
  async handler(input, ctx) {
547
566
  const state = await ctx.workspace.read();
548
567
  if (input.id) {
@@ -558,9 +577,9 @@ var folderReadTool = {
558
577
  var folderUpdateTool = {
559
578
  name: "folder.update",
560
579
  description: "Move a folder to a new parent (or to root with parentId: null).",
561
- inputSchema: z4.object({
562
- id: z4.string(),
563
- parentId: z4.string().nullable()
580
+ inputSchema: z5.object({
581
+ id: z5.string(),
582
+ parentId: z5.string().nullable()
564
583
  }),
565
584
  async handler(input, ctx) {
566
585
  const out = await ctx.workspace.apply({
@@ -574,23 +593,23 @@ var folderUpdateTool = {
574
593
  var folderDeleteTool = {
575
594
  name: "folder.delete",
576
595
  description: "Delete a folder. Direct children (sub-folders + requests) are reparented to the deleted folder's parent.",
577
- inputSchema: z4.object({ id: z4.string() }),
596
+ inputSchema: z5.object({ id: z5.string() }),
578
597
  async handler(input, ctx) {
579
598
  const out = await ctx.workspace.apply({ kind: "folder.delete", id: input.id });
580
599
  return { changedIds: out.changedIds };
581
600
  }
582
601
  };
583
- var VARIABLE = z4.object({
584
- key: z4.string(),
585
- value: z4.string(),
586
- encrypted: z4.boolean().default(false)
602
+ var VARIABLE = z5.object({
603
+ key: z5.string(),
604
+ value: z5.string(),
605
+ encrypted: z5.boolean().default(false)
587
606
  });
588
607
  var environmentCreateTool = {
589
608
  name: "environment.create",
590
609
  description: "Create a new environment (or upsert one with the same name).",
591
- inputSchema: z4.object({
592
- name: z4.string(),
593
- variables: z4.array(VARIABLE).default([])
610
+ inputSchema: z5.object({
611
+ name: z5.string(),
612
+ variables: z5.array(VARIABLE).default([])
594
613
  }),
595
614
  async handler(input, ctx) {
596
615
  const env = { name: input.name, variables: input.variables };
@@ -601,7 +620,7 @@ var environmentCreateTool = {
601
620
  var environmentReadTool = {
602
621
  name: "environment.read",
603
622
  description: "Read environments \u2014 pass `name` for one, or omit for the full list.",
604
- inputSchema: z4.object({ name: z4.string().optional() }),
623
+ inputSchema: z5.object({ name: z5.string().optional() }),
605
624
  async handler(input, ctx) {
606
625
  const state = await ctx.workspace.read();
607
626
  if (input.name) {
@@ -618,9 +637,9 @@ var environmentReadTool = {
618
637
  var environmentUpdateTool = {
619
638
  name: "environment.update",
620
639
  description: "Replace the variables list of an environment.",
621
- inputSchema: z4.object({
622
- name: z4.string(),
623
- variables: z4.array(VARIABLE)
640
+ inputSchema: z5.object({
641
+ name: z5.string(),
642
+ variables: z5.array(VARIABLE)
624
643
  }),
625
644
  async handler(input, ctx) {
626
645
  const out = await ctx.workspace.apply({
@@ -633,7 +652,7 @@ var environmentUpdateTool = {
633
652
  var environmentDeleteTool = {
634
653
  name: "environment.delete",
635
654
  description: "Delete an environment by name.",
636
- inputSchema: z4.object({ name: z4.string() }),
655
+ inputSchema: z5.object({ name: z5.string() }),
637
656
  async handler(input, ctx) {
638
657
  const out = await ctx.workspace.apply({ kind: "environment.delete", name: input.name });
639
658
  return { changedIds: out.changedIds };
@@ -642,7 +661,7 @@ var environmentDeleteTool = {
642
661
  var environmentSetActiveTool = {
643
662
  name: "environment.set_active",
644
663
  description: "Set (or clear) the active environment. Pass `name: null` to deactivate the current environment.",
645
- inputSchema: z4.object({ name: z4.string().nullable() }),
664
+ inputSchema: z5.object({ name: z5.string().nullable() }),
646
665
  async handler(input, ctx) {
647
666
  const out = await ctx.workspace.apply({
648
667
  kind: "environment.setActive",
@@ -654,18 +673,18 @@ var environmentSetActiveTool = {
654
673
  var environmentSetPriorityTool = {
655
674
  name: "environment.set_priority",
656
675
  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.',
657
- inputSchema: z4.object({
658
- order: z4.array(
659
- z4.union([
660
- z4.string(),
661
- z4.object({
662
- kind: z4.literal("local"),
663
- name: z4.string()
676
+ inputSchema: z5.object({
677
+ order: z5.array(
678
+ z5.union([
679
+ z5.string(),
680
+ z5.object({
681
+ kind: z5.literal("local"),
682
+ name: z5.string()
664
683
  }),
665
- z4.object({
666
- kind: z4.literal("linked"),
667
- linkedWorkspaceId: z4.string(),
668
- envName: z4.string()
684
+ z5.object({
685
+ kind: z5.literal("linked"),
686
+ linkedWorkspaceId: z5.string(),
687
+ envName: z5.string()
669
688
  })
670
689
  ])
671
690
  )
@@ -682,7 +701,7 @@ var environmentSetPriorityTool = {
682
701
  var environmentExportTool = {
683
702
  name: "environment.export",
684
703
  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.",
685
- inputSchema: z4.object({ name: z4.string() }),
704
+ inputSchema: z5.object({ name: z5.string() }),
686
705
  async handler(input, ctx) {
687
706
  const state = await ctx.workspace.read();
688
707
  const env = state.synced.environments.items[input.name];
@@ -700,9 +719,9 @@ var environmentExportTool = {
700
719
  var environmentImportTool = {
701
720
  name: "environment.import",
702
721
  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.",
703
- inputSchema: z4.object({
704
- json: z4.string().min(1),
705
- overwrite: z4.boolean().default(false)
722
+ inputSchema: z5.object({
723
+ json: z5.string().min(1),
724
+ overwrite: z5.boolean().default(false)
706
725
  }),
707
726
  async handler(input, ctx) {
708
727
  let parsed;
@@ -732,17 +751,17 @@ var environmentImportTool = {
732
751
  return { ok: true, name: env.name, changedIds: out.changedIds };
733
752
  }
734
753
  };
735
- var PLAN_STEP = z4.object({
736
- requestId: z4.string(),
737
- linkedWorkspaceId: z4.string().optional()
754
+ var PLAN_STEP = z5.object({
755
+ requestId: z5.string(),
756
+ linkedWorkspaceId: z5.string().optional()
738
757
  });
739
758
  var planCreateTool = {
740
759
  name: "plan.create",
741
760
  description: "Create a new execution plan (sequence of request steps).",
742
- inputSchema: z4.object({
743
- name: z4.string().default("New plan"),
744
- steps: z4.array(PLAN_STEP).default([]),
745
- envPriorityOrder: z4.array(z4.string()).default([])
761
+ inputSchema: z5.object({
762
+ name: z5.string().default("New plan"),
763
+ steps: z5.array(PLAN_STEP).default([]),
764
+ envPriorityOrder: z5.array(z5.string()).default([])
746
765
  }),
747
766
  async handler(input, ctx) {
748
767
  const id = generateId2();
@@ -762,7 +781,7 @@ var planCreateTool = {
762
781
  var planReadTool = {
763
782
  name: "plan.read",
764
783
  description: "Read a plan by id, or list all plans when no id is provided.",
765
- inputSchema: z4.object({ id: z4.string().optional() }),
784
+ inputSchema: z5.object({ id: z5.string().optional() }),
766
785
  async handler(input, ctx) {
767
786
  const state = await ctx.workspace.read();
768
787
  if (input.id) {
@@ -778,12 +797,12 @@ var planReadTool = {
778
797
  var planUpdateTool = {
779
798
  name: "plan.update",
780
799
  description: "Patch fields on an existing plan.",
781
- inputSchema: z4.object({
782
- id: z4.string(),
783
- patch: z4.object({
784
- name: z4.string().optional(),
785
- steps: z4.array(PLAN_STEP).optional(),
786
- envPriorityOrder: z4.array(z4.string()).optional()
800
+ inputSchema: z5.object({
801
+ id: z5.string(),
802
+ patch: z5.object({
803
+ name: z5.string().optional(),
804
+ steps: z5.array(PLAN_STEP).optional(),
805
+ envPriorityOrder: z5.array(z5.string()).optional()
787
806
  }).strict()
788
807
  }),
789
808
  async handler(input, ctx) {
@@ -804,7 +823,7 @@ var planUpdateTool = {
804
823
  var planDeleteTool = {
805
824
  name: "plan.delete",
806
825
  description: "Delete a plan by id. Drops history rows referencing this plan.",
807
- inputSchema: z4.object({ id: z4.string() }),
826
+ inputSchema: z5.object({ id: z5.string() }),
808
827
  async handler(input, ctx) {
809
828
  const out = await ctx.workspace.apply({ kind: "plan.delete", id: input.id });
810
829
  return { changedIds: out.changedIds };
@@ -813,11 +832,11 @@ var planDeleteTool = {
813
832
  var planAddStepTool = {
814
833
  name: "plan.add_step",
815
834
  description: "Append a step to an execution plan. Optional `position` (0-based) inserts at that index instead.",
816
- inputSchema: z4.object({
817
- planId: z4.string(),
818
- requestId: z4.string(),
819
- linkedWorkspaceId: z4.string().optional(),
820
- position: z4.number().int().nonnegative().optional()
835
+ inputSchema: z5.object({
836
+ planId: z5.string(),
837
+ requestId: z5.string(),
838
+ linkedWorkspaceId: z5.string().optional(),
839
+ position: z5.number().int().nonnegative().optional()
821
840
  }),
822
841
  async handler(input, ctx) {
823
842
  const state = await ctx.workspace.read();
@@ -843,9 +862,9 @@ var planAddStepTool = {
843
862
  var planRemoveStepTool = {
844
863
  name: "plan.remove_step",
845
864
  description: "Remove a step from a plan by 0-based index.",
846
- inputSchema: z4.object({
847
- planId: z4.string(),
848
- index: z4.number().int().nonnegative()
865
+ inputSchema: z5.object({
866
+ planId: z5.string(),
867
+ index: z5.number().int().nonnegative()
849
868
  }),
850
869
  async handler(input, ctx) {
851
870
  const state = await ctx.workspace.read();
@@ -865,9 +884,9 @@ var planRemoveStepTool = {
865
884
  var planReorderStepsTool = {
866
885
  name: "plan.reorder_steps",
867
886
  description: "Replace the plan steps with a new permutation. The supplied indices must reference valid current step indices.",
868
- inputSchema: z4.object({
869
- planId: z4.string(),
870
- order: z4.array(z4.number().int().nonnegative())
887
+ inputSchema: z5.object({
888
+ planId: z5.string(),
889
+ order: z5.array(z5.number().int().nonnegative())
871
890
  }),
872
891
  async handler(input, ctx) {
873
892
  const state = await ctx.workspace.read();
@@ -889,13 +908,13 @@ var planReorderStepsTool = {
889
908
  return { ok: true, changedIds: out.changedIds };
890
909
  }
891
910
  };
892
- var PLAN_VARIABLE = z4.object({ key: z4.string(), value: z4.string() });
911
+ var PLAN_VARIABLE = z5.object({ key: z5.string(), value: z5.string() });
893
912
  var planSetVariablesTool = {
894
913
  name: "plan.set_variables",
895
914
  description: "Replace the plan-scoped variables. These live highest-priority during plan runs (above environment vars, below context vars).",
896
- inputSchema: z4.object({
897
- planId: z4.string(),
898
- variables: z4.array(PLAN_VARIABLE)
915
+ inputSchema: z5.object({
916
+ planId: z5.string(),
917
+ variables: z5.array(PLAN_VARIABLE)
899
918
  }),
900
919
  async handler(input, ctx) {
901
920
  const state = await ctx.workspace.read();
@@ -911,9 +930,9 @@ var planSetVariablesTool = {
911
930
  var planRunTool = {
912
931
  name: "plan.run",
913
932
  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.",
914
- inputSchema: z4.object({
915
- id: z4.string(),
916
- withAssertions: z4.boolean().default(true)
933
+ inputSchema: z5.object({
934
+ id: z5.string(),
935
+ withAssertions: z5.boolean().default(true)
917
936
  }),
918
937
  async handler(input, ctx) {
919
938
  const state = await ctx.workspace.read();
@@ -927,18 +946,18 @@ var planRunTool = {
927
946
  };
928
947
  }
929
948
  };
930
- var ASSERTION = z4.object({
931
- id: z4.string().optional(),
932
- kind: z4.enum(["status", "header", "json-path", "duration"]),
933
- op: z4.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
934
- target: z4.string().optional(),
935
- expected: z4.union([z4.string(), z4.number()])
949
+ var ASSERTION = z5.object({
950
+ id: z5.string().optional(),
951
+ kind: z5.enum(["status", "header", "json-path", "duration"]),
952
+ op: z5.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
953
+ target: z5.string().optional(),
954
+ expected: z5.union([z5.string(), z5.number()])
936
955
  });
937
956
  var assertionCreateTool = {
938
957
  name: "assertion.create",
939
958
  description: "Add an assertion to a request.",
940
- inputSchema: z4.object({
941
- requestId: z4.string(),
959
+ inputSchema: z5.object({
960
+ requestId: z5.string(),
942
961
  assertion: ASSERTION
943
962
  }),
944
963
  async handler(input, ctx) {
@@ -957,9 +976,9 @@ var assertionCreateTool = {
957
976
  var assertionReadTool = {
958
977
  name: "assertion.read",
959
978
  description: "List assertions for a request, or fetch a single assertion by id.",
960
- inputSchema: z4.object({
961
- requestId: z4.string(),
962
- assertionId: z4.string().optional()
979
+ inputSchema: z5.object({
980
+ requestId: z5.string(),
981
+ assertionId: z5.string().optional()
963
982
  }),
964
983
  async handler(input, ctx) {
965
984
  const state = await ctx.workspace.read();
@@ -975,8 +994,8 @@ var assertionReadTool = {
975
994
  var assertionUpdateTool = {
976
995
  name: "assertion.update",
977
996
  description: "Replace an existing assertion (matched by `assertion.id`).",
978
- inputSchema: z4.object({
979
- requestId: z4.string(),
997
+ inputSchema: z5.object({
998
+ requestId: z5.string(),
980
999
  assertion: ASSERTION.required({ id: true })
981
1000
  }),
982
1001
  async handler(input, ctx) {
@@ -991,9 +1010,9 @@ var assertionUpdateTool = {
991
1010
  var assertionDeleteTool = {
992
1011
  name: "assertion.delete",
993
1012
  description: "Remove an assertion from a request.",
994
- inputSchema: z4.object({
995
- requestId: z4.string(),
996
- assertionId: z4.string()
1013
+ inputSchema: z5.object({
1014
+ requestId: z5.string(),
1015
+ assertionId: z5.string()
997
1016
  }),
998
1017
  async handler(input, ctx) {
999
1018
  const out = await ctx.workspace.apply({
@@ -1006,21 +1025,53 @@ var assertionDeleteTool = {
1006
1025
  };
1007
1026
  var workspaceReadTool = {
1008
1027
  name: "workspace.read",
1009
- description: "Return the full `{ synced, local }` workspace pair. Use sparingly \u2014 entity-specific tools are more efficient for small reads.",
1010
- inputSchema: z4.object({}),
1011
- async handler(_input, ctx) {
1012
- return await ctx.workspace.read();
1028
+ 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.',
1029
+ inputSchema: z5.object({
1030
+ workspaceId: z5.string().min(1).max(256).optional().describe(
1031
+ 'Optional workspace id (from `workspace.list`). Omit to read the active workspace; the tool will switch to the "multiple workspaces" envelope when ambiguous.'
1032
+ )
1033
+ }),
1034
+ async handler(input, ctx) {
1035
+ if (input.workspaceId) {
1036
+ const provider = ctx.workspaces.for(input.workspaceId);
1037
+ const state2 = await provider.read();
1038
+ return {
1039
+ kind: "single",
1040
+ workspaceId: state2.synced.workspaceId,
1041
+ synced: state2.synced,
1042
+ local: state2.local
1043
+ };
1044
+ }
1045
+ const summaries = await ctx.workspaces.list();
1046
+ if (summaries.length > 1) {
1047
+ return {
1048
+ kind: "multiple-workspaces",
1049
+ activeWorkspaceId: ctx.workspaces.activeId(),
1050
+ workspaceCount: summaries.length,
1051
+ workspaces: summaries,
1052
+ 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.`
1053
+ };
1054
+ }
1055
+ const state = await ctx.workspace.read();
1056
+ return {
1057
+ kind: "single",
1058
+ workspaceId: state.synced.workspaceId,
1059
+ synced: state.synced,
1060
+ local: state.local
1061
+ };
1013
1062
  }
1014
1063
  };
1015
1064
  var workspaceWriteTool = {
1016
1065
  name: "workspace.write",
1017
- 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.",
1018
- inputSchema: z4.object({
1019
- synced: z4.unknown().optional(),
1020
- local: z4.unknown().optional()
1066
+ 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.",
1067
+ inputSchema: z5.object({
1068
+ workspaceId: z5.string().min(1).max(256).optional().describe("Optional workspace id; omit to write the active workspace."),
1069
+ synced: z5.unknown().optional(),
1070
+ local: z5.unknown().optional()
1021
1071
  }),
1022
1072
  async handler(input, ctx) {
1023
- const next = await ctx.workspace.write({
1073
+ const provider = input.workspaceId ? ctx.workspaces.for(input.workspaceId) : ctx.workspace;
1074
+ const next = await provider.write({
1024
1075
  synced: input.synced,
1025
1076
  local: input.local
1026
1077
  });
@@ -1029,16 +1080,16 @@ var workspaceWriteTool = {
1029
1080
  };
1030
1081
 
1031
1082
  // src/tools/history.ts
1032
- import { z as z5 } from "zod";
1083
+ import { z as z6 } from "zod";
1033
1084
  var historyListRunsTool = {
1034
1085
  name: "history.list_runs",
1035
1086
  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.",
1036
- inputSchema: z5.object({
1037
- requestId: z5.string().optional(),
1038
- ok: z5.boolean().optional(),
1039
- since: z5.string().optional(),
1040
- until: z5.string().optional(),
1041
- limit: z5.number().int().positive().max(500).default(100)
1087
+ inputSchema: z6.object({
1088
+ requestId: z6.string().optional(),
1089
+ ok: z6.boolean().optional(),
1090
+ since: z6.string().optional(),
1091
+ until: z6.string().optional(),
1092
+ limit: z6.number().int().positive().max(500).default(100)
1042
1093
  }),
1043
1094
  async handler(input, ctx) {
1044
1095
  const state = await ctx.workspace.read();
@@ -1072,7 +1123,7 @@ var historyListRunsTool = {
1072
1123
  var historyGetRunTool = {
1073
1124
  name: "history.get_run",
1074
1125
  description: "Fetch a single history row in full (headers, body preview, assertion results).",
1075
- inputSchema: z5.object({ id: z5.string() }),
1126
+ inputSchema: z6.object({ id: z6.string() }),
1076
1127
  async handler(input, ctx) {
1077
1128
  const state = await ctx.workspace.read();
1078
1129
  const run = state.local.history.requestRuns.find((r) => r.id === input.id);
@@ -1083,7 +1134,7 @@ var historyGetRunTool = {
1083
1134
  var historyDeleteRunTool = {
1084
1135
  name: "history.delete_run",
1085
1136
  description: "Delete a single request-run row by id.",
1086
- inputSchema: z5.object({ id: z5.string() }),
1137
+ inputSchema: z6.object({ id: z6.string() }),
1087
1138
  async handler(input, ctx) {
1088
1139
  const out = await ctx.workspace.apply({ kind: "history.delete_run", runId: input.id });
1089
1140
  return { deleted: out.changedIds.length, changedIds: out.changedIds };
@@ -1092,8 +1143,8 @@ var historyDeleteRunTool = {
1092
1143
  var historyPurgeTool = {
1093
1144
  name: "history.purge_by_age",
1094
1145
  description: "Drop every request-run + plan-run older than `olderThanDays` days. Pass 0 to clear all history.",
1095
- inputSchema: z5.object({
1096
- olderThanDays: z5.number().nonnegative()
1146
+ inputSchema: z6.object({
1147
+ olderThanDays: z6.number().nonnegative()
1097
1148
  }),
1098
1149
  async handler(input, ctx) {
1099
1150
  const olderThanMs = input.olderThanDays * 24 * 60 * 60 * 1e3;
@@ -1103,15 +1154,15 @@ var historyPurgeTool = {
1103
1154
  };
1104
1155
 
1105
1156
  // src/tools/codebase.ts
1106
- import { z as z6 } from "zod";
1157
+ import { z as z7 } from "zod";
1107
1158
  var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "options", "head"];
1108
1159
  var codebaseExtractCollectionTool = {
1109
1160
  name: "codebase.extract_collection",
1110
1161
  description: "Scan source code for HTTP route definitions (Express, FastAPI, NestJS, Spring) and return candidate requests for the user to confirm before import.",
1111
- inputSchema: z6.object({
1112
- source: z6.string().min(1),
1162
+ inputSchema: z7.object({
1163
+ source: z7.string().min(1),
1113
1164
  /** Hint to limit which framework patterns to apply. Empty = try all. */
1114
- frameworks: z6.array(z6.enum(["express", "fastapi", "nest", "spring"])).default([])
1165
+ frameworks: z7.array(z7.enum(["express", "fastapi", "nest", "spring"])).default([])
1115
1166
  }),
1116
1167
  async handler(input) {
1117
1168
  const enabled = new Set(
@@ -1192,18 +1243,18 @@ var codebaseExtractCollectionTool = {
1192
1243
  };
1193
1244
 
1194
1245
  // src/tools/prompt.ts
1195
- import { z as z7 } from "zod";
1246
+ import { z as z8 } from "zod";
1196
1247
  import { generateId as generateId3, makeDefaultMockResponse, makeDefaultRequestSchema } from "@apicircle/shared";
1197
1248
  var promptCreateEnvironmentTool = {
1198
1249
  name: "prompt.create_environment",
1199
1250
  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.",
1200
- inputSchema: z7.object({
1201
- name: z7.string(),
1202
- variables: z7.array(
1203
- z7.object({
1204
- key: z7.string(),
1205
- value: z7.string(),
1206
- encrypted: z7.boolean().default(false)
1251
+ inputSchema: z8.object({
1252
+ name: z8.string(),
1253
+ variables: z8.array(
1254
+ z8.object({
1255
+ key: z8.string(),
1256
+ value: z8.string(),
1257
+ encrypted: z8.boolean().default(false)
1207
1258
  })
1208
1259
  )
1209
1260
  }),
@@ -1216,13 +1267,13 @@ var promptCreateEnvironmentTool = {
1216
1267
  var promptCreateAssertionTool = {
1217
1268
  name: "prompt.create_assertion",
1218
1269
  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".',
1219
- inputSchema: z7.object({
1220
- requestId: z7.string(),
1221
- assertion: z7.object({
1222
- kind: z7.enum(["status", "header", "json-path", "duration"]),
1223
- op: z7.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1224
- target: z7.string().optional(),
1225
- expected: z7.union([z7.string(), z7.number()])
1270
+ inputSchema: z8.object({
1271
+ requestId: z8.string(),
1272
+ assertion: z8.object({
1273
+ kind: z8.enum(["status", "header", "json-path", "duration"]),
1274
+ op: z8.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1275
+ target: z8.string().optional(),
1276
+ expected: z8.union([z8.string(), z8.number()])
1226
1277
  })
1227
1278
  }),
1228
1279
  async handler(input, ctx) {
@@ -1241,10 +1292,10 @@ var promptCreateAssertionTool = {
1241
1292
  var promptCreatePlanTool = {
1242
1293
  name: "prompt.create_plan",
1243
1294
  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.",
1244
- inputSchema: z7.object({
1245
- name: z7.string(),
1246
- stepRequestIds: z7.array(z7.string()).default([]),
1247
- envPriorityOrder: z7.array(z7.string()).default([])
1295
+ inputSchema: z8.object({
1296
+ name: z8.string(),
1297
+ stepRequestIds: z8.array(z8.string()).default([]),
1298
+ envPriorityOrder: z8.array(z8.string()).default([])
1248
1299
  }),
1249
1300
  async handler(input, ctx) {
1250
1301
  const state = await ctx.workspace.read();
@@ -1273,51 +1324,51 @@ var promptCreatePlanTool = {
1273
1324
  return { ok: true, id, changedIds: out.changedIds };
1274
1325
  }
1275
1326
  };
1276
- var HTTP_METHOD2 = z7.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
1277
- var HEADER_OR_QUERY = z7.object({
1278
- key: z7.string(),
1279
- value: z7.string(),
1280
- enabled: z7.boolean().default(true)
1327
+ var HTTP_METHOD2 = z8.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
1328
+ var HEADER_OR_QUERY = z8.object({
1329
+ key: z8.string(),
1330
+ value: z8.string(),
1331
+ enabled: z8.boolean().default(true)
1281
1332
  });
1282
- var REQUEST_BODY = z7.object({
1283
- type: z7.enum(["none", "json", "text", "xml", "graphql", "urlencoded"]).default("none"),
1284
- content: z7.string().default(""),
1285
- variables: z7.string().optional()
1333
+ var REQUEST_BODY = z8.object({
1334
+ type: z8.enum(["none", "json", "text", "xml", "graphql", "urlencoded"]).default("none"),
1335
+ content: z8.string().default(""),
1336
+ variables: z8.string().optional()
1286
1337
  });
1287
- var PROMPT_AUTH = z7.discriminatedUnion("type", [
1288
- z7.object({ type: z7.literal("none") }),
1289
- z7.object({ type: z7.literal("inherit") }),
1290
- z7.object({ type: z7.literal("bearer"), token: z7.string().default("") }),
1291
- z7.object({
1292
- type: z7.literal("basic"),
1293
- username: z7.string().default(""),
1294
- password: z7.string().default("")
1338
+ var PROMPT_AUTH = z8.discriminatedUnion("type", [
1339
+ z8.object({ type: z8.literal("none") }),
1340
+ z8.object({ type: z8.literal("inherit") }),
1341
+ z8.object({ type: z8.literal("bearer"), token: z8.string().default("") }),
1342
+ z8.object({
1343
+ type: z8.literal("basic"),
1344
+ username: z8.string().default(""),
1345
+ password: z8.string().default("")
1295
1346
  }),
1296
- z7.object({
1297
- type: z7.literal("api-key"),
1298
- key: z7.string().default(""),
1299
- value: z7.string().default(""),
1300
- addTo: z7.enum(["header", "query", "cookie"]).default("header")
1347
+ z8.object({
1348
+ type: z8.literal("api-key"),
1349
+ key: z8.string().default(""),
1350
+ value: z8.string().default(""),
1351
+ addTo: z8.enum(["header", "query", "cookie"]).default("header")
1301
1352
  }),
1302
- z7.object({
1303
- type: z7.literal("custom-header"),
1304
- key: z7.string().default(""),
1305
- value: z7.string().default("")
1353
+ z8.object({
1354
+ type: z8.literal("custom-header"),
1355
+ key: z8.string().default(""),
1356
+ value: z8.string().default("")
1306
1357
  })
1307
1358
  ]);
1308
- var PROMPT_ASSERTION = z7.object({
1309
- kind: z7.enum(["status", "header", "json-path", "duration"]),
1310
- op: z7.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1311
- target: z7.string().optional(),
1312
- expected: z7.union([z7.string(), z7.number()])
1359
+ var PROMPT_ASSERTION = z8.object({
1360
+ kind: z8.enum(["status", "header", "json-path", "duration"]),
1361
+ op: z8.enum(["equals", "not-equals", "contains", "lt", "gt", "matches"]),
1362
+ target: z8.string().optional(),
1363
+ expected: z8.union([z8.string(), z8.number()])
1313
1364
  });
1314
- var ENDPOINT_RESPONSE = z7.object({
1315
- status: z7.number().int().min(100).max(599).default(200),
1316
- jsonBody: z7.string().default("{}"),
1317
- contentType: z7.string().default("application/json")
1365
+ var ENDPOINT_RESPONSE = z8.object({
1366
+ status: z8.number().int().min(100).max(599).default(200),
1367
+ jsonBody: z8.string().default("{}"),
1368
+ contentType: z8.string().default("application/json")
1318
1369
  });
1319
- var VALIDATION_RULE_NL = z7.object({
1320
- kind: z7.enum([
1370
+ var VALIDATION_RULE_NL = z8.object({
1371
+ kind: z8.enum([
1321
1372
  "header-required",
1322
1373
  "header-equals",
1323
1374
  "header-matches",
@@ -1328,50 +1379,50 @@ var VALIDATION_RULE_NL = z7.object({
1328
1379
  "body-required",
1329
1380
  "content-type-equals"
1330
1381
  ]),
1331
- target: z7.string().default(""),
1332
- expected: z7.string().optional(),
1333
- message: z7.string().optional(),
1334
- enabled: z7.boolean().default(true),
1335
- failResponse: z7.object({
1336
- status: z7.number().int().min(100).max(599).default(400),
1337
- jsonBody: z7.string().default('{"error":"validation failed"}')
1382
+ target: z8.string().default(""),
1383
+ expected: z8.string().optional(),
1384
+ message: z8.string().optional(),
1385
+ enabled: z8.boolean().default(true),
1386
+ failResponse: z8.object({
1387
+ status: z8.number().int().min(100).max(599).default(400),
1388
+ jsonBody: z8.string().default('{"error":"validation failed"}')
1338
1389
  }).default({})
1339
1390
  });
1340
- var CONDITION_CLAUSE_NL = z7.object({
1341
- scope: z7.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
1342
- target: z7.string(),
1343
- op: z7.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
1344
- value: z7.string().optional()
1391
+ var CONDITION_CLAUSE_NL = z8.object({
1392
+ scope: z8.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
1393
+ target: z8.string(),
1394
+ op: z8.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
1395
+ value: z8.string().optional()
1345
1396
  });
1346
- var RESPONSE_RULE_NL = z7.object({
1347
- name: z7.string(),
1348
- enabled: z7.boolean().default(true),
1349
- when: z7.array(CONDITION_CLAUSE_NL).default([]),
1350
- response: z7.object({
1351
- status: z7.number().int().min(100).max(599).default(200),
1352
- jsonBody: z7.string().default("{}")
1397
+ var RESPONSE_RULE_NL = z8.object({
1398
+ name: z8.string(),
1399
+ enabled: z8.boolean().default(true),
1400
+ when: z8.array(CONDITION_CLAUSE_NL).default([]),
1401
+ response: z8.object({
1402
+ status: z8.number().int().min(100).max(599).default(200),
1403
+ jsonBody: z8.string().default("{}")
1353
1404
  }).default({})
1354
1405
  });
1355
- var MULTIPLIER_NL = z7.object({
1356
- name: z7.string().optional(),
1357
- source: z7.object({
1358
- kind: z7.enum(["query", "pathParam", "header", "body-json-path"]),
1359
- key: z7.string()
1406
+ var MULTIPLIER_NL = z8.object({
1407
+ name: z8.string().optional(),
1408
+ source: z8.object({
1409
+ kind: z8.enum(["query", "pathParam", "header", "body-json-path"]),
1410
+ key: z8.string()
1360
1411
  }),
1361
- targetJsonPath: z7.string(),
1362
- defaultCount: z7.number().int().nonnegative().default(0),
1363
- min: z7.number().int().nonnegative().optional(),
1364
- max: z7.number().int().nonnegative().optional()
1412
+ targetJsonPath: z8.string(),
1413
+ defaultCount: z8.number().int().nonnegative().default(0),
1414
+ min: z8.number().int().nonnegative().optional(),
1415
+ max: z8.number().int().nonnegative().optional()
1365
1416
  });
1366
- var ENDPOINT_INPUT = z7.object({
1417
+ var ENDPOINT_INPUT = z8.object({
1367
1418
  method: HTTP_METHOD2,
1368
- pathPattern: z7.string().min(1),
1369
- name: z7.string().optional(),
1370
- description: z7.string().optional(),
1419
+ pathPattern: z8.string().min(1),
1420
+ name: z8.string().optional(),
1421
+ description: z8.string().optional(),
1371
1422
  response: ENDPOINT_RESPONSE.optional(),
1372
- validationRules: z7.array(VALIDATION_RULE_NL).default([]),
1373
- responseRules: z7.array(RESPONSE_RULE_NL).default([]),
1374
- multipliers: z7.array(MULTIPLIER_NL).default([])
1423
+ validationRules: z8.array(VALIDATION_RULE_NL).default([]),
1424
+ responseRules: z8.array(RESPONSE_RULE_NL).default([]),
1425
+ multipliers: z8.array(MULTIPLIER_NL).default([])
1375
1426
  });
1376
1427
  function buildRequestBody(input) {
1377
1428
  if (!input) return { type: "none", content: "" };
@@ -1464,17 +1515,17 @@ function patchEndpoint(mock, endpointId, patcher) {
1464
1515
  var promptCreateRequestTool = {
1465
1516
  name: "prompt.create_request",
1466
1517
  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.",
1467
- inputSchema: z7.object({
1468
- name: z7.string().default("New request"),
1518
+ inputSchema: z8.object({
1519
+ name: z8.string().default("New request"),
1469
1520
  method: HTTP_METHOD2.default("GET"),
1470
- url: z7.string().default(""),
1471
- folderId: z7.string().nullable().optional(),
1472
- headers: z7.array(HEADER_OR_QUERY).default([]),
1473
- queryParams: z7.array(HEADER_OR_QUERY).default([]),
1474
- pathParams: z7.record(z7.string(), z7.string()).optional(),
1521
+ url: z8.string().default(""),
1522
+ folderId: z8.string().nullable().optional(),
1523
+ headers: z8.array(HEADER_OR_QUERY).default([]),
1524
+ queryParams: z8.array(HEADER_OR_QUERY).default([]),
1525
+ pathParams: z8.record(z8.string(), z8.string()).optional(),
1475
1526
  body: REQUEST_BODY.optional(),
1476
1527
  auth: PROMPT_AUTH.optional(),
1477
- assertions: z7.array(PROMPT_ASSERTION).default([])
1528
+ assertions: z8.array(PROMPT_ASSERTION).default([])
1478
1529
  }),
1479
1530
  async handler(input, ctx) {
1480
1531
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1504,19 +1555,19 @@ var promptCreateRequestTool = {
1504
1555
  var promptUpdateRequestTool = {
1505
1556
  name: "prompt.update_request",
1506
1557
  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.",
1507
- inputSchema: z7.object({
1508
- id: z7.string(),
1509
- patch: z7.object({
1510
- name: z7.string().optional(),
1558
+ inputSchema: z8.object({
1559
+ id: z8.string(),
1560
+ patch: z8.object({
1561
+ name: z8.string().optional(),
1511
1562
  method: HTTP_METHOD2.optional(),
1512
- url: z7.string().optional(),
1513
- folderId: z7.string().nullable().optional(),
1514
- headers: z7.array(HEADER_OR_QUERY).optional(),
1515
- queryParams: z7.array(HEADER_OR_QUERY).optional(),
1516
- pathParams: z7.record(z7.string(), z7.string()).optional(),
1563
+ url: z8.string().optional(),
1564
+ folderId: z8.string().nullable().optional(),
1565
+ headers: z8.array(HEADER_OR_QUERY).optional(),
1566
+ queryParams: z8.array(HEADER_OR_QUERY).optional(),
1567
+ pathParams: z8.record(z8.string(), z8.string()).optional(),
1517
1568
  body: REQUEST_BODY.optional(),
1518
1569
  auth: PROMPT_AUTH.optional(),
1519
- assertions: z7.array(PROMPT_ASSERTION).optional()
1570
+ assertions: z8.array(PROMPT_ASSERTION).optional()
1520
1571
  }).strict()
1521
1572
  }),
1522
1573
  async handler(input, ctx) {
@@ -1544,17 +1595,17 @@ var promptUpdateRequestTool = {
1544
1595
  return { ok: true, changedIds: out.changedIds };
1545
1596
  }
1546
1597
  };
1547
- var FOLDER_TREE_NODE = z7.lazy(
1548
- () => z7.object({
1549
- name: z7.string(),
1550
- children: z7.array(FOLDER_TREE_NODE).optional()
1598
+ var FOLDER_TREE_NODE = z8.lazy(
1599
+ () => z8.object({
1600
+ name: z8.string(),
1601
+ children: z8.array(FOLDER_TREE_NODE).optional()
1551
1602
  })
1552
1603
  );
1553
1604
  var promptCreateFolderTreeTool = {
1554
1605
  name: "prompt.create_folder_tree",
1555
1606
  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.",
1556
- inputSchema: z7.object({
1557
- parentId: z7.string().nullable().optional(),
1607
+ inputSchema: z8.object({
1608
+ parentId: z8.string().nullable().optional(),
1558
1609
  tree: FOLDER_TREE_NODE
1559
1610
  }),
1560
1611
  async handler(input, ctx) {
@@ -1580,9 +1631,9 @@ var promptCreateFolderTreeTool = {
1580
1631
  var promptAddPlanStepsTool = {
1581
1632
  name: "prompt.add_plan_steps",
1582
1633
  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.",
1583
- inputSchema: z7.object({
1584
- planId: z7.string(),
1585
- requestIds: z7.array(z7.string()).min(1)
1634
+ inputSchema: z8.object({
1635
+ planId: z8.string(),
1636
+ requestIds: z8.array(z8.string()).min(1)
1586
1637
  }),
1587
1638
  async handler(input, ctx) {
1588
1639
  const state = await ctx.workspace.read();
@@ -1612,9 +1663,9 @@ var promptAddPlanStepsTool = {
1612
1663
  var promptSetPlanVariablesTool = {
1613
1664
  name: "prompt.set_plan_variables",
1614
1665
  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.",
1615
- inputSchema: z7.object({
1616
- planId: z7.string(),
1617
- variables: z7.array(z7.object({ key: z7.string(), value: z7.string() }))
1666
+ inputSchema: z8.object({
1667
+ planId: z8.string(),
1668
+ variables: z8.array(z8.object({ key: z8.string(), value: z8.string() }))
1618
1669
  }),
1619
1670
  async handler(input, ctx) {
1620
1671
  const state = await ctx.workspace.read();
@@ -1630,10 +1681,10 @@ var promptSetPlanVariablesTool = {
1630
1681
  var promptCreateMockServerTool = {
1631
1682
  name: "prompt.create_mock_server",
1632
1683
  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.",
1633
- inputSchema: z7.object({
1634
- name: z7.string().min(1),
1635
- defaultPort: z7.number().int().positive().nullable().optional(),
1636
- endpoints: z7.array(ENDPOINT_INPUT).default([])
1684
+ inputSchema: z8.object({
1685
+ name: z8.string().min(1),
1686
+ defaultPort: z8.number().int().positive().nullable().optional(),
1687
+ endpoints: z8.array(ENDPOINT_INPUT).default([])
1637
1688
  }),
1638
1689
  async handler(input, ctx) {
1639
1690
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1660,16 +1711,16 @@ var promptCreateMockServerTool = {
1660
1711
  var promptAddMockEndpointTool = {
1661
1712
  name: "prompt.add_mock_endpoint",
1662
1713
  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.",
1663
- inputSchema: z7.object({
1664
- mockId: z7.string(),
1714
+ inputSchema: z8.object({
1715
+ mockId: z8.string(),
1665
1716
  method: HTTP_METHOD2,
1666
- pathPattern: z7.string().min(1),
1667
- name: z7.string().optional(),
1668
- description: z7.string().optional(),
1717
+ pathPattern: z8.string().min(1),
1718
+ name: z8.string().optional(),
1719
+ description: z8.string().optional(),
1669
1720
  response: ENDPOINT_RESPONSE.optional(),
1670
- validationRules: z7.array(VALIDATION_RULE_NL).default([]),
1671
- responseRules: z7.array(RESPONSE_RULE_NL).default([]),
1672
- multipliers: z7.array(MULTIPLIER_NL).default([])
1721
+ validationRules: z8.array(VALIDATION_RULE_NL).default([]),
1722
+ responseRules: z8.array(RESPONSE_RULE_NL).default([]),
1723
+ multipliers: z8.array(MULTIPLIER_NL).default([])
1673
1724
  }),
1674
1725
  async handler(input, ctx) {
1675
1726
  const state = await ctx.workspace.read();
@@ -1691,10 +1742,10 @@ var promptAddMockEndpointTool = {
1691
1742
  var promptSetEndpointValidationRulesTool = {
1692
1743
  name: "prompt.set_endpoint_validation_rules",
1693
1744
  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.",
1694
- inputSchema: z7.object({
1695
- mockId: z7.string(),
1696
- endpointId: z7.string(),
1697
- rules: z7.array(VALIDATION_RULE_NL)
1745
+ inputSchema: z8.object({
1746
+ mockId: z8.string(),
1747
+ endpointId: z8.string(),
1748
+ rules: z8.array(VALIDATION_RULE_NL)
1698
1749
  }),
1699
1750
  async handler(input, ctx) {
1700
1751
  const state = await ctx.workspace.read();
@@ -1725,10 +1776,10 @@ var promptSetEndpointValidationRulesTool = {
1725
1776
  var promptSetEndpointResponseRulesTool = {
1726
1777
  name: "prompt.set_endpoint_response_rules",
1727
1778
  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.",
1728
- inputSchema: z7.object({
1729
- mockId: z7.string(),
1730
- endpointId: z7.string(),
1731
- rules: z7.array(RESPONSE_RULE_NL)
1779
+ inputSchema: z8.object({
1780
+ mockId: z8.string(),
1781
+ endpointId: z8.string(),
1782
+ rules: z8.array(RESPONSE_RULE_NL)
1732
1783
  }),
1733
1784
  async handler(input, ctx) {
1734
1785
  const state = await ctx.workspace.read();
@@ -1763,10 +1814,10 @@ var promptSetEndpointResponseRulesTool = {
1763
1814
  var promptSetEndpointMultipliersTool = {
1764
1815
  name: "prompt.set_endpoint_multipliers",
1765
1816
  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.",
1766
- inputSchema: z7.object({
1767
- mockId: z7.string(),
1768
- endpointId: z7.string(),
1769
- multipliers: z7.array(MULTIPLIER_NL)
1817
+ inputSchema: z8.object({
1818
+ mockId: z8.string(),
1819
+ endpointId: z8.string(),
1820
+ multipliers: z8.array(MULTIPLIER_NL)
1770
1821
  }),
1771
1822
  async handler(input, ctx) {
1772
1823
  const state = await ctx.workspace.read();
@@ -1795,7 +1846,7 @@ var promptSetEndpointMultipliersTool = {
1795
1846
  };
1796
1847
 
1797
1848
  // src/tools/mocks.ts
1798
- import { z as z8 } from "zod";
1849
+ import { z as z9 } from "zod";
1799
1850
  import { generateId as generateId4, makeDefaultMockResponse as makeDefaultMockResponse2, makeDefaultRequestSchema as makeDefaultRequestSchema2 } from "@apicircle/shared";
1800
1851
  import { parseSourceToEndpoints } from "@apicircle/mock-server-core";
1801
1852
  async function ingestSource(source, name) {
@@ -1818,10 +1869,10 @@ async function ingestSource(source, name) {
1818
1869
  var mockCreateFromOpenApiTool = {
1819
1870
  name: "mock.create_from_openapi",
1820
1871
  description: "Create a mock server from an OpenAPI / Swagger spec (YAML or JSON).",
1821
- inputSchema: z8.object({
1822
- name: z8.string(),
1823
- spec: z8.string().min(1),
1824
- format: z8.enum(["json", "yaml"]).default("json")
1872
+ inputSchema: z9.object({
1873
+ name: z9.string(),
1874
+ spec: z9.string().min(1),
1875
+ format: z9.enum(["json", "yaml"]).default("json")
1825
1876
  }),
1826
1877
  async handler(input, ctx) {
1827
1878
  const { mock, warnings } = await ingestSource(
@@ -1840,7 +1891,7 @@ var mockCreateFromOpenApiTool = {
1840
1891
  var mockCreateFromPostmanTool = {
1841
1892
  name: "mock.create_from_postman",
1842
1893
  description: "Create a mock server from a Postman v2/v2.1 collection.",
1843
- inputSchema: z8.object({ name: z8.string(), collection: z8.string().min(1) }),
1894
+ inputSchema: z9.object({ name: z9.string(), collection: z9.string().min(1) }),
1844
1895
  async handler(input, ctx) {
1845
1896
  const { mock, warnings } = await ingestSource(
1846
1897
  { kind: "postman", collection: input.collection },
@@ -1858,7 +1909,7 @@ var mockCreateFromPostmanTool = {
1858
1909
  var mockCreateFromInsomniaTool = {
1859
1910
  name: "mock.create_from_insomnia",
1860
1911
  description: "Create a mock server from an Insomnia v4 export.",
1861
- inputSchema: z8.object({ name: z8.string(), export: z8.string().min(1) }),
1912
+ inputSchema: z9.object({ name: z9.string(), export: z9.string().min(1) }),
1862
1913
  async handler(input, ctx) {
1863
1914
  const { mock, warnings } = await ingestSource(
1864
1915
  { kind: "insomnia", export: input.export },
@@ -1876,7 +1927,7 @@ var mockCreateFromInsomniaTool = {
1876
1927
  var mockImportPostmanMockCollectionTool = {
1877
1928
  name: "mock.import_postman_mock_collection",
1878
1929
  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.",
1879
- inputSchema: z8.object({ name: z8.string(), collection: z8.string().min(1) }),
1930
+ inputSchema: z9.object({ name: z9.string(), collection: z9.string().min(1) }),
1880
1931
  async handler(input, ctx) {
1881
1932
  const { mock, warnings } = await ingestSource(
1882
1933
  { kind: "postman", collection: input.collection },
@@ -1894,7 +1945,7 @@ var mockImportPostmanMockCollectionTool = {
1894
1945
  var mockListTool = {
1895
1946
  name: "mock.list",
1896
1947
  description: "List all mock servers in the workspace plus their runtime status (running / stopped, port).",
1897
- inputSchema: z8.object({}),
1948
+ inputSchema: z9.object({}),
1898
1949
  async handler(_input, ctx) {
1899
1950
  const state = await ctx.workspace.read();
1900
1951
  const running = await ctx.mock.list();
@@ -1916,9 +1967,9 @@ var mockListTool = {
1916
1967
  var mockStartTool = {
1917
1968
  name: "mock.start",
1918
1969
  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.",
1919
- inputSchema: z8.object({
1920
- id: z8.string(),
1921
- port: z8.number().int().positive().optional()
1970
+ inputSchema: z9.object({
1971
+ id: z9.string(),
1972
+ port: z9.number().int().positive().optional()
1922
1973
  }),
1923
1974
  async handler(input, ctx) {
1924
1975
  const state = await ctx.workspace.read();
@@ -1935,7 +1986,7 @@ var mockStartTool = {
1935
1986
  var mockStopTool = {
1936
1987
  name: "mock.stop",
1937
1988
  description: "Stop a running mock server by id (no-op if not running).",
1938
- inputSchema: z8.object({ id: z8.string() }),
1989
+ inputSchema: z9.object({ id: z9.string() }),
1939
1990
  async handler(input, ctx) {
1940
1991
  try {
1941
1992
  await ctx.mock.stop(input.id);
@@ -1948,7 +1999,7 @@ var mockStopTool = {
1948
1999
  var mockDeleteTool = {
1949
2000
  name: "mock.delete",
1950
2001
  description: "Delete a mock server definition. Stops it first if it's running.",
1951
- inputSchema: z8.object({ id: z8.string() }),
2002
+ inputSchema: z9.object({ id: z9.string() }),
1952
2003
  async handler(input, ctx) {
1953
2004
  try {
1954
2005
  await ctx.mock.stop(input.id);
@@ -1958,13 +2009,13 @@ var mockDeleteTool = {
1958
2009
  return { ok: true, changedIds: out.changedIds };
1959
2010
  }
1960
2011
  };
1961
- var HTTP_METHOD3 = z8.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2012
+ var HTTP_METHOD3 = z9.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
1962
2013
  var mockCreateManualTool = {
1963
2014
  name: "mock.create_manual",
1964
2015
  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.",
1965
- inputSchema: z8.object({
1966
- name: z8.string().min(1),
1967
- defaultPort: z8.number().int().positive().nullable().optional()
2016
+ inputSchema: z9.object({
2017
+ name: z9.string().min(1),
2018
+ defaultPort: z9.number().int().positive().nullable().optional()
1968
2019
  }),
1969
2020
  async handler(input, ctx) {
1970
2021
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1987,7 +2038,7 @@ var mockCreateManualTool = {
1987
2038
  var mockListEndpointsTool = {
1988
2039
  name: "mock.list_endpoints",
1989
2040
  description: "List endpoints for a mock server (id, method, path, name).",
1990
- inputSchema: z8.object({ mockId: z8.string() }),
2041
+ inputSchema: z9.object({ mockId: z9.string() }),
1991
2042
  async handler(input, ctx) {
1992
2043
  const state = await ctx.workspace.read();
1993
2044
  const mock = state.synced.mockServers[input.mockId];
@@ -2006,10 +2057,10 @@ var mockListEndpointsTool = {
2006
2057
  };
2007
2058
  }
2008
2059
  };
2009
- var ENDPOINT_RESPONSE2 = z8.object({
2010
- status: z8.number().int().min(100).max(599).default(200),
2011
- jsonBody: z8.string().default("{}"),
2012
- contentType: z8.string().default("application/json")
2060
+ var ENDPOINT_RESPONSE2 = z9.object({
2061
+ status: z9.number().int().min(100).max(599).default(200),
2062
+ jsonBody: z9.string().default("{}"),
2063
+ contentType: z9.string().default("application/json")
2013
2064
  });
2014
2065
  function buildDefaultEndpoint(args) {
2015
2066
  const response = args.response ?? {
@@ -2038,12 +2089,12 @@ function buildDefaultEndpoint(args) {
2038
2089
  var mockAddEndpointTool = {
2039
2090
  name: "mock.add_endpoint",
2040
2091
  description: "Append a new endpoint to a mock server. Defaults to a 200 JSON response of `{}`. Returns the new endpoint id.",
2041
- inputSchema: z8.object({
2042
- mockId: z8.string(),
2092
+ inputSchema: z9.object({
2093
+ mockId: z9.string(),
2043
2094
  method: HTTP_METHOD3,
2044
- pathPattern: z8.string().min(1),
2045
- name: z8.string().optional(),
2046
- description: z8.string().optional(),
2095
+ pathPattern: z9.string().min(1),
2096
+ name: z9.string().optional(),
2097
+ description: z9.string().optional(),
2047
2098
  response: ENDPOINT_RESPONSE2.optional()
2048
2099
  }),
2049
2100
  async handler(input, ctx) {
@@ -2066,13 +2117,13 @@ var mockAddEndpointTool = {
2066
2117
  var mockUpdateEndpointTool = {
2067
2118
  name: "mock.update_endpoint",
2068
2119
  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.",
2069
- inputSchema: z8.object({
2070
- mockId: z8.string(),
2071
- endpointId: z8.string(),
2120
+ inputSchema: z9.object({
2121
+ mockId: z9.string(),
2122
+ endpointId: z9.string(),
2072
2123
  method: HTTP_METHOD3.optional(),
2073
- pathPattern: z8.string().optional(),
2074
- name: z8.string().optional(),
2075
- description: z8.string().optional(),
2124
+ pathPattern: z9.string().optional(),
2125
+ name: z9.string().optional(),
2126
+ description: z9.string().optional(),
2076
2127
  response: ENDPOINT_RESPONSE2.partial().optional()
2077
2128
  }),
2078
2129
  async handler(input, ctx) {
@@ -2113,7 +2164,7 @@ var mockUpdateEndpointTool = {
2113
2164
  var mockDeleteEndpointTool = {
2114
2165
  name: "mock.delete_endpoint",
2115
2166
  description: "Remove an endpoint from a mock server.",
2116
- inputSchema: z8.object({ mockId: z8.string(), endpointId: z8.string() }),
2167
+ inputSchema: z9.object({ mockId: z9.string(), endpointId: z9.string() }),
2117
2168
  async handler(input, ctx) {
2118
2169
  const state = await ctx.workspace.read();
2119
2170
  const mock = state.synced.mockServers[input.mockId];
@@ -2133,9 +2184,9 @@ var mockDeleteEndpointTool = {
2133
2184
  return { ok: true, changedIds: out.changedIds };
2134
2185
  }
2135
2186
  };
2136
- var VALIDATION_RULE = z8.object({
2137
- id: z8.string().optional(),
2138
- kind: z8.enum([
2187
+ var VALIDATION_RULE = z9.object({
2188
+ id: z9.string().optional(),
2189
+ kind: z9.enum([
2139
2190
  "header-required",
2140
2191
  "header-equals",
2141
2192
  "header-matches",
@@ -2146,43 +2197,43 @@ var VALIDATION_RULE = z8.object({
2146
2197
  "body-required",
2147
2198
  "content-type-equals"
2148
2199
  ]),
2149
- target: z8.string().default(""),
2150
- expected: z8.string().optional(),
2151
- message: z8.string().optional(),
2152
- enabled: z8.boolean().default(true),
2153
- failResponse: z8.object({
2154
- status: z8.number().int().min(100).max(599).default(400),
2155
- jsonBody: z8.string().default('{"error":"validation failed"}')
2200
+ target: z9.string().default(""),
2201
+ expected: z9.string().optional(),
2202
+ message: z9.string().optional(),
2203
+ enabled: z9.boolean().default(true),
2204
+ failResponse: z9.object({
2205
+ status: z9.number().int().min(100).max(599).default(400),
2206
+ jsonBody: z9.string().default('{"error":"validation failed"}')
2156
2207
  }).default({})
2157
2208
  });
2158
- var CONDITION_CLAUSE = z8.object({
2159
- id: z8.string().optional(),
2160
- scope: z8.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
2161
- target: z8.string(),
2162
- op: z8.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
2163
- value: z8.string().optional()
2209
+ var CONDITION_CLAUSE = z9.object({
2210
+ id: z9.string().optional(),
2211
+ scope: z9.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
2212
+ target: z9.string(),
2213
+ op: z9.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
2214
+ value: z9.string().optional()
2164
2215
  });
2165
- var RESPONSE_RULE = z8.object({
2166
- id: z8.string().optional(),
2167
- name: z8.string(),
2168
- enabled: z8.boolean().default(true),
2169
- when: z8.array(CONDITION_CLAUSE).default([]),
2170
- response: z8.object({
2171
- status: z8.number().int().min(100).max(599).default(200),
2172
- jsonBody: z8.string().default("{}")
2216
+ var RESPONSE_RULE = z9.object({
2217
+ id: z9.string().optional(),
2218
+ name: z9.string(),
2219
+ enabled: z9.boolean().default(true),
2220
+ when: z9.array(CONDITION_CLAUSE).default([]),
2221
+ response: z9.object({
2222
+ status: z9.number().int().min(100).max(599).default(200),
2223
+ jsonBody: z9.string().default("{}")
2173
2224
  }).default({})
2174
2225
  });
2175
- var MULTIPLIER = z8.object({
2176
- id: z8.string().optional(),
2177
- name: z8.string().optional(),
2178
- source: z8.object({
2179
- kind: z8.enum(["query", "pathParam", "header", "body-json-path"]),
2180
- key: z8.string()
2226
+ var MULTIPLIER = z9.object({
2227
+ id: z9.string().optional(),
2228
+ name: z9.string().optional(),
2229
+ source: z9.object({
2230
+ kind: z9.enum(["query", "pathParam", "header", "body-json-path"]),
2231
+ key: z9.string()
2181
2232
  }),
2182
- targetJsonPath: z8.string(),
2183
- defaultCount: z8.number().int().nonnegative().default(0),
2184
- min: z8.number().int().nonnegative().optional(),
2185
- max: z8.number().int().nonnegative().optional()
2233
+ targetJsonPath: z9.string(),
2234
+ defaultCount: z9.number().int().nonnegative().default(0),
2235
+ min: z9.number().int().nonnegative().optional(),
2236
+ max: z9.number().int().nonnegative().optional()
2186
2237
  });
2187
2238
  function defaultJsonResponseConfig(args) {
2188
2239
  return {
@@ -2207,10 +2258,10 @@ function patchEndpoint2(mock, endpointId, patcher) {
2207
2258
  var mockSetValidationRulesTool = {
2208
2259
  name: "mock.set_validation_rules",
2209
2260
  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.",
2210
- inputSchema: z8.object({
2211
- mockId: z8.string(),
2212
- endpointId: z8.string(),
2213
- rules: z8.array(VALIDATION_RULE)
2261
+ inputSchema: z9.object({
2262
+ mockId: z9.string(),
2263
+ endpointId: z9.string(),
2264
+ rules: z9.array(VALIDATION_RULE)
2214
2265
  }),
2215
2266
  async handler(input, ctx) {
2216
2267
  const state = await ctx.workspace.read();
@@ -2237,10 +2288,10 @@ var mockSetValidationRulesTool = {
2237
2288
  var mockSetResponseRulesTool = {
2238
2289
  name: "mock.set_response_rules",
2239
2290
  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.",
2240
- inputSchema: z8.object({
2241
- mockId: z8.string(),
2242
- endpointId: z8.string(),
2243
- rules: z8.array(RESPONSE_RULE)
2291
+ inputSchema: z9.object({
2292
+ mockId: z9.string(),
2293
+ endpointId: z9.string(),
2294
+ rules: z9.array(RESPONSE_RULE)
2244
2295
  }),
2245
2296
  async handler(input, ctx) {
2246
2297
  const state = await ctx.workspace.read();
@@ -2271,10 +2322,10 @@ var mockSetResponseRulesTool = {
2271
2322
  var mockSetMultipliersTool = {
2272
2323
  name: "mock.set_multipliers",
2273
2324
  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.",
2274
- inputSchema: z8.object({
2275
- mockId: z8.string(),
2276
- endpointId: z8.string(),
2277
- multipliers: z8.array(MULTIPLIER)
2325
+ inputSchema: z9.object({
2326
+ mockId: z9.string(),
2327
+ endpointId: z9.string(),
2328
+ multipliers: z9.array(MULTIPLIER)
2278
2329
  }),
2279
2330
  async handler(input, ctx) {
2280
2331
  const state = await ctx.workspace.read();
@@ -2310,6 +2361,7 @@ var TOOL_REGISTRY = [
2310
2361
  importInsomniaTool,
2311
2362
  importHarTool,
2312
2363
  generateCodeTool,
2364
+ workspaceListTool,
2313
2365
  workspaceReadTool,
2314
2366
  workspaceWriteTool,
2315
2367
  requestCreateTool,
@@ -2380,6 +2432,63 @@ function getTool(name) {
2380
2432
  return TOOL_REGISTRY.find((t) => t.name === name);
2381
2433
  }
2382
2434
 
2435
+ // src/providers/Workspaces.ts
2436
+ var SingleWorkspaceAdapter = class {
2437
+ constructor(provider, workspaceId, displayName = "Workspace") {
2438
+ this.provider = provider;
2439
+ this.workspaceId = workspaceId;
2440
+ this.displayName = displayName;
2441
+ }
2442
+ provider;
2443
+ workspaceId;
2444
+ displayName;
2445
+ async list() {
2446
+ const state = await this.provider.read();
2447
+ const id = state.synced.workspaceId;
2448
+ this.workspaceId = id;
2449
+ return [
2450
+ {
2451
+ id,
2452
+ name: this.displayName,
2453
+ isActive: true,
2454
+ createdAt: state.synced.meta.createdAt,
2455
+ lastOpenedAt: state.synced.meta.updatedAt,
2456
+ counts: {
2457
+ requests: Object.keys(state.synced.collections.requests).length,
2458
+ folders: Object.keys(state.synced.collections.folders).length,
2459
+ environments: Object.keys(state.synced.environments.items).length,
2460
+ mockServers: Object.keys(state.synced.mockServers ?? {}).length,
2461
+ plans: Object.keys(state.synced.executionPlans ?? {}).length
2462
+ }
2463
+ }
2464
+ ];
2465
+ }
2466
+ for(workspaceId) {
2467
+ if (this.workspaceId && workspaceId !== this.workspaceId) {
2468
+ throw new WorkspaceNotFoundError(workspaceId);
2469
+ }
2470
+ return this.provider;
2471
+ }
2472
+ activeId() {
2473
+ return this.workspaceId;
2474
+ }
2475
+ setActive(workspaceId) {
2476
+ if (this.workspaceId && workspaceId !== this.workspaceId) {
2477
+ throw new WorkspaceNotFoundError(workspaceId);
2478
+ }
2479
+ return Promise.resolve();
2480
+ }
2481
+ };
2482
+ var WorkspaceNotFoundError = class extends Error {
2483
+ code = "workspace-not-found";
2484
+ workspaceId;
2485
+ constructor(workspaceId) {
2486
+ super(`No workspace with id "${workspaceId}" is available on this server.`);
2487
+ this.name = "WorkspaceNotFoundError";
2488
+ this.workspaceId = workspaceId;
2489
+ }
2490
+ };
2491
+
2383
2492
  // src/providers/InMemoryWorkspaceProvider.ts
2384
2493
  import { applyMutation } from "@apicircle/core";
2385
2494
  var InMemoryWorkspaceProvider = class {
@@ -2440,6 +2549,115 @@ var FileBackedWorkspaceProvider = class {
2440
2549
  }
2441
2550
  };
2442
2551
 
2552
+ // src/providers/MultiWorkspaceProvider.ts
2553
+ import {
2554
+ loadRegistry,
2555
+ loadWorkspaceById,
2556
+ saveRegistry,
2557
+ setActiveWorkspace as setActiveWorkspaceOnDisk,
2558
+ workspaceDirFor
2559
+ } from "@apicircle/core/workspace/registry";
2560
+ var MultiWorkspaceProvider = class {
2561
+ constructor(registryRoot) {
2562
+ this.registryRoot = registryRoot;
2563
+ }
2564
+ registryRoot;
2565
+ active = null;
2566
+ activeWorkspaceId = null;
2567
+ /**
2568
+ * Hydrate the active provider from disk. Must be called once before the
2569
+ * MCP host boots so `ctx.workspace.read()` doesn't race the first
2570
+ * registry-load. Returns the registry the boot can log.
2571
+ */
2572
+ async init() {
2573
+ const registry = await loadRegistry(this.registryRoot) ?? {
2574
+ schemaVersion: 1,
2575
+ activeWorkspaceId: null,
2576
+ workspaces: []
2577
+ };
2578
+ if (registry.activeWorkspaceId) {
2579
+ this.activeWorkspaceId = registry.activeWorkspaceId;
2580
+ this.active = new FileBackedWorkspaceProvider(
2581
+ workspaceDirFor(this.registryRoot, registry.activeWorkspaceId)
2582
+ );
2583
+ }
2584
+ return registry;
2585
+ }
2586
+ /** The provider tool handlers see as `ctx.workspace`. */
2587
+ activeProvider() {
2588
+ if (!this.active) {
2589
+ throw new Error(
2590
+ "No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
2591
+ );
2592
+ }
2593
+ return this.active;
2594
+ }
2595
+ // ─── Workspaces interface ──────────────────────────────────────────────────
2596
+ async list() {
2597
+ const registry = await loadRegistry(this.registryRoot) ?? {
2598
+ schemaVersion: 1,
2599
+ activeWorkspaceId: null,
2600
+ workspaces: []
2601
+ };
2602
+ const out = [];
2603
+ for (const entry of registry.workspaces) {
2604
+ let counts = null;
2605
+ try {
2606
+ const state = await loadWorkspaceById(this.registryRoot, entry.id);
2607
+ if (state) {
2608
+ counts = {
2609
+ requests: Object.keys(state.synced.collections.requests).length,
2610
+ folders: Object.keys(state.synced.collections.folders).length,
2611
+ environments: Object.keys(state.synced.environments.items).length,
2612
+ mockServers: Object.keys(state.synced.mockServers ?? {}).length,
2613
+ plans: Object.keys(state.synced.executionPlans ?? {}).length
2614
+ };
2615
+ }
2616
+ } catch {
2617
+ counts = null;
2618
+ }
2619
+ out.push({
2620
+ id: entry.id,
2621
+ name: entry.name,
2622
+ isActive: entry.id === registry.activeWorkspaceId,
2623
+ createdAt: entry.createdAt,
2624
+ lastOpenedAt: entry.lastOpenedAt,
2625
+ counts
2626
+ });
2627
+ }
2628
+ return out;
2629
+ }
2630
+ for(workspaceId) {
2631
+ return new FileBackedWorkspaceProvider(workspaceDirFor(this.registryRoot, workspaceId));
2632
+ }
2633
+ activeId() {
2634
+ return this.activeWorkspaceId;
2635
+ }
2636
+ async setActive(workspaceId) {
2637
+ const registry = await loadRegistry(this.registryRoot);
2638
+ if (!registry || !registry.workspaces.some((w) => w.id === workspaceId)) {
2639
+ throw new WorkspaceNotFoundError(workspaceId);
2640
+ }
2641
+ const next = await setActiveWorkspaceOnDisk(this.registryRoot, workspaceId);
2642
+ void next;
2643
+ this.activeWorkspaceId = workspaceId;
2644
+ this.active = new FileBackedWorkspaceProvider(workspaceDirFor(this.registryRoot, workspaceId));
2645
+ }
2646
+ /**
2647
+ * Idempotent registry write — used by tests / tools that need to
2648
+ * persist registry updates that didn't go through `setActive`.
2649
+ */
2650
+ async writeRegistry(registry) {
2651
+ await saveRegistry(this.registryRoot, registry);
2652
+ this.activeWorkspaceId = registry.activeWorkspaceId;
2653
+ if (registry.activeWorkspaceId) {
2654
+ this.active = new FileBackedWorkspaceProvider(
2655
+ workspaceDirFor(this.registryRoot, registry.activeWorkspaceId)
2656
+ );
2657
+ }
2658
+ }
2659
+ };
2660
+
2443
2661
  // src/providers/InProcessMockController.ts
2444
2662
  import {
2445
2663
  startMockServer,
@@ -2483,10 +2701,19 @@ var InProcessMockController = class {
2483
2701
 
2484
2702
  // src/index.ts
2485
2703
  function createMcpServer(options) {
2704
+ const workspaces = options.workspaces ?? new SingleWorkspaceAdapter(
2705
+ options.workspace,
2706
+ null
2707
+ /* discovered on first list() */
2708
+ );
2486
2709
  return new McpHost({
2487
2710
  serverInfo: options.serverInfo,
2488
2711
  tools: options.tools ?? TOOL_REGISTRY,
2489
- context: { workspace: options.workspace, mock: options.mock }
2712
+ context: {
2713
+ workspace: options.workspace,
2714
+ workspaces,
2715
+ mock: options.mock
2716
+ }
2490
2717
  });
2491
2718
  }
2492
2719
  export {
@@ -2494,7 +2721,10 @@ export {
2494
2721
  InMemoryWorkspaceProvider,
2495
2722
  InProcessMockController,
2496
2723
  McpHost,
2724
+ MultiWorkspaceProvider,
2725
+ SingleWorkspaceAdapter,
2497
2726
  TOOL_REGISTRY,
2727
+ WorkspaceNotFoundError,
2498
2728
  createMcpServer,
2499
2729
  getTool
2500
2730
  };