@langwatch/mcp-server 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +7 -0
- package/.npmignore +41 -0
- package/CHANGELOG.md +280 -200
- package/CONTRIBUTING.md +10 -0
- package/README.md +82 -52
- package/dist/add-dashboard-widget-4ZC5UN73.js +50 -0
- package/dist/add-dashboard-widget-ACC6QYBG.js +49 -0
- package/dist/archive-project-KWFM3I5Z.js +19 -0
- package/dist/{archive-project-4EQGLBJJ.js → archive-project-SPVHXK6A.js} +4 -4
- package/dist/archive-run-plan-65QN3XRX.js +16 -0
- package/dist/archive-run-plan-Z4SYE6BP.js +15 -0
- package/dist/{archive-scenario-W6BXYDZ3.js → archive-scenario-A6QV5LOW.js} +4 -4
- package/dist/archive-scenario-FXEYQNEH.js +19 -0
- package/dist/archive-test-suite-5ADQ3KPQ.js +15 -0
- package/dist/archive-test-suite-NKZDZY3D.js +16 -0
- package/dist/assign-prompt-tag-5IUZXJ2T.js +23 -0
- package/dist/{assign-prompt-tag-VLXUWEKU.js → assign-prompt-tag-HBZLBIFD.js} +8 -4
- package/dist/{chunk-CDYQOY5U.js → chunk-3OCG3QPU.js} +7 -10
- package/dist/chunk-3YLN3ATJ.js +241 -0
- package/dist/chunk-5FNCQRCX.js +91 -0
- package/dist/chunk-6YZUOD34.js +55 -0
- package/dist/{chunk-WVI5CJ6S.js → chunk-7AQLPVFL.js} +2 -2
- package/dist/chunk-7VNSRCXO.js +129 -0
- package/dist/{chunk-7RHG56OH.js → chunk-B4J3PJLX.js} +11 -10
- package/dist/{chunk-ZXKLPC2E.js → chunk-BIIKE3EI.js} +1 -1
- package/dist/{chunk-37SMPMQS.js → chunk-DMM3JQ42.js} +7 -2
- package/dist/{chunk-NUFPTHKU.js → chunk-FOKVY3BK.js} +1 -1
- package/dist/{chunk-WT4PO4A3.js → chunk-H6WGMG2T.js} +10 -7
- package/dist/{chunk-TOXJQDIZ.js → chunk-HGELNIVY.js} +135 -51
- package/dist/{chunk-UJPXFA26.js → chunk-HHMNZAPD.js} +101 -74
- package/dist/{chunk-J42FWRJ2.js → chunk-I7ATIZED.js} +7 -7
- package/dist/{chunk-AQJO2EI5.js → chunk-ILZDO25L.js} +7 -10
- package/dist/{chunk-4QC7SA2L.js → chunk-J7PQKBLD.js} +884 -559
- package/dist/{chunk-YX3A3CFM.js → chunk-JRJPH75H.js} +7 -13
- package/dist/chunk-K2I7Z3LZ.js +28 -0
- package/dist/{chunk-5CCZFUXS.js → chunk-NKMTIHIJ.js} +6 -9
- package/dist/chunk-O7EMWEVJ.js +44 -0
- package/dist/chunk-PTDR56Z3.js +37 -0
- package/dist/chunk-RABYBFX5.js +43 -0
- package/dist/chunk-SUPUUC4W.js +64 -0
- package/dist/{chunk-SFB4MU6H.js → chunk-SXFC3FNH.js} +4 -4
- package/dist/chunk-U24EACC4.js +67 -0
- package/dist/chunk-UNJM3BIL.js +21 -0
- package/dist/{chunk-ZWIL4EFL.js → chunk-UTJCWXPZ.js} +4 -4
- package/dist/chunk-UWLE5T2F.js +312 -0
- package/dist/chunk-VMWJD732.js +57 -0
- package/dist/chunk-XUIJYNRJ.js +15 -0
- package/dist/chunk-Y34DHR5K.js +67 -0
- package/dist/chunk-YCARO42Z.js +516 -0
- package/dist/config-EAKW6F26.js +16 -0
- package/dist/config.cjs +109 -0
- package/dist/config.js +5 -4
- package/dist/{create-agent-7XHO6UIS.js → create-agent-AYZ3LB3V.js} +4 -4
- package/dist/create-agent-S5ZBCEFT.js +23 -0
- package/dist/create-annotation-LHNCLHYP.js +29 -0
- package/dist/{create-annotation-YQXRQC6A.js → create-annotation-VUSXULQK.js} +10 -5
- package/dist/{create-api-key-YUS36KBU.js → create-api-key-NNYKARMG.js} +5 -7
- package/dist/create-api-key-T4KSNPKB.js +24 -0
- package/dist/create-dashboard-DHVFMEU4.js +18 -0
- package/dist/{create-dashboard-4Z5JO5LB.js → create-dashboard-WTKKS5Z6.js} +4 -4
- package/dist/{create-dataset-E6NMSVX7.js → create-dataset-2MC4SD4O.js} +6 -6
- package/dist/create-dataset-LUP5PVDM.js +22 -0
- package/dist/create-dataset-records-NSKBZP3G.js +20 -0
- package/dist/{create-dataset-records-SKHPRD2K.js → create-dataset-records-W4GLN2FV.js} +5 -7
- package/dist/{create-evaluator-JXF5PF33.js → create-evaluator-643G6LXL.js} +4 -4
- package/dist/create-evaluator-HMTYO5SS.js +27 -0
- package/dist/create-mcp-server.cjs +9513 -0
- package/dist/create-mcp-server.js +2647 -5
- package/dist/create-project-6N7PIYRY.js +44 -0
- package/dist/{create-project-CS2CB57Y.js → create-project-KAIC46UY.js} +4 -4
- package/dist/{create-prompt-F5RMX3L3.js → create-prompt-E67LXIYT.js} +5 -7
- package/dist/create-prompt-PKJ7EDRR.js +37 -0
- package/dist/create-prompt-tag-4YO3FSJ3.js +17 -0
- package/dist/{create-prompt-tag-GMRWTEFR.js → create-prompt-tag-OPQUEXWB.js} +3 -3
- package/dist/create-scenario-5PJBMAXO.js +33 -0
- package/dist/{create-scenario-JDR2VB2H.js → create-scenario-UEM77OE7.js} +11 -4
- package/dist/create-test-suite-4IPXVSFQ.js +37 -0
- package/dist/create-test-suite-SZEPVXSE.js +36 -0
- package/dist/create-trigger-5HZSCRAN.js +49 -0
- package/dist/create-trigger-PRELMT6J.js +48 -0
- package/dist/delete-agent-JUIZWAXG.js +18 -0
- package/dist/{delete-agent-O3EEH4S5.js → delete-agent-QMEK3XHK.js} +4 -4
- package/dist/delete-annotation-HY3O2OGR.js +17 -0
- package/dist/{delete-annotation-XP6FQM7A.js → delete-annotation-VKIQWUYW.js} +4 -4
- package/dist/delete-dashboard-76N66XXL.js +18 -0
- package/dist/{delete-dashboard-RFUNYCNP.js → delete-dashboard-JVIVHNJM.js} +4 -4
- package/dist/delete-dataset-HQCR66MG.js +18 -0
- package/dist/{delete-dataset-3MJFRDLP.js → delete-dataset-WKRUW4FP.js} +5 -7
- package/dist/{delete-dataset-records-WPMU5GL5.js → delete-dataset-records-C2IQFFVQ.js} +5 -7
- package/dist/delete-dataset-records-SDUHQLLM.js +19 -0
- package/dist/delete-evaluator-4SNSCLEE.js +16 -0
- package/dist/delete-evaluator-QP3QHTLN.js +15 -0
- package/dist/delete-prompt-tag-NZ4JQMYG.js +17 -0
- package/dist/{delete-prompt-tag-KNMZVSPB.js → delete-prompt-tag-YYQ4VMAD.js} +3 -3
- package/dist/{delete-workflow-3NUXO46U.js → delete-workflow-5J46T73T.js} +4 -4
- package/dist/delete-workflow-JSWABL5F.js +17 -0
- package/dist/discover-evaluator-schema-UHWA5HVL.js +120 -0
- package/dist/discover-evaluator-schema-XKCLLCB3.js +118 -0
- package/dist/discover-scenario-schema-NOJY6S7E.js +137 -0
- package/dist/discover-scenario-schema-OOFIQKOG.js +139 -0
- package/dist/{discover-schema-L6QDPON3.js → discover-schema-55YJBFGQ.js} +181 -186
- package/dist/discover-schema-A33SLMXW.js +442 -0
- package/dist/get-agent-LJQX7OF4.js +69 -0
- package/dist/get-agent-OTIYUFZA.js +70 -0
- package/dist/get-analytics-DCS72353.js +80 -0
- package/dist/get-analytics-IQZKJHLW.js +79 -0
- package/dist/get-dashboard-5NYJEIX2.js +29 -0
- package/dist/{get-dashboard-KHBEGZGL.js → get-dashboard-QMTYDQAZ.js} +4 -4
- package/dist/get-dataset-ALSP7RTX.js +64 -0
- package/dist/get-dataset-X4B5JDG2.js +63 -0
- package/dist/{get-evaluator-ADMKVWYZ.js → get-evaluator-ISMC4LLI.js} +4 -4
- package/dist/get-evaluator-XEUZGZCH.js +47 -0
- package/dist/{get-project-RAK7URQJ.js → get-project-5VKROSDH.js} +4 -4
- package/dist/get-project-RM6FRCBK.js +26 -0
- package/dist/get-prompt-OWXJZHQQ.js +95 -0
- package/dist/get-prompt-W52DY36E.js +94 -0
- package/dist/get-run-plan-BE3M2AUX.js +59 -0
- package/dist/get-run-plan-RUXQ6PAX.js +58 -0
- package/dist/get-scenario-RNM7BAX2.js +37 -0
- package/dist/{get-scenario-ZTAJJL5T.js → get-scenario-YM4ODWW6.js} +8 -4
- package/dist/get-simulation-run-6GIATQE5.js +69 -0
- package/dist/{get-simulation-run-T2ZQWYH2.js → get-simulation-run-LCFSMPGQ.js} +33 -20
- package/dist/get-test-suite-GC2O7YH6.js +49 -0
- package/dist/get-test-suite-P5LJ6VLF.js +48 -0
- package/dist/get-trace-KBRVV4NN.js +45 -0
- package/dist/{get-trace-K7U234Q4.js → get-trace-SEF3FMR5.js} +11 -13
- package/dist/get-workflow-7PVED7JE.js +25 -0
- package/dist/{get-workflow-L5L2PSK3.js → get-workflow-YXULW4GC.js} +4 -4
- package/dist/{http-security-EU3CHTY7.js → http-security-DNLBNQZL.js} +2 -2
- package/dist/{http-server-YXHFPPGA.js → http-server-IR4QOS43.js} +4071 -342
- package/dist/index.js +60221 -38077
- package/dist/langwatch-api-annotations-6SA4OB4C.js +17 -0
- package/dist/{langwatch-api-annotations-FDAS6NQ7.js → langwatch-api-annotations-TBHLCQ5R.js} +4 -4
- package/dist/langwatch-api-dashboards-6EO2VVZW.js +17 -0
- package/dist/{langwatch-api-dashboards-JTVCS7ZJ.js → langwatch-api-dashboards-NHU5BZD7.js} +4 -4
- package/dist/langwatch-api-monitors-CBF3FGIM.js +47 -0
- package/dist/{langwatch-api-monitors-3J7UIQME.js → langwatch-api-monitors-YNWA2M5Y.js} +11 -16
- package/dist/langwatch-api-secrets-ADTOJELP.js +56 -0
- package/dist/langwatch-api-secrets-RNFO4LF2.js +55 -0
- package/dist/langwatch-api-triggers-KQDQC6L5.js +22 -0
- package/dist/langwatch-api-triggers-QS7HDIVW.js +23 -0
- package/dist/{langwatch-api-workflows-NBJX5KOG.js → langwatch-api-workflows-OKC4FXIF.js} +4 -4
- package/dist/langwatch-api-workflows-XGJE6SR5.js +15 -0
- package/dist/list-agents-HVYWEQMI.js +43 -0
- package/dist/list-agents-RBAOCF53.js +42 -0
- package/dist/list-annotations-HJH4NBLD.js +38 -0
- package/dist/{list-annotations-ZTVP4EEL.js → list-annotations-J4UAHSUN.js} +10 -5
- package/dist/{list-api-keys-CWD4UQT5.js → list-api-keys-7UTJRZDG.js} +10 -5
- package/dist/list-api-keys-A2PMV435.js +42 -0
- package/dist/list-dashboard-boards-OPWROHMG.js +33 -0
- package/dist/list-dashboard-boards-U2GJ7GC5.js +34 -0
- package/dist/{list-dashboards-PWG6P6KR.js → list-dashboards-ROSYFLDQ.js} +4 -4
- package/dist/list-dashboards-RQ635ZU7.js +32 -0
- package/dist/{list-dataset-records-IOE7LQXS.js → list-dataset-records-M5TKA6LK.js} +14 -10
- package/dist/list-dataset-records-WFM7F33C.js +43 -0
- package/dist/{list-datasets-ZT4JYUDT.js → list-datasets-B5X4P7BQ.js} +9 -13
- package/dist/list-datasets-THUKQHPX.js +56 -0
- package/dist/{list-evaluators-SJHC7VBH.js → list-evaluators-MYH2OVCI.js} +5 -7
- package/dist/list-evaluators-NLVWF6K7.js +32 -0
- package/dist/{list-model-providers-PPD2WPD2.js → list-model-providers-3JUGP4YI.js} +4 -4
- package/dist/list-model-providers-H3XKORFO.js +35 -0
- package/dist/{list-projects-K7QAIX2I.js → list-projects-6EJ5YYEW.js} +5 -7
- package/dist/list-projects-YNNSRMLC.js +40 -0
- package/dist/{list-prompt-tags-FOO6TNHB.js → list-prompt-tags-NVFQO6WX.js} +3 -3
- package/dist/list-prompt-tags-XUQTTAH4.js +23 -0
- package/dist/{list-prompts-SC2HAUFG.js → list-prompts-HSIMMHTF.js} +4 -6
- package/dist/list-prompts-IRI6AIXO.js +30 -0
- package/dist/list-run-plans-PX2XI6EK.js +46 -0
- package/dist/list-run-plans-QALLCN2K.js +45 -0
- package/dist/list-scenarios-QZRPVHS4.js +46 -0
- package/dist/list-scenarios-SSJQEMJP.js +45 -0
- package/dist/list-simulation-runs-KNOLGMDV.js +53 -0
- package/dist/{list-simulation-runs-KF5THG5H.js → list-simulation-runs-OEADLLQR.js} +19 -9
- package/dist/list-test-suites-ANAQBLDG.js +34 -0
- package/dist/list-test-suites-N4WVLBKK.js +35 -0
- package/dist/list-trigger-fires-JV3WFYYG.js +19 -0
- package/dist/list-trigger-fires-WTW4DVIW.js +20 -0
- package/dist/list-triggers-SQNIG35Q.js +45 -0
- package/dist/list-triggers-V4Y356AK.js +46 -0
- package/dist/{list-workflows-QSJWURCK.js → list-workflows-IMSX7SXU.js} +5 -7
- package/dist/list-workflows-ZHTX5LCL.js +33 -0
- package/dist/{rename-prompt-tag-EKWU5QF3.js → rename-prompt-tag-IQI7MHOI.js} +3 -3
- package/dist/rename-prompt-tag-PA2BRIE6.js +18 -0
- package/dist/rename-test-suite-4J7UX5S2.js +21 -0
- package/dist/rename-test-suite-NNVLMLKY.js +20 -0
- package/dist/report-issue-AS34ZMJF.js +898 -0
- package/dist/report-issue-DCZFLGDO.js +899 -0
- package/dist/rerun-run-plan-TG2V4WM4.js +22 -0
- package/dist/rerun-run-plan-ZI5OY3VA.js +21 -0
- package/dist/revoke-api-key-3J3ZA4UB.js +15 -0
- package/dist/{revoke-api-key-FTRTZMOF.js → revoke-api-key-SFMEFWJC.js} +4 -4
- package/dist/run-agent-W4VJP72Z.js +75 -0
- package/dist/run-agent-YNV6ZHW4.js +74 -0
- package/dist/run-plan-6C77GWG4.js +38 -0
- package/dist/run-plan-PAARQSI4.js +39 -0
- package/dist/run-query-KJDH544C.js +47 -0
- package/dist/run-query-T4QW2Z5O.js +46 -0
- package/dist/run-test-suite-SQJELEZX.js +26 -0
- package/dist/run-test-suite-STB6NUJM.js +25 -0
- package/dist/{run-workflow-QPXC627O.js → run-workflow-D6PQNBI6.js} +4 -4
- package/dist/{run-agent-6SM4XNA7.js → run-workflow-SP32SO5M.js} +10 -11
- package/dist/search-traces-HCBGOG6R.js +170 -0
- package/dist/search-traces-HSTQ6WTQ.js +171 -0
- package/dist/{set-model-provider-3WKT63ZN.js → set-model-provider-GF7NXUJA.js} +4 -4
- package/dist/set-model-provider-KDQ7C4EZ.js +33 -0
- package/dist/{update-agent-SMAEHLXH.js → update-agent-DWG43T2P.js} +4 -4
- package/dist/update-agent-MXNOBS4H.js +20 -0
- package/dist/update-dashboard-widget-CJKJLCDY.js +71 -0
- package/dist/update-dashboard-widget-CYQDS2NR.js +70 -0
- package/dist/update-dataset-DQH3OHOU.js +22 -0
- package/dist/{update-dataset-BDUK7TUO.js → update-dataset-SQZ6T4WX.js} +6 -6
- package/dist/{update-dataset-record-4ZSDRBSW.js → update-dataset-record-3AT4IPBC.js} +4 -4
- package/dist/update-dataset-record-7PLDGTMR.js +19 -0
- package/dist/update-evaluator-4LFTKFPT.js +24 -0
- package/dist/{update-evaluator-LS6TIBY7.js → update-evaluator-IGTX6PX4.js} +4 -4
- package/dist/{update-project-LKVYXENH.js → update-project-PLJIOZS6.js} +4 -4
- package/dist/update-project-QVHSJRUP.js +24 -0
- package/dist/update-prompt-GDKRYJI3.js +140 -0
- package/dist/update-prompt-MY263DYF.js +141 -0
- package/dist/update-scenario-7KA3JDXA.js +34 -0
- package/dist/{update-scenario-NEXCAQI7.js → update-scenario-MOHRQKAQ.js} +11 -4
- package/package.json +39 -44
- package/pyproject.toml +8 -8
- package/src/config.d.ts +4 -0
- package/src/config.ts +20 -29
- package/src/create-mcp-server.ts +1021 -596
- package/src/documentation-fetch.ts +21 -20
- package/src/http-security.ts +166 -135
- package/src/http-server.ts +189 -224
- package/src/index.ts +14 -16
- package/src/langwatch-api-agents.ts +202 -21
- package/src/langwatch-api-annotations.ts +11 -12
- package/src/langwatch-api-api-keys.ts +8 -9
- package/src/langwatch-api-boards.ts +46 -0
- package/src/langwatch-api-dashboard-widgets.ts +112 -0
- package/src/langwatch-api-dashboards.ts +13 -10
- package/src/langwatch-api-datasets.ts +98 -32
- package/src/langwatch-api-evaluators.ts +13 -19
- package/src/langwatch-api-model-providers.ts +4 -4
- package/src/langwatch-api-monitors.ts +14 -18
- package/src/langwatch-api-projects.ts +27 -9
- package/src/langwatch-api-query.ts +114 -0
- package/src/langwatch-api-run-plans.ts +140 -0
- package/src/langwatch-api-scenarios.ts +17 -10
- package/src/langwatch-api-secrets.ts +31 -24
- package/src/langwatch-api-simulation-runs.ts +10 -7
- package/src/langwatch-api-test-suites.ts +116 -0
- package/src/langwatch-api-triggers.ts +96 -34
- package/src/langwatch-api-workflows.ts +9 -9
- package/src/langwatch-api.ts +143 -109
- package/src/public-http-request.ts +21 -12
- package/src/schemas/analytics-metrics.ts +3 -15
- package/src/schemas/create-dataset.ts +3 -4
- package/src/schemas/run-plan.ts +76 -0
- package/src/schemas/suite-fields.ts +132 -0
- package/src/schemas/triggers.ts +365 -0
- package/src/tools/add-dashboard-widget.ts +63 -0
- package/src/tools/archive-project.ts +2 -4
- package/src/tools/archive-run-plan.ts +10 -0
- package/src/tools/archive-scenario.ts +2 -4
- package/src/tools/archive-test-suite.ts +10 -0
- package/src/tools/assign-prompt-tag.ts +6 -2
- package/src/tools/create-agent.ts +1 -1
- package/src/tools/create-annotation.ts +7 -2
- package/src/tools/create-api-key.ts +4 -6
- package/src/tools/create-dashboard.ts +1 -1
- package/src/tools/create-dataset-records.ts +4 -8
- package/src/tools/create-dataset.ts +5 -7
- package/src/tools/create-evaluator.ts +3 -5
- package/src/tools/create-project.ts +2 -2
- package/src/tools/create-prompt-tag.ts +2 -4
- package/src/tools/create-prompt.ts +9 -18
- package/src/tools/create-scenario.ts +11 -5
- package/src/tools/create-test-suite.ts +38 -0
- package/src/tools/create-trigger.ts +55 -0
- package/src/tools/dashboard-exists.ts +25 -0
- package/src/tools/dashboard-widget-queries.ts +32 -0
- package/src/tools/delete-agent.ts +1 -1
- package/src/tools/delete-annotation.ts +1 -1
- package/src/tools/delete-dashboard.ts +1 -1
- package/src/tools/delete-dataset-records.ts +4 -8
- package/src/tools/delete-dataset.ts +3 -7
- package/src/tools/delete-evaluator.ts +4 -6
- package/src/tools/delete-prompt-tag.ts +2 -4
- package/src/tools/delete-workflow.ts +1 -1
- package/src/tools/discover-evaluator-schema.ts +34 -25
- package/src/tools/discover-scenario-schema.ts +86 -12
- package/src/tools/discover-schema.ts +149 -27
- package/src/tools/experiment-run-status.ts +2 -8
- package/src/tools/format-dataset-mutation.ts +5 -10
- package/src/tools/format-run-plan.ts +54 -0
- package/src/tools/format-scenario.ts +12 -0
- package/src/tools/format-suite-details.ts +90 -0
- package/src/tools/get-agent.ts +54 -3
- package/src/tools/get-analytics.ts +52 -46
- package/src/tools/get-dashboard.ts +1 -1
- package/src/tools/get-dataset.ts +34 -16
- package/src/tools/get-evaluator.ts +4 -11
- package/src/tools/get-experiment-results.ts +219 -186
- package/src/tools/get-project.ts +2 -4
- package/src/tools/get-prompt.ts +100 -37
- package/src/tools/get-run-plan.ts +60 -0
- package/src/tools/get-scenario.ts +6 -5
- package/src/tools/get-simulation-run.ts +32 -20
- package/src/tools/get-test-suite.ts +46 -0
- package/src/tools/get-trace.ts +16 -17
- package/src/tools/get-workflow.ts +1 -1
- package/src/tools/list-agents.ts +20 -3
- package/src/tools/list-annotations.ts +8 -5
- package/src/tools/list-api-keys.ts +7 -2
- package/src/tools/list-dashboard-boards.ts +28 -0
- package/src/tools/list-dashboards.ts +1 -1
- package/src/tools/list-dataset-records.ts +8 -6
- package/src/tools/list-datasets.ts +12 -23
- package/src/tools/list-evaluators.ts +4 -8
- package/src/tools/list-experiment-runs.ts +5 -9
- package/src/tools/list-experiments.ts +5 -11
- package/src/tools/list-model-providers.ts +3 -5
- package/src/tools/list-projects.ts +2 -4
- package/src/tools/list-prompt-tags.ts +3 -3
- package/src/tools/list-prompts.ts +4 -8
- package/src/tools/list-run-plans.ts +47 -0
- package/src/tools/list-scenarios.ts +26 -17
- package/src/tools/list-simulation-runs.ts +13 -5
- package/src/tools/list-test-suites.ts +35 -0
- package/src/tools/list-trigger-fires.ts +17 -0
- package/src/tools/list-triggers.ts +39 -0
- package/src/tools/list-workflows.ts +2 -4
- package/src/tools/rename-prompt-tag.ts +1 -1
- package/src/tools/rename-test-suite.ts +15 -0
- package/src/tools/report-issue.ts +5 -9
- package/src/tools/rerun-run-plan.ts +18 -0
- package/src/tools/revoke-api-key.ts +2 -4
- package/src/tools/run-agent.ts +65 -14
- package/src/tools/run-experiment.ts +36 -37
- package/src/tools/run-plan.ts +46 -0
- package/src/tools/run-query.ts +47 -0
- package/src/tools/run-test-suite.ts +27 -0
- package/src/tools/run-workflow.ts +2 -5
- package/src/tools/search-traces.ts +161 -47
- package/src/tools/set-model-provider.ts +4 -6
- package/src/tools/test-agent.ts +18 -0
- package/src/tools/update-agent.ts +1 -1
- package/src/tools/update-dashboard-widget.ts +97 -0
- package/src/tools/update-dataset-record.ts +1 -1
- package/src/tools/update-dataset.ts +5 -7
- package/src/tools/update-evaluator.ts +3 -5
- package/src/tools/update-project.ts +1 -1
- package/src/tools/update-prompt.ts +185 -15
- package/src/tools/update-scenario.ts +11 -5
- package/src/tools/update-test-suite.ts +40 -0
- package/src/utils/date-parsing.ts +1 -1
- package/src/utils/escape-markdown.ts +3 -8
- package/src/utils/format-evaluations.ts +6 -6
- package/src/utils/markdown-table.ts +58 -0
- package/src/utils/trace-id-shape.ts +20 -0
- package/tsconfig.app.json +24 -0
- package/tsconfig.json +19 -12
- package/uv.lock +416 -296
- package/vitest.integration.config.js +10 -0
- package/vitest.scenario.config.js +9 -0
- package/.eslintrc.cjs +0 -36
- package/dist/archive-suite-4T6BSRS7.js +0 -16
- package/dist/chunk-77BNWQ3U.js +0 -86
- package/dist/chunk-A2LEVYQ5.js +0 -217
- package/dist/chunk-TJCXFNDT.js +0 -24
- package/dist/chunk-YZLYHLLI.js +0 -48
- package/dist/config.d.ts +0 -17
- package/dist/create-mcp-server.d.ts +0 -10
- package/dist/create-suite-LZS6QIAF.js +0 -39
- package/dist/delete-evaluator-33SP2JFX.js +0 -16
- package/dist/discover-evaluator-schema-XTAD4X7D.js +0 -2191
- package/dist/discover-scenario-schema-FE64QK2D.js +0 -67
- package/dist/get-agent-Y7MWZ65F.js +0 -29
- package/dist/get-analytics-TETCSOK6.js +0 -73
- package/dist/get-dataset-RCMU5VAH.js +0 -49
- package/dist/get-prompt-OY5B7TRI.js +0 -46
- package/dist/get-suite-J7JVWO6O.js +0 -45
- package/dist/index.d.ts +0 -2
- package/dist/langwatch-api-secrets-BUBSNCNO.js +0 -41
- package/dist/langwatch-api-triggers-EDFUCJN6.js +0 -31
- package/dist/list-agents-KFHKGPXV.js +0 -33
- package/dist/list-scenarios-F2WYNRIE.js +0 -41
- package/dist/list-suites-CRY4VWW6.js +0 -43
- package/dist/report-issue-XYVAPAYJ.js +0 -412
- package/dist/run-suite-NKZCJ2IN.js +0 -30
- package/dist/search-traces-A6HFKPLJ.js +0 -79
- package/dist/update-prompt-ODCRQRN6.js +0 -25
- package/dist/update-suite-SNO3GS7T.js +0 -40
- package/src/__tests__/all-tools.integration.test.ts +0 -1626
- package/src/__tests__/config.unit.test.ts +0 -202
- package/src/__tests__/datasets.integration.test.ts +0 -620
- package/src/__tests__/datasets.unit.test.ts +0 -244
- package/src/__tests__/date-parsing.unit.test.ts +0 -78
- package/src/__tests__/discover-evaluator-schema.unit.test.ts +0 -89
- package/src/__tests__/discover-schema.unit.test.ts +0 -118
- package/src/__tests__/docker.integration.test.ts +0 -318
- package/src/__tests__/documentation-fetch-security.integration.test.ts +0 -182
- package/src/__tests__/evaluator-tools.unit.test.ts +0 -262
- package/src/__tests__/experiment-list-tools.unit.test.ts +0 -157
- package/src/__tests__/experiment-status.unit.test.ts +0 -194
- package/src/__tests__/feature-map-tool-drift.unit.test.ts +0 -93
- package/src/__tests__/get-experiment-results.unit.test.ts +0 -313
- package/src/__tests__/http-security-origin.unit.test.ts +0 -97
- package/src/__tests__/http-security-sessions.unit.test.ts +0 -209
- package/src/__tests__/http-security-verifier.unit.test.ts +0 -248
- package/src/__tests__/http-server-allocation.integration.test.ts +0 -279
- package/src/__tests__/http-server-origin.integration.test.ts +0 -169
- package/src/__tests__/http-server-session-auth.integration.test.ts +0 -341
- package/src/__tests__/http-transport.integration.test.ts +0 -395
- package/src/__tests__/integration.integration.test.ts +0 -288
- package/src/__tests__/langwatch-api.unit.test.ts +0 -661
- package/src/__tests__/model-provider-tools.unit.test.ts +0 -190
- package/src/__tests__/project-api-key-tools.unit.test.ts +0 -451
- package/src/__tests__/public-http-request-security.integration.test.ts +0 -118
- package/src/__tests__/report-issue.integration.test.ts +0 -147
- package/src/__tests__/scenario-tools.integration.test.ts +0 -286
- package/src/__tests__/scenario-tools.unit.test.ts +0 -188
- package/src/__tests__/schemas.unit.test.ts +0 -85
- package/src/__tests__/support/http-server-harness.ts +0 -138
- package/src/__tests__/tools.unit.test.ts +0 -924
- package/src/langwatch-api-suites.ts +0 -118
- package/src/schemas/filter-fields.ts +0 -119
- package/src/schemas/index.ts +0 -4
- package/src/tools/archive-suite.ts +0 -12
- package/src/tools/create-suite.ts +0 -42
- package/src/tools/get-suite.ts +0 -47
- package/src/tools/list-suites.ts +0 -43
- package/src/tools/run-suite.ts +0 -28
- package/src/tools/update-suite.ts +0 -44
package/src/create-mcp-server.ts
CHANGED
|
@@ -2,21 +2,43 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import packageJson from "../package.json" with { type: "json" };
|
|
5
|
-
import { requireApiKey } from "./config.
|
|
6
|
-
import { fetchDocumentation } from "./documentation-fetch.
|
|
5
|
+
import { requireApiKey } from "./config.ts";
|
|
6
|
+
import { fetchDocumentation } from "./documentation-fetch.ts";
|
|
7
|
+
import { createDatasetSchema, datasetColumnDefinitionSchema } from "./schemas/create-dataset.ts";
|
|
7
8
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
runParametersSchema,
|
|
10
|
+
runPlanScopeSchema,
|
|
11
|
+
runPlanTargetSchema,
|
|
12
|
+
} from "./schemas/run-plan.ts";
|
|
13
|
+
import {
|
|
14
|
+
evaluatorAttachmentsSchema,
|
|
15
|
+
scenarioFieldValuesSchema,
|
|
16
|
+
suiteFieldsSchema,
|
|
17
|
+
} from "./schemas/suite-fields.ts";
|
|
18
|
+
import {
|
|
19
|
+
actionParamsSchema,
|
|
20
|
+
alertTypeSchema,
|
|
21
|
+
graphAlertSchema,
|
|
22
|
+
notificationCadenceSchema,
|
|
23
|
+
reportSchema,
|
|
24
|
+
SLACK_DELIVERY_NOTE,
|
|
25
|
+
templatesSchema,
|
|
26
|
+
TRIGGER_FILTER_QUERY_DESCRIPTION,
|
|
27
|
+
TRIGGER_FILTERS_DESCRIPTION,
|
|
28
|
+
triggerActionSchema,
|
|
29
|
+
validateActionParamsForAction,
|
|
30
|
+
} from "./schemas/triggers.ts";
|
|
31
|
+
import { handleExperimentResults } from "./tools/get-experiment-results.ts";
|
|
32
|
+
import { handleExperimentListRuns } from "./tools/list-experiment-runs.ts";
|
|
33
|
+
import { handleExperimentList } from "./tools/list-experiments.ts";
|
|
34
|
+
import { handleRunExperiment, handleExperimentStatus } from "./tools/run-experiment.ts";
|
|
35
|
+
import { handleTestAgent } from "./tools/test-agent.ts";
|
|
36
|
+
import { handleUpdateTestSuite } from "./tools/update-test-suite.ts";
|
|
15
37
|
|
|
16
38
|
const modelSchema = z
|
|
17
39
|
.string()
|
|
18
40
|
.describe(
|
|
19
|
-
'Model in "provider/model-name" format, e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-5-20250929"'
|
|
41
|
+
'Model in "provider/model-name" format, e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-5-20250929"',
|
|
20
42
|
);
|
|
21
43
|
|
|
22
44
|
/**
|
|
@@ -59,6 +81,29 @@ function withToolLogging<T extends unknown[], R>(
|
|
|
59
81
|
}
|
|
60
82
|
|
|
61
83
|
function registerTools(server: McpServer): void {
|
|
84
|
+
registerDocsTools(server);
|
|
85
|
+
registerObservabilityTools(server);
|
|
86
|
+
registerPromptTools(server);
|
|
87
|
+
registerScenarioTools(server);
|
|
88
|
+
registerRunPlanTools(server);
|
|
89
|
+
registerTestSuiteTools(server);
|
|
90
|
+
registerSimulationRunTools(server);
|
|
91
|
+
registerEvaluatorTools(server);
|
|
92
|
+
registerModelProviderTools(server);
|
|
93
|
+
registerAgentTools(server);
|
|
94
|
+
registerDashboardTools(server);
|
|
95
|
+
registerWorkflowTools(server);
|
|
96
|
+
registerAnnotationTools(server);
|
|
97
|
+
registerTriggerTools(server);
|
|
98
|
+
registerMonitorTools(server);
|
|
99
|
+
registerSecretTools(server);
|
|
100
|
+
registerExperimentTools(server);
|
|
101
|
+
registerDatasetTools(server);
|
|
102
|
+
registerProjectTools(server);
|
|
103
|
+
registerApiKeyTools(server);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function registerDocsTools(server: McpServer): void {
|
|
62
107
|
server.tool(
|
|
63
108
|
"fetch_langwatch_docs",
|
|
64
109
|
"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",
|
|
@@ -67,13 +112,13 @@ function registerTools(server: McpServer): void {
|
|
|
67
112
|
.string()
|
|
68
113
|
.optional()
|
|
69
114
|
.describe(
|
|
70
|
-
"The full url of the specific doc page. If not provided, the docs index will be fetched."
|
|
115
|
+
"The full url of the specific doc page. If not provided, the docs index will be fetched.",
|
|
71
116
|
),
|
|
72
117
|
},
|
|
73
118
|
withToolLogging("fetch_langwatch_docs", async ({ url }) => {
|
|
74
119
|
const text = await fetchDocumentation("langwatch", url);
|
|
75
120
|
return { content: [{ type: "text", text }] };
|
|
76
|
-
})
|
|
121
|
+
}),
|
|
77
122
|
);
|
|
78
123
|
|
|
79
124
|
server.tool(
|
|
@@ -83,36 +128,33 @@ function registerTools(server: McpServer): void {
|
|
|
83
128
|
user_approved: z
|
|
84
129
|
.boolean()
|
|
85
130
|
.describe(
|
|
86
|
-
"Must be true, and only after the user explicitly agreed to send this report to LangWatch"
|
|
131
|
+
"Must be true, and only after the user explicitly agreed to send this report to LangWatch",
|
|
87
132
|
),
|
|
88
133
|
title: z.string().max(300).describe("One-line description of the issue"),
|
|
89
134
|
summary: z
|
|
90
135
|
.string()
|
|
91
136
|
.optional()
|
|
92
137
|
.describe(
|
|
93
|
-
"What you were trying to do, what went wrong (verbatim errors), and what you had to figure out the hard way"
|
|
138
|
+
"What you were trying to do, what went wrong (verbatim errors), and what you had to figure out the hard way",
|
|
94
139
|
),
|
|
95
140
|
session_content: z
|
|
96
141
|
.string()
|
|
97
142
|
.optional()
|
|
98
143
|
.describe(
|
|
99
|
-
"Optional raw session transcript or log excerpt (JSONL or plain text); redacted locally before sending"
|
|
144
|
+
"Optional raw session transcript or log excerpt (JSONL or plain text); redacted locally before sending",
|
|
100
145
|
),
|
|
101
|
-
contact_email: z
|
|
102
|
-
.string()
|
|
103
|
-
.optional()
|
|
104
|
-
.describe("Optional contact email for follow-up"),
|
|
146
|
+
contact_email: z.string().optional().describe("Optional contact email for follow-up"),
|
|
105
147
|
agent: z
|
|
106
148
|
.string()
|
|
107
149
|
.optional()
|
|
108
150
|
.describe("Which coding agent this is, e.g. claude-code, codex, cursor"),
|
|
109
151
|
},
|
|
110
152
|
withToolLogging("report_issue", async (params) => {
|
|
111
|
-
const { handleReportIssue } = await import("./tools/report-issue.
|
|
153
|
+
const { handleReportIssue } = await import("./tools/report-issue.ts");
|
|
112
154
|
return {
|
|
113
155
|
content: [{ type: "text", text: await handleReportIssue(params) }],
|
|
114
156
|
};
|
|
115
|
-
})
|
|
157
|
+
}),
|
|
116
158
|
);
|
|
117
159
|
|
|
118
160
|
server.tool(
|
|
@@ -123,24 +165,26 @@ function registerTools(server: McpServer): void {
|
|
|
123
165
|
.string()
|
|
124
166
|
.optional()
|
|
125
167
|
.describe(
|
|
126
|
-
"The full url of the specific doc page. If not provided, the docs index will be fetched."
|
|
168
|
+
"The full url of the specific doc page. If not provided, the docs index will be fetched.",
|
|
127
169
|
),
|
|
128
170
|
},
|
|
129
171
|
withToolLogging("fetch_scenario_docs", async ({ url }) => {
|
|
130
172
|
const text = await fetchDocumentation("scenario", url);
|
|
131
173
|
return { content: [{ type: "text", text }] };
|
|
132
|
-
})
|
|
174
|
+
}),
|
|
133
175
|
);
|
|
176
|
+
}
|
|
134
177
|
|
|
135
|
-
|
|
136
|
-
|
|
178
|
+
// --- Observability Tools (require API key) ---
|
|
179
|
+
function registerObservabilityTools(server: McpServer): void {
|
|
137
180
|
server.tool(
|
|
138
181
|
"discover_schema",
|
|
139
|
-
"Discover
|
|
182
|
+
"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.",
|
|
140
183
|
{
|
|
141
184
|
category: z
|
|
142
185
|
.enum([
|
|
143
186
|
"filters",
|
|
187
|
+
"lwql",
|
|
144
188
|
"metrics",
|
|
145
189
|
"aggregations",
|
|
146
190
|
"groups",
|
|
@@ -148,113 +192,149 @@ function registerTools(server: McpServer): void {
|
|
|
148
192
|
"evaluators",
|
|
149
193
|
"all",
|
|
150
194
|
])
|
|
151
|
-
.describe(
|
|
195
|
+
.describe(
|
|
196
|
+
"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",
|
|
197
|
+
),
|
|
152
198
|
evaluatorType: z
|
|
153
199
|
.string()
|
|
154
200
|
.optional()
|
|
155
201
|
.describe(
|
|
156
|
-
"When category is 'evaluators', provide a specific evaluator type (e.g. 'langevals/llm_boolean') to get its full schema details"
|
|
202
|
+
"When category is 'evaluators', provide a specific evaluator type (e.g. 'langevals/llm_boolean') to get its full schema details",
|
|
157
203
|
),
|
|
158
204
|
},
|
|
159
|
-
async ({ category, evaluatorType }) => {
|
|
205
|
+
withToolLogging("discover_schema", async ({ category, evaluatorType }) => {
|
|
160
206
|
if (category === "scenarios") {
|
|
161
|
-
const { formatScenarioSchema } = await import(
|
|
162
|
-
"./tools/discover-scenario-schema.js"
|
|
163
|
-
);
|
|
207
|
+
const { formatScenarioSchema } = await import("./tools/discover-scenario-schema.ts");
|
|
164
208
|
return {
|
|
165
209
|
content: [{ type: "text", text: formatScenarioSchema() }],
|
|
166
210
|
};
|
|
167
211
|
}
|
|
168
212
|
if (category === "evaluators") {
|
|
169
|
-
const { formatEvaluatorSchema } = await import(
|
|
170
|
-
"./tools/discover-evaluator-schema.js"
|
|
171
|
-
);
|
|
213
|
+
const { formatEvaluatorSchema } = await import("./tools/discover-evaluator-schema.ts");
|
|
172
214
|
return {
|
|
173
|
-
content: [
|
|
174
|
-
{ type: "text", text: formatEvaluatorSchema(evaluatorType) },
|
|
175
|
-
],
|
|
215
|
+
content: [{ type: "text", text: formatEvaluatorSchema(evaluatorType) }],
|
|
176
216
|
};
|
|
177
217
|
}
|
|
178
|
-
const { formatSchema } = await import("./tools/discover-schema.
|
|
179
|
-
|
|
218
|
+
const { formatSchema, needsQueryReference } = await import("./tools/discover-schema.ts");
|
|
219
|
+
// The filter and analytics-SQL halves are the PLATFORM's registries, not
|
|
220
|
+
// copies of them, so those categories need the credential. The static
|
|
221
|
+
// ones still answer without it.
|
|
222
|
+
if (needsQueryReference(category)) requireApiKey();
|
|
223
|
+
let text = await formatSchema(category);
|
|
180
224
|
if (category === "all") {
|
|
181
|
-
const { formatScenarioSchema } = await import(
|
|
182
|
-
"./tools/discover-scenario-schema.js"
|
|
183
|
-
);
|
|
225
|
+
const { formatScenarioSchema } = await import("./tools/discover-scenario-schema.ts");
|
|
184
226
|
text += "\n\n" + formatScenarioSchema();
|
|
185
|
-
const { formatEvaluatorSchema } = await import(
|
|
186
|
-
"./tools/discover-evaluator-schema.js"
|
|
187
|
-
);
|
|
227
|
+
const { formatEvaluatorSchema } = await import("./tools/discover-evaluator-schema.ts");
|
|
188
228
|
text += "\n\n" + formatEvaluatorSchema();
|
|
189
229
|
}
|
|
190
230
|
return { content: [{ type: "text", text }] };
|
|
191
|
-
}
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
server.tool(
|
|
235
|
+
"run_query",
|
|
236
|
+
"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 — 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.",
|
|
237
|
+
{
|
|
238
|
+
sql: z
|
|
239
|
+
.string()
|
|
240
|
+
.describe(
|
|
241
|
+
"The SELECT to run. Filter on the dataset's time column so the read is bounded, and never select a whole attribute map",
|
|
242
|
+
),
|
|
243
|
+
parameters: z
|
|
244
|
+
.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()]))
|
|
245
|
+
.optional()
|
|
246
|
+
.describe(
|
|
247
|
+
'Values for the parameters the statement declares, e.g. {"days": 7} for {days:UInt32}',
|
|
248
|
+
),
|
|
249
|
+
},
|
|
250
|
+
withToolLogging("run_query", async (params) => {
|
|
251
|
+
requireApiKey();
|
|
252
|
+
const { handleRunQuery } = await import("./tools/run-query.ts");
|
|
253
|
+
return {
|
|
254
|
+
content: [{ type: "text", text: await handleRunQuery(params) }],
|
|
255
|
+
};
|
|
256
|
+
}),
|
|
192
257
|
);
|
|
193
258
|
|
|
194
259
|
server.tool(
|
|
195
260
|
"search_traces",
|
|
196
|
-
"Search LangWatch traces
|
|
261
|
+
"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 — free text does not match trace IDs.",
|
|
197
262
|
{
|
|
198
|
-
query: z
|
|
263
|
+
query: z
|
|
264
|
+
.string()
|
|
265
|
+
.optional()
|
|
266
|
+
.describe(
|
|
267
|
+
"Text search query. Matches captured input/output, the trace name and span names ONLY. It does not match trace IDs — to fetch a known trace ID use get_trace, or pass traceIds here for several at once.",
|
|
268
|
+
),
|
|
269
|
+
traceIds: z
|
|
270
|
+
.array(z.string().min(1))
|
|
271
|
+
.min(1)
|
|
272
|
+
.max(1000)
|
|
273
|
+
.optional()
|
|
274
|
+
.describe(
|
|
275
|
+
"Exact trace IDs to fetch (1–1000). 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.",
|
|
276
|
+
),
|
|
199
277
|
filters: z
|
|
200
278
|
.record(z.string(), z.array(z.string()))
|
|
201
279
|
.optional()
|
|
202
280
|
.describe(
|
|
203
|
-
'Filter traces. Format: {"field": ["value"]}. Use discover_schema for field names.'
|
|
281
|
+
'Filter traces by the older per-field map. Format: {"field": ["value"]}. Use discover_schema for field names.',
|
|
204
282
|
),
|
|
205
|
-
|
|
283
|
+
filter: z
|
|
206
284
|
.string()
|
|
207
285
|
.optional()
|
|
208
286
|
.describe(
|
|
209
|
-
'
|
|
287
|
+
'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.',
|
|
210
288
|
),
|
|
211
|
-
|
|
289
|
+
startDate: z
|
|
212
290
|
.string()
|
|
291
|
+
.min(1)
|
|
213
292
|
.optional()
|
|
214
|
-
.describe(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
.describe("Results per page (default: 25, max: 1000)"),
|
|
219
|
-
scrollId: z
|
|
293
|
+
.describe(
|
|
294
|
+
'Start of the window: ISO date, or relative like "24h", "7d", "4w", "3m" — 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.',
|
|
295
|
+
),
|
|
296
|
+
endDate: z
|
|
220
297
|
.string()
|
|
298
|
+
.min(1)
|
|
221
299
|
.optional()
|
|
222
|
-
.describe("
|
|
300
|
+
.describe("End of the window: ISO date or relative. Default: now"),
|
|
301
|
+
pageSize: z.number().optional().describe("Results per page (default: 25, max: 1000)"),
|
|
302
|
+
scrollId: z.string().optional().describe("Pagination token from previous search"),
|
|
223
303
|
format: z
|
|
224
304
|
.enum(["digest", "json"])
|
|
225
305
|
.optional()
|
|
226
|
-
.describe(
|
|
227
|
-
"Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"
|
|
228
|
-
),
|
|
306
|
+
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
229
307
|
},
|
|
230
308
|
withToolLogging("search_traces", async (params) => {
|
|
231
309
|
requireApiKey();
|
|
232
|
-
const { handleSearchTraces } = await import("./tools/search-traces.
|
|
310
|
+
const { handleSearchTraces } = await import("./tools/search-traces.ts");
|
|
233
311
|
return {
|
|
234
312
|
content: [{ type: "text", text: await handleSearchTraces(params) }],
|
|
235
313
|
};
|
|
236
|
-
})
|
|
314
|
+
}),
|
|
237
315
|
);
|
|
238
316
|
|
|
239
317
|
server.tool(
|
|
240
318
|
"get_trace",
|
|
241
|
-
"Get full details of a single trace by ID. Returns AI-readable trace digest by default. Use format: 'json' for full raw data including all spans.",
|
|
319
|
+
"Get full details of a single trace by ID. This is the right tool whenever you have a trace ID — search_traces does not match IDs. A full ID resolves at any age; a unique 8–31 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.",
|
|
242
320
|
{
|
|
243
|
-
traceId: z
|
|
321
|
+
traceId: z
|
|
322
|
+
.string()
|
|
323
|
+
.describe(
|
|
324
|
+
"The trace ID to retrieve. A full ID resolves at any age; a unique 8–31 character hex prefix also resolves, git-style, within the last 90 days.",
|
|
325
|
+
),
|
|
244
326
|
format: z
|
|
245
327
|
.enum(["digest", "json"])
|
|
246
328
|
.optional()
|
|
247
|
-
.describe(
|
|
248
|
-
"Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"
|
|
249
|
-
),
|
|
329
|
+
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
250
330
|
},
|
|
251
331
|
withToolLogging("get_trace", async (params) => {
|
|
252
332
|
requireApiKey();
|
|
253
|
-
const { handleGetTrace } = await import("./tools/get-trace.
|
|
333
|
+
const { handleGetTrace } = await import("./tools/get-trace.ts");
|
|
254
334
|
return {
|
|
255
335
|
content: [{ type: "text", text: await handleGetTrace(params) }],
|
|
256
336
|
};
|
|
257
|
-
})
|
|
337
|
+
}),
|
|
258
338
|
);
|
|
259
339
|
|
|
260
340
|
server.tool(
|
|
@@ -264,44 +344,40 @@ function registerTools(server: McpServer): void {
|
|
|
264
344
|
metric: z
|
|
265
345
|
.string()
|
|
266
346
|
.describe(
|
|
267
|
-
'Metric in "category.name" format, e.g., "metadata.trace_id", "performance.total_cost"'
|
|
347
|
+
'Metric in "category.name" format, e.g., "metadata.trace_id", "performance.total_cost"',
|
|
268
348
|
),
|
|
269
349
|
aggregation: z
|
|
270
350
|
.string()
|
|
271
351
|
.optional()
|
|
272
352
|
.describe(
|
|
273
|
-
"Aggregation type: avg, sum, min, max, median, p90, p95, p99, cardinality, terms. Default: avg"
|
|
353
|
+
"Aggregation type: avg, sum, min, max, median, p90, p95, p99, cardinality, terms. Default: avg",
|
|
274
354
|
),
|
|
275
355
|
startDate: z
|
|
276
356
|
.string()
|
|
277
357
|
.optional()
|
|
278
|
-
.describe(
|
|
279
|
-
'Start date: ISO or relative ("7d", "30d"). Default: 7 days ago'
|
|
280
|
-
),
|
|
358
|
+
.describe('Start date: ISO or relative ("7d", "30d"). Default: 7 days ago'),
|
|
281
359
|
endDate: z.string().optional().describe("End date. Default: now"),
|
|
282
360
|
timeZone: z.string().optional().describe("Timezone. Default: UTC"),
|
|
283
361
|
groupBy: z
|
|
284
362
|
.string()
|
|
285
363
|
.optional()
|
|
286
364
|
.describe("Group results by field. Use discover_schema for options."),
|
|
287
|
-
filters: z
|
|
288
|
-
.record(z.string(), z.array(z.string()))
|
|
289
|
-
.optional()
|
|
290
|
-
.describe("Filters to apply"),
|
|
365
|
+
filters: z.record(z.string(), z.array(z.string())).optional().describe("Filters to apply"),
|
|
291
366
|
},
|
|
292
367
|
withToolLogging("get_analytics", async (params) => {
|
|
293
368
|
requireApiKey();
|
|
294
|
-
const { handleGetAnalytics } = await import("./tools/get-analytics.
|
|
369
|
+
const { handleGetAnalytics } = await import("./tools/get-analytics.ts");
|
|
295
370
|
return {
|
|
296
371
|
content: [{ type: "text", text: await handleGetAnalytics(params) }],
|
|
297
372
|
};
|
|
298
|
-
})
|
|
373
|
+
}),
|
|
299
374
|
);
|
|
375
|
+
}
|
|
300
376
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
377
|
+
// --- Platform Prompt Tools (require API key) ---
|
|
378
|
+
// These tools manage prompts on the LangWatch platform via API.
|
|
379
|
+
// For code-based prompt management, see `fetch_langwatch_docs` for the CLI/SDK approach.
|
|
380
|
+
function registerPromptTools(server: McpServer): void {
|
|
305
381
|
server.tool(
|
|
306
382
|
"platform_create_prompt",
|
|
307
383
|
`Create a new prompt on the LangWatch platform.
|
|
@@ -317,32 +393,31 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
|
|
|
317
393
|
handle: z
|
|
318
394
|
.string()
|
|
319
395
|
.optional()
|
|
320
|
-
.describe(
|
|
321
|
-
"URL-friendly handle (auto-generated from name if omitted)"
|
|
322
|
-
),
|
|
396
|
+
.describe("URL-friendly handle (auto-generated from name if omitted)"),
|
|
323
397
|
messages: z
|
|
324
398
|
.array(
|
|
325
399
|
z.object({
|
|
326
|
-
role: z
|
|
327
|
-
.enum(["system", "user", "assistant"])
|
|
328
|
-
.describe("Message role"),
|
|
400
|
+
role: z.enum(["system", "user", "assistant"]).describe("Message role"),
|
|
329
401
|
content: z.string().describe("Message content"),
|
|
330
|
-
})
|
|
402
|
+
}),
|
|
331
403
|
)
|
|
332
404
|
.describe("Prompt messages"),
|
|
333
405
|
model: modelSchema,
|
|
334
|
-
tags: z
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
406
|
+
tags: z
|
|
407
|
+
.array(z.string())
|
|
408
|
+
.optional()
|
|
409
|
+
.describe(
|
|
410
|
+
'Tags to assign to the initial version (e.g., ["production", "staging"]). ' +
|
|
411
|
+
'Built-in tags: "latest" (auto-assigned), "production", "staging". Custom tags must be created first.',
|
|
412
|
+
),
|
|
338
413
|
},
|
|
339
414
|
withToolLogging("platform_create_prompt", async (params) => {
|
|
340
415
|
requireApiKey();
|
|
341
|
-
const { handleCreatePrompt } = await import("./tools/create-prompt.
|
|
416
|
+
const { handleCreatePrompt } = await import("./tools/create-prompt.ts");
|
|
342
417
|
return {
|
|
343
418
|
content: [{ type: "text", text: await handleCreatePrompt(params) }],
|
|
344
419
|
};
|
|
345
|
-
})
|
|
420
|
+
}),
|
|
346
421
|
);
|
|
347
422
|
|
|
348
423
|
server.tool(
|
|
@@ -351,26 +426,30 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
|
|
|
351
426
|
{},
|
|
352
427
|
withToolLogging("platform_list_prompts", async () => {
|
|
353
428
|
requireApiKey();
|
|
354
|
-
const { handleListPrompts } = await import("./tools/list-prompts.
|
|
429
|
+
const { handleListPrompts } = await import("./tools/list-prompts.ts");
|
|
355
430
|
return {
|
|
356
431
|
content: [{ type: "text", text: await handleListPrompts() }],
|
|
357
432
|
};
|
|
358
|
-
})
|
|
433
|
+
}),
|
|
359
434
|
);
|
|
360
435
|
|
|
361
436
|
server.tool(
|
|
362
437
|
"platform_get_prompt",
|
|
363
|
-
"Get a specific prompt from the LangWatch platform by ID or handle, including messages, model config, and version history.",
|
|
438
|
+
"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.",
|
|
364
439
|
{
|
|
365
440
|
idOrHandle: z.string().describe("Prompt ID or handle"),
|
|
366
|
-
version: z
|
|
367
|
-
|
|
441
|
+
version: z.number().optional().describe("Specific version number (default: latest)"),
|
|
442
|
+
tag: z
|
|
443
|
+
.string()
|
|
368
444
|
.optional()
|
|
369
|
-
.describe(
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
445
|
+
.describe(
|
|
446
|
+
'Fetch the version pointed to by this tag (e.g., "production", "staging"). ' +
|
|
447
|
+
'Alternatively, use shorthand in idOrHandle: "pizza-prompt:production"',
|
|
448
|
+
),
|
|
449
|
+
format: z
|
|
450
|
+
.enum(["digest", "json"])
|
|
451
|
+
.optional()
|
|
452
|
+
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
374
453
|
},
|
|
375
454
|
withToolLogging("platform_get_prompt", async (params) => {
|
|
376
455
|
if (params.version != null && params.tag) {
|
|
@@ -380,11 +459,11 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
|
|
|
380
459
|
};
|
|
381
460
|
}
|
|
382
461
|
requireApiKey();
|
|
383
|
-
const { handleGetPrompt } = await import("./tools/get-prompt.
|
|
462
|
+
const { handleGetPrompt } = await import("./tools/get-prompt.ts");
|
|
384
463
|
return {
|
|
385
464
|
content: [{ type: "text", text: await handleGetPrompt(params) }],
|
|
386
465
|
};
|
|
387
|
-
})
|
|
466
|
+
}),
|
|
388
467
|
);
|
|
389
468
|
|
|
390
469
|
server.tool(
|
|
@@ -397,31 +476,30 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
|
|
|
397
476
|
z.object({
|
|
398
477
|
role: z.enum(["system", "user", "assistant"]),
|
|
399
478
|
content: z.string(),
|
|
400
|
-
})
|
|
479
|
+
}),
|
|
401
480
|
)
|
|
402
481
|
.optional()
|
|
403
482
|
.describe("Updated messages"),
|
|
404
483
|
model: modelSchema.optional(),
|
|
405
|
-
commitMessage: z
|
|
406
|
-
|
|
407
|
-
.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
),
|
|
484
|
+
commitMessage: z.string().describe("Commit message describing the change"),
|
|
485
|
+
tags: z
|
|
486
|
+
.array(z.string())
|
|
487
|
+
.optional()
|
|
488
|
+
.describe("Tags to assign to the new version created by this update."),
|
|
411
489
|
},
|
|
412
490
|
withToolLogging("platform_update_prompt", async (params) => {
|
|
413
491
|
requireApiKey();
|
|
414
|
-
const { handleUpdatePrompt } = await import("./tools/update-prompt.
|
|
492
|
+
const { handleUpdatePrompt } = await import("./tools/update-prompt.ts");
|
|
415
493
|
return {
|
|
416
494
|
content: [{ type: "text", text: await handleUpdatePrompt(params) }],
|
|
417
495
|
};
|
|
418
|
-
})
|
|
496
|
+
}),
|
|
419
497
|
);
|
|
420
498
|
|
|
421
499
|
server.tool(
|
|
422
500
|
"platform_assign_prompt_tag",
|
|
423
501
|
'Assign a tag (e.g. "production") to a specific version of a prompt. ' +
|
|
424
|
-
|
|
502
|
+
'Use this to "deploy" a version by promoting it to the production tag.',
|
|
425
503
|
{
|
|
426
504
|
idOrHandle: z.string().describe("Prompt ID or handle"),
|
|
427
505
|
tag: z.string().describe('Tag name (e.g., "production", "staging")'),
|
|
@@ -429,41 +507,41 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
|
|
|
429
507
|
},
|
|
430
508
|
withToolLogging("platform_assign_prompt_tag", async (params) => {
|
|
431
509
|
requireApiKey();
|
|
432
|
-
const { handleAssignPromptTag } = await import("./tools/assign-prompt-tag.
|
|
510
|
+
const { handleAssignPromptTag } = await import("./tools/assign-prompt-tag.ts");
|
|
433
511
|
return {
|
|
434
512
|
content: [{ type: "text", text: await handleAssignPromptTag(params) }],
|
|
435
513
|
};
|
|
436
|
-
})
|
|
514
|
+
}),
|
|
437
515
|
);
|
|
438
516
|
|
|
439
517
|
server.tool(
|
|
440
518
|
"platform_list_prompt_tags",
|
|
441
519
|
"List all prompt tag definitions for the organization. " +
|
|
442
|
-
|
|
520
|
+
"Shows built-in tags (latest, production, staging) and any custom tags.",
|
|
443
521
|
{},
|
|
444
522
|
withToolLogging("platform_list_prompt_tags", async () => {
|
|
445
523
|
requireApiKey();
|
|
446
|
-
const { handleListPromptTags } = await import("./tools/list-prompt-tags.
|
|
524
|
+
const { handleListPromptTags } = await import("./tools/list-prompt-tags.ts");
|
|
447
525
|
return {
|
|
448
526
|
content: [{ type: "text", text: await handleListPromptTags() }],
|
|
449
527
|
};
|
|
450
|
-
})
|
|
528
|
+
}),
|
|
451
529
|
);
|
|
452
530
|
|
|
453
531
|
server.tool(
|
|
454
532
|
"platform_create_prompt_tag",
|
|
455
533
|
"Create a custom prompt tag definition for the organization. " +
|
|
456
|
-
|
|
534
|
+
'Tag names must be non-numeric and not "latest".',
|
|
457
535
|
{
|
|
458
536
|
name: z.string().describe("Tag name to create"),
|
|
459
537
|
},
|
|
460
538
|
withToolLogging("platform_create_prompt_tag", async (params) => {
|
|
461
539
|
requireApiKey();
|
|
462
|
-
const { handleCreatePromptTag } = await import("./tools/create-prompt-tag.
|
|
540
|
+
const { handleCreatePromptTag } = await import("./tools/create-prompt-tag.ts");
|
|
463
541
|
return {
|
|
464
542
|
content: [{ type: "text", text: await handleCreatePromptTag(params) }],
|
|
465
543
|
};
|
|
466
|
-
})
|
|
544
|
+
}),
|
|
467
545
|
);
|
|
468
546
|
|
|
469
547
|
server.tool(
|
|
@@ -475,11 +553,11 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
|
|
|
475
553
|
},
|
|
476
554
|
withToolLogging("platform_rename_prompt_tag", async (params) => {
|
|
477
555
|
requireApiKey();
|
|
478
|
-
const { handleRenamePromptTag } = await import("./tools/rename-prompt-tag.
|
|
556
|
+
const { handleRenamePromptTag } = await import("./tools/rename-prompt-tag.ts");
|
|
479
557
|
return {
|
|
480
558
|
content: [{ type: "text", text: await handleRenamePromptTag(params) }],
|
|
481
559
|
};
|
|
482
|
-
})
|
|
560
|
+
}),
|
|
483
561
|
);
|
|
484
562
|
|
|
485
563
|
server.tool(
|
|
@@ -490,17 +568,18 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
|
|
|
490
568
|
},
|
|
491
569
|
withToolLogging("platform_delete_prompt_tag", async (params) => {
|
|
492
570
|
requireApiKey();
|
|
493
|
-
const { handleDeletePromptTag } = await import("./tools/delete-prompt-tag.
|
|
571
|
+
const { handleDeletePromptTag } = await import("./tools/delete-prompt-tag.ts");
|
|
494
572
|
return {
|
|
495
573
|
content: [{ type: "text", text: await handleDeletePromptTag(params) }],
|
|
496
574
|
};
|
|
497
|
-
})
|
|
575
|
+
}),
|
|
498
576
|
);
|
|
577
|
+
}
|
|
499
578
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
579
|
+
// --- Platform Scenario Tools (require API key) ---
|
|
580
|
+
// These tools manage scenarios on the LangWatch platform via API.
|
|
581
|
+
// For code-based scenario testing, see `fetch_scenario_docs` for the SDK approach.
|
|
582
|
+
function registerScenarioTools(server: McpServer): void {
|
|
504
583
|
server.tool(
|
|
505
584
|
"platform_create_scenario",
|
|
506
585
|
`Create a new scenario on the LangWatch platform. Call discover_schema({ category: 'scenarios' }) first to learn how to write effective situations and criteria.
|
|
@@ -515,55 +594,49 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
515
594
|
name: z.string().describe("Scenario name"),
|
|
516
595
|
situation: z
|
|
517
596
|
.string()
|
|
518
|
-
.describe(
|
|
519
|
-
"The context or setup describing what the user/agent is doing"
|
|
520
|
-
),
|
|
597
|
+
.describe("The context or setup describing what the user/agent is doing"),
|
|
521
598
|
criteria: z
|
|
522
599
|
.array(z.string())
|
|
523
600
|
.optional()
|
|
524
|
-
.describe(
|
|
525
|
-
"Pass/fail conditions the agent's response must satisfy"
|
|
526
|
-
),
|
|
601
|
+
.describe("Pass/fail conditions the agent's response must satisfy"),
|
|
527
602
|
labels: z
|
|
528
603
|
.array(z.string())
|
|
529
604
|
.optional()
|
|
530
605
|
.describe("Tags for organizing and filtering scenarios"),
|
|
606
|
+
testSuiteId: z
|
|
607
|
+
.string()
|
|
608
|
+
.nullish()
|
|
609
|
+
.describe(
|
|
610
|
+
"The test suite to file this scenario in. Pass a test suite ID, or null to unfile it.",
|
|
611
|
+
),
|
|
612
|
+
fields: scenarioFieldValuesSchema.optional(),
|
|
531
613
|
},
|
|
532
614
|
withToolLogging("platform_create_scenario", async (params) => {
|
|
533
615
|
requireApiKey();
|
|
534
|
-
const { handleCreateScenario } = await import(
|
|
535
|
-
"./tools/create-scenario.js"
|
|
536
|
-
);
|
|
616
|
+
const { handleCreateScenario } = await import("./tools/create-scenario.ts");
|
|
537
617
|
return {
|
|
538
|
-
content: [
|
|
539
|
-
{ type: "text", text: await handleCreateScenario(params) },
|
|
540
|
-
],
|
|
618
|
+
content: [{ type: "text", text: await handleCreateScenario(params) }],
|
|
541
619
|
};
|
|
542
|
-
})
|
|
620
|
+
}),
|
|
543
621
|
);
|
|
544
622
|
|
|
545
623
|
server.tool(
|
|
546
624
|
"platform_list_scenarios",
|
|
547
625
|
"List all scenarios on the LangWatch platform. Returns AI-readable digest by default.",
|
|
548
626
|
{
|
|
627
|
+
testSuiteId: z.string().optional().describe("Only the scenarios filed in this test suite"),
|
|
549
628
|
format: z
|
|
550
629
|
.enum(["digest", "json"])
|
|
551
630
|
.optional()
|
|
552
|
-
.describe(
|
|
553
|
-
"Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"
|
|
554
|
-
),
|
|
631
|
+
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
555
632
|
},
|
|
556
633
|
withToolLogging("platform_list_scenarios", async (params) => {
|
|
557
634
|
requireApiKey();
|
|
558
|
-
const { handleListScenarios } = await import(
|
|
559
|
-
"./tools/list-scenarios.js"
|
|
560
|
-
);
|
|
635
|
+
const { handleListScenarios } = await import("./tools/list-scenarios.ts");
|
|
561
636
|
return {
|
|
562
|
-
content: [
|
|
563
|
-
{ type: "text", text: await handleListScenarios(params) },
|
|
564
|
-
],
|
|
637
|
+
content: [{ type: "text", text: await handleListScenarios(params) }],
|
|
565
638
|
};
|
|
566
|
-
})
|
|
639
|
+
}),
|
|
567
640
|
);
|
|
568
641
|
|
|
569
642
|
server.tool(
|
|
@@ -574,17 +647,15 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
574
647
|
format: z
|
|
575
648
|
.enum(["digest", "json"])
|
|
576
649
|
.optional()
|
|
577
|
-
.describe(
|
|
578
|
-
"Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"
|
|
579
|
-
),
|
|
650
|
+
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
580
651
|
},
|
|
581
652
|
withToolLogging("platform_get_scenario", async (params) => {
|
|
582
653
|
requireApiKey();
|
|
583
|
-
const { handleGetScenario } = await import("./tools/get-scenario.
|
|
654
|
+
const { handleGetScenario } = await import("./tools/get-scenario.ts");
|
|
584
655
|
return {
|
|
585
656
|
content: [{ type: "text", text: await handleGetScenario(params) }],
|
|
586
657
|
};
|
|
587
|
-
})
|
|
658
|
+
}),
|
|
588
659
|
);
|
|
589
660
|
|
|
590
661
|
server.tool(
|
|
@@ -594,26 +665,23 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
594
665
|
scenarioId: z.string().describe("The scenario ID to update"),
|
|
595
666
|
name: z.string().optional().describe("Updated scenario name"),
|
|
596
667
|
situation: z.string().optional().describe("Updated situation"),
|
|
597
|
-
criteria: z
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
.
|
|
601
|
-
|
|
602
|
-
.
|
|
603
|
-
|
|
604
|
-
|
|
668
|
+
criteria: z.array(z.string()).optional().describe("Updated criteria"),
|
|
669
|
+
labels: z.array(z.string()).optional().describe("Updated labels"),
|
|
670
|
+
testSuiteId: z
|
|
671
|
+
.string()
|
|
672
|
+
.nullish()
|
|
673
|
+
.describe(
|
|
674
|
+
"The test suite to file this scenario in. Pass a test suite ID, or null to unfile it.",
|
|
675
|
+
),
|
|
676
|
+
fields: scenarioFieldValuesSchema.optional(),
|
|
605
677
|
},
|
|
606
678
|
withToolLogging("platform_update_scenario", async (params) => {
|
|
607
679
|
requireApiKey();
|
|
608
|
-
const { handleUpdateScenario } = await import(
|
|
609
|
-
"./tools/update-scenario.js"
|
|
610
|
-
);
|
|
680
|
+
const { handleUpdateScenario } = await import("./tools/update-scenario.ts");
|
|
611
681
|
return {
|
|
612
|
-
content: [
|
|
613
|
-
{ type: "text", text: await handleUpdateScenario(params) },
|
|
614
|
-
],
|
|
682
|
+
content: [{ type: "text", text: await handleUpdateScenario(params) }],
|
|
615
683
|
};
|
|
616
|
-
})
|
|
684
|
+
}),
|
|
617
685
|
);
|
|
618
686
|
|
|
619
687
|
server.tool(
|
|
@@ -624,131 +692,333 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
624
692
|
},
|
|
625
693
|
withToolLogging("platform_archive_scenario", async (params) => {
|
|
626
694
|
requireApiKey();
|
|
627
|
-
const { handleArchiveScenario } = await import(
|
|
628
|
-
"./tools/archive-scenario.js"
|
|
629
|
-
);
|
|
695
|
+
const { handleArchiveScenario } = await import("./tools/archive-scenario.ts");
|
|
630
696
|
return {
|
|
631
|
-
content: [
|
|
632
|
-
{ type: "text", text: await handleArchiveScenario(params) },
|
|
633
|
-
],
|
|
697
|
+
content: [{ type: "text", text: await handleArchiveScenario(params) }],
|
|
634
698
|
};
|
|
635
|
-
})
|
|
699
|
+
}),
|
|
636
700
|
);
|
|
701
|
+
}
|
|
637
702
|
|
|
638
|
-
|
|
639
|
-
|
|
703
|
+
// --- Platform Run Plan Tools (require API key) ---
|
|
704
|
+
// A run plan is what you run, and its NAME identifies it: running a name
|
|
705
|
+
// that exists replaces that plan's configuration, running a new name
|
|
706
|
+
// creates the plan. A test suite groups scenarios; running one is
|
|
707
|
+
// sugar that creates or joins the plan "<suite name> <target name>".
|
|
708
|
+
function registerRunPlanTools(server: McpServer): void {
|
|
709
|
+
server.tool(
|
|
710
|
+
"platform_run_plan",
|
|
711
|
+
"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.",
|
|
712
|
+
{
|
|
713
|
+
name: z
|
|
714
|
+
.string()
|
|
715
|
+
.optional()
|
|
716
|
+
.describe(
|
|
717
|
+
"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.",
|
|
718
|
+
),
|
|
719
|
+
scope: runPlanScopeSchema.describe(
|
|
720
|
+
"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.",
|
|
721
|
+
),
|
|
722
|
+
scenarioIds: z
|
|
723
|
+
.array(z.string())
|
|
724
|
+
.optional()
|
|
725
|
+
.describe("The scenarios to run. Read only when scope.mode is 'scenarios'."),
|
|
726
|
+
targets: z
|
|
727
|
+
.array(runPlanTargetSchema)
|
|
728
|
+
.describe(
|
|
729
|
+
"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.",
|
|
730
|
+
),
|
|
731
|
+
repeatCount: z
|
|
732
|
+
.number()
|
|
733
|
+
.int()
|
|
734
|
+
.min(1)
|
|
735
|
+
.max(5)
|
|
736
|
+
.optional()
|
|
737
|
+
.describe("How many times to run each scenario against each target (1 to 5, default 1)"),
|
|
738
|
+
simulatorModel: z
|
|
739
|
+
.string()
|
|
740
|
+
.optional()
|
|
741
|
+
.describe("Model that plays the user. Omit for the project default."),
|
|
742
|
+
judgeModel: z
|
|
743
|
+
.string()
|
|
744
|
+
.optional()
|
|
745
|
+
.describe("Model that judges the criteria. Omit for the project default."),
|
|
746
|
+
evaluators: evaluatorAttachmentsSchema
|
|
747
|
+
.optional()
|
|
748
|
+
.describe(
|
|
749
|
+
"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.",
|
|
750
|
+
),
|
|
751
|
+
parameters: runParametersSchema
|
|
752
|
+
.optional()
|
|
753
|
+
.describe(
|
|
754
|
+
"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.",
|
|
755
|
+
),
|
|
756
|
+
note: z
|
|
757
|
+
.string()
|
|
758
|
+
.max(200)
|
|
759
|
+
.optional()
|
|
760
|
+
.describe("One short line saying why this run was started (max 200 characters)"),
|
|
761
|
+
idempotencyKey: z
|
|
762
|
+
.string()
|
|
763
|
+
.optional()
|
|
764
|
+
.describe(
|
|
765
|
+
"Send the same key to make a retry join the run it already started instead of starting a second one.",
|
|
766
|
+
),
|
|
767
|
+
},
|
|
768
|
+
withToolLogging("platform_run_plan", async (params) => {
|
|
769
|
+
requireApiKey();
|
|
770
|
+
const { handleRunPlan } = await import("./tools/run-plan.ts");
|
|
771
|
+
return {
|
|
772
|
+
content: [{ type: "text", text: await handleRunPlan(params) }],
|
|
773
|
+
};
|
|
774
|
+
}),
|
|
775
|
+
);
|
|
640
776
|
|
|
641
777
|
server.tool(
|
|
642
|
-
"
|
|
643
|
-
"List
|
|
778
|
+
"platform_list_run_plans",
|
|
779
|
+
"List the run plans of the project. A run plan holds the configuration a run used, keyed by its name.",
|
|
644
780
|
{
|
|
781
|
+
includeArchived: z.boolean().optional().describe("Include archived plans (default false)"),
|
|
645
782
|
format: z
|
|
646
783
|
.enum(["digest", "json"])
|
|
647
784
|
.optional()
|
|
648
785
|
.describe("Output format: 'digest' (default) or 'json' (raw data)"),
|
|
649
786
|
},
|
|
650
|
-
withToolLogging("
|
|
787
|
+
withToolLogging("platform_list_run_plans", async (params) => {
|
|
651
788
|
requireApiKey();
|
|
652
|
-
const {
|
|
789
|
+
const { handleListRunPlans } = await import("./tools/list-run-plans.ts");
|
|
653
790
|
return {
|
|
654
|
-
content: [{ type: "text", text: await
|
|
791
|
+
content: [{ type: "text", text: await handleListRunPlans(params) }],
|
|
655
792
|
};
|
|
656
|
-
})
|
|
793
|
+
}),
|
|
657
794
|
);
|
|
658
795
|
|
|
659
796
|
server.tool(
|
|
660
|
-
"
|
|
661
|
-
"Get
|
|
797
|
+
"platform_get_run_plan",
|
|
798
|
+
"Get the full configuration of a run plan: what it covers, its targets, its repeat count and its models.",
|
|
662
799
|
{
|
|
663
|
-
id: z.string().describe("The
|
|
800
|
+
id: z.string().describe("The run plan ID"),
|
|
664
801
|
format: z
|
|
665
802
|
.enum(["digest", "json"])
|
|
666
803
|
.optional()
|
|
667
804
|
.describe("Output format: 'digest' (default) or 'json'"),
|
|
668
805
|
},
|
|
669
|
-
withToolLogging("
|
|
806
|
+
withToolLogging("platform_get_run_plan", async (params) => {
|
|
807
|
+
requireApiKey();
|
|
808
|
+
const { handleGetRunPlan } = await import("./tools/get-run-plan.ts");
|
|
809
|
+
return {
|
|
810
|
+
content: [{ type: "text", text: await handleGetRunPlan(params) }],
|
|
811
|
+
};
|
|
812
|
+
}),
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
server.tool(
|
|
816
|
+
"platform_rerun_run_plan",
|
|
817
|
+
"Run a plan again with the configuration it already holds. Only the parameters and the note belong to the new run.",
|
|
818
|
+
{
|
|
819
|
+
id: z.string().describe("The run plan ID to run again"),
|
|
820
|
+
parameters: runParametersSchema
|
|
821
|
+
.optional()
|
|
822
|
+
.describe("Values for the parameters the scenarios declare, by name."),
|
|
823
|
+
note: z
|
|
824
|
+
.string()
|
|
825
|
+
.max(200)
|
|
826
|
+
.optional()
|
|
827
|
+
.describe("One short line saying why this run was started (max 200 characters)"),
|
|
828
|
+
},
|
|
829
|
+
withToolLogging("platform_rerun_run_plan", async (params) => {
|
|
830
|
+
requireApiKey();
|
|
831
|
+
const { handleRerunRunPlan } = await import("./tools/rerun-run-plan.ts");
|
|
832
|
+
return {
|
|
833
|
+
content: [{ type: "text", text: await handleRerunRunPlan(params) }],
|
|
834
|
+
};
|
|
835
|
+
}),
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
server.tool(
|
|
839
|
+
"platform_archive_run_plan",
|
|
840
|
+
"Archive a run plan. Its past runs stay readable and the name is free for a new plan.",
|
|
841
|
+
{
|
|
842
|
+
id: z.string().describe("The run plan ID to archive"),
|
|
843
|
+
},
|
|
844
|
+
withToolLogging("platform_archive_run_plan", async (params) => {
|
|
845
|
+
requireApiKey();
|
|
846
|
+
const { handleArchiveRunPlan } = await import("./tools/archive-run-plan.ts");
|
|
847
|
+
return {
|
|
848
|
+
content: [{ type: "text", text: await handleArchiveRunPlan(params) }],
|
|
849
|
+
};
|
|
850
|
+
}),
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// --- Platform Test Suite Tools (require API key) ---
|
|
855
|
+
// A test suite groups scenarios: a name and the scenarios filed in it.
|
|
856
|
+
function registerTestSuiteTools(server: McpServer): void {
|
|
857
|
+
server.tool(
|
|
858
|
+
"platform_list_test_suites",
|
|
859
|
+
"List the test suites of the project. A test suite groups scenarios.",
|
|
860
|
+
{
|
|
861
|
+
format: z
|
|
862
|
+
.enum(["digest", "json"])
|
|
863
|
+
.optional()
|
|
864
|
+
.describe("Output format: 'digest' (default) or 'json' (raw data)"),
|
|
865
|
+
},
|
|
866
|
+
withToolLogging("platform_list_test_suites", async (params) => {
|
|
670
867
|
requireApiKey();
|
|
671
|
-
const {
|
|
868
|
+
const { handleListTestSuites } = await import("./tools/list-test-suites.ts");
|
|
672
869
|
return {
|
|
673
|
-
content: [{ type: "text", text: await
|
|
870
|
+
content: [{ type: "text", text: await handleListTestSuites(params) }],
|
|
674
871
|
};
|
|
675
|
-
})
|
|
872
|
+
}),
|
|
676
873
|
);
|
|
677
874
|
|
|
678
875
|
server.tool(
|
|
679
|
-
"
|
|
680
|
-
"Create a
|
|
876
|
+
"platform_create_test_suite",
|
|
877
|
+
"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.",
|
|
681
878
|
{
|
|
682
|
-
name: z.string().describe("
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
targets: z.string().describe('JSON array of target objects, e.g. [{"type":"http","referenceId":"agent_abc"}]'),
|
|
686
|
-
repeatCount: z.number().optional().describe("Number of times to repeat each scenario-target pair (default: 1)"),
|
|
687
|
-
labels: z.array(z.string()).optional().describe("Tags for organizing suites"),
|
|
879
|
+
name: z.string().describe("Test suite name"),
|
|
880
|
+
fields: suiteFieldsSchema.optional(),
|
|
881
|
+
evaluators: evaluatorAttachmentsSchema.optional(),
|
|
688
882
|
},
|
|
689
|
-
withToolLogging("
|
|
883
|
+
withToolLogging("platform_create_test_suite", async (params) => {
|
|
690
884
|
requireApiKey();
|
|
691
|
-
const {
|
|
885
|
+
const { handleCreateTestSuite } = await import("./tools/create-test-suite.ts");
|
|
692
886
|
return {
|
|
693
|
-
content: [{ type: "text", text: await
|
|
887
|
+
content: [{ type: "text", text: await handleCreateTestSuite(params) }],
|
|
694
888
|
};
|
|
695
|
-
})
|
|
889
|
+
}),
|
|
696
890
|
);
|
|
697
891
|
|
|
698
892
|
server.tool(
|
|
699
|
-
"
|
|
700
|
-
"
|
|
893
|
+
"platform_get_test_suite",
|
|
894
|
+
"Get a test suite and the scenarios filed in it.",
|
|
701
895
|
{
|
|
702
|
-
id: z.string().describe("The suite ID
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
896
|
+
id: z.string().describe("The test suite ID"),
|
|
897
|
+
format: z
|
|
898
|
+
.enum(["digest", "json"])
|
|
899
|
+
.optional()
|
|
900
|
+
.describe("Output format: 'digest' (default) or 'json'"),
|
|
901
|
+
},
|
|
902
|
+
withToolLogging("platform_get_test_suite", async (params) => {
|
|
903
|
+
requireApiKey();
|
|
904
|
+
const { handleGetTestSuite } = await import("./tools/get-test-suite.ts");
|
|
905
|
+
return {
|
|
906
|
+
content: [{ type: "text", text: await handleGetTestSuite(params) }],
|
|
907
|
+
};
|
|
908
|
+
}),
|
|
909
|
+
);
|
|
910
|
+
|
|
911
|
+
server.tool(
|
|
912
|
+
"platform_update_test_suite",
|
|
913
|
+
"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.",
|
|
914
|
+
{
|
|
915
|
+
id: z.string().describe("The test suite ID"),
|
|
916
|
+
name: z.string().optional().describe("The new name. The slug is kept."),
|
|
917
|
+
fields: suiteFieldsSchema
|
|
918
|
+
.optional()
|
|
919
|
+
.describe(
|
|
920
|
+
"The full list of fields the suite declares. It replaces the list the suite holds.",
|
|
921
|
+
),
|
|
922
|
+
evaluators: evaluatorAttachmentsSchema
|
|
923
|
+
.optional()
|
|
924
|
+
.describe(
|
|
925
|
+
"The full list of evaluators attached to the suite. It replaces the list the suite holds.",
|
|
926
|
+
),
|
|
709
927
|
},
|
|
710
|
-
withToolLogging("
|
|
928
|
+
withToolLogging("platform_update_test_suite", async (params) => {
|
|
711
929
|
requireApiKey();
|
|
712
|
-
const { handleUpdateSuite } = await import("./tools/update-suite.js");
|
|
713
930
|
return {
|
|
714
|
-
content: [{ type: "text", text: await
|
|
931
|
+
content: [{ type: "text", text: await handleUpdateTestSuite(params) }],
|
|
715
932
|
};
|
|
716
|
-
})
|
|
933
|
+
}),
|
|
717
934
|
);
|
|
718
935
|
|
|
719
936
|
server.tool(
|
|
720
|
-
"
|
|
721
|
-
"
|
|
937
|
+
"platform_rename_test_suite",
|
|
938
|
+
"Rename a test suite.",
|
|
722
939
|
{
|
|
723
|
-
id: z.string().describe("The suite ID
|
|
940
|
+
id: z.string().describe("The test suite ID"),
|
|
941
|
+
name: z.string().describe("The new name"),
|
|
724
942
|
},
|
|
725
|
-
withToolLogging("
|
|
943
|
+
withToolLogging("platform_rename_test_suite", async (params) => {
|
|
726
944
|
requireApiKey();
|
|
727
|
-
const {
|
|
945
|
+
const { handleRenameTestSuite } = await import("./tools/rename-test-suite.ts");
|
|
728
946
|
return {
|
|
729
|
-
content: [{ type: "text", text: await
|
|
947
|
+
content: [{ type: "text", text: await handleRenameTestSuite(params) }],
|
|
730
948
|
};
|
|
731
|
-
})
|
|
949
|
+
}),
|
|
732
950
|
);
|
|
733
951
|
|
|
734
952
|
server.tool(
|
|
735
|
-
"
|
|
736
|
-
"Archive
|
|
953
|
+
"platform_archive_test_suite",
|
|
954
|
+
"Archive a test suite. The scenarios filed in it are archived with it.",
|
|
737
955
|
{
|
|
738
|
-
id: z.string().describe("The suite ID to archive"),
|
|
956
|
+
id: z.string().describe("The test suite ID to archive"),
|
|
739
957
|
},
|
|
740
|
-
withToolLogging("
|
|
958
|
+
withToolLogging("platform_archive_test_suite", async (params) => {
|
|
741
959
|
requireApiKey();
|
|
742
|
-
const {
|
|
960
|
+
const { handleArchiveTestSuite } = await import("./tools/archive-test-suite.ts");
|
|
743
961
|
return {
|
|
744
|
-
content: [{ type: "text", text: await
|
|
962
|
+
content: [{ type: "text", text: await handleArchiveTestSuite(params) }],
|
|
745
963
|
};
|
|
746
|
-
})
|
|
964
|
+
}),
|
|
747
965
|
);
|
|
748
966
|
|
|
749
|
-
|
|
750
|
-
|
|
967
|
+
server.tool(
|
|
968
|
+
"platform_run_test_suite",
|
|
969
|
+
"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.",
|
|
970
|
+
{
|
|
971
|
+
id: z.string().describe("The test suite ID to run"),
|
|
972
|
+
targets: z
|
|
973
|
+
.array(runPlanTargetSchema)
|
|
974
|
+
.describe(
|
|
975
|
+
"What to run the scenarios against. Every target runs every scenario, and the results show one column for each.",
|
|
976
|
+
),
|
|
977
|
+
name: z
|
|
978
|
+
.string()
|
|
979
|
+
.optional()
|
|
980
|
+
.describe(
|
|
981
|
+
"The run plan to run. Omit to let the server name it after the suite and the target.",
|
|
982
|
+
),
|
|
983
|
+
repeatCount: z
|
|
984
|
+
.number()
|
|
985
|
+
.int()
|
|
986
|
+
.min(1)
|
|
987
|
+
.max(5)
|
|
988
|
+
.optional()
|
|
989
|
+
.describe("How many times to run each scenario against each target (1 to 5, default 1)"),
|
|
990
|
+
simulatorModel: z
|
|
991
|
+
.string()
|
|
992
|
+
.optional()
|
|
993
|
+
.describe("Model that plays the user. Omit for the project default."),
|
|
994
|
+
judgeModel: z
|
|
995
|
+
.string()
|
|
996
|
+
.optional()
|
|
997
|
+
.describe("Model that judges the criteria. Omit for the project default."),
|
|
998
|
+
parameters: runParametersSchema
|
|
999
|
+
.optional()
|
|
1000
|
+
.describe(
|
|
1001
|
+
"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.",
|
|
1002
|
+
),
|
|
1003
|
+
note: z
|
|
1004
|
+
.string()
|
|
1005
|
+
.max(200)
|
|
1006
|
+
.optional()
|
|
1007
|
+
.describe("One short line saying why this run was started (max 200 characters)"),
|
|
1008
|
+
},
|
|
1009
|
+
withToolLogging("platform_run_test_suite", async (params) => {
|
|
1010
|
+
requireApiKey();
|
|
1011
|
+
const { handleRunTestSuite } = await import("./tools/run-test-suite.ts");
|
|
1012
|
+
return {
|
|
1013
|
+
content: [{ type: "text", text: await handleRunTestSuite(params) }],
|
|
1014
|
+
};
|
|
1015
|
+
}),
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
751
1018
|
|
|
1019
|
+
// --- Platform Simulation Run Tools (require API key) ---
|
|
1020
|
+
// These tools query simulation run results from the LangWatch platform.
|
|
1021
|
+
function registerSimulationRunTools(server: McpServer): void {
|
|
752
1022
|
server.tool(
|
|
753
1023
|
"platform_list_simulation_runs",
|
|
754
1024
|
"List simulation run results. Filter by scenario set or batch run ID to see specific results.",
|
|
@@ -760,32 +1030,33 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
760
1030
|
},
|
|
761
1031
|
withToolLogging("platform_list_simulation_runs", async (params) => {
|
|
762
1032
|
requireApiKey();
|
|
763
|
-
const { handleListSimulationRuns } = await import("./tools/list-simulation-runs.
|
|
1033
|
+
const { handleListSimulationRuns } = await import("./tools/list-simulation-runs.ts");
|
|
764
1034
|
return {
|
|
765
1035
|
content: [{ type: "text", text: await handleListSimulationRuns(params) }],
|
|
766
1036
|
};
|
|
767
|
-
})
|
|
1037
|
+
}),
|
|
768
1038
|
);
|
|
769
1039
|
|
|
770
1040
|
server.tool(
|
|
771
1041
|
"platform_get_simulation_run",
|
|
772
|
-
"Get full details of a simulation run including conversation messages, results, costs, and
|
|
1042
|
+
"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.",
|
|
773
1043
|
{
|
|
774
1044
|
scenarioRunId: z.string().describe("The simulation run ID"),
|
|
775
1045
|
format: z.enum(["digest", "json"]).optional().describe("Output format"),
|
|
776
1046
|
},
|
|
777
1047
|
withToolLogging("platform_get_simulation_run", async (params) => {
|
|
778
1048
|
requireApiKey();
|
|
779
|
-
const { handleGetSimulationRun } = await import("./tools/get-simulation-run.
|
|
1049
|
+
const { handleGetSimulationRun } = await import("./tools/get-simulation-run.ts");
|
|
780
1050
|
return {
|
|
781
1051
|
content: [{ type: "text", text: await handleGetSimulationRun(params) }],
|
|
782
1052
|
};
|
|
783
|
-
})
|
|
1053
|
+
}),
|
|
784
1054
|
);
|
|
1055
|
+
}
|
|
785
1056
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
1057
|
+
// --- Platform Evaluator Tools (require API key) ---
|
|
1058
|
+
// These tools manage evaluators on the LangWatch platform via API.
|
|
1059
|
+
function registerEvaluatorTools(server: McpServer): void {
|
|
789
1060
|
server.tool(
|
|
790
1061
|
"platform_create_evaluator",
|
|
791
1062
|
`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.`,
|
|
@@ -794,20 +1065,16 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
794
1065
|
config: z
|
|
795
1066
|
.record(z.string(), z.unknown())
|
|
796
1067
|
.describe(
|
|
797
|
-
'Evaluator config object. Must include "evaluatorType" (e.g. "langevals/llm_boolean") and optional "settings" overrides.'
|
|
1068
|
+
'Evaluator config object. Must include "evaluatorType" (e.g. "langevals/llm_boolean") and optional "settings" overrides.',
|
|
798
1069
|
),
|
|
799
1070
|
},
|
|
800
1071
|
withToolLogging("platform_create_evaluator", async (params) => {
|
|
801
1072
|
requireApiKey();
|
|
802
|
-
const { handleCreateEvaluator } = await import(
|
|
803
|
-
"./tools/create-evaluator.js"
|
|
804
|
-
);
|
|
1073
|
+
const { handleCreateEvaluator } = await import("./tools/create-evaluator.ts");
|
|
805
1074
|
return {
|
|
806
|
-
content: [
|
|
807
|
-
{ type: "text", text: await handleCreateEvaluator(params) },
|
|
808
|
-
],
|
|
1075
|
+
content: [{ type: "text", text: await handleCreateEvaluator(params) }],
|
|
809
1076
|
};
|
|
810
|
-
})
|
|
1077
|
+
}),
|
|
811
1078
|
);
|
|
812
1079
|
|
|
813
1080
|
server.tool(
|
|
@@ -816,32 +1083,26 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
816
1083
|
{},
|
|
817
1084
|
withToolLogging("platform_list_evaluators", async () => {
|
|
818
1085
|
requireApiKey();
|
|
819
|
-
const { handleListEvaluators } = await import(
|
|
820
|
-
"./tools/list-evaluators.js"
|
|
821
|
-
);
|
|
1086
|
+
const { handleListEvaluators } = await import("./tools/list-evaluators.ts");
|
|
822
1087
|
return {
|
|
823
1088
|
content: [{ type: "text", text: await handleListEvaluators() }],
|
|
824
1089
|
};
|
|
825
|
-
})
|
|
1090
|
+
}),
|
|
826
1091
|
);
|
|
827
1092
|
|
|
828
1093
|
server.tool(
|
|
829
1094
|
"platform_get_evaluator",
|
|
830
1095
|
"Get full details of an evaluator on the LangWatch platform by ID or slug, including config, input fields, and output fields.",
|
|
831
1096
|
{
|
|
832
|
-
idOrSlug: z
|
|
833
|
-
.string()
|
|
834
|
-
.describe("The evaluator ID or slug to retrieve"),
|
|
1097
|
+
idOrSlug: z.string().describe("The evaluator ID or slug to retrieve"),
|
|
835
1098
|
},
|
|
836
1099
|
withToolLogging("platform_get_evaluator", async (params) => {
|
|
837
1100
|
requireApiKey();
|
|
838
|
-
const { handleGetEvaluator } = await import(
|
|
839
|
-
"./tools/get-evaluator.js"
|
|
840
|
-
);
|
|
1101
|
+
const { handleGetEvaluator } = await import("./tools/get-evaluator.ts");
|
|
841
1102
|
return {
|
|
842
1103
|
content: [{ type: "text", text: await handleGetEvaluator(params) }],
|
|
843
1104
|
};
|
|
844
|
-
})
|
|
1105
|
+
}),
|
|
845
1106
|
);
|
|
846
1107
|
|
|
847
1108
|
server.tool(
|
|
@@ -853,21 +1114,15 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
853
1114
|
config: z
|
|
854
1115
|
.record(z.string(), z.unknown())
|
|
855
1116
|
.optional()
|
|
856
|
-
.describe(
|
|
857
|
-
"Updated config settings. Note: evaluatorType cannot be changed after creation."
|
|
858
|
-
),
|
|
1117
|
+
.describe("Updated config settings. Note: evaluatorType cannot be changed after creation."),
|
|
859
1118
|
},
|
|
860
1119
|
withToolLogging("platform_update_evaluator", async (params) => {
|
|
861
1120
|
requireApiKey();
|
|
862
|
-
const { handleUpdateEvaluator } = await import(
|
|
863
|
-
"./tools/update-evaluator.js"
|
|
864
|
-
);
|
|
1121
|
+
const { handleUpdateEvaluator } = await import("./tools/update-evaluator.ts");
|
|
865
1122
|
return {
|
|
866
|
-
content: [
|
|
867
|
-
{ type: "text", text: await handleUpdateEvaluator(params) },
|
|
868
|
-
],
|
|
1123
|
+
content: [{ type: "text", text: await handleUpdateEvaluator(params) }],
|
|
869
1124
|
};
|
|
870
|
-
})
|
|
1125
|
+
}),
|
|
871
1126
|
);
|
|
872
1127
|
|
|
873
1128
|
server.tool(
|
|
@@ -878,48 +1133,40 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
878
1133
|
},
|
|
879
1134
|
withToolLogging("platform_delete_evaluator", async (params) => {
|
|
880
1135
|
requireApiKey();
|
|
881
|
-
const { handleDeleteEvaluator } = await import("./tools/delete-evaluator.
|
|
1136
|
+
const { handleDeleteEvaluator } = await import("./tools/delete-evaluator.ts");
|
|
882
1137
|
return {
|
|
883
1138
|
content: [{ type: "text", text: await handleDeleteEvaluator(params) }],
|
|
884
1139
|
};
|
|
885
|
-
})
|
|
1140
|
+
}),
|
|
886
1141
|
);
|
|
1142
|
+
}
|
|
887
1143
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1144
|
+
// --- Platform Model Provider Tools (require API key) ---
|
|
1145
|
+
// These tools manage model provider API keys on the LangWatch platform.
|
|
1146
|
+
function registerModelProviderTools(server: McpServer): void {
|
|
891
1147
|
server.tool(
|
|
892
1148
|
"platform_set_model_provider",
|
|
893
1149
|
`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.`,
|
|
894
1150
|
{
|
|
895
1151
|
provider: z
|
|
896
1152
|
.string()
|
|
897
|
-
.describe(
|
|
898
|
-
'Provider name, e.g., "openai", "anthropic", "azure", "custom"'
|
|
899
|
-
),
|
|
1153
|
+
.describe('Provider name, e.g., "openai", "anthropic", "azure", "custom"'),
|
|
900
1154
|
enabled: z.boolean().describe("Whether the provider is enabled"),
|
|
901
1155
|
customKeys: z
|
|
902
1156
|
.record(z.string(), z.unknown())
|
|
903
1157
|
.optional()
|
|
904
1158
|
.describe(
|
|
905
|
-
'API key configuration, e.g. { "OPENAI_API_KEY": "sk-..." }. Omit to keep existing keys.'
|
|
1159
|
+
'API key configuration, e.g. { "OPENAI_API_KEY": "sk-..." }. Omit to keep existing keys.',
|
|
906
1160
|
),
|
|
907
|
-
defaultModel: z
|
|
908
|
-
.string()
|
|
909
|
-
.optional()
|
|
910
|
-
.describe("Set as project default model"),
|
|
1161
|
+
defaultModel: z.string().optional().describe("Set as project default model"),
|
|
911
1162
|
},
|
|
912
1163
|
withToolLogging("platform_set_model_provider", async (params) => {
|
|
913
1164
|
requireApiKey();
|
|
914
|
-
const { handleSetModelProvider } = await import(
|
|
915
|
-
"./tools/set-model-provider.js"
|
|
916
|
-
);
|
|
1165
|
+
const { handleSetModelProvider } = await import("./tools/set-model-provider.ts");
|
|
917
1166
|
return {
|
|
918
|
-
content: [
|
|
919
|
-
{ type: "text", text: await handleSetModelProvider(params) },
|
|
920
|
-
],
|
|
1167
|
+
content: [{ type: "text", text: await handleSetModelProvider(params) }],
|
|
921
1168
|
};
|
|
922
|
-
})
|
|
1169
|
+
}),
|
|
923
1170
|
);
|
|
924
1171
|
|
|
925
1172
|
server.tool(
|
|
@@ -928,48 +1175,47 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
928
1175
|
{},
|
|
929
1176
|
withToolLogging("platform_list_model_providers", async () => {
|
|
930
1177
|
requireApiKey();
|
|
931
|
-
const { handleListModelProviders } = await import(
|
|
932
|
-
"./tools/list-model-providers.js"
|
|
933
|
-
);
|
|
1178
|
+
const { handleListModelProviders } = await import("./tools/list-model-providers.ts");
|
|
934
1179
|
return {
|
|
935
1180
|
content: [{ type: "text", text: await handleListModelProviders() }],
|
|
936
1181
|
};
|
|
937
|
-
})
|
|
1182
|
+
}),
|
|
938
1183
|
);
|
|
1184
|
+
}
|
|
939
1185
|
|
|
940
|
-
|
|
941
|
-
|
|
1186
|
+
// --- Platform Agent Tools (require API key) ---
|
|
1187
|
+
function registerAgentTools(server: McpServer): void {
|
|
942
1188
|
server.tool(
|
|
943
1189
|
"platform_list_agents",
|
|
944
|
-
"List all agents in the LangWatch project with their names, types
|
|
1190
|
+
"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.",
|
|
945
1191
|
{},
|
|
946
1192
|
withToolLogging("platform_list_agents", async () => {
|
|
947
1193
|
requireApiKey();
|
|
948
|
-
const { handleListAgents } = await import("./tools/list-agents.
|
|
1194
|
+
const { handleListAgents } = await import("./tools/list-agents.ts");
|
|
949
1195
|
return {
|
|
950
1196
|
content: [{ type: "text", text: await handleListAgents() }],
|
|
951
1197
|
};
|
|
952
|
-
})
|
|
1198
|
+
}),
|
|
953
1199
|
);
|
|
954
1200
|
|
|
955
1201
|
server.tool(
|
|
956
1202
|
"platform_get_agent",
|
|
957
|
-
"Get detailed information about a specific agent by its ID, including its configuration.",
|
|
1203
|
+
"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.",
|
|
958
1204
|
{
|
|
959
1205
|
id: z.string().describe("The agent ID"),
|
|
960
1206
|
},
|
|
961
1207
|
withToolLogging("platform_get_agent", async (params) => {
|
|
962
1208
|
requireApiKey();
|
|
963
|
-
const { handleGetAgent } = await import("./tools/get-agent.
|
|
1209
|
+
const { handleGetAgent } = await import("./tools/get-agent.ts");
|
|
964
1210
|
return {
|
|
965
1211
|
content: [{ type: "text", text: await handleGetAgent(params) }],
|
|
966
1212
|
};
|
|
967
|
-
})
|
|
1213
|
+
}),
|
|
968
1214
|
);
|
|
969
1215
|
|
|
970
1216
|
server.tool(
|
|
971
1217
|
"platform_create_agent",
|
|
972
|
-
"Create a new agent. Supported types: 'signature' (LLM prompt), 'code' (Python), 'workflow' (sub-workflow), 'http' (external API).",
|
|
1218
|
+
"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.",
|
|
973
1219
|
{
|
|
974
1220
|
name: z.string().describe("Agent name"),
|
|
975
1221
|
type: z.enum(["signature", "code", "workflow", "http"]).describe("Agent type"),
|
|
@@ -977,12 +1223,16 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
977
1223
|
},
|
|
978
1224
|
withToolLogging("platform_create_agent", async (params) => {
|
|
979
1225
|
requireApiKey();
|
|
980
|
-
const { handleCreateAgent } = await import("./tools/create-agent.
|
|
981
|
-
const parsedConfig = params.config
|
|
1226
|
+
const { handleCreateAgent } = await import("./tools/create-agent.ts");
|
|
1227
|
+
const parsedConfig = params.config
|
|
1228
|
+
? (JSON.parse(params.config) as Record<string, unknown>)
|
|
1229
|
+
: undefined;
|
|
982
1230
|
return {
|
|
983
|
-
content: [
|
|
1231
|
+
content: [
|
|
1232
|
+
{ type: "text", text: await handleCreateAgent({ ...params, config: parsedConfig }) },
|
|
1233
|
+
],
|
|
984
1234
|
};
|
|
985
|
-
})
|
|
1235
|
+
}),
|
|
986
1236
|
);
|
|
987
1237
|
|
|
988
1238
|
server.tool(
|
|
@@ -992,16 +1242,23 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
992
1242
|
id: z.string().describe("The agent ID"),
|
|
993
1243
|
name: z.string().optional().describe("New agent name"),
|
|
994
1244
|
type: z.string().optional().describe("New agent type: signature, code, workflow, or http"),
|
|
995
|
-
config: z
|
|
1245
|
+
config: z
|
|
1246
|
+
.string()
|
|
1247
|
+
.optional()
|
|
1248
|
+
.describe("Updated configuration as JSON string (will be parsed)"),
|
|
996
1249
|
},
|
|
997
1250
|
withToolLogging("platform_update_agent", async (params) => {
|
|
998
1251
|
requireApiKey();
|
|
999
|
-
const { handleUpdateAgent } = await import("./tools/update-agent.
|
|
1000
|
-
const parsedConfig = params.config
|
|
1252
|
+
const { handleUpdateAgent } = await import("./tools/update-agent.ts");
|
|
1253
|
+
const parsedConfig = params.config
|
|
1254
|
+
? (JSON.parse(params.config) as Record<string, unknown>)
|
|
1255
|
+
: undefined;
|
|
1001
1256
|
return {
|
|
1002
|
-
content: [
|
|
1257
|
+
content: [
|
|
1258
|
+
{ type: "text", text: await handleUpdateAgent({ ...params, config: parsedConfig }) },
|
|
1259
|
+
],
|
|
1003
1260
|
};
|
|
1004
|
-
})
|
|
1261
|
+
}),
|
|
1005
1262
|
);
|
|
1006
1263
|
|
|
1007
1264
|
server.tool(
|
|
@@ -1012,42 +1269,71 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1012
1269
|
},
|
|
1013
1270
|
withToolLogging("platform_delete_agent", async (params) => {
|
|
1014
1271
|
requireApiKey();
|
|
1015
|
-
const { handleDeleteAgent } = await import("./tools/delete-agent.
|
|
1272
|
+
const { handleDeleteAgent } = await import("./tools/delete-agent.ts");
|
|
1016
1273
|
return {
|
|
1017
1274
|
content: [{ type: "text", text: await handleDeleteAgent(params) }],
|
|
1018
1275
|
};
|
|
1019
|
-
})
|
|
1276
|
+
}),
|
|
1020
1277
|
);
|
|
1021
1278
|
|
|
1022
1279
|
server.tool(
|
|
1023
1280
|
"platform_run_agent",
|
|
1024
|
-
"
|
|
1281
|
+
"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.",
|
|
1025
1282
|
{
|
|
1026
1283
|
id: z.string().describe("The agent ID to run"),
|
|
1027
|
-
input: z
|
|
1284
|
+
input: z
|
|
1285
|
+
.string()
|
|
1286
|
+
.optional()
|
|
1287
|
+
.describe(
|
|
1288
|
+
"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.",
|
|
1289
|
+
),
|
|
1290
|
+
message: z
|
|
1291
|
+
.string()
|
|
1292
|
+
.optional()
|
|
1293
|
+
.describe("One user message to send to a connected agent, instead of input"),
|
|
1294
|
+
parameters: z
|
|
1295
|
+
.record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
|
|
1296
|
+
.optional()
|
|
1297
|
+
.describe("Run parameter values for a connected agent, by name"),
|
|
1298
|
+
threadId: z.string().optional().describe("Continue a conversation on a connected agent"),
|
|
1028
1299
|
},
|
|
1029
1300
|
withToolLogging("platform_run_agent", async (params) => {
|
|
1030
1301
|
requireApiKey();
|
|
1031
|
-
const { handleRunAgent } = await import("./tools/run-agent.
|
|
1302
|
+
const { handleRunAgent } = await import("./tools/run-agent.ts");
|
|
1032
1303
|
return {
|
|
1033
1304
|
content: [{ type: "text", text: await handleRunAgent(params) }],
|
|
1034
1305
|
};
|
|
1035
|
-
})
|
|
1306
|
+
}),
|
|
1036
1307
|
);
|
|
1037
1308
|
|
|
1038
|
-
|
|
1309
|
+
server.tool(
|
|
1310
|
+
"platform_test_agent",
|
|
1311
|
+
'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.',
|
|
1312
|
+
{
|
|
1313
|
+
id: z.string().describe("The agent ID to test"),
|
|
1314
|
+
},
|
|
1315
|
+
withToolLogging("platform_test_agent", async (params) => {
|
|
1316
|
+
requireApiKey();
|
|
1317
|
+
return {
|
|
1318
|
+
content: [{ type: "text", text: await handleTestAgent(params) }],
|
|
1319
|
+
};
|
|
1320
|
+
}),
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1039
1323
|
|
|
1324
|
+
// --- Platform Dashboard Tools (require API key) ---
|
|
1325
|
+
function registerDashboardTools(server: McpServer): void {
|
|
1040
1326
|
server.tool(
|
|
1041
1327
|
"platform_list_dashboards",
|
|
1042
1328
|
"List all analytics dashboards in the LangWatch project.",
|
|
1043
1329
|
{},
|
|
1044
1330
|
withToolLogging("platform_list_dashboards", async () => {
|
|
1045
1331
|
requireApiKey();
|
|
1046
|
-
const { handleListDashboards } = await import("./tools/list-dashboards.
|
|
1332
|
+
const { handleListDashboards } = await import("./tools/list-dashboards.ts");
|
|
1047
1333
|
return {
|
|
1048
1334
|
content: [{ type: "text", text: await handleListDashboards() }],
|
|
1049
1335
|
};
|
|
1050
|
-
})
|
|
1336
|
+
}),
|
|
1051
1337
|
);
|
|
1052
1338
|
|
|
1053
1339
|
server.tool(
|
|
@@ -1058,11 +1344,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1058
1344
|
},
|
|
1059
1345
|
withToolLogging("platform_get_dashboard", async (params) => {
|
|
1060
1346
|
requireApiKey();
|
|
1061
|
-
const { handleGetDashboard } = await import("./tools/get-dashboard.
|
|
1347
|
+
const { handleGetDashboard } = await import("./tools/get-dashboard.ts");
|
|
1062
1348
|
return {
|
|
1063
1349
|
content: [{ type: "text", text: await handleGetDashboard(params) }],
|
|
1064
1350
|
};
|
|
1065
|
-
})
|
|
1351
|
+
}),
|
|
1066
1352
|
);
|
|
1067
1353
|
|
|
1068
1354
|
server.tool(
|
|
@@ -1073,11 +1359,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1073
1359
|
},
|
|
1074
1360
|
withToolLogging("platform_create_dashboard", async (params) => {
|
|
1075
1361
|
requireApiKey();
|
|
1076
|
-
const { handleCreateDashboard } = await import("./tools/create-dashboard.
|
|
1362
|
+
const { handleCreateDashboard } = await import("./tools/create-dashboard.ts");
|
|
1077
1363
|
return {
|
|
1078
1364
|
content: [{ type: "text", text: await handleCreateDashboard(params) }],
|
|
1079
1365
|
};
|
|
1080
|
-
})
|
|
1366
|
+
}),
|
|
1081
1367
|
);
|
|
1082
1368
|
|
|
1083
1369
|
server.tool(
|
|
@@ -1088,11 +1374,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1088
1374
|
},
|
|
1089
1375
|
withToolLogging("platform_delete_dashboard", async (params) => {
|
|
1090
1376
|
requireApiKey();
|
|
1091
|
-
const { handleDeleteDashboard } = await import("./tools/delete-dashboard.
|
|
1377
|
+
const { handleDeleteDashboard } = await import("./tools/delete-dashboard.ts");
|
|
1092
1378
|
return {
|
|
1093
1379
|
content: [{ type: "text", text: await handleDeleteDashboard(params) }],
|
|
1094
1380
|
};
|
|
1095
|
-
})
|
|
1381
|
+
}),
|
|
1096
1382
|
);
|
|
1097
1383
|
|
|
1098
1384
|
server.tool(
|
|
@@ -1104,27 +1390,127 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1104
1390
|
},
|
|
1105
1391
|
withToolLogging("platform_rename_dashboard", async (params) => {
|
|
1106
1392
|
requireApiKey();
|
|
1107
|
-
const { renameDashboard } = await import("./langwatch-api-dashboards.
|
|
1393
|
+
const { renameDashboard } = await import("./langwatch-api-dashboards.ts");
|
|
1108
1394
|
const result = await renameDashboard(params.id, { name: params.name });
|
|
1109
1395
|
return {
|
|
1110
|
-
content: [
|
|
1396
|
+
content: [
|
|
1397
|
+
{
|
|
1398
|
+
type: "text",
|
|
1399
|
+
text: `Dashboard "${result.name}" renamed successfully (ID: ${result.id}).`,
|
|
1400
|
+
},
|
|
1401
|
+
],
|
|
1402
|
+
};
|
|
1403
|
+
}),
|
|
1404
|
+
);
|
|
1405
|
+
|
|
1406
|
+
server.tool(
|
|
1407
|
+
"list_dashboard_boards",
|
|
1408
|
+
"Lists the project's boards 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 dashboards platform_list_dashboards lists hold no widgets.",
|
|
1409
|
+
{},
|
|
1410
|
+
withToolLogging("list_dashboard_boards", async () => {
|
|
1411
|
+
requireApiKey();
|
|
1412
|
+
const { handleListDashboardBoards } = await import("./tools/list-dashboard-boards.js");
|
|
1413
|
+
return {
|
|
1414
|
+
content: [{ type: "text", text: await handleListDashboardBoards() }],
|
|
1111
1415
|
};
|
|
1112
|
-
})
|
|
1416
|
+
}),
|
|
1113
1417
|
);
|
|
1114
1418
|
|
|
1115
|
-
|
|
1419
|
+
server.tool(
|
|
1420
|
+
"add_dashboard_widget",
|
|
1421
|
+
"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 — 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.",
|
|
1422
|
+
{
|
|
1423
|
+
dashboardId: z
|
|
1424
|
+
.string()
|
|
1425
|
+
.describe("The Dashboards board to add the widget to, as list_dashboard_boards lists it"),
|
|
1426
|
+
name: z.string().describe("The widget's display name"),
|
|
1427
|
+
code: z
|
|
1428
|
+
.string()
|
|
1429
|
+
.describe(
|
|
1430
|
+
"The widget's TSX source. Runs sandboxed with a global LW (LW.useChartQuery(name), LW.navigate, LW.theme) in scope.",
|
|
1431
|
+
),
|
|
1432
|
+
queries: z
|
|
1433
|
+
.union([
|
|
1434
|
+
z.record(z.string(), z.string()).describe('{"<name>": "<LWQL SQL>"}'),
|
|
1435
|
+
z
|
|
1436
|
+
.array(
|
|
1437
|
+
z.object({
|
|
1438
|
+
name: z.string().describe('Referenced from code as LW.useChartQuery("name")'),
|
|
1439
|
+
sql: z.string().describe("The LangWatchQL statement"),
|
|
1440
|
+
}),
|
|
1441
|
+
)
|
|
1442
|
+
.describe("Named LWQL queries, array form"),
|
|
1443
|
+
])
|
|
1444
|
+
.describe(
|
|
1445
|
+
"The widget's named LangWatchQL queries: either {name: sql} or an array of {name, sql}. Max 8. Validate each statement with run_query first.",
|
|
1446
|
+
),
|
|
1447
|
+
},
|
|
1448
|
+
withToolLogging("add_dashboard_widget", async (params) => {
|
|
1449
|
+
requireApiKey();
|
|
1450
|
+
const { handleAddDashboardWidget } = await import("./tools/add-dashboard-widget.js");
|
|
1451
|
+
return {
|
|
1452
|
+
content: [{ type: "text", text: await handleAddDashboardWidget(params) }],
|
|
1453
|
+
};
|
|
1454
|
+
}),
|
|
1455
|
+
);
|
|
1456
|
+
|
|
1457
|
+
server.tool(
|
|
1458
|
+
"update_dashboard_widget",
|
|
1459
|
+
"Changes one custom widget on a dashboard: its name, its TSX `code`, its named LangWatchQL `queries` or its `description`. 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.",
|
|
1460
|
+
{
|
|
1461
|
+
dashboardId: z.string().describe("The dashboard the widget is on"),
|
|
1462
|
+
widgetId: z.string().describe("The widget to change, as add_dashboard_widget returned it"),
|
|
1463
|
+
name: z.string().optional().describe("The widget's new display name"),
|
|
1464
|
+
code: z
|
|
1465
|
+
.string()
|
|
1466
|
+
.optional()
|
|
1467
|
+
.describe(
|
|
1468
|
+
"The widget's new TSX source. Runs sandboxed with a global LW (LW.useChartQuery(name), LW.navigate, LW.theme) in scope.",
|
|
1469
|
+
),
|
|
1470
|
+
queries: z
|
|
1471
|
+
.union([
|
|
1472
|
+
z.record(z.string(), z.string()).describe('{"<name>": "<LWQL SQL>"}'),
|
|
1473
|
+
z
|
|
1474
|
+
.array(
|
|
1475
|
+
z.object({
|
|
1476
|
+
name: z.string().describe('Referenced from code as LW.useChartQuery("name")'),
|
|
1477
|
+
sql: z.string().describe("The LangWatchQL statement"),
|
|
1478
|
+
}),
|
|
1479
|
+
)
|
|
1480
|
+
.describe("Named LWQL queries, array form"),
|
|
1481
|
+
])
|
|
1482
|
+
.optional()
|
|
1483
|
+
.describe(
|
|
1484
|
+
"The widget's new named LangWatchQL queries, replacing the stored set: either {name: sql} or an array of {name, sql}. Max 8.",
|
|
1485
|
+
),
|
|
1486
|
+
description: z
|
|
1487
|
+
.string()
|
|
1488
|
+
.optional()
|
|
1489
|
+
.describe("What the widget's info tip says: a sentence or two"),
|
|
1490
|
+
},
|
|
1491
|
+
withToolLogging("update_dashboard_widget", async (params) => {
|
|
1492
|
+
requireApiKey();
|
|
1493
|
+
const { handleUpdateDashboardWidget } = await import("./tools/update-dashboard-widget.js");
|
|
1494
|
+
return {
|
|
1495
|
+
content: [{ type: "text", text: await handleUpdateDashboardWidget(params) }],
|
|
1496
|
+
};
|
|
1497
|
+
}),
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1116
1500
|
|
|
1501
|
+
// --- Platform Workflow Tools (require API key) ---
|
|
1502
|
+
function registerWorkflowTools(server: McpServer): void {
|
|
1117
1503
|
server.tool(
|
|
1118
1504
|
"platform_list_workflows",
|
|
1119
1505
|
"List all workflows in the LangWatch project.",
|
|
1120
1506
|
{},
|
|
1121
1507
|
withToolLogging("platform_list_workflows", async () => {
|
|
1122
1508
|
requireApiKey();
|
|
1123
|
-
const { handleListWorkflows } = await import("./tools/list-workflows.
|
|
1509
|
+
const { handleListWorkflows } = await import("./tools/list-workflows.ts");
|
|
1124
1510
|
return {
|
|
1125
1511
|
content: [{ type: "text", text: await handleListWorkflows() }],
|
|
1126
1512
|
};
|
|
1127
|
-
})
|
|
1513
|
+
}),
|
|
1128
1514
|
);
|
|
1129
1515
|
|
|
1130
1516
|
server.tool(
|
|
@@ -1135,11 +1521,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1135
1521
|
},
|
|
1136
1522
|
withToolLogging("platform_get_workflow", async (params) => {
|
|
1137
1523
|
requireApiKey();
|
|
1138
|
-
const { handleGetWorkflow } = await import("./tools/get-workflow.
|
|
1524
|
+
const { handleGetWorkflow } = await import("./tools/get-workflow.ts");
|
|
1139
1525
|
return {
|
|
1140
1526
|
content: [{ type: "text", text: await handleGetWorkflow(params) }],
|
|
1141
1527
|
};
|
|
1142
|
-
})
|
|
1528
|
+
}),
|
|
1143
1529
|
);
|
|
1144
1530
|
|
|
1145
1531
|
server.tool(
|
|
@@ -1150,11 +1536,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1150
1536
|
},
|
|
1151
1537
|
withToolLogging("platform_delete_workflow", async (params) => {
|
|
1152
1538
|
requireApiKey();
|
|
1153
|
-
const { handleDeleteWorkflow } = await import("./tools/delete-workflow.
|
|
1539
|
+
const { handleDeleteWorkflow } = await import("./tools/delete-workflow.ts");
|
|
1154
1540
|
return {
|
|
1155
1541
|
content: [{ type: "text", text: await handleDeleteWorkflow(params) }],
|
|
1156
1542
|
};
|
|
1157
|
-
})
|
|
1543
|
+
}),
|
|
1158
1544
|
);
|
|
1159
1545
|
|
|
1160
1546
|
server.tool(
|
|
@@ -1166,15 +1552,16 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1166
1552
|
},
|
|
1167
1553
|
withToolLogging("platform_run_workflow", async (params) => {
|
|
1168
1554
|
requireApiKey();
|
|
1169
|
-
const { handleRunWorkflow } = await import("./tools/run-workflow.
|
|
1555
|
+
const { handleRunWorkflow } = await import("./tools/run-workflow.ts");
|
|
1170
1556
|
return {
|
|
1171
1557
|
content: [{ type: "text", text: await handleRunWorkflow(params) }],
|
|
1172
1558
|
};
|
|
1173
|
-
})
|
|
1559
|
+
}),
|
|
1174
1560
|
);
|
|
1561
|
+
}
|
|
1175
1562
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1563
|
+
// --- Platform Annotation Tools (require API key) ---
|
|
1564
|
+
function registerAnnotationTools(server: McpServer): void {
|
|
1178
1565
|
server.tool(
|
|
1179
1566
|
"platform_list_annotations",
|
|
1180
1567
|
"List all annotations for the project, optionally filtered by trace ID.",
|
|
@@ -1183,11 +1570,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1183
1570
|
},
|
|
1184
1571
|
withToolLogging("platform_list_annotations", async (params) => {
|
|
1185
1572
|
requireApiKey();
|
|
1186
|
-
const { handleListAnnotations } = await import("./tools/list-annotations.
|
|
1573
|
+
const { handleListAnnotations } = await import("./tools/list-annotations.ts");
|
|
1187
1574
|
return {
|
|
1188
1575
|
content: [{ type: "text", text: await handleListAnnotations(params) }],
|
|
1189
1576
|
};
|
|
1190
|
-
})
|
|
1577
|
+
}),
|
|
1191
1578
|
);
|
|
1192
1579
|
|
|
1193
1580
|
server.tool(
|
|
@@ -1201,11 +1588,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1201
1588
|
},
|
|
1202
1589
|
withToolLogging("platform_create_annotation", async (params) => {
|
|
1203
1590
|
requireApiKey();
|
|
1204
|
-
const { handleCreateAnnotation } = await import("./tools/create-annotation.
|
|
1591
|
+
const { handleCreateAnnotation } = await import("./tools/create-annotation.ts");
|
|
1205
1592
|
return {
|
|
1206
1593
|
content: [{ type: "text", text: await handleCreateAnnotation(params) }],
|
|
1207
1594
|
};
|
|
1208
|
-
})
|
|
1595
|
+
}),
|
|
1209
1596
|
);
|
|
1210
1597
|
|
|
1211
1598
|
server.tool(
|
|
@@ -1216,11 +1603,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1216
1603
|
},
|
|
1217
1604
|
withToolLogging("platform_delete_annotation", async (params) => {
|
|
1218
1605
|
requireApiKey();
|
|
1219
|
-
const { handleDeleteAnnotation } = await import("./tools/delete-annotation.
|
|
1606
|
+
const { handleDeleteAnnotation } = await import("./tools/delete-annotation.ts");
|
|
1220
1607
|
return {
|
|
1221
1608
|
content: [{ type: "text", text: await handleDeleteAnnotation(params) }],
|
|
1222
1609
|
};
|
|
1223
|
-
})
|
|
1610
|
+
}),
|
|
1224
1611
|
);
|
|
1225
1612
|
|
|
1226
1613
|
server.tool(
|
|
@@ -1231,7 +1618,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1231
1618
|
},
|
|
1232
1619
|
withToolLogging("platform_get_annotation", async (params) => {
|
|
1233
1620
|
requireApiKey();
|
|
1234
|
-
const { getAnnotation } = await import("./langwatch-api-annotations.
|
|
1621
|
+
const { getAnnotation } = await import("./langwatch-api-annotations.ts");
|
|
1235
1622
|
const annotation = await getAnnotation(params.id);
|
|
1236
1623
|
const lines = [
|
|
1237
1624
|
`**ID**: ${annotation.id}`,
|
|
@@ -1243,11 +1630,12 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1243
1630
|
return {
|
|
1244
1631
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
1245
1632
|
};
|
|
1246
|
-
})
|
|
1633
|
+
}),
|
|
1247
1634
|
);
|
|
1635
|
+
}
|
|
1248
1636
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1637
|
+
// --- Platform Trigger/Automation Tools (require API key) ---
|
|
1638
|
+
function registerTriggerTools(server: McpServer): void {
|
|
1251
1639
|
server.tool(
|
|
1252
1640
|
"platform_list_triggers",
|
|
1253
1641
|
"List all triggers (automations) in the project. Triggers automate actions like sending emails, Slack messages, or adding to datasets when conditions are met.",
|
|
@@ -1256,72 +1644,155 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1256
1644
|
},
|
|
1257
1645
|
withToolLogging("platform_list_triggers", async (params) => {
|
|
1258
1646
|
requireApiKey();
|
|
1259
|
-
const {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1277
|
-
})
|
|
1647
|
+
const { handleListTriggers } = await import("./tools/list-triggers.ts");
|
|
1648
|
+
return { content: [{ type: "text", text: await handleListTriggers(params) }] };
|
|
1649
|
+
}),
|
|
1650
|
+
);
|
|
1651
|
+
|
|
1652
|
+
server.tool(
|
|
1653
|
+
"platform_get_trigger",
|
|
1654
|
+
"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.",
|
|
1655
|
+
{
|
|
1656
|
+
id: z.string().describe("The trigger ID"),
|
|
1657
|
+
},
|
|
1658
|
+
withToolLogging("platform_get_trigger", async (params) => {
|
|
1659
|
+
requireApiKey();
|
|
1660
|
+
const { getTrigger } = await import("./langwatch-api-triggers.ts");
|
|
1661
|
+
const trigger = await getTrigger(params.id);
|
|
1662
|
+
return { content: [{ type: "text", text: JSON.stringify(trigger, null, 2) }] };
|
|
1663
|
+
}),
|
|
1278
1664
|
);
|
|
1279
1665
|
|
|
1280
1666
|
server.tool(
|
|
1281
1667
|
"platform_create_trigger",
|
|
1282
|
-
|
|
1668
|
+
[
|
|
1669
|
+
"Create a trigger (automation).",
|
|
1670
|
+
"",
|
|
1671
|
+
"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).",
|
|
1672
|
+
"`actionParams` is the delivery configuration for the channel named in `action` — 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.",
|
|
1673
|
+
SLACK_DELIVERY_NOTE,
|
|
1674
|
+
`filters: ${TRIGGER_FILTERS_DESCRIPTION}`,
|
|
1675
|
+
"The delivery channel cannot be changed afterwards.",
|
|
1676
|
+
].join("\n"),
|
|
1283
1677
|
{
|
|
1284
1678
|
name: z.string().describe("Trigger name"),
|
|
1285
|
-
action:
|
|
1286
|
-
|
|
1679
|
+
action: triggerActionSchema.describe("Which channel it delivers on"),
|
|
1680
|
+
actionParams: actionParamsSchema
|
|
1681
|
+
.optional()
|
|
1682
|
+
.describe("The delivery configuration the channel named in `action` reads. Defaults to {}"),
|
|
1683
|
+
filters: z.string().optional().describe(TRIGGER_FILTERS_DESCRIPTION),
|
|
1684
|
+
filterQuery: z.string().optional().describe(TRIGGER_FILTER_QUERY_DESCRIPTION),
|
|
1685
|
+
customGraphId: z.string().optional().describe("Set to make this an alert on that graph"),
|
|
1686
|
+
graphAlert: graphAlertSchema.optional(),
|
|
1687
|
+
report: reportSchema.optional().describe("Set to make this a scheduled report"),
|
|
1688
|
+
templates: templatesSchema.optional(),
|
|
1689
|
+
notificationCadence: notificationCadenceSchema.optional(),
|
|
1287
1690
|
message: z.string().optional().describe("Custom alert message"),
|
|
1288
|
-
alertType:
|
|
1691
|
+
alertType: alertTypeSchema.optional().describe("Alert severity. Required for a graph alert."),
|
|
1289
1692
|
},
|
|
1290
1693
|
withToolLogging("platform_create_trigger", async (params) => {
|
|
1291
1694
|
requireApiKey();
|
|
1292
|
-
const {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
try { filters = JSON.parse(params.filters) as Record<string, unknown>; }
|
|
1296
|
-
catch { return { content: [{ type: "text", text: "Error: filters must be valid JSON" }] }; }
|
|
1297
|
-
}
|
|
1298
|
-
const trigger = await createTrigger({
|
|
1299
|
-
name: params.name,
|
|
1300
|
-
action: params.action,
|
|
1301
|
-
filters,
|
|
1302
|
-
message: params.message,
|
|
1303
|
-
alertType: params.alertType,
|
|
1304
|
-
});
|
|
1305
|
-
return { content: [{ type: "text", text: `Trigger "${trigger.name}" created (ID: ${trigger.id}, Action: ${trigger.action}).` }] };
|
|
1306
|
-
})
|
|
1695
|
+
const { handleCreateTrigger } = await import("./tools/create-trigger.ts");
|
|
1696
|
+
return handleCreateTrigger(params);
|
|
1697
|
+
}),
|
|
1307
1698
|
);
|
|
1308
1699
|
|
|
1309
1700
|
server.tool(
|
|
1310
1701
|
"platform_update_trigger",
|
|
1311
|
-
|
|
1702
|
+
[
|
|
1703
|
+
"Update a trigger (automation). Anything left out is left alone.",
|
|
1704
|
+
"",
|
|
1705
|
+
"`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.",
|
|
1706
|
+
"The delivery channel, the kind of automation and an alert's graph cannot be changed.",
|
|
1707
|
+
].join("\n"),
|
|
1312
1708
|
{
|
|
1313
1709
|
id: z.string().describe("The trigger ID"),
|
|
1314
1710
|
name: z.string().optional().describe("New name"),
|
|
1315
|
-
active: z.boolean().optional().describe("
|
|
1316
|
-
|
|
1317
|
-
|
|
1711
|
+
active: z.boolean().optional().describe("Resume or pause it"),
|
|
1712
|
+
actionParams: actionParamsSchema
|
|
1713
|
+
.optional()
|
|
1714
|
+
.describe("The delivery configuration this automation should have from now on"),
|
|
1715
|
+
filters: z.string().optional().describe(TRIGGER_FILTERS_DESCRIPTION),
|
|
1716
|
+
filterQuery: z
|
|
1717
|
+
.string()
|
|
1718
|
+
.nullable()
|
|
1719
|
+
.optional()
|
|
1720
|
+
.describe(`${TRIGGER_FILTER_QUERY_DESCRIPTION} null clears the saved query.`),
|
|
1721
|
+
graphAlert: graphAlertSchema
|
|
1722
|
+
.optional()
|
|
1723
|
+
.describe("Only for an automation that is already a graph alert"),
|
|
1724
|
+
report: reportSchema
|
|
1725
|
+
.optional()
|
|
1726
|
+
.describe("Only for an automation that is already a scheduled report"),
|
|
1727
|
+
templates: templatesSchema.optional(),
|
|
1728
|
+
notificationCadence: notificationCadenceSchema.optional(),
|
|
1729
|
+
message: z
|
|
1730
|
+
.string()
|
|
1731
|
+
.nullable()
|
|
1732
|
+
.optional()
|
|
1733
|
+
.describe("New alert message. null removes the custom message"),
|
|
1734
|
+
alertType: alertTypeSchema
|
|
1735
|
+
.nullable()
|
|
1736
|
+
.optional()
|
|
1737
|
+
.describe("New alert severity. null clears it"),
|
|
1318
1738
|
},
|
|
1319
1739
|
withToolLogging("platform_update_trigger", async (params) => {
|
|
1320
1740
|
requireApiKey();
|
|
1321
|
-
const { updateTrigger } = await import("./langwatch-api-triggers.
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1741
|
+
const { getTrigger, updateTrigger } = await import("./langwatch-api-triggers.ts");
|
|
1742
|
+
if (params.actionParams !== undefined) {
|
|
1743
|
+
// The channel cannot change on update, so the SAVED action decides
|
|
1744
|
+
// which shape the replacement configuration must fit.
|
|
1745
|
+
const saved = await getTrigger(params.id);
|
|
1746
|
+
const boundParams = validateActionParamsForAction({
|
|
1747
|
+
action: saved.action,
|
|
1748
|
+
actionParams: params.actionParams,
|
|
1749
|
+
});
|
|
1750
|
+
if (!boundParams.ok) {
|
|
1751
|
+
const { toolError } = await import("./tools/create-trigger.ts");
|
|
1752
|
+
return toolError(boundParams.message);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
const { parseJsonObject, toolError } = await import("./tools/create-trigger.ts");
|
|
1756
|
+
const filters = params.filters ? parseJsonObject(params.filters) : undefined;
|
|
1757
|
+
if (params.filters && !filters) {
|
|
1758
|
+
return toolError("filters must be a JSON object");
|
|
1759
|
+
}
|
|
1760
|
+
const trigger = await updateTrigger({ ...params, filters });
|
|
1761
|
+
return {
|
|
1762
|
+
content: [
|
|
1763
|
+
{ type: "text", text: `Trigger "${trigger.name}" updated (active: ${trigger.active}).` },
|
|
1764
|
+
],
|
|
1765
|
+
};
|
|
1766
|
+
}),
|
|
1767
|
+
);
|
|
1768
|
+
|
|
1769
|
+
server.tool(
|
|
1770
|
+
"platform_test_fire_trigger",
|
|
1771
|
+
"Send a trigger's message to the destination it is configured with, to confirm it arrives. Nothing is recorded as a fire.",
|
|
1772
|
+
{
|
|
1773
|
+
id: z.string().describe("The trigger ID"),
|
|
1774
|
+
},
|
|
1775
|
+
withToolLogging("platform_test_fire_trigger", async (params) => {
|
|
1776
|
+
requireApiKey();
|
|
1777
|
+
const { testFireTrigger } = await import("./langwatch-api-triggers.ts");
|
|
1778
|
+
const result = await testFireTrigger(params.id);
|
|
1779
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1780
|
+
}),
|
|
1781
|
+
);
|
|
1782
|
+
|
|
1783
|
+
server.tool(
|
|
1784
|
+
"platform_list_trigger_fires",
|
|
1785
|
+
"What an automation has done, newest first. Metadata only — no message content. Answers with `nextCursor`; pass it back as `cursor` for the next page (null means there is none).",
|
|
1786
|
+
{
|
|
1787
|
+
id: z.string().describe("The trigger ID"),
|
|
1788
|
+
limit: z.number().int().positive().optional().describe("How many fires to return"),
|
|
1789
|
+
cursor: z.string().optional().describe("The `nextCursor` a previous call answered with"),
|
|
1790
|
+
},
|
|
1791
|
+
withToolLogging("platform_list_trigger_fires", async (params) => {
|
|
1792
|
+
requireApiKey();
|
|
1793
|
+
const { handleListTriggerFires } = await import("./tools/list-trigger-fires.ts");
|
|
1794
|
+
return { content: [{ type: "text", text: await handleListTriggerFires(params) }] };
|
|
1795
|
+
}),
|
|
1325
1796
|
);
|
|
1326
1797
|
|
|
1327
1798
|
server.tool(
|
|
@@ -1332,28 +1803,29 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1332
1803
|
},
|
|
1333
1804
|
withToolLogging("platform_delete_trigger", async (params) => {
|
|
1334
1805
|
requireApiKey();
|
|
1335
|
-
const { deleteTrigger } = await import("./langwatch-api-triggers.
|
|
1806
|
+
const { deleteTrigger } = await import("./langwatch-api-triggers.ts");
|
|
1336
1807
|
const result = await deleteTrigger(params.id);
|
|
1337
1808
|
return { content: [{ type: "text", text: `Trigger ${result.id} deleted.` }] };
|
|
1338
|
-
})
|
|
1809
|
+
}),
|
|
1339
1810
|
);
|
|
1811
|
+
}
|
|
1340
1812
|
|
|
1341
|
-
|
|
1342
|
-
|
|
1813
|
+
// --- Platform Monitor Tools (require API key) ---
|
|
1814
|
+
function registerMonitorTools(server: McpServer): void {
|
|
1343
1815
|
server.tool(
|
|
1344
1816
|
"platform_list_monitors",
|
|
1345
1817
|
"List all online evaluation monitors for the project.",
|
|
1346
1818
|
{},
|
|
1347
1819
|
withToolLogging("platform_list_monitors", async () => {
|
|
1348
1820
|
requireApiKey();
|
|
1349
|
-
const { listMonitors } = await import("./langwatch-api-monitors.
|
|
1821
|
+
const { listMonitors } = await import("./langwatch-api-monitors.ts");
|
|
1350
1822
|
const monitors = await listMonitors();
|
|
1351
1823
|
if (monitors.length === 0) {
|
|
1352
1824
|
return { content: [{ type: "text", text: "No monitors found." }] };
|
|
1353
1825
|
}
|
|
1354
1826
|
const lines = monitors.map(
|
|
1355
1827
|
(m) =>
|
|
1356
|
-
`• ${m.name} (id: ${m.id}, type: ${m.checkType}, ${m.enabled ? "enabled" : "disabled"}, mode: ${m.executionMode}, sample: ${Math.round(m.sample * 100)}%)
|
|
1828
|
+
`• ${m.name} (id: ${m.id}, type: ${m.checkType}, ${m.enabled ? "enabled" : "disabled"}, mode: ${m.executionMode}, sample: ${Math.round(m.sample * 100)}%)`,
|
|
1357
1829
|
);
|
|
1358
1830
|
return {
|
|
1359
1831
|
content: [
|
|
@@ -1363,7 +1835,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1363
1835
|
},
|
|
1364
1836
|
],
|
|
1365
1837
|
};
|
|
1366
|
-
})
|
|
1838
|
+
}),
|
|
1367
1839
|
);
|
|
1368
1840
|
|
|
1369
1841
|
server.tool(
|
|
@@ -1374,7 +1846,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1374
1846
|
},
|
|
1375
1847
|
withToolLogging("platform_get_monitor", async (params) => {
|
|
1376
1848
|
requireApiKey();
|
|
1377
|
-
const { getMonitor } = await import("./langwatch-api-monitors.
|
|
1849
|
+
const { getMonitor } = await import("./langwatch-api-monitors.ts");
|
|
1378
1850
|
const m = await getMonitor(params.id);
|
|
1379
1851
|
const lines = [
|
|
1380
1852
|
`**${m.name}** (${m.id})`,
|
|
@@ -1387,7 +1859,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1387
1859
|
`- Created: ${m.createdAt}`,
|
|
1388
1860
|
].filter(Boolean);
|
|
1389
1861
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1390
|
-
})
|
|
1862
|
+
}),
|
|
1391
1863
|
);
|
|
1392
1864
|
|
|
1393
1865
|
server.tool(
|
|
@@ -1395,27 +1867,17 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1395
1867
|
"Create a new online evaluation monitor that runs an evaluator on incoming traces.",
|
|
1396
1868
|
{
|
|
1397
1869
|
name: z.string().describe("Monitor name"),
|
|
1398
|
-
checkType: z
|
|
1399
|
-
.string()
|
|
1400
|
-
.describe("Evaluator check type (e.g. ragas/toxicity, custom/my-eval)"),
|
|
1870
|
+
checkType: z.string().describe("Evaluator check type (e.g. ragas/toxicity, custom/my-eval)"),
|
|
1401
1871
|
executionMode: z
|
|
1402
1872
|
.enum(["ON_MESSAGE", "AS_GUARDRAIL", "MANUALLY"])
|
|
1403
1873
|
.optional()
|
|
1404
1874
|
.describe("When to run: ON_MESSAGE (default), AS_GUARDRAIL, or MANUALLY"),
|
|
1405
|
-
sample: z
|
|
1406
|
-
|
|
1407
|
-
.min(0)
|
|
1408
|
-
.max(1)
|
|
1409
|
-
.optional()
|
|
1410
|
-
.describe("Sampling rate 0.0-1.0 (default: 1.0)"),
|
|
1411
|
-
evaluatorId: z
|
|
1412
|
-
.string()
|
|
1413
|
-
.optional()
|
|
1414
|
-
.describe("Link to a saved evaluator by ID"),
|
|
1875
|
+
sample: z.number().min(0).max(1).optional().describe("Sampling rate 0.0-1.0 (default: 1.0)"),
|
|
1876
|
+
evaluatorId: z.string().optional().describe("Link to a saved evaluator by ID"),
|
|
1415
1877
|
},
|
|
1416
1878
|
withToolLogging("platform_create_monitor", async (params) => {
|
|
1417
1879
|
requireApiKey();
|
|
1418
|
-
const { createMonitor } = await import("./langwatch-api-monitors.
|
|
1880
|
+
const { createMonitor } = await import("./langwatch-api-monitors.ts");
|
|
1419
1881
|
const monitor = await createMonitor(params);
|
|
1420
1882
|
return {
|
|
1421
1883
|
content: [
|
|
@@ -1425,7 +1887,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1425
1887
|
},
|
|
1426
1888
|
],
|
|
1427
1889
|
};
|
|
1428
|
-
})
|
|
1890
|
+
}),
|
|
1429
1891
|
);
|
|
1430
1892
|
|
|
1431
1893
|
server.tool(
|
|
@@ -1443,7 +1905,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1443
1905
|
},
|
|
1444
1906
|
withToolLogging("platform_update_monitor", async (params) => {
|
|
1445
1907
|
requireApiKey();
|
|
1446
|
-
const { updateMonitor } = await import("./langwatch-api-monitors.
|
|
1908
|
+
const { updateMonitor } = await import("./langwatch-api-monitors.ts");
|
|
1447
1909
|
const monitor = await updateMonitor(params);
|
|
1448
1910
|
return {
|
|
1449
1911
|
content: [
|
|
@@ -1453,7 +1915,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1453
1915
|
},
|
|
1454
1916
|
],
|
|
1455
1917
|
};
|
|
1456
|
-
})
|
|
1918
|
+
}),
|
|
1457
1919
|
);
|
|
1458
1920
|
|
|
1459
1921
|
server.tool(
|
|
@@ -1464,32 +1926,29 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1464
1926
|
},
|
|
1465
1927
|
withToolLogging("platform_delete_monitor", async (params) => {
|
|
1466
1928
|
requireApiKey();
|
|
1467
|
-
const { deleteMonitor } = await import("./langwatch-api-monitors.
|
|
1929
|
+
const { deleteMonitor } = await import("./langwatch-api-monitors.ts");
|
|
1468
1930
|
const result = await deleteMonitor(params.id);
|
|
1469
1931
|
return {
|
|
1470
|
-
content: [
|
|
1471
|
-
{ type: "text", text: `Monitor ${result.id} deleted.` },
|
|
1472
|
-
],
|
|
1932
|
+
content: [{ type: "text", text: `Monitor ${result.id} deleted.` }],
|
|
1473
1933
|
};
|
|
1474
|
-
})
|
|
1934
|
+
}),
|
|
1475
1935
|
);
|
|
1936
|
+
}
|
|
1476
1937
|
|
|
1477
|
-
|
|
1478
|
-
|
|
1938
|
+
// --- Platform Secret Tools (require API key) ---
|
|
1939
|
+
function registerSecretTools(server: McpServer): void {
|
|
1479
1940
|
server.tool(
|
|
1480
1941
|
"platform_list_secrets",
|
|
1481
1942
|
"List all project secrets (values are never returned, only metadata).",
|
|
1482
1943
|
{},
|
|
1483
1944
|
withToolLogging("platform_list_secrets", async () => {
|
|
1484
1945
|
requireApiKey();
|
|
1485
|
-
const { listSecrets } = await import("./langwatch-api-secrets.
|
|
1946
|
+
const { listSecrets } = await import("./langwatch-api-secrets.ts");
|
|
1486
1947
|
const secrets = await listSecrets();
|
|
1487
1948
|
if (secrets.length === 0) {
|
|
1488
1949
|
return { content: [{ type: "text", text: "No secrets found." }] };
|
|
1489
1950
|
}
|
|
1490
|
-
const lines = secrets.map(
|
|
1491
|
-
(s) => `• ${s.name} (id: ${s.id}, updated: ${s.updatedAt})`
|
|
1492
|
-
);
|
|
1951
|
+
const lines = secrets.map((s) => `• ${s.name} (id: ${s.id}, updated: ${s.updatedAt})`);
|
|
1493
1952
|
return {
|
|
1494
1953
|
content: [
|
|
1495
1954
|
{
|
|
@@ -1498,7 +1957,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1498
1957
|
},
|
|
1499
1958
|
],
|
|
1500
1959
|
};
|
|
1501
|
-
})
|
|
1960
|
+
}),
|
|
1502
1961
|
);
|
|
1503
1962
|
|
|
1504
1963
|
server.tool(
|
|
@@ -1510,7 +1969,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1510
1969
|
},
|
|
1511
1970
|
withToolLogging("platform_create_secret", async (params) => {
|
|
1512
1971
|
requireApiKey();
|
|
1513
|
-
const { createSecret } = await import("./langwatch-api-secrets.
|
|
1972
|
+
const { createSecret } = await import("./langwatch-api-secrets.ts");
|
|
1514
1973
|
const secret = await createSecret({ name: params.name, value: params.value });
|
|
1515
1974
|
return {
|
|
1516
1975
|
content: [
|
|
@@ -1520,7 +1979,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1520
1979
|
},
|
|
1521
1980
|
],
|
|
1522
1981
|
};
|
|
1523
|
-
})
|
|
1982
|
+
}),
|
|
1524
1983
|
);
|
|
1525
1984
|
|
|
1526
1985
|
server.tool(
|
|
@@ -1532,7 +1991,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1532
1991
|
},
|
|
1533
1992
|
withToolLogging("platform_update_secret", async (params) => {
|
|
1534
1993
|
requireApiKey();
|
|
1535
|
-
const { updateSecret } = await import("./langwatch-api-secrets.
|
|
1994
|
+
const { updateSecret } = await import("./langwatch-api-secrets.ts");
|
|
1536
1995
|
const secret = await updateSecret({ id: params.id, value: params.value });
|
|
1537
1996
|
return {
|
|
1538
1997
|
content: [
|
|
@@ -1542,7 +2001,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1542
2001
|
},
|
|
1543
2002
|
],
|
|
1544
2003
|
};
|
|
1545
|
-
})
|
|
2004
|
+
}),
|
|
1546
2005
|
);
|
|
1547
2006
|
|
|
1548
2007
|
server.tool(
|
|
@@ -1553,14 +2012,15 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1553
2012
|
},
|
|
1554
2013
|
withToolLogging("platform_delete_secret", async (params) => {
|
|
1555
2014
|
requireApiKey();
|
|
1556
|
-
const { deleteSecret } = await import("./langwatch-api-secrets.
|
|
2015
|
+
const { deleteSecret } = await import("./langwatch-api-secrets.ts");
|
|
1557
2016
|
const result = await deleteSecret(params.id);
|
|
1558
2017
|
return { content: [{ type: "text", text: `Secret ${result.id} deleted.` }] };
|
|
1559
|
-
})
|
|
2018
|
+
}),
|
|
1560
2019
|
);
|
|
2020
|
+
}
|
|
1561
2021
|
|
|
1562
|
-
|
|
1563
|
-
|
|
2022
|
+
// --- Platform Experiment Execution Tools (require API key) ---
|
|
2023
|
+
function registerExperimentTools(server: McpServer): void {
|
|
1564
2024
|
server.tool(
|
|
1565
2025
|
"platform_run_experiment",
|
|
1566
2026
|
"Start an experiment run by slug. Returns a run ID for polling status.",
|
|
@@ -1572,7 +2032,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1572
2032
|
return {
|
|
1573
2033
|
content: [{ type: "text", text: await handleRunExperiment(params) }],
|
|
1574
2034
|
};
|
|
1575
|
-
})
|
|
2035
|
+
}),
|
|
1576
2036
|
);
|
|
1577
2037
|
|
|
1578
2038
|
server.tool(
|
|
@@ -1592,7 +2052,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1592
2052
|
return {
|
|
1593
2053
|
content: [{ type: "text", text: await handleExperimentStatus(params) }],
|
|
1594
2054
|
};
|
|
1595
|
-
})
|
|
2055
|
+
}),
|
|
1596
2056
|
);
|
|
1597
2057
|
|
|
1598
2058
|
server.tool(
|
|
@@ -1605,16 +2065,14 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1605
2065
|
.positive()
|
|
1606
2066
|
.max(100)
|
|
1607
2067
|
.optional()
|
|
1608
|
-
.describe(
|
|
1609
|
-
"Maximum number of experiments to include (default 25, hard-capped at 100)",
|
|
1610
|
-
),
|
|
2068
|
+
.describe("Maximum number of experiments to include (default 25, hard-capped at 100)"),
|
|
1611
2069
|
},
|
|
1612
2070
|
withToolLogging("platform_experiment_list", async (params) => {
|
|
1613
2071
|
requireApiKey();
|
|
1614
2072
|
return {
|
|
1615
2073
|
content: [{ type: "text", text: await handleExperimentList(params) }],
|
|
1616
2074
|
};
|
|
1617
|
-
})
|
|
2075
|
+
}),
|
|
1618
2076
|
);
|
|
1619
2077
|
|
|
1620
2078
|
server.tool(
|
|
@@ -1623,34 +2081,28 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1623
2081
|
{
|
|
1624
2082
|
experimentSlug: z
|
|
1625
2083
|
.string()
|
|
1626
|
-
.describe(
|
|
1627
|
-
"Experiment slug from platform_experiment_list (e.g. 'checkout-flow')",
|
|
1628
|
-
),
|
|
2084
|
+
.describe("Experiment slug from platform_experiment_list (e.g. 'checkout-flow')"),
|
|
1629
2085
|
limit: z
|
|
1630
2086
|
.number()
|
|
1631
2087
|
.int()
|
|
1632
2088
|
.positive()
|
|
1633
2089
|
.max(100)
|
|
1634
2090
|
.optional()
|
|
1635
|
-
.describe(
|
|
1636
|
-
"Maximum number of runs to include (default 25, hard-capped at 100)",
|
|
1637
|
-
),
|
|
2091
|
+
.describe("Maximum number of runs to include (default 25, hard-capped at 100)"),
|
|
1638
2092
|
},
|
|
1639
2093
|
withToolLogging("platform_experiment_list_runs", async (params) => {
|
|
1640
2094
|
requireApiKey();
|
|
1641
2095
|
return {
|
|
1642
2096
|
content: [{ type: "text", text: await handleExperimentListRuns(params) }],
|
|
1643
2097
|
};
|
|
1644
|
-
})
|
|
2098
|
+
}),
|
|
1645
2099
|
);
|
|
1646
2100
|
|
|
1647
2101
|
server.tool(
|
|
1648
2102
|
"platform_experiment_results",
|
|
1649
2103
|
"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 — narrow with `filter: 'failed'` or `evaluator` to see what matters.",
|
|
1650
2104
|
{
|
|
1651
|
-
runId: z
|
|
1652
|
-
.string()
|
|
1653
|
-
.describe("The run ID returned from platform_run_experiment"),
|
|
2105
|
+
runId: z.string().describe("The run ID returned from platform_run_experiment"),
|
|
1654
2106
|
experimentSlug: z
|
|
1655
2107
|
.string()
|
|
1656
2108
|
.optional()
|
|
@@ -1680,16 +2132,15 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1680
2132
|
withToolLogging("platform_experiment_results", async (params) => {
|
|
1681
2133
|
requireApiKey();
|
|
1682
2134
|
return {
|
|
1683
|
-
content: [
|
|
1684
|
-
{ type: "text", text: await handleExperimentResults(params) },
|
|
1685
|
-
],
|
|
2135
|
+
content: [{ type: "text", text: await handleExperimentResults(params) }],
|
|
1686
2136
|
};
|
|
1687
|
-
})
|
|
2137
|
+
}),
|
|
1688
2138
|
);
|
|
2139
|
+
}
|
|
1689
2140
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
2141
|
+
// --- Platform Dataset Tools (require API key) ---
|
|
2142
|
+
// These tools manage datasets on the LangWatch platform via API.
|
|
2143
|
+
function registerDatasetTools(server: McpServer): void {
|
|
1693
2144
|
server.tool(
|
|
1694
2145
|
"platform_list_datasets",
|
|
1695
2146
|
"List all datasets on the LangWatch platform with their names, slugs, columns, and record counts. Returns AI-readable digest by default.",
|
|
@@ -1697,20 +2148,16 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1697
2148
|
format: z
|
|
1698
2149
|
.enum(["digest", "json"])
|
|
1699
2150
|
.optional()
|
|
1700
|
-
.describe(
|
|
1701
|
-
"Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"
|
|
1702
|
-
),
|
|
2151
|
+
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
1703
2152
|
},
|
|
1704
2153
|
async (params) => {
|
|
1705
|
-
const { requireApiKey } = await import("./config.
|
|
2154
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1706
2155
|
requireApiKey();
|
|
1707
|
-
const { handleListDatasets } = await import(
|
|
1708
|
-
"./tools/list-datasets.js"
|
|
1709
|
-
);
|
|
2156
|
+
const { handleListDatasets } = await import("./tools/list-datasets.ts");
|
|
1710
2157
|
return {
|
|
1711
2158
|
content: [{ type: "text", text: await handleListDatasets(params) }],
|
|
1712
2159
|
};
|
|
1713
|
-
}
|
|
2160
|
+
},
|
|
1714
2161
|
);
|
|
1715
2162
|
|
|
1716
2163
|
server.tool(
|
|
@@ -1721,18 +2168,16 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1721
2168
|
format: z
|
|
1722
2169
|
.enum(["digest", "json"])
|
|
1723
2170
|
.optional()
|
|
1724
|
-
.describe(
|
|
1725
|
-
"Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"
|
|
1726
|
-
),
|
|
2171
|
+
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
1727
2172
|
},
|
|
1728
2173
|
async (params) => {
|
|
1729
|
-
const { requireApiKey } = await import("./config.
|
|
2174
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1730
2175
|
requireApiKey();
|
|
1731
|
-
const { handleGetDataset } = await import("./tools/get-dataset.
|
|
2176
|
+
const { handleGetDataset } = await import("./tools/get-dataset.ts");
|
|
1732
2177
|
return {
|
|
1733
2178
|
content: [{ type: "text", text: await handleGetDataset(params) }],
|
|
1734
2179
|
};
|
|
1735
|
-
}
|
|
2180
|
+
},
|
|
1736
2181
|
);
|
|
1737
2182
|
|
|
1738
2183
|
server.tool(
|
|
@@ -1740,15 +2185,13 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1740
2185
|
"Create a new dataset on the LangWatch platform.",
|
|
1741
2186
|
createDatasetSchema.shape,
|
|
1742
2187
|
async (params) => {
|
|
1743
|
-
const { requireApiKey } = await import("./config.
|
|
2188
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1744
2189
|
requireApiKey();
|
|
1745
|
-
const { handleCreateDataset } = await import(
|
|
1746
|
-
"./tools/create-dataset.js"
|
|
1747
|
-
);
|
|
2190
|
+
const { handleCreateDataset } = await import("./tools/create-dataset.ts");
|
|
1748
2191
|
return {
|
|
1749
2192
|
content: [{ type: "text", text: await handleCreateDataset(params) }],
|
|
1750
2193
|
};
|
|
1751
|
-
}
|
|
2194
|
+
},
|
|
1752
2195
|
);
|
|
1753
2196
|
|
|
1754
2197
|
server.tool(
|
|
@@ -1763,15 +2206,13 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1763
2206
|
.describe("Updated column definitions"),
|
|
1764
2207
|
},
|
|
1765
2208
|
async (params) => {
|
|
1766
|
-
const { requireApiKey } = await import("./config.
|
|
2209
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1767
2210
|
requireApiKey();
|
|
1768
|
-
const { handleUpdateDataset } = await import(
|
|
1769
|
-
"./tools/update-dataset.js"
|
|
1770
|
-
);
|
|
2211
|
+
const { handleUpdateDataset } = await import("./tools/update-dataset.ts");
|
|
1771
2212
|
return {
|
|
1772
2213
|
content: [{ type: "text", text: await handleUpdateDataset(params) }],
|
|
1773
2214
|
};
|
|
1774
|
-
}
|
|
2215
|
+
},
|
|
1775
2216
|
);
|
|
1776
2217
|
|
|
1777
2218
|
server.tool(
|
|
@@ -1781,30 +2222,21 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1781
2222
|
slugOrId: z.string().describe("The dataset slug or ID to delete"),
|
|
1782
2223
|
},
|
|
1783
2224
|
async (params) => {
|
|
1784
|
-
const { requireApiKey } = await import("./config.
|
|
2225
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1785
2226
|
requireApiKey();
|
|
1786
|
-
const { handleDeleteDataset } = await import(
|
|
1787
|
-
"./tools/delete-dataset.js"
|
|
1788
|
-
);
|
|
2227
|
+
const { handleDeleteDataset } = await import("./tools/delete-dataset.ts");
|
|
1789
2228
|
return {
|
|
1790
2229
|
content: [{ type: "text", text: await handleDeleteDataset(params) }],
|
|
1791
2230
|
};
|
|
1792
|
-
}
|
|
2231
|
+
},
|
|
1793
2232
|
);
|
|
1794
2233
|
|
|
1795
2234
|
server.tool(
|
|
1796
2235
|
"platform_list_dataset_records",
|
|
1797
2236
|
"List records in a dataset on the LangWatch platform with pagination.",
|
|
1798
2237
|
{
|
|
1799
|
-
slugOrId: z
|
|
1800
|
-
|
|
1801
|
-
.describe("The dataset slug or ID to list records from"),
|
|
1802
|
-
page: z
|
|
1803
|
-
.number()
|
|
1804
|
-
.int()
|
|
1805
|
-
.min(1)
|
|
1806
|
-
.optional()
|
|
1807
|
-
.describe("Page number (default: 1)"),
|
|
2238
|
+
slugOrId: z.string().describe("The dataset slug or ID to list records from"),
|
|
2239
|
+
page: z.number().int().min(1).optional().describe("Page number (default: 1)"),
|
|
1808
2240
|
limit: z
|
|
1809
2241
|
.number()
|
|
1810
2242
|
.int()
|
|
@@ -1818,26 +2250,20 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1818
2250
|
.describe("Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"),
|
|
1819
2251
|
},
|
|
1820
2252
|
async (params) => {
|
|
1821
|
-
const { requireApiKey } = await import("./config.
|
|
2253
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1822
2254
|
requireApiKey();
|
|
1823
|
-
const { handleListDatasetRecords } = await import(
|
|
1824
|
-
"./tools/list-dataset-records.js"
|
|
1825
|
-
);
|
|
2255
|
+
const { handleListDatasetRecords } = await import("./tools/list-dataset-records.ts");
|
|
1826
2256
|
return {
|
|
1827
|
-
content: [
|
|
1828
|
-
{ type: "text", text: await handleListDatasetRecords(params) },
|
|
1829
|
-
],
|
|
2257
|
+
content: [{ type: "text", text: await handleListDatasetRecords(params) }],
|
|
1830
2258
|
};
|
|
1831
|
-
}
|
|
2259
|
+
},
|
|
1832
2260
|
);
|
|
1833
2261
|
|
|
1834
2262
|
server.tool(
|
|
1835
2263
|
"platform_create_dataset_records",
|
|
1836
2264
|
"Add records to a dataset on the LangWatch platform in batch (max 1000 per call).",
|
|
1837
2265
|
{
|
|
1838
|
-
slugOrId: z
|
|
1839
|
-
.string()
|
|
1840
|
-
.describe("The dataset slug or ID to add records to"),
|
|
2266
|
+
slugOrId: z.string().describe("The dataset slug or ID to add records to"),
|
|
1841
2267
|
entries: z
|
|
1842
2268
|
.array(z.record(z.string(), z.unknown()))
|
|
1843
2269
|
.min(1)
|
|
@@ -1845,90 +2271,75 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1845
2271
|
.describe("Array of record entries to create (key-value objects matching dataset columns)"),
|
|
1846
2272
|
},
|
|
1847
2273
|
async (params) => {
|
|
1848
|
-
const { requireApiKey } = await import("./config.
|
|
2274
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1849
2275
|
requireApiKey();
|
|
1850
|
-
const { handleCreateDatasetRecords } = await import(
|
|
1851
|
-
"./tools/create-dataset-records.js"
|
|
1852
|
-
);
|
|
2276
|
+
const { handleCreateDatasetRecords } = await import("./tools/create-dataset-records.ts");
|
|
1853
2277
|
return {
|
|
1854
|
-
content: [
|
|
1855
|
-
{ type: "text", text: await handleCreateDatasetRecords(params) },
|
|
1856
|
-
],
|
|
2278
|
+
content: [{ type: "text", text: await handleCreateDatasetRecords(params) }],
|
|
1857
2279
|
};
|
|
1858
|
-
}
|
|
2280
|
+
},
|
|
1859
2281
|
);
|
|
1860
2282
|
|
|
1861
2283
|
server.tool(
|
|
1862
2284
|
"platform_update_dataset_record",
|
|
1863
2285
|
"Update a single record in a dataset on the LangWatch platform.",
|
|
1864
2286
|
{
|
|
1865
|
-
slugOrId: z
|
|
1866
|
-
.string()
|
|
1867
|
-
.describe("The dataset slug or ID containing the record"),
|
|
2287
|
+
slugOrId: z.string().describe("The dataset slug or ID containing the record"),
|
|
1868
2288
|
recordId: z.string().describe("The record ID to update"),
|
|
1869
|
-
entry: z
|
|
1870
|
-
.record(z.string(), z.unknown())
|
|
1871
|
-
.describe("Updated record entry (key-value object)"),
|
|
2289
|
+
entry: z.record(z.string(), z.unknown()).describe("Updated record entry (key-value object)"),
|
|
1872
2290
|
},
|
|
1873
2291
|
async (params) => {
|
|
1874
|
-
const { requireApiKey } = await import("./config.
|
|
2292
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1875
2293
|
requireApiKey();
|
|
1876
|
-
const { handleUpdateDatasetRecord } = await import(
|
|
1877
|
-
"./tools/update-dataset-record.js"
|
|
1878
|
-
);
|
|
2294
|
+
const { handleUpdateDatasetRecord } = await import("./tools/update-dataset-record.ts");
|
|
1879
2295
|
return {
|
|
1880
|
-
content: [
|
|
1881
|
-
{ type: "text", text: await handleUpdateDatasetRecord(params) },
|
|
1882
|
-
],
|
|
2296
|
+
content: [{ type: "text", text: await handleUpdateDatasetRecord(params) }],
|
|
1883
2297
|
};
|
|
1884
|
-
}
|
|
2298
|
+
},
|
|
1885
2299
|
);
|
|
1886
2300
|
|
|
1887
2301
|
server.tool(
|
|
1888
2302
|
"platform_delete_dataset_records",
|
|
1889
2303
|
"Delete records from a dataset on the LangWatch platform by their IDs.",
|
|
1890
2304
|
{
|
|
1891
|
-
slugOrId: z
|
|
1892
|
-
|
|
1893
|
-
.describe("The dataset slug or ID containing the records"),
|
|
1894
|
-
recordIds: z
|
|
1895
|
-
.array(z.string())
|
|
1896
|
-
.min(1)
|
|
1897
|
-
.max(1000)
|
|
1898
|
-
.describe("Array of record IDs to delete"),
|
|
2305
|
+
slugOrId: z.string().describe("The dataset slug or ID containing the records"),
|
|
2306
|
+
recordIds: z.array(z.string()).min(1).max(1000).describe("Array of record IDs to delete"),
|
|
1899
2307
|
},
|
|
1900
2308
|
async (params) => {
|
|
1901
|
-
const { requireApiKey } = await import("./config.
|
|
2309
|
+
const { requireApiKey } = await import("./config.ts");
|
|
1902
2310
|
requireApiKey();
|
|
1903
|
-
const { handleDeleteDatasetRecords } = await import(
|
|
1904
|
-
"./tools/delete-dataset-records.js"
|
|
1905
|
-
);
|
|
2311
|
+
const { handleDeleteDatasetRecords } = await import("./tools/delete-dataset-records.ts");
|
|
1906
2312
|
return {
|
|
1907
|
-
content: [
|
|
1908
|
-
{ type: "text", text: await handleDeleteDatasetRecords(params) },
|
|
1909
|
-
],
|
|
2313
|
+
content: [{ type: "text", text: await handleDeleteDatasetRecords(params) }],
|
|
1910
2314
|
};
|
|
1911
|
-
}
|
|
2315
|
+
},
|
|
1912
2316
|
);
|
|
2317
|
+
}
|
|
1913
2318
|
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
2319
|
+
// --- Platform Project Tools ---
|
|
2320
|
+
// These tools manage projects on the LangWatch platform via the REST API.
|
|
2321
|
+
// They require an API key with org-level permissions.
|
|
2322
|
+
function registerProjectTools(server: McpServer): void {
|
|
1918
2323
|
server.tool(
|
|
1919
2324
|
"platform_list_projects",
|
|
1920
2325
|
"List all projects in your LangWatch organization. Requires an org-level API key.",
|
|
1921
2326
|
{
|
|
1922
2327
|
page: z.number().int().positive().optional().describe("Page number (default: 1)"),
|
|
1923
|
-
limit: z
|
|
2328
|
+
limit: z
|
|
2329
|
+
.number()
|
|
2330
|
+
.int()
|
|
2331
|
+
.positive()
|
|
2332
|
+
.max(1000)
|
|
2333
|
+
.optional()
|
|
2334
|
+
.describe("Results per page (default: 100)"),
|
|
1924
2335
|
},
|
|
1925
2336
|
withToolLogging("platform_list_projects", async (params) => {
|
|
1926
2337
|
requireApiKey();
|
|
1927
|
-
const { handleListProjects } = await import("./tools/list-projects.
|
|
2338
|
+
const { handleListProjects } = await import("./tools/list-projects.ts");
|
|
1928
2339
|
return {
|
|
1929
2340
|
content: [{ type: "text", text: await handleListProjects(params) }],
|
|
1930
2341
|
};
|
|
1931
|
-
})
|
|
2342
|
+
}),
|
|
1932
2343
|
);
|
|
1933
2344
|
|
|
1934
2345
|
server.tool(
|
|
@@ -1939,11 +2350,11 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
|
|
|
1939
2350
|
},
|
|
1940
2351
|
withToolLogging("platform_get_project", async (params) => {
|
|
1941
2352
|
requireApiKey();
|
|
1942
|
-
const { handleGetProject } = await import("./tools/get-project.
|
|
2353
|
+
const { handleGetProject } = await import("./tools/get-project.ts");
|
|
1943
2354
|
return {
|
|
1944
2355
|
content: [{ type: "text", text: await handleGetProject(params) }],
|
|
1945
2356
|
};
|
|
1946
|
-
})
|
|
2357
|
+
}),
|
|
1947
2358
|
);
|
|
1948
2359
|
|
|
1949
2360
|
server.tool(
|
|
@@ -1960,11 +2371,11 @@ You must provide either teamId (to add the project to an existing team) or newTe
|
|
|
1960
2371
|
},
|
|
1961
2372
|
withToolLogging("platform_create_project", async (params) => {
|
|
1962
2373
|
requireApiKey();
|
|
1963
|
-
const { handleCreateProject } = await import("./tools/create-project.
|
|
2374
|
+
const { handleCreateProject } = await import("./tools/create-project.ts");
|
|
1964
2375
|
return {
|
|
1965
2376
|
content: [{ type: "text", text: await handleCreateProject(params) }],
|
|
1966
2377
|
};
|
|
1967
|
-
})
|
|
2378
|
+
}),
|
|
1968
2379
|
);
|
|
1969
2380
|
|
|
1970
2381
|
server.tool(
|
|
@@ -1975,15 +2386,18 @@ You must provide either teamId (to add the project to an existing team) or newTe
|
|
|
1975
2386
|
name: z.string().optional().describe("New project name"),
|
|
1976
2387
|
language: z.string().optional().describe("New programming language"),
|
|
1977
2388
|
framework: z.string().optional().describe("New framework"),
|
|
1978
|
-
piiRedactionLevel: z
|
|
2389
|
+
piiRedactionLevel: z
|
|
2390
|
+
.enum(["STRICT", "ESSENTIAL", "DISABLED"])
|
|
2391
|
+
.optional()
|
|
2392
|
+
.describe("PII redaction level"),
|
|
1979
2393
|
},
|
|
1980
2394
|
withToolLogging("platform_update_project", async (params) => {
|
|
1981
2395
|
requireApiKey();
|
|
1982
|
-
const { handleUpdateProject } = await import("./tools/update-project.
|
|
2396
|
+
const { handleUpdateProject } = await import("./tools/update-project.ts");
|
|
1983
2397
|
return {
|
|
1984
2398
|
content: [{ type: "text", text: await handleUpdateProject(params) }],
|
|
1985
2399
|
};
|
|
1986
|
-
})
|
|
2400
|
+
}),
|
|
1987
2401
|
);
|
|
1988
2402
|
|
|
1989
2403
|
server.tool(
|
|
@@ -1994,27 +2408,28 @@ You must provide either teamId (to add the project to an existing team) or newTe
|
|
|
1994
2408
|
},
|
|
1995
2409
|
withToolLogging("platform_archive_project", async (params) => {
|
|
1996
2410
|
requireApiKey();
|
|
1997
|
-
const { handleArchiveProject } = await import("./tools/archive-project.
|
|
2411
|
+
const { handleArchiveProject } = await import("./tools/archive-project.ts");
|
|
1998
2412
|
return {
|
|
1999
2413
|
content: [{ type: "text", text: await handleArchiveProject(params) }],
|
|
2000
2414
|
};
|
|
2001
|
-
})
|
|
2415
|
+
}),
|
|
2002
2416
|
);
|
|
2417
|
+
}
|
|
2003
2418
|
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2419
|
+
// --- Platform API Key Tools (require org-level API key) ---
|
|
2420
|
+
// These tools manage API keys on the LangWatch platform via the REST API.
|
|
2421
|
+
function registerApiKeyTools(server: McpServer): void {
|
|
2007
2422
|
server.tool(
|
|
2008
2423
|
"platform_list_api_keys",
|
|
2009
2424
|
"List all API keys in your LangWatch organization. Shows key metadata, status (active/revoked/expired), and role bindings. API key tokens are never returned.",
|
|
2010
2425
|
{},
|
|
2011
2426
|
withToolLogging("platform_list_api_keys", async () => {
|
|
2012
2427
|
requireApiKey();
|
|
2013
|
-
const { handleListApiKeys } = await import("./tools/list-api-keys.
|
|
2428
|
+
const { handleListApiKeys } = await import("./tools/list-api-keys.ts");
|
|
2014
2429
|
return {
|
|
2015
2430
|
content: [{ type: "text", text: await handleListApiKeys() }],
|
|
2016
2431
|
};
|
|
2017
|
-
})
|
|
2432
|
+
}),
|
|
2018
2433
|
);
|
|
2019
2434
|
|
|
2020
2435
|
server.tool(
|
|
@@ -2026,24 +2441,34 @@ You must provide either teamId (to add the project to an existing team) or newTe
|
|
|
2026
2441
|
|
|
2027
2442
|
The token is returned once and cannot be retrieved again.`,
|
|
2028
2443
|
{
|
|
2029
|
-
keyType: z
|
|
2444
|
+
keyType: z
|
|
2445
|
+
.enum(["personal", "service"])
|
|
2446
|
+
.describe("Key type: 'personal' (user-tied) or 'service' (headless)"),
|
|
2030
2447
|
name: z.string().describe("Key name (max 100 chars)"),
|
|
2031
2448
|
description: z.string().optional().describe("Key description (max 500 chars)"),
|
|
2032
2449
|
expiresAt: z.string().optional().describe("Expiration date (ISO string)"),
|
|
2033
|
-
bindings: z
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2450
|
+
bindings: z
|
|
2451
|
+
.array(
|
|
2452
|
+
z.object({
|
|
2453
|
+
role: z.enum(["ADMIN", "MEMBER", "VIEWER"]).describe("Role"),
|
|
2454
|
+
scopeType: z.enum(["ORGANIZATION", "TEAM", "PROJECT"]).describe("Scope type"),
|
|
2455
|
+
scopeId: z.string().describe("Scope ID (org, team, or project ID)"),
|
|
2456
|
+
}),
|
|
2457
|
+
)
|
|
2458
|
+
.optional()
|
|
2459
|
+
.describe("Role bindings (required for personal keys)"),
|
|
2460
|
+
projectIds: z
|
|
2461
|
+
.array(z.string())
|
|
2462
|
+
.optional()
|
|
2463
|
+
.describe("Project IDs for service keys (creates ADMIN bindings per project)"),
|
|
2039
2464
|
},
|
|
2040
2465
|
withToolLogging("platform_create_api_key", async (params) => {
|
|
2041
2466
|
requireApiKey();
|
|
2042
|
-
const { handleCreateApiKey } = await import("./tools/create-api-key.
|
|
2467
|
+
const { handleCreateApiKey } = await import("./tools/create-api-key.ts");
|
|
2043
2468
|
return {
|
|
2044
2469
|
content: [{ type: "text", text: await handleCreateApiKey(params) }],
|
|
2045
2470
|
};
|
|
2046
|
-
})
|
|
2471
|
+
}),
|
|
2047
2472
|
);
|
|
2048
2473
|
|
|
2049
2474
|
server.tool(
|
|
@@ -2054,10 +2479,10 @@ The token is returned once and cannot be retrieved again.`,
|
|
|
2054
2479
|
},
|
|
2055
2480
|
withToolLogging("platform_revoke_api_key", async (params) => {
|
|
2056
2481
|
requireApiKey();
|
|
2057
|
-
const { handleRevokeApiKey } = await import("./tools/revoke-api-key.
|
|
2482
|
+
const { handleRevokeApiKey } = await import("./tools/revoke-api-key.ts");
|
|
2058
2483
|
return {
|
|
2059
2484
|
content: [{ type: "text", text: await handleRevokeApiKey(params) }],
|
|
2060
2485
|
};
|
|
2061
|
-
})
|
|
2486
|
+
}),
|
|
2062
2487
|
);
|
|
2063
2488
|
}
|