@langwatch/mcp-server 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +7 -0
  3. package/dist/archive-project-HZBRPR2S.js +19 -0
  4. package/dist/{archive-scenario-YKGLJ4MZ.js → archive-scenario-P5RRITRM.js} +3 -3
  5. package/dist/{archive-suite-QFNDO3Y3.js → archive-suite-NUUFD2EM.js} +3 -3
  6. package/dist/{assign-prompt-tag-Y2B4CBBT.js → assign-prompt-tag-FKCCZPML.js} +2 -2
  7. package/dist/{chunk-YH2IBTEL.js → chunk-42ZHHHIJ.js} +14 -3
  8. package/dist/chunk-ADSJ2FDA.js +44 -0
  9. package/dist/{chunk-L6QENXYA.js → chunk-BB7EOKHR.js} +2 -2
  10. package/dist/chunk-BBJ5WAJB.js +24 -0
  11. package/dist/{chunk-PZUWCWOS.js → chunk-BYWYM2RP.js} +2 -2
  12. package/dist/{chunk-NACSR6B7.js → chunk-CCM4ZFGF.js} +2 -2
  13. package/dist/{chunk-FBRKHHJV.js → chunk-CHKCEZBP.js} +2 -2
  14. package/dist/{chunk-PO7FM7IX.js → chunk-HKY3GW45.js} +2 -2
  15. package/dist/{chunk-KZNBCKWG.js → chunk-JHOSVOHT.js} +2 -2
  16. package/dist/{chunk-B57MZ4WI.js → chunk-JVLJKQJG.js} +657 -88
  17. package/dist/{chunk-HDJDFKZR.js → chunk-R7C7OUVG.js} +2 -2
  18. package/dist/{chunk-OVQF3D5R.js → chunk-TROIWSEQ.js} +2 -2
  19. package/dist/{chunk-CPUPGDNA.js → chunk-X2SEURT2.js} +2 -2
  20. package/dist/{chunk-YI3OG4PI.js → chunk-XC62KJIT.js} +3 -3
  21. package/dist/{create-agent-YRSDH7P7.js → create-agent-K5Q7BFGZ.js} +3 -3
  22. package/dist/{create-annotation-FYQNKMVJ.js → create-annotation-KN3GE3DK.js} +3 -3
  23. package/dist/create-api-key-OQI5PUNQ.js +26 -0
  24. package/dist/{create-dashboard-CORBR4YQ.js → create-dashboard-G4QA5AXP.js} +3 -3
  25. package/dist/{create-dataset-ZC63T6BB.js → create-dataset-WQSC37N7.js} +3 -3
  26. package/dist/{create-dataset-records-ILVNVKVR.js → create-dataset-records-B4RL3FLC.js} +3 -3
  27. package/dist/{create-evaluator-2AU3PXFB.js → create-evaluator-YFFQIR7E.js} +3 -3
  28. package/dist/create-mcp-server.js +2 -1
  29. package/dist/create-project-CUVQ67VN.js +44 -0
  30. package/dist/{create-prompt-NKRJ47YY.js → create-prompt-NGXKSP4X.js} +2 -2
  31. package/dist/{create-prompt-tag-S4JX3ULL.js → create-prompt-tag-IUKYPVHH.js} +2 -2
  32. package/dist/{create-scenario-BPRF75KF.js → create-scenario-FNBXALH3.js} +3 -3
  33. package/dist/{create-suite-4YN7D6K2.js → create-suite-DJEPD4ZB.js} +3 -3
  34. package/dist/{delete-agent-Y72R2YQA.js → delete-agent-TJI5XBBM.js} +3 -3
  35. package/dist/{delete-annotation-UHWDMHSH.js → delete-annotation-KRP53TUU.js} +3 -3
  36. package/dist/{delete-dashboard-LPWT36KN.js → delete-dashboard-N5SZJIR3.js} +3 -3
  37. package/dist/{delete-dataset-GGPQRHKX.js → delete-dataset-KDUOHL2P.js} +3 -3
  38. package/dist/{delete-dataset-records-LTG5LT26.js → delete-dataset-records-DM4W3PAW.js} +3 -3
  39. package/dist/{delete-evaluator-F7QLXDHU.js → delete-evaluator-7ZATLJOA.js} +3 -3
  40. package/dist/{delete-prompt-tag-B5UKZL45.js → delete-prompt-tag-I6HNMSVJ.js} +2 -2
  41. package/dist/{delete-workflow-4DGUDIDE.js → delete-workflow-QEO74JQR.js} +3 -3
  42. package/dist/{get-agent-YEVICUX4.js → get-agent-3KDWCP5N.js} +3 -3
  43. package/dist/{get-analytics-3W5CMU3F.js → get-analytics-GPVJX7SF.js} +2 -2
  44. package/dist/{get-dashboard-NCQHN6MW.js → get-dashboard-YPTIE5YY.js} +3 -3
  45. package/dist/{get-dataset-KBK6PH36.js → get-dataset-6DQFYITM.js} +3 -3
  46. package/dist/{get-evaluator-QDUL5LXR.js → get-evaluator-PCM6XQAU.js} +3 -3
  47. package/dist/get-project-JCMWXVLU.js +26 -0
  48. package/dist/{get-prompt-XCSSNRKR.js → get-prompt-5XAGWWBG.js} +2 -2
  49. package/dist/{get-scenario-FNRKOY7E.js → get-scenario-7XBCDZWX.js} +3 -3
  50. package/dist/{get-simulation-run-WOEKUF7K.js → get-simulation-run-AV24FJWI.js} +3 -3
  51. package/dist/{get-suite-U5AOD5TC.js → get-suite-4DBVHFEM.js} +3 -3
  52. package/dist/{get-trace-MUWEJM2Z.js → get-trace-MRBDS3HJ.js} +2 -2
  53. package/dist/{get-workflow-SOA7MT7L.js → get-workflow-UR4Y6UFX.js} +3 -3
  54. package/dist/{http-server-7DMOCB75.js → http-server-DEGNV5EP.js} +3 -2
  55. package/dist/index.js +1060 -220
  56. package/dist/{langwatch-api-annotations-Q57NDUVR.js → langwatch-api-annotations-ZJDRB4JH.js} +3 -3
  57. package/dist/{langwatch-api-dashboards-VBWWXV3Y.js → langwatch-api-dashboards-WARLI6NF.js} +3 -3
  58. package/dist/{langwatch-api-monitors-L3IY7FDT.js → langwatch-api-monitors-4XKXHYCG.js} +2 -2
  59. package/dist/{langwatch-api-secrets-S5HHF3XR.js → langwatch-api-secrets-6SHK5G7Q.js} +2 -2
  60. package/dist/{langwatch-api-triggers-ME33QE26.js → langwatch-api-triggers-5SX6R2WU.js} +2 -2
  61. package/dist/{langwatch-api-workflows-4CJQAYP2.js → langwatch-api-workflows-NLJENNXE.js} +3 -3
  62. package/dist/{list-agents-3HGIBJFM.js → list-agents-NECO7MPI.js} +3 -3
  63. package/dist/{list-annotations-KWXYVEPI.js → list-annotations-3OJEO774.js} +3 -3
  64. package/dist/list-api-keys-AWW7ROLB.js +37 -0
  65. package/dist/{list-dashboards-VQ2LH666.js → list-dashboards-DVPVVVKW.js} +3 -3
  66. package/dist/{list-dataset-records-RNRGROJP.js → list-dataset-records-I4BYORF5.js} +3 -3
  67. package/dist/{list-datasets-I4A2LT2S.js → list-datasets-WUAE5HGX.js} +3 -3
  68. package/dist/{list-evaluators-AKKRJ5MX.js → list-evaluators-B6OSVAM2.js} +3 -3
  69. package/dist/{list-model-providers-DVFEHFHZ.js → list-model-providers-NL6IP4UZ.js} +3 -3
  70. package/dist/list-projects-5FHMJ4PQ.js +42 -0
  71. package/dist/{list-prompt-tags-PL5OISUA.js → list-prompt-tags-Z2DD6FLQ.js} +2 -2
  72. package/dist/{list-prompts-7VTFWE2Z.js → list-prompts-VNXKHK7T.js} +2 -2
  73. package/dist/{list-scenarios-5BZWZ6ZN.js → list-scenarios-CFZHXL76.js} +3 -3
  74. package/dist/{list-simulation-runs-WCZ6RLPE.js → list-simulation-runs-AM2BXLBW.js} +3 -3
  75. package/dist/{list-suites-RRQ2JX5Y.js → list-suites-ZDOHAAXF.js} +3 -3
  76. package/dist/{list-workflows-UQB5FCX5.js → list-workflows-WBRBKLIT.js} +3 -3
  77. package/dist/{rename-prompt-tag-TURD6KYU.js → rename-prompt-tag-A4RJUK7Z.js} +2 -2
  78. package/dist/revoke-api-key-ZSPSPF5G.js +15 -0
  79. package/dist/{run-agent-BEJ4K4CM.js → run-agent-JFNDY7MR.js} +3 -3
  80. package/dist/{run-suite-U7V432G2.js → run-suite-TH7LO5TJ.js} +3 -3
  81. package/dist/{run-workflow-5WX7DK5T.js → run-workflow-CK3JP5U2.js} +3 -3
  82. package/dist/{search-traces-ZCHNOMTX.js → search-traces-3BBVYPC3.js} +2 -2
  83. package/dist/{set-model-provider-6CFHMCNE.js → set-model-provider-WOC5FNMV.js} +3 -3
  84. package/dist/{update-agent-UE6UWLZP.js → update-agent-YMEV5MTL.js} +3 -3
  85. package/dist/{update-dataset-4XQZZE4S.js → update-dataset-EYT5POFM.js} +3 -3
  86. package/dist/{update-dataset-record-GNNM5TKM.js → update-dataset-record-VFF5JTX4.js} +3 -3
  87. package/dist/{update-evaluator-LHVDXIZD.js → update-evaluator-6IJHFG2E.js} +3 -3
  88. package/dist/update-project-OIT4WPFI.js +24 -0
  89. package/dist/{update-prompt-3XEHECJ7.js → update-prompt-ZU2DD6KM.js} +2 -2
  90. package/dist/{update-scenario-DFPREYCD.js → update-scenario-3VUTPDRU.js} +3 -3
  91. package/dist/{update-suite-RATU5THO.js → update-suite-C6JFDQHL.js} +3 -3
  92. package/package.json +2 -2
  93. package/pnpm-workspace.yaml +2 -0
  94. package/pyproject.toml +3 -0
  95. package/src/__tests__/experiment-list-tools.unit.test.ts +157 -0
  96. package/src/__tests__/get-experiment-results.unit.test.ts +222 -0
  97. package/src/__tests__/project-api-key-tools.unit.test.ts +451 -0
  98. package/src/create-mcp-server.ts +257 -13
  99. package/src/langwatch-api-api-keys.ts +54 -0
  100. package/src/langwatch-api-projects.ts +82 -0
  101. package/src/langwatch-api.ts +17 -3
  102. package/src/tools/archive-project.ts +14 -0
  103. package/src/tools/create-api-key.ts +29 -0
  104. package/src/tools/create-project.ts +45 -0
  105. package/src/tools/get-experiment-results.ts +273 -0
  106. package/src/tools/get-project.ts +20 -0
  107. package/src/tools/list-api-keys.ts +33 -0
  108. package/src/tools/list-experiment-runs.ts +142 -0
  109. package/src/tools/list-experiments.ts +95 -0
  110. package/src/tools/list-projects.ts +41 -0
  111. package/src/tools/revoke-api-key.ts +9 -0
  112. package/src/tools/{run-evaluation.ts → run-experiment.ts} +5 -5
  113. package/src/tools/update-project.ts +22 -0
  114. package/uv.lock +7 -3
  115. package/dist/run-evaluation-AJ7WZNQD.js +0 -64
