@langwatch/mcp-server 0.4.0 → 0.6.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 (91) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/archive-scenario-YFD5THOR.js +19 -0
  3. package/dist/archive-scenario-YFD5THOR.js.map +1 -0
  4. package/dist/chunk-5UOPNRXW.js +37 -0
  5. package/dist/chunk-5UOPNRXW.js.map +1 -0
  6. package/dist/chunk-6U4TCGFC.js +40 -0
  7. package/dist/chunk-6U4TCGFC.js.map +1 -0
  8. package/dist/chunk-IX6QJKAD.js +22 -0
  9. package/dist/chunk-IX6QJKAD.js.map +1 -0
  10. package/dist/{chunk-HOPTUDCZ.js → chunk-LLRQIF52.js} +5 -12
  11. package/dist/chunk-LLRQIF52.js.map +1 -0
  12. package/dist/create-evaluator-E5X5ZP3B.js +27 -0
  13. package/dist/create-evaluator-E5X5ZP3B.js.map +1 -0
  14. package/dist/create-prompt-7Z35MIL6.js +36 -0
  15. package/dist/create-prompt-7Z35MIL6.js.map +1 -0
  16. package/dist/create-scenario-DIMPJRPY.js +26 -0
  17. package/dist/create-scenario-DIMPJRPY.js.map +1 -0
  18. package/dist/discover-evaluator-schema-H23XCLNE.js +1402 -0
  19. package/dist/discover-evaluator-schema-H23XCLNE.js.map +1 -0
  20. package/dist/discover-scenario-schema-MEEEVND7.js +65 -0
  21. package/dist/discover-scenario-schema-MEEEVND7.js.map +1 -0
  22. package/dist/{get-analytics-3IFTN6MY.js → get-analytics-4YJW4S5L.js} +2 -2
  23. package/dist/get-evaluator-WDEH2F7M.js +47 -0
  24. package/dist/get-evaluator-WDEH2F7M.js.map +1 -0
  25. package/dist/{get-prompt-2ZB5B3QC.js → get-prompt-F6PDVC76.js} +2 -5
  26. package/dist/get-prompt-F6PDVC76.js.map +1 -0
  27. package/dist/get-scenario-H24ZYNT5.js +33 -0
  28. package/dist/get-scenario-H24ZYNT5.js.map +1 -0
  29. package/dist/{get-trace-7IXKKCJJ.js → get-trace-27USKGO7.js} +2 -2
  30. package/dist/index.js +27066 -8845
  31. package/dist/index.js.map +1 -1
  32. package/dist/list-evaluators-KRGI72EH.js +34 -0
  33. package/dist/list-evaluators-KRGI72EH.js.map +1 -0
  34. package/dist/list-model-providers-A5YCFTPI.js +35 -0
  35. package/dist/list-model-providers-A5YCFTPI.js.map +1 -0
  36. package/dist/{list-prompts-J72LTP7Z.js → list-prompts-LKJSE7XN.js} +6 -7
  37. package/dist/list-prompts-LKJSE7XN.js.map +1 -0
  38. package/dist/list-scenarios-ZK5CMGC4.js +40 -0
  39. package/dist/list-scenarios-ZK5CMGC4.js.map +1 -0
  40. package/dist/{search-traces-RW2NDHN5.js → search-traces-SOKAAMAR.js} +2 -2
  41. package/dist/set-model-provider-7MGULZDH.js +33 -0
  42. package/dist/set-model-provider-7MGULZDH.js.map +1 -0
  43. package/dist/update-evaluator-A3XINFLJ.js +24 -0
  44. package/dist/update-evaluator-A3XINFLJ.js.map +1 -0
  45. package/dist/update-prompt-IW7X2UQM.js +22 -0
  46. package/dist/update-prompt-IW7X2UQM.js.map +1 -0
  47. package/dist/update-scenario-ZT7TOBFR.js +27 -0
  48. package/dist/update-scenario-ZT7TOBFR.js.map +1 -0
  49. package/package.json +11 -11
  50. package/src/__tests__/all-tools.integration.test.ts +1337 -0
  51. package/src/__tests__/discover-evaluator-schema.unit.test.ts +89 -0
  52. package/src/__tests__/evaluator-tools.unit.test.ts +262 -0
  53. package/src/__tests__/integration.integration.test.ts +9 -34
  54. package/src/__tests__/langwatch-api.unit.test.ts +4 -32
  55. package/src/__tests__/model-provider-tools.unit.test.ts +190 -0
  56. package/src/__tests__/scenario-tools.integration.test.ts +286 -0
  57. package/src/__tests__/scenario-tools.unit.test.ts +185 -0
  58. package/src/__tests__/tools.unit.test.ts +59 -65
  59. package/src/index.ts +338 -48
  60. package/src/langwatch-api-evaluators.ts +70 -0
  61. package/src/langwatch-api-model-providers.ts +41 -0
  62. package/src/langwatch-api-scenarios.ts +67 -0
  63. package/src/langwatch-api.ts +6 -30
  64. package/src/tools/archive-scenario.ts +19 -0
  65. package/src/tools/create-evaluator.ts +33 -0
  66. package/src/tools/create-prompt.ts +30 -5
  67. package/src/tools/create-scenario.ts +30 -0
  68. package/src/tools/discover-evaluator-schema.ts +143 -0
  69. package/src/tools/discover-scenario-schema.ts +71 -0
  70. package/src/tools/get-evaluator.ts +53 -0
  71. package/src/tools/get-prompt.ts +1 -4
  72. package/src/tools/get-scenario.ts +36 -0
  73. package/src/tools/list-evaluators.ts +37 -0
  74. package/src/tools/list-model-providers.ts +40 -0
  75. package/src/tools/list-prompts.ts +5 -6
  76. package/src/tools/list-scenarios.ts +47 -0
  77. package/src/tools/set-model-provider.ts +46 -0
  78. package/src/tools/update-evaluator.ts +30 -0
  79. package/src/tools/update-prompt.ts +9 -25
  80. package/src/tools/update-scenario.ts +32 -0
  81. package/uv.lock +1788 -1322
  82. package/dist/chunk-HOPTUDCZ.js.map +0 -1
  83. package/dist/create-prompt-UBC537BJ.js +0 -22
  84. package/dist/create-prompt-UBC537BJ.js.map +0 -1
  85. package/dist/get-prompt-2ZB5B3QC.js.map +0 -1
  86. package/dist/list-prompts-J72LTP7Z.js.map +0 -1
  87. package/dist/update-prompt-G6HHZSUM.js +0 -31
  88. package/dist/update-prompt-G6HHZSUM.js.map +0 -1
  89. /package/dist/{get-analytics-3IFTN6MY.js.map → get-analytics-4YJW4S5L.js.map} +0 -0
  90. /package/dist/{get-trace-7IXKKCJJ.js.map → get-trace-27USKGO7.js.map} +0 -0
  91. /package/dist/{search-traces-RW2NDHN5.js.map → search-traces-SOKAAMAR.js.map} +0 -0
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { formatEvaluatorSchema } from "../tools/discover-evaluator-schema.js";
3
+
4
+ describe("formatEvaluatorSchema()", () => {
5
+ describe("when called without evaluatorType (overview)", () => {
6
+ it("includes the overview header", () => {
7
+ const result = formatEvaluatorSchema();
8
+ expect(result).toContain("# Available Evaluator Types");
9
+ });
10
+
11
+ it("includes category sections", () => {
12
+ const result = formatEvaluatorSchema();
13
+ expect(result).toContain("## safety");
14
+ });
15
+
16
+ it("includes evaluator type identifiers", () => {
17
+ const result = formatEvaluatorSchema();
18
+ expect(result).toContain("openai/moderation");
19
+ expect(result).toContain("langevals/exact_match");
20
+ });
21
+
22
+ it("includes evaluator display names", () => {
23
+ const result = formatEvaluatorSchema();
24
+ expect(result).toContain("OpenAI Moderation");
25
+ });
26
+
27
+ it("instructs the agent to use evaluatorType parameter for full details", () => {
28
+ const result = formatEvaluatorSchema();
29
+ expect(result).toContain(
30
+ "discover_schema({ category: 'evaluators', evaluatorType: '<type>' })",
31
+ );
32
+ });
33
+ });
34
+
35
+ describe("when called with a valid evaluatorType (detail)", () => {
36
+ it("includes the evaluator name in the heading", () => {
37
+ const result = formatEvaluatorSchema("openai/moderation");
38
+ expect(result).toContain("# OpenAI Moderation");
39
+ });
40
+
41
+ it("includes the evaluator type in backticks", () => {
42
+ const result = formatEvaluatorSchema("openai/moderation");
43
+ expect(result).toContain("`openai/moderation`");
44
+ });
45
+
46
+ it("includes the category", () => {
47
+ const result = formatEvaluatorSchema("openai/moderation");
48
+ expect(result).toContain("**Category**: safety");
49
+ });
50
+
51
+ it("includes settings with descriptions and defaults", () => {
52
+ const result = formatEvaluatorSchema("openai/moderation");
53
+ expect(result).toContain("## Settings");
54
+ expect(result).toContain("**model**");
55
+ expect(result).toContain("Default:");
56
+ });
57
+
58
+ it("includes required/optional fields section", () => {
59
+ const result = formatEvaluatorSchema("openai/moderation");
60
+ expect(result).toContain("## Fields");
61
+ });
62
+
63
+ it("includes result fields", () => {
64
+ const result = formatEvaluatorSchema("openai/moderation");
65
+ expect(result).toContain("## Result Fields");
66
+ });
67
+
68
+ it("includes a usage example with evaluatorType", () => {
69
+ const result = formatEvaluatorSchema("openai/moderation");
70
+ expect(result).toContain("## Usage Example");
71
+ expect(result).toContain('"evaluatorType"');
72
+ });
73
+
74
+ it("includes env vars when the evaluator requires them", () => {
75
+ const result = formatEvaluatorSchema("azure/content_safety");
76
+ expect(result).toContain("## Required Environment Variables");
77
+ expect(result).toContain("AZURE_CONTENT_SAFETY_ENDPOINT");
78
+ });
79
+ });
80
+
81
+ describe("when called with an unknown evaluatorType", () => {
82
+ it("returns an error message with guidance", () => {
83
+ const result = formatEvaluatorSchema("nonexistent/type");
84
+ expect(result).toContain("Unknown evaluator type");
85
+ expect(result).toContain("nonexistent/type");
86
+ expect(result).toContain("discover_schema");
87
+ });
88
+ });
89
+ });
@@ -0,0 +1,262 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ vi.mock("../langwatch-api-evaluators.js", async (importOriginal) => {
4
+ const actual = await importOriginal() as Record<string, unknown>;
5
+ return {
6
+ ...actual,
7
+ listEvaluators: vi.fn(),
8
+ getEvaluator: vi.fn(),
9
+ createEvaluator: vi.fn(),
10
+ updateEvaluator: vi.fn(),
11
+ };
12
+ });
13
+
14
+ import {
15
+ listEvaluators,
16
+ getEvaluator,
17
+ createEvaluator,
18
+ updateEvaluator,
19
+ } from "../langwatch-api-evaluators.js";
20
+
21
+ import { handleListEvaluators } from "../tools/list-evaluators.js";
22
+ import { handleGetEvaluator } from "../tools/get-evaluator.js";
23
+ import { handleCreateEvaluator } from "../tools/create-evaluator.js";
24
+ import { handleUpdateEvaluator } from "../tools/update-evaluator.js";
25
+
26
+ const mockListEvaluators = vi.mocked(listEvaluators);
27
+ const mockGetEvaluator = vi.mocked(getEvaluator);
28
+ const mockCreateEvaluator = vi.mocked(createEvaluator);
29
+ const mockUpdateEvaluator = vi.mocked(updateEvaluator);
30
+
31
+ beforeEach(() => {
32
+ vi.clearAllMocks();
33
+ });
34
+
35
+ describe("handleListEvaluators()", () => {
36
+ const sampleEvaluators = [
37
+ {
38
+ id: "evaluator_abc123",
39
+ projectId: "proj_1",
40
+ name: "Toxicity Check",
41
+ slug: "toxicity-check",
42
+ type: "evaluator",
43
+ config: { evaluatorType: "openai/moderation" },
44
+ workflowId: null,
45
+ copiedFromEvaluatorId: null,
46
+ createdAt: "2024-01-01T00:00:00.000Z",
47
+ updatedAt: "2024-01-01T00:00:00.000Z",
48
+ fields: [{ identifier: "input", type: "str" }],
49
+ outputFields: [{ identifier: "passed", type: "bool" }],
50
+ },
51
+ {
52
+ id: "evaluator_def456",
53
+ projectId: "proj_1",
54
+ name: "Exact Match",
55
+ slug: "exact-match",
56
+ type: "evaluator",
57
+ config: { evaluatorType: "langevals/exact_match" },
58
+ workflowId: null,
59
+ copiedFromEvaluatorId: null,
60
+ createdAt: "2024-01-02T00:00:00.000Z",
61
+ updatedAt: "2024-01-02T00:00:00.000Z",
62
+ fields: [
63
+ { identifier: "output", type: "str" },
64
+ { identifier: "expected_output", type: "str" },
65
+ ],
66
+ outputFields: [{ identifier: "passed", type: "bool" }],
67
+ },
68
+ ];
69
+
70
+ describe("when evaluators exist", () => {
71
+ let result: string;
72
+
73
+ beforeEach(async () => {
74
+ mockListEvaluators.mockResolvedValue(sampleEvaluators);
75
+ result = await handleListEvaluators();
76
+ });
77
+
78
+ it("includes evaluator id", () => {
79
+ expect(result).toContain("evaluator_abc123");
80
+ });
81
+
82
+ it("includes evaluator name", () => {
83
+ expect(result).toContain("Toxicity Check");
84
+ });
85
+
86
+ it("includes evaluator type", () => {
87
+ expect(result).toContain("openai/moderation");
88
+ });
89
+
90
+ it("includes slug", () => {
91
+ expect(result).toContain("toxicity-check");
92
+ });
93
+
94
+ it("includes all evaluators in the list", () => {
95
+ expect(result).toContain("evaluator_def456");
96
+ });
97
+
98
+ it("includes the total count header", () => {
99
+ expect(result).toContain("# Evaluators (2 total)");
100
+ });
101
+ });
102
+
103
+ describe("when no evaluators exist", () => {
104
+ let result: string;
105
+
106
+ beforeEach(async () => {
107
+ mockListEvaluators.mockResolvedValue([]);
108
+ result = await handleListEvaluators();
109
+ });
110
+
111
+ it("returns a no-evaluators message", () => {
112
+ expect(result).toContain("No evaluators found");
113
+ });
114
+
115
+ it("includes a tip to use platform_create_evaluator", () => {
116
+ expect(result).toContain("platform_create_evaluator");
117
+ });
118
+ });
119
+ });
120
+
121
+ describe("handleGetEvaluator()", () => {
122
+ const sampleEvaluator = {
123
+ id: "evaluator_abc123",
124
+ projectId: "proj_1",
125
+ name: "Toxicity Check",
126
+ slug: "toxicity-check",
127
+ type: "evaluator",
128
+ config: {
129
+ evaluatorType: "openai/moderation",
130
+ settings: { model: "text-moderation-stable" },
131
+ },
132
+ workflowId: null,
133
+ copiedFromEvaluatorId: null,
134
+ createdAt: "2024-01-01T00:00:00.000Z",
135
+ updatedAt: "2024-01-01T00:00:00.000Z",
136
+ fields: [
137
+ { identifier: "input", type: "str" },
138
+ { identifier: "output", type: "str", optional: true },
139
+ ],
140
+ outputFields: [
141
+ { identifier: "passed", type: "bool" },
142
+ { identifier: "score", type: "float" },
143
+ ],
144
+ };
145
+
146
+ describe("when evaluator is found", () => {
147
+ let result: string;
148
+
149
+ beforeEach(async () => {
150
+ mockGetEvaluator.mockResolvedValue(sampleEvaluator);
151
+ result = await handleGetEvaluator({ idOrSlug: "evaluator_abc123" });
152
+ });
153
+
154
+ it("includes the evaluator name in the heading", () => {
155
+ expect(result).toContain("# Evaluator: Toxicity Check");
156
+ });
157
+
158
+ it("includes the evaluator type", () => {
159
+ expect(result).toContain("openai/moderation");
160
+ });
161
+
162
+ it("includes the config as JSON", () => {
163
+ expect(result).toContain("text-moderation-stable");
164
+ });
165
+
166
+ it("includes input fields", () => {
167
+ expect(result).toContain("## Input Fields");
168
+ expect(result).toContain("**input** (str)");
169
+ });
170
+
171
+ it("marks optional fields", () => {
172
+ expect(result).toContain("(optional)");
173
+ });
174
+
175
+ it("includes output fields", () => {
176
+ expect(result).toContain("## Output Fields");
177
+ expect(result).toContain("**passed** (bool)");
178
+ });
179
+ });
180
+ });
181
+
182
+ describe("handleCreateEvaluator()", () => {
183
+ describe("when creation succeeds", () => {
184
+ let result: string;
185
+
186
+ beforeEach(async () => {
187
+ mockCreateEvaluator.mockResolvedValue({
188
+ id: "evaluator_new123",
189
+ projectId: "proj_1",
190
+ name: "My LLM Judge",
191
+ slug: "my-llm-judge",
192
+ type: "evaluator",
193
+ config: { evaluatorType: "langevals/llm_boolean" },
194
+ workflowId: null,
195
+ copiedFromEvaluatorId: null,
196
+ createdAt: "2024-01-01T00:00:00.000Z",
197
+ updatedAt: "2024-01-01T00:00:00.000Z",
198
+ fields: [{ identifier: "input", type: "str" }],
199
+ outputFields: [{ identifier: "passed", type: "bool" }],
200
+ });
201
+ result = await handleCreateEvaluator({
202
+ name: "My LLM Judge",
203
+ config: { evaluatorType: "langevals/llm_boolean" },
204
+ });
205
+ });
206
+
207
+ it("confirms creation", () => {
208
+ expect(result).toContain("Evaluator created successfully!");
209
+ });
210
+
211
+ it("includes the generated ID", () => {
212
+ expect(result).toContain("evaluator_new123");
213
+ });
214
+
215
+ it("includes the slug", () => {
216
+ expect(result).toContain("my-llm-judge");
217
+ });
218
+
219
+ it("includes the evaluator type", () => {
220
+ expect(result).toContain("langevals/llm_boolean");
221
+ });
222
+ });
223
+ });
224
+
225
+ describe("handleUpdateEvaluator()", () => {
226
+ describe("when update succeeds", () => {
227
+ let result: string;
228
+
229
+ beforeEach(async () => {
230
+ mockUpdateEvaluator.mockResolvedValue({
231
+ id: "evaluator_abc123",
232
+ projectId: "proj_1",
233
+ name: "Updated Name",
234
+ slug: "toxicity-check",
235
+ type: "evaluator",
236
+ config: { evaluatorType: "openai/moderation" },
237
+ workflowId: null,
238
+ copiedFromEvaluatorId: null,
239
+ createdAt: "2024-01-01T00:00:00.000Z",
240
+ updatedAt: "2024-01-02T00:00:00.000Z",
241
+ fields: [],
242
+ outputFields: [],
243
+ });
244
+ result = await handleUpdateEvaluator({
245
+ evaluatorId: "evaluator_abc123",
246
+ name: "Updated Name",
247
+ });
248
+ });
249
+
250
+ it("confirms update", () => {
251
+ expect(result).toContain("Evaluator updated successfully!");
252
+ });
253
+
254
+ it("includes the evaluator ID", () => {
255
+ expect(result).toContain("evaluator_abc123");
256
+ });
257
+
258
+ it("includes the updated name", () => {
259
+ expect(result).toContain("Updated Name");
260
+ });
261
+ });
262
+ });
@@ -49,14 +49,12 @@ const CANNED_PROMPTS_LIST = [
49
49
  id: "p1",
50
50
  handle: "greeting-bot",
51
51
  name: "Greeting Bot",
52
- description: "A friendly greeting bot",
53
52
  latestVersionNumber: 3,
54
53
  },
