@langwatch/mcp-server 2.0.0 → 2.2.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 (430) hide show
  1. package/.gitignore +7 -0
  2. package/.npmignore +41 -0
  3. package/CHANGELOG.md +280 -200
  4. package/CONTRIBUTING.md +10 -0
  5. package/README.md +82 -52
  6. package/dist/add-dashboard-widget-4ZC5UN73.js +50 -0
  7. package/dist/add-dashboard-widget-ACC6QYBG.js +49 -0
  8. package/dist/archive-project-KWFM3I5Z.js +19 -0
  9. package/dist/{archive-project-4EQGLBJJ.js → archive-project-SPVHXK6A.js} +4 -4
  10. package/dist/archive-run-plan-65QN3XRX.js +16 -0
  11. package/dist/archive-run-plan-Z4SYE6BP.js +15 -0
  12. package/dist/{archive-scenario-W6BXYDZ3.js → archive-scenario-A6QV5LOW.js} +4 -4
  13. package/dist/archive-scenario-FXEYQNEH.js +19 -0
  14. package/dist/archive-test-suite-5ADQ3KPQ.js +15 -0
  15. package/dist/archive-test-suite-NKZDZY3D.js +16 -0
  16. package/dist/assign-prompt-tag-5IUZXJ2T.js +23 -0
  17. package/dist/{assign-prompt-tag-VLXUWEKU.js → assign-prompt-tag-HBZLBIFD.js} +8 -4
  18. package/dist/{chunk-CDYQOY5U.js → chunk-3OCG3QPU.js} +7 -10
  19. package/dist/chunk-3YLN3ATJ.js +241 -0
  20. package/dist/chunk-5FNCQRCX.js +91 -0
  21. package/dist/chunk-6YZUOD34.js +55 -0
  22. package/dist/{chunk-WVI5CJ6S.js → chunk-7AQLPVFL.js} +2 -2
  23. package/dist/chunk-7VNSRCXO.js +129 -0
  24. package/dist/{chunk-7RHG56OH.js → chunk-B4J3PJLX.js} +11 -10
  25. package/dist/{chunk-ZXKLPC2E.js → chunk-BIIKE3EI.js} +1 -1
  26. package/dist/{chunk-37SMPMQS.js → chunk-DMM3JQ42.js} +7 -2
  27. package/dist/{chunk-NUFPTHKU.js → chunk-FOKVY3BK.js} +1 -1
  28. package/dist/{chunk-WT4PO4A3.js → chunk-H6WGMG2T.js} +10 -7
  29. package/dist/{chunk-TOXJQDIZ.js → chunk-HGELNIVY.js} +135 -51
  30. package/dist/{chunk-UJPXFA26.js → chunk-HHMNZAPD.js} +101 -74
  31. package/dist/{chunk-J42FWRJ2.js → chunk-I7ATIZED.js} +7 -7
  32. package/dist/{chunk-AQJO2EI5.js → chunk-ILZDO25L.js} +7 -10
  33. package/dist/{chunk-4QC7SA2L.js → chunk-J7PQKBLD.js} +884 -559
  34. package/dist/{chunk-YX3A3CFM.js → chunk-JRJPH75H.js} +7 -13
  35. package/dist/chunk-K2I7Z3LZ.js +28 -0
  36. package/dist/{chunk-5CCZFUXS.js → chunk-NKMTIHIJ.js} +6 -9
  37. package/dist/chunk-O7EMWEVJ.js +44 -0
  38. package/dist/chunk-PTDR56Z3.js +37 -0
  39. package/dist/chunk-RABYBFX5.js +43 -0
  40. package/dist/chunk-SUPUUC4W.js +64 -0
  41. package/dist/{chunk-SFB4MU6H.js → chunk-SXFC3FNH.js} +4 -4
  42. package/dist/chunk-U24EACC4.js +67 -0
  43. package/dist/chunk-UNJM3BIL.js +21 -0
  44. package/dist/{chunk-ZWIL4EFL.js → chunk-UTJCWXPZ.js} +4 -4
  45. package/dist/chunk-UWLE5T2F.js +312 -0
  46. package/dist/chunk-VMWJD732.js +57 -0
  47. package/dist/chunk-XUIJYNRJ.js +15 -0
  48. package/dist/chunk-Y34DHR5K.js +67 -0
  49. package/dist/chunk-YCARO42Z.js +516 -0
  50. package/dist/config-EAKW6F26.js +16 -0
  51. package/dist/config.cjs +109 -0
  52. package/dist/config.js +5 -4
  53. package/dist/{create-agent-7XHO6UIS.js → create-agent-AYZ3LB3V.js} +4 -4
  54. package/dist/create-agent-S5ZBCEFT.js +23 -0
  55. package/dist/create-annotation-LHNCLHYP.js +29 -0
  56. package/dist/{create-annotation-YQXRQC6A.js → create-annotation-VUSXULQK.js} +10 -5
  57. package/dist/{create-api-key-YUS36KBU.js → create-api-key-NNYKARMG.js} +5 -7
  58. package/dist/create-api-key-T4KSNPKB.js +24 -0
  59. package/dist/create-dashboard-DHVFMEU4.js +18 -0
  60. package/dist/{create-dashboard-4Z5JO5LB.js → create-dashboard-WTKKS5Z6.js} +4 -4
  61. package/dist/{create-dataset-E6NMSVX7.js → create-dataset-2MC4SD4O.js} +6 -6
  62. package/dist/create-dataset-LUP5PVDM.js +22 -0
  63. package/dist/create-dataset-records-NSKBZP3G.js +20 -0
  64. package/dist/{create-dataset-records-SKHPRD2K.js → create-dataset-records-W4GLN2FV.js} +5 -7
  65. package/dist/{create-evaluator-JXF5PF33.js → create-evaluator-643G6LXL.js} +4 -4
  66. package/dist/create-evaluator-HMTYO5SS.js +27 -0
  67. package/dist/create-mcp-server.cjs +9513 -0
  68. package/dist/create-mcp-server.js +2647 -5
  69. package/dist/create-project-6N7PIYRY.js +44 -0
  70. package/dist/{create-project-CS2CB57Y.js → create-project-KAIC46UY.js} +4 -4
  71. package/dist/{create-prompt-F5RMX3L3.js → create-prompt-E67LXIYT.js} +5 -7
  72. package/dist/create-prompt-PKJ7EDRR.js +37 -0
  73. package/dist/create-prompt-tag-4YO3FSJ3.js +17 -0
  74. package/dist/{create-prompt-tag-GMRWTEFR.js → create-prompt-tag-OPQUEXWB.js} +3 -3
  75. package/dist/create-scenario-5PJBMAXO.js +33 -0
  76. package/dist/{create-scenario-JDR2VB2H.js → create-scenario-UEM77OE7.js} +11 -4
  77. package/dist/create-test-suite-4IPXVSFQ.js +37 -0
  78. package/dist/create-test-suite-SZEPVXSE.js +36 -0
  79. package/dist/create-trigger-5HZSCRAN.js +49 -0
  80. package/dist/create-trigger-PRELMT6J.js +48 -0
  81. package/dist/delete-agent-JUIZWAXG.js +18 -0
  82. package/dist/{delete-agent-O3EEH4S5.js → delete-agent-QMEK3XHK.js} +4 -4
  83. package/dist/delete-annotation-HY3O2OGR.js +17 -0
  84. package/dist/{delete-annotation-XP6FQM7A.js → delete-annotation-VKIQWUYW.js} +4 -4
  85. package/dist/delete-dashboard-76N66XXL.js +18 -0
  86. package/dist/{delete-dashboard-RFUNYCNP.js → delete-dashboard-JVIVHNJM.js} +4 -4
  87. package/dist/delete-dataset-HQCR66MG.js +18 -0
  88. package/dist/{delete-dataset-3MJFRDLP.js → delete-dataset-WKRUW4FP.js} +5 -7
  89. package/dist/{delete-dataset-records-WPMU5GL5.js → delete-dataset-records-C2IQFFVQ.js} +5 -7
  90. package/dist/delete-dataset-records-SDUHQLLM.js +19 -0
  91. package/dist/delete-evaluator-4SNSCLEE.js +16 -0
  92. package/dist/delete-evaluator-QP3QHTLN.js +15 -0
  93. package/dist/delete-prompt-tag-NZ4JQMYG.js +17 -0
  94. package/dist/{delete-prompt-tag-KNMZVSPB.js → delete-prompt-tag-YYQ4VMAD.js} +3 -3
  95. package/dist/{delete-workflow-3NUXO46U.js → delete-workflow-5J46T73T.js} +4 -4
  96. package/dist/delete-workflow-JSWABL5F.js +17 -0
  97. package/dist/discover-evaluator-schema-UHWA5HVL.js +120 -0
  98. package/dist/discover-evaluator-schema-XKCLLCB3.js +118 -0
  99. package/dist/discover-scenario-schema-NOJY6S7E.js +137 -0
  100. package/dist/discover-scenario-schema-OOFIQKOG.js +139 -0
  101. package/dist/{discover-schema-L6QDPON3.js → discover-schema-55YJBFGQ.js} +181 -186
  102. package/dist/discover-schema-A33SLMXW.js +442 -0
  103. package/dist/get-agent-LJQX7OF4.js +69 -0
  104. package/dist/get-agent-OTIYUFZA.js +70 -0
  105. package/dist/get-analytics-DCS72353.js +80 -0
  106. package/dist/get-analytics-IQZKJHLW.js +79 -0
  107. package/dist/get-dashboard-5NYJEIX2.js +29 -0
  108. package/dist/{get-dashboard-KHBEGZGL.js → get-dashboard-QMTYDQAZ.js} +4 -4
  109. package/dist/get-dataset-ALSP7RTX.js +64 -0
  110. package/dist/get-dataset-X4B5JDG2.js +63 -0
  111. package/dist/{get-evaluator-ADMKVWYZ.js → get-evaluator-ISMC4LLI.js} +4 -4
  112. package/dist/get-evaluator-XEUZGZCH.js +47 -0
  113. package/dist/{get-project-RAK7URQJ.js → get-project-5VKROSDH.js} +4 -4
  114. package/dist/get-project-RM6FRCBK.js +26 -0
  115. package/dist/get-prompt-OWXJZHQQ.js +95 -0
  116. package/dist/get-prompt-W52DY36E.js +94 -0
  117. package/dist/get-run-plan-BE3M2AUX.js +59 -0
  118. package/dist/get-run-plan-RUXQ6PAX.js +58 -0
  119. package/dist/get-scenario-RNM7BAX2.js +37 -0
  120. package/dist/{get-scenario-ZTAJJL5T.js → get-scenario-YM4ODWW6.js} +8 -4
  121. package/dist/get-simulation-run-6GIATQE5.js +69 -0
  122. package/dist/{get-simulation-run-T2ZQWYH2.js → get-simulation-run-LCFSMPGQ.js} +33 -20
  123. package/dist/get-test-suite-GC2O7YH6.js +49 -0
  124. package/dist/get-test-suite-P5LJ6VLF.js +48 -0
  125. package/dist/get-trace-KBRVV4NN.js +45 -0
  126. package/dist/{get-trace-K7U234Q4.js → get-trace-SEF3FMR5.js} +11 -13
  127. package/dist/get-workflow-7PVED7JE.js +25 -0
  128. package/dist/{get-workflow-L5L2PSK3.js → get-workflow-YXULW4GC.js} +4 -4
  129. package/dist/{http-security-EU3CHTY7.js → http-security-DNLBNQZL.js} +2 -2
  130. package/dist/{http-server-YXHFPPGA.js → http-server-IR4QOS43.js} +4071 -342
  131. package/dist/index.js +60221 -38077
  132. package/dist/langwatch-api-annotations-6SA4OB4C.js +17 -0
  133. package/dist/{langwatch-api-annotations-FDAS6NQ7.js → langwatch-api-annotations-TBHLCQ5R.js} +4 -4
  134. package/dist/langwatch-api-dashboards-6EO2VVZW.js +17 -0
  135. package/dist/{langwatch-api-dashboards-JTVCS7ZJ.js → langwatch-api-dashboards-NHU5BZD7.js} +4 -4
  136. package/dist/langwatch-api-monitors-CBF3FGIM.js +47 -0
  137. package/dist/{langwatch-api-monitors-3J7UIQME.js → langwatch-api-monitors-YNWA2M5Y.js} +11 -16
  138. package/dist/langwatch-api-secrets-ADTOJELP.js +56 -0
  139. package/dist/langwatch-api-secrets-RNFO4LF2.js +55 -0
  140. package/dist/langwatch-api-triggers-KQDQC6L5.js +22 -0
  141. package/dist/langwatch-api-triggers-QS7HDIVW.js +23 -0
  142. package/dist/{langwatch-api-workflows-NBJX5KOG.js → langwatch-api-workflows-OKC4FXIF.js} +4 -4
  143. package/dist/langwatch-api-workflows-XGJE6SR5.js +15 -0
  144. package/dist/list-agents-HVYWEQMI.js +43 -0
  145. package/dist/list-agents-RBAOCF53.js +42 -0
  146. package/dist/list-annotations-HJH4NBLD.js +38 -0
  147. package/dist/{list-annotations-ZTVP4EEL.js → list-annotations-J4UAHSUN.js} +10 -5
  148. package/dist/{list-api-keys-CWD4UQT5.js → list-api-keys-7UTJRZDG.js} +10 -5
  149. package/dist/list-api-keys-A2PMV435.js +42 -0
  150. package/dist/list-dashboard-boards-OPWROHMG.js +33 -0
  151. package/dist/list-dashboard-boards-U2GJ7GC5.js +34 -0
  152. package/dist/{list-dashboards-PWG6P6KR.js → list-dashboards-ROSYFLDQ.js} +4 -4
  153. package/dist/list-dashboards-RQ635ZU7.js +32 -0
  154. package/dist/{list-dataset-records-IOE7LQXS.js → list-dataset-records-M5TKA6LK.js} +14 -10
  155. package/dist/list-dataset-records-WFM7F33C.js +43 -0
  156. package/dist/{list-datasets-ZT4JYUDT.js → list-datasets-B5X4P7BQ.js} +9 -13
  157. package/dist/list-datasets-THUKQHPX.js +56 -0
  158. package/dist/{list-evaluators-SJHC7VBH.js → list-evaluators-MYH2OVCI.js} +5 -7
  159. package/dist/list-evaluators-NLVWF6K7.js +32 -0
  160. package/dist/{list-model-providers-PPD2WPD2.js → list-model-providers-3JUGP4YI.js} +4 -4
  161. package/dist/list-model-providers-H3XKORFO.js +35 -0
  162. package/dist/{list-projects-K7QAIX2I.js → list-projects-6EJ5YYEW.js} +5 -7
  163. package/dist/list-projects-YNNSRMLC.js +40 -0
  164. package/dist/{list-prompt-tags-FOO6TNHB.js → list-prompt-tags-NVFQO6WX.js} +3 -3
  165. package/dist/list-prompt-tags-XUQTTAH4.js +23 -0
  166. package/dist/{list-prompts-SC2HAUFG.js → list-prompts-HSIMMHTF.js} +4 -6
  167. package/dist/list-prompts-IRI6AIXO.js +30 -0
  168. package/dist/list-run-plans-PX2XI6EK.js +46 -0
  169. package/dist/list-run-plans-QALLCN2K.js +45 -0
  170. package/dist/list-scenarios-QZRPVHS4.js +46 -0
  171. package/dist/list-scenarios-SSJQEMJP.js +45 -0
  172. package/dist/list-simulation-runs-KNOLGMDV.js +53 -0
  173. package/dist/{list-simulation-runs-KF5THG5H.js → list-simulation-runs-OEADLLQR.js} +19 -9
  174. package/dist/list-test-suites-ANAQBLDG.js +34 -0
  175. package/dist/list-test-suites-N4WVLBKK.js +35 -0
  176. package/dist/list-trigger-fires-JV3WFYYG.js +19 -0
  177. package/dist/list-trigger-fires-WTW4DVIW.js +20 -0
  178. package/dist/list-triggers-SQNIG35Q.js +45 -0
  179. package/dist/list-triggers-V4Y356AK.js +46 -0
  180. package/dist/{list-workflows-QSJWURCK.js → list-workflows-IMSX7SXU.js} +5 -7
  181. package/dist/list-workflows-ZHTX5LCL.js +33 -0
  182. package/dist/{rename-prompt-tag-EKWU5QF3.js → rename-prompt-tag-IQI7MHOI.js} +3 -3
  183. package/dist/rename-prompt-tag-PA2BRIE6.js +18 -0
  184. package/dist/rename-test-suite-4J7UX5S2.js +21 -0
  185. package/dist/rename-test-suite-NNVLMLKY.js +20 -0
  186. package/dist/report-issue-AS34ZMJF.js +898 -0
  187. package/dist/report-issue-DCZFLGDO.js +899 -0
  188. package/dist/rerun-run-plan-TG2V4WM4.js +22 -0
  189. package/dist/rerun-run-plan-ZI5OY3VA.js +21 -0
  190. package/dist/revoke-api-key-3J3ZA4UB.js +15 -0
  191. package/dist/{revoke-api-key-FTRTZMOF.js → revoke-api-key-SFMEFWJC.js} +4 -4
  192. package/dist/run-agent-W4VJP72Z.js +75 -0
  193. package/dist/run-agent-YNV6ZHW4.js +74 -0
  194. package/dist/run-plan-6C77GWG4.js +38 -0
  195. package/dist/run-plan-PAARQSI4.js +39 -0
  196. package/dist/run-query-KJDH544C.js +47 -0
  197. package/dist/run-query-T4QW2Z5O.js +46 -0
  198. package/dist/run-test-suite-SQJELEZX.js +26 -0
  199. package/dist/run-test-suite-STB6NUJM.js +25 -0
  200. package/dist/{run-workflow-QPXC627O.js → run-workflow-D6PQNBI6.js} +4 -4
  201. package/dist/{run-agent-6SM4XNA7.js → run-workflow-SP32SO5M.js} +10 -11
  202. package/dist/search-traces-HCBGOG6R.js +170 -0
  203. package/dist/search-traces-HSTQ6WTQ.js +171 -0
  204. package/dist/{set-model-provider-3WKT63ZN.js → set-model-provider-GF7NXUJA.js} +4 -4
  205. package/dist/set-model-provider-KDQ7C4EZ.js +33 -0
  206. package/dist/{update-agent-SMAEHLXH.js → update-agent-DWG43T2P.js} +4 -4
  207. package/dist/update-agent-MXNOBS4H.js +20 -0
  208. package/dist/update-dashboard-widget-CJKJLCDY.js +71 -0
  209. package/dist/update-dashboard-widget-CYQDS2NR.js +70 -0
  210. package/dist/update-dataset-DQH3OHOU.js +22 -0
  211. package/dist/{update-dataset-BDUK7TUO.js → update-dataset-SQZ6T4WX.js} +6 -6
  212. package/dist/{update-dataset-record-4ZSDRBSW.js → update-dataset-record-3AT4IPBC.js} +4 -4
  213. package/dist/update-dataset-record-7PLDGTMR.js +19 -0
  214. package/dist/update-evaluator-4LFTKFPT.js +24 -0
  215. package/dist/{update-evaluator-LS6TIBY7.js → update-evaluator-IGTX6PX4.js} +4 -4
  216. package/dist/{update-project-LKVYXENH.js → update-project-PLJIOZS6.js} +4 -4
  217. package/dist/update-project-QVHSJRUP.js +24 -0
  218. package/dist/update-prompt-GDKRYJI3.js +140 -0
  219. package/dist/update-prompt-MY263DYF.js +141 -0
  220. package/dist/update-scenario-7KA3JDXA.js +34 -0
  221. package/dist/{update-scenario-NEXCAQI7.js → update-scenario-MOHRQKAQ.js} +11 -4
  222. package/package.json +39 -44
  223. package/pyproject.toml +8 -8
  224. package/src/config.d.ts +4 -0
  225. package/src/config.ts +20 -29
  226. package/src/create-mcp-server.ts +1021 -596
  227. package/src/documentation-fetch.ts +21 -20
  228. package/src/http-security.ts +166 -135
  229. package/src/http-server.ts +189 -224
  230. package/src/index.ts +14 -16
  231. package/src/langwatch-api-agents.ts +202 -21
  232. package/src/langwatch-api-annotations.ts +11 -12
  233. package/src/langwatch-api-api-keys.ts +8 -9
  234. package/src/langwatch-api-boards.ts +46 -0
  235. package/src/langwatch-api-dashboard-widgets.ts +112 -0
  236. package/src/langwatch-api-dashboards.ts +13 -10
  237. package/src/langwatch-api-datasets.ts +98 -32
  238. package/src/langwatch-api-evaluators.ts +13 -19
  239. package/src/langwatch-api-model-providers.ts +4 -4
  240. package/src/langwatch-api-monitors.ts +14 -18
  241. package/src/langwatch-api-projects.ts +27 -9
  242. package/src/langwatch-api-query.ts +114 -0
  243. package/src/langwatch-api-run-plans.ts +140 -0
  244. package/src/langwatch-api-scenarios.ts +17 -10
  245. package/src/langwatch-api-secrets.ts +31 -24
  246. package/src/langwatch-api-simulation-runs.ts +10 -7
  247. package/src/langwatch-api-test-suites.ts +116 -0
  248. package/src/langwatch-api-triggers.ts +96 -34
  249. package/src/langwatch-api-workflows.ts +9 -9
  250. package/src/langwatch-api.ts +143 -109
  251. package/src/public-http-request.ts +21 -12
  252. package/src/schemas/analytics-metrics.ts +3 -15
  253. package/src/schemas/create-dataset.ts +3 -4
  254. package/src/schemas/run-plan.ts +76 -0
  255. package/src/schemas/suite-fields.ts +132 -0
  256. package/src/schemas/triggers.ts +365 -0
  257. package/src/tools/add-dashboard-widget.ts +63 -0
  258. package/src/tools/archive-project.ts +2 -4
  259. package/src/tools/archive-run-plan.ts +10 -0
  260. package/src/tools/archive-scenario.ts +2 -4
  261. package/src/tools/archive-test-suite.ts +10 -0
  262. package/src/tools/assign-prompt-tag.ts +6 -2
  263. package/src/tools/create-agent.ts +1 -1
  264. package/src/tools/create-annotation.ts +7 -2
  265. package/src/tools/create-api-key.ts +4 -6
  266. package/src/tools/create-dashboard.ts +1 -1
  267. package/src/tools/create-dataset-records.ts +4 -8
  268. package/src/tools/create-dataset.ts +5 -7
  269. package/src/tools/create-evaluator.ts +3 -5
  270. package/src/tools/create-project.ts +2 -2
  271. package/src/tools/create-prompt-tag.ts +2 -4
  272. package/src/tools/create-prompt.ts +9 -18
  273. package/src/tools/create-scenario.ts +11 -5
  274. package/src/tools/create-test-suite.ts +38 -0
  275. package/src/tools/create-trigger.ts +55 -0
  276. package/src/tools/dashboard-exists.ts +25 -0
  277. package/src/tools/dashboard-widget-queries.ts +32 -0
  278. package/src/tools/delete-agent.ts +1 -1
  279. package/src/tools/delete-annotation.ts +1 -1
  280. package/src/tools/delete-dashboard.ts +1 -1
  281. package/src/tools/delete-dataset-records.ts +4 -8
  282. package/src/tools/delete-dataset.ts +3 -7
  283. package/src/tools/delete-evaluator.ts +4 -6
  284. package/src/tools/delete-prompt-tag.ts +2 -4
  285. package/src/tools/delete-workflow.ts +1 -1
  286. package/src/tools/discover-evaluator-schema.ts +34 -25
  287. package/src/tools/discover-scenario-schema.ts +86 -12
  288. package/src/tools/discover-schema.ts +149 -27
  289. package/src/tools/experiment-run-status.ts +2 -8
  290. package/src/tools/format-dataset-mutation.ts +5 -10
  291. package/src/tools/format-run-plan.ts +54 -0
  292. package/src/tools/format-scenario.ts +12 -0
  293. package/src/tools/format-suite-details.ts +90 -0
  294. package/src/tools/get-agent.ts +54 -3
  295. package/src/tools/get-analytics.ts +52 -46
  296. package/src/tools/get-dashboard.ts +1 -1
  297. package/src/tools/get-dataset.ts +34 -16
  298. package/src/tools/get-evaluator.ts +4 -11
  299. package/src/tools/get-experiment-results.ts +219 -186
  300. package/src/tools/get-project.ts +2 -4
  301. package/src/tools/get-prompt.ts +100 -37
  302. package/src/tools/get-run-plan.ts +60 -0
  303. package/src/tools/get-scenario.ts +6 -5
  304. package/src/tools/get-simulation-run.ts +32 -20
  305. package/src/tools/get-test-suite.ts +46 -0
  306. package/src/tools/get-trace.ts +16 -17
  307. package/src/tools/get-workflow.ts +1 -1
  308. package/src/tools/list-agents.ts +20 -3
  309. package/src/tools/list-annotations.ts +8 -5
  310. package/src/tools/list-api-keys.ts +7 -2
  311. package/src/tools/list-dashboard-boards.ts +28 -0
  312. package/src/tools/list-dashboards.ts +1 -1
  313. package/src/tools/list-dataset-records.ts +8 -6
  314. package/src/tools/list-datasets.ts +12 -23
  315. package/src/tools/list-evaluators.ts +4 -8
  316. package/src/tools/list-experiment-runs.ts +5 -9
  317. package/src/tools/list-experiments.ts +5 -11
  318. package/src/tools/list-model-providers.ts +3 -5
  319. package/src/tools/list-projects.ts +2 -4
  320. package/src/tools/list-prompt-tags.ts +3 -3
  321. package/src/tools/list-prompts.ts +4 -8
  322. package/src/tools/list-run-plans.ts +47 -0
  323. package/src/tools/list-scenarios.ts +26 -17
  324. package/src/tools/list-simulation-runs.ts +13 -5
  325. package/src/tools/list-test-suites.ts +35 -0
  326. package/src/tools/list-trigger-fires.ts +17 -0
  327. package/src/tools/list-triggers.ts +39 -0
  328. package/src/tools/list-workflows.ts +2 -4
  329. package/src/tools/rename-prompt-tag.ts +1 -1
  330. package/src/tools/rename-test-suite.ts +15 -0
  331. package/src/tools/report-issue.ts +5 -9
  332. package/src/tools/rerun-run-plan.ts +18 -0
  333. package/src/tools/revoke-api-key.ts +2 -4
  334. package/src/tools/run-agent.ts +65 -14
  335. package/src/tools/run-experiment.ts +36 -37
  336. package/src/tools/run-plan.ts +46 -0
  337. package/src/tools/run-query.ts +47 -0
  338. package/src/tools/run-test-suite.ts +27 -0
  339. package/src/tools/run-workflow.ts +2 -5
  340. package/src/tools/search-traces.ts +161 -47
  341. package/src/tools/set-model-provider.ts +4 -6
  342. package/src/tools/test-agent.ts +18 -0
  343. package/src/tools/update-agent.ts +1 -1
  344. package/src/tools/update-dashboard-widget.ts +97 -0
  345. package/src/tools/update-dataset-record.ts +1 -1
  346. package/src/tools/update-dataset.ts +5 -7
  347. package/src/tools/update-evaluator.ts +3 -5
  348. package/src/tools/update-project.ts +1 -1
  349. package/src/tools/update-prompt.ts +185 -15
  350. package/src/tools/update-scenario.ts +11 -5
  351. package/src/tools/update-test-suite.ts +40 -0
  352. package/src/utils/date-parsing.ts +1 -1
  353. package/src/utils/escape-markdown.ts +3 -8
  354. package/src/utils/format-evaluations.ts +6 -6
  355. package/src/utils/markdown-table.ts +58 -0
  356. package/src/utils/trace-id-shape.ts +20 -0
  357. package/tsconfig.app.json +24 -0
  358. package/tsconfig.json +19 -12
  359. package/uv.lock +416 -296
  360. package/vitest.integration.config.js +10 -0
  361. package/vitest.scenario.config.js +9 -0
  362. package/.eslintrc.cjs +0 -36
  363. package/dist/archive-suite-4T6BSRS7.js +0 -16
  364. package/dist/chunk-77BNWQ3U.js +0 -86
  365. package/dist/chunk-A2LEVYQ5.js +0 -217
  366. package/dist/chunk-TJCXFNDT.js +0 -24
  367. package/dist/chunk-YZLYHLLI.js +0 -48
  368. package/dist/config.d.ts +0 -17
  369. package/dist/create-mcp-server.d.ts +0 -10
  370. package/dist/create-suite-LZS6QIAF.js +0 -39
  371. package/dist/delete-evaluator-33SP2JFX.js +0 -16
  372. package/dist/discover-evaluator-schema-XTAD4X7D.js +0 -2191
  373. package/dist/discover-scenario-schema-FE64QK2D.js +0 -67
  374. package/dist/get-agent-Y7MWZ65F.js +0 -29
  375. package/dist/get-analytics-TETCSOK6.js +0 -73
  376. package/dist/get-dataset-RCMU5VAH.js +0 -49
  377. package/dist/get-prompt-OY5B7TRI.js +0 -46
  378. package/dist/get-suite-J7JVWO6O.js +0 -45
  379. package/dist/index.d.ts +0 -2
  380. package/dist/langwatch-api-secrets-BUBSNCNO.js +0 -41
  381. package/dist/langwatch-api-triggers-EDFUCJN6.js +0 -31
  382. package/dist/list-agents-KFHKGPXV.js +0 -33
  383. package/dist/list-scenarios-F2WYNRIE.js +0 -41
  384. package/dist/list-suites-CRY4VWW6.js +0 -43
  385. package/dist/report-issue-XYVAPAYJ.js +0 -412
  386. package/dist/run-suite-NKZCJ2IN.js +0 -30
  387. package/dist/search-traces-A6HFKPLJ.js +0 -79
  388. package/dist/update-prompt-ODCRQRN6.js +0 -25
  389. package/dist/update-suite-SNO3GS7T.js +0 -40
  390. package/src/__tests__/all-tools.integration.test.ts +0 -1626
  391. package/src/__tests__/config.unit.test.ts +0 -202
  392. package/src/__tests__/datasets.integration.test.ts +0 -620
  393. package/src/__tests__/datasets.unit.test.ts +0 -244
  394. package/src/__tests__/date-parsing.unit.test.ts +0 -78
  395. package/src/__tests__/discover-evaluator-schema.unit.test.ts +0 -89
  396. package/src/__tests__/discover-schema.unit.test.ts +0 -118
  397. package/src/__tests__/docker.integration.test.ts +0 -318
  398. package/src/__tests__/documentation-fetch-security.integration.test.ts +0 -182
  399. package/src/__tests__/evaluator-tools.unit.test.ts +0 -262
  400. package/src/__tests__/experiment-list-tools.unit.test.ts +0 -157
  401. package/src/__tests__/experiment-status.unit.test.ts +0 -194
  402. package/src/__tests__/feature-map-tool-drift.unit.test.ts +0 -93
  403. package/src/__tests__/get-experiment-results.unit.test.ts +0 -313
  404. package/src/__tests__/http-security-origin.unit.test.ts +0 -97
  405. package/src/__tests__/http-security-sessions.unit.test.ts +0 -209
  406. package/src/__tests__/http-security-verifier.unit.test.ts +0 -248
  407. package/src/__tests__/http-server-allocation.integration.test.ts +0 -279
  408. package/src/__tests__/http-server-origin.integration.test.ts +0 -169
  409. package/src/__tests__/http-server-session-auth.integration.test.ts +0 -341
  410. package/src/__tests__/http-transport.integration.test.ts +0 -395
  411. package/src/__tests__/integration.integration.test.ts +0 -288
  412. package/src/__tests__/langwatch-api.unit.test.ts +0 -661
  413. package/src/__tests__/model-provider-tools.unit.test.ts +0 -190
  414. package/src/__tests__/project-api-key-tools.unit.test.ts +0 -451
  415. package/src/__tests__/public-http-request-security.integration.test.ts +0 -118
  416. package/src/__tests__/report-issue.integration.test.ts +0 -147
  417. package/src/__tests__/scenario-tools.integration.test.ts +0 -286
  418. package/src/__tests__/scenario-tools.unit.test.ts +0 -188
  419. package/src/__tests__/schemas.unit.test.ts +0 -85
  420. package/src/__tests__/support/http-server-harness.ts +0 -138
  421. package/src/__tests__/tools.unit.test.ts +0 -924
  422. package/src/langwatch-api-suites.ts +0 -118
  423. package/src/schemas/filter-fields.ts +0 -119
  424. package/src/schemas/index.ts +0 -4
  425. package/src/tools/archive-suite.ts +0 -12
  426. package/src/tools/create-suite.ts +0 -42
  427. package/src/tools/get-suite.ts +0 -47
  428. package/src/tools/list-suites.ts +0 -43
  429. package/src/tools/run-suite.ts +0 -28
  430. package/src/tools/update-suite.ts +0 -44