@@ -1,3 +1,7 @@
1
+ import {
2
+ LangWatchApiError,
3
+ makeRequest
4
+ } from "./chunk-42ZHHHIJ.js";
1
5
  import {
2
6
  requireApiKey
3
7
  } from "./chunk-KOO5733S.js";
@@ -9,7 +13,7 @@ import { z as z2 } from "zod";
9
13
  // package.json
10
14
  var package_default = {
11
15
  name: "@langwatch/mcp-server",
12
- version: "0.8.0",
16
+ version: "0.9.0",
13
17
  description: "An MCP server for Langwatch.",
14
18
  type: "module",
15
19
  main: "./dist/index.js",
@@ -60,7 +64,7 @@ var package_default = {
60
64
  "@types/node": "^25.6.0",
61
65
  "@types/yargs": "^17.0.33",
62
66
  "@typescript/native-preview": "7.0.0-dev.20250911.1",
63
- ai: "^6.0.0",
67
+ ai: "^6.0.161",
64
68
  dotenv: "^17.2.2",
65
69
  esbuild: "^0.25.0",
66
70
  eslint: "^10.2.1",
@@ -88,6 +92,7 @@ var package_default = {
88
92
  pnpm: {
89
93
  overrides: {
90
94
  protobufjs: ">=7.5.6 <8.0.0 || >=8.0.2",
95
+ "langsmith@<0.6.0": "0.6.0",
91
96
  "fast-uri@<3.1.2": "3.1.2"
92
97
  }
93
98
  }
@@ -119,6 +124,379 @@ var createDatasetSchema = z.object({
119
124
  columnTypes: z.array(datasetColumnDefinitionSchema).optional().describe("Column definitions for the dataset")
120
125
  });
121
126
 
127
+ // src/tools/get-experiment-results.ts
128
+ var DEFAULT_ROW_CAP = 50;
129
+ var rowKey = (index, targetId) => `${index}:${targetId ?? ""}`;
130
+ var summarizeEntry = (entry) => {
131
+ const candidates = ["input", "question", "query", "prompt", "user"];
132
+ for (const key of candidates) {
133
+ const value = entry[key];
134
+ if (typeof value === "string" && value.length > 0) {
135
+ return value.length > 80 ? `${value.slice(0, 77)}...` : value;
136
+ }
137
+ }
138
+ return "";
139
+ };
140
+ var isFailedEvaluation = (e) => e.status === "error" || e.passed === false;
141
+ var isFailedRow = ({
142
+ entry,
143
+ evaluations
144
+ }) => Boolean(entry.error) || evaluations.some(isFailedEvaluation);
145
+ async function handleExperimentResults(params) {
146
+ const filter = params.filter ?? "all";
147
+ const evaluatorFilter = params.evaluator?.trim();
148
+ const limit = typeof params.limit === "number" && params.limit > 0 ? Math.min(params.limit, DEFAULT_ROW_CAP) : DEFAULT_ROW_CAP;
149
+ const search = new URLSearchParams();
150
+ if (params.experimentSlug) search.set("experimentSlug", params.experimentSlug);
151
+ const qs = search.toString() ? `?${search.toString()}` : "";
152
+ let results;
153
+ try {
154
+ results = await makeRequest(
155
+ "GET",
156
+ `/api/experiments/runs/${encodeURIComponent(params.runId)}/results${qs}`
157
+ );
158
+ } catch (error) {
159
+ const message = error instanceof Error ? error.message : String(error);
160
+ const status = error instanceof LangWatchApiError ? error.status : error && typeof error === "object" && "status" in error && typeof error.status === "number" ? error.status : void 0;
161
+ if (status === 404 || status === void 0 && /404|not found/i.test(message)) {
162
+ return [
163
+ `# Evaluation Results: ${params.runId}`,
164
+ "",
165
+ "**Status**: not found",
166
+ "",
167
+ `Could not load results for run \`${params.runId}\`. The run may still be in progress, or the id may be incorrect.`,
168
+ "",
169
+ "> Try `platform_experiment_status` first to confirm the run id and that it is completed."
170
+ ].join("\n");
171
+ }
172
+ throw error;
173
+ }
174
+ if (!results || results.timestamps.finishedAt == null && results.timestamps.stoppedAt == null) {
175
+ return [
176
+ `# Evaluation Results: ${params.runId}`,
177
+ "",
178
+ "**Status**: results are not yet available",
179
+ "",
180
+ `Results for run \`${params.runId}\` are not ready yet.`,
181
+ "",
182
+ "> Try `platform_experiment_status` first to confirm the run id and that it is completed."
183
+ ].join("\n");
184
+ }
185
+ const evaluationsByRow = /* @__PURE__ */ new Map();
186
+ for (const evaluation of results.evaluations) {
187
+ if (evaluatorFilter && evaluation.evaluator !== evaluatorFilter) continue;
188
+ const key = rowKey(evaluation.index, evaluation.targetId);
189
+ const list = evaluationsByRow.get(key) ?? [];
190
+ list.push(evaluation);
191
+ evaluationsByRow.set(key, list);
192
+ }
193
+ const evaluatorNames = Array.from(
194
+ new Set(
195
+ (evaluatorFilter ? results.evaluations.filter((e) => e.evaluator === evaluatorFilter) : results.evaluations).map((e) => e.evaluator)
196
+ )
197
+ );
198
+ let rows = results.dataset.map((entry) => ({
199
+ entry,
200
+ evaluations: evaluationsByRow.get(rowKey(entry.index, entry.targetId)) ?? []
201
+ }));
202
+ if (filter === "failed") {
203
+ rows = rows.filter(
204
+ (r) => isFailedRow({ entry: r.entry, evaluations: r.evaluations })
205
+ );
206
+ }
207
+ const totalMatching = rows.length;
208
+ const rowsForSummary = rows;
209
+ const truncated = rows.length > limit;
210
+ rows = rows.slice(0, limit);
211
+ const evaluatorAverages = /* @__PURE__ */ new Map();
212
+ for (const r of rowsForSummary) {
213
+ for (const e of r.evaluations) {
214
+ const stats = evaluatorAverages.get(e.evaluator) ?? {
215
+ sum: 0,
216
+ count: 0,
217
+ passed: 0,
218
+ failed: 0,
219
+ errored: 0
220
+ };
221
+ if (typeof e.score === "number") {
222
+ stats.sum += e.score;
223
+ stats.count += 1;
224
+ }
225
+ if (e.status === "error") stats.errored += 1;
226
+ else if (e.passed === true) stats.passed += 1;
227
+ else if (e.passed === false) stats.failed += 1;
228
+ evaluatorAverages.set(e.evaluator, stats);
229
+ }
230
+ }
231
+ const lines = [];
232
+ lines.push(`# Evaluation Results: ${results.runId}`);
233
+ lines.push("");
234
+ lines.push(`**Experiment**: ${results.experimentId}`);
235
+ lines.push(`**Total rows**: ${results.dataset.length}`);
236
+ lines.push(`**Total evaluations**: ${results.evaluations.length}`);
237
+ if (filter === "failed") {
238
+ lines.push(`**Filter**: failed only`);
239
+ }
240
+ if (evaluatorFilter) {
241
+ lines.push(`**Evaluator filter**: ${evaluatorFilter}`);
242
+ }
243
+ lines.push("");
244
+ if (evaluatorAverages.size > 0) {
245
+ lines.push("## Evaluator Summary");
246
+ lines.push("");
247
+ lines.push("| Evaluator | Avg Score | Passed | Failed | Errored |");
248
+ lines.push("| --- | --- | --- | --- | --- |");
249
+ for (const [name, stats] of evaluatorAverages) {
250
+ const avg = stats.count > 0 ? (stats.sum / stats.count).toFixed(3) : "\u2014";
251
+ lines.push(
252
+ `| ${name} | ${avg} | ${stats.passed} | ${stats.failed} | ${stats.errored} |`
253
+ );
254
+ }
255
+ lines.push("");
256
+ }
257
+ if (rows.length === 0) {
258
+ lines.push("_No rows matched the filter._");
259
+ return lines.join("\n");
260
+ }
261
+ lines.push(
262
+ `## Rows (${rows.length}${truncated ? ` of ${totalMatching}` : ""})`
263
+ );
264
+ lines.push("");
265
+ for (const { entry, evaluations } of rows) {
266
+ const summary = summarizeEntry(entry.entry);
267
+ lines.push(`### Row #${entry.index}${summary ? ` \u2014 ${summary}` : ""}`);
268
+ if (entry.error) {
269
+ lines.push(`- **Error**: ${entry.error}`);
270
+ }
271
+ if (entry.traceId) {
272
+ lines.push(`- **Trace ID**: \`${entry.traceId}\``);
273
+ }
274
+ if (evaluations.length === 0) {
275
+ lines.push(
276
+ evaluatorNames.length === 0 ? "- _No evaluations recorded_" : "- _No evaluations recorded for this row_"
277
+ );
278
+ }
279
+ for (const e of evaluations) {
280
+ const parts = [`**${e.evaluator}**`];
281
+ if (e.status === "error") parts.push("status=error");
282
+ else if (typeof e.score === "number") parts.push(`score=${e.score.toFixed(3)}`);
283
+ if (typeof e.passed === "boolean") {
284
+ parts.push(`passed=${e.passed ? "yes" : "no"}`);
285
+ }
286
+ if (e.label) parts.push(`label=${e.label}`);
287
+ lines.push(`- ${parts.join(" \xB7 ")}`);
288
+ if (e.details) {
289
+ lines.push(` - details: ${e.details}`);
290
+ }
291
+ }
292
+ lines.push("");
293
+ }
294
+ if (truncated) {
295
+ lines.push(
296
+ `> Output truncated to ${limit} rows of ${totalMatching} matching to protect the agent's context window. Pass \`limit\` to expand or filter further.`
297
+ );
298
+ }
299
+ return lines.join("\n");
300
+ }
301
+
302
+ // src/tools/list-experiment-runs.ts
303
+ var DEFAULT_LIMIT = 25;
304
+ var MAX_LIMIT = 100;
305
+ var formatEpoch = (epoch) => {
306
+ if (!epoch || !Number.isFinite(epoch)) return "\u2014";
307
+ return new Date(epoch).toISOString().replace("T", " ").slice(0, 19) + " UTC";
308
+ };
309
+ var passRate = (evaluations) => {
310
+ const entries = Object.values(evaluations ?? {});
311
+ if (entries.length === 0) return "\u2014";
312
+ const passed = entries.filter((e) => typeof e.averagePassed === "number");
313
+ if (passed.length === 0) {
314
+ const scored = entries.filter((e) => typeof e.averageScore === "number");
315
+ if (scored.length === 0) return "\u2014";
316
+ const avg2 = scored.reduce((sum, e) => sum + (e.averageScore ?? 0), 0) / scored.length;
317
+ return `${avg2.toFixed(2)} avg`;
318
+ }
319
+ const avg = passed.reduce((sum, e) => sum + (e.averagePassed ?? 0), 0) / passed.length;
320
+ return `${(avg * 100).toFixed(0)}% pass`;
321
+ };
322
+ var runStatus = (run) => {
323
+ if (run.timestamps.stoppedAt) return "stopped";
324
+ if (run.timestamps.finishedAt) return "completed";
325
+ return "running";
326
+ };
327
+ async function handleExperimentListRuns(params) {
328
+ const requested = typeof params.limit === "number" && params.limit > 0 ? params.limit : DEFAULT_LIMIT;
329
+ const effectiveLimit = Math.min(requested, MAX_LIMIT);
330
+ const search = new URLSearchParams();
331
+ search.set("experimentSlug", params.experimentSlug);
332
+ search.set("pageSize", String(effectiveLimit));
333
+ let result;
334
+ try {
335
+ result = await makeRequest(
336
+ "GET",
337
+ `/api/experiments/runs?${search.toString()}`
338
+ );
339
+ } catch (error) {
340
+ const message = error instanceof Error ? error.message : String(error);
341
+ if (/404|not found/i.test(message)) {
342
+ return [
343
+ `# Evaluation Runs: ${params.experimentSlug}`,
344
+ "",
345
+ "**Status**: experiment not found",
346
+ "",
347
+ `No experiment with slug \`${params.experimentSlug}\` exists in this project.`,
348
+ "",
349
+ "> Run `platform_experiment_list` to discover available experiment slugs."
350
+ ].join("\n");
351
+ }
352
+ throw error;
353
+ }
354
+ if (result.runs.length === 0) {
355
+ return [
356
+ `# Evaluation Runs: ${result.experimentSlug}`,
357
+ "",
358
+ "_This experiment has no runs yet._",
359
+ "",
360
+ `> Trigger a run with \`platform_run_experiment\` (slug: \`${result.experimentSlug}\`) to populate this list.`
361
+ ].join("\n");
362
+ }
363
+ const lines = [];
364
+ lines.push(`# Evaluation Runs: ${result.experimentSlug}`);
365
+ lines.push("");
366
+ lines.push(
367
+ `Showing ${result.runs.length} of ${result.pagination.totalHits} run${result.pagination.totalHits === 1 ? "" : "s"}.`
368
+ );
369
+ if (result.pagination.hasMore) {
370
+ lines.push(
371
+ `> More runs exist beyond this limit. Re-run with a higher \`limit\` (max ${MAX_LIMIT}) if needed.`
372
+ );
373
+ }
374
+ lines.push("");
375
+ lines.push("| Run ID | Status | Started | Finished | Result |");
376
+ lines.push("|--------|--------|---------|----------|--------|");
377
+ for (const run of result.runs) {
378
+ lines.push(
379
+ `| \`${run.runId}\` | ${runStatus(run)} | ${formatEpoch(run.timestamps.createdAt)} | ${formatEpoch(run.timestamps.finishedAt ?? null)} | ${passRate(run.summary?.evaluations ?? {})} |`
380
+ );
381
+ }
382
+ lines.push("");
383
+ lines.push(
384
+ "> Use `platform_experiment_status <runId>` for live progress, or `platform_experiment_results <runId>` for per-row results."
385
+ );
386
+ return lines.join("\n");
387
+ }
388
+
389
+ // src/tools/list-experiments.ts
390
+ var DEFAULT_LIMIT2 = 25;
391
+ var MAX_LIMIT2 = 100;
392
+ var formatTimestamp = (iso) => {
393
+ if (!iso) return "\u2014";
394
+ const d = new Date(iso);
395
+ if (Number.isNaN(d.getTime())) return iso;
396
+ return d.toISOString().replace("T", " ").slice(0, 19) + " UTC";
397
+ };
398
+ var escapeMarkdownTableCell = (value) => value.replace(/[\\|]/g, (c) => `\\${c}`).replace(/\n/g, " ");
399
+ var formatInlineCode = (value) => `\`${value.replace(/[\\`|]/g, (c) => `\\${c}`).replace(/\n/g, " ")}\``;
400
+ async function handleExperimentList(params) {
401
+ const requested = typeof params.limit === "number" && params.limit > 0 ? params.limit : DEFAULT_LIMIT2;
402
+ const effectiveLimit = Math.min(requested, MAX_LIMIT2);
403
+ const search = new URLSearchParams();
404
+ search.set("pageSize", String(effectiveLimit));
405
+ const result = await makeRequest(
406
+ "GET",
407
+ `/api/experiments?${search.toString()}`
408
+ );
409
+ if (result.experiments.length === 0) {
410
+ return [
411
+ "# Experiments",
412
+ "",
413
+ "_No experiments found in this project._",
414
+ "",
415
+ "> Create an experiment in the LangWatch dashboard, then re-run this tool to discover its slug."
416
+ ].join("\n");
417
+ }
418
+ const lines = [];
419
+ lines.push("# Experiments");
420
+ lines.push("");
421
+ lines.push(
422
+ `Showing ${result.experiments.length} of ${result.pagination.totalHits} experiment${result.pagination.totalHits === 1 ? "" : "s"}.`
423
+ );
424
+ if (result.pagination.hasMore) {
425
+ lines.push(
426
+ `> More experiments exist beyond this limit. Re-run with a higher \`limit\` (max ${MAX_LIMIT2}) if needed.`
427
+ );
428
+ }
429
+ lines.push("");
430
+ lines.push("| Slug | Name | Runs | Last Run |");
431
+ lines.push("|------|------|------|----------|");
432
+ for (const exp of result.experiments) {
433
+ const name = escapeMarkdownTableCell(exp.name ?? exp.slug);
434
+ lines.push(
435
+ `| ${formatInlineCode(exp.slug)} | ${name} | ${exp.runsCount} | ${formatTimestamp(exp.lastRunAt)} |`
436
+ );
437
+ }
438
+ lines.push("");
439
+ lines.push(
440
+ "> Use `platform_experiment_list_runs` with one of these slugs to see its runs."
441
+ );
442
+ return lines.join("\n");
443
+ }
444
+
445
+ // src/tools/run-experiment.ts
446
+ async function handleRunExperiment(params) {
447
+ const result = await makeRequest(
448
+ "POST",
449
+ `/api/experiments/${encodeURIComponent(params.slug)}/run`
450
+ );
451
+ const lines = [];
452
+ lines.push(`Evaluation started!
453
+ `);
454
+ lines.push(`**Run ID**: ${result.runId}`);
455
+ lines.push(`**Status**: ${result.status}`);
456
+ lines.push(`**Total cells**: ${result.total}`);
457
+ if (result.runUrl) lines.push(`**View at**: ${result.runUrl}`);
458
+ lines.push("");
459
+ lines.push(
460
+ `> Use \`platform_experiment_status\` with run ID "${result.runId}" to check progress.`
461
+ );
462
+ return lines.join("\n");
463
+ }
464
+ async function handleExperimentStatus(params) {
465
+ const status = await makeRequest(
466
+ "GET",
467
+ `/api/experiments/runs/${encodeURIComponent(params.runId)}`
468
+ );
469
+ const lines = [];
470
+ lines.push(`# Evaluation Run ${status.runId}
471
+ `);
472
+ lines.push(`**Status**: ${status.status}`);
473
+ lines.push(`**Progress**: ${status.progress}/${status.total} cells`);
474
+ if (status.startedAt) {
475
+ lines.push(`**Started**: ${new Date(status.startedAt).toISOString()}`);
476
+ }
477
+ if (status.finishedAt) {
478
+ lines.push(`**Finished**: ${new Date(status.finishedAt).toISOString()}`);
479
+ }
480
+ if (status.summary) {
481
+ lines.push("\n## Summary\n");
482
+ if (status.summary.completedCells !== void 0) {
483
+ lines.push(`**Completed**: ${status.summary.completedCells}`);
484
+ }
485
+ if (status.summary.failedCells) {
486
+ lines.push(`**Failed**: ${status.summary.failedCells}`);
487
+ }
488
+ if (status.summary.duration) {
489
+ lines.push(
490
+ `**Duration**: ${(status.summary.duration / 1e3).toFixed(1)}s`
491
+ );
492
+ }
493
+ if (status.summary.runUrl) {
494
+ lines.push(`**View results**: ${status.summary.runUrl}`);
495
+ }
496
+ }
497
+ return lines.join("\n");
498
+ }
499
+
122
500
  // src/create-mcp-server.ts
