callman-mcp 0.1.0

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.
Files changed (244) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +203 -0
  4. package/dist/client/api.d.ts +109 -0
  5. package/dist/client/api.d.ts.map +1 -0
  6. package/dist/client/api.js +180 -0
  7. package/dist/client/api.js.map +1 -0
  8. package/dist/client/errors.d.ts +27 -0
  9. package/dist/client/errors.d.ts.map +1 -0
  10. package/dist/client/errors.js +98 -0
  11. package/dist/client/errors.js.map +1 -0
  12. package/dist/client/http.d.ts +34 -0
  13. package/dist/client/http.d.ts.map +1 -0
  14. package/dist/client/http.js +121 -0
  15. package/dist/client/http.js.map +1 -0
  16. package/dist/client/types.d.ts +327 -0
  17. package/dist/client/types.d.ts.map +1 -0
  18. package/dist/client/types.js +5 -0
  19. package/dist/client/types.js.map +1 -0
  20. package/dist/config.d.ts +20 -0
  21. package/dist/config.d.ts.map +1 -0
  22. package/dist/config.js +69 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/context.d.ts +35 -0
  25. package/dist/context.d.ts.map +1 -0
  26. package/dist/context.js +91 -0
  27. package/dist/context.js.map +1 -0
  28. package/dist/domain/diagram/build.d.ts +260 -0
  29. package/dist/domain/diagram/build.d.ts.map +1 -0
  30. package/dist/domain/diagram/build.js +160 -0
  31. package/dist/domain/diagram/build.js.map +1 -0
  32. package/dist/domain/diagram/layout.d.ts +17 -0
  33. package/dist/domain/diagram/layout.d.ts.map +1 -0
  34. package/dist/domain/diagram/layout.js +61 -0
  35. package/dist/domain/diagram/layout.js.map +1 -0
  36. package/dist/domain/diagram/patch.d.ts +330 -0
  37. package/dist/domain/diagram/patch.d.ts.map +1 -0
  38. package/dist/domain/diagram/patch.js +176 -0
  39. package/dist/domain/diagram/patch.js.map +1 -0
  40. package/dist/domain/diagram/summarize.d.ts +41 -0
  41. package/dist/domain/diagram/summarize.d.ts.map +1 -0
  42. package/dist/domain/diagram/summarize.js +47 -0
  43. package/dist/domain/diagram/summarize.js.map +1 -0
  44. package/dist/domain/importDetect.d.ts +6 -0
  45. package/dist/domain/importDetect.d.ts.map +1 -0
  46. package/dist/domain/importDetect.js +32 -0
  47. package/dist/domain/importDetect.js.map +1 -0
  48. package/dist/domain/requestShape.d.ts +796 -0
  49. package/dist/domain/requestShape.d.ts.map +1 -0
  50. package/dist/domain/requestShape.js +250 -0
  51. package/dist/domain/requestShape.js.map +1 -0
  52. package/dist/domain/resolve.d.ts +30 -0
  53. package/dist/domain/resolve.d.ts.map +1 -0
  54. package/dist/domain/resolve.js +56 -0
  55. package/dist/domain/resolve.js.map +1 -0
  56. package/dist/domain/scenario/build.d.ts +270 -0
  57. package/dist/domain/scenario/build.d.ts.map +1 -0
  58. package/dist/domain/scenario/build.js +135 -0
  59. package/dist/domain/scenario/build.js.map +1 -0
  60. package/dist/domain/scenario/summarize.d.ts +206 -0
  61. package/dist/domain/scenario/summarize.d.ts.map +1 -0
  62. package/dist/domain/scenario/summarize.js +138 -0
  63. package/dist/domain/scenario/summarize.js.map +1 -0
  64. package/dist/index.d.ts +3 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +142 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/lib/cache.d.ts +12 -0
  69. package/dist/lib/cache.d.ts.map +1 -0
  70. package/dist/lib/cache.js +40 -0
  71. package/dist/lib/cache.js.map +1 -0
  72. package/dist/lib/ids.d.ts +5 -0
  73. package/dist/lib/ids.d.ts.map +1 -0
  74. package/dist/lib/ids.js +19 -0
  75. package/dist/lib/ids.js.map +1 -0
  76. package/dist/lib/log.d.ts +10 -0
  77. package/dist/lib/log.d.ts.map +1 -0
  78. package/dist/lib/log.js +25 -0
  79. package/dist/lib/log.js.map +1 -0
  80. package/dist/lib/mask.d.ts +24 -0
  81. package/dist/lib/mask.d.ts.map +1 -0
  82. package/dist/lib/mask.js +63 -0
  83. package/dist/lib/mask.js.map +1 -0
  84. package/dist/lib/pagination.d.ts +21 -0
  85. package/dist/lib/pagination.d.ts.map +1 -0
  86. package/dist/lib/pagination.js +40 -0
  87. package/dist/lib/pagination.js.map +1 -0
  88. package/dist/lib/truncate.d.ts +25 -0
  89. package/dist/lib/truncate.d.ts.map +1 -0
  90. package/dist/lib/truncate.js +45 -0
  91. package/dist/lib/truncate.js.map +1 -0
  92. package/dist/main.d.ts +9 -0
  93. package/dist/main.d.ts.map +1 -0
  94. package/dist/main.js +12 -0
  95. package/dist/main.js.map +1 -0
  96. package/dist/prompts/index.d.ts +4 -0
  97. package/dist/prompts/index.d.ts.map +1 -0
  98. package/dist/prompts/index.js +72 -0
  99. package/dist/prompts/index.js.map +1 -0
  100. package/dist/resources/docs/diagramSchema.d.ts +2 -0
  101. package/dist/resources/docs/diagramSchema.d.ts.map +1 -0
  102. package/dist/resources/docs/diagramSchema.js +65 -0
  103. package/dist/resources/docs/diagramSchema.js.map +1 -0
  104. package/dist/resources/docs/overview.d.ts +2 -0
  105. package/dist/resources/docs/overview.d.ts.map +1 -0
  106. package/dist/resources/docs/overview.js +46 -0
  107. package/dist/resources/docs/overview.js.map +1 -0
  108. package/dist/resources/docs/requestSchema.d.ts +2 -0
  109. package/dist/resources/docs/requestSchema.d.ts.map +1 -0
  110. package/dist/resources/docs/requestSchema.js +70 -0
  111. package/dist/resources/docs/requestSchema.js.map +1 -0
  112. package/dist/resources/generated/aiPacks.d.ts +3 -0
  113. package/dist/resources/generated/aiPacks.d.ts.map +1 -0
  114. package/dist/resources/generated/aiPacks.js +11 -0
  115. package/dist/resources/generated/aiPacks.js.map +1 -0
  116. package/dist/resources/index.d.ts +4 -0
  117. package/dist/resources/index.d.ts.map +1 -0
  118. package/dist/resources/index.js +114 -0
  119. package/dist/resources/index.js.map +1 -0
  120. package/dist/run/executeCollection.d.ts +19 -0
  121. package/dist/run/executeCollection.d.ts.map +1 -0
  122. package/dist/run/executeCollection.js +89 -0
  123. package/dist/run/executeCollection.js.map +1 -0
  124. package/dist/run/loadBundle.d.ts +9 -0
  125. package/dist/run/loadBundle.d.ts.map +1 -0
  126. package/dist/run/loadBundle.js +242 -0
  127. package/dist/run/loadBundle.js.map +1 -0
  128. package/dist/run/resolveTemplate.d.ts +2 -0
  129. package/dist/run/resolveTemplate.d.ts.map +1 -0
  130. package/dist/run/resolveTemplate.js +12 -0
  131. package/dist/run/resolveTemplate.js.map +1 -0
  132. package/dist/run/summarize.d.ts +73 -0
  133. package/dist/run/summarize.d.ts.map +1 -0
  134. package/dist/run/summarize.js +69 -0
  135. package/dist/run/summarize.js.map +1 -0
  136. package/dist/run/types.d.ts +46 -0
  137. package/dist/run/types.d.ts.map +1 -0
  138. package/dist/run/types.js +2 -0
  139. package/dist/run/types.js.map +1 -0
  140. package/dist/server.d.ts +23 -0
  141. package/dist/server.d.ts.map +1 -0
  142. package/dist/server.js +25 -0
  143. package/dist/server.js.map +1 -0
  144. package/dist/tools/catalog.d.ts +18 -0
  145. package/dist/tools/catalog.d.ts.map +1 -0
  146. package/dist/tools/catalog.js +66 -0
  147. package/dist/tools/catalog.js.map +1 -0
  148. package/dist/tools/collections.d.ts +4 -0
  149. package/dist/tools/collections.d.ts.map +1 -0
  150. package/dist/tools/collections.js +201 -0
  151. package/dist/tools/collections.js.map +1 -0
  152. package/dist/tools/diagrams.d.ts +4 -0
  153. package/dist/tools/diagrams.d.ts.map +1 -0
  154. package/dist/tools/diagrams.js +226 -0
  155. package/dist/tools/diagrams.js.map +1 -0
  156. package/dist/tools/environments.d.ts +4 -0
  157. package/dist/tools/environments.d.ts.map +1 -0
  158. package/dist/tools/environments.js +150 -0
  159. package/dist/tools/environments.js.map +1 -0
  160. package/dist/tools/import.d.ts +6 -0
  161. package/dist/tools/import.d.ts.map +1 -0
  162. package/dist/tools/import.js +144 -0
  163. package/dist/tools/import.js.map +1 -0
  164. package/dist/tools/index.d.ts +5 -0
  165. package/dist/tools/index.d.ts.map +1 -0
  166. package/dist/tools/index.js +26 -0
  167. package/dist/tools/index.js.map +1 -0
  168. package/dist/tools/lookups.d.ts +33 -0
  169. package/dist/tools/lookups.d.ts.map +1 -0
  170. package/dist/tools/lookups.js +132 -0
  171. package/dist/tools/lookups.js.map +1 -0
  172. package/dist/tools/meta.d.ts +4 -0
  173. package/dist/tools/meta.d.ts.map +1 -0
  174. package/dist/tools/meta.js +78 -0
  175. package/dist/tools/meta.js.map +1 -0
  176. package/dist/tools/requests.d.ts +4 -0
  177. package/dist/tools/requests.d.ts.map +1 -0
  178. package/dist/tools/requests.js +247 -0
  179. package/dist/tools/requests.js.map +1 -0
  180. package/dist/tools/run.d.ts +4 -0
  181. package/dist/tools/run.d.ts.map +1 -0
  182. package/dist/tools/run.js +167 -0
  183. package/dist/tools/run.js.map +1 -0
  184. package/dist/tools/scenarios.d.ts +4 -0
  185. package/dist/tools/scenarios.d.ts.map +1 -0
  186. package/dist/tools/scenarios.js +242 -0
  187. package/dist/tools/scenarios.js.map +1 -0
  188. package/dist/tools/shared.d.ts +20 -0
  189. package/dist/tools/shared.d.ts.map +1 -0
  190. package/dist/tools/shared.js +74 -0
  191. package/dist/tools/shared.js.map +1 -0
  192. package/dist/tools/workspaces.d.ts +4 -0
  193. package/dist/tools/workspaces.d.ts.map +1 -0
  194. package/dist/tools/workspaces.js +80 -0
  195. package/dist/tools/workspaces.js.map +1 -0
  196. package/dist-cjs/client/api.js +183 -0
  197. package/dist-cjs/client/errors.js +104 -0
  198. package/dist-cjs/client/http.js +124 -0
  199. package/dist-cjs/client/types.js +5 -0
  200. package/dist-cjs/config.js +73 -0
  201. package/dist-cjs/context.js +94 -0
  202. package/dist-cjs/domain/diagram/build.js +164 -0
  203. package/dist-cjs/domain/diagram/layout.js +98 -0
  204. package/dist-cjs/domain/diagram/patch.js +179 -0
  205. package/dist-cjs/domain/diagram/summarize.js +52 -0
  206. package/dist-cjs/domain/importDetect.js +40 -0
  207. package/dist-cjs/domain/requestShape.js +260 -0
  208. package/dist-cjs/domain/resolve.js +63 -0
  209. package/dist-cjs/domain/scenario/build.js +140 -0
  210. package/dist-cjs/domain/scenario/summarize.js +144 -0
  211. package/dist-cjs/lib/cache.js +43 -0
  212. package/dist-cjs/lib/ids.js +24 -0
  213. package/dist-cjs/lib/log.js +28 -0
  214. package/dist-cjs/lib/mask.js +72 -0
  215. package/dist-cjs/lib/pagination.js +48 -0
  216. package/dist-cjs/lib/truncate.js +52 -0
  217. package/dist-cjs/main.js +31 -0
  218. package/dist-cjs/package.json +3 -0
  219. package/dist-cjs/prompts/index.js +75 -0
  220. package/dist-cjs/resources/docs/diagramSchema.js +67 -0
  221. package/dist-cjs/resources/docs/overview.js +48 -0
  222. package/dist-cjs/resources/docs/requestSchema.js +72 -0
  223. package/dist-cjs/resources/generated/aiPacks.js +13 -0
  224. package/dist-cjs/resources/index.js +117 -0
  225. package/dist-cjs/run/executeCollection.js +93 -0
  226. package/dist-cjs/run/loadBundle.js +246 -0
  227. package/dist-cjs/run/resolveTemplate.js +15 -0
  228. package/dist-cjs/run/summarize.js +73 -0
  229. package/dist-cjs/run/types.js +2 -0
  230. package/dist-cjs/server.js +28 -0
  231. package/dist-cjs/tools/catalog.js +69 -0
  232. package/dist-cjs/tools/collections.js +204 -0
  233. package/dist-cjs/tools/diagrams.js +229 -0
  234. package/dist-cjs/tools/environments.js +153 -0
  235. package/dist-cjs/tools/import.js +147 -0
  236. package/dist-cjs/tools/index.js +31 -0
  237. package/dist-cjs/tools/lookups.js +149 -0
  238. package/dist-cjs/tools/meta.js +81 -0
  239. package/dist-cjs/tools/requests.js +250 -0
  240. package/dist-cjs/tools/run.js +203 -0
  241. package/dist-cjs/tools/scenarios.js +245 -0
  242. package/dist-cjs/tools/shared.js +81 -0
  243. package/dist-cjs/tools/workspaces.js +83 -0
  244. package/package.json +80 -0
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sequentialIds = exports.normalizeName = exports.isObjectId = exports.OBJECT_ID = void 0;
4
+ exports.OBJECT_ID = /^[0-9a-fA-F]{24}$/;
5
+ const isObjectId = (value) => typeof value === "string" && exports.OBJECT_ID.test(value);
6
+ exports.isObjectId = isObjectId;
7
+ const normalizeName = (value) => value.trim().toLowerCase();
8
+ exports.normalizeName = normalizeName;
9
+ // Deterministic, readable ids for generated documents (n1, n2 … / e1, e2 …).
10
+ const sequentialIds = (prefix, taken = []) => {
11
+ const used = new Set(taken);
12
+ let counter = 1;
13
+ return () => {
14
+ let candidate = `${prefix}${counter}`;
15
+ while (used.has(candidate)) {
16
+ counter += 1;
17
+ candidate = `${prefix}${counter}`;
18
+ }
19
+ used.add(candidate);
20
+ counter += 1;
21
+ return candidate;
22
+ };
23
+ };
24
+ exports.sequentialIds = sequentialIds;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.silentLogger = exports.createLogger = void 0;
4
+ const RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
5
+ const createLogger = (level, sink = (line) => console.error(line)) => {
6
+ const emit = (severity, message, meta) => {
7
+ if (RANK[severity] > RANK[level])
8
+ return;
9
+ const suffix = meta === undefined ? "" : ` ${safeJson(meta)}`;
10
+ sink(`[callman-mcp] ${severity}: ${message}${suffix}`);
11
+ };
12
+ return {
13
+ error: (message, meta) => emit("error", message, meta),
14
+ warn: (message, meta) => emit("warn", message, meta),
15
+ info: (message, meta) => emit("info", message, meta),
16
+ debug: (message, meta) => emit("debug", message, meta),
17
+ };
18
+ };
19
+ exports.createLogger = createLogger;
20
+ exports.silentLogger = { error() { }, warn() { }, info() { }, debug() { } };
21
+ const safeJson = (value) => {
22
+ try {
23
+ return JSON.stringify(value);
24
+ }
25
+ catch {
26
+ return String(value);
27
+ }
28
+ };
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ // Secret masking on the way OUT to the model. The backend already masks
3
+ // environment values for a personal token; this covers what it does not —
4
+ // request auth secrets, sensitive headers and anything a local run resolved.
5
+ // Same sentinel as the backend so a masked value is recognisable and can
6
+ // never be written back by accident (the server rejects it).
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.maskVariableValues = exports.maskAuth = exports.maskHeaderRecord = exports.maskHeaderValue = exports.isSensitiveHeader = exports.isMasked = exports.maskSecret = exports.MASK_SENTINEL = void 0;
9
+ exports.MASK_SENTINEL = "••••";
10
+ const maskSecret = (value) => value.length >= 8 ? `${exports.MASK_SENTINEL}${value.slice(-2)}` : exports.MASK_SENTINEL;
11
+ exports.maskSecret = maskSecret;
12
+ const isMasked = (value) => typeof value === "string" && value.startsWith(exports.MASK_SENTINEL);
13
+ exports.isMasked = isMasked;
14
+ const SENSITIVE_HEADERS = new Set([
15
+ "authorization",
16
+ "proxy-authorization",
17
+ "cookie",
18
+ "set-cookie",
19
+ "x-api-key",
20
+ "x-auth-token",
21
+ "api-key",
22
+ ]);
23
+ const isSensitiveHeader = (name) => SENSITIVE_HEADERS.has(name.trim().toLowerCase());
24
+ exports.isSensitiveHeader = isSensitiveHeader;
25
+ const maskHeaderValue = (name, value) => (0, exports.isSensitiveHeader)(name) && value ? (0, exports.maskSecret)(value) : value;
26
+ exports.maskHeaderValue = maskHeaderValue;
27
+ const maskHeaderRecord = (headers) => {
28
+ const out = {};
29
+ for (const [key, value] of Object.entries(headers ?? {}))
30
+ out[key] = (0, exports.maskHeaderValue)(key, value);
31
+ return out;
32
+ };
33
+ exports.maskHeaderRecord = maskHeaderRecord;
34
+ // Compact, masked view of a request's auth: enough to know HOW it
35
+ // authenticates, never the credential itself.
36
+ const maskAuth = (auth) => {
37
+ const type = auth?.type ?? "none";
38
+ switch (type) {
39
+ case "bearer":
40
+ return { type, token: auth?.bearerToken ? (0, exports.maskSecret)(auth.bearerToken) : "" };
41
+ case "basic":
42
+ return {
43
+ type,
44
+ username: auth?.basicUsername ?? "",
45
+ password: auth?.basicPassword ? (0, exports.maskSecret)(auth.basicPassword) : "",
46
+ };
47
+ case "apikey":
48
+ return {
49
+ type,
50
+ key: auth?.apiKeyKey ?? "",
51
+ value: auth?.apiKeyValue ? (0, exports.maskSecret)(auth.apiKeyValue) : "",
52
+ location: auth?.apiKeyLocation ?? "header",
53
+ };
54
+ case "oauth2": {
55
+ const oauth2 = auth?.oauth2 ?? {};
56
+ return {
57
+ type,
58
+ grantType: oauth2.grantType ?? "client_credentials",
59
+ tokenUrl: oauth2.tokenUrl ?? "",
60
+ clientId: oauth2.clientId ?? "",
61
+ clientSecret: typeof oauth2.clientSecret === "string" && oauth2.clientSecret ? (0, exports.maskSecret)(oauth2.clientSecret) : "",
62
+ scope: oauth2.scope ?? "",
63
+ };
64
+ }
65
+ default:
66
+ return { type: "none" };
67
+ }
68
+ };
69
+ exports.maskAuth = maskAuth;
70
+ // Mask every `value` in a key/value list (environment / scenario variables).
71
+ const maskVariableValues = (variables) => variables.map((variable) => ({ ...variable, value: (0, exports.maskSecret)(variable.value), valueMasked: true }));
72
+ exports.maskVariableValues = maskVariableValues;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sliceWithCursor = exports.nextCursorFor = exports.pageFromCursor = exports.clampLimit = exports.decodeCursor = exports.encodeCursor = void 0;
4
+ const truncate_js_1 = require("./truncate.js");
5
+ const encodeCursor = (cursor) => Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
6
+ exports.encodeCursor = encodeCursor;
7
+ const decodeCursor = (raw) => {
8
+ if (!raw)
9
+ return null;
10
+ try {
11
+ const parsed = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
12
+ if (typeof parsed.page !== "number" || typeof parsed.limit !== "number")
13
+ return null;
14
+ if (!Number.isInteger(parsed.page) || parsed.page < 1)
15
+ return null;
16
+ return { page: parsed.page, limit: (0, exports.clampLimit)(parsed.limit) };
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ };
22
+ exports.decodeCursor = decodeCursor;
23
+ const clampLimit = (limit) => Math.min(truncate_js_1.LIMITS.LIST_MAX, Math.max(1, Math.floor(limit ?? truncate_js_1.LIMITS.LIST_DEFAULT)));
24
+ exports.clampLimit = clampLimit;
25
+ const pageFromCursor = (cursor, limit) => {
26
+ const decoded = (0, exports.decodeCursor)(cursor);
27
+ if (decoded)
28
+ return decoded;
29
+ return { page: 1, limit: (0, exports.clampLimit)(limit) };
30
+ };
31
+ exports.pageFromCursor = pageFromCursor;
32
+ const nextCursorFor = (pagination) => pagination.page < pagination.totalPages
33
+ ? (0, exports.encodeCursor)({ page: pagination.page + 1, limit: pagination.limit })
34
+ : null;
35
+ exports.nextCursorFor = nextCursorFor;
36
+ // Client-side slice for list endpoints that are not paginated server-side.
37
+ const sliceWithCursor = (items, cursor, limit) => {
38
+ const { page, limit: size } = (0, exports.pageFromCursor)(cursor, limit);
39
+ const start = (page - 1) * size;
40
+ const slice = items.slice(start, start + size);
41
+ const totalPages = Math.max(1, Math.ceil(items.length / size));
42
+ return {
43
+ items: slice,
44
+ total: items.length,
45
+ nextCursor: page < totalPages ? (0, exports.encodeCursor)({ page: page + 1, limit: size }) : null,
46
+ };
47
+ };
48
+ exports.sliceWithCursor = sliceWithCursor;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ // Output-size discipline: every tool result is bounded so a large workspace
3
+ // can never blow a model's context. The caps are deliberate constants — a
4
+ // tool that needs more takes an explicit `full: true` and still hits the
5
+ // FULL_* ceilings, reporting `truncated: true` when it does.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.byteLength = exports.safeStringify = exports.jsonPreview = exports.clipBytes = exports.clip = exports.LIMITS = void 0;
8
+ exports.LIMITS = Object.freeze({
9
+ URL: 120,
10
+ NAME: 120,
11
+ BODY_PREVIEW: 2_000,
12
+ FULL_BODY: 64 * 1024,
13
+ RUN_BODY_DEFAULT: 16 * 1024,
14
+ RUN_BODY_MAX: 256 * 1024,
15
+ SCENARIO_FULL: 256 * 1024,
16
+ DIAGRAM_FULL: 512 * 1024,
17
+ SEARCH_RESULTS: 50,
18
+ TREE_ITEMS: 5_000,
19
+ LIST_DEFAULT: 50,
20
+ LIST_MAX: 100,
21
+ });
22
+ const clip = (value, max) => value.length > max ? `${value.slice(0, max)}…` : value;
23
+ exports.clip = clip;
24
+ // Byte-bounded clip for bodies (UTF-8 aware: cut on a character boundary).
25
+ const clipBytes = (value, maxBytes) => {
26
+ const totalBytes = Buffer.byteLength(value, "utf8");
27
+ if (totalBytes <= maxBytes)
28
+ return { text: value, truncated: false, totalBytes };
29
+ let end = Math.min(value.length, maxBytes);
30
+ while (end > 0 && Buffer.byteLength(value.slice(0, end), "utf8") > maxBytes)
31
+ end -= Math.ceil((end * 0.05) || 1);
32
+ return { text: `${value.slice(0, end)}…`, truncated: true, totalBytes };
33
+ };
34
+ exports.clipBytes = clipBytes;
35
+ const jsonPreview = (value, max = exports.LIMITS.BODY_PREVIEW) => {
36
+ if (value === null || value === undefined)
37
+ return null;
38
+ const text = typeof value === "string" ? value : (0, exports.safeStringify)(value);
39
+ return (0, exports.clip)(text, max);
40
+ };
41
+ exports.jsonPreview = jsonPreview;
42
+ const safeStringify = (value, space) => {
43
+ try {
44
+ return JSON.stringify(value, null, space) ?? "null";
45
+ }
46
+ catch {
47
+ return String(value);
48
+ }
49
+ };
50
+ exports.safeStringify = safeStringify;
51
+ const byteLength = (value) => Buffer.byteLength(typeof value === "string" ? value : (0, exports.safeStringify)(value), "utf8");
52
+ exports.byteLength = byteLength;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.silentLogger = exports.createLogger = exports.CallmanApi = exports.CallmanHttpClient = exports.hintFor = exports.ToolInputError = exports.CallmanNetworkError = exports.CallmanApiError = exports.toolAvailability = exports.TOOL_CATALOG = exports.createToolContext = exports.resolveConfig = exports.normalizeApiUrl = exports.DEFAULT_CLOUD_API_URL = exports.SERVER_NAME = exports.SERVER_INSTRUCTIONS = exports.createCallmanMcpServer = void 0;
4
+ // Library entry (`import { createCallmanMcpServer } from "callman-mcp"`) —
5
+ // for hosts that embed the tool layer (the Callman backend's remote MCP
6
+ // endpoint) and for tests. The bin lives in index.ts.
7
+ var server_js_1 = require("./server.js");
8
+ Object.defineProperty(exports, "createCallmanMcpServer", { enumerable: true, get: function () { return server_js_1.createCallmanMcpServer; } });
9
+ Object.defineProperty(exports, "SERVER_INSTRUCTIONS", { enumerable: true, get: function () { return server_js_1.SERVER_INSTRUCTIONS; } });
10
+ Object.defineProperty(exports, "SERVER_NAME", { enumerable: true, get: function () { return server_js_1.SERVER_NAME; } });
11
+ var config_js_1 = require("./config.js");
12
+ Object.defineProperty(exports, "DEFAULT_CLOUD_API_URL", { enumerable: true, get: function () { return config_js_1.DEFAULT_CLOUD_API_URL; } });
13
+ Object.defineProperty(exports, "normalizeApiUrl", { enumerable: true, get: function () { return config_js_1.normalizeApiUrl; } });
14
+ Object.defineProperty(exports, "resolveConfig", { enumerable: true, get: function () { return config_js_1.resolveConfig; } });
15
+ var context_js_1 = require("./context.js");
16
+ Object.defineProperty(exports, "createToolContext", { enumerable: true, get: function () { return context_js_1.createToolContext; } });
17
+ var catalog_js_1 = require("./tools/catalog.js");
18
+ Object.defineProperty(exports, "TOOL_CATALOG", { enumerable: true, get: function () { return catalog_js_1.TOOL_CATALOG; } });
19
+ Object.defineProperty(exports, "toolAvailability", { enumerable: true, get: function () { return catalog_js_1.toolAvailability; } });
20
+ var errors_js_1 = require("./client/errors.js");
21
+ Object.defineProperty(exports, "CallmanApiError", { enumerable: true, get: function () { return errors_js_1.CallmanApiError; } });
22
+ Object.defineProperty(exports, "CallmanNetworkError", { enumerable: true, get: function () { return errors_js_1.CallmanNetworkError; } });
23
+ Object.defineProperty(exports, "ToolInputError", { enumerable: true, get: function () { return errors_js_1.ToolInputError; } });
24
+ Object.defineProperty(exports, "hintFor", { enumerable: true, get: function () { return errors_js_1.hintFor; } });
25
+ var http_js_1 = require("./client/http.js");
26
+ Object.defineProperty(exports, "CallmanHttpClient", { enumerable: true, get: function () { return http_js_1.CallmanHttpClient; } });
27
+ var api_js_1 = require("./client/api.js");
28
+ Object.defineProperty(exports, "CallmanApi", { enumerable: true, get: function () { return api_js_1.CallmanApi; } });
29
+ var log_js_1 = require("./lib/log.js");
30
+ Object.defineProperty(exports, "createLogger", { enumerable: true, get: function () { return log_js_1.createLogger; } });
31
+ Object.defineProperty(exports, "silentLogger", { enumerable: true, get: function () { return log_js_1.silentLogger; } });
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPrompts = void 0;
4
+ const zod_1 = require("zod");
5
+ // Prompts: reusable playbooks a user picks in their client ("/callman:…").
6
+ // Each one tells the model which resources to read and which tools to call,
7
+ // in which order — the workflows that need discipline to come out right.
8
+ const user = (text) => ({ messages: [{ role: "user", content: { type: "text", text } }] });
9
+ const where = (workspace) => (workspace ? ` in the Callman workspace "${workspace}"` : "");
10
+ const registerPrompts = (server, _ctx) => {
11
+ server.registerPrompt("import_endpoints_from_code", {
12
+ title: "Import the endpoints of this codebase into Callman",
13
+ description: "Find the HTTP routes in the project's source, build one request per endpoint (method, path, query, headers, body from the DTO) and create them in a Callman collection with folders per resource.",
14
+ argsSchema: {
15
+ path: zod_1.z.string().optional().describe("Directory or file to scan (default: the whole project)"),
16
+ collection: zod_1.z.string().optional().describe("Target collection name (existing or new); default: named after the project"),
17
+ workspace: zod_1.z.string().optional(),
18
+ },
19
+ }, ({ path, collection, workspace }) => user(`Import the HTTP endpoints defined in ${path ? `\`${path}\`` : "this project"} into Callman${where(workspace)}.
20
+
21
+ Steps:
22
+ 1. Call \`whoami\` to confirm the connection${workspace ? "" : " and the workspace"}.
23
+ 2. Read the routing code yourself (framework route definitions, controllers, OpenAPI files if present). For every endpoint collect: method, path (turn \`:id\` / \`{id}\` params into Callman path variables \`:id\`), query parameters, required headers, and a realistic JSON body from the request DTO/validation schema. Group endpoints by resource (users, orders …) — one folder per resource.
24
+ 3. Use \`{{baseUrl}}\` for the host. Note which environment variables the collection needs (baseUrl, accessToken …).
25
+ 4. Call \`list_collections\`. If a collection named "${collection ?? "<project name>"}" exists, add to it with \`create_requests_bulk\` (\`collection:\`); otherwise create it with \`create_requests_bulk\` (\`collectionName:\` + \`folders\` skeleton + \`folderKey\` per request). For more than 50 requests run with \`dryRun: true\` first and show me the summary.
26
+ 5. If the API has a login/token endpoint, give that request a \`postResponseScript\` that stores the token: \`pm.environment.set("accessToken", pm.response.json().token)\`, and use \`{ "type": "bearer", "token": "{{accessToken}}" }\` on the others.
27
+ 6. Offer to create an environment (\`create_environment\`) with the variables you found, with placeholder values I must fill in.
28
+ 7. Finish with a short summary: collection, folders, request count, variables to set.`));
29
+ server.registerPrompt("design_system_diagram", {
30
+ title: "Draw a system-design diagram of this codebase",
31
+ description: "Identify services, data stores, queues, external APIs and infrastructure in the project and draw an architecture diagram in Callman's Diagram Studio.",
32
+ argsSchema: {
33
+ scope: zod_1.z.string().optional().describe("What to diagram (default: the whole system); e.g. 'the order pipeline'"),
34
+ name: zod_1.z.string().optional().describe("Diagram name"),
35
+ },
36
+ }, ({ scope, name }) => user(`Draw a system-design diagram of ${scope ? scope : "this system"} in Callman.
37
+
38
+ Steps:
39
+ 1. Read \`callman://diagram/components\` — use ONLY those \`type\` values.
40
+ 2. From the code and configuration (docker-compose, Kubernetes manifests, terraform, env files, package manifests) identify: services/apps, databases and caches, message brokers/queues, external APIs, auth providers, observability, cloud resources, and the boundaries they live in (VPC, cluster, namespace).
41
+ 3. Model 8–25 nodes with 1–3 word labels. Put components inside boundaries with \`parent\` (declare the container node). Give each edge a meaning: \`sync\` for request/response calls, \`async\` for events/callbacks, \`bus\` for streams/queues, \`fallback\` for retries or optional paths. Short edge labels (protocol or event name) only where they add information.
42
+ 4. Call \`create_diagram\` with \`name: "${name ?? "<system name>"}"\`, no positions (automatic layout). If it reports unknown types, fix them with the suggestions and retry.
43
+ 5. Describe the diagram in a few sentences and tell me it is in the Callman Diagrams library (Refresh to see it). For corrections use \`patch_diagram\`, not a full rebuild.`));
44
+ server.registerPrompt("create_scenario_from_flow", {
45
+ title: "Create a test scenario for a user flow",
46
+ description: "Turn a described flow (e.g. 'sign up, log in, create an order, check it appears') into a runnable Callman scenario using the saved requests.",
47
+ argsSchema: {
48
+ flow: zod_1.z.string().describe("The flow to test, in plain words"),
49
+ collection: zod_1.z.string().optional().describe("Collection whose requests to use"),
50
+ workspace: zod_1.z.string().optional(),
51
+ },
52
+ }, ({ flow, collection, workspace }) => user(`Create a Callman scenario that tests this flow${where(workspace)}: ${flow}
53
+
54
+ Steps:
55
+ 1. Read \`callman://docs/scenario-nodes\` for the node types and their config fields.
56
+ 2. Find the requests to reuse with \`search_requests\`${collection ? ` in the collection "${collection}"` : ""} and read the ones you need with \`get_request\` (method, url, body, auth). Request nodes carry their own config — copy method/url/headers/body from the saved request and use \`{{variables}}\` for values produced by earlier steps.
57
+ 3. Build the graph: one \`request\` node per call; an \`assertion\` node after each important response (status code, key fields); \`set-vars\` to capture ids/tokens from responses for later steps; a \`condition\` with \`yes\`/\`no\` edges where the flow branches; \`poll\` for eventually-consistent checks; an \`end\` node last. Add a scenario environment with the variables the flow needs.
58
+ 4. Call \`create_scenario\` with \`dryRun: true\`, review the resolved graph, then create it for real.
59
+ 5. Offer to run it with \`run_scenario\` and to explain the report. On-prem: mention that the scenario is a draft until approved in the desktop app.`));
60
+ server.registerPrompt("document_collection", {
61
+ title: "Document an API collection",
62
+ description: "Read a Callman collection and write API documentation (Markdown or an OpenAPI skeleton) from its requests.",
63
+ argsSchema: {
64
+ collection: zod_1.z.string().describe("Collection id or name"),
65
+ format: zod_1.z.string().optional().describe("markdown (default) or openapi"),
66
+ workspace: zod_1.z.string().optional(),
67
+ },
68
+ }, ({ collection, format, workspace }) => user(`Document the Callman collection "${collection}"${where(workspace)} as ${format === "openapi" ? "an OpenAPI 3.1 skeleton" : "Markdown"}.
69
+
70
+ Steps:
71
+ 1. \`get_collection_tree\` for the folders and requests, then \`get_request\` (\`full: true\`) for each request you document. Secrets stay masked — never try to reveal them.
72
+ 2. Group by folder. For each request: purpose (infer from the name/URL), method + path, path/query parameters, headers that matter, a request body example, what the post-response script asserts (as the expected response), and the variables it depends on.
73
+ 3. Write the document to a file in the project (e.g. \`docs/api/${collection.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}.${format === "openapi" ? "yaml" : "md"}\`) and summarise what it covers and what was unclear.`));
74
+ };
75
+ exports.registerPrompts = registerPrompts;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DIAGRAM_SCHEMA_DOC = void 0;
4
+ exports.DIAGRAM_SCHEMA_DOC = `# Diagrams (create_diagram, update_diagram, patch_diagram)
5
+
6
+ A diagram is a **DiagramDocument** (schemaVersion 1): typed nodes with positions and sizes,
7
+ edges between nodes, a title. You write the compact form below; the server assigns ids,
8
+ sizes from the component catalog, edge styles from the semantic, and lays the nodes out.
9
+
10
+ ## Compact input
11
+ \`\`\`jsonc
12
+ {
13
+ "name": "Order platform", // library entry name
14
+ "title": "Order platform — runtime view",
15
+ "nodes": [
16
+ { "id": "gw", "type": "net.gateway", "label": "API Gateway" },
17
+ { "id": "orders","type": "net.service", "label": "Orders service", "parent": "k8s" },
18
+ { "id": "k8s", "type": "net.zone", "label": "Kubernetes" }, // a container
19
+ { "id": "db", "type": "db.postgres", "label": "orders db" },
20
+ { "id": "bus", "type": "mq.kafka", "label": "events" }
21
+ ],
22
+ "edges": [
23
+ { "source": "gw", "target": "orders", "label": "REST", "semantic": "sync" },
24
+ { "source": "orders", "target": "db", "semantic": "sync" },
25
+ { "source": "orders", "target": "bus", "label": "OrderCreated" } // → bus automatically
26
+ ]
27
+ }
28
+ \`\`\`
29
+
30
+ - \`type\` MUST be a catalog type — read \`callman://diagram/components\` (or one category:
31
+ \`callman://diagram/components/aws\`). Unknown types are rejected with suggestions; pass
32
+ \`strictTypes: false\` to draw them as plain rectangles instead.
33
+ - \`parent\` puts a node inside a container node (zones, frames, pools). Declare the
34
+ container as a node too.
35
+ - Edge \`semantic\`: \`sync\` (solid — request/response, default), \`async\` (dashed — event,
36
+ callback), \`fallback\` (dotted — retry / optional path), \`bus\` (heavy — message bus /
37
+ stream; automatic when an endpoint is a messaging component).
38
+ - Positions are optional; when any node lacks one the whole diagram is laid out
39
+ (\`direction\`: DOWN, RIGHT, UP, LEFT). Give explicit \`position: {x,y}\` only when the user
40
+ asks for a specific arrangement.
41
+ - Keep diagrams readable: 8–25 nodes, labels of 1–3 words, one edge per relationship.
42
+
43
+ ## Editing: patch_diagram ops
44
+ \`\`\`jsonc
45
+ { "op": "addNode", "node": { "type": "db.redis", "label": "cache" } }
46
+ { "op": "updateNode", "id": "orders", "changes": { "label": "Orders API" } }
47
+ { "op": "removeNode", "id": "bus" } // its edges are removed too
48
+ { "op": "addEdge", "edge": { "source": "orders", "target": "n6", "semantic": "async" } }
49
+ { "op": "updateEdge", "id": "e2", "changes": { "label": "gRPC" } }
50
+ { "op": "removeEdge", "id": "e3" }
51
+ { "op": "setTitle", "title": "New title" }
52
+ \`\`\`
53
+ \`relayout\`: \`new\` (default — only new nodes get a place), \`all\` (re-run layout), \`none\`.
54
+
55
+ ## Stored document (get_diagram view: "full")
56
+ \`\`\`ts
57
+ interface DiagramDocument {
58
+ schemaVersion: 1; id: string;
59
+ meta: { title: string; createdAt: string; updatedAt: string };
60
+ canvas?: { background?: "grid" | "dots" | "blank"; gridSize?: number };
61
+ nodes: { id; type; position: {x,y}; size: {width,height}; label?; parentId?; style?; data? }[];
62
+ edges: { id; source?; target?; label?; routing?; style?; markerEnd?; waypoints? }[];
63
+ }
64
+ \`\`\`
65
+ Edge meaning is derived from \`style\` (dashed + dashPattern + strokeWidth) — you never set
66
+ style directly; use \`semantic\`.
67
+ `;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OVERVIEW_DOC = void 0;
4
+ exports.OVERVIEW_DOC = `# Working with Callman through MCP
5
+
6
+ Callman is an API client and test platform (collections of HTTP requests, environments,
7
+ multi-step test scenarios, system-design diagrams). This server exposes it to you through
8
+ tools; everything you do goes to the same server the Callman desktop app uses, as the user
9
+ whose token is configured, limited to that token's permissions and the user's workspace role.
10
+
11
+ ## Start here
12
+ 1. \`whoami\` — proves the connection and tells you the workspaces, features and which tools
13
+ this token can use. If several workspaces exist and none is default, pass \`workspace\`
14
+ to workspace-scoped tools (or ask the user which one).
15
+ 2. Look before you create: \`list_collections\` / \`get_collection_tree\` / \`search_requests\`.
16
+ Creating a collection whose name exists is refused on purpose.
17
+ 3. Names work as references everywhere an id does (collection, folder, request, environment,
18
+ scenario, diagram, workspace). Ambiguous names come back as a candidate list — pick one.
19
+ 4. Preview big writes with \`dryRun: true\` (bulk create, imports, scenarios, diagrams).
20
+ 5. Deletions need \`confirm: true\`. Ask the user unless they explicitly requested the deletion.
21
+
22
+ ## Requests
23
+ - Use \`{{variable}}\` for hosts and secrets (\`{{baseUrl}}/users\`) and put the values in an
24
+ environment (\`create_environment\` / \`set_environment_variables\`). Use \`:id\` for path
25
+ variables. See \`callman://docs/request-schema\`.
26
+ - Bodies: a JSON value, a raw string, or \`{ mode: raw|formdata|urlencoded }\`.
27
+ - Scripts run in a sandbox with the \`pm.*\` API — \`callman://docs/scripting/pm-api\`.
28
+ - \`run_request\` / \`run_collection\` execute on the user's machine with real variable values;
29
+ you only get results and the names of variables scripts changed.
30
+
31
+ ## Secrets
32
+ Environment values come back masked (\`••••ab\`) unless the token holds \`environments:reveal\`
33
+ and you ask for \`reveal: true\`. Never write a masked value back — the server refuses it.
34
+
35
+ ## Scenarios
36
+ Multi-step flows as a graph of typed nodes (request, condition, assertion, set-vars, poll,
37
+ db, kafka, script, wait, end …). Read \`callman://docs/scenario-nodes\` before building one;
38
+ partial node configs are merged over defaults. Runs happen on the server (\`run_scenario\`).
39
+
40
+ ## Diagrams
41
+ System-design diagrams use a fixed component catalog — \`callman://diagram/components\`.
42
+ Build with \`create_diagram\` (auto layout), edit with \`patch_diagram\`, share with
43
+ \`share_diagram\`. See \`callman://docs/diagram-schema\`.
44
+
45
+ ## After writing
46
+ The desktop app refreshes when the user switches back to it. Tell the user what you created
47
+ and where (collection / folder names).
48
+ `;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REQUEST_SCHEMA_DOC = void 0;
4
+ exports.REQUEST_SCHEMA_DOC = `# Request input (create_request, create_requests_bulk, update_request)
5
+
6
+ \`\`\`jsonc
7
+ {
8
+ "name": "Create order", // 1–120 chars, shown in the sidebar
9
+ "method": "POST", // GET POST PUT PATCH DELETE HEAD OPTIONS TRACE
10
+ "url": "{{baseUrl}}/orders/:orderId", // {{var}} = environment/global variable, :name = path variable
11
+ "headers": [{ "key": "Content-Type", "value": "application/json" }],
12
+ "query": [{ "key": "expand", "value": "items", "enabled": true }],
13
+ "pathVariables": [{ "key": "orderId", "value": "123" }],
14
+ "body": { "sku": "A1", "qty": 2 }, // see Body
15
+ "auth": { "type": "bearer", "token": "{{accessToken}}" }, // see Auth
16
+ "preRequestScript": "", // pm.* script before sending
17
+ "postResponseScript": "pm.test('created', () => pm.response.to.have.status(201));\\npm.environment.set('orderId', pm.response.json().id);",
18
+ "testsScript": "", // legacy tests script (same API)
19
+ "folder": "Orders/Write", // folder id, name or path (create_request / existing collection)
20
+ "folderKey": "orders" // create_requests_bulk with collectionName: key from \`folders\`
21
+ }
22
+ \`\`\`
23
+
24
+ ## Body
25
+ | You send | Stored as |
26
+ |---|---|
27
+ | a JSON object / array / number / boolean | that JSON value (JSON tab in the editor) |
28
+ | a string | parsed as JSON when valid; otherwise raw text/xml/html by sniffing |
29
+ | \`{ "mode": "raw", "raw": "<xml/>", "language": "xml" }\` | raw body with that language (json, xml, text, html) |
30
+ | \`{ "mode": "formdata", "entries": [{ "key": "file", "value": "a.txt" }] }\` | multipart form |
31
+ | \`{ "mode": "urlencoded", "entries": [ … ] }\` | x-www-form-urlencoded |
32
+ | \`{ "mode": "none" }\` or omitted | no body |
33
+
34
+ Set \`Content-Type\` yourself for raw non-JSON bodies; JSON gets it automatically at send time.
35
+
36
+ ## Auth
37
+ \`\`\`jsonc
38
+ { "type": "none" }
39
+ { "type": "bearer", "token": "{{accessToken}}" }
40
+ { "type": "basic", "username": "{{user}}", "password": "{{pass}}" }
41
+ { "type": "apiKey", "key": "X-API-Key", "value": "{{apiKey}}", "location": "header" } // or "query"
42
+ { "type": "oauth2", "grantType": "client_credentials", "tokenUrl": "{{authUrl}}/token",
43
+ "clientId": "{{clientId}}", "clientSecret": "{{clientSecret}}", "scope": "read write" }
44
+ \`\`\`
45
+ Prefer \`{{variables}}\` over literal secrets — the environment keeps them out of the request.
46
+
47
+ ## Variables
48
+ - \`{{name}}\` resolves from the active environment, then global variables, then values set
49
+ by scripts during a run (\`pm.environment.set\`).
50
+ - Dynamic values: \`{{$uuid}}\`, \`{{$timestamp}}\`, \`{{$randomInt}}\`, \`{{$randomEmail}}\` … —
51
+ see \`callman://docs/scripting/templating\`.
52
+ - Data-driven rows: \`{{data.column}}\`.
53
+
54
+ ## Scripts (pm.* API)
55
+ \`pm.test(name, fn)\`, \`pm.expect(value)\`, \`pm.response.json()\` / \`.code\` / \`.headers\`,
56
+ \`pm.environment.set/get\`, \`pm.globals\`, \`pm.variables\`, \`pm.request\`, \`pm.sendRequest\`,
57
+ \`pm.db.query\` (when a DB connection is attached). Full reference:
58
+ \`callman://docs/scripting/pm-api\`; sandbox limits: \`callman://docs/scripting/sandbox\`.
59
+
60
+ ## Folder skeleton (create_requests_bulk with collectionName)
61
+ \`\`\`jsonc
62
+ {
63
+ "collectionName": "Users API",
64
+ "folders": [
65
+ { "key": "users", "name": "Users" },
66
+ { "key": "users-admin", "name": "Admin", "parentKey": "users" }
67
+ ],
68
+ "requests": [ { "name": "List users", "method": "GET", "url": "{{baseUrl}}/users", "folderKey": "users" } ]
69
+ }
70
+ \`\`\`
71
+ The whole payload is created atomically (all or nothing).
72
+ `;