@langwatch/mcp-server 0.6.0 → 0.7.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 (258) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/Dockerfile +22 -0
  3. package/README.md +51 -8
  4. package/dist/{archive-scenario-YFD5THOR.js → archive-scenario-WJ2A7ETN.js} +4 -4
  5. package/dist/archive-suite-4JOM4SSL.js +15 -0
  6. package/dist/archive-suite-4JOM4SSL.js.map +1 -0
  7. package/dist/assign-prompt-tag-GL7EAAF3.js +19 -0
  8. package/dist/assign-prompt-tag-GL7EAAF3.js.map +1 -0
  9. package/dist/{chunk-6U4TCGFC.js → chunk-2GT7IFWK.js} +2 -2
  10. package/dist/{chunk-LLRQIF52.js → chunk-2WPLXEKF.js} +49 -5
  11. package/dist/chunk-2WPLXEKF.js.map +1 -0
  12. package/dist/chunk-4AUB2BIA.js +71 -0
  13. package/dist/chunk-4AUB2BIA.js.map +1 -0
  14. package/dist/chunk-BWWY5KEF.js +71 -0
  15. package/dist/chunk-BWWY5KEF.js.map +1 -0
  16. package/dist/{chunk-IX6QJKAD.js → chunk-CCWCIJJ3.js} +2 -2
  17. package/dist/chunk-E5JYYU3J.js +1567 -0
  18. package/dist/chunk-E5JYYU3J.js.map +1 -0
  19. package/dist/chunk-EGH5RPXM.js +25 -0
  20. package/dist/chunk-EGH5RPXM.js.map +1 -0
  21. package/dist/chunk-EUQDVOSU.js +39 -0
  22. package/dist/chunk-EUQDVOSU.js.map +1 -0
  23. package/dist/{chunk-5UOPNRXW.js → chunk-HYMIWO3U.js} +10 -3
  24. package/dist/{chunk-5UOPNRXW.js.map → chunk-HYMIWO3U.js.map} +1 -1
  25. package/dist/chunk-IE4M5OGE.js +42 -0
  26. package/dist/chunk-IE4M5OGE.js.map +1 -0
  27. package/dist/chunk-LUMEZEHL.js +48 -0
  28. package/dist/chunk-LUMEZEHL.js.map +1 -0
  29. package/dist/chunk-NUFPTHKU.js +9 -0
  30. package/dist/chunk-NUFPTHKU.js.map +1 -0
  31. package/dist/chunk-WQG2DFHX.js +86 -0
  32. package/dist/chunk-WQG2DFHX.js.map +1 -0
  33. package/dist/chunk-WVI5CJ6S.js +21 -0
  34. package/dist/chunk-WVI5CJ6S.js.map +1 -0
  35. package/dist/chunk-ZVS4UKN2.js +35 -0
  36. package/dist/chunk-ZVS4UKN2.js.map +1 -0
  37. package/dist/config.d.ts +17 -0
  38. package/dist/config.js +13 -0
  39. package/dist/create-agent-MBOOT3XK.js +23 -0
  40. package/dist/create-agent-MBOOT3XK.js.map +1 -0
  41. package/dist/create-annotation-XMV6A7ED.js +24 -0
  42. package/dist/create-annotation-XMV6A7ED.js.map +1 -0
  43. package/dist/create-dashboard-VRKLFVZJ.js +18 -0
  44. package/dist/create-dashboard-VRKLFVZJ.js.map +1 -0
  45. package/dist/create-dataset-7HQPZCRE.js +22 -0
  46. package/dist/create-dataset-7HQPZCRE.js.map +1 -0
  47. package/dist/create-dataset-records-2NIVFYTV.js +22 -0
  48. package/dist/create-dataset-records-2NIVFYTV.js.map +1 -0
  49. package/dist/{create-evaluator-E5X5ZP3B.js → create-evaluator-WYFRFLRC.js} +4 -4
  50. package/dist/create-mcp-server.d.ts +8 -0
  51. package/dist/create-mcp-server.js +8 -0
  52. package/dist/create-mcp-server.js.map +1 -0
  53. package/dist/{create-prompt-7Z35MIL6.js → create-prompt-IEN7O6JT.js} +7 -4
  54. package/dist/create-prompt-IEN7O6JT.js.map +1 -0
  55. package/dist/create-prompt-tag-CAEZ45VO.js +17 -0
  56. package/dist/create-prompt-tag-CAEZ45VO.js.map +1 -0
  57. package/dist/{create-scenario-DIMPJRPY.js → create-scenario-6YHSMGLP.js} +4 -4
  58. package/dist/create-suite-H5XOFDE5.js +38 -0
  59. package/dist/create-suite-H5XOFDE5.js.map +1 -0
  60. package/dist/delete-agent-JKTNBWAA.js +18 -0
  61. package/dist/delete-agent-JKTNBWAA.js.map +1 -0
  62. package/dist/delete-annotation-ACMDV6QO.js +17 -0
  63. package/dist/delete-annotation-ACMDV6QO.js.map +1 -0
  64. package/dist/delete-dashboard-VJSR7CRU.js +18 -0
  65. package/dist/delete-dashboard-VJSR7CRU.js.map +1 -0
  66. package/dist/delete-dataset-RTTTWP4D.js +20 -0
  67. package/dist/delete-dataset-RTTTWP4D.js.map +1 -0
  68. package/dist/delete-dataset-records-KNQTIPQK.js +21 -0
  69. package/dist/delete-dataset-records-KNQTIPQK.js.map +1 -0
  70. package/dist/delete-evaluator-7FVC5WM4.js +15 -0
  71. package/dist/delete-evaluator-7FVC5WM4.js.map +1 -0
  72. package/dist/delete-prompt-tag-ZMZUAGFW.js +17 -0
  73. package/dist/delete-prompt-tag-ZMZUAGFW.js.map +1 -0
  74. package/dist/delete-workflow-EYQ3T4RQ.js +17 -0
  75. package/dist/delete-workflow-EYQ3T4RQ.js.map +1 -0
  76. package/dist/get-agent-E3QSTVKG.js +28 -0
  77. package/dist/get-agent-E3QSTVKG.js.map +1 -0
  78. package/dist/{get-analytics-4YJW4S5L.js → get-analytics-2OHV763I.js} +22 -5
  79. package/dist/get-analytics-2OHV763I.js.map +1 -0
  80. package/dist/get-dashboard-PC7JDHH6.js +29 -0
  81. package/dist/get-dashboard-PC7JDHH6.js.map +1 -0
  82. package/dist/get-dataset-H5IK5FVU.js +48 -0
  83. package/dist/get-dataset-H5IK5FVU.js.map +1 -0
  84. package/dist/{get-evaluator-WDEH2F7M.js → get-evaluator-UWVUPYTM.js} +4 -4
  85. package/dist/{get-prompt-F6PDVC76.js → get-prompt-WZUOLLQC.js} +4 -4
  86. package/dist/get-prompt-WZUOLLQC.js.map +1 -0
  87. package/dist/{get-scenario-H24ZYNT5.js → get-scenario-Q6LCNUYN.js} +4 -4
  88. package/dist/get-simulation-run-Z2XKO256.js +56 -0
  89. package/dist/get-simulation-run-Z2XKO256.js.map +1 -0
  90. package/dist/get-suite-BXK5TN7P.js +44 -0
  91. package/dist/get-suite-BXK5TN7P.js.map +1 -0
  92. package/dist/{get-trace-27USKGO7.js → get-trace-4EJAYVBG.js} +3 -3
  93. package/dist/get-workflow-O34QJLHD.js +25 -0
  94. package/dist/get-workflow-O34QJLHD.js.map +1 -0
  95. package/dist/http-server-ISHF7NJX.js +224 -0
  96. package/dist/http-server-ISHF7NJX.js.map +1 -0
  97. package/dist/index.js +61109 -31519
  98. package/dist/index.js.map +1 -1
  99. package/dist/langwatch-api-annotations-ERBG27RF.js +17 -0
  100. package/dist/langwatch-api-annotations-ERBG27RF.js.map +1 -0
  101. package/dist/langwatch-api-dashboards-G6TR7VVO.js +17 -0
  102. package/dist/langwatch-api-dashboards-G6TR7VVO.js.map +1 -0
  103. package/dist/langwatch-api-monitors-WSXZLS2B.js +52 -0
  104. package/dist/langwatch-api-monitors-WSXZLS2B.js.map +1 -0
  105. package/dist/langwatch-api-secrets-RR5DKG2X.js +40 -0
  106. package/dist/langwatch-api-secrets-RR5DKG2X.js.map +1 -0
  107. package/dist/langwatch-api-triggers-2RTNSWQY.js +30 -0
  108. package/dist/langwatch-api-triggers-2RTNSWQY.js.map +1 -0
  109. package/dist/langwatch-api-workflows-VJRIIDZ7.js +15 -0
  110. package/dist/langwatch-api-workflows-VJRIIDZ7.js.map +1 -0
  111. package/dist/list-agents-AW3P6FJV.js +32 -0
  112. package/dist/list-agents-AW3P6FJV.js.map +1 -0
  113. package/dist/list-annotations-QNCGWPQ3.js +33 -0
  114. package/dist/list-annotations-QNCGWPQ3.js.map +1 -0
  115. package/dist/list-dashboards-R7RK2IXL.js +32 -0
  116. package/dist/list-dashboards-R7RK2IXL.js.map +1 -0
  117. package/dist/list-dataset-records-WVCSQP7P.js +39 -0
  118. package/dist/list-dataset-records-WVCSQP7P.js.map +1 -0
  119. package/dist/list-datasets-KEXRLDEU.js +60 -0
  120. package/dist/list-datasets-KEXRLDEU.js.map +1 -0
  121. package/dist/{list-evaluators-KRGI72EH.js → list-evaluators-7R2LB4JH.js} +4 -4
  122. package/dist/{list-model-providers-A5YCFTPI.js → list-model-providers-OBYJCKBX.js} +4 -4
  123. package/dist/list-prompt-tags-6LY3ZPYI.js +23 -0
  124. package/dist/list-prompt-tags-6LY3ZPYI.js.map +1 -0
  125. package/dist/{list-prompts-LKJSE7XN.js → list-prompts-VWEZ4CDE.js} +3 -3
  126. package/dist/{list-scenarios-ZK5CMGC4.js → list-scenarios-TXD4W2ZJ.js} +4 -4
  127. package/dist/list-simulation-runs-RHPOLMFI.js +43 -0
  128. package/dist/list-simulation-runs-RHPOLMFI.js.map +1 -0
  129. package/dist/list-suites-FSGBX27Z.js +42 -0
  130. package/dist/list-suites-FSGBX27Z.js.map +1 -0
  131. package/dist/list-workflows-VRGEZ3EN.js +35 -0
  132. package/dist/list-workflows-VRGEZ3EN.js.map +1 -0
  133. package/dist/rename-prompt-tag-VOJJLX4M.js +18 -0
  134. package/dist/rename-prompt-tag-VOJJLX4M.js.map +1 -0
  135. package/dist/run-agent-KTCC7TB7.js +36 -0
  136. package/dist/run-agent-KTCC7TB7.js.map +1 -0
  137. package/dist/run-evaluation-2X3V3Q6L.js +64 -0
  138. package/dist/run-evaluation-2X3V3Q6L.js.map +1 -0
  139. package/dist/run-suite-BTMHGI2D.js +29 -0
  140. package/dist/run-suite-BTMHGI2D.js.map +1 -0
  141. package/dist/run-workflow-IGAYGKJL.js +36 -0
  142. package/dist/run-workflow-IGAYGKJL.js.map +1 -0
  143. package/dist/{search-traces-SOKAAMAR.js → search-traces-VYFUIMXH.js} +3 -3
  144. package/dist/{set-model-provider-7MGULZDH.js → set-model-provider-W5IUI326.js} +4 -4
  145. package/dist/update-agent-4BB7EYLR.js +20 -0
  146. package/dist/update-agent-4BB7EYLR.js.map +1 -0
  147. package/dist/update-dataset-CXBKZ4W3.js +22 -0
  148. package/dist/update-dataset-CXBKZ4W3.js.map +1 -0
  149. package/dist/update-dataset-record-HOHAMHXG.js +19 -0
  150. package/dist/update-dataset-record-HOHAMHXG.js.map +1 -0
  151. package/dist/{update-evaluator-A3XINFLJ.js → update-evaluator-IVOR6FTR.js} +4 -4
  152. package/dist/{update-prompt-IW7X2UQM.js → update-prompt-OWQAKE7K.js} +5 -3
  153. package/dist/update-prompt-OWQAKE7K.js.map +1 -0
  154. package/dist/{update-scenario-ZT7TOBFR.js → update-scenario-GDFVILPN.js} +4 -4
  155. package/dist/update-suite-BXYUYSPT.js +39 -0
  156. package/dist/update-suite-BXYUYSPT.js.map +1 -0
  157. package/package.json +17 -3
  158. package/src/__tests__/all-tools.integration.test.ts +344 -0
  159. package/src/__tests__/config.unit.test.ts +81 -1
  160. package/src/__tests__/datasets.integration.test.ts +603 -0
  161. package/src/__tests__/datasets.unit.test.ts +240 -0
  162. package/src/__tests__/docker.integration.test.ts +234 -0
  163. package/src/__tests__/http-transport.integration.test.ts +397 -0
  164. package/src/__tests__/langwatch-api.unit.test.ts +174 -0
  165. package/src/__tests__/tools.unit.test.ts +177 -0
  166. package/src/config.d.ts +15 -0
  167. package/src/config.ts +83 -7
  168. package/src/create-mcp-server.d.ts +6 -0
  169. package/src/create-mcp-server.ts +1799 -0
  170. package/src/http-server.ts +329 -0
  171. package/src/index.ts +22 -593
  172. package/src/langwatch-api-agents.ts +108 -0
  173. package/src/langwatch-api-annotations.ts +49 -0
  174. package/src/langwatch-api-dashboards.ts +48 -0
  175. package/src/langwatch-api-datasets.ts +191 -0
  176. package/src/langwatch-api-evaluators.ts +10 -0
  177. package/src/langwatch-api-monitors.ts +82 -0
  178. package/src/langwatch-api-secrets.ts +48 -0
  179. package/src/langwatch-api-simulation-runs.ts +51 -0
  180. package/src/langwatch-api-suites.ts +118 -0
  181. package/src/langwatch-api-triggers.ts +48 -0
  182. package/src/langwatch-api-workflows.ts +41 -0
  183. package/src/langwatch-api.ts +60 -2
  184. package/src/schemas/create-dataset.ts +48 -0
  185. package/src/schemas/index.ts +1 -0
  186. package/src/tools/archive-suite.ts +12 -0
  187. package/src/tools/assign-prompt-tag.ts +16 -0
  188. package/src/tools/create-agent.ts +15 -0
  189. package/src/tools/create-annotation.ts +18 -0
  190. package/src/tools/create-dashboard.ts +7 -0
  191. package/src/tools/create-dataset-records.ts +23 -0
  192. package/src/tools/create-dataset.ts +22 -0
  193. package/src/tools/create-prompt-tag.ts +12 -0
  194. package/src/tools/create-prompt.ts +4 -0
  195. package/src/tools/create-suite.ts +42 -0
  196. package/src/tools/delete-agent.ts +7 -0
  197. package/src/tools/delete-annotation.ts +7 -0
  198. package/src/tools/delete-dashboard.ts +7 -0
  199. package/src/tools/delete-dataset-records.ts +22 -0
  200. package/src/tools/delete-dataset.ts +20 -0
  201. package/src/tools/delete-evaluator.ts +12 -0
  202. package/src/tools/delete-prompt-tag.ts +12 -0
  203. package/src/tools/delete-workflow.ts +7 -0
  204. package/src/tools/format-dataset-mutation.ts +25 -0
  205. package/src/tools/get-agent.ts +21 -0
  206. package/src/tools/get-analytics.ts +30 -3
  207. package/src/tools/get-dashboard.ts +22 -0
  208. package/src/tools/get-dataset.ts +63 -0
  209. package/src/tools/get-prompt.ts +2 -1
  210. package/src/tools/get-simulation-run.ts +60 -0
  211. package/src/tools/get-suite.ts +47 -0
  212. package/src/tools/get-workflow.ts +17 -0
  213. package/src/tools/list-agents.ts +27 -0
  214. package/src/tools/list-annotations.ts +33 -0
  215. package/src/tools/list-dashboards.ts +27 -0
  216. package/src/tools/list-dataset-records.ts +46 -0
  217. package/src/tools/list-datasets.ts +80 -0
  218. package/src/tools/list-prompt-tags.ts +21 -0
  219. package/src/tools/list-simulation-runs.ts +49 -0
  220. package/src/tools/list-suites.ts +43 -0
  221. package/src/tools/list-workflows.ts +31 -0
  222. package/src/tools/rename-prompt-tag.ts +14 -0
  223. package/src/tools/run-agent.ts +36 -0
  224. package/src/tools/run-evaluation.ts +87 -0
  225. package/src/tools/run-suite.ts +28 -0
  226. package/src/tools/run-workflow.ts +36 -0
  227. package/src/tools/update-agent.ts +13 -0
  228. package/src/tools/update-dataset-record.ts +21 -0
  229. package/src/tools/update-dataset.ts +23 -0
  230. package/src/tools/update-prompt.ts +3 -0
  231. package/src/tools/update-suite.ts +44 -0
  232. package/src/utils/escape-markdown.ts +12 -0
  233. package/tsup.config.ts +5 -1
  234. package/dist/chunk-AAQNA53E.js +0 -28
  235. package/dist/chunk-AAQNA53E.js.map +0 -1
  236. package/dist/chunk-LLRQIF52.js.map +0 -1
  237. package/dist/config-FIQWQRUB.js +0 -11
  238. package/dist/create-prompt-7Z35MIL6.js.map +0 -1
  239. package/dist/get-analytics-4YJW4S5L.js.map +0 -1
  240. package/dist/get-prompt-F6PDVC76.js.map +0 -1
  241. package/dist/update-prompt-IW7X2UQM.js.map +0 -1
  242. /package/dist/{archive-scenario-YFD5THOR.js.map → archive-scenario-WJ2A7ETN.js.map} +0 -0
  243. /package/dist/{chunk-6U4TCGFC.js.map → chunk-2GT7IFWK.js.map} +0 -0
  244. /package/dist/{chunk-IX6QJKAD.js.map → chunk-CCWCIJJ3.js.map} +0 -0
  245. /package/dist/{config-FIQWQRUB.js.map → config.js.map} +0 -0
  246. /package/dist/{create-evaluator-E5X5ZP3B.js.map → create-evaluator-WYFRFLRC.js.map} +0 -0
  247. /package/dist/{create-scenario-DIMPJRPY.js.map → create-scenario-6YHSMGLP.js.map} +0 -0
  248. /package/dist/{get-evaluator-WDEH2F7M.js.map → get-evaluator-UWVUPYTM.js.map} +0 -0
  249. /package/dist/{get-scenario-H24ZYNT5.js.map → get-scenario-Q6LCNUYN.js.map} +0 -0
  250. /package/dist/{get-trace-27USKGO7.js.map → get-trace-4EJAYVBG.js.map} +0 -0
  251. /package/dist/{list-evaluators-KRGI72EH.js.map → list-evaluators-7R2LB4JH.js.map} +0 -0
  252. /package/dist/{list-model-providers-A5YCFTPI.js.map → list-model-providers-OBYJCKBX.js.map} +0 -0
  253. /package/dist/{list-prompts-LKJSE7XN.js.map → list-prompts-VWEZ4CDE.js.map} +0 -0
  254. /package/dist/{list-scenarios-ZK5CMGC4.js.map → list-scenarios-TXD4W2ZJ.js.map} +0 -0
  255. /package/dist/{search-traces-SOKAAMAR.js.map → search-traces-VYFUIMXH.js.map} +0 -0
  256. /package/dist/{set-model-provider-7MGULZDH.js.map → set-model-provider-W5IUI326.js.map} +0 -0
  257. /package/dist/{update-evaluator-A3XINFLJ.js.map → update-evaluator-IVOR6FTR.js.map} +0 -0
  258. /package/dist/{update-scenario-ZT7TOBFR.js.map → update-scenario-GDFVILPN.js.map} +0 -0