123
501
  var modelSchema = z2.string().describe(
124
502
  'Model in "provider/model-name" format, e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-5-20250929"'
@@ -259,7 +637,7 @@ function registerTools(server) {
259
637
  },
260
638
  withToolLogging("search_traces", async (params) => {
261
639
  requireApiKey();
262
- const { handleSearchTraces } = await import("./search-traces-ZCHNOMTX.js");
640
+ const { handleSearchTraces } = await import("./search-traces-3BBVYPC3.js");
263
641
  return {
264
642
  content: [{ type: "text", text: await handleSearchTraces(params) }]
265
643
  };
@@ -276,7 +654,7 @@ function registerTools(server) {
276
654
  },
277
655
  withToolLogging("get_trace", async (params) => {
278
656
  requireApiKey();
279
- const { handleGetTrace } = await import("./get-trace-MUWEJM2Z.js");
657
+ const { handleGetTrace } = await import("./get-trace-MRBDS3HJ.js");
280
658
  return {
281
659
  content: [{ type: "text", text: await handleGetTrace(params) }]
282
660
  };
@@ -302,7 +680,7 @@ function registerTools(server) {
302
680
  },
303
681
  withToolLogging("get_analytics", async (params) => {
304
682
  requireApiKey();
305
- const { handleGetAnalytics } = await import("./get-analytics-3W5CMU3F.js");
683
+ const { handleGetAnalytics } = await import("./get-analytics-GPVJX7SF.js");
306
684
  return {
307
685
  content: [{ type: "text", text: await handleGetAnalytics(params) }]
308
686
  };
@@ -336,7 +714,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
336
714
  },
337
715
  withToolLogging("platform_create_prompt", async (params) => {
338
716
  requireApiKey();
339
- const { handleCreatePrompt } = await import("./create-prompt-NKRJ47YY.js");
717
+ const { handleCreatePrompt } = await import("./create-prompt-NGXKSP4X.js");
340
718
  return {
341
719
  content: [{ type: "text", text: await handleCreatePrompt(params) }]
342
720
  };
@@ -348,7 +726,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
348
726
  {},
349
727
  withToolLogging("platform_list_prompts", async () => {
350
728
  requireApiKey();
351
- const { handleListPrompts } = await import("./list-prompts-7VTFWE2Z.js");
729
+ const { handleListPrompts } = await import("./list-prompts-VNXKHK7T.js");
352
730
  return {
353
731
  content: [{ type: "text", text: await handleListPrompts() }]
354
732
  };
@@ -372,7 +750,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
372
750
  };
373
751
  }
374
752
  requireApiKey();
375
- const { handleGetPrompt } = await import("./get-prompt-XCSSNRKR.js");
753
+ const { handleGetPrompt } = await import("./get-prompt-5XAGWWBG.js");
376
754
  return {
377
755
  content: [{ type: "text", text: await handleGetPrompt(params) }]
378
756
  };
@@ -397,7 +775,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
397
775
  },
398
776
  withToolLogging("platform_update_prompt", async (params) => {
399
777
  requireApiKey();
400
- const { handleUpdatePrompt } = await import("./update-prompt-3XEHECJ7.js");
778
+ const { handleUpdatePrompt } = await import("./update-prompt-ZU2DD6KM.js");
401
779
  return {
402
780
  content: [{ type: "text", text: await handleUpdatePrompt(params) }]
403
781
  };
@@ -413,7 +791,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
413
791
  },
414
792
  withToolLogging("platform_assign_prompt_tag", async (params) => {
415
793
  requireApiKey();
416
- const { handleAssignPromptTag } = await import("./assign-prompt-tag-Y2B4CBBT.js");
794
+ const { handleAssignPromptTag } = await import("./assign-prompt-tag-FKCCZPML.js");
417
795
  return {
418
796
  content: [{ type: "text", text: await handleAssignPromptTag(params) }]
419
797
  };
@@ -425,7 +803,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
425
803
  {},
426
804
  withToolLogging("platform_list_prompt_tags", async () => {
427
805
  requireApiKey();
428
- const { handleListPromptTags } = await import("./list-prompt-tags-PL5OISUA.js");
806
+ const { handleListPromptTags } = await import("./list-prompt-tags-Z2DD6FLQ.js");
429
807
  return {
430
808
  content: [{ type: "text", text: await handleListPromptTags() }]
431
809
  };
@@ -439,7 +817,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
439
817
  },
440
818
  withToolLogging("platform_create_prompt_tag", async (params) => {
441
819
  requireApiKey();
442
- const { handleCreatePromptTag } = await import("./create-prompt-tag-S4JX3ULL.js");
820
+ const { handleCreatePromptTag } = await import("./create-prompt-tag-IUKYPVHH.js");
443
821
  return {
444
822
  content: [{ type: "text", text: await handleCreatePromptTag(params) }]
445
823
  };
@@ -454,7 +832,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
454
832
  },
455
833
  withToolLogging("platform_rename_prompt_tag", async (params) => {
456
834
  requireApiKey();
457
- const { handleRenamePromptTag } = await import("./rename-prompt-tag-TURD6KYU.js");
835
+ const { handleRenamePromptTag } = await import("./rename-prompt-tag-A4RJUK7Z.js");
458
836
  return {
459
837
  content: [{ type: "text", text: await handleRenamePromptTag(params) }]
460
838
  };
@@ -468,7 +846,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
468
846
  },
469
847
  withToolLogging("platform_delete_prompt_tag", async (params) => {
470
848
  requireApiKey();
471
- const { handleDeletePromptTag } = await import("./delete-prompt-tag-B5UKZL45.js");
849
+ const { handleDeletePromptTag } = await import("./delete-prompt-tag-I6HNMSVJ.js");
472
850
  return {
473
851
  content: [{ type: "text", text: await handleDeletePromptTag(params) }]
474
852
  };
@@ -496,7 +874,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
496
874
  },
497
875
  withToolLogging("platform_create_scenario", async (params) => {
498
876
  requireApiKey();
499
- const { handleCreateScenario } = await import("./create-scenario-BPRF75KF.js");
877
+ const { handleCreateScenario } = await import("./create-scenario-FNBXALH3.js");
500
878
  return {
501
879
  content: [
502
880
  { type: "text", text: await handleCreateScenario(params) }
@@ -514,7 +892,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
514
892
  },
515
893
  withToolLogging("platform_list_scenarios", async (params) => {
516
894
  requireApiKey();
517
- const { handleListScenarios } = await import("./list-scenarios-5BZWZ6ZN.js");
895
+ const { handleListScenarios } = await import("./list-scenarios-CFZHXL76.js");
518
896
  return {
519
897
  content: [
520
898
  { type: "text", text: await handleListScenarios(params) }
@@ -533,7 +911,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
533
911
  },
534
912
  withToolLogging("platform_get_scenario", async (params) => {
535
913
  requireApiKey();
536
- const { handleGetScenario } = await import("./get-scenario-FNRKOY7E.js");
914
+ const { handleGetScenario } = await import("./get-scenario-7XBCDZWX.js");
537
915
  return {
538
916
  content: [{ type: "text", text: await handleGetScenario(params) }]
539
917
  };
@@ -551,7 +929,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
551
929
  },
552
930
  withToolLogging("platform_update_scenario", async (params) => {
553
931
  requireApiKey();
554
- const { handleUpdateScenario } = await import("./update-scenario-DFPREYCD.js");
932
+ const { handleUpdateScenario } = await import("./update-scenario-3VUTPDRU.js");
555
933
  return {
556
934
  content: [
557
935
  { type: "text", text: await handleUpdateScenario(params) }
@@ -567,7 +945,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
567
945
  },
568
946
  withToolLogging("platform_archive_scenario", async (params) => {
569
947
  requireApiKey();
570
- const { handleArchiveScenario } = await import("./archive-scenario-YKGLJ4MZ.js");
948
+ const { handleArchiveScenario } = await import("./archive-scenario-P5RRITRM.js");
571
949
  return {
572
950
  content: [
573
951
  { type: "text", text: await handleArchiveScenario(params) }
@@ -583,7 +961,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
583
961
  },
584
962
  withToolLogging("platform_list_suites", async (params) => {
585
963
  requireApiKey();
586
- const { handleListSuites } = await import("./list-suites-RRQ2JX5Y.js");
964
+ const { handleListSuites } = await import("./list-suites-ZDOHAAXF.js");
587
965
  return {
588
966
  content: [{ type: "text", text: await handleListSuites(params) }]
589
967
  };
@@ -598,7 +976,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
598
976
  },
599
977
  withToolLogging("platform_get_suite", async (params) => {
600
978
  requireApiKey();
601
- const { handleGetSuite } = await import("./get-suite-U5AOD5TC.js");
979
+ const { handleGetSuite } = await import("./get-suite-4DBVHFEM.js");
602
980
  return {
603
981
  content: [{ type: "text", text: await handleGetSuite(params) }]
604
982
  };
@@ -617,7 +995,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
617
995
  },
618
996
  withToolLogging("platform_create_suite", async (params) => {
619
997
  requireApiKey();
620
- const { handleCreateSuite } = await import("./create-suite-4YN7D6K2.js");
998
+ const { handleCreateSuite } = await import("./create-suite-DJEPD4ZB.js");
621
999
  return {
622
1000
  content: [{ type: "text", text: await handleCreateSuite(params) }]
623
1001
  };
@@ -637,7 +1015,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
637
1015
  },
638
1016
  withToolLogging("platform_update_suite", async (params) => {
639
1017
  requireApiKey();
640
- const { handleUpdateSuite } = await import("./update-suite-RATU5THO.js");
1018
+ const { handleUpdateSuite } = await import("./update-suite-C6JFDQHL.js");
641
1019
  return {
642
1020
  content: [{ type: "text", text: await handleUpdateSuite(params) }]
643
1021
  };
@@ -651,7 +1029,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
651
1029
  },
652
1030
  withToolLogging("platform_run_suite", async (params) => {
653
1031
  requireApiKey();
654
- const { handleRunSuite } = await import("./run-suite-U7V432G2.js");
1032
+ const { handleRunSuite } = await import("./run-suite-TH7LO5TJ.js");
655
1033
  return {
656
1034
  content: [{ type: "text", text: await handleRunSuite(params) }]
657
1035
  };
@@ -665,7 +1043,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
665
1043
  },
666
1044
  withToolLogging("platform_archive_suite", async (params) => {
667
1045
  requireApiKey();
668
- const { handleArchiveSuite } = await import("./archive-suite-QFNDO3Y3.js");
1046
+ const { handleArchiveSuite } = await import("./archive-suite-NUUFD2EM.js");
669
1047
  return {
670
1048
  content: [{ type: "text", text: await handleArchiveSuite(params) }]
671
1049
  };
@@ -682,7 +1060,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
682
1060
  },
683
1061
  withToolLogging("platform_list_simulation_runs", async (params) => {
684
1062
  requireApiKey();
685
- const { handleListSimulationRuns } = await import("./list-simulation-runs-WCZ6RLPE.js");
1063
+ const { handleListSimulationRuns } = await import("./list-simulation-runs-AM2BXLBW.js");
686
1064
  return {
687
1065
  content: [{ type: "text", text: await handleListSimulationRuns(params) }]
688
1066
  };
@@ -697,7 +1075,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
697
1075
  },
698
1076
  withToolLogging("platform_get_simulation_run", async (params) => {
699
1077
  requireApiKey();
700
- const { handleGetSimulationRun } = await import("./get-simulation-run-WOEKUF7K.js");
1078
+ const { handleGetSimulationRun } = await import("./get-simulation-run-AV24FJWI.js");
701
1079
  return {
702
1080
  content: [{ type: "text", text: await handleGetSimulationRun(params) }]
703
1081
  };
@@ -714,7 +1092,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
714
1092
  },
715
1093
  withToolLogging("platform_create_evaluator", async (params) => {
716
1094
  requireApiKey();
717
- const { handleCreateEvaluator } = await import("./create-evaluator-2AU3PXFB.js");
1095
+ const { handleCreateEvaluator } = await import("./create-evaluator-YFFQIR7E.js");
718
1096
  return {
719
1097
  content: [
720
1098
  { type: "text", text: await handleCreateEvaluator(params) }
@@ -728,7 +1106,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
728
1106
  {},
729
1107
  withToolLogging("platform_list_evaluators", async () => {
730
1108
  requireApiKey();
731
- const { handleListEvaluators } = await import("./list-evaluators-AKKRJ5MX.js");
1109
+ const { handleListEvaluators } = await import("./list-evaluators-B6OSVAM2.js");
732
1110
  return {
733
1111
  content: [{ type: "text", text: await handleListEvaluators() }]
734
1112
  };
@@ -742,7 +1120,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
742
1120
  },
743
1121
  withToolLogging("platform_get_evaluator", async (params) => {
744
1122
  requireApiKey();
745
- const { handleGetEvaluator } = await import("./get-evaluator-QDUL5LXR.js");
1123
+ const { handleGetEvaluator } = await import("./get-evaluator-PCM6XQAU.js");
746
1124
  return {
747
1125
  content: [{ type: "text", text: await handleGetEvaluator(params) }]
748
1126
  };
@@ -760,7 +1138,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
760
1138
  },
761
1139
  withToolLogging("platform_update_evaluator", async (params) => {
762
1140
  requireApiKey();
763
- const { handleUpdateEvaluator } = await import("./update-evaluator-LHVDXIZD.js");
1141
+ const { handleUpdateEvaluator } = await import("./update-evaluator-6IJHFG2E.js");
764
1142
  return {
765
1143
  content: [
766
1144
  { type: "text", text: await handleUpdateEvaluator(params) }
@@ -776,7 +1154,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
776
1154
  },
777
1155
  withToolLogging("platform_delete_evaluator", async (params) => {
778
1156
  requireApiKey();
779
- const { handleDeleteEvaluator } = await import("./delete-evaluator-F7QLXDHU.js");
1157
+ const { handleDeleteEvaluator } = await import("./delete-evaluator-7ZATLJOA.js");
780
1158
  return {
781
1159
  content: [{ type: "text", text: await handleDeleteEvaluator(params) }]
782
1160
  };
@@ -797,7 +1175,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
797
1175
  },
798
1176
  withToolLogging("platform_set_model_provider", async (params) => {
799
1177
  requireApiKey();
800
- const { handleSetModelProvider } = await import("./set-model-provider-6CFHMCNE.js");
1178
+ const { handleSetModelProvider } = await import("./set-model-provider-WOC5FNMV.js");
801
1179
  return {
802
1180
  content: [
803
1181
  { type: "text", text: await handleSetModelProvider(params) }
@@ -811,7 +1189,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
811
1189
  {},
812
1190
  withToolLogging("platform_list_model_providers", async () => {
813
1191
  requireApiKey();
814
- const { handleListModelProviders } = await import("./list-model-providers-DVFEHFHZ.js");
1192
+ const { handleListModelProviders } = await import("./list-model-providers-NL6IP4UZ.js");
815
1193
  return {
816
1194
  content: [{ type: "text", text: await handleListModelProviders() }]
817
1195
  };
@@ -823,7 +1201,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
823
1201
  {},
824
1202
  withToolLogging("platform_list_agents", async () => {
825
1203
  requireApiKey();
826
- const { handleListAgents } = await import("./list-agents-3HGIBJFM.js");
1204
+ const { handleListAgents } = await import("./list-agents-NECO7MPI.js");
827
1205
  return {
828
1206
  content: [{ type: "text", text: await handleListAgents() }]
829
1207
  };
@@ -837,7 +1215,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
837
1215
  },
838
1216
  withToolLogging("platform_get_agent", async (params) => {
839
1217
  requireApiKey();
840
- const { handleGetAgent } = await import("./get-agent-YEVICUX4.js");
1218
+ const { handleGetAgent } = await import("./get-agent-3KDWCP5N.js");
841
1219
  return {
842
1220
  content: [{ type: "text", text: await handleGetAgent(params) }]
843
1221
  };
@@ -853,7 +1231,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
853
1231
  },
854
1232
  withToolLogging("platform_create_agent", async (params) => {
855
1233
  requireApiKey();
856
- const { handleCreateAgent } = await import("./create-agent-YRSDH7P7.js");
1234
+ const { handleCreateAgent } = await import("./create-agent-K5Q7BFGZ.js");
857
1235
  const parsedConfig = params.config ? JSON.parse(params.config) : void 0;
858
1236
  return {
859
1237
  content: [{ type: "text", text: await handleCreateAgent({ ...params, config: parsedConfig }) }]
@@ -871,7 +1249,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
871
1249
  },
872
1250
  withToolLogging("platform_update_agent", async (params) => {
873
1251
  requireApiKey();
874
- const { handleUpdateAgent } = await import("./update-agent-UE6UWLZP.js");
1252
+ const { handleUpdateAgent } = await import("./update-agent-YMEV5MTL.js");
875
1253
  const parsedConfig = params.config ? JSON.parse(params.config) : void 0;
876
1254
  return {
877
1255
  content: [{ type: "text", text: await handleUpdateAgent({ ...params, config: parsedConfig }) }]
@@ -886,7 +1264,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
886
1264
  },
887
1265
  withToolLogging("platform_delete_agent", async (params) => {
888
1266
  requireApiKey();
889
- const { handleDeleteAgent } = await import("./delete-agent-Y72R2YQA.js");
1267
+ const { handleDeleteAgent } = await import("./delete-agent-TJI5XBBM.js");
890
1268
  return {
891
1269
  content: [{ type: "text", text: await handleDeleteAgent(params) }]
892
1270
  };
@@ -901,7 +1279,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
901
1279
  },
902
1280
  withToolLogging("platform_run_agent", async (params) => {
903
1281
  requireApiKey();
904
- const { handleRunAgent } = await import("./run-agent-BEJ4K4CM.js");
1282
+ const { handleRunAgent } = await import("./run-agent-JFNDY7MR.js");
905
1283
  return {
906
1284
  content: [{ type: "text", text: await handleRunAgent(params) }]
907
1285
  };
@@ -913,7 +1291,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
913
1291
  {},
914
1292
  withToolLogging("platform_list_dashboards", async () => {
915
1293
  requireApiKey();
916
- const { handleListDashboards } = await import("./list-dashboards-VQ2LH666.js");
1294
+ const { handleListDashboards } = await import("./list-dashboards-DVPVVVKW.js");
917
1295
  return {
918
1296
  content: [{ type: "text", text: await handleListDashboards() }]
919
1297
  };
@@ -927,7 +1305,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
927
1305
  },
928
1306
  withToolLogging("platform_get_dashboard", async (params) => {
929
1307
  requireApiKey();
930
- const { handleGetDashboard } = await import("./get-dashboard-NCQHN6MW.js");
1308
+ const { handleGetDashboard } = await import("./get-dashboard-YPTIE5YY.js");
931
1309
  return {
932
1310
  content: [{ type: "text", text: await handleGetDashboard(params) }]
933
1311
  };
@@ -941,7 +1319,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
941
1319
  },
942
1320
  withToolLogging("platform_create_dashboard", async (params) => {
943
1321
  requireApiKey();
944
- const { handleCreateDashboard } = await import("./create-dashboard-CORBR4YQ.js");
1322
+ const { handleCreateDashboard } = await import("./create-dashboard-G4QA5AXP.js");
945
1323
  return {
946
1324
  content: [{ type: "text", text: await handleCreateDashboard(params) }]
947
1325
  };
@@ -955,7 +1333,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
955
1333
  },
956
1334
  withToolLogging("platform_delete_dashboard", async (params) => {
957
1335
  requireApiKey();
958
- const { handleDeleteDashboard } = await import("./delete-dashboard-LPWT36KN.js");
1336
+ const { handleDeleteDashboard } = await import("./delete-dashboard-N5SZJIR3.js");
959
1337
  return {
960
1338
  content: [{ type: "text", text: await handleDeleteDashboard(params) }]
961
1339
  };
@@ -970,7 +1348,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
970
1348
  },
971
1349
  withToolLogging("platform_rename_dashboard", async (params) => {
972
1350
  requireApiKey();
973
- const { renameDashboard } = await import("./langwatch-api-dashboards-VBWWXV3Y.js");
1351
+ const { renameDashboard } = await import("./langwatch-api-dashboards-WARLI6NF.js");
974
1352
  const result = await renameDashboard(params.id, { name: params.name });
975
1353
  return {
976
1354
  content: [{ type: "text", text: `Dashboard "${result.name}" renamed successfully (ID: ${result.id}).` }]
@@ -983,7 +1361,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
983
1361
  {},
984
1362
  withToolLogging("platform_list_workflows", async () => {
985
1363
  requireApiKey();
986
- const { handleListWorkflows } = await import("./list-workflows-UQB5FCX5.js");
1364
+ const { handleListWorkflows } = await import("./list-workflows-WBRBKLIT.js");
987
1365
  return {
988
1366
  content: [{ type: "text", text: await handleListWorkflows() }]
989
1367
  };
@@ -997,7 +1375,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
997
1375
  },
998
1376
  withToolLogging("platform_get_workflow", async (params) => {
999
1377
  requireApiKey();
1000
- const { handleGetWorkflow } = await import("./get-workflow-SOA7MT7L.js");
1378
+ const { handleGetWorkflow } = await import("./get-workflow-UR4Y6UFX.js");
1001
1379
  return {
1002
1380
  content: [{ type: "text", text: await handleGetWorkflow(params) }]
1003
1381
  };
@@ -1011,7 +1389,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1011
1389
  },
1012
1390
  withToolLogging("platform_delete_workflow", async (params) => {
1013
1391
  requireApiKey();
1014
- const { handleDeleteWorkflow } = await import("./delete-workflow-4DGUDIDE.js");
1392
+ const { handleDeleteWorkflow } = await import("./delete-workflow-QEO74JQR.js");
1015
1393
  return {
1016
1394
  content: [{ type: "text", text: await handleDeleteWorkflow(params) }]
1017
1395
  };
@@ -1026,7 +1404,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1026
1404
  },
1027
1405
  withToolLogging("platform_run_workflow", async (params) => {
1028
1406
  requireApiKey();
1029
- const { handleRunWorkflow } = await import("./run-workflow-5WX7DK5T.js");
1407
+ const { handleRunWorkflow } = await import("./run-workflow-CK3JP5U2.js");
1030
1408
  return {
1031
1409
  content: [{ type: "text", text: await handleRunWorkflow(params) }]
1032
1410
  };
@@ -1040,7 +1418,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1040
1418
  },
1041
1419
  withToolLogging("platform_list_annotations", async (params) => {
1042
1420
  requireApiKey();
1043
- const { handleListAnnotations } = await import("./list-annotations-KWXYVEPI.js");
1421
+ const { handleListAnnotations } = await import("./list-annotations-3OJEO774.js");
1044
1422
  return {
1045
1423
  content: [{ type: "text", text: await handleListAnnotations(params) }]
1046
1424
  };
@@ -1057,7 +1435,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1057
1435
  },
1058
1436
  withToolLogging("platform_create_annotation", async (params) => {
1059
1437
  requireApiKey();
1060
- const { handleCreateAnnotation } = await import("./create-annotation-FYQNKMVJ.js");
1438
+ const { handleCreateAnnotation } = await import("./create-annotation-KN3GE3DK.js");
1061
1439
  return {
1062
1440
  content: [{ type: "text", text: await handleCreateAnnotation(params) }]
1063
1441
  };
@@ -1071,7 +1449,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1071
1449
  },
1072
1450
  withToolLogging("platform_delete_annotation", async (params) => {
1073
1451
  requireApiKey();
1074
- const { handleDeleteAnnotation } = await import("./delete-annotation-UHWDMHSH.js");
1452
+ const { handleDeleteAnnotation } = await import("./delete-annotation-KRP53TUU.js");
1075
1453
  return {
1076
1454
  content: [{ type: "text", text: await handleDeleteAnnotation(params) }]
1077
1455
  };
@@ -1085,7 +1463,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1085
1463
  },
1086
1464
  withToolLogging("platform_get_annotation", async (params) => {
1087
1465
  requireApiKey();
1088
- const { getAnnotation } = await import("./langwatch-api-annotations-Q57NDUVR.js");
1466
+ const { getAnnotation } = await import("./langwatch-api-annotations-ZJDRB4JH.js");
1089
1467
  const annotation = await getAnnotation(params.id);
1090
1468
  const lines = [
1091
1469
  `**ID**: ${annotation.id}`,
@@ -1107,7 +1485,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1107
1485
  },
1108
1486
  withToolLogging("platform_list_triggers", async (params) => {
1109
1487
  requireApiKey();
1110
- const { listTriggers } = await import("./langwatch-api-triggers-ME33QE26.js");
1488
+ const { listTriggers } = await import("./langwatch-api-triggers-5SX6R2WU.js");
1111
1489
  const triggers = await listTriggers();
1112
1490
  if (params.format === "json") {
1113
1491
  return { content: [{ type: "text", text: JSON.stringify(triggers, null, 2) }] };
@@ -1140,7 +1518,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1140
1518
  },
1141
1519
  withToolLogging("platform_create_trigger", async (params) => {
1142
1520
  requireApiKey();
1143
- const { createTrigger } = await import("./langwatch-api-triggers-ME33QE26.js");
1521
+ const { createTrigger } = await import("./langwatch-api-triggers-5SX6R2WU.js");
1144
1522
  let filters = {};
1145
1523
  if (params.filters) {
1146
1524
  try {
@@ -1171,7 +1549,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1171
1549
  },
1172
1550
  withToolLogging("platform_update_trigger", async (params) => {
1173
1551
  requireApiKey();
1174
- const { updateTrigger } = await import("./langwatch-api-triggers-ME33QE26.js");
1552
+ const { updateTrigger } = await import("./langwatch-api-triggers-5SX6R2WU.js");
1175
1553
  const trigger = await updateTrigger(params);
1176
1554
  return { content: [{ type: "text", text: `Trigger "${trigger.name}" updated (active: ${trigger.active}).` }] };
1177
1555
  })
@@ -1184,7 +1562,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1184
1562
  },
1185
1563
  withToolLogging("platform_delete_trigger", async (params) => {
1186
1564
  requireApiKey();
1187
- const { deleteTrigger } = await import("./langwatch-api-triggers-ME33QE26.js");
1565
+ const { deleteTrigger } = await import("./langwatch-api-triggers-5SX6R2WU.js");
1188
1566
  const result = await deleteTrigger(params.id);
1189
1567
  return { content: [{ type: "text", text: `Trigger ${result.id} deleted.` }] };
1190
1568
  })
@@ -1195,7 +1573,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1195
1573
  {},
1196
1574
  withToolLogging("platform_list_monitors", async () => {
1197
1575
  requireApiKey();
1198
- const { listMonitors } = await import("./langwatch-api-monitors-L3IY7FDT.js");
1576
+ const { listMonitors } = await import("./langwatch-api-monitors-4XKXHYCG.js");
1199
1577
  const monitors = await listMonitors();
1200
1578
  if (monitors.length === 0) {
1201
1579
  return { content: [{ type: "text", text: "No monitors found." }] };
@@ -1222,7 +1600,7 @@ ${lines.join("\n")}`
1222
1600
  },
1223
1601
  withToolLogging("platform_get_monitor", async (params) => {
1224
1602
  requireApiKey();
1225
- const { getMonitor } = await import("./langwatch-api-monitors-L3IY7FDT.js");
1603
+ const { getMonitor } = await import("./langwatch-api-monitors-4XKXHYCG.js");
1226
1604
  const m = await getMonitor(params.id);
1227
1605
  const lines = [
1228
1606
  `**${m.name}** (${m.id})`,
@@ -1249,7 +1627,7 @@ ${lines.join("\n")}`
1249
1627
  },
1250
1628
  withToolLogging("platform_create_monitor", async (params) => {
1251
1629
  requireApiKey();
1252
- const { createMonitor } = await import("./langwatch-api-monitors-L3IY7FDT.js");
1630
+ const { createMonitor } = await import("./langwatch-api-monitors-4XKXHYCG.js");
1253
1631
  const monitor = await createMonitor(params);
1254
1632
  return {
1255
1633
  content: [
@@ -1273,7 +1651,7 @@ ${lines.join("\n")}`
1273
1651
  },
1274
1652
  withToolLogging("platform_update_monitor", async (params) => {
1275
1653
  requireApiKey();
1276
- const { updateMonitor } = await import("./langwatch-api-monitors-L3IY7FDT.js");
1654
+ const { updateMonitor } = await import("./langwatch-api-monitors-4XKXHYCG.js");
1277
1655
  const monitor = await updateMonitor(params);
1278
1656
  return {
1279
1657
  content: [
@@ -1293,7 +1671,7 @@ ${lines.join("\n")}`
1293
1671
  },
1294
1672
  withToolLogging("platform_delete_monitor", async (params) => {
1295
1673
  requireApiKey();
1296
- const { deleteMonitor } = await import("./langwatch-api-monitors-L3IY7FDT.js");
1674
+ const { deleteMonitor } = await import("./langwatch-api-monitors-4XKXHYCG.js");
1297
1675
  const result = await deleteMonitor(params.id);
1298
1676
  return {
1299
1677
  content: [
@@ -1308,7 +1686,7 @@ ${lines.join("\n")}`
1308
1686
  {},
1309
1687
  withToolLogging("platform_list_secrets", async () => {
1310
1688
  requireApiKey();
1311
- const { listSecrets } = await import("./langwatch-api-secrets-S5HHF3XR.js");
1689
+ const { listSecrets } = await import("./langwatch-api-secrets-6SHK5G7Q.js");
1312
1690
  const secrets = await listSecrets();
1313
1691
  if (secrets.length === 0) {
1314
1692
  return { content: [{ type: "text", text: "No secrets found." }] };
@@ -1336,7 +1714,7 @@ ${lines.join("\n")}`
1336
1714
  },
1337
1715
  withToolLogging("platform_create_secret", async (params) => {
1338
1716
  requireApiKey();
1339
- const { createSecret } = await import("./langwatch-api-secrets-S5HHF3XR.js");
1717
+ const { createSecret } = await import("./langwatch-api-secrets-6SHK5G7Q.js");
1340
1718
  const secret = await createSecret({ name: params.name, value: params.value });
1341
1719
  return {
1342
1720
  content: [
@@ -1357,7 +1735,7 @@ ${lines.join("\n")}`
1357
1735
  },
1358
1736
  withToolLogging("platform_update_secret", async (params) => {
1359
1737
  requireApiKey();
1360
- const { updateSecret } = await import("./langwatch-api-secrets-S5HHF3XR.js");
1738
+ const { updateSecret } = await import("./langwatch-api-secrets-6SHK5G7Q.js");
1361
1739
  const secret = await updateSecret({ id: params.id, value: params.value });
1362
1740
  return {
1363
1741
  content: [
@@ -1377,36 +1755,92 @@ ${lines.join("\n")}`
1377
1755
  },
1378
1756
  withToolLogging("platform_delete_secret", async (params) => {
1379
1757
  requireApiKey();
1380
- const { deleteSecret } = await import("./langwatch-api-secrets-S5HHF3XR.js");
1758
+ const { deleteSecret } = await import("./langwatch-api-secrets-6SHK5G7Q.js");
1381
1759
  const result = await deleteSecret(params.id);
1382
1760
  return { content: [{ type: "text", text: `Secret ${result.id} deleted.` }] };
1383
1761
  })
1384
1762
  );
1385
1763
  server.tool(
1386
- "platform_run_evaluation",
1387
- "Start an evaluation run by slug. Returns a run ID for polling status.",
1764
+ "platform_run_experiment",
1765
+ "Start an experiment run by slug. Returns a run ID for polling status.",
1388
1766
  {
1389
- slug: z2.string().describe("The evaluation slug to run")
1767
+ slug: z2.string().describe("The experiment slug to run")
1390
1768
  },
1391
- withToolLogging("platform_run_evaluation", async (params) => {
1769
+ withToolLogging("platform_run_experiment", async (params) => {
1392
1770
  requireApiKey();
1393
- const { handleRunEvaluation } = await import("./run-evaluation-AJ7WZNQD.js");
1394
1771
  return {
1395
- content: [{ type: "text", text: await handleRunEvaluation(params) }]
1772
+ content: [{ type: "text", text: await handleRunExperiment(params) }]
1396
1773
  };
1397
1774
  })
1398
1775
  );
1399
1776
  server.tool(
1400
- "platform_evaluation_status",
1401
- "Check the status of an evaluation run. Returns progress and summary when completed.",
1777
+ "platform_experiment_status",
1778
+ "Check the status of an experiment run. Returns progress and summary when completed.",
1402
1779
  {
1403
- runId: z2.string().describe("The run ID returned from platform_run_evaluation")
1780
+ runId: z2.string().describe("The run ID returned from platform_run_experiment")
1404
1781
  },
1405
- withToolLogging("platform_evaluation_status", async (params) => {
1782
+ withToolLogging("platform_experiment_status", async (params) => {
1406
1783
  requireApiKey();
1407
- const { handleEvaluationStatus } = await import("./run-evaluation-AJ7WZNQD.js");
1408
1784
  return {
1409
- content: [{ type: "text", text: await handleEvaluationStatus(params) }]
1785
+ content: [{ type: "text", text: await handleExperimentStatus(params) }]
1786
+ };
1787
+ })
1788
+ );
1789
+ server.tool(
1790
+ "platform_experiment_list",
1791
+ "List experiments configured in the LangWatch project. Returns a markdown table with each experiment's slug, name, last-run timestamp, and run count. Use this to discover experiment slugs before drilling into runs with platform_experiment_list_runs. Output is capped (default 25, max 100) to protect the agent's context window.",
1792
+ {
1793
+ limit: z2.number().int().positive().max(100).optional().describe(
1794
+ "Maximum number of experiments to include (default 25, hard-capped at 100)"
1795
+ )
1796
+ },
1797
+ withToolLogging("platform_experiment_list", async (params) => {
1798
+ requireApiKey();
1799
+ return {
1800
+ content: [{ type: "text", text: await handleExperimentList(params) }]
1801
+ };
1802
+ })
1803
+ );
1804
+ server.tool(
1805
+ "platform_experiment_list_runs",
1806
+ "List experiment runs for a specific experiment slug. Returns a markdown table with each run's id, status, started/finished timestamps, and pass-rate summary. Use this to discover run ids before calling platform_experiment_results. Output is capped (default 25, max 100) to protect the agent's context window.",
1807
+ {
1808
+ experimentSlug: z2.string().describe(
1809
+ "Experiment slug from platform_experiment_list (e.g. 'checkout-flow')"
1810
+ ),
1811
+ limit: z2.number().int().positive().max(100).optional().describe(
1812
+ "Maximum number of runs to include (default 25, hard-capped at 100)"
1813
+ )
1814
+ },
1815
+ withToolLogging("platform_experiment_list_runs", async (params) => {
1816
+ requireApiKey();
1817
+ return {
1818
+ content: [{ type: "text", text: await handleExperimentListRuns(params) }]
1819
+ };
1820
+ })
1821
+ );
1822
+ server.tool(
1823
+ "platform_experiment_results",
1824
+ "Fetch per-row results for a completed experiment run so you can debug evaluator scores and missed rows. Returns a markdown report: per-evaluator averages plus row-by-row scores and failure details. Output is capped at 50 rows to protect the agent's context window \u2014 narrow with `filter: 'failed'` or `evaluator` to see what matters.",
1825
+ {
1826
+ runId: z2.string().describe("The run ID returned from platform_run_experiment"),
1827
+ experimentSlug: z2.string().optional().describe(
1828
+ "Experiment slug \u2014 required when fetching results for runs older than 24h (Redis state may have expired). Use platform_experiment_list_runs to find the slug."
1829
+ ),
1830
+ filter: z2.enum(["all", "failed"]).optional().describe(
1831
+ "Filter rows: 'failed' shows only rows that errored or failed an evaluation; 'all' (default) shows everything"
1832
+ ),
1833
+ evaluator: z2.string().optional().describe("Show only this evaluator's results (matched on `evaluator` field)"),
1834
+ limit: z2.number().int().positive().max(50).optional().describe(
1835
+ "Maximum number of rows to include in the markdown output (default 50, hard-capped at 50)."
1836
+ )
1837
+ },
1838
+ withToolLogging("platform_experiment_results", async (params) => {
1839
+ requireApiKey();
1840
+ return {
1841
+ content: [
1842
+ { type: "text", text: await handleExperimentResults(params) }
1843
+ ]
1410
1844
  };
1411
1845
  })
1412
1846
  );
@@ -1421,7 +1855,7 @@ ${lines.join("\n")}`
1421
1855
  async (params) => {
1422
1856
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1423
1857
  requireApiKey2();
1424
- const { handleListDatasets } = await import("./list-datasets-I4A2LT2S.js");
1858
+ const { handleListDatasets } = await import("./list-datasets-WUAE5HGX.js");
1425
1859
  return {
1426
1860
  content: [{ type: "text", text: await handleListDatasets(params) }]
1427
1861
  };
@@ -1439,7 +1873,7 @@ ${lines.join("\n")}`
1439
1873
  async (params) => {
1440
1874
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1441
1875
  requireApiKey2();
1442
- const { handleGetDataset } = await import("./get-dataset-KBK6PH36.js");
1876
+ const { handleGetDataset } = await import("./get-dataset-6DQFYITM.js");
1443
1877
  return {
1444
1878
  content: [{ type: "text", text: await handleGetDataset(params) }]
1445
1879
  };
@@ -1452,7 +1886,7 @@ ${lines.join("\n")}`
1452
1886
  async (params) => {
1453
1887
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1454
1888
  requireApiKey2();
1455
- const { handleCreateDataset } = await import("./create-dataset-ZC63T6BB.js");
1889
+ const { handleCreateDataset } = await import("./create-dataset-WQSC37N7.js");
1456
1890
  return {
1457
1891
  content: [{ type: "text", text: await handleCreateDataset(params) }]
1458
1892
  };
@@ -1469,7 +1903,7 @@ ${lines.join("\n")}`
1469
1903
  async (params) => {
1470
1904
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1471
1905
  requireApiKey2();
1472
- const { handleUpdateDataset } = await import("./update-dataset-4XQZZE4S.js");
1906
+ const { handleUpdateDataset } = await import("./update-dataset-EYT5POFM.js");
1473
1907
  return {
1474
1908
  content: [{ type: "text", text: await handleUpdateDataset(params) }]
1475
1909
  };
@@ -1484,7 +1918,7 @@ ${lines.join("\n")}`
1484
1918
  async (params) => {
1485
1919
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1486
1920
  requireApiKey2();
1487
- const { handleDeleteDataset } = await import("./delete-dataset-GGPQRHKX.js");
1921
+ const { handleDeleteDataset } = await import("./delete-dataset-KDUOHL2P.js");
1488
1922
  return {
1489
1923
  content: [{ type: "text", text: await handleDeleteDataset(params) }]
1490
1924
  };
@@ -1502,7 +1936,7 @@ ${lines.join("\n")}`
1502
1936
  async (params) => {
1503
1937
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1504
1938
  requireApiKey2();
1505
- const { handleListDatasetRecords } = await import("./list-dataset-records-RNRGROJP.js");
1939
+ const { handleListDatasetRecords } = await import("./list-dataset-records-I4BYORF5.js");
1506
1940
  return {
1507
1941
  content: [
1508
1942
  { type: "text", text: await handleListDatasetRecords(params) }
@@ -1520,7 +1954,7 @@ ${lines.join("\n")}`
1520
1954
  async (params) => {
1521
1955
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1522
1956
  requireApiKey2();
1523
- const { handleCreateDatasetRecords } = await import("./create-dataset-records-ILVNVKVR.js");
1957
+ const { handleCreateDatasetRecords } = await import("./create-dataset-records-B4RL3FLC.js");
1524
1958
  return {
1525
1959
  content: [
1526
1960
  { type: "text", text: await handleCreateDatasetRecords(params) }
@@ -1539,7 +1973,7 @@ ${lines.join("\n")}`
1539
1973
  async (params) => {
1540
1974
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1541
1975
  requireApiKey2();
1542
- const { handleUpdateDatasetRecord } = await import("./update-dataset-record-GNNM5TKM.js");
1976
+ const { handleUpdateDatasetRecord } = await import("./update-dataset-record-VFF5JTX4.js");
1543
1977
  return {
1544
1978
  content: [
1545
1979
  { type: "text", text: await handleUpdateDatasetRecord(params) }
@@ -1557,7 +1991,7 @@ ${lines.join("\n")}`
1557
1991
  async (params) => {
1558
1992
  const { requireApiKey: requireApiKey2 } = await import("./config.js");
1559
1993
  requireApiKey2();
1560
- const { handleDeleteDatasetRecords } = await import("./delete-dataset-records-LTG5LT26.js");
1994
+ const { handleDeleteDatasetRecords } = await import("./delete-dataset-records-DM4W3PAW.js");
1561
1995
  return {
1562
1996
  content: [
1563
1997
  { type: "text", text: await handleDeleteDatasetRecords(params) }
@@ -1565,9 +1999,144 @@ ${lines.join("\n")}`
1565
1999
  };
1566
2000
  }
1567
2001
  );
2002
+ server.tool(
2003
+ "platform_list_projects",
2004
+ "List all projects in your LangWatch organization. Requires an org-level API key.",
2005
+ {
2006
+ page: z2.number().int().positive().optional().describe("Page number (default: 1)"),
2007
+ limit: z2.number().int().positive().max(1e3).optional().describe("Results per page (default: 100)")
2008
+ },
2009
+ withToolLogging("platform_list_projects", async (params) => {
2010
+ requireApiKey();
2011
+ const { handleListProjects } = await import("./list-projects-5FHMJ4PQ.js");
2012
+ return {
2013
+ content: [{ type: "text", text: await handleListProjects(params) }]
2014
+ };
2015
+ })
2016
+ );
2017
+ server.tool(
2018
+ "platform_get_project",
2019
+ "Get details of a specific project by its ID.",
2020
+ {
2021
+ id: z2.string().describe("The project ID")
2022
+ },
2023
+ withToolLogging("platform_get_project", async (params) => {
2024
+ requireApiKey();
2025
+ const { handleGetProject } = await import("./get-project-JCMWXVLU.js");
2026
+ return {
2027
+ content: [{ type: "text", text: await handleGetProject(params) }]
2028
+ };
2029
+ })
2030
+ );
2031
+ server.tool(
2032
+ "platform_create_project",
2033
+ `Create a new project in your LangWatch organization. Returns a one-time service API key for the project.
2034
+
2035
+ You must provide either teamId (to add the project to an existing team) or newTeamName (to create a new team for the project).`,
2036
+ {
2037
+ name: z2.string().describe("Project name"),
2038
+ language: z2.string().describe('Programming language (e.g. "python", "typescript")'),
2039
+ framework: z2.string().describe('Framework (e.g. "openai", "langchain", "custom")'),
2040
+ teamId: z2.string().optional().describe("ID of an existing team to add the project to"),
2041
+ newTeamName: z2.string().optional().describe("Name for a new team to create for this project")
2042
+ },
2043
+ withToolLogging("platform_create_project", async (params) => {
2044
+ requireApiKey();
2045
+ const { handleCreateProject } = await import("./create-project-CUVQ67VN.js");
2046
+ return {
2047
+ content: [{ type: "text", text: await handleCreateProject(params) }]
2048
+ };
2049
+ })
2050
+ );
2051
+ server.tool(
2052
+ "platform_update_project",
2053
+ "Update an existing project's name, language, framework, or PII redaction level.",
2054
+ {
2055
+ id: z2.string().describe("The project ID to update"),
2056
+ name: z2.string().optional().describe("New project name"),
2057
+ language: z2.string().optional().describe("New programming language"),
2058
+ framework: z2.string().optional().describe("New framework"),
2059
+ piiRedactionLevel: z2.enum(["STRICT", "ESSENTIAL", "DISABLED"]).optional().describe("PII redaction level")
2060
+ },
2061
+ withToolLogging("platform_update_project", async (params) => {
2062
+ requireApiKey();
2063
+ const { handleUpdateProject } = await import("./update-project-OIT4WPFI.js");
2064
+ return {
2065
+ content: [{ type: "text", text: await handleUpdateProject(params) }]
2066
+ };
2067
+ })
2068
+ );
2069
+ server.tool(
2070
+ "platform_archive_project",
2071
+ "Archive (soft-delete) a project. The project can be restored later if needed.",
2072
+ {
2073
+ id: z2.string().describe("The project ID to archive")
2074
+ },
2075
+ withToolLogging("platform_archive_project", async (params) => {
2076
+ requireApiKey();
2077
+ const { handleArchiveProject } = await import("./archive-project-HZBRPR2S.js");
2078
+ return {
2079
+ content: [{ type: "text", text: await handleArchiveProject(params) }]
2080
+ };
2081
+ })
2082
+ );
2083
+ server.tool(
2084
+ "platform_list_api_keys",
2085
+ "List all API keys in your LangWatch organization. Shows key metadata, status (active/revoked/expired), and role bindings. API key tokens are never returned.",
2086
+ {},
2087
+ withToolLogging("platform_list_api_keys", async () => {
2088
+ requireApiKey();
2089
+ const { handleListApiKeys } = await import("./list-api-keys-AWW7ROLB.js");
2090
+ return {
2091
+ content: [{ type: "text", text: await handleListApiKeys() }]
2092
+ };
2093
+ })
2094
+ );
2095
+ server.tool(
2096
+ "platform_create_api_key",
2097
+ `Create a new API key. Two types available:
2098
+
2099
+ - **personal**: Tied to a user, requires explicit role bindings (e.g. ADMIN on PROJECT:proj_123).
2100
+ - **service**: Not tied to a user. Use projectIds for project-scoped service keys, or omit for org-wide access.
2101
+
2102
+ The token is returned once and cannot be retrieved again.`,
2103
+ {
2104
+ keyType: z2.enum(["personal", "service"]).describe("Key type: 'personal' (user-tied) or 'service' (headless)"),
2105
+ name: z2.string().describe("Key name (max 100 chars)"),
2106
+ description: z2.string().optional().describe("Key description (max 500 chars)"),
2107
+ expiresAt: z2.string().optional().describe("Expiration date (ISO string)"),
2108
+ bindings: z2.array(z2.object({
2109
+ role: z2.enum(["ADMIN", "MEMBER", "VIEWER"]).describe("Role"),
2110
+ scopeType: z2.enum(["ORGANIZATION", "TEAM", "PROJECT"]).describe("Scope type"),
2111
+ scopeId: z2.string().describe("Scope ID (org, team, or project ID)")
2112
+ })).optional().describe("Role bindings (required for personal keys)"),
2113
+ projectIds: z2.array(z2.string()).optional().describe("Project IDs for service keys (creates ADMIN bindings per project)")
2114
+ },
2115
+ withToolLogging("platform_create_api_key", async (params) => {
2116
+ requireApiKey();
2117
+ const { handleCreateApiKey } = await import("./create-api-key-OQI5PUNQ.js");
2118
+ return {
2119
+ content: [{ type: "text", text: await handleCreateApiKey(params) }]
2120
+ };
2121
+ })
2122
+ );
2123
+ server.tool(
2124
+ "platform_revoke_api_key",
2125
+ "Revoke an API key. Once revoked, the key can no longer be used for authentication.",
2126
+ {
2127
+ id: z2.string().describe("The API key ID to revoke")
2128
+ },
2129
+ withToolLogging("platform_revoke_api_key", async (params) => {
2130
+ requireApiKey();
2131
+ const { handleRevokeApiKey } = await import("./revoke-api-key-ZSPSPF5G.js");
2132
+ return {
2133
+ content: [{ type: "text", text: await handleRevokeApiKey(params) }]
2134
+ };
2135
+ })
2136
+ );
1568
2137
  }
1569
2138
 
1570
2139
  export {
1571
2140
  createMcpServer
1572
2141
  };
1573
- //# sourceMappingURL=chunk-B57MZ4WI.js.map
2142
+ //# sourceMappingURL=chunk-JVLJKQJG.js.map