55
54
  {
56
55
  id: "p2",
57
56
  handle: "qa-assistant",
58
57
  name: "QA Assistant",
59
- description: null,
60
58
  latestVersionNumber: 1,
61
59
  },
62
60
  ];
@@ -65,14 +63,12 @@ const CANNED_PROMPT_DETAIL = {
65
63
  id: "p1",
66
64
  handle: "greeting-bot",
67
65
  name: "Greeting Bot",
68
- description: "A friendly greeting bot",
69
66
  latestVersionNumber: 3,
70
67
  versions: [
71
68
  {
72
69
  version: 3,
73
70
  commitMessage: "Updated tone",
74
- model: "gpt-4o",
75
- modelProvider: "openai",
71
+ model: "openai/gpt-4o",
76
72
  messages: [{ role: "system", content: "You are a friendly bot." }],
77
73
  },
78
74
  { version: 2, commitMessage: "Added greeting" },
@@ -128,12 +124,6 @@ function createMockServer(): Server {
128
124
  } else if (url === "/api/prompts" && req.method === "GET") {
129
125
  res.writeHead(200);
130
126
  res.end(JSON.stringify(CANNED_PROMPTS_LIST));
131
- } else if (
132
- url.match(/^\/api\/prompts\/[^/]+\/versions/) &&
133
- req.method === "POST"
134
- ) {
135
- res.writeHead(200);
136
- res.end(JSON.stringify(CANNED_PROMPT_UPDATED));
137
127
  } else if (url.match(/^\/api\/prompts\/[^/]+$/) && req.method === "GET") {
138
128
  res.writeHead(200);
139
129
  res.end(JSON.stringify(CANNED_PROMPT_DETAIL));
@@ -142,7 +132,7 @@ function createMockServer(): Server {
142
132
  res.end(JSON.stringify(CANNED_PROMPT_CREATED));
143
133
  } else if (
144
134
  url.match(/^\/api\/prompts\/[^/]+$/) &&
145
- req.method === "POST"
135
+ req.method === "PUT"
146
136
  ) {
147
137
  res.writeHead(200);
148
138
  res.end(JSON.stringify(CANNED_PROMPT_UPDATED));
@@ -222,7 +212,7 @@ describe("MCP tools integration", () => {
222
212
  });
223
213
  });
224
214
 
225
- describe("list_prompts", () => {
215
+ describe("platform_list_prompts", () => {
226
216
  it("returns formatted prompt list from mock server", async () => {
227
217
  const { handleListPrompts } = await import("../tools/list-prompts.js");
228
218
  const result = await handleListPrompts();
@@ -232,7 +222,7 @@ describe("MCP tools integration", () => {
232
222
  });
233
223
  });
234
224
 
235
- describe("get_prompt", () => {
225
+ describe("platform_get_prompt", () => {
236
226
  it("returns formatted prompt details from mock server", async () => {
237
227
  const { handleGetPrompt } = await import("../tools/get-prompt.js");
238
228
  const result = await handleGetPrompt({ idOrHandle: "greeting-bot" });
@@ -243,7 +233,7 @@ describe("MCP tools integration", () => {
243
233
  });
244
234
  });
245
235
 
246
- describe("create_prompt", () => {
236
+ describe("platform_create_prompt", () => {
247
237
  it("returns success message from mock server", async () => {
248
238
  const { handleCreatePrompt } = await import(
249
239
  "../tools/create-prompt.js"
@@ -251,41 +241,26 @@ describe("MCP tools integration", () => {
251
241
  const result = await handleCreatePrompt({
252
242
  name: "New Prompt",
253
243
  messages: [{ role: "system", content: "You are helpful." }],
254
- model: "gpt-4o",
255
- modelProvider: "openai",
244
+ model: "openai/gpt-4o",
256
245
  });
257
246
  expect(result).toContain("created successfully");
258
247
  expect(result).toContain("p-new");
259
248
  });
260
249
  });
261
250
 
262
- describe("update_prompt", () => {
263
- it("returns success message for in-place update from mock server", async () => {
251
+ describe("platform_update_prompt", () => {
252
+ it("returns success message from mock server", async () => {
264
253
  const { handleUpdatePrompt } = await import(
265
254
  "../tools/update-prompt.js"
266
255
  );
267
256
  const result = await handleUpdatePrompt({
268
257
  idOrHandle: "greeting-bot",
269
- model: "gpt-4o-mini",
258
+ model: "openai/gpt-4o-mini",
270
259
  commitMessage: "Switch to mini",
271
260
  });
272
261
  expect(result).toContain("updated successfully");
273
262
  expect(result).toContain("Switch to mini");
274
263
  });
275
-
276
- it("returns success message for version creation from mock server", async () => {
277
- const { handleUpdatePrompt } = await import(
278
- "../tools/update-prompt.js"
279
- );
280
- const result = await handleUpdatePrompt({
281
- idOrHandle: "greeting-bot",
282
- messages: [{ role: "system", content: "Be concise." }],
283
- createVersion: true,
284
- commitMessage: "Make concise",
285
- });
286
- expect(result).toContain("version created");
287
- expect(result).toContain("Make concise");
288
- });
289
264
  });
290
265
 
291
266
  describe("when API key is invalid", () => {
@@ -208,10 +208,9 @@ describe("langwatch-api", () => {
208
208
  it("sends POST to /api/prompts with body", async () => {
209
209
  const { createPrompt } = await import("../langwatch-api.js");
210
210
  const data = {
211
- name: "Test Prompt",
211
+ handle: "test-prompt",
212
212
  messages: [{ role: "system", content: "You are helpful." }],
213
- model: "gpt-4o",
214
- modelProvider: "openai",
213
+ model: "openai/gpt-4o",
215
214
  };
216
215
  const responseData = { id: "new-id", ...data };
217
216
  mockJsonResponse(responseData);
@@ -234,7 +233,7 @@ describe("langwatch-api", () => {
234
233
  });
235
234
 
236
235
  describe("updatePrompt()", () => {
237
- it("sends POST to /api/prompts/{id} with body", async () => {
236
+ it("sends PUT to /api/prompts/{id} with body", async () => {
238
237
  const { updatePrompt } = await import("../langwatch-api.js");
239
238
  const data = {
240
239
  messages: [{ role: "system", content: "Updated" }],
@@ -248,34 +247,7 @@ describe("langwatch-api", () => {
248
247
  expect(mockFetch).toHaveBeenCalledWith(
249
248
  `${TEST_ENDPOINT}/api/prompts/${encodeURIComponent("p1")}`,
250
249
  expect.objectContaining({
251
- method: "POST",
252
- headers: expect.objectContaining({
253
- "X-Auth-Token": TEST_API_KEY,
254
- "Content-Type": "application/json",
255
- }),
256
- body: JSON.stringify(data),
257
- })
258
- );
259
- expect(result).toEqual(responseData);
260
- });
261
- });
262
-
263
- describe("createPromptVersion()", () => {
264
- it("sends POST to /api/prompts/{id}/versions with body", async () => {
265
- const { createPromptVersion } = await import("../langwatch-api.js");
266
- const data = {
267
- messages: [{ role: "user", content: "new version" }],
268
- commitMessage: "v2",
269
- };
270
- const responseData = { version: 2 };
271
- mockJsonResponse(responseData);
272
-
273
- const result = await createPromptVersion("p1", data);
274
-
275
- expect(mockFetch).toHaveBeenCalledWith(
276
- `${TEST_ENDPOINT}/api/prompts/${encodeURIComponent("p1")}/versions`,
277
- expect.objectContaining({
278
- method: "POST",
250
+ method: "PUT",
279
251
  headers: expect.objectContaining({
280
252
  "X-Auth-Token": TEST_API_KEY,
281
253
  "Content-Type": "application/json",