@@ -0,0 +1,76 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Typed inputs for the run plan tools, mirroring the platform's REST
5
+ * validation (`suiteScopeSchema` and `suiteTargetSchema`) so an agent
6
+ * learns what's accepted before the request goes out.
7
+ */
8
+
9
+ /** What a run plan covers. */
10
+ export const runPlanScopeSchema = z.discriminatedUnion("mode", [
11
+ z.object({ mode: z.literal("all") }).describe("Every non-archived scenario of the project"),
12
+ z
13
+ .object({
14
+ mode: z.literal("test_suites"),
15
+ testSuiteIds: z.array(z.string()).describe("The test suite IDs whose scenarios to run"),
16
+ })
17
+ .describe("The non-archived scenarios filed in the given test suites"),
18
+ z
19
+ .object({
20
+ mode: z.literal("labels"),
21
+ labels: z.array(z.string()).describe("The labels to select scenarios by"),
22
+ })
23
+ .describe("The non-archived scenarios carrying at least one of the given labels"),
24
+ z
25
+ .object({ mode: z.literal("scenarios") })
26
+ .describe("The scenarios named in scenarioIds, picked by hand"),
27
+ ]);
28
+
29
+ /** The values a run supplies for the parameters its scenarios declare. */
30
+ export const runParametersSchema = z.record(
31
+ z.string(),
32
+ z.union([z.string(), z.number(), z.boolean()]),
33
+ );
34
+
35
+ /**
36
+ * One thing a plan runs its scenarios against, and the values it alone
37
+ * runs with. Parameters make a comparison run: two targets may name the
38
+ * same agent with different values, each run and reported on its own.
39
+ */
40
+ export const runPlanTargetSchema = z.object({
41
+ type: z
42
+ .enum(["connected", "prompt", "http", "code", "workflow"])
43
+ .describe(
44
+ "What kind of thing the target is. A connected agent is one that registered itself from code with connectAgent (TypeScript) or connect_agent (Python).",
45
+ ),
46
+ referenceId: z
47
+ .string()
48
+ .describe(
49
+ "The ID of the prompt, agent or workflow to run against. A connected agent may also be named as <name>@<environment>, for example support-agent@production; the platform resolves it.",
50
+ ),
51
+ parameters: runParametersSchema
52
+ .optional()
53
+ .describe(
54
+ "Parameter values this target alone runs with, by name. They override the run-level parameters for this target. Name the same agent twice with different values here to compare it on two models.",
55
+ ),
56
+ });
57
+
58
+ /**
59
+ * One target as `/api/v1` carries it. Tool input spells per-target
60
+ * values `parameters`, beside run-level `parameters`; the REST body
61
+ * spells them `runParameters` since a target holds the values it runs with.
62
+ */
63
+ export interface RunPlanTargetWire {
64
+ type: z.infer<typeof runPlanTargetSchema>["type"];
65
+ referenceId: string;
66
+ runParameters?: z.infer<typeof runParametersSchema>;
67
+ }
68
+
69
+ /** The targets of a tool call, as the REST body carries them. */
70
+ export function toWireTargets(targets: z.infer<typeof runPlanTargetSchema>[]): RunPlanTargetWire[] {
71
+ return targets.map((target) => ({
72
+ type: target.type,
73
+ referenceId: target.referenceId,
74
+ ...(target.parameters ? { runParameters: target.parameters } : {}),
75
+ }));
76
+ }
@@ -0,0 +1,132 @@
1
+ import { z } from "zod";
2
+
3
+ // Typed inputs for suite fields, evaluators, and scenario values that mirror the
4
+ // server-side validation schemas so agents know what's accepted before sending.
5
+
6
+ /** The value types a field can hold. */
7
+ export const SUITE_FIELD_TYPES = ["text", "number", "boolean"] as const;
8
+
9
+ /** One field a test suite declares. */
10
+ export const suiteFieldSchema = z.object({
11
+ identifier: z
12
+ .string()
13
+ .min(1)
14
+ .max(64)
15
+ .regex(/^[a-z][a-z0-9_]*$/, "Lowercase letters, digits and underscores, starting with a letter")
16
+ .describe(
17
+ "The field name, as scenarios and evaluator mappings address it, for example golden_sql. Lowercase letters, digits and underscores, starting with a letter. situation, criteria, name, input and output are reserved.",
18
+ ),
19
+ type: z.enum(SUITE_FIELD_TYPES).describe("The value type every scenario carries for this field."),
20
+ });
21
+
22
+ /** The fields a test suite declares, in the order the platform shows them. */
23
+ export const suiteFieldsSchema = z
24
+ .array(suiteFieldSchema)
25
+ .max(30)
26
+ .describe(
27
+ "The fields the test suite declares, in order. Up to 30. Every scenario filed in the suite carries one value per field, and an evaluator attached to the suite reads them through its mappings.",
28
+ );
29
+
30
+ /** The sources an evaluator input can read from. */
31
+ export const SCENARIO_MAPPING_SOURCE_IDS = ["conversation", "scenario", "trace"] as const;
32
+
33
+ /** Where one evaluator input reads its value: a source path or a literal. */
34
+ export const scenarioMappingSchema = z
35
+ .discriminatedUnion("type", [
36
+ z.object({
37
+ type: z.literal("source"),
38
+ sourceId: z
39
+ .enum(SCENARIO_MAPPING_SOURCE_IDS)
40
+ .describe(
41
+ "conversation, scenario or trace. See discover_schema({ category: 'scenarios' }) for the paths each one offers.",
42
+ ),
43
+ path: z
44
+ .array(z.string().min(1).max(256))
45
+ .min(1)
46
+ .max(3)
47
+ .describe(
48
+ "The path inside the source. conversation: [first_user_message] | [last_agent_message] | [transcript] | [messages]. scenario: [situation] | [criteria] | [fields, <identifier>]. trace: [contexts] | [tool_calls, <toolName>, input|output].",
49
+ ),
50
+ }),
51
+ z.object({
52
+ type: z.literal("value"),
53
+ value: z.string().max(65_536).describe("A literal the input reads."),
54
+ }),
55
+ ])
56
+ .describe(
57
+ "Where one evaluator input reads its value: a path into the conversation, the scenario or the trace, or a literal value.",
58
+ );
59
+
60
+ /** One evaluator attached to a test suite or a run plan. */
61
+ export const evaluatorAttachmentSchema = z.object({
62
+ id: z
63
+ .string()
64
+ .min(1)
65
+ .max(64)
66
+ .optional()
67
+ .describe(
68
+ "The attachment id, stable across edits. Leave it out on a new attachment and one is generated.",
69
+ ),
70
+ evaluatorId: z
71
+ .string()
72
+ .min(1)
73
+ .describe(
74
+ "The id of the saved evaluator to run. Read it from platform_list_evaluators or platform_get_evaluator.",
75
+ ),
76
+ required: z
77
+ .boolean()
78
+ .optional()
79
+ .describe(
80
+ "Whether a failing result fails the scenario. Defaults to true. A score-only evaluator reports and never gates, whatever this says.",
81
+ ),
82
+ mappings: z
83
+ .record(z.string().min(1).max(128), scenarioMappingSchema)
84
+ .describe(
85
+ "Where each evaluator input reads its value, keyed by input name. Read the inputs from platform_get_evaluator. A required input with no mapping makes the platform refuse the run until it is set.",
86
+ ),
87
+ });
88
+
89
+ /** The evaluators attached to a test suite or a run plan. */
90
+ export const evaluatorAttachmentsSchema = z
91
+ .array(evaluatorAttachmentSchema)
92
+ .max(20)
93
+ .describe(
94
+ "The evaluators that run after every scenario run, with their mappings. Up to 20. A required evaluator that fails fails the scenario; a score-only one reports beside the verdict.",
95
+ );
96
+
97
+ /** The values a scenario carries for the fields its suite declares. */
98
+ export const scenarioFieldValuesSchema = z
99
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
100
+ .describe(
101
+ "The scenario's value for each field its test suite declares, by identifier, for example { golden_sql: 'SELECT ...' }. A text field takes a string, a number field a number and a boolean field true or false. A field the suite does not declare is refused. On update, an empty object clears the values.",
102
+ );
103
+
104
+ export type SuiteField = z.infer<typeof suiteFieldSchema>;
105
+ export type ScenarioMapping = z.infer<typeof scenarioMappingSchema>;
106
+ export type EvaluatorAttachmentInput = z.infer<typeof evaluatorAttachmentSchema>;
107
+ export type ScenarioFieldValues = z.infer<typeof scenarioFieldValuesSchema>;
108
+
109
+ /** One attachment as the REST surface carries it: id and gate settled. */
110
+ export interface EvaluatorAttachmentWire {
111
+ id: string;
112
+ evaluatorId: string;
113
+ required: boolean;
114
+ mappings: Record<string, ScenarioMapping>;
115
+ }
116
+
117
+ const newAttachmentId = (): string => `att_${crypto.randomUUID().replace(/-/g, "").slice(0, 21)}`;
118
+
119
+ /**
120
+ * The attachments of a tool call, as the REST body carries them: a missing
121
+ * id is generated and a missing gate defaults to required.
122
+ */
123
+ export function toWireAttachments(
124
+ attachments: EvaluatorAttachmentInput[],
125
+ ): EvaluatorAttachmentWire[] {
126
+ return attachments.map((attachment) => ({
127
+ id: attachment.id ?? newAttachmentId(),
128
+ evaluatorId: attachment.evaluatorId,
129
+ required: attachment.required ?? true,
130
+ mappings: attachment.mappings,
131
+ }));
132
+ }
@@ -0,0 +1,365 @@
1
+ import { z } from "zod";
2
+
3
+ /** The shape of an automation on the `/api/v1/triggers` surface, stated once for
4
+ * the tool registrations and the API client. Every type is inferred from its
5
+ * schema, so what the API sends and what a caller reads cannot drift. */
6
+
7
+ export const triggerActionSchema = z.enum([
8
+ "SEND_EMAIL",
9
+ "SEND_SLACK_MESSAGE",
10
+ "SEND_WEBHOOK",
11
+ "ADD_TO_DATASET",
12
+ "ADD_TO_ANNOTATION_QUEUE",
13
+ ]);
14
+ export const alertTypeSchema = z.enum(["CRITICAL", "WARNING", "INFO"]);
15
+
16
+ /** The delivery configuration each channel reads, stated per channel so an agent
17
+ * can configure one without a round trip. Each passes unknown fields through:
18
+ * every Slack field is optional, so a webhook destination would otherwise read
19
+ * as an empty Slack one and be sent as nothing. */
20
+ const emailActionParamsSchema = z
21
+ .object({
22
+ members: z
23
+ .array(z.string())
24
+ .min(1)
25
+ .describe("Who receives the email. Any address, not only teammates."),
26
+ })
27
+ .passthrough();
28
+
29
+ const slackActionParamsSchema = z
30
+ .object({
31
+ slackIntegrationId: z
32
+ .string()
33
+ .optional()
34
+ .describe(
35
+ "The Slack connection to post through (an organization connection or one of this project's; they are listed in LangWatch under Settings, Integrations, Slack). A bot connection also needs `slackChannelId`; a webhook connection needs nothing else. Preferred over `slackWebhook` and `slackBotToken`.",
36
+ ),
37
+ slackDelivery: z
38
+ .enum(["webhook", "bot"])
39
+ .optional()
40
+ .describe(
41
+ "How the message reaches Slack. `webhook` posts to an incoming webhook URL, `bot` posts as the LangWatch Slack app. With `slackIntegrationId` it follows the connection's kind. Absent without a connection means `webhook`.",
42
+ ),
43
+ slackWebhook: z
44
+ .string()
45
+ .optional()
46
+ .describe(
47
+ "Legacy, accepted for one release: an incoming webhook URL (https://hooks.slack.com/...). It is stored as a Slack connection and the automation keeps only the connection id. Send `slackIntegrationId` instead.",
48
+ ),
49
+ slackChannelId: z
50
+ .string()
51
+ .optional()
52
+ .describe("The channel the bot posts in, for a bot connection or `bot` delivery."),
53
+ slackBotToken: z
54
+ .string()
55
+ .optional()
56
+ .describe(
57
+ "Legacy, accepted for one release: a bot token, stored as a Slack connection. It never reads back. Send `slackIntegrationId` instead.",
58
+ ),
59
+ slackBotTokenSet: z
60
+ .boolean()
61
+ .optional()
62
+ .describe(
63
+ "Legacy. Read: whether an automation not yet moved to a connection stores its own bot token. Write: true keeps it.",
64
+ ),
65
+ })
66
+ .passthrough();
67
+
68
+ const webhookActionParamsSchema = z
69
+ .object({
70
+ url: z.string().describe("Where the request goes. https only, and not a private host."),
71
+ method: z.enum(["POST", "PUT", "PATCH"]).optional(),
72
+ headers: z
73
+ .record(z.string(), z.string())
74
+ .optional()
75
+ .describe(
76
+ "Static headers sent with every delivery. The values are credentials: they read back as [redacted], and sending [redacted] back keeps the stored ones. Changing `url` means sending the values again in the same call.",
77
+ ),
78
+ bodyTemplate: z
79
+ .string()
80
+ .nullable()
81
+ .optional()
82
+ .describe(
83
+ "A Liquid template for the JSON body. Absent sends the standard LangWatch envelope.",
84
+ ),
85
+ signingSecret: z
86
+ .string()
87
+ .nullable()
88
+ .optional()
89
+ .describe("Signs every delivery so the receiver can verify it."),
90
+ })
91
+ .passthrough();
92
+
93
+ const datasetActionParamsSchema = z
94
+ .object({
95
+ datasetId: z.string().describe("The dataset matched traces are appended to."),
96
+ datasetMapping: z
97
+ .object({
98
+ mapping: z.record(z.string(), z.unknown()),
99
+ expansions: z.array(z.string()).optional(),
100
+ })
101
+ .describe("How a trace becomes a row in that dataset."),
102
+ })
103
+ .passthrough();
104
+
105
+ const annotationQueueActionParamsSchema = z
106
+ .object({
107
+ annotators: z
108
+ .array(z.object({ id: z.string(), name: z.string() }))
109
+ .min(1)
110
+ .describe("Who the queued items go to."),
111
+ })
112
+ .passthrough();
113
+
114
+ export const actionParamsSchema = z.union([
115
+ emailActionParamsSchema,
116
+ slackActionParamsSchema,
117
+ webhookActionParamsSchema,
118
+ datasetActionParamsSchema,
119
+ annotationQueueActionParamsSchema,
120
+ ]);
121
+
122
+ /** The write-side Slack shape: a redacted read round-trips, but a WRITE with no
123
+ * destination cannot deliver, so it is refused with the field named. A
124
+ * connection decides its own kind; without one, the legacy fields need a
125
+ * webhook URL, or a channel and a token. */
126
+ const slackActionParamsWriteSchema = slackActionParamsSchema.superRefine((params, ctx) => {
127
+ const delivery = params.slackDelivery ?? "webhook";
128
+ if (delivery === "bot" && !params.slackChannelId) {
129
+ ctx.addIssue({
130
+ code: "custom",
131
+ path: ["slackChannelId"],
132
+ message: params.slackIntegrationId
133
+ ? "a bot connection needs the channel to post in"
134
+ : "bot delivery needs the channel to post in",
135
+ });
136
+ }
137
+ if (params.slackIntegrationId) return;
138
+ if (delivery === "webhook" && !params.slackWebhook) {
139
+ ctx.addIssue({
140
+ code: "custom",
141
+ path: ["slackIntegrationId"],
142
+ message: "name the Slack connection to post through",
143
+ });
144
+ }
145
+ if (delivery === "bot" && !params.slackBotToken && params.slackBotTokenSet !== true) {
146
+ ctx.addIssue({
147
+ code: "custom",
148
+ path: ["slackIntegrationId"],
149
+ message: "bot delivery needs the Slack connection to post through",
150
+ });
151
+ }
152
+ });
153
+
154
+ const ACTION_PARAMS_SCHEMA_BY_ACTION: Partial<Record<string, z.ZodTypeAny>> = {
155
+ SEND_EMAIL: emailActionParamsSchema,
156
+ SEND_SLACK_MESSAGE: slackActionParamsWriteSchema,
157
+ SEND_WEBHOOK: webhookActionParamsSchema,
158
+ ADD_TO_DATASET: datasetActionParamsSchema,
159
+ ADD_TO_ANNOTATION_QUEUE: annotationQueueActionParamsSchema,
160
+ };
161
+
162
+ /** A WRITE's `actionParams` is bound to the channel named in `action`, or a
163
+ * `SEND_WEBHOOK` with `{}` slips through the all-optional Slack shape and is
164
+ * refused server-side with worse words. An action this build does not know
165
+ * validates against the loose union, the tolerance reads keep. */
166
+ export function validateActionParamsForAction({
167
+ action,
168
+ actionParams,
169
+ }: {
170
+ action: string;
171
+ actionParams: unknown;
172
+ }): { ok: true } | { ok: false; message: string } {
173
+ const schema = ACTION_PARAMS_SCHEMA_BY_ACTION[action] ?? actionParamsSchema;
174
+ const parsed = schema.safeParse(actionParams);
175
+ if (parsed.success) return { ok: true };
176
+ const issues = parsed.error.issues
177
+ .map((issue) => `${issue.path.join(".") || "(actionParams)"}: ${issue.message}`)
178
+ .join("; ");
179
+ return {
180
+ ok: false,
181
+ message: `actionParams does not fit ${action} — ${issues}`,
182
+ };
183
+ }
184
+
185
+ /** How `filters` is written. A keyed field sent flat never matches, so each
186
+ * keyed shape is spelled out; see the `/api/v1/triggers` 422 for the refusal. */
187
+ export const TRIGGER_FILTERS_DESCRIPTION = [
188
+ "Trace conditions as a JSON object string.",
189
+ 'Unkeyed fields take a list of values: {"traces.error":["true"]}.',
190
+ 'Keyed fields nest a key above the list. evaluations.* is keyed by the MONITOR id (the `id` from `platform_list_monitors` / GET /api/v1/monitors, not its evaluatorId): {"evaluations.passed":{"<monitorId>":["false"]}}.',
191
+ 'metadata.value is keyed by the metadata key: {"metadata.value":{"<key>":["true"]}}.',
192
+ "A keyed field sent as a bare list, or keyed by anything but a monitor id, is refused.",
193
+ ].join(" ");
194
+
195
+ export const TRIGGER_FILTER_QUERY_DESCRIPTION = [
196
+ "Trace query in the syntax the traces view uses; the alternative to `filters`, and it supersedes them.",
197
+ 'Examples: status:error · evaluator:"<evaluator name>" AND evaluatorVerdict:fail · trace.attribute.<key>:<value>.',
198
+ ].join(" ");
199
+
200
+ /** Stated on the create tool: Slack needs a connection or it never posts. */
201
+ export const SLACK_DELIVERY_NOTE =
202
+ 'For SEND_SLACK_MESSAGE, send {"slackIntegrationId":"<connection id>"} for a webhook connection, or {"slackIntegrationId":"<connection id>","slackChannelId":"C..."} for a bot connection. Slack connections are listed and added in LangWatch under Settings, Integrations, Slack (organization-wide or for one project); they are also listed by GET /api/v1/slack-connections and `langwatch slack-connection list`, or ask the user for the id. slackIntegrationId is preferred: a legacy {"slackWebhook":"https://hooks.slack.com/..."} or {"slackDelivery":"bot","slackBotToken":"xoxb-...","slackChannelId":"C..."} is still accepted for one release and is stored as a connection. A Slack automation with no connection will never post.';
203
+
204
+ export const graphAlertSchema = z
205
+ .object({
206
+ seriesName: z.string().describe("The series on the graph to watch."),
207
+ operator: z.enum(["gt", "lt", "gte", "lte", "eq"]),
208
+ threshold: z.number(),
209
+ timePeriod: z
210
+ .union([
211
+ z.literal(1),
212
+ z.literal(5),
213
+ z.literal(15),
214
+ z.literal(30),
215
+ z.literal(60),
216
+ z.literal(1440),
217
+ ])
218
+ .describe("The window, in minutes, the series is read over."),
219
+ })
220
+ .describe("The rule an alert fires by. Send it with `customGraphId` and `alertType`.");
221
+
222
+ /** What a scheduled report renders and when. Structured, not a loose record: an
223
+ * agent that cannot see the field names guesses them, and a guess is a 422.
224
+ * The server holds a write to this same shape, so looseness only delays it. */
225
+ export const reportSchema = z
226
+ .object({
227
+ source: z
228
+ .discriminatedUnion("kind", [
229
+ z.object({
230
+ kind: z.literal("dashboard"),
231
+ dashboardId: z.string(),
232
+ }),
233
+ z.object({
234
+ kind: z.literal("customGraph"),
235
+ customGraphId: z.string(),
236
+ }),
237
+ z.object({
238
+ kind: z.literal("traceQuery"),
239
+ filters: z.record(z.string(), z.unknown()).optional(),
240
+ metric: z.string().optional(),
241
+ topN: z.number().int().min(1).max(100).optional(),
242
+ }),
243
+ ])
244
+ .describe("What the report renders: a dashboard, one graph, or a table of traces."),
245
+ schedule: z
246
+ .object({
247
+ cron: z
248
+ .string()
249
+ .describe(
250
+ 'A 5-field cron expression (minute hour day-of-month month day-of-week), for example "0 9 * * 1". It can send at most every 15 minutes.',
251
+ ),
252
+ timezone: z.string().describe('An IANA timezone, for example "Europe/Amsterdam" or "UTC".'),
253
+ })
254
+ .describe("When it sends."),
255
+ compareToPrevious: z
256
+ .boolean()
257
+ .optional()
258
+ .describe("Include a this-period-versus-last comparison."),
259
+ })
260
+ .describe(
261
+ "What a scheduled report renders and when. Send it with a channel that can carry a message: email or Slack.",
262
+ );
263
+
264
+ export const templatesSchema = z
265
+ .object({
266
+ slackTemplateType: z.enum(["string", "block_kit"]).nullable().optional(),
267
+ slackTemplate: z.string().nullable().optional(),
268
+ emailSubjectTemplate: z.string().nullable().optional(),
269
+ emailBodyTemplate: z.string().nullable().optional(),
270
+ })
271
+ .describe(
272
+ "The Liquid templates the message is rendered from. Absent fields render the LangWatch default.",
273
+ );
274
+
275
+ export const notificationCadenceSchema = z
276
+ .enum(["immediate", "5min_digest", "15min_digest", "hourly_digest"])
277
+ .describe(
278
+ "How often a notification automation may send. A new one starts on a five-minute digest.",
279
+ );
280
+
281
+ /**
282
+ * What a read answers with. Permissive on purpose: an MCP client talks to whichever LangWatch it
283
+ * is pointed at, so an unknown field is carried through and a field a deployment does not send
284
+ * yet is absent rather than fatal.
285
+ */
286
+ export const triggerSchema = z
287
+ .object({
288
+ id: z.string(),
289
+ name: z.string(),
290
+ action: z.string(),
291
+ /** Where it delivers. The rule it fires by is stated separately, below,
292
+ * the same way a write states it. */
293
+ actionParams: z.record(z.string(), z.unknown()).default({}),
294
+ graphAlert: graphAlertSchema.nullable().optional(),
295
+ // A read stays tolerant of a deployment that words this differently; a
296
+ // write is held to `reportSchema`.
297
+ report: z.record(z.string(), z.unknown()).nullable().optional(),
298
+ filters: z.record(z.string(), z.unknown()).default({}),
299
+ filterQuery: z.string().nullable().optional(),
300
+ kind: z.string().optional(),
301
+ customGraphId: z.string().nullable().optional(),
302
+ notificationCadence: z.string().nullable().optional(),
303
+ traceDebounceMs: z.number().nullable().optional(),
304
+ templates: z.record(z.string(), z.unknown()).optional(),
305
+ active: z.boolean(),
306
+ message: z.string().nullable(),
307
+ alertType: z.string().nullable(),
308
+ createdAt: z.string(),
309
+ updatedAt: z.string(),
310
+ platformUrl: z.string().optional(),
311
+ })
312
+ .passthrough();
313
+
314
+ const triggerFireSchema = z
315
+ .object({
316
+ id: z.string(),
317
+ triggerId: z.string(),
318
+ customGraphId: z.string().nullable(),
319
+ firedAt: z.string(),
320
+ resolvedAt: z.string().nullable(),
321
+ })
322
+ .passthrough();
323
+
324
+ /** A page of fires: `{ fires, nextCursor }`, or the bare array an older
325
+ * deployment answers with, which has no next page. */
326
+ export const triggerFirePageSchema = z.union([
327
+ z.array(triggerFireSchema).transform((fires) => ({ fires, nextCursor: null })),
328
+ z
329
+ .object({
330
+ fires: z.array(triggerFireSchema).optional(),
331
+ data: z.array(triggerFireSchema).optional(),
332
+ nextCursor: z.string().nullable().optional(),
333
+ })
334
+ .transform(({ fires, data, nextCursor }) => ({
335
+ fires: fires ?? data ?? [],
336
+ nextCursor: nextCursor ?? null,
337
+ })),
338
+ ]);
339
+
340
+ export const testFireResultSchema = z
341
+ .object({
342
+ channel: z.string(),
343
+ recipientCount: z.number(),
344
+ usedDefault: z.boolean(),
345
+ missingVariables: z.array(z.string()).default([]),
346
+ errors: z.array(z.string()).default([]),
347
+ httpStatus: z.number().optional(),
348
+ })
349
+ .passthrough();
350
+
351
+ export const deletedTriggerSchema = z.object({
352
+ id: z.string(),
353
+ deleted: z.boolean(),
354
+ });
355
+
356
+ export type Trigger = z.infer<typeof triggerSchema>;
357
+ export type TriggerFirePage = z.infer<typeof triggerFirePageSchema>;
358
+ export type TestFireResult = z.infer<typeof testFireResultSchema>;
359
+ export type TriggerAction = z.infer<typeof triggerActionSchema>;
360
+ export type TriggerAlertType = z.infer<typeof alertTypeSchema>;
361
+ export type TriggerActionParams = z.infer<typeof actionParamsSchema>;
362
+ export type GraphAlertRule = z.infer<typeof graphAlertSchema>;
363
+ export type ReportRule = z.infer<typeof reportSchema>;
364
+ export type TriggerTemplates = z.infer<typeof templatesSchema>;
365
+ export type NotificationCadence = z.infer<typeof notificationCadenceSchema>;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The `add_dashboard_widget` MCP tool: create, then place, as the platform's own write path
3
+ * does. The dashboard is checked first so a typo leaves no unplaced widget behind, and a
4
+ * failed placement deletes the widget it just created.
5
+ */
6
+
7
+ import {
8
+ assignDashboardWidgetToDashboard,
9
+ createDashboardWidget,
10
+ deleteDashboardWidget,
11
+ } from "../langwatch-api-dashboard-widgets.js";
12
+ import { getCurrentProject } from "../langwatch-api-projects.js";
13
+ import { assertDashboardExists } from "./dashboard-exists.js";
14
+ import {
15
+ type DashboardWidgetQueriesInput,
16
+ normalizeDashboardWidgetQueries,
17
+ } from "./dashboard-widget-queries.js";
18
+
19
+ export interface AddDashboardWidgetParams {
20
+ dashboardId: string;
21
+ name: string;
22
+ code: string;
23
+ queries: DashboardWidgetQueriesInput;
24
+ }
25
+
26
+ export async function handleAddDashboardWidget(params: AddDashboardWidgetParams): Promise<string> {
27
+ const queries = normalizeDashboardWidgetQueries({ queries: params.queries });
28
+
29
+ const project = await getCurrentProject();
30
+ await assertDashboardExists({ projectId: project.id, dashboardId: params.dashboardId });
31
+
32
+ const widget = await createDashboardWidget({
33
+ projectId: project.id,
34
+ name: params.name,
35
+ code: params.code,
36
+ queries,
37
+ });
38
+
39
+ try {
40
+ await assignDashboardWidgetToDashboard({
41
+ projectId: project.id,
42
+ widgetId: widget.id,
43
+ dashboardId: params.dashboardId,
44
+ });
45
+ } catch (error) {
46
+ // The widget now exists but is not on the dashboard we promised — never
47
+ // leave it dangling because the dashboard vanished between the check
48
+ // above and this call.
49
+ await deleteDashboardWidget({
50
+ projectId: project.id,
51
+ widgetId: widget.id,
52
+ }).catch(() => {
53
+ // Best-effort cleanup; the assign error below is what the caller needs.
54
+ });
55
+ throw error;
56
+ }
57
+
58
+ return [
59
+ `Widget "${params.name}" created and added to dashboard "${params.dashboardId}".`,
60
+ `**Widget ID**: ${widget.id}`,
61
+ `**Queries**: ${queries.map((q) => q.name).join(", ")}`,
62
+ ].join("\n");
63
+ }
@@ -1,8 +1,6 @@
1
- import { archiveProject as apiArchiveProject } from "../langwatch-api-projects.js";
1
+ import { archiveProject as apiArchiveProject } from "../langwatch-api-projects.ts";
2
2
 