package/package.json CHANGED
@@ -1,15 +1,27 @@
1
1
  {
2
2
  "name": "@langwatch/mcp-server",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "An MCP server for Langwatch.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
7
+ "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "import": "./dist/index.mjs",
11
+ "import": "./dist/index.js",
12
12
  "require": "./dist/index.js"
13
+ },
14
+ "./create-mcp-server": {
15
+ "types": "./src/create-mcp-server.d.ts",
16
+ "import": "./dist/create-mcp-server.js",
17
+ "require": "./dist/create-mcp-server.js",
18
+ "default": "./dist/create-mcp-server.js"
19
+ },
20
+ "./config": {
21
+ "types": "./src/config.d.ts",
22
+ "import": "./dist/config.js",
23
+ "require": "./dist/config.js",
24
+ "default": "./dist/config.js"
13
25
  }
14
26
  },
15
27
  "repository": {
@@ -29,6 +41,7 @@
29
41
  "@langwatch/scenario": "^0.4.6",
30
42
  "@types/debug": "^4.1.12",
31
43
  "@types/eslint__js": "^8.42.3",
44
+ "@types/express": "^5.0.6",
32
45
  "@types/node": "^16.0.0",
33
46
  "@types/yargs": "^17.0.33",
34
47
  "@typescript/native-preview": "7.0.0-dev.20250911.1",
@@ -47,6 +60,7 @@
47
60
  "@opentelemetry/sdk-node": "^0.204.0",
48
61
  "chalk": "^5.6.2",
49
62
  "date-fns": "^4.1.0",
63
+ "express": "^5.2.1",
50
64
  "node-pty": "^1.0.0",
51
65
  "vitest": "^3.2.4",
52
66
  "yargs": "^17.7.2",
@@ -278,6 +278,50 @@ const CANNED_MODEL_PROVIDERS_LIST = {
278
278
  },
279
279
  };
