@langwatch/mcp-server 2.1.0 → 3.0.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 (428) hide show
  1. package/.gitignore +7 -0
  2. package/.npmignore +41 -0
  3. package/CHANGELOG.md +218 -201
  4. package/CONTRIBUTING.md +10 -0
  5. package/README.md +70 -68
  6. package/dist/add-dashboard-widget-55UI6H22.js +44 -0
  7. package/dist/add-dashboard-widget-MJHIC3CC.js +45 -0
  8. package/dist/{archive-project-W3IKI7UL.js → archive-project-K5UD7A6X.js} +4 -4
  9. package/dist/archive-project-L4PAKT34.js +19 -0
  10. package/dist/{archive-run-plan-474OKHHU.js → archive-run-plan-5HSQG2HX.js} +4 -4
  11. package/dist/archive-run-plan-AOKNYQIO.js +15 -0
  12. package/dist/archive-scenario-2CWJW2VD.js +19 -0
  13. package/dist/{archive-scenario-V5UNYK26.js → archive-scenario-5IJQESHH.js} +4 -4
  14. package/dist/{archive-test-suite-O7FLDMXK.js → archive-test-suite-OPB7NFI7.js} +4 -4
  15. package/dist/archive-test-suite-ZC3TN6VH.js +15 -0
  16. package/dist/assign-prompt-tag-6DDE6ZZ5.js +23 -0
  17. package/dist/{assign-prompt-tag-Q46WRB6P.js → assign-prompt-tag-D7O23MJM.js} +8 -4
  18. package/dist/chunk-3YLN3ATJ.js +241 -0
  19. package/dist/{chunk-EYXMLE5X.js → chunk-4RX3G62A.js} +56 -47
  20. package/dist/{chunk-UF6XIGYR.js → chunk-6YZUOD34.js} +2 -5
  21. package/dist/{chunk-WVI5CJ6S.js → chunk-7AQLPVFL.js} +2 -2
  22. package/dist/chunk-7CZQDZ2K.js +319 -0
  23. package/dist/{chunk-RVAE2MEA.js → chunk-7GETGE6B.js} +7 -7
  24. package/dist/{chunk-OJLYYJWI.js → chunk-AASW2TO2.js} +7 -10
  25. package/dist/{chunk-QAMP3CGR.js → chunk-B4RFWIUP.js} +4 -4
  26. package/dist/{chunk-ZXKLPC2E.js → chunk-BIIKE3EI.js} +1 -1
  27. package/dist/{chunk-G4ZAZTZW.js → chunk-D2HS73VI.js} +7 -10
  28. package/dist/{chunk-37SMPMQS.js → chunk-DMM3JQ42.js} +7 -2
  29. package/dist/{chunk-WT4PO4A3.js → chunk-DULFP3MK.js} +10 -7
  30. package/dist/chunk-EYPJBL7P.js +516 -0
  31. package/dist/{chunk-NUFPTHKU.js → chunk-FOKVY3BK.js} +1 -1
  32. package/dist/chunk-GA5JUSPT.js +64 -0
  33. package/dist/{chunk-UJPXFA26.js → chunk-HHMNZAPD.js} +101 -74
  34. package/dist/chunk-HLQJXUR6.js +67 -0
  35. package/dist/{chunk-JTQJRNFJ.js → chunk-KILVK3FS.js} +4 -4
  36. package/dist/chunk-MOCU5XWF.js +79 -0
  37. package/dist/chunk-O2WGVCED.js +41 -0
  38. package/dist/{chunk-HV4DWKBI.js → chunk-O7EMWEVJ.js} +2 -4
  39. package/dist/{chunk-UFOYFYI7.js → chunk-QXLMHLRC.js} +6 -9
  40. package/dist/{chunk-QRO35SOM.js → chunk-R5YJIYPF.js} +981 -747
  41. package/dist/{chunk-JR6DJZOU.js → chunk-RABYBFX5.js} +4 -10
  42. package/dist/{chunk-SR6QPRIE.js → chunk-RFC2SBEX.js} +7 -13
  43. package/dist/chunk-SHSVN3SE.js +21 -0
  44. package/dist/{chunk-CKNAH5TG.js → chunk-U24EACC4.js} +3 -10
  45. package/dist/{chunk-56OGTS24.js → chunk-VOUGDY2O.js} +4 -11
  46. package/dist/chunk-WCKZDOGF.js +31 -0
  47. package/dist/chunk-XQYH52SI.js +129 -0
  48. package/dist/{chunk-7E4FZIYJ.js → chunk-XUIJYNRJ.js} +1 -1
  49. package/dist/{chunk-6DJDEXET.js → chunk-YCIUEZVK.js} +6 -12
  50. package/dist/config-2WQGMAEN.js +16 -0
  51. package/dist/config.cjs +109 -0
  52. package/dist/config.js +5 -4
  53. package/dist/{create-agent-JFJ2K6X3.js → create-agent-6JNLMYQD.js} +4 -4
  54. package/dist/create-agent-IRZOU4R5.js +23 -0
  55. package/dist/create-analytics-report-N4L4V5QL.js +19 -0
  56. package/dist/create-analytics-report-QEHXG7FU.js +18 -0
  57. package/dist/{create-annotation-EUXXQ6NS.js → create-annotation-CCEGIZ67.js} +10 -5
  58. package/dist/create-annotation-DTHCMWHB.js +29 -0
  59. package/dist/{create-api-key-BGBB76JX.js → create-api-key-JKM2DSZO.js} +5 -7
  60. package/dist/create-api-key-JPEUIXGM.js +24 -0
  61. package/dist/create-dataset-DJNXYJ5B.js +22 -0
  62. package/dist/{create-dataset-FD5VAXU4.js → create-dataset-WON4SOTC.js} +6 -6
  63. package/dist/create-dataset-records-TMEOTLTO.js +20 -0
  64. package/dist/{create-dataset-records-WQUXXOHI.js → create-dataset-records-UJBW3C7C.js} +5 -7
  65. package/dist/create-evaluator-N5AEHI4K.js +27 -0
  66. package/dist/{create-evaluator-6NV33Z52.js → create-evaluator-XYNY2YFY.js} +4 -4
  67. package/dist/create-mcp-server.cjs +9712 -0
  68. package/dist/create-mcp-server.js +2757 -10
  69. package/dist/{create-project-ZQ7TFIO2.js → create-project-EY4XZ7TH.js} +4 -4
  70. package/dist/create-project-YL345T5L.js +44 -0
  71. package/dist/{create-prompt-BS6QRCUU.js → create-prompt-JZQNQ7I3.js} +5 -7
  72. package/dist/create-prompt-ZUMQHHX7.js +37 -0
  73. package/dist/create-prompt-tag-CJ5FZ436.js +17 -0
  74. package/dist/{create-prompt-tag-FCQ5ELSQ.js → create-prompt-tag-JFP2Q3PO.js} +3 -3
  75. package/dist/create-scenario-CQU2SSFB.js +33 -0
  76. package/dist/{create-scenario-DRPTPXU3.js → create-scenario-S6OWWKJO.js} +5 -5
  77. package/dist/create-test-suite-OLGCAGJA.js +36 -0
  78. package/dist/{create-test-suite-ZIZCCFJ4.js → create-test-suite-Y7P2RI4L.js} +6 -6
  79. package/dist/create-trigger-4CNBTISJ.js +48 -0
  80. package/dist/create-trigger-RQ653WYF.js +49 -0
  81. package/dist/dashboards-BA7M6CMO.js +90 -0
  82. package/dist/dashboards-U663GUIB.js +91 -0
  83. package/dist/{delete-agent-SA4JMUZF.js → delete-agent-J2Q4DRZ2.js} +4 -4
  84. package/dist/delete-agent-ZAU4LTXB.js +18 -0
  85. package/dist/delete-analytics-report-QK6X7P4S.js +19 -0
  86. package/dist/delete-analytics-report-YUYLDHLL.js +18 -0
  87. package/dist/delete-annotation-7X4NYXXM.js +17 -0
  88. package/dist/{delete-annotation-X3KKK7QY.js → delete-annotation-KJOCDLV3.js} +4 -4
  89. package/dist/{delete-dataset-W6EZ44HY.js → delete-dataset-NXP7ZPPV.js} +5 -7
  90. package/dist/delete-dataset-RUTZBBVD.js +18 -0
  91. package/dist/{delete-dataset-records-7CNOTALB.js → delete-dataset-records-4Z7DZCDW.js} +5 -7
  92. package/dist/delete-dataset-records-75UAR5IS.js +19 -0
  93. package/dist/delete-evaluator-E3CUNGTQ.js +16 -0
  94. package/dist/delete-evaluator-RI4GALGX.js +15 -0
  95. package/dist/delete-prompt-tag-LE3WJPKS.js +17 -0
  96. package/dist/{delete-prompt-tag-QB3OCZUV.js → delete-prompt-tag-S4KBZTNG.js} +3 -3
  97. package/dist/delete-workflow-4CZFIQCQ.js +17 -0
  98. package/dist/{delete-workflow-NG2VXYYM.js → delete-workflow-PWQPDW6H.js} +4 -4
  99. package/dist/discover-evaluator-schema-UHWA5HVL.js +120 -0
  100. package/dist/discover-evaluator-schema-XKCLLCB3.js +118 -0
  101. package/dist/discover-scenario-schema-NOJY6S7E.js +137 -0
  102. package/dist/{discover-scenario-schema-W7KBXA4J.js → discover-scenario-schema-OOFIQKOG.js} +37 -17
  103. package/dist/discover-schema-7WHIIATI.js +442 -0
  104. package/dist/{discover-schema-L6QDPON3.js → discover-schema-J2ANOBKO.js} +181 -186
  105. package/dist/get-agent-5T3CXQOR.js +69 -0
  106. package/dist/{get-agent-7THF7HCU.js → get-agent-ZKYQJUQ7.js} +15 -11
  107. package/dist/get-analytics-4MPEK4KV.js +79 -0
  108. package/dist/get-analytics-ZLI3A4F5.js +80 -0
  109. package/dist/get-analytics-report-JCKYMA2S.js +29 -0
  110. package/dist/get-analytics-report-SKIP2JWH.js +30 -0
  111. package/dist/get-dataset-APGZBYMH.js +64 -0
  112. package/dist/get-dataset-LWXDVMVE.js +63 -0
  113. package/dist/get-evaluator-3A7BJBAB.js +47 -0
  114. package/dist/{get-evaluator-RZ44UIN6.js → get-evaluator-IWNJINRY.js} +4 -4
  115. package/dist/get-project-EQVB4LEL.js +26 -0
  116. package/dist/{get-project-QSQEAB33.js → get-project-RK6AJTEP.js} +4 -4
  117. package/dist/{get-prompt-GVKXF5NL.js → get-prompt-TLG2T2DS.js} +53 -58
  118. package/dist/get-prompt-ZER557X3.js +94 -0
  119. package/dist/get-run-plan-5X7DDJRN.js +58 -0
  120. package/dist/{get-run-plan-NY7J5GRE.js → get-run-plan-PEHRO5M3.js} +7 -9
  121. package/dist/{get-scenario-Q3LLQE4T.js → get-scenario-375W5D6I.js} +5 -5
  122. package/dist/get-scenario-UQX45ZZO.js +37 -0
  123. package/dist/{get-simulation-run-WINEYDMK.js → get-simulation-run-2NBXJADG.js} +26 -22
  124. package/dist/get-simulation-run-QZTLGL7W.js +69 -0
  125. package/dist/{get-test-suite-4HKA7OKS.js → get-test-suite-GVIBDG7K.js} +5 -5
  126. package/dist/get-test-suite-OODIR3RI.js +48 -0
  127. package/dist/{get-trace-RCUV7N4O.js → get-trace-AGCHVPJK.js} +11 -13
  128. package/dist/get-trace-KJLYAZRC.js +45 -0
  129. package/dist/get-workflow-3TV2YYC5.js +25 -0
  130. package/dist/{get-workflow-EEOC7Y45.js → get-workflow-7LZSVPP2.js} +4 -4
  131. package/dist/{http-security-EU3CHTY7.js → http-security-DNLBNQZL.js} +2 -2
  132. package/dist/{http-server-WCDUARTH.js → http-server-7CCN3EHJ.js} +4249 -325
  133. package/dist/index.js +59595 -38027
  134. package/dist/langwatch-api-analytics-reports-6CF6KUET.js +17 -0
  135. package/dist/langwatch-api-analytics-reports-RPFB5AO5.js +18 -0
  136. package/dist/{langwatch-api-annotations-6DHVZWUK.js → langwatch-api-annotations-H4AI4TKG.js} +4 -4
  137. package/dist/langwatch-api-annotations-QSJNUQQS.js +17 -0
  138. package/dist/langwatch-api-monitors-ENHLOL3S.js +47 -0
  139. package/dist/{langwatch-api-monitors-6ISFJ7WX.js → langwatch-api-monitors-OHSGADTH.js} +11 -16
  140. package/dist/langwatch-api-secrets-AGW6UUMD.js +55 -0
  141. package/dist/langwatch-api-secrets-CLTS766Q.js +56 -0
  142. package/dist/langwatch-api-triggers-G5UWFNN2.js +22 -0
  143. package/dist/langwatch-api-triggers-QMR7KEXW.js +23 -0
  144. package/dist/langwatch-api-workflows-HKTBIKKY.js +15 -0
  145. package/dist/{langwatch-api-workflows-6UJL5U4Z.js → langwatch-api-workflows-OMROZH2O.js} +4 -4
  146. package/dist/{list-agents-AMVDQPQL.js → list-agents-BYWDNAHT.js} +4 -4
  147. package/dist/list-agents-MG24MHZE.js +42 -0
  148. package/dist/list-analytics-reports-G3EPWGSQ.js +32 -0
  149. package/dist/list-analytics-reports-PPWIFRES.js +33 -0
  150. package/dist/list-annotations-QCLGEK5A.js +38 -0
  151. package/dist/{list-annotations-6PR5NLIC.js → list-annotations-Y4EUBJHG.js} +10 -5
  152. package/dist/list-api-keys-DAVOBPFK.js +42 -0
  153. package/dist/{list-api-keys-GVPNBIHQ.js → list-api-keys-JMW5WRXX.js} +10 -5
  154. package/dist/list-dashboards-2P3VJFRS.js +29 -0
  155. package/dist/list-dashboards-MW5O3MKH.js +30 -0
  156. package/dist/list-dataset-records-KEHT7G6Q.js +43 -0
  157. package/dist/{list-dataset-records-O67HZ35Y.js → list-dataset-records-KMLLMZAZ.js} +14 -10
  158. package/dist/{list-datasets-WXZL6UMP.js → list-datasets-P3JK2ZHS.js} +9 -13
  159. package/dist/list-datasets-Z7OOE4KI.js +56 -0
  160. package/dist/{list-evaluators-PAX72WCN.js → list-evaluators-7RRESBOU.js} +5 -7
  161. package/dist/list-evaluators-VATJVOHR.js +32 -0
  162. package/dist/{list-model-providers-VVLZMLCX.js → list-model-providers-OXDRPUCJ.js} +4 -4
  163. package/dist/list-model-providers-VXQJWDXU.js +35 -0
  164. package/dist/{list-projects-AQVI6SUK.js → list-projects-HUVK7L3W.js} +5 -7
  165. package/dist/list-projects-X72ZXVY5.js +40 -0
  166. package/dist/{list-prompt-tags-WZFBJOHG.js → list-prompt-tags-3I3LWFIR.js} +3 -3
  167. package/dist/list-prompt-tags-DRHUH2ND.js +23 -0
  168. package/dist/list-prompts-6DX3QECJ.js +30 -0
  169. package/dist/{list-prompts-BQOHRHYT.js → list-prompts-PS423RU3.js} +4 -6
  170. package/dist/list-run-plans-5SXPOV3X.js +45 -0
  171. package/dist/{list-run-plans-UBAU2DGH.js → list-run-plans-6VNHINPY.js} +5 -5
  172. package/dist/list-scenarios-EQWCW7Q4.js +45 -0
  173. package/dist/{list-scenarios-MS4IUIVM.js → list-scenarios-GKNH3YND.js} +13 -11
  174. package/dist/{list-simulation-runs-D6YF4ZOU.js → list-simulation-runs-KHY7KALI.js} +12 -8
  175. package/dist/list-simulation-runs-PUDK32C2.js +53 -0
  176. package/dist/list-test-suites-7H2DRILK.js +34 -0
  177. package/dist/{list-test-suites-L7R2O6DR.js → list-test-suites-GY56LGLY.js} +4 -4
  178. package/dist/list-trigger-fires-7RZUP54Y.js +19 -0
  179. package/dist/list-trigger-fires-OZUF65LI.js +20 -0
  180. package/dist/list-triggers-BGUCUXTJ.js +45 -0
  181. package/dist/list-triggers-XC5KMX4C.js +46 -0
  182. package/dist/list-workflows-55NYC3PC.js +33 -0
  183. package/dist/{list-workflows-6MV4XJHF.js → list-workflows-HV7FUWZA.js} +5 -7
  184. package/dist/{rename-prompt-tag-K5LQ4CY6.js → rename-prompt-tag-2I7Z5FCY.js} +3 -3
  185. package/dist/rename-prompt-tag-SZYTOWDX.js +18 -0
  186. package/dist/{rename-test-suite-NTYHAO5X.js → rename-test-suite-27AYY33K.js} +4 -4
  187. package/dist/rename-test-suite-CLIEPZDO.js +20 -0
  188. package/dist/report-issue-5HSL7CNB.js +924 -0
  189. package/dist/{report-issue-RHZZ33IS.js → report-issue-N7ND5TM6.js} +107 -67
  190. package/dist/{rerun-run-plan-MUDWJBE4.js → rerun-run-plan-O3VUZ6CG.js} +5 -5
  191. package/dist/rerun-run-plan-OJTVVGJI.js +21 -0
  192. package/dist/{revoke-api-key-6LAZ3QZZ.js → revoke-api-key-BU6BKGCG.js} +4 -4
  193. package/dist/revoke-api-key-NZNCFOIR.js +15 -0
  194. package/dist/run-agent-244O4Y5M.js +75 -0
  195. package/dist/run-agent-HPCGZGYW.js +74 -0
  196. package/dist/run-plan-2ZPGGZ4H.js +38 -0
  197. package/dist/{run-plan-T4ZYOZGA.js → run-plan-KSXUYQT3.js} +7 -7
  198. package/dist/run-query-4LMOQ5UW.js +47 -0
  199. package/dist/run-query-BQUHIUC2.js +46 -0
  200. package/dist/{run-test-suite-L4MZ7G4D.js → run-test-suite-P3WLVPXC.js} +6 -6
  201. package/dist/run-test-suite-UEOMCON6.js +25 -0
  202. package/dist/run-workflow-EFZ6HG5B.js +36 -0
  203. package/dist/{run-workflow-DFIS23AP.js → run-workflow-I4IHEN54.js} +4 -4
  204. package/dist/search-traces-6IR7PR4L.js +170 -0
  205. package/dist/search-traces-BYML2NQ4.js +171 -0
  206. package/dist/set-model-provider-RBNGWFVI.js +33 -0
  207. package/dist/{set-model-provider-XRNMFOVR.js → set-model-provider-SHEVKWRC.js} +4 -4
  208. package/dist/{update-agent-DUWNYBNA.js → update-agent-PTKTM2B5.js} +4 -4
  209. package/dist/update-agent-ZPXWRMS2.js +20 -0
  210. package/dist/update-dashboard-widget-HKCBXH7X.js +62 -0
  211. package/dist/update-dashboard-widget-IP2TTQ5G.js +63 -0
  212. package/dist/update-dataset-GHIN6JR3.js +22 -0
  213. package/dist/{update-dataset-YSOMDCOB.js → update-dataset-IRVMY5PF.js} +6 -6
  214. package/dist/update-dataset-record-6VI7K2CL.js +19 -0
  215. package/dist/{update-dataset-record-DEA2ITYR.js → update-dataset-record-Z3L7ACNF.js} +4 -4
  216. package/dist/update-evaluator-5NLNE6VY.js +24 -0
  217. package/dist/{update-evaluator-NH7Q5GWV.js → update-evaluator-FEH7H6NS.js} +4 -4
  218. package/dist/update-project-75H3LDQT.js +24 -0
  219. package/dist/{update-project-Q26FBSYH.js → update-project-QPR3QX55.js} +4 -4
  220. package/dist/update-prompt-BCDFTNLK.js +140 -0
  221. package/dist/{update-prompt-ZQQ2C3KU.js → update-prompt-NPRXYSWA.js} +27 -29
  222. package/dist/update-scenario-BNOMDCMV.js +34 -0
  223. package/dist/{update-scenario-WUX34JAR.js → update-scenario-EM5XGMB7.js} +5 -5
  224. package/package.json +35 -39
  225. package/pyproject.toml +23 -8
  226. package/src/config.d.ts +6 -0
  227. package/src/config.ts +22 -29
  228. package/src/create-mcp-server.ts +887 -634
  229. package/src/documentation-fetch.ts +21 -20
  230. package/src/http-security.ts +166 -135
  231. package/src/http-server.ts +189 -224
  232. package/src/index.ts +14 -16
  233. package/src/langwatch-api-agents.ts +18 -21
  234. package/src/langwatch-api-analytics-reports.ts +62 -0
  235. package/src/langwatch-api-annotations.ts +11 -12
  236. package/src/langwatch-api-api-keys.ts +8 -9
  237. package/src/langwatch-api-dashboard-widgets.ts +47 -0
  238. package/src/langwatch-api-dashboards.ts +109 -24
  239. package/src/langwatch-api-datasets.ts +98 -32
  240. package/src/langwatch-api-evaluators.ts +13 -19
  241. package/src/langwatch-api-model-providers.ts +4 -4
  242. package/src/langwatch-api-monitors.ts +14 -18
  243. package/src/langwatch-api-projects.ts +10 -9
  244. package/src/langwatch-api-query.ts +114 -0
  245. package/src/langwatch-api-run-plans.ts +12 -29
  246. package/src/langwatch-api-scenarios.ts +9 -11
  247. package/src/langwatch-api-secrets.ts +31 -24
  248. package/src/langwatch-api-simulation-runs.ts +6 -8
  249. package/src/langwatch-api-test-suites.ts +14 -33
  250. package/src/langwatch-api-triggers.ts +96 -34
  251. package/src/langwatch-api-workflows.ts +9 -9
  252. package/src/langwatch-api.ts +118 -124
  253. package/src/public-http-request.ts +21 -12
  254. package/src/schemas/analytics-metrics.ts +3 -15
  255. package/src/schemas/create-dataset.ts +3 -4
  256. package/src/schemas/run-plan.ts +13 -29
  257. package/src/schemas/suite-fields.ts +6 -23
  258. package/src/schemas/triggers.ts +365 -0
  259. package/src/schemas/widget-sources.ts +33 -0
  260. package/src/tools/add-dashboard-widget.ts +51 -0
  261. package/src/tools/archive-project.ts +2 -4
  262. package/src/tools/archive-run-plan.ts +2 -4
  263. package/src/tools/archive-scenario.ts +2 -4
  264. package/src/tools/archive-test-suite.ts +2 -4
  265. package/src/tools/assign-prompt-tag.ts +6 -2
  266. package/src/tools/create-agent.ts +1 -1
  267. package/src/tools/create-analytics-report.ts +7 -0
  268. package/src/tools/create-annotation.ts +7 -2
  269. package/src/tools/create-api-key.ts +4 -6
  270. package/src/tools/create-dataset-records.ts +4 -8
  271. package/src/tools/create-dataset.ts +5 -7
  272. package/src/tools/create-evaluator.ts +3 -5
  273. package/src/tools/create-project.ts +2 -2
  274. package/src/tools/create-prompt-tag.ts +2 -4
  275. package/src/tools/create-prompt.ts +9 -18
  276. package/src/tools/create-scenario.ts +5 -7
  277. package/src/tools/create-test-suite.ts +4 -9
  278. package/src/tools/create-trigger.ts +55 -0
  279. package/src/tools/dashboard-widget-queries.ts +45 -0
  280. package/src/tools/dashboards.ts +104 -0
  281. package/src/tools/delete-agent.ts +1 -1
  282. package/src/tools/delete-analytics-report.ts +7 -0
  283. package/src/tools/delete-annotation.ts +1 -1
  284. package/src/tools/delete-dataset-records.ts +4 -8
  285. package/src/tools/delete-dataset.ts +3 -7
  286. package/src/tools/delete-evaluator.ts +4 -6
  287. package/src/tools/delete-prompt-tag.ts +2 -4
  288. package/src/tools/delete-workflow.ts +1 -1
  289. package/src/tools/discover-evaluator-schema.ts +34 -25
  290. package/src/tools/discover-scenario-schema.ts +41 -21
  291. package/src/tools/discover-schema.ts +149 -27
  292. package/src/tools/experiment-run-status.ts +2 -8
  293. package/src/tools/format-dataset-mutation.ts +5 -10
  294. package/src/tools/format-run-plan.ts +7 -21
  295. package/src/tools/format-scenario.ts +2 -4
  296. package/src/tools/format-suite-details.ts +6 -12
  297. package/src/tools/get-agent.ts +15 -6
  298. package/src/tools/get-analytics-report.ts +23 -0
  299. package/src/tools/get-analytics.ts +52 -46
  300. package/src/tools/get-dataset.ts +34 -16
  301. package/src/tools/get-evaluator.ts +4 -11
  302. package/src/tools/get-experiment-results.ts +219 -186
  303. package/src/tools/get-project.ts +2 -4
  304. package/src/tools/get-prompt.ts +63 -84
  305. package/src/tools/get-run-plan.ts +4 -6
  306. package/src/tools/get-scenario.ts +4 -6
  307. package/src/tools/get-simulation-run.ts +27 -24
  308. package/src/tools/get-test-suite.ts +2 -5
  309. package/src/tools/get-trace.ts +16 -17
  310. package/src/tools/get-workflow.ts +1 -1
  311. package/src/tools/list-agents.ts +1 -1
  312. package/src/tools/list-analytics-reports.ts +27 -0
  313. package/src/tools/list-annotations.ts +8 -5
  314. package/src/tools/list-api-keys.ts +7 -2
  315. package/src/tools/list-dashboards.ts +20 -21
  316. package/src/tools/list-dataset-records.ts +8 -6
  317. package/src/tools/list-datasets.ts +12 -23
  318. package/src/tools/list-evaluators.ts +4 -8
  319. package/src/tools/list-experiment-runs.ts +5 -9
  320. package/src/tools/list-experiments.ts +5 -11
  321. package/src/tools/list-model-providers.ts +3 -5
  322. package/src/tools/list-projects.ts +2 -4
  323. package/src/tools/list-prompt-tags.ts +3 -3
  324. package/src/tools/list-prompts.ts +4 -8
  325. package/src/tools/list-run-plans.ts +2 -2
  326. package/src/tools/list-scenarios.ts +14 -15
  327. package/src/tools/list-simulation-runs.ts +8 -4
  328. package/src/tools/list-test-suites.ts +1 -1
  329. package/src/tools/list-trigger-fires.ts +17 -0
  330. package/src/tools/list-triggers.ts +39 -0
  331. package/src/tools/list-workflows.ts +2 -4
  332. package/src/tools/rename-prompt-tag.ts +1 -1
  333. package/src/tools/rename-test-suite.ts +2 -5
  334. package/src/tools/report-issue.ts +7 -9
  335. package/src/tools/rerun-run-plan.ts +2 -5
  336. package/src/tools/revoke-api-key.ts +2 -4
  337. package/src/tools/run-agent.ts +29 -26
  338. package/src/tools/run-experiment.ts +37 -34
  339. package/src/tools/run-plan.ts +4 -7
  340. package/src/tools/run-query.ts +47 -0
  341. package/src/tools/run-test-suite.ts +4 -7
  342. package/src/tools/run-workflow.ts +2 -5
  343. package/src/tools/search-traces.ts +161 -47
  344. package/src/tools/set-model-provider.ts +4 -6
  345. package/src/tools/test-agent.ts +2 -2
  346. package/src/tools/update-agent.ts +1 -1
  347. package/src/tools/update-dashboard-widget.ts +86 -0
  348. package/src/tools/update-dataset-record.ts +1 -1
  349. package/src/tools/update-dataset.ts +5 -7
  350. package/src/tools/update-evaluator.ts +3 -5
  351. package/src/tools/update-project.ts +1 -1
  352. package/src/tools/update-prompt.ts +44 -59
  353. package/src/tools/update-scenario.ts +5 -7
  354. package/src/tools/update-test-suite.ts +6 -11
  355. package/src/utils/date-parsing.ts +1 -1
  356. package/src/utils/escape-markdown.ts +3 -8
  357. package/src/utils/format-evaluations.ts +6 -6
  358. package/src/utils/markdown-table.ts +58 -0
  359. package/src/utils/trace-id-shape.ts +20 -0
  360. package/tsconfig.app.json +24 -0
  361. package/tsconfig.json +14 -12
  362. package/uv.lock +667 -341
  363. package/vitest.integration.config.js +10 -0
  364. package/vitest.scenario.config.js +9 -0
  365. package/.eslintrc.cjs +0 -36
  366. package/dist/chunk-AN4OUA4D.js +0 -86
  367. package/dist/chunk-GELJ2MQR.js +0 -226
  368. package/dist/chunk-K37ZNPXK.js +0 -24
  369. package/dist/chunk-S4GVX36W.js +0 -39
  370. package/dist/config.d.ts +0 -17
  371. package/dist/create-dashboard-Z76AIPLS.js +0 -19
  372. package/dist/create-mcp-server.d.ts +0 -10
  373. package/dist/delete-dashboard-LYMRPF27.js +0 -19
  374. package/dist/delete-evaluator-RKYC4TNB.js +0 -16
  375. package/dist/discover-evaluator-schema-ESSHQGPY.js +0 -1912
  376. package/dist/get-analytics-R6AZD3AW.js +0 -73
  377. package/dist/get-dashboard-P4WWVHFU.js +0 -30
  378. package/dist/get-dataset-AKTYSQDI.js +0 -49
  379. package/dist/index.d.ts +0 -2
  380. package/dist/langwatch-api-dashboards-2KJL2M42.js +0 -18
  381. package/dist/langwatch-api-secrets-OEBD6GBU.js +0 -41
  382. package/dist/langwatch-api-triggers-5E6DDGX6.js +0 -31
  383. package/dist/list-dashboards-Z3BZKDGF.js +0 -33
  384. package/dist/run-agent-CAGWGTYT.js +0 -72
  385. package/dist/search-traces-4XTJYXIK.js +0 -79
  386. package/src/__tests__/agent-tools.unit.test.ts +0 -257
  387. package/src/__tests__/all-tools.integration.test.ts +0 -1872
  388. package/src/__tests__/config.unit.test.ts +0 -202
  389. package/src/__tests__/datasets.integration.test.ts +0 -620
  390. package/src/__tests__/datasets.unit.test.ts +0 -244
  391. package/src/__tests__/date-parsing.unit.test.ts +0 -78
  392. package/src/__tests__/discover-evaluator-schema.unit.test.ts +0 -89
  393. package/src/__tests__/discover-schema.unit.test.ts +0 -118
  394. package/src/__tests__/docker.integration.test.ts +0 -318
  395. package/src/__tests__/documentation-fetch-security.integration.test.ts +0 -182
  396. package/src/__tests__/evaluator-tools.unit.test.ts +0 -262
  397. package/src/__tests__/experiment-list-tools.unit.test.ts +0 -157
  398. package/src/__tests__/experiment-status.unit.test.ts +0 -194
  399. package/src/__tests__/feature-map-tool-drift.unit.test.ts +0 -93
  400. package/src/__tests__/get-experiment-results.unit.test.ts +0 -313
  401. package/src/__tests__/http-security-origin.unit.test.ts +0 -97
  402. package/src/__tests__/http-security-sessions.unit.test.ts +0 -209
  403. package/src/__tests__/http-security-verifier.unit.test.ts +0 -248
  404. package/src/__tests__/http-server-allocation.integration.test.ts +0 -279
  405. package/src/__tests__/http-server-origin.integration.test.ts +0 -169
  406. package/src/__tests__/http-server-session-auth.integration.test.ts +0 -341
  407. package/src/__tests__/http-transport.integration.test.ts +0 -395
  408. package/src/__tests__/integration.integration.test.ts +0 -284
  409. package/src/__tests__/langwatch-api.unit.test.ts +0 -661
  410. package/src/__tests__/model-provider-tools.unit.test.ts +0 -190
  411. package/src/__tests__/project-api-key-tools.unit.test.ts +0 -451
  412. package/src/__tests__/prompt-response-types.unit.test.ts +0 -51
  413. package/src/__tests__/prompts.integration.test.ts +0 -736
  414. package/src/__tests__/public-http-request-security.integration.test.ts +0 -118
  415. package/src/__tests__/report-issue.integration.test.ts +0 -147
  416. package/src/__tests__/run-plan-tools.unit.test.ts +0 -565
  417. package/src/__tests__/scenario-tools.integration.test.ts +0 -286
  418. package/src/__tests__/scenario-tools.unit.test.ts +0 -207
  419. package/src/__tests__/schemas.unit.test.ts +0 -85
  420. package/src/__tests__/simulation-run-tools.unit.test.ts +0 -116
  421. package/src/__tests__/support/http-server-harness.ts +0 -138
  422. package/src/__tests__/test-suite-tools.unit.test.ts +0 -615
  423. package/src/__tests__/tools.integration.test.ts +0 -896
  424. package/src/schemas/filter-fields.ts +0 -119
  425. package/src/schemas/index.ts +0 -9
  426. package/src/tools/create-dashboard.ts +0 -7
  427. package/src/tools/delete-dashboard.ts +0 -7
  428. package/src/tools/get-dashboard.ts +0 -22