3
- export async function handleArchiveProject(params: {
4
- id: string;
5
- }): Promise<string> {
3
+ export async function handleArchiveProject(params: { id: string }): Promise<string> {
6
4
  const result = await apiArchiveProject(params.id);
7
5
 
8
6
  return (
@@ -0,0 +1,10 @@
1
+ import { archiveRunPlan as apiArchiveRunPlan } from "../langwatch-api-run-plans.ts";
2
+
3
+ /**
4
+ * Handles the platform_archive_run_plan MCP tool invocation.
5
+ */
6
+ export async function handleArchiveRunPlan(params: { id: string }): Promise<string> {
7
+ const result = await apiArchiveRunPlan(params.id);
8
+
9
+ return `Run plan ${result.id} is archived. Its past runs stay readable, and the name is free for a new plan.`;
10
+ }
@@ -1,13 +1,11 @@
1
- import { archiveScenario as apiArchiveScenario } from "../langwatch-api-scenarios.js";
1
+ import { archiveScenario as apiArchiveScenario } from "../langwatch-api-scenarios.ts";
2
2
 
3
3
  /**
4
4
  * Handles the platform_archive_scenario MCP tool invocation.
5
5
  *
6
6
  * Archives (soft-deletes) a scenario and returns confirmation.
7
7
  */
8
- export async function handleArchiveScenario(params: {
9
- scenarioId: string;
10
- }): Promise<string> {
8
+ export async function handleArchiveScenario(params: { scenarioId: string }): Promise<string> {
11
9
  const result = await apiArchiveScenario(params.scenarioId);
12
10
 
13
11
  const lines: string[] = [];
@@ -0,0 +1,10 @@
1
+ import { archiveTestSuite as apiArchiveTestSuite } from "../langwatch-api-test-suites.ts";
2
+
3
+ /**
4
+ * Handles the platform_archive_test_suite MCP tool invocation.
5
+ */
6
+ export async function handleArchiveTestSuite(params: { id: string }): Promise<string> {
7
+ const result = await apiArchiveTestSuite(params.id);
8
+
9
+ return `Test suite ${result.id} is archived, and the scenarios filed in it are archived with it.`;
10
+ }