280
280
 
281
+ const CANNED_AGENTS_LIST = {
282
+ data: [
283
+ { id: "agent_abc", name: "Test Agent", type: "http", config: { url: "http://example.com" }, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
284
+ ],
285
+ pagination: { page: 1, limit: 50, total: 1, totalPages: 1 },
286
+ };
287
+
288
+ const CANNED_AGENT_DETAIL = {
289
+ id: "agent_abc", name: "Test Agent", type: "http", config: { url: "http://example.com" }, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z",
290
+ };
291
+
292
+ const CANNED_SUITES_LIST = [
293
+ { id: "suite_abc", name: "Regression Suite", slug: "regression-suite", description: null, scenarioIds: ["scen_abc123"], targets: [{ type: "http", referenceId: "agent_abc" }], repeatCount: 1, labels: [], createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
294
+ ];
295
+
296
+ const CANNED_SUITE_DETAIL = {
297
+ id: "suite_abc", name: "Regression Suite", slug: "regression-suite", description: "A test suite", scenarioIds: ["scen_abc123"], targets: [{ type: "http", referenceId: "agent_abc" }], repeatCount: 1, labels: [], createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z",
298
+ };
299
+
300
+ const CANNED_SUITE_CREATED = {
301
+ id: "suite_new", name: "New Suite", slug: "new-suite", description: null, scenarioIds: ["scen_abc123"], targets: [{ type: "http", referenceId: "agent_abc" }], repeatCount: 1, labels: [], createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z",
302
+ };
303
+
304
+ const CANNED_SUITE_RUN = {
305
+ scheduled: true, batchRunId: "batch_123", setId: "set_456", jobCount: 1, skippedArchived: { scenarios: [], targets: [] }, items: [{ scenarioRunId: "run_1", scenarioId: "scen_abc123", target: { type: "http", referenceId: "agent_abc" }, name: "Test" }],
306
+ };
307
+
308
+ const CANNED_SIMULATION_RUNS = {
309
+ runs: [{ scenarioRunId: "run_abc", scenarioId: "scen_abc123", batchRunId: "batch_xyz", name: "Login Flow", status: "SUCCESS", durationInMs: 5200, totalCost: 0.0042, timestamp: 1700000000000, updatedAt: 1700000001000 }],
310
+ hasMore: false,
311
+ };
312
+
313
+ const CANNED_SIMULATION_RUN_DETAIL = {
314
+ scenarioRunId: "run_abc", scenarioId: "scen_abc123", batchRunId: "batch_xyz", name: "Login Flow", status: "SUCCESS", durationInMs: 5200, totalCost: 0.0042, results: { verdict: "passed", reasoning: "All criteria met", metCriteria: ["Greets user"], unmetCriteria: [], error: null }, messages: [{ role: "user", content: "Hello" }, { role: "assistant", content: "Hi there!" }], timestamp: 1700000000000, updatedAt: 1700000001000,
315
+ };
316
+
317
+ const CANNED_DASHBOARDS_LIST = {
318
+ data: [{ id: "dash_abc", name: "Main Dashboard", order: 0, graphCount: 3, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" }],
319
+ };
320
+
321
+ const CANNED_WORKFLOWS_LIST = [
322
+ { id: "wf_abc", name: "Test Workflow", icon: null, description: "A workflow", isEvaluator: false, isComponent: false, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
323
+ ];
324
+
281
325
  const CANNED_MODEL_PROVIDER_SET = {
282
326
  openai: {
283
327
  provider: "openai",
@@ -436,6 +480,124 @@ function createMockServer(): Server {
436
480
  res.writeHead(200);
437
481
  res.end(JSON.stringify(CANNED_MODEL_PROVIDER_SET));
438
482
  }
483
+ // --- Agent endpoints ---
484
+ else if (url === "/api/agents" && method === "GET") {
485
+ res.writeHead(200);
486
+ res.end(JSON.stringify(CANNED_AGENTS_LIST));
487
+ } else if (url === "/api/agents" && method === "POST") {
488
+ res.writeHead(201);
489
+ res.end(JSON.stringify(CANNED_AGENT_DETAIL));
490
+ } else if (url?.match(/^\/api\/agents\/[^/]+$/) && method === "GET") {
491
+ res.writeHead(200);
492
+ res.end(JSON.stringify(CANNED_AGENT_DETAIL));
493
+ } else if (url?.match(/^\/api\/agents\/[^/]+$/) && method === "PATCH") {
494
+ res.writeHead(200);
495
+ res.end(JSON.stringify(CANNED_AGENT_DETAIL));
496
+ } else if (url?.match(/^\/api\/agents\/[^/]+$/) && method === "DELETE") {
497
+ res.writeHead(200);
498
+ res.end(JSON.stringify({ id: "agent_abc", name: "Test Agent" }));
499
+ }
500
+ // --- Suite endpoints ---
501
+ else if (url === "/api/suites" && method === "GET") {
502
+ res.writeHead(200);
503
+ res.end(JSON.stringify(CANNED_SUITES_LIST));
504
+ } else if (url === "/api/suites" && method === "POST") {
505
+ res.writeHead(201);
506
+ res.end(JSON.stringify(CANNED_SUITE_CREATED));
507
+ } else if (url?.match(/^\/api\/suites\/[^/]+\/run$/) && method === "POST") {
508
+ res.writeHead(200);
509
+ res.end(JSON.stringify(CANNED_SUITE_RUN));
510
+ } else if (url?.match(/^\/api\/suites\/[^/]+\/duplicate$/) && method === "POST") {
511
+ res.writeHead(201);
512
+ res.end(JSON.stringify(CANNED_SUITE_CREATED));
513
+ } else if (url?.match(/^\/api\/suites\/[^/]+$/) && method === "GET") {
514
+ res.writeHead(200);
515
+ res.end(JSON.stringify(CANNED_SUITE_DETAIL));
516
+ } else if (url?.match(/^\/api\/suites\/[^/]+$/) && method === "PATCH") {
517
+ res.writeHead(200);
518
+ res.end(JSON.stringify(CANNED_SUITE_DETAIL));
519
+ } else if (url?.match(/^\/api\/suites\/[^/]+$/) && method === "DELETE") {
520
+ res.writeHead(200);
521
+ res.end(JSON.stringify({ id: "suite_abc", archived: true }));
522
+ }
523
+ // --- Simulation Run endpoints ---
524
+ else if (url?.match(/^\/api\/simulation-runs\/[^/]+$/) && method === "GET") {
525
+ res.writeHead(200);
526
+ res.end(JSON.stringify(CANNED_SIMULATION_RUN_DETAIL));
527
+ } else if (url?.match(/^\/api\/simulation-runs/) && method === "GET") {
528
+ res.writeHead(200);
529
+ res.end(JSON.stringify(CANNED_SIMULATION_RUNS));
530
+ }
531
+ // --- Dashboard endpoints ---
532
+ else if (url === "/api/dashboards" && method === "GET") {
533
+ res.writeHead(200);
534
+ res.end(JSON.stringify(CANNED_DASHBOARDS_LIST));
535
+ } else if (url === "/api/dashboards" && method === "POST") {
536
+ res.writeHead(201);
537
+ res.end(JSON.stringify({ id: "dash_new", name: "New Dashboard" }));
538
+ } else if (url?.match(/^\/api\/dashboards\/[^/]+$/) && method === "GET") {
539
+ res.writeHead(200);
540
+ res.end(JSON.stringify(CANNED_DASHBOARDS_LIST.data[0]));
541
+ } else if (url?.match(/^\/api\/dashboards\/[^/]+$/) && method === "DELETE") {
542
+ res.writeHead(200);
543
+ res.end(JSON.stringify({ id: "dash_abc", name: "Main Dashboard" }));
544
+ }
545
+ // --- Workflow endpoints ---
546
+ else if (url === "/api/workflows" && method === "GET") {
547
+ res.writeHead(200);
548
+ res.end(JSON.stringify(CANNED_WORKFLOWS_LIST));
549
+ } else if (url?.match(/^\/api\/workflows\/[^/]+\/run$/) && method === "POST") {
550
+ res.writeHead(200);
551
+ res.end(JSON.stringify({ output: "workflow result" }));
552
+ } else if (url?.match(/^\/api\/workflows\/[^/]+$/) && method === "GET") {
553
+ res.writeHead(200);
554
+ res.end(JSON.stringify(CANNED_WORKFLOWS_LIST[0]));
555
+ } else if (url?.match(/^\/api\/workflows\/[^/]+$/) && method === "DELETE") {
556
+ res.writeHead(200);
557
+ res.end(JSON.stringify({ id: "wf_abc", archived: true }));
558
+ }
559
+ // --- Monitor endpoints ---
560
+ else if (url === "/api/monitors" && method === "GET") {
561
+ res.writeHead(200);
562
+ res.end(JSON.stringify([
563
+ { id: "mon_abc", name: "Toxicity Check", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
564
+ ]));
565
+ } else if (url === "/api/monitors" && method === "POST") {
566
+ res.writeHead(201);
567
+ res.end(JSON.stringify({ id: "mon_new", name: "New Monitor", slug: "new-monitor-abc12", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" }));
568
+ } else if (url?.match(/^\/api\/monitors\/[^/]+\/toggle$/) && method === "POST") {
569
+ const monitorId = url?.split("/")[3];
570
+ res.writeHead(200);
571
+ res.end(JSON.stringify({ id: monitorId, enabled: true }));
572
+ } else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "GET") {
573
+ res.writeHead(200);
574
+ res.end(JSON.stringify({ id: "mon_abc", name: "Toxicity Check", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" }));
575
+ } else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "PATCH") {
576
+ res.writeHead(200);
577
+ res.end(JSON.stringify({ id: "mon_abc", name: "Updated Monitor", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: false, executionMode: "ON_MESSAGE", sample: 0.5, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z" }));
578
+ } else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "DELETE") {
579
+ const monitorId = url?.split("/").pop();
580
+ res.writeHead(200);
581
+ res.end(JSON.stringify({ id: monitorId, deleted: true }));
582
+ }
583
+ // --- Secret endpoints ---
584
+ else if (url === "/api/secrets" && method === "GET") {
585
+ res.writeHead(200);
586
+ res.end(JSON.stringify([
587
+ { id: "secret_abc", projectId: "proj_123", name: "MY_API_KEY", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
588
+ { id: "secret_def", projectId: "proj_123", name: "DB_PASSWORD", createdAt: "2026-01-02T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z" },
589
+ ]));
590
+ } else if (url === "/api/secrets" && method === "POST") {
591
+ res.writeHead(201);
592
+ res.end(JSON.stringify({ id: "secret_new", projectId: "proj_123", name: "NEW_SECRET", createdAt: "2026-01-03T00:00:00Z", updatedAt: "2026-01-03T00:00:00Z" }));
593
+ } else if (url?.match(/^\/api\/secrets\/[^/]+$/) && method === "PUT") {
594
+ res.writeHead(200);
595
+ res.end(JSON.stringify({ id: "secret_abc", projectId: "proj_123", name: "MY_API_KEY", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-03T00:00:00Z" }));
596
+ } else if (url?.match(/^\/api\/secrets\/[^/]+$/) && method === "DELETE") {
597
+ const secretId = url?.split("/").pop();
598
+ res.writeHead(200);
599
+ res.end(JSON.stringify({ id: secretId, deleted: true }));
600
+ }
439
601
  // --- Fallback ---
440
602
  else {
441
603
  res.writeHead(404);
@@ -1334,4 +1496,186 @@ describe("All MCP tools integration", () => {
1334
1496
  await expect(handleListModelProviders()).rejects.toThrow("401");
1335
1497
  });
1336
1498
  });
1499
+
1500
+ // =====================
1501
+ // Suite Tools
1502
+ // =====================
1503
+ describe("platform_list_suites", () => {
1504
+ it("returns formatted suite list", async () => {
1505
+ const { handleListSuites } = await import("../tools/list-suites.js");
1506
+ const result = await handleListSuites({});
1507
+
1508
+ expect(result).toContain("Suites / Run Plans (1 total)");
1509
+ expect(result).toContain("Regression Suite");
1510
+ });
1511
+ });
1512
+
1513
+ describe("platform_get_suite", () => {
1514
+ it("returns suite details", async () => {
1515
+ const { handleGetSuite } = await import("../tools/get-suite.js");
1516
+ const result = await handleGetSuite({ id: "suite_abc" });
1517
+
1518
+ expect(result).toContain("Regression Suite");
1519
+ expect(result).toContain("suite_abc");
1520
+ });
1521
+ });
1522
+
1523
+ describe("platform_create_suite", () => {
1524
+ it("creates a suite and returns confirmation", async () => {
1525
+ const { handleCreateSuite } = await import("../tools/create-suite.js");
1526
+ const result = await handleCreateSuite({
1527
+ name: "New Suite",
1528
+ scenarioIds: ["scen_abc123"],
1529
+ targets: JSON.stringify([{ type: "http", referenceId: "agent_abc" }]),
1530
+ });
1531
+
1532
+ expect(result).toContain("created successfully");
1533
+ expect(result).toContain("New Suite");
1534
+ });
1535
+ });
1536
+
1537
+ describe("platform_run_suite", () => {
1538
+ it("triggers a suite run and returns batch info", async () => {
1539
+ const { handleRunSuite } = await import("../tools/run-suite.js");
1540
+ const result = await handleRunSuite({ id: "suite_abc" });
1541
+
1542
+ expect(result).toContain("scheduled successfully");
1543
+ expect(result).toContain("batch_123");
1544
+ });
1545
+ });
1546
+
1547
+ describe("platform_archive_suite", () => {
1548
+ it("archives the suite", async () => {
1549
+ const { handleArchiveSuite } = await import("../tools/archive-suite.js");
1550
+ const result = await handleArchiveSuite({ id: "suite_abc" });
1551
+
1552
+ expect(result).toContain("archived");
1553
+ });
1554
+ });
1555
+
1556
+ // =====================
1557
+ // Simulation Run Tools
1558
+ // =====================
1559
+ describe("platform_list_simulation_runs", () => {
1560
+ it("returns formatted run list", async () => {
1561
+ const { handleListSimulationRuns } = await import("../tools/list-simulation-runs.js");
1562
+ const result = await handleListSimulationRuns({});
1563
+
1564
+ expect(result).toContain("Simulation Runs");
1565
+ expect(result).toContain("Login Flow");
1566
+ });
1567
+ });
1568
+
1569
+ describe("platform_get_simulation_run", () => {
1570
+ it("returns run details with conversation", async () => {
1571
+ const { handleGetSimulationRun } = await import("../tools/get-simulation-run.js");
1572
+ const result = await handleGetSimulationRun({ scenarioRunId: "run_abc" });
1573
+
1574
+ expect(result).toContain("Login Flow");
1575
+ expect(result).toContain("passed");
1576
+ expect(result).toContain("Hello");
1577
+ });
1578
+ });
1579
+
1580
+ // =====================
1581
+ // Monitor Tools
1582
+ // =====================
1583
+ describe("platform_list_monitors", () => {
1584
+ it("returns formatted monitor list", async () => {
1585
+ const { listMonitors } = await import("../langwatch-api-monitors.js");
1586
+ const monitors = await listMonitors();
1587
+ expect(monitors).toHaveLength(1);
1588
+ expect(monitors[0]!.name).toBe("Toxicity Check");
1589
+ expect(monitors[0]!.checkType).toBe("ragas/toxicity");
1590
+ });
1591
+ });
1592
+
1593
+ describe("platform_get_monitor", () => {
1594
+ it("returns monitor details", async () => {
1595
+ const { getMonitor } = await import("../langwatch-api-monitors.js");
1596
+ const monitor = await getMonitor("mon_abc");
1597
+ expect(monitor.id).toBe("mon_abc");
1598
+ expect(monitor.name).toBe("Toxicity Check");
1599
+ expect(monitor.enabled).toBe(true);
1600
+ });
1601
+ });
1602
+
1603
+ describe("platform_create_monitor", () => {
1604
+ it("creates a monitor and returns metadata", async () => {
1605
+ const { createMonitor } = await import("../langwatch-api-monitors.js");
1606
+ const monitor = await createMonitor({ name: "New Monitor", checkType: "ragas/toxicity" });
1607
+ expect(monitor.id).toBe("mon_new");
1608
+ expect(monitor.name).toBe("New Monitor");
1609
+ });
1610
+ });
1611
+
1612
+ describe("platform_update_monitor", () => {
1613
+ it("updates a monitor", async () => {
1614
+ const { updateMonitor } = await import("../langwatch-api-monitors.js");
1615
+ const monitor = await updateMonitor({ id: "mon_abc", enabled: false });
1616
+ expect(monitor.id).toBe("mon_abc");
1617
+ expect(monitor.enabled).toBe(false);
1618
+ });
1619
+ });
1620
+
1621
+ describe("platform_delete_monitor", () => {
1622
+ it("deletes a monitor", async () => {
1623
+ const { deleteMonitor } = await import("../langwatch-api-monitors.js");
1624
+ const result = await deleteMonitor("mon_abc");
1625
+ expect(result.id).toBe("mon_abc");
1626
+ expect(result.deleted).toBe(true);
1627
+ });
1628
+ });
1629
+
1630
+ // =====================
1631
+ // Secret Tools
1632
+ // =====================
1633
+ describe("platform_list_secrets", () => {
1634
+ it("returns formatted secret list", async () => {
1635
+ const { listSecrets } = await import("../langwatch-api-secrets.js");
1636
+ const secrets = await listSecrets();
1637
+ expect(secrets).toHaveLength(2);
1638
+ expect(secrets[0]!.name).toBe("MY_API_KEY");
1639
+ });
1640
+ });
1641
+
1642
+ describe("platform_create_secret", () => {
1643
+ it("creates a secret and returns metadata", async () => {
1644
+ const { createSecret } = await import("../langwatch-api-secrets.js");
1645
+ const secret = await createSecret({ name: "NEW_SECRET", value: "sk-123" });
1646
+ expect(secret.id).toBe("secret_new");
1647
+ expect(secret.name).toBe("NEW_SECRET");
1648
+ });
1649
+ });
1650
+
1651
+ describe("platform_update_secret", () => {
1652
+ it("updates a secret value", async () => {
1653
+ const { updateSecret } = await import("../langwatch-api-secrets.js");
1654
+ const secret = await updateSecret({ id: "secret_abc", value: "new-val" });
1655
+ expect(secret.id).toBe("secret_abc");
1656
+ expect(secret.name).toBe("MY_API_KEY");
1657
+ });
1658
+ });
1659
+
1660
+ describe("platform_delete_secret", () => {
1661
+ it("deletes a secret", async () => {
1662
+ const { deleteSecret } = await import("../langwatch-api-secrets.js");
1663
+ const result = await deleteSecret("secret_abc");
1664
+ expect(result.id).toBe("secret_abc");
1665
+ expect(result.deleted).toBe(true);
1666
+ });
1667
+ });
1668
+
1669
+ // =====================
1670
+ // Agent Run Tool
1671
+ // =====================
1672
+ describe("platform_run_workflow", () => {
1673
+ it("executes a workflow and returns result", async () => {
1674
+ const { handleRunWorkflow } = await import("../tools/run-workflow.js");
1675
+ const result = await handleRunWorkflow({ id: "wf_abc" });
1676
+
1677
+ expect(result).toContain("executed successfully");
1678
+ expect(result).toContain("workflow result");
1679
+ });
1680
+ });
1337
1681
  });
@@ -1,5 +1,10 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
- import { initConfig, getConfig, requireApiKey } from "../config.js";
2
+ import {
3
+ initConfig,
4
+ getConfig,
5
+ requireApiKey,
6
+ runWithConfig,
7
+ } from "../config.js";
3
8
 
4
9
  describe("config", () => {
5
10
  let originalApiKey: string | undefined;
@@ -81,9 +86,84 @@ describe("config", () => {
81
86
 
82
87
  describe("getConfig", () => {
83
88
  it("throws when config has not been initialized", async () => {
89
+ // globalThis survives vi.resetModules(), so clear it explicitly
90
+ delete (globalThis as Record<string, unknown>).__langwatch_mcp_config;
91
+ delete (globalThis as Record<string, unknown>).__langwatch_mcp_config_storage;
84
92
  vi.resetModules();
85
93
  const freshConfig = await import("../config.js");
86
94
  expect(() => freshConfig.getConfig()).toThrow("Config not initialized");
87
95
  });
88
96
  });
97
+
98
+ describe("runWithConfig()", () => {
99
+ it("overrides global config within the callback", () => {
100
+ initConfig({ apiKey: "global-key" });
101
+
102
+ runWithConfig(
103
+ { apiKey: "session-key", endpoint: "https://session.example.com" },
104
+ () => {
105
+ expect(getConfig().apiKey).toBe("session-key");
106
+ expect(getConfig().endpoint).toBe("https://session.example.com");
107
+ }
108
+ );
109
+ });
110
+
111
+ it("restores global config after the callback completes", () => {
112
+ initConfig({ apiKey: "global-key" });
113
+
114
+ runWithConfig(
115
+ { apiKey: "session-key", endpoint: "https://session.example.com" },
116
+ () => {
117
+ // inside: session config
118
+ }
119
+ );
120
+
121
+ expect(getConfig().apiKey).toBe("global-key");
122
+ });
123
+
124
+ it("isolates concurrent async contexts", async () => {
125
+ initConfig({ apiKey: "global-key" });
126
+
127
+ const results: string[] = [];
128
+
129
+ await Promise.all([
130
+ runWithConfig(
131
+ { apiKey: "key-a", endpoint: "https://a.example.com" },
132
+ async () => {
133
+ await new Promise((r) => setTimeout(r, 10));
134
+ results.push(requireApiKey());
135
+ }
136
+ ),
137
+ runWithConfig(
138
+ { apiKey: "key-b", endpoint: "https://b.example.com" },
139
+ async () => {
140
+ await new Promise((r) => setTimeout(r, 5));
141
+ results.push(requireApiKey());
142
+ }
143
+ ),
144
+ ]);
145
+
146
+ expect(results).toContain("key-a");
147
+ expect(results).toContain("key-b");
148
+ });
149
+
150
+ it("returns the callback result", () => {
151
+ initConfig({});
152
+ const result = runWithConfig(
153
+ { apiKey: "key", endpoint: "https://example.com" },
154
+ () => 42
155
+ );
156
+ expect(result).toBe(42);
157
+ });
158
+
159
+ it("makes requireApiKey() use the scoped key", () => {
160
+ initConfig({});
161
+ runWithConfig(
162
+ { apiKey: "scoped-key", endpoint: "https://example.com" },
163
+ () => {
164
+ expect(requireApiKey()).toBe("scoped-key");
165
+ }
166
+ );
167
+ });
168
+ });
89
169
  });