@@ -1,14 +1,2761 @@
1
1
  import {
2
- createMcpServer
3
- } from "./chunk-QRO35SOM.js";
4
- import "./chunk-EYXMLE5X.js";
5
- import "./chunk-HV4DWKBI.js";
6
- import "./chunk-CKNAH5TG.js";
7
- import "./chunk-UF6XIGYR.js";
8
- import "./chunk-6DJDEXET.js";
9
- import "./chunk-GELJ2MQR.js";
10
- import "./chunk-WT4PO4A3.js";
11
- import "./chunk-SAS5OB7F.js";
2
+ SLACK_DELIVERY_NOTE,
3
+ TRIGGER_FILTERS_DESCRIPTION,
4
+ TRIGGER_FILTER_QUERY_DESCRIPTION,
5
+ actionParamsSchema,
6
+ alertTypeSchema,
7
+ graphAlertSchema,
8
+ notificationCadenceSchema,
9
+ reportSchema,
10
+ templatesSchema,
11
+ triggerActionSchema,
12
+ validateActionParamsForAction
13
+ } from "./chunk-3YLN3ATJ.js";
14
+ import {
15
+ testAgent
16
+ } from "./chunk-EYPJBL7P.js";
17
+ import {
18
+ runParametersSchema,
19
+ runPlanScopeSchema,
20
+ runPlanTargetSchema
21
+ } from "./chunk-O7EMWEVJ.js";
22
+ import {
23
+ evaluatorAttachmentsSchema,
24
+ scenarioFieldValuesSchema,
25
+ suiteFieldsSchema,
26
+ toWireAttachments
27
+ } from "./chunk-U24EACC4.js";
28
+ import {
29
+ formatEvaluatorAttachments,
30
+ formatSuiteFields
31
+ } from "./chunk-6YZUOD34.js";
32
+ import {
33
+ updateTestSuite
34
+ } from "./chunk-YCIUEZVK.js";
35
+ import {
36
+ LangWatchApiError,
37
+ makeRequest,
38
+ package_default
39
+ } from "./chunk-7CZQDZ2K.js";
40
+ import {
41
+ requireApiKey
42
+ } from "./chunk-DULFP3MK.js";
43
+
44
+ // src/create-mcp-server.ts
45
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
46
+ import { z as z3 } from "zod";
47
+
48
+ // src/documentation-fetch.ts
49
+ var TRUSTED_DOCUMENTATION_ORIGIN = "https://langwatch.ai";
50
+ var MAX_DOCUMENTATION_BYTES = 2 * 1024 * 1024;
51
+ var DOCUMENTATION_TIMEOUT_MS = 3e4;
52
+ var DOCUMENTATION_CONTENT_TYPES = /* @__PURE__ */ new Set(["text/markdown", "text/plain"]);
53
+ var DOCUMENTATION_CONFIG = {
54
+ langwatch: {
55
+ defaultPath: "/docs/llms.txt",
56
+ namespace: "/docs"
57
+ },
58
+ scenario: {
59
+ defaultPath: "/scenario/llms.txt",
60
+ namespace: "/scenario"
61
+ }
62
+ };
63
+ function documentationUrlError(namespace) {
64
+ return new Error(
65
+ `Only a trusted LangWatch documentation URL under ${TRUSTED_DOCUMENTATION_ORIGIN}${namespace}/ may be fetched`
66
+ );
67
+ }
68
+ function resolveDocumentationUrl(kind, input) {
69
+ const { defaultPath, namespace } = DOCUMENTATION_CONFIG[kind];
70
+ const value = input?.trim();
71
+ let url;
72
+ try {
73
+ if (!value) {
74
+ url = new URL(defaultPath, TRUSTED_DOCUMENTATION_ORIGIN);
75
+ } else if (/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(value) || value.startsWith("//")) {
76
+ url = new URL(value, TRUSTED_DOCUMENTATION_ORIGIN);
77
+ } else {
78
+ const relativePath = value.replace(/^\/+/, "");
79
+ const namespacePath = namespace.slice(1);
80
+ const path = relativePath === namespacePath || relativePath.startsWith(`${namespacePath}/`) ? `/${relativePath}` : `${namespace}/${relativePath}`;
81
+ url = new URL(path, TRUSTED_DOCUMENTATION_ORIGIN);
82
+ }
83
+ } catch {
84
+ throw documentationUrlError(namespace);
85
+ }
86
+ const isTrustedDocumentationUrl = url.protocol === "https:" && url.hostname === "langwatch.ai" && url.port === "" && url.username === "" && url.password === "" && url.pathname.startsWith(`${namespace}/`);
87
+ if (!isTrustedDocumentationUrl) {
88
+ throw documentationUrlError(namespace);
89
+ }
90
+ const lowerPath = url.pathname.toLowerCase();
91
+ if (!lowerPath.endsWith(".md") && !lowerPath.endsWith(".txt")) {
92
+ url.pathname += ".md";
93
+ }
94
+ return url;
95
+ }
96
+ async function fetchDocumentation(kind, input, fetchImplementation = fetch) {
97
+ const url = resolveDocumentationUrl(kind, input);
98
+ const response = await fetchImplementation(url, {
99
+ redirect: "error",
100
+ signal: AbortSignal.timeout(DOCUMENTATION_TIMEOUT_MS)
101
+ });
102
+ if (!response.ok) {
103
+ throw new Error(`Documentation request failed with status ${response.status}`);
104
+ }
105
+ const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim() ?? "";
106
+ if (!DOCUMENTATION_CONTENT_TYPES.has(contentType)) {
107
+ throw new Error("Documentation response has an unexpected content type");
108
+ }
109
+ const contentLength = Number(response.headers.get("content-length"));
110
+ if (Number.isFinite(contentLength) && contentLength > MAX_DOCUMENTATION_BYTES) {
111
+ throw new Error("Documentation response is too large");
112
+ }
113
+ if (!response.body) {
114
+ return "";
115
+ }
116
+ const reader = response.body.getReader();
117
+ const decoder = new TextDecoder();
118
+ let receivedBytes = 0;
119
+ let text = "";
120
+ for (; ; ) {
121
+ const { done, value } = await reader.read();
122
+ if (done) {
123
+ return text + decoder.decode();
124
+ }
125
+ receivedBytes += value.byteLength;
126
+ if (receivedBytes > MAX_DOCUMENTATION_BYTES) {
127
+ await reader.cancel();
128
+ throw new Error("Documentation response is too large");
129
+ }
130
+ text += decoder.decode(value, { stream: true });
131
+ }
132
+ }
133
+
134
+ // src/schemas/create-dataset.ts
135
+ import { z } from "zod";
136
+ var DATASET_COLUMN_TYPES = [
137
+ "string",
138
+ "boolean",
139
+ "number",
140
+ "date",
141
+ "list",
142
+ "json",
143
+ "spans",
144
+ "rag_contexts",
145
+ "chat_messages",
146
+ "annotations",
147
+ "evaluations",
148
+ "image",
149
+ "file"
150
+ ];
151
+ var datasetColumnTypeSchema = z.enum(DATASET_COLUMN_TYPES);
152
+ var datasetColumnDefinitionSchema = z.object({
153
+ name: z.string().describe("Column name"),
154
+ type: datasetColumnTypeSchema.describe("Column type")
155
+ });
156
+ var createDatasetSchema = z.object({
157
+ name: z.string().min(1).describe("Dataset name"),
158
+ columnTypes: z.array(datasetColumnDefinitionSchema).optional().describe("Column definitions for the dataset")
159
+ });
160
+
161
+ // src/schemas/widget-sources.ts
162
+ import { z as z2 } from "zod";
163
+ var WIDGET_SOURCES = [
164
+ "application",
165
+ "simulation",
166
+ "evaluation",
167
+ "sample",
168
+ "coding_agent",
169
+ "ai_tool",
170
+ "gateway",
171
+ "workflow",
172
+ "playground",
173
+ "langy",
174
+ "other"
175
+ ];
176
+ var widgetSourcesInput = z2.array(z2.enum(WIDGET_SOURCES)).max(WIDGET_SOURCES.length).nullable().optional().describe(
177
+ `The trace origins every query of the widget counts: any of ${WIDGET_SOURCES.join(", ")} (other = any origin not listed). A new widget counts ["application"]; add "langy" to count your own conversations, as a widget about Langy's requests needs. null makes it unset again, counting the default.`
178
+ );
179
+
180
+ // src/tools/experiment-run-status.ts
181
+ var INTERRUPTED_THRESHOLD_MS = 5 * 60 * 1e3;
182
+ var deriveRunStatus = (timestamps, now = Date.now()) => {
183
+ if (timestamps.stoppedAt != null) return "stopped";
184
+ if (timestamps.finishedAt != null) return "completed";
185
+ if (timestamps.updatedAt != null && now - timestamps.updatedAt > INTERRUPTED_THRESHOLD_MS) {
186
+ return "interrupted";
187
+ }
188
+ return "running";
189
+ };
190
+ var isTerminalStatus = (status) => status === "completed" || status === "stopped";
191
+
192
+ // src/tools/get-experiment-results.ts
193
+ var DEFAULT_ROW_CAP = 50;
194
+ var rowKey = (index, targetId) => `${index}:${targetId ?? ""}`;
195
+ var summarizeEntry = (entry) => {
196
+ const candidates = ["input", "question", "query", "prompt", "user"];
197
+ for (const key of candidates) {
198
+ const value = entry[key];
199
+ if (typeof value === "string" && value.length > 0) {
200
+ return value.length > 80 ? `${value.slice(0, 77)}...` : value;
201
+ }
202
+ }
203
+ return "";
204
+ };
205
+ var isFailedEvaluation = (e) => e.status === "error" || e.passed === false;
206
+ var isFailedRow = ({
207
+ entry,
208
+ evaluations
209
+ }) => Boolean(entry.error) || evaluations.some(isFailedEvaluation);
210
+ function hasNumericStatus(error) {
211
+ if (typeof error !== "object" || error === null) return false;
212
+ return "status" in error && typeof error.status === "number";
213
+ }
214
+ function isNotFoundError(error) {
215
+ const message = error instanceof Error ? error.message : String(error);
216
+ let status;
217
+ if (error instanceof LangWatchApiError) status = error.status;
218
+ else if (hasNumericStatus(error)) status = error.status;
219
+ return status === 404 || status === void 0 && /404|not found/i.test(message);
220
+ }
221
+ async function fetchRunResults(params) {
222
+ const search = new URLSearchParams();
223
+ if (params.experimentSlug) search.set("experimentSlug", params.experimentSlug);
224
+ const qs = search.toString() ? `?${search.toString()}` : "";
225
+ return await makeRequest(
226
+ "GET",
227
+ `/api/v1/experiments/runs/${encodeURIComponent(params.runId)}/results${qs}`
228
+ );
229
+ }
230
+ function groupEvaluationsByRow({
231
+ evaluations,
232
+ evaluatorFilter
233
+ }) {
234
+ const byRow = /* @__PURE__ */ new Map();
235
+ for (const evaluation of evaluations) {
236
+ if (evaluatorFilter && evaluation.evaluator !== evaluatorFilter) continue;
237
+ const key = rowKey(evaluation.index, evaluation.targetId);
238
+ const list = byRow.get(key) ?? [];
239
+ list.push(evaluation);
240
+ byRow.set(key, list);
241
+ }
242
+ return byRow;
243
+ }
244
+ function tallyEvaluatorStats(rows) {
245
+ const averages = /* @__PURE__ */ new Map();
246
+ for (const r of rows) {
247
+ for (const e of r.evaluations) {
248
+ const stats = averages.get(e.evaluator) ?? {
249
+ sum: 0,
250
+ count: 0,
251
+ passed: 0,
252
+ failed: 0,
253
+ errored: 0
254
+ };
255
+ if (typeof e.score === "number") {
256
+ stats.sum += e.score;
257
+ stats.count += 1;
258
+ }
259
+ if (e.status === "error") stats.errored += 1;
260
+ else if (e.passed === true) stats.passed += 1;
261
+ else if (e.passed === false) stats.failed += 1;
262
+ averages.set(e.evaluator, stats);
263
+ }
264
+ }
265
+ return averages;
266
+ }
267
+ function headerLines({
268
+ results,
269
+ runStatus: runStatus2,
270
+ filter,
271
+ evaluatorFilter
272
+ }) {
273
+ const lines = [];
274
+ lines.push(`# Evaluation Results: ${results.runId}`);
275
+ lines.push("");
276
+ lines.push(`**Experiment**: ${results.experimentId}`);
277
+ lines.push(`**Status**: ${runStatus2}`);
278
+ if (typeof results.total === "number" && results.total > 0) {
279
+ lines.push(`**Progress**: ${results.progress ?? results.dataset.length}/${results.total} rows`);
280
+ }
281
+ lines.push(`**Total rows**: ${results.dataset.length}`);
282
+ lines.push(`**Total evaluations**: ${results.evaluations.length}`);
283
+ if (filter === "failed") lines.push(`**Filter**: failed only`);
284
+ if (evaluatorFilter) lines.push(`**Evaluator filter**: ${evaluatorFilter}`);
285
+ if (!isTerminalStatus(runStatus2)) {
286
+ lines.push("");
287
+ lines.push(
288
+ runStatus2 === "interrupted" ? "> These are partial results. The run never sent a finished/stopped marker and has had no updates recently, so it likely was interrupted before completing. The rows below are everything recorded so far." : "> These are partial results. The run is still in progress, so more rows may appear on a later call."
289
+ );
290
+ }
291
+ const stored = results.completeness;
292
+ if (isTerminalStatus(runStatus2) && stored && !stored.complete) {
293
+ lines.push("");
294
+ lines.push(
295
+ `> These are partial results. The run has ended, but the platform is still storing it: ${storedCount(stored.dataset)} rows and ${storedCount(stored.evaluations)} evaluations so far. Call again shortly for the whole run.`
296
+ );
297
+ }
298
+ lines.push("");
299
+ return lines;
300
+ }
301
+ function evaluatorSummaryLines(averages) {
302
+ if (averages.size === 0) return [];
303
+ const lines = [
304
+ "## Evaluator Summary",
305
+ "",
306
+ "| Evaluator | Avg Score | Passed | Failed | Errored |",
307
+ "| --- | --- | --- | --- | --- |"
308
+ ];
309
+ for (const [name, stats] of averages) {
310
+ const avg = stats.count > 0 ? (stats.sum / stats.count).toFixed(3) : "\u2014";
311
+ lines.push(`| ${name} | ${avg} | ${stats.passed} | ${stats.failed} | ${stats.errored} |`);
312
+ }
313
+ lines.push("");
314
+ return lines;
315
+ }
316
+ var storedCount = ({ received, expected }) => expected === null ? `${received}` : `${received} of ${expected}`;
317
+ function noRowsLine({ filter, runStatus: runStatus2 }) {
318
+ if (filter === "failed") return "_No rows matched the filter._";
319
+ if (runStatus2 === "running")
320
+ return "_No rows recorded yet. The run is still in progress; call again shortly._";
321
+ if (runStatus2 === "interrupted") return "_No rows were recorded before the run was interrupted._";
322
+ return "_No rows recorded for this run._";
323
+ }
324
+ function evaluationLines(e) {
325
+ const parts = [`**${e.evaluator}**`];
326
+ if (e.status === "error") parts.push("status=error");
327
+ else if (typeof e.score === "number") parts.push(`score=${e.score.toFixed(3)}`);
328
+ if (typeof e.passed === "boolean") parts.push(`passed=${e.passed ? "yes" : "no"}`);
329
+ if (e.label) parts.push(`label=${e.label}`);
330
+ const lines = [`- ${parts.join(" \xB7 ")}`];
331
+ if (e.details) lines.push(` - details: ${e.details}`);
332
+ return lines;
333
+ }
334
+ function rowLines({
335
+ row: { entry, evaluations },
336
+ anyEvaluators
337
+ }) {
338
+ const summary = summarizeEntry(entry.entry);
339
+ const lines = [`### Row #${entry.index}${summary ? ` \u2014 ${summary}` : ""}`];
340
+ if (entry.error) lines.push(`- **Error**: ${entry.error}`);
341
+ if (entry.traceId) lines.push(`- **Trace ID**: \`${entry.traceId}\``);
342
+ if (evaluations.length === 0) {
343
+ lines.push(
344
+ anyEvaluators ? "- _No evaluations recorded for this row_" : "- _No evaluations recorded_"
345
+ );
346
+ }
347
+ for (const e of evaluations) lines.push(...evaluationLines(e));
348
+ lines.push("");
349
+ return lines;
350
+ }
351
+ function notFoundReport({ runId, expired }) {
352
+ const lines = [`# Evaluation Results: ${runId}`, "", "**Status**: not found", ""];
353
+ if (expired) {
354
+ lines.push(
355
+ `Could not load results for run \`${runId}\`. The run id may be incorrect, or its run state may have expired (Redis keeps it for 24h).`,
356
+ "",
357
+ "> Pass `experimentSlug` to load results for runs older than 24h. Discover the slug with `platform_experiment_list`, then `platform_experiment_list_runs` for the run ids."
358
+ );
359
+ } else {
360
+ lines.push(
361
+ `Could not load results for run \`${runId}\`.`,
362
+ "",
363
+ "> Pass `experimentSlug` if the run is older than 24h. Discover the slug with `platform_experiment_list`, then `platform_experiment_list_runs` to confirm the run id."
364
+ );
365
+ }
366
+ return lines.join("\n");
367
+ }
368
+ async function handleExperimentResults(params) {
369
+ const filter = params.filter ?? "all";
370
+ const evaluatorFilter = params.evaluator?.trim();
371
+ const limit = typeof params.limit === "number" && params.limit > 0 ? Math.min(params.limit, DEFAULT_ROW_CAP) : DEFAULT_ROW_CAP;
372
+ let results;
373
+ try {
374
+ results = await fetchRunResults(params);
375
+ } catch (error) {
376
+ if (isNotFoundError(error)) return notFoundReport({ runId: params.runId, expired: true });
377
+ throw error;
378
+ }
379
+ if (!results) return notFoundReport({ runId: params.runId, expired: false });
380
+ const runStatus2 = deriveRunStatus(results.timestamps);
381
+ const evaluationsByRow = groupEvaluationsByRow({
382
+ evaluations: results.evaluations,
383
+ evaluatorFilter
384
+ });
385
+ const evaluatorNames = new Set(
386
+ (evaluatorFilter ? results.evaluations.filter((e) => e.evaluator === evaluatorFilter) : results.evaluations).map((e) => e.evaluator)
387
+ );
388
+ const allRows = results.dataset.map((entry) => ({
389
+ entry,
390
+ evaluations: evaluationsByRow.get(rowKey(entry.index, entry.targetId)) ?? []
391
+ }));
392
+ const matching = filter === "failed" ? allRows.filter((r) => isFailedRow(r)) : allRows;
393
+ const truncated = matching.length > limit;
394
+ const rows = matching.slice(0, limit);
395
+ const lines = [
396
+ ...headerLines({ results, runStatus: runStatus2, filter, evaluatorFilter }),
397
+ ...evaluatorSummaryLines(tallyEvaluatorStats(matching))
398
+ ];
399
+ if (rows.length === 0) {
400
+ lines.push(noRowsLine({ filter, runStatus: runStatus2 }));
401
+ return lines.join("\n");
402
+ }
403
+ lines.push(`## Rows (${rows.length}${truncated ? ` of ${matching.length}` : ""})`);
404
+ lines.push("");
405
+ for (const row of rows) lines.push(...rowLines({ row, anyEvaluators: evaluatorNames.size > 0 }));
406
+ if (truncated) {
407
+ lines.push(
408
+ `> Output truncated to ${limit} rows of ${matching.length} matching to protect the agent's context window. Pass \`limit\` to expand or filter further.`
409
+ );
410
+ }
411
+ return lines.join("\n");
412
+ }
413
+
414
+ // src/tools/list-experiment-runs.ts
415
+ var DEFAULT_LIMIT = 25;
416
+ var MAX_LIMIT = 100;
417
+ var formatEpoch = (epoch) => {
418
+ if (!epoch || !Number.isFinite(epoch)) return "\u2014";
419
+ return new Date(epoch).toISOString().replace("T", " ").slice(0, 19) + " UTC";
420
+ };
421
+ var passRate = (evaluations) => {
422
+ const entries = Object.values(evaluations ?? {});
423
+ if (entries.length === 0) return "\u2014";
424
+ const passed = entries.filter((e) => typeof e.averagePassed === "number");
425
+ if (passed.length === 0) {
426
+ const scored = entries.filter((e) => typeof e.averageScore === "number");
427
+ if (scored.length === 0) return "\u2014";
428
+ const avg2 = scored.reduce((sum, e) => sum + (e.averageScore ?? 0), 0) / scored.length;
429
+ return `${avg2.toFixed(2)} avg`;
430
+ }
431
+ const avg = passed.reduce((sum, e) => sum + (e.averagePassed ?? 0), 0) / passed.length;
432
+ return `${(avg * 100).toFixed(0)}% pass`;
433
+ };
434
+ var runStatus = (run) => {
435
+ if (run.timestamps.stoppedAt) return "stopped";
436
+ if (run.timestamps.finishedAt) return "completed";
437
+ return "running";
438
+ };
439
+ async function handleExperimentListRuns(params) {
440
+ const requested = typeof params.limit === "number" && params.limit > 0 ? params.limit : DEFAULT_LIMIT;
441
+ const effectiveLimit = Math.min(requested, MAX_LIMIT);
442
+ const search = new URLSearchParams();
443
+ search.set("experimentSlug", params.experimentSlug);
444
+ search.set("pageSize", String(effectiveLimit));
445
+ let result;
446
+ try {
447
+ result = await makeRequest(
448
+ "GET",
449
+ `/api/v1/experiments/runs?${search.toString()}`
450
+ );
451
+ } catch (error) {
452
+ const message = error instanceof Error ? error.message : String(error);
453
+ if (/404|not found/i.test(message)) {
454
+ return [
455
+ `# Evaluation Runs: ${params.experimentSlug}`,
456
+ "",
457
+ "**Status**: experiment not found",
458
+ "",
459
+ `No experiment with slug \`${params.experimentSlug}\` exists in this project.`,
460
+ "",
461
+ "> Run `platform_experiment_list` to discover available experiment slugs."
462
+ ].join("\n");
463
+ }
464
+ throw error;
465
+ }
466
+ if (result.runs.length === 0) {
467
+ return [
468
+ `# Evaluation Runs: ${result.experimentSlug}`,
469
+ "",
470
+ "_This experiment has no runs yet._",
471
+ "",
472
+ `> Trigger a run with \`platform_run_experiment\` (slug: \`${result.experimentSlug}\`) to populate this list.`
473
+ ].join("\n");
474
+ }
475
+ const lines = [];
476
+ lines.push(`# Evaluation Runs: ${result.experimentSlug}`);
477
+ lines.push("");
478
+ lines.push(
479
+ `Showing ${result.runs.length} of ${result.pagination.totalHits} run${result.pagination.totalHits === 1 ? "" : "s"}.`
480
+ );
481
+ if (result.pagination.hasMore) {
482
+ lines.push(
483
+ `> More runs exist beyond this limit. Re-run with a higher \`limit\` (max ${MAX_LIMIT}) if needed.`
484
+ );
485
+ }
486
+ lines.push("");
487
+ lines.push("| Run ID | Status | Started | Finished | Result |");
488
+ lines.push("|--------|--------|---------|----------|--------|");
489
+ for (const run of result.runs) {
490
+ lines.push(
491
+ `| \`${run.runId}\` | ${runStatus(run)} | ${formatEpoch(run.timestamps.createdAt)} | ${formatEpoch(run.timestamps.finishedAt ?? null)} | ${passRate(run.summary?.evaluations ?? {})} |`
492
+ );
493
+ }
494
+ lines.push("");
495
+ lines.push(
496
+ "> Use `platform_experiment_status <runId>` for live progress, or `platform_experiment_results <runId>` for per-row results."
497
+ );
498
+ return lines.join("\n");
499
+ }
500
+
501
+ // src/tools/list-experiments.ts
502
+ var DEFAULT_LIMIT2 = 25;
503
+ var MAX_LIMIT2 = 100;
504
+ var formatTimestamp = (iso) => {
505
+ if (!iso) return "\u2014";
506
+ const d = new Date(iso);
507
+ if (Number.isNaN(d.getTime())) return iso;
508
+ return d.toISOString().replace("T", " ").slice(0, 19) + " UTC";
509
+ };
510
+ var escapeMarkdownTableCell = (value) => value.replace(/[\\|]/g, (c) => `\\${c}`).replace(/\n/g, " ");
511
+ var formatInlineCode = (value) => `\`${value.replace(/[\\`|]/g, (c) => `\\${c}`).replace(/\n/g, " ")}\``;
512
+ async function handleExperimentList(params) {
513
+ const requested = typeof params.limit === "number" && params.limit > 0 ? params.limit : DEFAULT_LIMIT2;
514
+ const effectiveLimit = Math.min(requested, MAX_LIMIT2);
515
+ const search = new URLSearchParams();
516
+ search.set("pageSize", String(effectiveLimit));
517
+ const result = await makeRequest(
518
+ "GET",
519
+ `/api/v1/experiments?${search.toString()}`
520
+ );
521
+ if (result.experiments.length === 0) {
522
+ return [
523
+ "# Experiments",
524
+ "",
525
+ "_No experiments found in this project._",
526
+ "",
527
+ "> Create an experiment in the LangWatch dashboard, then re-run this tool to discover its slug."
528
+ ].join("\n");
529
+ }
530
+ const lines = [];
531
+ lines.push("# Experiments");
532
+ lines.push("");
533
+ lines.push(
534
+ `Showing ${result.experiments.length} of ${result.pagination.totalHits} experiment${result.pagination.totalHits === 1 ? "" : "s"}.`
535
+ );
536
+ if (result.pagination.hasMore) {
537
+ lines.push(
538
+ `> More experiments exist beyond this limit. Re-run with a higher \`limit\` (max ${MAX_LIMIT2}) if needed.`
539
+ );
540
+ }
541
+ lines.push("");
542
+ lines.push("| Slug | Name | Runs | Last Run |");
543
+ lines.push("|------|------|------|----------|");
544
+ for (const exp of result.experiments) {
545
+ const name = escapeMarkdownTableCell(exp.name ?? exp.slug);
546
+ lines.push(
547
+ `| ${formatInlineCode(exp.slug)} | ${name} | ${exp.runsCount} | ${formatTimestamp(exp.lastRunAt)} |`
548
+ );
549
+ }
550
+ lines.push("");
551
+ lines.push("> Use `platform_experiment_list_runs` with one of these slugs to see its runs.");
552
+ return lines.join("\n");
553
+ }
554
+
555
+ // src/tools/run-experiment.ts
556
+ async function handleRunExperiment(params) {
557
+ const result = await makeRequest(
558
+ "POST",
559
+ `/api/v1/experiments/${encodeURIComponent(params.slug)}/run`,
560
+ params.concurrency !== void 0 ? { concurrency: params.concurrency } : void 0
561
+ );
562
+ const lines = [];
563
+ lines.push(`Evaluation started!
564
+ `);
565
+ lines.push(`**Run ID**: ${result.runId}`);
566
+ lines.push(`**Status**: ${result.status}`);
567
+ lines.push(`**Total cells**: ${result.total}`);
568
+ if (result.runUrl) lines.push(`**View at**: ${result.runUrl}`);
569
+ lines.push("");
570
+ lines.push(
571
+ `> Use \`platform_experiment_status\` with run ID "${result.runId}" to check progress.`
572
+ );
573
+ return lines.join("\n");
574
+ }
575
+ async function statusFromResults(params) {
576
+ const search = new URLSearchParams();
577
+ if (params.experimentSlug) search.set("experimentSlug", params.experimentSlug);
578
+ const qs = search.toString() ? `?${search.toString()}` : "";
579
+ let results;
580
+ try {
581
+ results = await makeRequest(
582
+ "GET",
583
+ `/api/v1/experiments/runs/${encodeURIComponent(params.runId)}/results${qs}`
584
+ );
585
+ } catch (error) {
586
+ const code = error instanceof LangWatchApiError ? error.status : void 0;
587
+ const message = error instanceof Error ? error.message : String(error);
588
+ if (code === 404 || code === void 0 && /404|not found/i.test(message)) {
589
+ return null;
590
+ }
591
+ throw error;
592
+ }
593
+ const status = deriveRunStatus(results.timestamps);
594
+ const total = results.total ?? results.dataset?.length ?? 0;
595
+ const progress = results.progress ?? results.dataset?.length ?? 0;
596
+ const lines = [];
597
+ lines.push(`# Evaluation Run ${params.runId}
598
+ `);
599
+ lines.push(`**Status**: ${status}`);
600
+ lines.push(`**Progress**: ${progress}/${total} cells`);
601
+ if (results.timestamps.createdAt) {
602
+ lines.push(`**Started**: ${new Date(results.timestamps.createdAt).toISOString()}`);
603
+ }
604
+ if (results.timestamps.finishedAt) {
605
+ lines.push(`**Finished**: ${new Date(results.timestamps.finishedAt).toISOString()}`);
606
+ }
607
+ if (results.timestamps.stoppedAt) {
608
+ lines.push(`**Stopped**: ${new Date(results.timestamps.stoppedAt).toISOString()}`);
609
+ }
610
+ lines.push("");
611
+ lines.push(
612
+ "> Use `platform_experiment_results` to fetch the per-row scores (partial results are available even while running)."
613
+ );
614
+ return lines.join("\n");
615
+ }
616
+ async function statusAfterFailedRead({
617
+ params,
618
+ error
619
+ }) {
620
+ const code = error instanceof LangWatchApiError ? error.status : void 0;
621
+ const message = error instanceof Error ? error.message : String(error);
622
+ if (code === 404 || /404|not found/i.test(message)) {
623
+ const fallback = await statusFromResults(params);
624
+ if (fallback) return fallback;
625
+ return [
626
+ `# Evaluation Run ${params.runId}`,
627
+ "",
628
+ "**Status**: not found",
629
+ "",
630
+ `Could not find run \`${params.runId}\`. SDK-logged runs and runs older than 24h are not in the live run-state and must be resolved by experiment slug.`,
631
+ "",
632
+ "> Pass `experimentSlug`: discover it with `platform_experiment_list`, then use `platform_experiment_list_runs` for the run ids. Or fetch the rows directly with `platform_experiment_results`."
633
+ ].join("\n");
634
+ }
635
+ throw error;
636
+ }
637
+ async function handleExperimentStatus(params) {
638
+ let status;
639
+ try {
640
+ status = await makeRequest(
641
+ "GET",
642
+ `/api/v1/experiments/runs/${encodeURIComponent(params.runId)}`
643
+ );
644
+ } catch (error) {
645
+ return statusAfterFailedRead({ params, error });
646
+ }
647
+ const lines = [];
648
+ lines.push(`# Evaluation Run ${status.runId}
649
+ `);
650
+ lines.push(`**Status**: ${status.status}`);
651
+ lines.push(`**Progress**: ${status.progress}/${status.total} cells`);
652
+ if (status.startedAt) {
653
+ lines.push(`**Started**: ${new Date(status.startedAt).toISOString()}`);
654
+ }
655
+ if (status.finishedAt) {
656
+ lines.push(`**Finished**: ${new Date(status.finishedAt).toISOString()}`);
657
+ }
658
+ if (status.summary) {
659
+ lines.push("\n## Summary\n");
660
+ if (status.summary.completedCells !== void 0) {
661
+ lines.push(`**Completed**: ${status.summary.completedCells}`);
662
+ }
663
+ if (status.summary.failedCells) {
664
+ lines.push(`**Failed**: ${status.summary.failedCells}`);
665
+ }
666
+ if (status.summary.duration) {
667
+ lines.push(`**Duration**: ${(status.summary.duration / 1e3).toFixed(1)}s`);
668
+ }
669
+ if (status.summary.runUrl) {
670
+ lines.push(`**View results**: ${status.summary.runUrl}`);
671
+ }
672
+ }
673
+ return lines.join("\n");
674
+ }
675
+
676
+ // src/tools/test-agent.ts
677
+ async function handleTestAgent(params) {
678
+ const run = await testAgent(params.id);
679
+ return [
680
+ 'Test run scheduled. The user sends "ping"; the run succeeds when the agent answers.',
681
+ "",
682
+ `**Scenario run ID:** ${run.scenarioRunId}`,
683
+ `**Batch run ID:** ${run.batchRunId}`,
684
+ "",
685
+ `Follow it with platform_get_simulation_run using scenarioRunId "${run.scenarioRunId}".`
686
+ ].join("\n");
687
+ }
688
+
689
+ // src/tools/update-test-suite.ts
690
+ async function handleUpdateTestSuite(params) {
691
+ const suite = await updateTestSuite({
692
+ id: params.id,
693
+ ...params.name !== void 0 ? { name: params.name } : {},
694
+ ...params.fields !== void 0 ? { fields: params.fields } : {},
695
+ ...params.evaluators !== void 0 ? { evaluators: toWireAttachments(params.evaluators) } : {}
696
+ });
697
+ return [
698
+ `Test suite "${suite.name}" updated.`,
699
+ "",
700
+ `**ID**: ${suite.id}`,
701
+ `**Slug**: ${suite.slug}`,
702
+ `**Scenarios**: ${suite.scenarioCount}`,
703
+ ...formatSuiteFields(suite.fields),
704
+ ...formatEvaluatorAttachments(suite.evaluators),
705
+ "",
706
+ `**View**: ${suite.platformUrl}`
707
+ ].join("\n");
708
+ }
709
+
710
+ // src/create-mcp-server.ts
711
+ var modelSchema = z3.string().describe(
712
+ 'Model in "provider/model-name" format, e.g., "openai/gpt-6-luna", "anthropic/claude-sonnet-4-5-20250929"'
713
+ );
714
+ function createMcpServer() {
715
+ const server = new McpServer({
716
+ name: "LangWatch",
717
+ version: package_default.version
718
+ });
719
+ registerTools(server);
720
+ return server;
721
+ }
722
+ function withToolLogging(toolName, fn) {
723
+ return async (...args) => {
724
+ try {
725
+ return await fn(...args);
726
+ } catch (error) {
727
+ console.error(
728
+ `[MCP tool] ${toolName} failed:`,
729
+ error instanceof Error ? error.message : error,
730
+ error instanceof Error ? error.stack : ""
731
+ );
732
+ throw error;
733
+ }
734
+ };
735
+ }
736
+ function registerTools(server) {
737
+ registerDocsTools(server);
738
+ registerObservabilityTools(server);
739
+ registerPromptTools(server);
740
+ registerScenarioTools(server);
741
+ registerRunPlanTools(server);
742
+ registerTestSuiteTools(server);
743
+ registerSimulationRunTools(server);
744
+ registerEvaluatorTools(server);
745
+ registerModelProviderTools(server);
746
+ registerAgentTools(server);
747
+ registerAnalyticsReportTools(server);
748
+ registerDashboardTools(server);
749
+ registerWorkflowTools(server);
750
+ registerAnnotationTools(server);
751
+ registerTriggerTools(server);
752
+ registerMonitorTools(server);
753
+ registerSecretTools(server);
754
+ registerExperimentTools(server);
755
+ registerDatasetTools(server);
756
+ registerProjectTools(server);
757
+ registerApiKeyTools(server);
758
+ }
759
+ function registerDocsTools(server) {
760
+ server.tool(
761
+ "fetch_langwatch_docs",
762
+ "Fetches the LangWatch docs for understanding how to implement LangWatch in your codebase. Always use this tool when the user asks for help with LangWatch. Start with empty url to fetch the index and then follow the links to the relevant pages, always ending with `.md` extension",
763
+ {
764
+ url: z3.string().optional().describe(
765
+ "The full url of the specific doc page. If not provided, the docs index will be fetched."
766
+ )
767
+ },
768
+ withToolLogging("fetch_langwatch_docs", async ({ url }) => {
769
+ const text = await fetchDocumentation("langwatch", url);
770
+ return { content: [{ type: "text", text }] };
771
+ })
772
+ );
773
+ server.tool(
774
+ "report_issue",
775
+ "Report an issue to the LangWatch team: anything that did not work, confused you, or took trial and error while using LangWatch (docs, MCP tools, CLI, SDKs, the platform). Use it whenever you struggled, it is how rough edges get fixed for every agent. Ask the user for permission first, then call with user_approved=true. Works without an API key. Put VERBATIM error messages in the summary; optionally attach raw session content. Secrets, API keys, emails and phone numbers are redacted locally before anything is sent.",
776
+ {
777
+ user_approved: z3.boolean().describe(
778
+ "Must be true, and only after the user explicitly agreed to send this report to LangWatch"
779
+ ),
780
+ title: z3.string().max(300).describe("One-line description of the issue"),
781
+ summary: z3.string().optional().describe(
782
+ "What you were trying to do, what went wrong (verbatim errors), and what you had to figure out the hard way"
783
+ ),
784
+ session_content: z3.string().optional().describe(
785
+ "Optional raw session transcript or log excerpt (JSONL or plain text); redacted locally before sending"
786
+ ),
787
+ contact_email: z3.string().optional().describe("Optional contact email for follow-up"),
788
+ agent: z3.string().optional().describe("Which coding agent this is, e.g. claude-code, codex, cursor")
789
+ },
790
+ withToolLogging("report_issue", async (params) => {
791
+ const { handleReportIssue } = await import("./report-issue-5HSL7CNB.js");
792
+ return {
793
+ content: [{ type: "text", text: await handleReportIssue(params) }]
794
+ };
795
+ })
796
+ );
797
+ server.tool(
798
+ "fetch_scenario_docs",
799
+ "Fetches the Scenario docs for understanding how to implement Scenario agent tests in your codebase. Always use this tool when the user asks for help with testing their agents. Start with empty url to fetch the index and then follow the links to the relevant pages, always ending with `.md` extension",
800
+ {
801
+ url: z3.string().optional().describe(
802
+ "The full url of the specific doc page. If not provided, the docs index will be fetched."
803
+ )
804
+ },
805
+ withToolLogging("fetch_scenario_docs", async ({ url }) => {
806
+ const text = await fetchDocumentation("scenario", url);
807
+ return { content: [{ type: "text", text }] };
808
+ })
809
+ );
810
+ }
811
+ function registerObservabilityTools(server) {
812
+ server.tool(
813
+ "discover_schema",
814
+ "Discover what LangWatch can be queried with: the trace filter fields and syntax, the analytics SQL views and columns, the analytics metrics, aggregation types, group-by options, scenario schema and evaluator types. Call this before using search_traces, run_query, get_analytics, scenario tools or evaluator tools, so you never guess a field, a column or a value.",
815
+ {
816
+ category: z3.enum([
817
+ "filters",
818
+ "lwql",
819
+ "metrics",
820
+ "aggregations",
821
+ "groups",
822
+ "scenarios",
823
+ "evaluators",
824
+ "all"
825
+ ]).describe(
826
+ "Which schema category to discover. 'filters' is the trace filter language, 'lwql' the analytics SQL views; both are read from the platform and need the API key"
827
+ ),
828
+ evaluatorType: z3.string().optional().describe(
829
+ "When category is 'evaluators', provide a specific evaluator type (e.g. 'langevals/llm_boolean') to get its full schema details"
830
+ )
831
+ },
832
+ withToolLogging("discover_schema", async ({ category, evaluatorType }) => {
833
+ if (category === "scenarios") {
834
+ const { formatScenarioSchema } = await import("./discover-scenario-schema-NOJY6S7E.js");
835
+ return {
836
+ content: [{ type: "text", text: formatScenarioSchema() }]
837
+ };
838
+ }
839
+ if (category === "evaluators") {
840
+ const { formatEvaluatorSchema } = await import("./discover-evaluator-schema-XKCLLCB3.js");
841
+ return {
842
+ content: [{ type: "text", text: formatEvaluatorSchema(evaluatorType) }]
843
+ };
844
+ }
845
+ const { formatSchema, needsQueryReference } = await import("./discover-schema-7WHIIATI.js");
846
+ if (needsQueryReference(category)) requireApiKey();
847
+ let text = await formatSchema(category);
848
+ if (category === "all") {
849
+ const { formatScenarioSchema } = await import("./discover-scenario-schema-NOJY6S7E.js");
850
+ text += "\n\n" + formatScenarioSchema();
851
+ const { formatEvaluatorSchema } = await import("./discover-evaluator-schema-XKCLLCB3.js");
852
+ text += "\n\n" + formatEvaluatorSchema();
853
+ }
854
+ return { content: [{ type: "text", text }] };
855
+ })
856
+ );
857
+ server.tool(
858
+ "run_query",
859
+ "Run one read-only analytics SQL statement (LangWatchQL) over this project's traces, spans, evaluations and metrics, and get the rows back as a table. This is the tool for a count, a rate, a sum, a percentile or any of them grouped or over time \u2014 questions search_traces would need many calls to answer. The statement runs exactly as written. Call discover_schema with category 'lwql' first for the views, columns and worked statements.",
860
+ {
861
+ sql: z3.string().describe(
862
+ "The SELECT to run. Filter on the dataset's time column so the read is bounded, and never select a whole attribute map"
863
+ ),
864
+ parameters: z3.record(z3.string(), z3.union([z3.string(), z3.number(), z3.boolean(), z3.null()])).optional().describe(
865
+ 'Values for the parameters the statement declares, e.g. {"days": 7} for {days:UInt32}'
866
+ )
867
+ },
868
+ withToolLogging("run_query", async (params) => {
869
+ requireApiKey();
870
+ const { handleRunQuery } = await import("./run-query-BQUHIUC2.js");
871
+ return {
872
+ content: [{ type: "text", text: await handleRunQuery(params) }]
873
+ };
874
+ })
875
+ );
876
+ server.tool(
877
+ "search_traces",
878
+ "Search LangWatch traces by content, with filters and a date range. Returns AI-readable trace digests by default. Use format: 'json' for full raw data. To fetch a trace you already have the ID for, use get_trace instead \u2014 free text does not match trace IDs.",
879
+ {
880
+ query: z3.string().optional().describe(
881
+ "Text search query. Matches captured input/output, the trace name and span names ONLY. It does not match trace IDs \u2014 to fetch a known trace ID use get_trace, or pass traceIds here for several at once."
882
+ ),
883
+ traceIds: z3.array(z3.string().min(1)).min(1).max(1e3).optional().describe(
884
+ "Exact trace IDs to fetch (1\u20131000). Use instead of query when you already know the IDs. When set and no startDate is given, the window defaults to the last 90 days rather than 24 hours."
885
+ ),
886
+ filters: z3.record(z3.string(), z3.array(z3.string())).optional().describe(
887
+ 'Filter traces by the older per-field map. Format: {"field": ["value"]}. Use discover_schema for field names.'
888
+ ),
889
+ filter: z3.string().optional().describe(
890
+ 'Filter traces with the Trace Explorer\'s own query language: "status:error AND model:gpt-*", "trace.attribute.langwatch.user_id:alice", "evaluatorVerdict:fail", a quoted phrase for free text. Reaches attribute keys, span events and evaluator verdicts that the `filters` map cannot. Combined with `filters` and `query` when you send more than one. Call discover_schema with category \'filters\' for every field and the syntax.'
891
+ ),
892
+ startDate: z3.string().min(1).optional().describe(
893
+ 'Start of the window: ISO date, or relative like "24h", "7d", "4w", "3m" \u2014 units are h (hours), d (days), w (weeks), m (30-day months). Defaults to 24h ago for a text search, or 90d when traceIds is set. Widen this when a search comes back empty.'
894
+ ),
895
+ endDate: z3.string().min(1).optional().describe("End of the window: ISO date or relative. Default: now"),
896
+ pageSize: z3.number().optional().describe("Results per page (default: 25, max: 1000)"),
897
+ scrollId: z3.string().optional().describe("Pagination token from previous search"),
898
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
899
+ },
900
+ withToolLogging("search_traces", async (params) => {
901
+ requireApiKey();
902
+ const { handleSearchTraces } = await import("./search-traces-6IR7PR4L.js");
903
+ return {
904
+ content: [{ type: "text", text: await handleSearchTraces(params) }]
905
+ };
906
+ })
907
+ );
908
+ server.tool(
909
+ "get_trace",
910
+ "Get full details of a single trace by ID. This is the right tool whenever you have a trace ID \u2014 search_traces does not match IDs. A full ID resolves at any age; a unique 8\u201331 character hex prefix resolves within the last 90 days. Returns AI-readable trace digest by default. Use format: 'json' for full raw data including all spans.",
911
+ {
912
+ traceId: z3.string().describe(
913
+ "The trace ID to retrieve. A full ID resolves at any age; a unique 8\u201331 character hex prefix also resolves, git-style, within the last 90 days."
914
+ ),
915
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
916
+ },
917
+ withToolLogging("get_trace", async (params) => {
918
+ requireApiKey();
919
+ const { handleGetTrace } = await import("./get-trace-KJLYAZRC.js");
920
+ return {
921
+ content: [{ type: "text", text: await handleGetTrace(params) }]
922
+ };
923
+ })
924
+ );
925
+ server.tool(
926
+ "get_analytics",
927
+ 'Query analytics timeseries from LangWatch. Metrics use "category.name" format (e.g., "performance.completion_time"). Use discover_schema to see available metrics.',
928
+ {
929
+ metric: z3.string().describe(
930
+ 'Metric in "category.name" format, e.g., "metadata.trace_id", "performance.total_cost"'
931
+ ),
932
+ aggregation: z3.string().optional().describe(
933
+ "Aggregation type: avg, sum, min, max, median, p90, p95, p99, cardinality, terms. Default: avg"
934
+ ),
935
+ startDate: z3.string().optional().describe('Start date: ISO or relative ("7d", "30d"). Default: 7 days ago'),
936
+ endDate: z3.string().optional().describe("End date. Default: now"),
937
+ timeZone: z3.string().optional().describe("Timezone. Default: UTC"),
938
+ groupBy: z3.string().optional().describe("Group results by field. Use discover_schema for options."),
939
+ filters: z3.record(z3.string(), z3.array(z3.string())).optional().describe("Filters to apply")
940
+ },
941
+ withToolLogging("get_analytics", async (params) => {
942
+ requireApiKey();
943
+ const { handleGetAnalytics } = await import("./get-analytics-4MPEK4KV.js");
944
+ return {
945
+ content: [{ type: "text", text: await handleGetAnalytics(params) }]
946
+ };
947
+ })
948
+ );
949
+ }
950
+ function registerPromptTools(server) {
951
+ server.tool(
952
+ "platform_create_prompt",
953
+ `Create a new prompt on the LangWatch platform.
954
+
955
+ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
956
+
957
+ 1. Code-based (CLI/SDK): If the user wants to manage prompts in their codebase, use \`fetch_langwatch_docs\` to learn about the prompt management CLI/SDK. This lets them version-control prompts and pull them into code.
958
+
959
+ 2. Platform-based (LangWatch UI): If the user wants to manage prompts directly on the LangWatch platform, use the \`platform_\` MCP tools (\`platform_create_prompt\`, \`platform_update_prompt\`, etc.).
960
+ `,
961
+ {
962
+ name: z3.string().describe("Prompt display name"),
963
+ handle: z3.string().optional().describe("URL-friendly handle (auto-generated from name if omitted)"),
964
+ messages: z3.array(
965
+ z3.object({
966
+ role: z3.enum(["system", "user", "assistant"]).describe("Message role"),
967
+ content: z3.string().describe("Message content")
968
+ })
969
+ ).describe("Prompt messages"),
970
+ model: modelSchema,
971
+ tags: z3.array(z3.string()).optional().describe(
972
+ 'Tags to assign to the initial version (e.g., ["production", "staging"]). Built-in tags: "latest" (auto-assigned), "production", "staging". Custom tags must be created first.'
973
+ )
974
+ },
975
+ withToolLogging("platform_create_prompt", async (params) => {
976
+ requireApiKey();
977
+ const { handleCreatePrompt } = await import("./create-prompt-ZUMQHHX7.js");
978
+ return {
979
+ content: [{ type: "text", text: await handleCreatePrompt(params) }]
980
+ };
981
+ })
982
+ );
983
+ server.tool(
984
+ "platform_list_prompts",
985
+ "List all prompts configured on the LangWatch platform.",
986
+ {},
987
+ withToolLogging("platform_list_prompts", async () => {
988
+ requireApiKey();
989
+ const { handleListPrompts } = await import("./list-prompts-6DX3QECJ.js");
990
+ return {
991
+ content: [{ type: "text", text: await handleListPrompts() }]
992
+ };
993
+ })
994
+ );
995
+ server.tool(
996
+ "platform_get_prompt",
997
+ "Get a specific prompt from the LangWatch platform by ID or handle, including messages, model config, and version history. Use format: 'json' for the full raw API payload, or 'digest' (default) for a formatted summary.",
998
+ {
999
+ idOrHandle: z3.string().describe("Prompt ID or handle"),
1000
+ version: z3.number().optional().describe("Specific version number (default: latest)"),
1001
+ tag: z3.string().optional().describe(
1002
+ 'Fetch the version pointed to by this tag (e.g., "production", "staging"). Alternatively, use shorthand in idOrHandle: "pizza-prompt:production"'
1003
+ ),
1004
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
1005
+ },
1006
+ withToolLogging("platform_get_prompt", async (params) => {
1007
+ if (params.version != null && params.tag) {
1008
+ return {
1009
+ content: [{ type: "text", text: "Error: Provide either 'version' or 'tag', not both." }],
1010
+ isError: true
1011
+ };
1012
+ }
1013
+ requireApiKey();
1014
+ const { handleGetPrompt } = await import("./get-prompt-ZER557X3.js");
1015
+ return {
1016
+ content: [{ type: "text", text: await handleGetPrompt(params) }]
1017
+ };
1018
+ })
1019
+ );
1020
+ server.tool(
1021
+ "platform_update_prompt",
1022
+ "Update an existing prompt on the LangWatch platform. Every update creates a new version.",
1023
+ {
1024
+ idOrHandle: z3.string().describe("Prompt ID or handle to update"),
1025
+ messages: z3.array(
1026
+ z3.object({
1027
+ role: z3.enum(["system", "user", "assistant"]),
1028
+ content: z3.string()
1029
+ })
1030
+ ).optional().describe("Updated messages"),
1031
+ model: modelSchema.optional(),
1032
+ commitMessage: z3.string().describe("Commit message describing the change"),
1033
+ tags: z3.array(z3.string()).optional().describe("Tags to assign to the new version created by this update.")
1034
+ },
1035
+ withToolLogging("platform_update_prompt", async (params) => {
1036
+ requireApiKey();
1037
+ const { handleUpdatePrompt } = await import("./update-prompt-BCDFTNLK.js");
1038
+ return {
1039
+ content: [{ type: "text", text: await handleUpdatePrompt(params) }]
1040
+ };
1041
+ })
1042
+ );
1043
+ server.tool(
1044
+ "platform_assign_prompt_tag",
1045
+ 'Assign a tag (e.g. "production") to a specific version of a prompt. Use this to "deploy" a version by promoting it to the production tag.',
1046
+ {
1047
+ idOrHandle: z3.string().describe("Prompt ID or handle"),
1048
+ tag: z3.string().describe('Tag name (e.g., "production", "staging")'),
1049
+ versionId: z3.string().describe("The version ID to assign the tag to")
1050
+ },
1051
+ withToolLogging("platform_assign_prompt_tag", async (params) => {
1052
+ requireApiKey();
1053
+ const { handleAssignPromptTag } = await import("./assign-prompt-tag-6DDE6ZZ5.js");
1054
+ return {
1055
+ content: [{ type: "text", text: await handleAssignPromptTag(params) }]
1056
+ };
1057
+ })
1058
+ );
1059
+ server.tool(
1060
+ "platform_list_prompt_tags",
1061
+ "List all prompt tag definitions for the organization. Shows built-in tags (latest, production, staging) and any custom tags.",
1062
+ {},
1063
+ withToolLogging("platform_list_prompt_tags", async () => {
1064
+ requireApiKey();
1065
+ const { handleListPromptTags } = await import("./list-prompt-tags-DRHUH2ND.js");
1066
+ return {
1067
+ content: [{ type: "text", text: await handleListPromptTags() }]
1068
+ };
1069
+ })
1070
+ );
1071
+ server.tool(
1072
+ "platform_create_prompt_tag",
1073
+ 'Create a custom prompt tag definition for the organization. Tag names must be non-numeric and not "latest".',
1074
+ {
1075
+ name: z3.string().describe("Tag name to create")
1076
+ },
1077
+ withToolLogging("platform_create_prompt_tag", async (params) => {
1078
+ requireApiKey();
1079
+ const { handleCreatePromptTag } = await import("./create-prompt-tag-CJ5FZ436.js");
1080
+ return {
1081
+ content: [{ type: "text", text: await handleCreatePromptTag(params) }]
1082
+ };
1083
+ })
1084
+ );
1085
+ server.tool(
1086
+ "platform_rename_prompt_tag",
1087
+ 'Rename an existing prompt tag. The "latest" tag cannot be renamed.',
1088
+ {
1089
+ tag: z3.string().describe("Current tag name to rename"),
1090
+ name: z3.string().describe("New tag name")
1091
+ },
1092
+ withToolLogging("platform_rename_prompt_tag", async (params) => {
1093
+ requireApiKey();
1094
+ const { handleRenamePromptTag } = await import("./rename-prompt-tag-SZYTOWDX.js");
1095
+ return {
1096
+ content: [{ type: "text", text: await handleRenamePromptTag(params) }]
1097
+ };
1098
+ })
1099
+ );
1100
+ server.tool(
1101
+ "platform_delete_prompt_tag",
1102
+ 'Delete a prompt tag and all its assignments. The "latest" tag cannot be deleted.',
1103
+ {
1104
+ tag: z3.string().describe("Tag name to delete")
1105
+ },
1106
+ withToolLogging("platform_delete_prompt_tag", async (params) => {
1107
+ requireApiKey();
1108
+ const { handleDeletePromptTag } = await import("./delete-prompt-tag-LE3WJPKS.js");
1109
+ return {
1110
+ content: [{ type: "text", text: await handleDeletePromptTag(params) }]
1111
+ };
1112
+ })
1113
+ );
1114
+ }
1115
+ function registerScenarioTools(server) {
1116
+ server.tool(
1117
+ "platform_create_scenario",
1118
+ `Create a new scenario on the LangWatch platform. Call discover_schema({ category: 'scenarios' }) first to learn how to write effective situations and criteria.
1119
+
1120
+ NOTE: Scenarios can be created two ways. Determine which approach the user needs:
1121
+
1122
+ 1. Code-based (local testing): If the user has a codebase with an AI agent they want to test, use \`fetch_scenario_docs\` to learn about the Scenario Python/TypeScript SDK. This lets them run tests locally and iterate in code.
1123
+
1124
+ 2. Platform-based (LangWatch UI): If the user wants to manage scenarios directly on the LangWatch platform, use the \`platform_\` MCP tools (\`platform_create_scenario\`, \`platform_update_scenario\`, etc.).
1125
+ `,
1126
+ {
1127
+ name: z3.string().describe("Scenario name"),
1128
+ situation: z3.string().describe("The context or setup describing what the user/agent is doing"),
1129
+ criteria: z3.array(z3.string()).optional().describe("Pass/fail conditions the agent's response must satisfy"),
1130
+ labels: z3.array(z3.string()).optional().describe("Tags for organizing and filtering scenarios"),
1131
+ testSuiteId: z3.string().nullish().describe(
1132
+ "The test suite to file this scenario in. Pass a test suite ID, or null to unfile it."
1133
+ ),
1134
+ fields: scenarioFieldValuesSchema.optional()
1135
+ },
1136
+ withToolLogging("platform_create_scenario", async (params) => {
1137
+ requireApiKey();
1138
+ const { handleCreateScenario } = await import("./create-scenario-CQU2SSFB.js");
1139
+ return {
1140
+ content: [{ type: "text", text: await handleCreateScenario(params) }]
1141
+ };
1142
+ })
1143
+ );
1144
+ server.tool(
1145
+ "platform_list_scenarios",
1146
+ "List all scenarios on the LangWatch platform. Returns AI-readable digest by default.",
1147
+ {
1148
+ testSuiteId: z3.string().optional().describe("Only the scenarios filed in this test suite"),
1149
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
1150
+ },
1151
+ withToolLogging("platform_list_scenarios", async (params) => {
1152
+ requireApiKey();
1153
+ const { handleListScenarios } = await import("./list-scenarios-EQWCW7Q4.js");
1154
+ return {
1155
+ content: [{ type: "text", text: await handleListScenarios(params) }]
1156
+ };
1157
+ })
1158
+ );
1159
+ server.tool(
1160
+ "platform_get_scenario",
1161
+ "Get full details of a scenario on the LangWatch platform by ID, including situation, criteria, and labels.",
1162
+ {
1163
+ scenarioId: z3.string().describe("The scenario ID to retrieve"),
1164
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
1165
+ },
1166
+ withToolLogging("platform_get_scenario", async (params) => {
1167
+ requireApiKey();
1168
+ const { handleGetScenario } = await import("./get-scenario-UQX45ZZO.js");
1169
+ return {
1170
+ content: [{ type: "text", text: await handleGetScenario(params) }]
1171
+ };
1172
+ })
1173
+ );
1174
+ server.tool(
1175
+ "platform_update_scenario",
1176
+ "Update an existing scenario on the LangWatch platform.",
1177
+ {
1178
+ scenarioId: z3.string().describe("The scenario ID to update"),
1179
+ name: z3.string().optional().describe("Updated scenario name"),
1180
+ situation: z3.string().optional().describe("Updated situation"),
1181
+ criteria: z3.array(z3.string()).optional().describe("Updated criteria"),
1182
+ labels: z3.array(z3.string()).optional().describe("Updated labels"),
1183
+ testSuiteId: z3.string().nullish().describe(
1184
+ "The test suite to file this scenario in. Pass a test suite ID, or null to unfile it."
1185
+ ),
1186
+ fields: scenarioFieldValuesSchema.optional()
1187
+ },
1188
+ withToolLogging("platform_update_scenario", async (params) => {
1189
+ requireApiKey();
1190
+ const { handleUpdateScenario } = await import("./update-scenario-BNOMDCMV.js");
1191
+ return {
1192
+ content: [{ type: "text", text: await handleUpdateScenario(params) }]
1193
+ };
1194
+ })
1195
+ );
1196
+ server.tool(
1197
+ "platform_archive_scenario",
1198
+ "Archive (soft-delete) a scenario on the LangWatch platform.",
1199
+ {
1200
+ scenarioId: z3.string().describe("The scenario ID to archive")
1201
+ },
1202
+ withToolLogging("platform_archive_scenario", async (params) => {
1203
+ requireApiKey();
1204
+ const { handleArchiveScenario } = await import("./archive-scenario-2CWJW2VD.js");
1205
+ return {
1206
+ content: [{ type: "text", text: await handleArchiveScenario(params) }]
1207
+ };
1208
+ })
1209
+ );
1210
+ }
1211
+ function registerRunPlanTools(server) {
1212
+ server.tool(
1213
+ "platform_run_plan",
1214
+ "Run scenarios against targets. The plan name identifies the run plan: an existing name is re-run with the configuration you send here, a new name creates the plan. Configuration is the scope, the targets, the repeat count and the models; parameters, the note and the idempotency key belong to this run alone. Send more than one target to compare them in the same run, and give a target its own parameters to compare one agent on two models.",
1215
+ {
1216
+ name: z3.string().optional().describe(
1217
+ "The run plan to run. An existing name is replaced with this configuration, a new name creates the plan. Omit to let the server name it after the suite and the target."
1218
+ ),
1219
+ scope: runPlanScopeSchema.describe(
1220
+ "What the plan covers: every scenario in the project, the scenarios filed in given test suites, the scenarios carrying given labels, or the hand-picked list in scenarioIds."
1221
+ ),
1222
+ scenarioIds: z3.array(z3.string()).optional().describe("The scenarios to run. Read only when scope.mode is 'scenarios'."),
1223
+ targets: z3.array(runPlanTargetSchema).describe(
1224
+ "What to run the scenarios against. Every target runs every scenario, and the results show one column for each, so this is how one run compares two agents or one agent on two models."
1225
+ ),
1226
+ repeatCount: z3.number().int().min(1).max(5).optional().describe("How many times to run each scenario against each target (1 to 5, default 1)"),
1227
+ simulatorModel: z3.string().optional().describe("Model that plays the user. Omit for the project default."),
1228
+ judgeModel: z3.string().optional().describe("Model that judges the criteria. Omit for the project default."),
1229
+ evaluators: evaluatorAttachmentsSchema.optional().describe(
1230
+ "The plan's own evaluators, run beside the ones attached to the test suites its scenarios belong to. A plan evaluator reads the conversation and the trace, never a scenario field. Omit to keep what the plan already holds. See discover_schema({ category: 'scenarios' }) for the mapping paths."
1231
+ ),
1232
+ parameters: runParametersSchema.optional().describe(
1233
+ "Values for the parameters the scenarios declare, by name. They apply to every target; a target that names the same parameter overrides them for itself."
1234
+ ),
1235
+ note: z3.string().max(200).optional().describe("One short line saying why this run was started (max 200 characters)"),
1236
+ idempotencyKey: z3.string().optional().describe(
1237
+ "Send the same key to make a retry join the run it already started instead of starting a second one."
1238
+ )
1239
+ },
1240
+ withToolLogging("platform_run_plan", async (params) => {
1241
+ requireApiKey();
1242
+ const { handleRunPlan } = await import("./run-plan-2ZPGGZ4H.js");
1243
+ return {
1244
+ content: [{ type: "text", text: await handleRunPlan(params) }]
1245
+ };
1246
+ })
1247
+ );
1248
+ server.tool(
1249
+ "platform_list_run_plans",
1250
+ "List the run plans of the project. A run plan holds the configuration a run used, keyed by its name.",
1251
+ {
1252
+ includeArchived: z3.boolean().optional().describe("Include archived plans (default false)"),
1253
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default) or 'json' (raw data)")
1254
+ },
1255
+ withToolLogging("platform_list_run_plans", async (params) => {
1256
+ requireApiKey();
1257
+ const { handleListRunPlans } = await import("./list-run-plans-5SXPOV3X.js");
1258
+ return {
1259
+ content: [{ type: "text", text: await handleListRunPlans(params) }]
1260
+ };
1261
+ })
1262
+ );
1263
+ server.tool(
1264
+ "platform_get_run_plan",
1265
+ "Get the full configuration of a run plan: what it covers, its targets, its repeat count and its models.",
1266
+ {
1267
+ id: z3.string().describe("The run plan ID"),
1268
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default) or 'json'")
1269
+ },
1270
+ withToolLogging("platform_get_run_plan", async (params) => {
1271
+ requireApiKey();
1272
+ const { handleGetRunPlan } = await import("./get-run-plan-5X7DDJRN.js");
1273
+ return {
1274
+ content: [{ type: "text", text: await handleGetRunPlan(params) }]
1275
+ };
1276
+ })
1277
+ );
1278
+ server.tool(
1279
+ "platform_rerun_run_plan",
1280
+ "Run a plan again with the configuration it already holds. Only the parameters and the note belong to the new run.",
1281
+ {
1282
+ id: z3.string().describe("The run plan ID to run again"),
1283
+ parameters: runParametersSchema.optional().describe("Values for the parameters the scenarios declare, by name."),
1284
+ note: z3.string().max(200).optional().describe("One short line saying why this run was started (max 200 characters)")
1285
+ },
1286
+ withToolLogging("platform_rerun_run_plan", async (params) => {
1287
+ requireApiKey();
1288
+ const { handleRerunRunPlan } = await import("./rerun-run-plan-OJTVVGJI.js");
1289
+ return {
1290
+ content: [{ type: "text", text: await handleRerunRunPlan(params) }]
1291
+ };
1292
+ })
1293
+ );
1294
+ server.tool(
1295
+ "platform_archive_run_plan",
1296
+ "Archive a run plan. Its past runs stay readable and the name is free for a new plan.",
1297
+ {
1298
+ id: z3.string().describe("The run plan ID to archive")
1299
+ },
1300
+ withToolLogging("platform_archive_run_plan", async (params) => {
1301
+ requireApiKey();
1302
+ const { handleArchiveRunPlan } = await import("./archive-run-plan-AOKNYQIO.js");
1303
+ return {
1304
+ content: [{ type: "text", text: await handleArchiveRunPlan(params) }]
1305
+ };
1306
+ })
1307
+ );
1308
+ }
1309
+ function registerTestSuiteTools(server) {
1310
+ server.tool(
1311
+ "platform_list_test_suites",
1312
+ "List the test suites of the project. A test suite groups scenarios.",
1313
+ {
1314
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default) or 'json' (raw data)")
1315
+ },
1316
+ withToolLogging("platform_list_test_suites", async (params) => {
1317
+ requireApiKey();
1318
+ const { handleListTestSuites } = await import("./list-test-suites-7H2DRILK.js");
1319
+ return {
1320
+ content: [{ type: "text", text: await handleListTestSuites(params) }]
1321
+ };
1322
+ })
1323
+ );
1324
+ server.tool(
1325
+ "platform_create_test_suite",
1326
+ "Create a test suite. A test suite groups scenarios: file a scenario in it by passing the suite ID as testSuiteId on platform_create_scenario or platform_update_scenario. A suite can declare typed fields every scenario carries a value for, and attach saved evaluators that run after every scenario run with mappings from their inputs to the conversation, the scenario's fields or the trace. Call discover_schema({ category: 'scenarios' }) first for the field rules and the mapping paths.",
1327
+ {
1328
+ name: z3.string().describe("Test suite name"),
1329
+ fields: suiteFieldsSchema.optional(),
1330
+ evaluators: evaluatorAttachmentsSchema.optional()
1331
+ },
1332
+ withToolLogging("platform_create_test_suite", async (params) => {
1333
+ requireApiKey();
1334
+ const { handleCreateTestSuite } = await import("./create-test-suite-OLGCAGJA.js");
1335
+ return {
1336
+ content: [{ type: "text", text: await handleCreateTestSuite(params) }]
1337
+ };
1338
+ })
1339
+ );
1340
+ server.tool(
1341
+ "platform_get_test_suite",
1342
+ "Get a test suite and the scenarios filed in it.",
1343
+ {
1344
+ id: z3.string().describe("The test suite ID"),
1345
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default) or 'json'")
1346
+ },
1347
+ withToolLogging("platform_get_test_suite", async (params) => {
1348
+ requireApiKey();
1349
+ const { handleGetTestSuite } = await import("./get-test-suite-OODIR3RI.js");
1350
+ return {
1351
+ content: [{ type: "text", text: await handleGetTestSuite(params) }]
1352
+ };
1353
+ })
1354
+ );
1355
+ server.tool(
1356
+ "platform_update_test_suite",
1357
+ "Edit a test suite: any of its name, its fields and its evaluators. A field list or an evaluator list replaces the one the suite holds; a key left out keeps what the suite has. A field an attached evaluator still reads cannot be removed. Call discover_schema({ category: 'scenarios' }) first for the field rules and the mapping paths.",
1358
+ {
1359
+ id: z3.string().describe("The test suite ID"),
1360
+ name: z3.string().optional().describe("The new name. The slug is kept."),
1361
+ fields: suiteFieldsSchema.optional().describe(
1362
+ "The full list of fields the suite declares. It replaces the list the suite holds."
1363
+ ),
1364
+ evaluators: evaluatorAttachmentsSchema.optional().describe(
1365
+ "The full list of evaluators attached to the suite. It replaces the list the suite holds."
1366
+ )
1367
+ },
1368
+ withToolLogging("platform_update_test_suite", async (params) => {
1369
+ requireApiKey();
1370
+ return {
1371
+ content: [{ type: "text", text: await handleUpdateTestSuite(params) }]
1372
+ };
1373
+ })
1374
+ );
1375
+ server.tool(
1376
+ "platform_rename_test_suite",
1377
+ "Rename a test suite.",
1378
+ {
1379
+ id: z3.string().describe("The test suite ID"),
1380
+ name: z3.string().describe("The new name")
1381
+ },
1382
+ withToolLogging("platform_rename_test_suite", async (params) => {
1383
+ requireApiKey();
1384
+ const { handleRenameTestSuite } = await import("./rename-test-suite-CLIEPZDO.js");
1385
+ return {
1386
+ content: [{ type: "text", text: await handleRenameTestSuite(params) }]
1387
+ };
1388
+ })
1389
+ );
1390
+ server.tool(
1391
+ "platform_archive_test_suite",
1392
+ "Archive a test suite. The scenarios filed in it are archived with it.",
1393
+ {
1394
+ id: z3.string().describe("The test suite ID to archive")
1395
+ },
1396
+ withToolLogging("platform_archive_test_suite", async (params) => {
1397
+ requireApiKey();
1398
+ const { handleArchiveTestSuite } = await import("./archive-test-suite-ZC3TN6VH.js");
1399
+ return {
1400
+ content: [{ type: "text", text: await handleArchiveTestSuite(params) }]
1401
+ };
1402
+ })
1403
+ );
1404
+ server.tool(
1405
+ "platform_run_test_suite",
1406
+ "Run every scenario of a test suite against targets. This creates or joins the run plan named '<suite name> <target name>' unless you send a name of your own. Send more than one target to compare them in the same run, and give a target its own parameters to compare one agent on two models.",
1407
+ {
1408
+ id: z3.string().describe("The test suite ID to run"),
1409
+ targets: z3.array(runPlanTargetSchema).describe(
1410
+ "What to run the scenarios against. Every target runs every scenario, and the results show one column for each."
1411
+ ),
1412
+ name: z3.string().optional().describe(
1413
+ "The run plan to run. Omit to let the server name it after the suite and the target."
1414
+ ),
1415
+ repeatCount: z3.number().int().min(1).max(5).optional().describe("How many times to run each scenario against each target (1 to 5, default 1)"),
1416
+ simulatorModel: z3.string().optional().describe("Model that plays the user. Omit for the project default."),
1417
+ judgeModel: z3.string().optional().describe("Model that judges the criteria. Omit for the project default."),
1418
+ parameters: runParametersSchema.optional().describe(
1419
+ "Values for the parameters the scenarios declare, by name. They apply to every target; a target that names the same parameter overrides them for itself."
1420
+ ),
1421
+ note: z3.string().max(200).optional().describe("One short line saying why this run was started (max 200 characters)")
1422
+ },
1423
+ withToolLogging("platform_run_test_suite", async (params) => {
1424
+ requireApiKey();
1425
+ const { handleRunTestSuite } = await import("./run-test-suite-UEOMCON6.js");
1426
+ return {
1427
+ content: [{ type: "text", text: await handleRunTestSuite(params) }]
1428
+ };
1429
+ })
1430
+ );
1431
+ }
1432
+ function registerSimulationRunTools(server) {
1433
+ server.tool(
1434
+ "platform_list_simulation_runs",
1435
+ "List simulation run results. Filter by scenario set or batch run ID to see specific results.",
1436
+ {
1437
+ scenarioSetId: z3.string().optional().describe("Filter by scenario set ID"),
1438
+ batchRunId: z3.string().optional().describe("Filter by batch run ID"),
1439
+ limit: z3.number().optional().describe("Max results to return (default: 20)"),
1440
+ format: z3.enum(["digest", "json"]).optional().describe("Output format")
1441
+ },
1442
+ withToolLogging("platform_list_simulation_runs", async (params) => {
1443
+ requireApiKey();
1444
+ const { handleListSimulationRuns } = await import("./list-simulation-runs-PUDK32C2.js");
1445
+ return {
1446
+ content: [{ type: "text", text: await handleListSimulationRuns(params) }]
1447
+ };
1448
+ })
1449
+ );
1450
+ server.tool(
1451
+ "platform_get_simulation_run",
1452
+ "Get full details of a simulation run including conversation messages, results, costs, the verdict and the result of every evaluator attached to its test suite or run plan.",
1453
+ {
1454
+ scenarioRunId: z3.string().describe("The simulation run ID"),
1455
+ format: z3.enum(["digest", "json"]).optional().describe("Output format")
1456
+ },
1457
+ withToolLogging("platform_get_simulation_run", async (params) => {
1458
+ requireApiKey();
1459
+ const { handleGetSimulationRun } = await import("./get-simulation-run-QZTLGL7W.js");
1460
+ return {
1461
+ content: [{ type: "text", text: await handleGetSimulationRun(params) }]
1462
+ };
1463
+ })
1464
+ );
1465
+ }
1466
+ function registerEvaluatorTools(server) {
1467
+ server.tool(
1468
+ "platform_create_evaluator",
1469
+ `Create an evaluator on the LangWatch platform. Useful for setting up LLM-as-judge and other evaluators to use in evaluation notebooks. Call discover_schema({ category: 'evaluators' }) first to see available evaluator types and their settings.`,
1470
+ {
1471
+ name: z3.string().describe("Evaluator name"),
1472
+ config: z3.record(z3.string(), z3.unknown()).describe(
1473
+ 'Evaluator config object. Must include "evaluatorType" (e.g. "langevals/llm_boolean") and optional "settings" overrides.'
1474
+ )
1475
+ },
1476
+ withToolLogging("platform_create_evaluator", async (params) => {
1477
+ requireApiKey();
1478
+ const { handleCreateEvaluator } = await import("./create-evaluator-N5AEHI4K.js");
1479
+ return {
1480
+ content: [{ type: "text", text: await handleCreateEvaluator(params) }]
1481
+ };
1482
+ })
1483
+ );
1484
+ server.tool(
1485
+ "platform_list_evaluators",
1486
+ "List all evaluators configured on the LangWatch platform.",
1487
+ {},
1488
+ withToolLogging("platform_list_evaluators", async () => {
1489
+ requireApiKey();
1490
+ const { handleListEvaluators } = await import("./list-evaluators-VATJVOHR.js");
1491
+ return {
1492
+ content: [{ type: "text", text: await handleListEvaluators() }]
1493
+ };
1494
+ })
1495
+ );
1496
+ server.tool(
1497
+ "platform_get_evaluator",
1498
+ "Get full details of an evaluator on the LangWatch platform by ID or slug, including config, input fields, and output fields.",
1499
+ {
1500
+ idOrSlug: z3.string().describe("The evaluator ID or slug to retrieve")
1501
+ },
1502
+ withToolLogging("platform_get_evaluator", async (params) => {
1503
+ requireApiKey();
1504
+ const { handleGetEvaluator } = await import("./get-evaluator-3A7BJBAB.js");
1505
+ return {
1506
+ content: [{ type: "text", text: await handleGetEvaluator(params) }]
1507
+ };
1508
+ })
1509
+ );
1510
+ server.tool(
1511
+ "platform_update_evaluator",
1512
+ "Update an existing evaluator on the LangWatch platform. The evaluatorType in config cannot be changed after creation.",
1513
+ {
1514
+ evaluatorId: z3.string().describe("The evaluator ID to update"),
1515
+ name: z3.string().optional().describe("Updated evaluator name"),
1516
+ config: z3.record(z3.string(), z3.unknown()).optional().describe("Updated config settings. Note: evaluatorType cannot be changed after creation.")
1517
+ },
1518
+ withToolLogging("platform_update_evaluator", async (params) => {
1519
+ requireApiKey();
1520
+ const { handleUpdateEvaluator } = await import("./update-evaluator-5NLNE6VY.js");
1521
+ return {
1522
+ content: [{ type: "text", text: await handleUpdateEvaluator(params) }]
1523
+ };
1524
+ })
1525
+ );
1526
+ server.tool(
1527
+ "platform_delete_evaluator",
1528
+ "Archive (soft-delete) an evaluator by ID or slug.",
1529
+ {
1530
+ idOrSlug: z3.string().describe("The evaluator ID or slug to archive")
1531
+ },
1532
+ withToolLogging("platform_delete_evaluator", async (params) => {
1533
+ requireApiKey();
1534
+ const { handleDeleteEvaluator } = await import("./delete-evaluator-RI4GALGX.js");
1535
+ return {
1536
+ content: [{ type: "text", text: await handleDeleteEvaluator(params) }]
1537
+ };
1538
+ })
1539
+ );
1540
+ }
1541
+ function registerModelProviderTools(server) {
1542
+ server.tool(
1543
+ "platform_set_model_provider",
1544
+ `Set or update a model provider on the LangWatch platform. Use this to configure API keys (e.g. OPENAI_API_KEY) needed to run evaluators. The API key is stored securely and never returned in responses. Omit customKeys to update other settings without changing existing keys.`,
1545
+ {
1546
+ provider: z3.string().describe('Provider name, e.g., "openai", "anthropic", "azure", "custom"'),
1547
+ enabled: z3.boolean().describe("Whether the provider is enabled"),
1548
+ customKeys: z3.record(z3.string(), z3.unknown()).optional().describe(
1549
+ 'API key configuration, e.g. { "OPENAI_API_KEY": "sk-..." }. Omit to keep existing keys.'
1550
+ ),
1551
+ defaultModel: z3.string().optional().describe("Set as project default model")
1552
+ },
1553
+ withToolLogging("platform_set_model_provider", async (params) => {
1554
+ requireApiKey();
1555
+ const { handleSetModelProvider } = await import("./set-model-provider-RBNGWFVI.js");
1556
+ return {
1557
+ content: [{ type: "text", text: await handleSetModelProvider(params) }]
1558
+ };
1559
+ })
1560
+ );
1561
+ server.tool(
1562
+ "platform_list_model_providers",
1563
+ "List all model providers configured on the LangWatch platform. API keys are masked in the response.",
1564
+ {},
1565
+ withToolLogging("platform_list_model_providers", async () => {
1566
+ requireApiKey();
1567
+ const { handleListModelProviders } = await import("./list-model-providers-VXQJWDXU.js");
1568
+ return {
1569
+ content: [{ type: "text", text: await handleListModelProviders() }]
1570
+ };
1571
+ })
1572
+ );
1573
+ }
1574
+ function registerAgentTools(server) {
1575
+ server.tool(
1576
+ "platform_list_agents",
1577
+ "List all agents in the LangWatch project with their names, types and IDs. A connected agent (one that registered itself from code with connectAgent or connect_agent) also shows its environment, whether it is online, how many instances are connected, and its owner.",
1578
+ {},
1579
+ withToolLogging("platform_list_agents", async () => {
1580
+ requireApiKey();
1581
+ const { handleListAgents } = await import("./list-agents-MG24MHZE.js");
1582
+ return {
1583
+ content: [{ type: "text", text: await handleListAgents() }]
1584
+ };
1585
+ })
1586
+ );
1587
+ server.tool(
1588
+ "platform_get_agent",
1589
+ "Get detailed information about a specific agent by its ID, including its configuration. A connected agent also shows its environment, status, the run parameters it declared (with options and defaults) and the instances connected right now.",
1590
+ {
1591
+ id: z3.string().describe("The agent ID")
1592
+ },
1593
+ withToolLogging("platform_get_agent", async (params) => {
1594
+ requireApiKey();
1595
+ const { handleGetAgent } = await import("./get-agent-5T3CXQOR.js");
1596
+ return {
1597
+ content: [{ type: "text", text: await handleGetAgent(params) }]
1598
+ };
1599
+ })
1600
+ );
1601
+ server.tool(
1602
+ "platform_create_agent",
1603
+ "Create a new agent. Supported types: 'signature' (LLM prompt), 'code' (Python), 'workflow' (sub-workflow), 'http' (external API). A 'connected' agent is not created here: it registers itself from code, with connectAgent from langwatch/agent in TypeScript or langwatch.connect_agent in Python, and appears in the list once that process runs.",
1604
+ {
1605
+ name: z3.string().describe("Agent name"),
1606
+ type: z3.enum(["signature", "code", "workflow", "http"]).describe("Agent type"),
1607
+ config: z3.string().optional().describe("Agent configuration as JSON string (will be parsed)")
1608
+ },
1609
+ withToolLogging("platform_create_agent", async (params) => {
1610
+ requireApiKey();
1611
+ const { handleCreateAgent } = await import("./create-agent-IRZOU4R5.js");
1612
+ const parsedConfig = params.config ? JSON.parse(params.config) : void 0;
1613
+ return {
1614
+ content: [
1615
+ { type: "text", text: await handleCreateAgent({ ...params, config: parsedConfig }) }
1616
+ ]
1617
+ };
1618
+ })
1619
+ );
1620
+ server.tool(
1621
+ "platform_update_agent",
1622
+ "Update an existing agent's name, type, or configuration.",
1623
+ {
1624
+ id: z3.string().describe("The agent ID"),
1625
+ name: z3.string().optional().describe("New agent name"),
1626
+ type: z3.string().optional().describe("New agent type: signature, code, workflow, or http"),
1627
+ config: z3.string().optional().describe("Updated configuration as JSON string (will be parsed)")
1628
+ },
1629
+ withToolLogging("platform_update_agent", async (params) => {
1630
+ requireApiKey();
1631
+ const { handleUpdateAgent } = await import("./update-agent-ZPXWRMS2.js");
1632
+ const parsedConfig = params.config ? JSON.parse(params.config) : void 0;
1633
+ return {
1634
+ content: [
1635
+ { type: "text", text: await handleUpdateAgent({ ...params, config: parsedConfig }) }
1636
+ ]
1637
+ };
1638
+ })
1639
+ );
1640
+ server.tool(
1641
+ "platform_delete_agent",
1642
+ "Archive (soft-delete) an agent by its ID.",
1643
+ {
1644
+ id: z3.string().describe("The agent ID to archive")
1645
+ },
1646
+ withToolLogging("platform_delete_agent", async (params) => {
1647
+ requireApiKey();
1648
+ const { handleDeleteAgent } = await import("./delete-agent-ZAU4LTXB.js");
1649
+ return {
1650
+ content: [{ type: "text", text: await handleDeleteAgent(params) }]
1651
+ };
1652
+ })
1653
+ );
1654
+ server.tool(
1655
+ "platform_run_agent",
1656
+ "Run one turn of an agent. A connected agent runs through the platform relay on a live instance: give `message` (one user turn) or `input` with a `messages` list, plus `parameters` and `threadId` to continue a conversation. HTTP agents call their configured URL directly; workflow-linked agents execute via the workflow engine.",
1657
+ {
1658
+ id: z3.string().describe("The agent ID to run"),
1659
+ input: z3.string().optional().describe(
1660
+ "Input data as a JSON object string. For a connected agent it is the relay body: messages (OpenAI style), and optionally threadId, session and params."
1661
+ ),
1662
+ message: z3.string().optional().describe("One user message to send to a connected agent, instead of input"),
1663
+ parameters: z3.record(z3.string(), z3.union([z3.string(), z3.number(), z3.boolean()])).optional().describe("Run parameter values for a connected agent, by name"),
1664
+ threadId: z3.string().optional().describe("Continue a conversation on a connected agent")
1665
+ },
1666
+ withToolLogging("platform_run_agent", async (params) => {
1667
+ requireApiKey();
1668
+ const { handleRunAgent } = await import("./run-agent-HPCGZGYW.js");
1669
+ return {
1670
+ content: [{ type: "text", text: await handleRunAgent(params) }]
1671
+ };
1672
+ })
1673
+ );
1674
+ server.tool(
1675
+ "platform_test_agent",
1676
+ 'Test an agent with one scripted scenario run: the user sends "ping", the agent answers, and the run succeeds when the answer arrives. No model is used, and no scenario, run plan or test suite is added to the project. Answers at once with the scenario run id to follow with platform_get_simulation_run; the run itself is asynchronous.',
1677
+ {
1678
+ id: z3.string().describe("The agent ID to test")
1679
+ },
1680
+ withToolLogging("platform_test_agent", async (params) => {
1681
+ requireApiKey();
1682
+ return {
1683
+ content: [{ type: "text", text: await handleTestAgent(params) }]
1684
+ };
1685
+ })
1686
+ );
1687
+ }
1688
+ function registerAnalyticsReportTools(server) {
1689
+ server.tool(
1690
+ "platform_list_analytics_reports",
1691
+ "List the Analytics Reports in the LangWatch project (/api/v1/analytics/reports). Dashboards, which hold widgets, are listed by list_dashboards.",
1692
+ {},
1693
+ withToolLogging("platform_list_analytics_reports", async () => {
1694
+ requireApiKey();
1695
+ const { handleListAnalyticsReports } = await import("./list-analytics-reports-G3EPWGSQ.js");
1696
+ return {
1697
+ content: [{ type: "text", text: await handleListAnalyticsReports() }]
1698
+ };
1699
+ })
1700
+ );
1701
+ server.tool(
1702
+ "platform_get_analytics_report",
1703
+ "Get an Analytics Report by its ID, including its graphs. A Dashboards dashboard's ID is not found here; read it with get_dashboard.",
1704
+ {
1705
+ reportId: z3.string().describe("The Analytics Report ID")
1706
+ },
1707
+ withToolLogging("platform_get_analytics_report", async (params) => {
1708
+ requireApiKey();
1709
+ const { handleGetAnalyticsReport } = await import("./get-analytics-report-JCKYMA2S.js");
1710
+ return {
1711
+ content: [{ type: "text", text: await handleGetAnalyticsReport(params) }]
1712
+ };
1713
+ })
1714
+ );
1715
+ server.tool(
1716
+ "platform_create_analytics_report",
1717
+ "Create a new Analytics Report.",
1718
+ {
1719
+ name: z3.string().describe("Analytics Report name")
1720
+ },
1721
+ withToolLogging("platform_create_analytics_report", async (params) => {
1722
+ requireApiKey();
1723
+ const { handleCreateAnalyticsReport } = await import("./create-analytics-report-QEHXG7FU.js");
1724
+ return {
1725
+ content: [
1726
+ {
1727
+ type: "text",
1728
+ text: await handleCreateAnalyticsReport(params)
1729
+ }
1730
+ ]
1731
+ };
1732
+ })
1733
+ );
1734
+ server.tool(
1735
+ "platform_delete_analytics_report",
1736
+ "Delete an Analytics Report and all its graphs.",
1737
+ {
1738
+ reportId: z3.string().describe("The Analytics Report ID to delete")
1739
+ },
1740
+ withToolLogging("platform_delete_analytics_report", async (params) => {
1741
+ requireApiKey();
1742
+ const { handleDeleteAnalyticsReport } = await import("./delete-analytics-report-YUYLDHLL.js");
1743
+ return {
1744
+ content: [
1745
+ {
1746
+ type: "text",
1747
+ text: await handleDeleteAnalyticsReport(params)
1748
+ }
1749
+ ]
1750
+ };
1751
+ })
1752
+ );
1753
+ server.tool(
1754
+ "platform_rename_analytics_report",
1755
+ "Rename an Analytics Report.",
1756
+ {
1757
+ reportId: z3.string().describe("The Analytics Report ID to rename"),
1758
+ name: z3.string().describe("The new name for the Analytics Report")
1759
+ },
1760
+ withToolLogging("platform_rename_analytics_report", async (params) => {
1761
+ requireApiKey();
1762
+ const { renameAnalyticsReport } = await import("./langwatch-api-analytics-reports-6CF6KUET.js");
1763
+ const result = await renameAnalyticsReport({ reportId: params.reportId, name: params.name });
1764
+ return {
1765
+ content: [
1766
+ {
1767
+ type: "text",
1768
+ text: `Analytics Report "${result.name}" renamed successfully (ID: ${result.id}).`
1769
+ }
1770
+ ]
1771
+ };
1772
+ })
1773
+ );
1774
+ }
1775
+ function registerDashboardTools(server) {
1776
+ server.tool(
1777
+ "list_dashboards",
1778
+ "Lists the project's dashboards in the Dashboards area, with each one's ID and widget count. Use an ID from here as the dashboardId of add_dashboard_widget and update_dashboard_widget; the Analytics Reports platform_list_analytics_reports lists hold no widgets.",
1779
+ {},
1780
+ withToolLogging("list_dashboards", async () => {
1781
+ requireApiKey();
1782
+ const { handleListDashboards } = await import("./list-dashboards-2P3VJFRS.js");
1783
+ return {
1784
+ content: [{ type: "text", text: await handleListDashboards() }]
1785
+ };
1786
+ })
1787
+ );
1788
+ server.tool(
1789
+ "add_dashboard_widget",
1790
+ "Creates a custom dashboard widget and places it on the given dashboard. A widget's `code` is a TSX file run in a sandbox: it receives a global `LW` with `LW.useChartQuery(name)` (reads one of the widget's own named queries), `LW.navigate` and `LW.theme`. `queries` are named LangWatchQL SQL statements the code can read by name \u2014 validate each statement's SQL first with run_query before calling this tool, since a query only runs inside the sandbox at render time and a mistake surfaces there, not here. Up to 8 queries per widget.",
1791
+ {
1792
+ dashboardId: z3.string().describe("The dashboard to add the widget to, as list_dashboards lists it"),
1793
+ name: z3.string().describe("The widget's display name"),
1794
+ code: z3.string().describe(
1795
+ "The widget's TSX source. Runs sandboxed with a global LW (LW.useChartQuery(name), LW.navigate, LW.theme) in scope."
1796
+ ),
1797
+ queries: z3.union([
1798
+ z3.record(z3.string(), z3.string()).describe('{"<name>": "<LWQL SQL>"}'),
1799
+ z3.array(
1800
+ z3.object({
1801
+ name: z3.string().describe('Referenced from code as LW.useChartQuery("name")'),
1802
+ sql: z3.string().describe("The LangWatchQL statement")
1803
+ })
1804
+ ).describe("Named LWQL queries, array form")
1805
+ ]).describe(
1806
+ "The widget's named LangWatchQL queries: either {name: sql} or an array of {name, sql}. Max 8. Validate each statement with run_query first."
1807
+ ),
1808
+ sources: widgetSourcesInput
1809
+ },
1810
+ withToolLogging("add_dashboard_widget", async (params) => {
1811
+ requireApiKey();
1812
+ const { handleAddDashboardWidget } = await import("./add-dashboard-widget-55UI6H22.js");
1813
+ return {
1814
+ content: [{ type: "text", text: await handleAddDashboardWidget(params) }]
1815
+ };
1816
+ })
1817
+ );
1818
+ server.tool(
1819
+ "update_dashboard_widget",
1820
+ "Changes one custom widget on a dashboard: its name, its TSX `code`, its named LangWatchQL `queries`, its `description` or the `sources` its queries count. Pass only the fields to change; every field left out keeps its stored value. Validate any new query's SQL with run_query first. Returns the updated widget.",
1821
+ {
1822
+ dashboardId: z3.string().describe("The dashboard the widget is on"),
1823
+ widgetId: z3.string().describe("The widget to change, as add_dashboard_widget returned it"),
1824
+ name: z3.string().optional().describe("The widget's new display name"),
1825
+ code: z3.string().optional().describe(
1826
+ "The widget's new TSX source. Runs sandboxed with a global LW (LW.useChartQuery(name), LW.navigate, LW.theme) in scope."
1827
+ ),
1828
+ queries: z3.union([
1829
+ z3.record(z3.string(), z3.string()).describe('{"<name>": "<LWQL SQL>"}'),
1830
+ z3.array(
1831
+ z3.object({
1832
+ name: z3.string().describe('Referenced from code as LW.useChartQuery("name")'),
1833
+ sql: z3.string().describe("The LangWatchQL statement")
1834
+ })
1835
+ ).describe("Named LWQL queries, array form")
1836
+ ]).optional().describe(
1837
+ "The widget's new named LangWatchQL queries, replacing the stored set: either {name: sql} or an array of {name, sql}. Max 8."
1838
+ ),
1839
+ description: z3.string().optional().describe("What the widget's info tip says: a sentence or two"),
1840
+ sources: widgetSourcesInput
1841
+ },
1842
+ withToolLogging("update_dashboard_widget", async (params) => {
1843
+ requireApiKey();
1844
+ const { handleUpdateDashboardWidget } = await import("./update-dashboard-widget-HKCBXH7X.js");
1845
+ return {
1846
+ content: [{ type: "text", text: await handleUpdateDashboardWidget(params) }]
1847
+ };
1848
+ })
1849
+ );
1850
+ registerDashboardCrudTools(server);
1851
+ }
1852
+ function registerDashboardCrudTools(server) {
1853
+ const dashboardId = z3.string().describe("The dashboard's ID, as list_dashboards lists it");
1854
+ const text = (value) => ({ content: [{ type: "text", text: value }] });
1855
+ server.tool(
1856
+ "get_dashboard",
1857
+ "Reads one dashboard in the Dashboards area: its name, scope, description and address. An Analytics Report's ID is not found here; read it with platform_get_analytics_report.",
1858
+ { dashboardId },
1859
+ withToolLogging("get_dashboard", async (params) => {
1860
+ requireApiKey();
1861
+ const { handleGetDashboard } = await import("./dashboards-BA7M6CMO.js");
1862
+ return text(await handleGetDashboard(params));
1863
+ })
1864
+ );
1865
+ server.tool(
1866
+ "create_dashboard",
1867
+ "Creates an empty dashboard in the Dashboards area. Add widgets to it with add_dashboard_widget.",
1868
+ { name: z3.string().describe("The dashboard's name") },
1869
+ withToolLogging("create_dashboard", async (params) => {
1870
+ requireApiKey();
1871
+ const { handleCreateDashboard } = await import("./dashboards-BA7M6CMO.js");
1872
+ return text(await handleCreateDashboard(params));
1873
+ })
1874
+ );
1875
+ server.tool(
1876
+ "rename_dashboard",
1877
+ "Changes a dashboard's name and/or description; a field left out keeps its value.",
1878
+ {
1879
+ dashboardId,
1880
+ name: z3.string().optional().describe("The dashboard's new name"),
1881
+ description: z3.string().optional().describe("The dashboard's new description")
1882
+ },
1883
+ withToolLogging("rename_dashboard", async (params) => {
1884
+ requireApiKey();
1885
+ const { handleRenameDashboard } = await import("./dashboards-BA7M6CMO.js");
1886
+ return text(await handleRenameDashboard(params));
1887
+ })
1888
+ );
1889
+ server.tool(
1890
+ "delete_dashboard",
1891
+ "Deletes a dashboard together with the widgets on it. This cannot be undone.",
1892
+ { dashboardId },
1893
+ withToolLogging("delete_dashboard", async (params) => {
1894
+ requireApiKey();
1895
+ const { handleDeleteDashboard } = await import("./dashboards-BA7M6CMO.js");
1896
+ return text(await handleDeleteDashboard(params));
1897
+ })
1898
+ );
1899
+ server.tool(
1900
+ "list_dashboard_widgets",
1901
+ "Lists the custom widgets on one dashboard, with each widget's ID and query names.",
1902
+ { dashboardId },
1903
+ withToolLogging("list_dashboard_widgets", async (params) => {
1904
+ requireApiKey();
1905
+ const { handleListDashboardWidgets } = await import("./dashboards-BA7M6CMO.js");
1906
+ return text(await handleListDashboardWidgets(params));
1907
+ })
1908
+ );
1909
+ server.tool(
1910
+ "delete_dashboard_widget",
1911
+ "Deletes one custom widget from a dashboard. A widget on another dashboard is not found.",
1912
+ {
1913
+ dashboardId,
1914
+ widgetId: z3.string().describe("The widget's ID, as list_dashboard_widgets lists it")
1915
+ },
1916
+ withToolLogging("delete_dashboard_widget", async (params) => {
1917
+ requireApiKey();
1918
+ const { handleDeleteDashboardWidget } = await import("./dashboards-BA7M6CMO.js");
1919
+ return text(await handleDeleteDashboardWidget(params));
1920
+ })
1921
+ );
1922
+ }
1923
+ function registerWorkflowTools(server) {
1924
+ server.tool(
1925
+ "platform_list_workflows",
1926
+ "List all workflows in the LangWatch project.",
1927
+ {},
1928
+ withToolLogging("platform_list_workflows", async () => {
1929
+ requireApiKey();
1930
+ const { handleListWorkflows } = await import("./list-workflows-55NYC3PC.js");
1931
+ return {
1932
+ content: [{ type: "text", text: await handleListWorkflows() }]
1933
+ };
1934
+ })
1935
+ );
1936
+ server.tool(
1937
+ "platform_get_workflow",
1938
+ "Get a workflow by its ID.",
1939
+ {
1940
+ id: z3.string().describe("The workflow ID")
1941
+ },
1942
+ withToolLogging("platform_get_workflow", async (params) => {
1943
+ requireApiKey();
1944
+ const { handleGetWorkflow } = await import("./get-workflow-3TV2YYC5.js");
1945
+ return {
1946
+ content: [{ type: "text", text: await handleGetWorkflow(params) }]
1947
+ };
1948
+ })
1949
+ );
1950
+ server.tool(
1951
+ "platform_delete_workflow",
1952
+ "Archive (soft-delete) a workflow by its ID.",
1953
+ {
1954
+ id: z3.string().describe("The workflow ID to archive")
1955
+ },
1956
+ withToolLogging("platform_delete_workflow", async (params) => {
1957
+ requireApiKey();
1958
+ const { handleDeleteWorkflow } = await import("./delete-workflow-4CZFIQCQ.js");
1959
+ return {
1960
+ content: [{ type: "text", text: await handleDeleteWorkflow(params) }]
1961
+ };
1962
+ })
1963
+ );
1964
+ server.tool(
1965
+ "platform_run_workflow",
1966
+ "Execute a workflow with JSON input. Returns the workflow output.",
1967
+ {
1968
+ id: z3.string().describe("The workflow ID to run"),
1969
+ input: z3.string().optional().describe("Input data as a JSON object string")
1970
+ },
1971
+ withToolLogging("platform_run_workflow", async (params) => {
1972
+ requireApiKey();
1973
+ const { handleRunWorkflow } = await import("./run-workflow-EFZ6HG5B.js");
1974
+ return {
1975
+ content: [{ type: "text", text: await handleRunWorkflow(params) }]
1976
+ };
1977
+ })
1978
+ );
1979
+ }
1980
+ function registerAnnotationTools(server) {
1981
+ server.tool(
1982
+ "platform_list_annotations",
1983
+ "List all annotations for the project, optionally filtered by trace ID.",
1984
+ {
1985
+ traceId: z3.string().optional().describe("Filter annotations by trace ID")
1986
+ },
1987
+ withToolLogging("platform_list_annotations", async (params) => {
1988
+ requireApiKey();
1989
+ const { handleListAnnotations } = await import("./list-annotations-QCLGEK5A.js");
1990
+ return {
1991
+ content: [{ type: "text", text: await handleListAnnotations(params) }]
1992
+ };
1993
+ })
1994
+ );
1995
+ server.tool(
1996
+ "platform_create_annotation",
1997
+ "Create an annotation (thumbs up/down, comment) for a trace.",
1998
+ {
1999
+ traceId: z3.string().describe("The trace ID to annotate"),
2000
+ comment: z3.string().optional().describe("Annotation comment"),
2001
+ isThumbsUp: z3.boolean().optional().describe("True for positive feedback, false for negative"),
2002
+ email: z3.string().optional().describe("Email of the annotator")
2003
+ },
2004
+ withToolLogging("platform_create_annotation", async (params) => {
2005
+ requireApiKey();
2006
+ const { handleCreateAnnotation } = await import("./create-annotation-DTHCMWHB.js");
2007
+ return {
2008
+ content: [{ type: "text", text: await handleCreateAnnotation(params) }]
2009
+ };
2010
+ })
2011
+ );
2012
+ server.tool(
2013
+ "platform_delete_annotation",
2014
+ "Delete an annotation by its ID.",
2015
+ {
2016
+ id: z3.string().describe("The annotation ID to delete")
2017
+ },
2018
+ withToolLogging("platform_delete_annotation", async (params) => {
2019
+ requireApiKey();
2020
+ const { handleDeleteAnnotation } = await import("./delete-annotation-7X4NYXXM.js");
2021
+ return {
2022
+ content: [{ type: "text", text: await handleDeleteAnnotation(params) }]
2023
+ };
2024
+ })
2025
+ );
2026
+ server.tool(
2027
+ "platform_get_annotation",
2028
+ "Get annotation details by ID.",
2029
+ {
2030
+ id: z3.string().describe("The annotation ID")
2031
+ },
2032
+ withToolLogging("platform_get_annotation", async (params) => {
2033
+ requireApiKey();
2034
+ const { getAnnotation } = await import("./langwatch-api-annotations-QSJNUQQS.js");
2035
+ const annotation = await getAnnotation(params.id);
2036
+ const lines = [
2037
+ `**ID**: ${annotation.id}`,
2038
+ `**Trace ID**: ${annotation.traceId}`,
2039
+ `**Comment**: ${annotation.comment ?? "\u2014"}`,
2040
+ `**Score**: ${annotation.isThumbsUp ? "Thumbs Up" : "\u2014"}`,
2041
+ `**Created**: ${annotation.createdAt}`
2042
+ ];
2043
+ return {
2044
+ content: [{ type: "text", text: lines.join("\n") }]
2045
+ };
2046
+ })
2047
+ );
2048
+ }
2049
+ function registerTriggerTools(server) {
2050
+ server.tool(
2051
+ "platform_list_triggers",
2052
+ "List all triggers (automations) in the project. Triggers automate actions like sending emails, Slack messages, or adding to datasets when conditions are met.",
2053
+ {
2054
+ format: z3.enum(["digest", "json"]).optional().describe("Output format")
2055
+ },
2056
+ withToolLogging("platform_list_triggers", async (params) => {
2057
+ requireApiKey();
2058
+ const { handleListTriggers } = await import("./list-triggers-BGUCUXTJ.js");
2059
+ return { content: [{ type: "text", text: await handleListTriggers(params) }] };
2060
+ })
2061
+ );
2062
+ server.tool(
2063
+ "platform_get_trigger",
2064
+ "Read one trigger (automation) by its ID, including its delivery configuration. Credential values come back as [redacted]; sending them back unchanged on an update keeps the stored values.",
2065
+ {
2066
+ id: z3.string().describe("The trigger ID")
2067
+ },
2068
+ withToolLogging("platform_get_trigger", async (params) => {
2069
+ requireApiKey();
2070
+ const { getTrigger } = await import("./langwatch-api-triggers-G5UWFNN2.js");
2071
+ const trigger = await getTrigger(params.id);
2072
+ return { content: [{ type: "text", text: JSON.stringify(trigger, null, 2) }] };
2073
+ })
2074
+ );
2075
+ server.tool(
2076
+ "platform_create_trigger",
2077
+ [
2078
+ "Create a trigger (automation).",
2079
+ "",
2080
+ "It is about one of three things: matching traces (send `filters` or `filterQuery`), a metric crossing a threshold on a graph (an Alert: send `customGraphId`, `graphAlert` and `alertType`), or a schedule (a Report: send `report`, on an email or Slack channel).",
2081
+ "`actionParams` is the delivery configuration for the channel named in `action` \u2014 recipients for SEND_EMAIL, a destination for SEND_SLACK_MESSAGE, a URL for SEND_WEBHOOK, a dataset and mapping for ADD_TO_DATASET, annotators for ADD_TO_ANNOTATION_QUEUE. Omitted, it is sent as {} and the server refuses a channel that cannot deliver without one.",
2082
+ SLACK_DELIVERY_NOTE,
2083
+ `filters: ${TRIGGER_FILTERS_DESCRIPTION}`,
2084
+ "The delivery channel cannot be changed afterwards."
2085
+ ].join("\n"),
2086
+ {
2087
+ name: z3.string().describe("Trigger name"),
2088
+ action: triggerActionSchema.describe("Which channel it delivers on"),
2089
+ actionParams: actionParamsSchema.optional().describe("The delivery configuration the channel named in `action` reads. Defaults to {}"),
2090
+ filters: z3.string().optional().describe(TRIGGER_FILTERS_DESCRIPTION),
2091
+ filterQuery: z3.string().optional().describe(TRIGGER_FILTER_QUERY_DESCRIPTION),
2092
+ customGraphId: z3.string().optional().describe("Set to make this an alert on that graph"),
2093
+ graphAlert: graphAlertSchema.optional(),
2094
+ report: reportSchema.optional().describe("Set to make this a scheduled report"),
2095
+ templates: templatesSchema.optional(),
2096
+ notificationCadence: notificationCadenceSchema.optional(),
2097
+ message: z3.string().optional().describe("Custom alert message"),
2098
+ alertType: alertTypeSchema.optional().describe("Alert severity. Required for a graph alert.")
2099
+ },
2100
+ withToolLogging("platform_create_trigger", async (params) => {
2101
+ requireApiKey();
2102
+ const { handleCreateTrigger } = await import("./create-trigger-4CNBTISJ.js");
2103
+ return handleCreateTrigger(params);
2104
+ })
2105
+ );
2106
+ server.tool(
2107
+ "platform_update_trigger",
2108
+ [
2109
+ "Update a trigger (automation). Anything left out is left alone.",
2110
+ "",
2111
+ "`actionParams` replaces the delivery configuration as a whole, so send every field it should have from now on. A credential the read hid comes back as [redacted]; send that back to keep the stored value. Changing a webhook's `url` means sending its header values in the same call.",
2112
+ "The delivery channel, the kind of automation and an alert's graph cannot be changed."
2113
+ ].join("\n"),
2114
+ {
2115
+ id: z3.string().describe("The trigger ID"),
2116
+ name: z3.string().optional().describe("New name"),
2117
+ active: z3.boolean().optional().describe("Resume or pause it"),
2118
+ actionParams: actionParamsSchema.optional().describe("The delivery configuration this automation should have from now on"),
2119
+ filters: z3.string().optional().describe(TRIGGER_FILTERS_DESCRIPTION),
2120
+ filterQuery: z3.string().nullable().optional().describe(`${TRIGGER_FILTER_QUERY_DESCRIPTION} null clears the saved query.`),
2121
+ graphAlert: graphAlertSchema.optional().describe("Only for an automation that is already a graph alert"),
2122
+ report: reportSchema.optional().describe("Only for an automation that is already a scheduled report"),
2123
+ templates: templatesSchema.optional(),
2124
+ notificationCadence: notificationCadenceSchema.optional(),
2125
+ message: z3.string().nullable().optional().describe("New alert message. null removes the custom message"),
2126
+ alertType: alertTypeSchema.nullable().optional().describe("New alert severity. null clears it")
2127
+ },
2128
+ withToolLogging("platform_update_trigger", async (params) => {
2129
+ requireApiKey();
2130
+ const { getTrigger, updateTrigger } = await import("./langwatch-api-triggers-G5UWFNN2.js");
2131
+ if (params.actionParams !== void 0) {
2132
+ const saved = await getTrigger(params.id);
2133
+ const boundParams = validateActionParamsForAction({
2134
+ action: saved.action,
2135
+ actionParams: params.actionParams
2136
+ });
2137
+ if (!boundParams.ok) {
2138
+ const { toolError: toolError2 } = await import("./create-trigger-4CNBTISJ.js");
2139
+ return toolError2(boundParams.message);
2140
+ }
2141
+ }
2142
+ const { parseJsonObject, toolError } = await import("./create-trigger-4CNBTISJ.js");
2143
+ const filters = params.filters ? parseJsonObject(params.filters) : void 0;
2144
+ if (params.filters && !filters) {
2145
+ return toolError("filters must be a JSON object");
2146
+ }
2147
+ const trigger = await updateTrigger({ ...params, filters });
2148
+ return {
2149
+ content: [
2150
+ { type: "text", text: `Trigger "${trigger.name}" updated (active: ${trigger.active}).` }
2151
+ ]
2152
+ };
2153
+ })
2154
+ );
2155
+ server.tool(
2156
+ "platform_test_fire_trigger",
2157
+ "Send a trigger's message to the destination it is configured with, to confirm it arrives. Nothing is recorded as a fire.",
2158
+ {
2159
+ id: z3.string().describe("The trigger ID")
2160
+ },
2161
+ withToolLogging("platform_test_fire_trigger", async (params) => {
2162
+ requireApiKey();
2163
+ const { testFireTrigger } = await import("./langwatch-api-triggers-G5UWFNN2.js");
2164
+ const result = await testFireTrigger(params.id);
2165
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
2166
+ })
2167
+ );
2168
+ server.tool(
2169
+ "platform_list_trigger_fires",
2170
+ "What an automation has done, newest first. Metadata only \u2014 no message content. Answers with `nextCursor`; pass it back as `cursor` for the next page (null means there is none).",
2171
+ {
2172
+ id: z3.string().describe("The trigger ID"),
2173
+ limit: z3.number().int().positive().optional().describe("How many fires to return"),
2174
+ cursor: z3.string().optional().describe("The `nextCursor` a previous call answered with")
2175
+ },
2176
+ withToolLogging("platform_list_trigger_fires", async (params) => {
2177
+ requireApiKey();
2178
+ const { handleListTriggerFires } = await import("./list-trigger-fires-7RZUP54Y.js");
2179
+ return { content: [{ type: "text", text: await handleListTriggerFires(params) }] };
2180
+ })
2181
+ );
2182
+ server.tool(
2183
+ "platform_delete_trigger",
2184
+ "Delete a trigger (automation).",
2185
+ {
2186
+ id: z3.string().describe("The trigger ID to delete")
2187
+ },
2188
+ withToolLogging("platform_delete_trigger", async (params) => {
2189
+ requireApiKey();
2190
+ const { deleteTrigger } = await import("./langwatch-api-triggers-G5UWFNN2.js");
2191
+ const result = await deleteTrigger(params.id);
2192
+ return { content: [{ type: "text", text: `Trigger ${result.id} deleted.` }] };
2193
+ })
2194
+ );
2195
+ }
2196
+ function registerMonitorTools(server) {
2197
+ server.tool(
2198
+ "platform_list_monitors",
2199
+ "List all online evaluation monitors for the project.",
2200
+ {},
2201
+ withToolLogging("platform_list_monitors", async () => {
2202
+ requireApiKey();
2203
+ const { listMonitors } = await import("./langwatch-api-monitors-ENHLOL3S.js");
2204
+ const monitors = await listMonitors();
2205
+ if (monitors.length === 0) {
2206
+ return { content: [{ type: "text", text: "No monitors found." }] };
2207
+ }
2208
+ const lines = monitors.map(
2209
+ (m) => `\u2022 ${m.name} (id: ${m.id}, type: ${m.checkType}, ${m.enabled ? "enabled" : "disabled"}, mode: ${m.executionMode}, sample: ${Math.round(m.sample * 100)}%)`
2210
+ );
2211
+ return {
2212
+ content: [
2213
+ {
2214
+ type: "text",
2215
+ text: `Found ${monitors.length} monitor(s):
2216
+ ${lines.join("\n")}`
2217
+ }
2218
+ ]
2219
+ };
2220
+ })
2221
+ );
2222
+ server.tool(
2223
+ "platform_get_monitor",
2224
+ "Get details of a specific online evaluation monitor.",
2225
+ {
2226
+ id: z3.string().describe("The monitor ID")
2227
+ },
2228
+ withToolLogging("platform_get_monitor", async (params) => {
2229
+ requireApiKey();
2230
+ const { getMonitor } = await import("./langwatch-api-monitors-ENHLOL3S.js");
2231
+ const m = await getMonitor(params.id);
2232
+ const lines = [
2233
+ `**${m.name}** (${m.id})`,
2234
+ `- Type: ${m.checkType}`,
2235
+ `- Status: ${m.enabled ? "enabled" : "disabled"}`,
2236
+ `- Mode: ${m.executionMode}`,
2237
+ `- Sample: ${Math.round(m.sample * 100)}%`,
2238
+ `- Level: ${m.level}`,
2239
+ m.evaluatorId ? `- Evaluator: ${m.evaluatorId}` : null,
2240
+ `- Created: ${m.createdAt}`
2241
+ ].filter(Boolean);
2242
+ return { content: [{ type: "text", text: lines.join("\n") }] };
2243
+ })
2244
+ );
2245
+ server.tool(
2246
+ "platform_create_monitor",
2247
+ "Create a new online evaluation monitor that runs an evaluator on incoming traces.",
2248
+ {
2249
+ name: z3.string().describe("Monitor name"),
2250
+ checkType: z3.string().describe("Evaluator check type (e.g. ragas/toxicity, custom/my-eval)"),
2251
+ executionMode: z3.enum(["ON_MESSAGE", "AS_GUARDRAIL", "MANUALLY"]).optional().describe("When to run: ON_MESSAGE (default), AS_GUARDRAIL, or MANUALLY"),
2252
+ sample: z3.number().min(0).max(1).optional().describe("Sampling rate 0.0-1.0 (default: 1.0)"),
2253
+ evaluatorId: z3.string().optional().describe("Link to a saved evaluator by ID")
2254
+ },
2255
+ withToolLogging("platform_create_monitor", async (params) => {
2256
+ requireApiKey();
2257
+ const { createMonitor } = await import("./langwatch-api-monitors-ENHLOL3S.js");
2258
+ const monitor = await createMonitor(params);
2259
+ return {
2260
+ content: [
2261
+ {
2262
+ type: "text",
2263
+ text: `Monitor "${monitor.name}" created (id: ${monitor.id}, type: ${monitor.checkType}).`
2264
+ }
2265
+ ]
2266
+ };
2267
+ })
2268
+ );
2269
+ server.tool(
2270
+ "platform_update_monitor",
2271
+ "Update an existing online evaluation monitor.",
2272
+ {
2273
+ id: z3.string().describe("The monitor ID to update"),
2274
+ name: z3.string().optional().describe("New monitor name"),
2275
+ enabled: z3.boolean().optional().describe("Enable or disable the monitor"),
2276
+ executionMode: z3.enum(["ON_MESSAGE", "AS_GUARDRAIL", "MANUALLY"]).optional().describe("New execution mode"),
2277
+ sample: z3.number().min(0).max(1).optional().describe("New sampling rate")
2278
+ },
2279
+ withToolLogging("platform_update_monitor", async (params) => {
2280
+ requireApiKey();
2281
+ const { updateMonitor } = await import("./langwatch-api-monitors-ENHLOL3S.js");
2282
+ const monitor = await updateMonitor(params);
2283
+ return {
2284
+ content: [
2285
+ {
2286
+ type: "text",
2287
+ text: `Monitor "${monitor.name}" updated (${monitor.enabled ? "enabled" : "disabled"}).`
2288
+ }
2289
+ ]
2290
+ };
2291
+ })
2292
+ );
2293
+ server.tool(
2294
+ "platform_delete_monitor",
2295
+ "Delete an online evaluation monitor.",
2296
+ {
2297
+ id: z3.string().describe("The monitor ID to delete")
2298
+ },
2299
+ withToolLogging("platform_delete_monitor", async (params) => {
2300
+ requireApiKey();
2301
+ const { deleteMonitor } = await import("./langwatch-api-monitors-ENHLOL3S.js");
2302
+ const result = await deleteMonitor(params.id);
2303
+ return {
2304
+ content: [{ type: "text", text: `Monitor ${result.id} deleted.` }]
2305
+ };
2306
+ })
2307
+ );
2308
+ }
2309
+ function registerSecretTools(server) {
2310
+ server.tool(
2311
+ "platform_list_secrets",
2312
+ "List all project secrets (values are never returned, only metadata).",
2313
+ {},
2314
+ withToolLogging("platform_list_secrets", async () => {
2315
+ requireApiKey();
2316
+ const { listSecrets } = await import("./langwatch-api-secrets-AGW6UUMD.js");
2317
+ const secrets = await listSecrets();
2318
+ if (secrets.length === 0) {
2319
+ return { content: [{ type: "text", text: "No secrets found." }] };
2320
+ }
2321
+ const lines = secrets.map((s) => `\u2022 ${s.name} (id: ${s.id}, updated: ${s.updatedAt})`);
2322
+ return {
2323
+ content: [
2324
+ {
2325
+ type: "text",
2326
+ text: `Found ${secrets.length} secret(s):
2327
+ ${lines.join("\n")}`
2328
+ }
2329
+ ]
2330
+ };
2331
+ })
2332
+ );
2333
+ server.tool(
2334
+ "platform_create_secret",
2335
+ "Create a new encrypted project secret. Name must be UPPER_SNAKE_CASE (e.g. MY_API_KEY).",
2336
+ {
2337
+ name: z3.string().describe("Secret name in UPPER_SNAKE_CASE (e.g. OPENAI_API_KEY)"),
2338
+ value: z3.string().describe("The secret value (will be encrypted at rest)")
2339
+ },
2340
+ withToolLogging("platform_create_secret", async (params) => {
2341
+ requireApiKey();
2342
+ const { createSecret } = await import("./langwatch-api-secrets-AGW6UUMD.js");
2343
+ const secret = await createSecret({ name: params.name, value: params.value });
2344
+ return {
2345
+ content: [
2346
+ {
2347
+ type: "text",
2348
+ text: `Secret "${secret.name}" created (id: ${secret.id}).`
2349
+ }
2350
+ ]
2351
+ };
2352
+ })
2353
+ );
2354
+ server.tool(
2355
+ "platform_update_secret",
2356
+ "Update an existing project secret's value.",
2357
+ {
2358
+ id: z3.string().describe("The secret ID to update"),
2359
+ value: z3.string().describe("The new secret value (will be encrypted at rest)")
2360
+ },
2361
+ withToolLogging("platform_update_secret", async (params) => {
2362
+ requireApiKey();
2363
+ const { updateSecret } = await import("./langwatch-api-secrets-AGW6UUMD.js");
2364
+ const secret = await updateSecret({ id: params.id, value: params.value });
2365
+ return {
2366
+ content: [
2367
+ {
2368
+ type: "text",
2369
+ text: `Secret "${secret.name}" updated.`
2370
+ }
2371
+ ]
2372
+ };
2373
+ })
2374
+ );
2375
+ server.tool(
2376
+ "platform_delete_secret",
2377
+ "Delete a project secret.",
2378
+ {
2379
+ id: z3.string().describe("The secret ID to delete")
2380
+ },
2381
+ withToolLogging("platform_delete_secret", async (params) => {
2382
+ requireApiKey();
2383
+ const { deleteSecret } = await import("./langwatch-api-secrets-AGW6UUMD.js");
2384
+ const result = await deleteSecret(params.id);
2385
+ return { content: [{ type: "text", text: `Secret ${result.id} deleted.` }] };
2386
+ })
2387
+ );
2388
+ }
2389
+ function registerExperimentTools(server) {
2390
+ server.tool(
2391
+ "platform_run_experiment",
2392
+ "Start an experiment run by slug. Returns a run ID for polling status.",
2393
+ {
2394
+ slug: z3.string().describe("The experiment slug to run"),
2395
+ concurrency: z3.number().int().min(1).max(24).optional().describe(
2396
+ "Cells (one row against one target) to run at once, for this run only (default: the server's setting). Use 1 to run them one at a time, in order."
2397
+ )
2398
+ },
2399
+ withToolLogging("platform_run_experiment", async (params) => {
2400
+ requireApiKey();
2401
+ return {
2402
+ content: [{ type: "text", text: await handleRunExperiment(params) }]
2403
+ };
2404
+ })
2405
+ );
2406
+ server.tool(
2407
+ "platform_experiment_status",
2408
+ "Check the status of an experiment run (running, completed, stopped, or interrupted) plus its progress. Works for both platform-run and SDK-logged experiments; pass experimentSlug for SDK runs or runs older than 24h.",
2409
+ {
2410
+ runId: z3.string().describe("The run ID returned from platform_run_experiment"),
2411
+ experimentSlug: z3.string().optional().describe(
2412
+ "Experiment slug \u2014 required for SDK-logged runs and runs older than 24h (the Redis run-state they would otherwise be read from is absent/expired). Use platform_experiment_list_runs to find the slug."
2413
+ )
2414
+ },
2415
+ withToolLogging("platform_experiment_status", async (params) => {
2416
+ requireApiKey();
2417
+ return {
2418
+ content: [{ type: "text", text: await handleExperimentStatus(params) }]
2419
+ };
2420
+ })
2421
+ );
2422
+ server.tool(
2423
+ "platform_experiment_list",
2424
+ "List experiments configured in the LangWatch project. Returns a markdown table with each experiment's slug, name, last-run timestamp, and run count. Use this to discover experiment slugs before drilling into runs with platform_experiment_list_runs. Output is capped (default 25, max 100) to protect the agent's context window.",
2425
+ {
2426
+ limit: z3.number().int().positive().max(100).optional().describe("Maximum number of experiments to include (default 25, hard-capped at 100)")
2427
+ },
2428
+ withToolLogging("platform_experiment_list", async (params) => {
2429
+ requireApiKey();
2430
+ return {
2431
+ content: [{ type: "text", text: await handleExperimentList(params) }]
2432
+ };
2433
+ })
2434
+ );
2435
+ server.tool(
2436
+ "platform_experiment_list_runs",
2437
+ "List experiment runs for a specific experiment slug. Returns a markdown table with each run's id, status, started/finished timestamps, and pass-rate summary. Use this to discover run ids before calling platform_experiment_results. Output is capped (default 25, max 100) to protect the agent's context window.",
2438
+ {
2439
+ experimentSlug: z3.string().describe("Experiment slug from platform_experiment_list (e.g. 'checkout-flow')"),
2440
+ limit: z3.number().int().positive().max(100).optional().describe("Maximum number of runs to include (default 25, hard-capped at 100)")
2441
+ },
2442
+ withToolLogging("platform_experiment_list_runs", async (params) => {
2443
+ requireApiKey();
2444
+ return {
2445
+ content: [{ type: "text", text: await handleExperimentListRuns(params) }]
2446
+ };
2447
+ })
2448
+ );
2449
+ server.tool(
2450
+ "platform_experiment_results",
2451
+ "Fetch per-row results for an experiment run so you can debug evaluator scores and missed rows. Serves partial results for runs that are still running or were interrupted (it does not require a completed run), with the run status noted in the output. Returns a markdown report: per-evaluator averages plus row-by-row scores and failure details. Output is capped at 50 rows to protect the agent's context window \u2014 narrow with `filter: 'failed'` or `evaluator` to see what matters.",
2452
+ {
2453
+ runId: z3.string().describe("The run ID returned from platform_run_experiment"),
2454
+ experimentSlug: z3.string().optional().describe(
2455
+ "Experiment slug \u2014 required when fetching results for runs older than 24h (Redis state may have expired). Use platform_experiment_list_runs to find the slug."
2456
+ ),
2457
+ filter: z3.enum(["all", "failed"]).optional().describe(
2458
+ "Filter rows: 'failed' shows only rows that errored or failed an evaluation; 'all' (default) shows everything"
2459
+ ),
2460
+ evaluator: z3.string().optional().describe("Show only this evaluator's results (matched on `evaluator` field)"),
2461
+ limit: z3.number().int().positive().max(50).optional().describe(
2462
+ "Maximum number of rows to include in the markdown output (default 50, hard-capped at 50)."
2463
+ )
2464
+ },
2465
+ withToolLogging("platform_experiment_results", async (params) => {
2466
+ requireApiKey();
2467
+ return {
2468
+ content: [{ type: "text", text: await handleExperimentResults(params) }]
2469
+ };
2470
+ })
2471
+ );
2472
+ }
2473
+ function registerDatasetTools(server) {
2474
+ server.tool(
2475
+ "platform_list_datasets",
2476
+ "List all datasets on the LangWatch platform with their names, slugs, columns, and record counts. Returns AI-readable digest by default.",
2477
+ {
2478
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
2479
+ },
2480
+ async (params) => {
2481
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2482
+ requireApiKey2();
2483
+ const { handleListDatasets } = await import("./list-datasets-Z7OOE4KI.js");
2484
+ return {
2485
+ content: [{ type: "text", text: await handleListDatasets(params) }]
2486
+ };
2487
+ }
2488
+ );
2489
+ server.tool(
2490
+ "platform_get_dataset",
2491
+ "Get full details of a dataset on the LangWatch platform by slug or ID, including column definitions and a preview of records.",
2492
+ {
2493
+ slugOrId: z3.string().describe("The dataset slug or ID to retrieve"),
2494
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
2495
+ },
2496
+ async (params) => {
2497
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2498
+ requireApiKey2();
2499
+ const { handleGetDataset } = await import("./get-dataset-LWXDVMVE.js");
2500
+ return {
2501
+ content: [{ type: "text", text: await handleGetDataset(params) }]
2502
+ };
2503
+ }
2504
+ );
2505
+ server.tool(
2506
+ "platform_create_dataset",
2507
+ "Create a new dataset on the LangWatch platform.",
2508
+ createDatasetSchema.shape,
2509
+ async (params) => {
2510
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2511
+ requireApiKey2();
2512
+ const { handleCreateDataset } = await import("./create-dataset-DJNXYJ5B.js");
2513
+ return {
2514
+ content: [{ type: "text", text: await handleCreateDataset(params) }]
2515
+ };
2516
+ }
2517
+ );
2518
+ server.tool(
2519
+ "platform_update_dataset",
2520
+ "Update an existing dataset on the LangWatch platform (name and/or column types).",
2521
+ {
2522
+ slugOrId: z3.string().describe("The dataset slug or ID to update"),
2523
+ name: z3.string().optional().describe("Updated dataset name"),
2524
+ columnTypes: z3.array(datasetColumnDefinitionSchema).optional().describe("Updated column definitions")
2525
+ },
2526
+ async (params) => {
2527
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2528
+ requireApiKey2();
2529
+ const { handleUpdateDataset } = await import("./update-dataset-GHIN6JR3.js");
2530
+ return {
2531
+ content: [{ type: "text", text: await handleUpdateDataset(params) }]
2532
+ };
2533
+ }
2534
+ );
2535
+ server.tool(
2536
+ "platform_delete_dataset",
2537
+ "Delete (archive) a dataset on the LangWatch platform.",
2538
+ {
2539
+ slugOrId: z3.string().describe("The dataset slug or ID to delete")
2540
+ },
2541
+ async (params) => {
2542
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2543
+ requireApiKey2();
2544
+ const { handleDeleteDataset } = await import("./delete-dataset-RUTZBBVD.js");
2545
+ return {
2546
+ content: [{ type: "text", text: await handleDeleteDataset(params) }]
2547
+ };
2548
+ }
2549
+ );
2550
+ server.tool(
2551
+ "platform_list_dataset_records",
2552
+ "List records in a dataset on the LangWatch platform with pagination.",
2553
+ {
2554
+ slugOrId: z3.string().describe("The dataset slug or ID to list records from"),
2555
+ page: z3.number().int().min(1).optional().describe("Page number (default: 1)"),
2556
+ limit: z3.number().int().min(1).max(1e3).optional().describe("Records per page (default: 50, max: 1000)"),
2557
+ format: z3.enum(["digest", "json"]).optional().describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)")
2558
+ },
2559
+ async (params) => {
2560
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2561
+ requireApiKey2();
2562
+ const { handleListDatasetRecords } = await import("./list-dataset-records-KEHT7G6Q.js");
2563
+ return {
2564
+ content: [{ type: "text", text: await handleListDatasetRecords(params) }]
2565
+ };
2566
+ }
2567
+ );
2568
+ server.tool(
2569
+ "platform_create_dataset_records",
2570
+ "Add records to a dataset on the LangWatch platform in batch (max 1000 per call).",
2571
+ {
2572
+ slugOrId: z3.string().describe("The dataset slug or ID to add records to"),
2573
+ entries: z3.array(z3.record(z3.string(), z3.unknown())).min(1).max(1e3).describe("Array of record entries to create (key-value objects matching dataset columns)")
2574
+ },
2575
+ async (params) => {
2576
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2577
+ requireApiKey2();
2578
+ const { handleCreateDatasetRecords } = await import("./create-dataset-records-TMEOTLTO.js");
2579
+ return {
2580
+ content: [{ type: "text", text: await handleCreateDatasetRecords(params) }]
2581
+ };
2582
+ }
2583
+ );
2584
+ server.tool(
2585
+ "platform_update_dataset_record",
2586
+ "Update a single record in a dataset on the LangWatch platform.",
2587
+ {
2588
+ slugOrId: z3.string().describe("The dataset slug or ID containing the record"),
2589
+ recordId: z3.string().describe("The record ID to update"),
2590
+ entry: z3.record(z3.string(), z3.unknown()).describe("Updated record entry (key-value object)")
2591
+ },
2592
+ async (params) => {
2593
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2594
+ requireApiKey2();
2595
+ const { handleUpdateDatasetRecord } = await import("./update-dataset-record-6VI7K2CL.js");
2596
+ return {
2597
+ content: [{ type: "text", text: await handleUpdateDatasetRecord(params) }]
2598
+ };
2599
+ }
2600
+ );
2601
+ server.tool(
2602
+ "platform_delete_dataset_records",
2603
+ "Delete records from a dataset on the LangWatch platform by their IDs.",
2604
+ {
2605
+ slugOrId: z3.string().describe("The dataset slug or ID containing the records"),
2606
+ recordIds: z3.array(z3.string()).min(1).max(1e3).describe("Array of record IDs to delete")
2607
+ },
2608
+ async (params) => {
2609
+ const { requireApiKey: requireApiKey2 } = await import("./config.js");
2610
+ requireApiKey2();
2611
+ const { handleDeleteDatasetRecords } = await import("./delete-dataset-records-75UAR5IS.js");
2612
+ return {
2613
+ content: [{ type: "text", text: await handleDeleteDatasetRecords(params) }]
2614
+ };
2615
+ }
2616
+ );
2617
+ }
2618
+ function registerProjectTools(server) {
2619
+ server.tool(
2620
+ "platform_list_projects",
2621
+ "List all projects in your LangWatch organization. Requires an org-level API key.",
2622
+ {
2623
+ page: z3.number().int().positive().optional().describe("Page number (default: 1)"),
2624
+ limit: z3.number().int().positive().max(1e3).optional().describe("Results per page (default: 100)")
2625
+ },
2626
+ withToolLogging("platform_list_projects", async (params) => {
2627
+ requireApiKey();
2628
+ const { handleListProjects } = await import("./list-projects-X72ZXVY5.js");
2629
+ return {
2630
+ content: [{ type: "text", text: await handleListProjects(params) }]
2631
+ };
2632
+ })
2633
+ );
2634
+ server.tool(
2635
+ "platform_get_project",
2636
+ "Get details of a specific project by its ID.",
2637
+ {
2638
+ id: z3.string().describe("The project ID")
2639
+ },
2640
+ withToolLogging("platform_get_project", async (params) => {
2641
+ requireApiKey();
2642
+ const { handleGetProject } = await import("./get-project-EQVB4LEL.js");
2643
+ return {
2644
+ content: [{ type: "text", text: await handleGetProject(params) }]
2645
+ };
2646
+ })
2647
+ );
2648
+ server.tool(
2649
+ "platform_create_project",
2650
+ `Create a new project in your LangWatch organization. Returns a one-time service API key for the project.
2651
+
2652
+ You must provide either teamId (to add the project to an existing team) or newTeamName (to create a new team for the project).`,
2653
+ {
2654
+ name: z3.string().describe("Project name"),
2655
+ language: z3.string().describe('Programming language (e.g. "python", "typescript")'),
2656
+ framework: z3.string().describe('Framework (e.g. "openai", "langchain", "custom")'),
2657
+ teamId: z3.string().optional().describe("ID of an existing team to add the project to"),
2658
+ newTeamName: z3.string().optional().describe("Name for a new team to create for this project")
2659
+ },
2660
+ withToolLogging("platform_create_project", async (params) => {
2661
+ requireApiKey();
2662
+ const { handleCreateProject } = await import("./create-project-YL345T5L.js");
2663
+ return {
2664
+ content: [{ type: "text", text: await handleCreateProject(params) }]
2665
+ };
2666
+ })
2667
+ );
2668
+ server.tool(
2669
+ "platform_update_project",
2670
+ "Update an existing project's name, language, framework, or PII redaction level.",
2671
+ {
2672
+ id: z3.string().describe("The project ID to update"),
2673
+ name: z3.string().optional().describe("New project name"),
2674
+ language: z3.string().optional().describe("New programming language"),
2675
+ framework: z3.string().optional().describe("New framework"),
2676
+ piiRedactionLevel: z3.enum(["STRICT", "ESSENTIAL", "DISABLED"]).optional().describe("PII redaction level")
2677
+ },
2678
+ withToolLogging("platform_update_project", async (params) => {
2679
+ requireApiKey();
2680
+ const { handleUpdateProject } = await import("./update-project-75H3LDQT.js");
2681
+ return {
2682
+ content: [{ type: "text", text: await handleUpdateProject(params) }]
2683
+ };
2684
+ })
2685
+ );
2686
+ server.tool(
2687
+ "platform_archive_project",
2688
+ "Archive (soft-delete) a project. The project can be restored later if needed.",
2689
+ {
2690
+ id: z3.string().describe("The project ID to archive")
2691
+ },
2692
+ withToolLogging("platform_archive_project", async (params) => {
2693
+ requireApiKey();
2694
+ const { handleArchiveProject } = await import("./archive-project-L4PAKT34.js");
2695
+ return {
2696
+ content: [{ type: "text", text: await handleArchiveProject(params) }]
2697
+ };
2698
+ })
2699
+ );
2700
+ }
2701
+ function registerApiKeyTools(server) {
2702
+ server.tool(
2703
+ "platform_list_api_keys",
2704
+ "List all API keys in your LangWatch organization. Shows key metadata, status (active/revoked/expired), and role bindings. API key tokens are never returned.",
2705
+ {},
2706
+ withToolLogging("platform_list_api_keys", async () => {
2707
+ requireApiKey();
2708
+ const { handleListApiKeys } = await import("./list-api-keys-DAVOBPFK.js");
2709
+ return {
2710
+ content: [{ type: "text", text: await handleListApiKeys() }]
2711
+ };
2712
+ })
2713
+ );
2714
+ server.tool(
2715
+ "platform_create_api_key",
2716
+ `Create a new API key. Two types available:
2717
+
2718
+ - **personal**: Tied to a user, requires explicit role bindings (e.g. ADMIN on PROJECT:proj_123).
2719
+ - **service**: Not tied to a user. Use projectIds for project-scoped service keys, or omit for org-wide access.
2720
+
2721
+ The token is returned once and cannot be retrieved again.`,
2722
+ {
2723
+ keyType: z3.enum(["personal", "service"]).describe("Key type: 'personal' (user-tied) or 'service' (headless)"),
2724
+ name: z3.string().describe("Key name (max 100 chars)"),
2725
+ description: z3.string().optional().describe("Key description (max 500 chars)"),
2726
+ expiresAt: z3.string().optional().describe("Expiration date (ISO string)"),
2727
+ bindings: z3.array(
2728
+ z3.object({
2729
+ role: z3.enum(["ADMIN", "MEMBER", "VIEWER"]).describe("Role"),
2730
+ scopeType: z3.enum(["ORGANIZATION", "TEAM", "PROJECT"]).describe("Scope type"),
2731
+ scopeId: z3.string().describe("Scope ID (org, team, or project ID)")
2732
+ })
2733
+ ).optional().describe("Role bindings (required for personal keys)"),
2734
+ projectIds: z3.array(z3.string()).optional().describe("Project IDs for service keys (creates ADMIN bindings per project)")
2735
+ },
2736
+ withToolLogging("platform_create_api_key", async (params) => {
2737
+ requireApiKey();
2738
+ const { handleCreateApiKey } = await import("./create-api-key-JPEUIXGM.js");
2739
+ return {
2740
+ content: [{ type: "text", text: await handleCreateApiKey(params) }]
2741
+ };
2742
+ })
2743
+ );
2744
+ server.tool(
2745
+ "platform_revoke_api_key",
2746
+ "Revoke an API key. Once revoked, the key can no longer be used for authentication.",
2747
+ {
2748
+ id: z3.string().describe("The API key ID to revoke")
2749
+ },
2750
+ withToolLogging("platform_revoke_api_key", async (params) => {
2751
+ requireApiKey();
2752
+ const { handleRevokeApiKey } = await import("./revoke-api-key-NZNCFOIR.js");
2753
+ return {
2754
+ content: [{ type: "text", text: await handleRevokeApiKey(params) }]
2755
+ };
2756
+ })
2757
+ );
2758
+ }
12
2759
  export {
13
2760
  createMcpServer
14
2761
  };