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,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CallmanApi = void 0;
4
+ // Typed endpoint functions — one per backend route the tools use. Thin on
5
+ // purpose: no business rules here, only paths, headers and shapes. `ws` is
6
+ // the workspace id for workspace-scoped routes.
7
+ class CallmanApi {
8
+ http;
9
+ constructor(http) {
10
+ this.http = http;
11
+ }
12
+ // ── identity ────────────────────────────────────────────────────────
13
+ me() {
14
+ return this.http.request("GET", "/personal-tokens/me");
15
+ }
16
+ features() {
17
+ return this.http.request("GET", "/features");
18
+ }
19
+ // ── workspaces ──────────────────────────────────────────────────────
20
+ listWorkspaces() {
21
+ return this.http.request("GET", "/workspaces");
22
+ }
23
+ listMembers(workspaceId) {
24
+ return this.http.request("GET", `/workspaces/${workspaceId}/members`);
25
+ }
26
+ addMember(workspaceId, body) {
27
+ return this.http.request("POST", `/workspaces/${workspaceId}/members`, { body });
28
+ }
29
+ removeMember(workspaceId, memberId) {
30
+ return this.http.request("DELETE", `/workspaces/${workspaceId}/members/${memberId}`);
31
+ }
32
+ // ── collections / folders ───────────────────────────────────────────
33
+ listCollections(ws, page = 1, limit = 100) {
34
+ return this.http.request("GET", "/collections", { workspaceId: ws, query: { page, limit } });
35
+ }
36
+ async listAllCollections(ws) {
37
+ const out = [];
38
+ let page = 1;
39
+ for (;;) {
40
+ const result = await this.listCollections(ws, page, 100);
41
+ out.push(...result.items);
42
+ if (page >= result.pagination.totalPages || result.items.length === 0)
43
+ break;
44
+ page += 1;
45
+ if (page > 50)
46
+ break; // 5 000 collections — more than any workspace has
47
+ }
48
+ return out;
49
+ }
50
+ getCollection(ws, id) {
51
+ return this.http.request("GET", `/collections/${id}`, { workspaceId: ws });
52
+ }
53
+ createCollection(ws, body) {
54
+ return this.http.request("POST", "/collections", { workspaceId: ws, body });
55
+ }
56
+ updateCollection(ws, id, body) {
57
+ return this.http.request("PATCH", `/collections/${id}`, { workspaceId: ws, body });
58
+ }
59
+ deleteCollection(ws, id) {
60
+ return this.http.request("DELETE", `/collections/${id}`, { workspaceId: ws });
61
+ }
62
+ folderTree(ws, collectionId) {
63
+ return this.http.request("GET", `/folders/tree/${collectionId}`, { workspaceId: ws });
64
+ }
65
+ createFolder(ws, body) {
66
+ return this.http.request("POST", "/folders", { workspaceId: ws, body });
67
+ }
68
+ updateFolder(ws, id, body) {
69
+ return this.http.request("PATCH", `/folders/${id}`, { workspaceId: ws, body });
70
+ }
71
+ deleteFolder(ws, id) {
72
+ return this.http.request("DELETE", `/folders/${id}`, { workspaceId: ws });
73
+ }
74
+ exportCollection(ws, id, type) {
75
+ return this.http.request("GET", `/export/${id}`, { workspaceId: ws, query: { type } });
76
+ }
77
+ // ── requests ────────────────────────────────────────────────────────
78
+ requestTree(ws, collectionId) {
79
+ return this.http.request("GET", "/requests/tree", { workspaceId: ws, query: { collectionId } });
80
+ }
81
+ getRequest(ws, id) {
82
+ return this.http.request("GET", `/requests/${id}`, { workspaceId: ws });
83
+ }
84
+ createRequest(ws, body) {
85
+ return this.http.request("POST", "/requests", { workspaceId: ws, body });
86
+ }
87
+ updateRequest(ws, id, body) {
88
+ return this.http.request("PATCH", `/requests/${id}`, { workspaceId: ws, body });
89
+ }
90
+ deleteRequest(ws, id) {
91
+ return this.http.request("DELETE", `/requests/${id}`, { workspaceId: ws });
92
+ }
93
+ // ── import ──────────────────────────────────────────────────────────
94
+ importJson(ws, body) {
95
+ return this.http.request("POST", "/import/json", { workspaceId: ws, body, timeoutMs: 120_000 });
96
+ }
97
+ importScenario(ws, body) {
98
+ return this.http.request("POST", "/import/scenario", { workspaceId: ws, body, timeoutMs: 120_000 });
99
+ }
100
+ // ── environments / globals ──────────────────────────────────────────
101
+ listEnvironments(ws, reveal = false) {
102
+ return this.http.request("GET", "/environments", {
103
+ workspaceId: ws,
104
+ query: { page: 1, limit: 100, ...(reveal ? { reveal: "true" } : {}) },
105
+ });
106
+ }
107
+ getEnvironment(ws, id, reveal = false) {
108
+ return this.http.request("GET", `/environments/${id}`, { workspaceId: ws, query: reveal ? { reveal: "true" } : {} });
109
+ }
110
+ createEnvironment(ws, body) {
111
+ return this.http.request("POST", "/environments", { workspaceId: ws, body });
112
+ }
113
+ deleteEnvironment(ws, id) {
114
+ return this.http.request("DELETE", `/environments/${id}`, { workspaceId: ws });
115
+ }
116
+ addEnvironmentVariable(ws, id, body) {
117
+ return this.http.request("POST", `/environments/${id}/variables`, { workspaceId: ws, body });
118
+ }
119
+ updateEnvironmentVariable(ws, id, key, body) {
120
+ return this.http.request("PATCH", `/environments/${id}/variables/${encodeURIComponent(key)}`, { workspaceId: ws, body });
121
+ }
122
+ deleteEnvironmentVariable(ws, id, key) {
123
+ return this.http.request("DELETE", `/environments/${id}/variables/${encodeURIComponent(key)}`, { workspaceId: ws });
124
+ }
125
+ listGlobalVariables(ws, reveal = false) {
126
+ return this.http.request("GET", "/variables/global", { workspaceId: ws, query: reveal ? { reveal: "true" } : {} });
127
+ }
128
+ // ── scenarios ───────────────────────────────────────────────────────
129
+ listScenarios(ws) {
130
+ return this.http.request("GET", "/scenarios", { workspaceId: ws });
131
+ }
132
+ listScenarioFolders(ws) {
133
+ return this.http.request("GET", "/scenario-folders", { workspaceId: ws });
134
+ }
135
+ getScenario(ws, id, reveal = false) {
136
+ return this.http.request("GET", `/scenarios/${id}`, { workspaceId: ws, query: reveal ? { reveal: "true" } : {} });
137
+ }
138
+ createScenario(ws, body) {
139
+ return this.http.request("POST", "/scenarios", { workspaceId: ws, body });
140
+ }
141
+ patchScenario(ws, id, body) {
142
+ return this.http.request("PATCH", `/scenarios/${id}`, { workspaceId: ws, body });
143
+ }
144
+ deleteScenario(ws, id) {
145
+ return this.http.request("DELETE", `/scenarios/${id}`, { workspaceId: ws });
146
+ }
147
+ runScenario(ws, id, body) {
148
+ return this.http.request("POST", `/scenarios/${id}/run-server`, { workspaceId: ws, body });
149
+ }
150
+ listScenarioReports(ws, id, limit = 20) {
151
+ return this.http.request("GET", `/scenarios/${id}/reports`, { workspaceId: ws, query: { page: 1, limit } });
152
+ }
153
+ getScenarioReport(ws, id, reportId) {
154
+ return this.http.request("GET", `/scenarios/${id}/reports/${reportId}`, { workspaceId: ws });
155
+ }
156
+ // ── diagrams (user-owned, not workspace-scoped) ─────────────────────
157
+ listDiagrams() {
158
+ return this.http.request("GET", "/diagrams");
159
+ }
160
+ getDiagram(id) {
161
+ return this.http.request("GET", `/diagrams/${id}`);
162
+ }
163
+ createDiagram(body) {
164
+ return this.http.request("POST", "/diagrams", { body });
165
+ }
166
+ updateDiagram(id, body) {
167
+ return this.http.request("PUT", `/diagrams/${id}`, { body });
168
+ }
169
+ deleteDiagram(id) {
170
+ return this.http.request("DELETE", `/diagrams/${id}`);
171
+ }
172
+ shareDiagram(id, body) {
173
+ return this.http.request("POST", `/diagrams/${id}/share`, { body });
174
+ }
175
+ // ── local run bundle ────────────────────────────────────────────────
176
+ pipelineBundle(ws, collectionId, environmentId) {
177
+ return this.http.request("GET", "/pipeline/bundle", {
178
+ workspaceId: ws,
179
+ query: { collectionId, ...(environmentId ? { environmentId } : {}) },
180
+ });
181
+ }
182
+ }
183
+ exports.CallmanApi = CallmanApi;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ // Errors from the Callman API, carrying the backend's error code so tools can
3
+ // turn them into actionable text for the model (see `hintFor`).
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.hintFor = exports.ToolInputError = exports.CallmanNetworkError = exports.CallmanApiError = void 0;
6
+ class CallmanApiError extends Error {
7
+ status;
8
+ code;
9
+ details;
10
+ method;
11
+ path;
12
+ constructor(input) {
13
+ super(input.message);
14
+ this.name = "CallmanApiError";
15
+ this.status = input.status;
16
+ this.code = input.code;
17
+ this.details = input.details;
18
+ this.method = input.method;
19
+ this.path = input.path;
20
+ }
21
+ }
22
+ exports.CallmanApiError = CallmanApiError;
23
+ class CallmanNetworkError extends Error {
24
+ apiUrl;
25
+ cause;
26
+ constructor(apiUrl, message, cause) {
27
+ super(message);
28
+ this.name = "CallmanNetworkError";
29
+ this.apiUrl = apiUrl;
30
+ this.cause = cause;
31
+ }
32
+ }
33
+ exports.CallmanNetworkError = CallmanNetworkError;
34
+ // Raised by tools before any API call: bad references, missing scopes known
35
+ // up front, disabled features, ambiguous names. `data` is surfaced verbatim
36
+ // in structuredContent so the model can act (e.g. pick a candidate).
37
+ class ToolInputError extends Error {
38
+ code;
39
+ data;
40
+ constructor(code, message, data) {
41
+ super(message);
42
+ this.name = "ToolInputError";
43
+ this.code = code;
44
+ this.data = data;
45
+ }
46
+ }
47
+ exports.ToolInputError = ToolInputError;
48
+ const SCOPE_HINT = "Create a token with that permission in the Callman desktop app (Settings → AI tools → New token) and update the CALLMAN_TOKEN in your MCP config.";
49
+ const hintFor = (error) => {
50
+ switch (error.code) {
51
+ case "PAT_SCOPE_MISSING": {
52
+ const required = Array.isArray(error.details?.required)
53
+ ? (error.details.required ?? []).join(" or ")
54
+ : "an additional scope";
55
+ return `The token lacks the permission ${required}. ${SCOPE_HINT}`;
56
+ }
57
+ case "PAT_ROUTE_FORBIDDEN":
58
+ return "This area of Callman is not available to AI tools yet (mocks, connections, history, UI tests, admin). Use the desktop app for it.";
59
+ case "FEATURE_DISABLED":
60
+ return "AI tools are switched off for this account by an administrator (feature flag mcp). Ask them to enable it in the admin panel.";
61
+ case "SECRET_MASKED_VALUE_REJECTED":
62
+ return "A masked value (••••) was sent back as if it were real. Read the value with get_environment reveal:true (needs environments:reveal) or set a new real value.";
63
+ case "SCENARIO_VERSION_CONFLICT":
64
+ return "Someone changed the scenario since it was read. Re-read it with get_scenario, reapply your change on the current version, then call update_scenario with the new expectedVersion (or force:true to overwrite).";
65
+ case "DIAGRAM_DOCUMENT_INVALID":
66
+ return "The diagram document was rejected by the server's validator; fix the listed errors (unknown component types are listed under unknownTypes — read callman://diagram/components).";
67
+ case "PERSONAL_TOKEN_LIMIT":
68
+ return "You have reached the maximum number of active personal tokens. Revoke an old one in Settings → AI tools.";
69
+ case "WORKSPACE_MEMBER_USER_NOT_FOUND":
70
+ return "No Callman account exists for that email, and the server does not send invitations. Ask the person to sign up (cloud) or sign in once (on-prem, LDAP), then add them again.";
71
+ case "WORKSPACE_MEMBER_EXISTS":
72
+ return "That person is already a member of the workspace. Use list_workspace_members to see their role.";
73
+ case "WORKSPACE_MEMBER_REMOVE_FORBIDDEN":
74
+ return "That member cannot be removed (the last owner of a workspace must stay).";
75
+ case "VALIDATION_ERROR":
76
+ return "The request body did not pass validation — check the listed fields against callman://docs/request-schema.";
77
+ case "WORKSPACE_ACCESS_DENIED":
78
+ case "WORKSPACE_NOT_FOUND":
79
+ case "WORKSPACE_ID_REQUIRED":
80
+ case "WORKSPACE_REQUIRED":
81
+ return "Pass `workspace` (name or id) to the tool, or set CALLMAN_WORKSPACE_ID in the MCP config. Call list_workspaces to see the options.";
82
+ default:
83
+ break;
84
+ }
85
+ switch (error.status) {
86
+ case 401:
87
+ return "The token was rejected (missing, expired or revoked). Create a new token in the Callman desktop app (Settings → AI tools) and set CALLMAN_TOKEN in your MCP config.";
88
+ case 403:
89
+ return "Your workspace role does not allow this (viewers cannot write). Ask a workspace owner for the editor role, or pick another workspace.";
90
+ case 404:
91
+ return "Nothing matched that id. Use the list_* / search_* tools to find the right item — names work as references too.";
92
+ case 409:
93
+ return "That name already exists. Pick another name or reuse the existing item (list it first).";
94
+ case 413:
95
+ return "The payload is too large for the server. Split the import or reduce the document.";
96
+ case 429:
97
+ return "Rate limit reached for this token. Wait for the Retry-After period and try again with fewer calls.";
98
+ default:
99
+ return error.status >= 500
100
+ ? "The Callman server returned an error. Try again; if it persists, check the server logs."
101
+ : "See the error details.";
102
+ }
103
+ };
104
+ exports.hintFor = hintFor;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CallmanHttpClient = void 0;
4
+ const errors_js_1 = require("./errors.js");
5
+ const log_js_1 = require("../lib/log.js");
6
+ const isEnvelope = (value) => typeof value === "object" && value !== null && "success" in value;
7
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
+ class CallmanHttpClient {
9
+ apiUrl;
10
+ token;
11
+ fetchImpl;
12
+ timeoutMs;
13
+ userAgent;
14
+ logger;
15
+ constructor(options) {
16
+ this.apiUrl = options.apiUrl.replace(/\/+$/, "");
17
+ this.token = options.token;
18
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
19
+ this.timeoutMs = options.timeoutMs ?? 30_000;
20
+ this.userAgent = options.userAgent ?? "callman-mcp";
21
+ this.logger = options.logger ?? log_js_1.silentLogger;
22
+ }
23
+ get hasToken() {
24
+ return Boolean(this.token);
25
+ }
26
+ buildUrl(path, query) {
27
+ const url = new URL(`${this.apiUrl}/api${path.startsWith("/") ? path : `/${path}`}`);
28
+ for (const [key, value] of Object.entries(query ?? {})) {
29
+ if (value === undefined || value === null)
30
+ continue;
31
+ url.searchParams.set(key, String(value));
32
+ }
33
+ return url.toString();
34
+ }
35
+ async request(method, path, options = {}) {
36
+ const { status, data } = await this.requestRaw(method, path, options);
37
+ if (status === 204)
38
+ return undefined;
39
+ return data;
40
+ }
41
+ async requestRaw(method, path, options = {}) {
42
+ const url = this.buildUrl(path, options.query);
43
+ const retries = options.retries ?? (method === "GET" ? 2 : 0);
44
+ let attempt = 0;
45
+ for (;;) {
46
+ try {
47
+ return await this.once(method, path, url, options);
48
+ }
49
+ catch (error) {
50
+ const retryable = error instanceof errors_js_1.CallmanNetworkError ||
51
+ (error instanceof errors_js_1.CallmanApiError && error.status >= 500 && error.status !== 501);
52
+ if (!retryable || attempt >= retries)
53
+ throw error;
54
+ attempt += 1;
55
+ this.logger.debug(`retrying ${method} ${path} (attempt ${attempt + 1})`);
56
+ await sleep(200 * attempt);
57
+ }
58
+ }
59
+ }
60
+ async once(method, path, url, options) {
61
+ const headers = {
62
+ Accept: "application/json",
63
+ "User-Agent": this.userAgent,
64
+ };
65
+ if (this.token)
66
+ headers.Authorization = `Bearer ${this.token}`;
67
+ if (options.workspaceId)
68
+ headers["x-workspace-id"] = options.workspaceId;
69
+ if (options.body !== undefined)
70
+ headers["Content-Type"] = "application/json";
71
+ let response;
72
+ try {
73
+ response = await this.fetchImpl(url, {
74
+ method,
75
+ headers,
76
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
77
+ signal: AbortSignal.timeout(options.timeoutMs ?? this.timeoutMs),
78
+ });
79
+ }
80
+ catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ throw new errors_js_1.CallmanNetworkError(this.apiUrl, `Could not reach ${this.apiUrl} (${message}). Check CALLMAN_API_URL — on-prem servers need the same address the desktop app uses, reachable from this machine.`, error);
83
+ }
84
+ let parsed = null;
85
+ const text = await response.text();
86
+ if (text) {
87
+ try {
88
+ parsed = JSON.parse(text);
89
+ }
90
+ catch {
91
+ parsed = null;
92
+ }
93
+ }
94
+ if (!response.ok) {
95
+ const envelope = isEnvelope(parsed) ? parsed : null;
96
+ const retryAfter = response.headers.get("retry-after");
97
+ throw new errors_js_1.CallmanApiError({
98
+ status: response.status,
99
+ code: envelope?.code ?? (response.status === 404 && !envelope ? "ROUTE_NOT_FOUND" : `HTTP_${response.status}`),
100
+ message: envelope?.message ?? envelope?.error ?? `HTTP ${response.status}`,
101
+ details: retryAfter ? { ...(asRecord(envelope?.details) ?? {}), retryAfterSeconds: Number(retryAfter) } : envelope?.details,
102
+ method,
103
+ path,
104
+ });
105
+ }
106
+ if (response.status === 204 || parsed === null)
107
+ return { status: response.status, data: null };
108
+ if (!isEnvelope(parsed))
109
+ return { status: response.status, data: parsed };
110
+ if (parsed.success === false) {
111
+ throw new errors_js_1.CallmanApiError({
112
+ status: response.status,
113
+ code: parsed.code ?? "UNEXPECTED_FAILURE",
114
+ message: parsed.message ?? parsed.error ?? "Request failed",
115
+ details: parsed.details,
116
+ method,
117
+ path,
118
+ });
119
+ }
120
+ return { status: response.status, data: parsed.data ?? null };
121
+ }
122
+ }
123
+ exports.CallmanHttpClient = CallmanHttpClient;
124
+ const asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ // Backend response shapes the server reads. Mirrors the frozen fields of the
3
+ // Callman REST API (callman-backend `.claude/API_CONVENTIONS.md`) — additive
4
+ // fields are optional so older servers still parse.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ // Configuration for the callman-mcp server, resolved ONCE at startup from
3
+ // environment variables (the only host-specific input) and frozen. Every
4
+ // other module reads the resolved object — never process.env — so the same
5
+ // tool layer can be hosted by the backend later with a different source.
6
+ //
7
+ // CALLMAN_API_URL server ORIGIN (https://api.callman.io, or the
8
+ // on-prem address incl. any prefix); `/api` is
9
+ // appended here, exactly like callme-cli.
10
+ // CALLMAN_TOKEN personal token (cm_pat_…) from Settings → AI tools
11
+ // CALLMAN_TOKEN_FILE alternative: path of a file holding the token
12
+ // CALLMAN_WORKSPACE_ID default workspace (optional — tools resolve it)
13
+ // CALLMAN_ALLOW_RUN "0"/"false" disables local request execution
14
+ // CALLMAN_LOG_LEVEL silent | error | warn | info | debug (stderr)
15
+ // CALLMAN_TIMEOUT_MS per API call (default 30s)
16
+ // CALLMAN_RUN_TIMEOUT_MS whole local run (default 10 min)
17
+ // CALLMAN_MAX_BODY_BYTES response-body preview cap in run output (16 KB)
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.resolveConfig = exports.normalizeApiUrl = exports.MAX_BODY_BYTES_LIMIT = exports.DEFAULT_CLOUD_API_URL = void 0;
20
+ exports.DEFAULT_CLOUD_API_URL = "https://api.callman.io";
21
+ const LOG_LEVELS = ["silent", "error", "warn", "info", "debug"];
22
+ exports.MAX_BODY_BYTES_LIMIT = 256 * 1024;
23
+ const pick = (...candidates) => {
24
+ for (const candidate of candidates) {
25
+ if (typeof candidate === "string" && candidate.trim() !== "")
26
+ return candidate.trim();
27
+ }
28
+ return null;
29
+ };
30
+ const numberOr = (raw, fallback, min, max) => {
31
+ const parsed = raw === undefined ? NaN : Number(raw);
32
+ if (!Number.isFinite(parsed))
33
+ return fallback;
34
+ return Math.min(max, Math.max(min, Math.floor(parsed)));
35
+ };
36
+ const boolOr = (raw, fallback) => {
37
+ if (raw === undefined || raw.trim() === "")
38
+ return fallback;
39
+ return !["0", "false", "no", "off"].includes(raw.trim().toLowerCase());
40
+ };
41
+ const normalizeApiUrl = (raw) => {
42
+ let url = raw.trim().replace(/\/+$/, "");
43
+ // Accept the desktop's VITE_API_URL form (…/api) too — the client appends /api.
44
+ url = url.replace(/\/api$/i, "");
45
+ if (!/^https?:\/\//i.test(url)) {
46
+ throw new Error(`CALLMAN_API_URL must start with http:// or https:// (got "${raw}")`);
47
+ }
48
+ return url;
49
+ };
50
+ exports.normalizeApiUrl = normalizeApiUrl;
51
+ const resolveConfig = ({ env, readTokenFile }) => {
52
+ const apiUrl = (0, exports.normalizeApiUrl)(pick(env.CALLMAN_API_URL) ?? exports.DEFAULT_CLOUD_API_URL);
53
+ let token = pick(env.CALLMAN_TOKEN);
54
+ const tokenFile = pick(env.CALLMAN_TOKEN_FILE);
55
+ if (!token && tokenFile) {
56
+ if (!readTokenFile)
57
+ throw new Error("CALLMAN_TOKEN_FILE is set but no file reader is available");
58
+ token = readTokenFile(tokenFile).trim() || null;
59
+ }
60
+ const logLevelRaw = (pick(env.CALLMAN_LOG_LEVEL) ?? "warn").toLowerCase();
61
+ const logLevel = LOG_LEVELS.includes(logLevelRaw) ? logLevelRaw : "warn";
62
+ return Object.freeze({
63
+ apiUrl,
64
+ token,
65
+ defaultWorkspaceId: pick(env.CALLMAN_WORKSPACE_ID),
66
+ allowLocalRun: boolOr(env.CALLMAN_ALLOW_RUN, true),
67
+ logLevel,
68
+ timeoutMs: numberOr(env.CALLMAN_TIMEOUT_MS, 30_000, 1_000, 600_000),
69
+ runTimeoutMs: numberOr(env.CALLMAN_RUN_TIMEOUT_MS, 600_000, 1_000, 3_600_000),
70
+ maxBodyBytes: numberOr(env.CALLMAN_MAX_BODY_BYTES, 16 * 1024, 256, exports.MAX_BODY_BYTES_LIMIT),
71
+ });
72
+ };
73
+ exports.resolveConfig = resolveConfig;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createToolContext = void 0;
4
+ const api_js_1 = require("./client/api.js");
5
+ const errors_js_1 = require("./client/errors.js");
6
+ const http_js_1 = require("./client/http.js");
7
+ const cache_js_1 = require("./lib/cache.js");
8
+ const ids_js_1 = require("./lib/ids.js");
9
+ const log_js_1 = require("./lib/log.js");
10
+ const createToolContext = (options) => {
11
+ const logger = options.logger ?? log_js_1.silentLogger;
12
+ const http = new http_js_1.CallmanHttpClient({
13
+ apiUrl: options.config.apiUrl,
14
+ token: options.config.token,
15
+ fetch: options.fetch,
16
+ timeoutMs: options.config.timeoutMs,
17
+ userAgent: `callman-mcp/${options.version}`,
18
+ logger,
19
+ });
20
+ const api = new api_js_1.CallmanApi(http);
21
+ const cache = new cache_js_1.TtlCache(options.cacheTtlMs ?? 15_000);
22
+ let runLock = Promise.resolve();
23
+ const listWorkspaces = () => cache.getOrSet("workspaces", () => api.listWorkspaces());
24
+ const pickWorkspace = async (explicit, required) => {
25
+ const workspaces = await listWorkspaces();
26
+ const wanted = (explicit ?? "").trim() || options.config.defaultWorkspaceId || "";
27
+ if (wanted) {
28
+ const byId = workspaces.find((workspace) => workspace.id === wanted);
29
+ if (byId)
30
+ return byId;
31
+ const needle = (0, ids_js_1.normalizeName)(wanted);
32
+ const byName = workspaces.filter((workspace) => (0, ids_js_1.normalizeName)(workspace.name) === needle);
33
+ if (byName.length === 1)
34
+ return byName[0];
35
+ const byPartial = workspaces.filter((workspace) => (0, ids_js_1.normalizeName)(workspace.name).includes(needle));
36
+ if (byName.length === 0 && byPartial.length === 1)
37
+ return byPartial[0];
38
+ if ((0, ids_js_1.isObjectId)(wanted) && explicit) {
39
+ // A well-formed id that is not in the list: let the server answer
40
+ // (it may be a workspace the user was just added to).
41
+ return { id: wanted, name: wanted, role: "viewer" };
42
+ }
43
+ if (!required)
44
+ return null;
45
+ throw new errors_js_1.ToolInputError(byName.length > 1 || byPartial.length > 1 ? "WORKSPACE_AMBIGUOUS" : "WORKSPACE_NOT_FOUND", byName.length > 1 || byPartial.length > 1
46
+ ? `Several workspaces match "${wanted}". Pass the id instead.`
47
+ : `No workspace matches "${wanted}".`, { candidates: summarize(byPartial.length > 0 ? byPartial : workspaces) });
48
+ }
49
+ if (workspaces.length === 1)
50
+ return workspaces[0];
51
+ if (!required)
52
+ return null;
53
+ throw new errors_js_1.ToolInputError(workspaces.length === 0 ? "WORKSPACE_NONE" : "WORKSPACE_REQUIRED", workspaces.length === 0
54
+ ? "You are not a member of any workspace."
55
+ : "Several workspaces are available — pass `workspace` (name or id) or set CALLMAN_WORKSPACE_ID.", { candidates: summarize(workspaces) });
56
+ };
57
+ return {
58
+ config: options.config,
59
+ api,
60
+ logger,
61
+ cache,
62
+ version: options.version,
63
+ listWorkspaces,
64
+ getTokenScopes: () => cache.getOrSet("token-scopes", async () => {
65
+ try {
66
+ return (await api.me()).token.scopes ?? [];
67
+ }
68
+ catch (error) {
69
+ logger.debug("could not read token scopes", error instanceof Error ? error.message : error);
70
+ return [];
71
+ }
72
+ }),
73
+ resolveWorkspace: async (explicit) => (await pickWorkspace(explicit, true)),
74
+ resolveWorkspaceOrNull: (explicit) => pickWorkspace(explicit, false),
75
+ withRunLock: async (fn) => {
76
+ const previous = runLock;
77
+ let release = () => { };
78
+ runLock = new Promise((resolve) => {
79
+ release = resolve;
80
+ });
81
+ try {
82
+ await previous.catch(() => undefined);
83
+ return await fn();
84
+ }
85
+ finally {
86
+ release();
87
+ }
88
+ },
89
+ invalidate: (prefix) => (prefix ? cache.delete(prefix) : cache.clear()),
90
+ pollDelaysMs: options.pollDelaysMs ?? { fast: 2000, slow: 5000 },
91
+ };
92
+ };
93
+ exports.createToolContext = createToolContext;
94
+ const summarize = (workspaces) => workspaces.slice(0, 20).map((workspace) => ({ id: workspace.id, name: workspace.name, role: workspace.role }));