@arizeai/phoenix-mcp 3.1.5 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,12 @@
1
1
  import { createPrompt, promptVersion } from "@arizeai/phoenix-client/prompts";
2
- import { addPromptVersionTagSchema, createPromptSchema, getLatestPromptSchema, getPromptByIdentifierSchema, getPromptVersionByTagSchema, getPromptVersionSchema, listPromptsSchema, listPromptVersionsSchema, listPromptVersionTagsSchema, } from "./promptSchemas.js";
3
- // Tool descriptions as template literals for better readability
2
+ import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./constants.js";
3
+ import { fetchAllPages } from "./pagination.js";
4
+ import { addPromptVersionTagSchema, createPromptSchema, getLatestPromptSchema, getPromptSchema, getPromptByIdentifierSchema, getPromptVersionByTagSchema, getPromptVersionSchema, listPromptsSchema, listPromptVersionsSchema, listPromptVersionTagsSchema, } from "./promptSchemas.js";
5
+ import { getResponseData } from "./responseUtils.js";
6
+ import { jsonResponse, textResponse } from "./toolResults.js";
7
+ // ---------------------------------------------------------------------------
8
+ // Tool descriptions
9
+ // ---------------------------------------------------------------------------
4
10
  const LIST_PROMPTS_DESCRIPTION = `Get a list of all the prompts.
5
11
 
6
12
  Prompts (templates, prompt templates) are versioned templates for input messages to an LLM.
@@ -9,11 +15,11 @@ to use when generating outputs.
9
15
 
10
16
  Returns a list of prompt objects with their IDs, names, and descriptions.
11
17
 
12
- Example usage:
18
+ Example usage:
13
19
  List all available prompts
14
20
 
15
- Expected return:
16
- Array of prompt objects with metadata.
21
+ Expected return:
22
+ Array of prompt objects with metadata.
17
23
  Example: [{
18
24
  "name": "article-summarizer",
19
25
  "description": "Summarizes an article into concise bullet points",
@@ -22,11 +28,11 @@ Expected return:
22
28
  }]`;
23
29
  const GET_LATEST_PROMPT_DESCRIPTION = `Get the latest version of a prompt. Returns the prompt version with its template, model configuration, and invocation parameters.
24
30
 
25
- Example usage:
31
+ Example usage:
26
32
  Get the latest version of a prompt named 'article-summarizer'
27
33
 
28
- Expected return:
29
- Prompt version object with template and configuration.
34
+ Expected return:
35
+ Prompt version object with template and configuration.
30
36
  Example: {
31
37
  "description": "Initial version",
32
38
  "model_provider": "OPENAI",
@@ -40,7 +46,7 @@ Expected return:
40
46
  },
41
47
  {
42
48
  "role": "user",
43
- "content": "Please summarize the following {{topic}} article:\n\n{{article}}"
49
+ "content": "Please summarize the following {{topic}} article:\\n\\n{{article}}"
44
50
  }
45
51
  ]
46
52
  },
@@ -54,285 +60,210 @@ Expected return:
54
60
  }`;
55
61
  const GET_PROMPT_BY_IDENTIFIER_DESCRIPTION = `Get a prompt's latest version by its identifier (name or ID). Returns the prompt version with its template, model configuration, and invocation parameters.
56
62
 
57
- Example usage:
63
+ Example usage:
58
64
  Get the latest version of a prompt with name 'article-summarizer'
59
65
 
60
- Expected return:
61
- Prompt version object with template and configuration.
62
- Example: {
63
- "description": "Initial version",
64
- "model_provider": "OPENAI",
65
- "model_name": "gpt-3.5-turbo",
66
- "template": {
67
- "type": "chat",
68
- "messages": [
69
- {
70
- "role": "system",
71
- "content": "You are an expert summarizer. Create clear, concise bullet points highlighting the key information."
72
- },
73
- {
74
- "role": "user",
75
- "content": "Please summarize the following {{topic}} article:\n\n{{article}}"
76
- }
77
- ]
78
- },
79
- "template_type": "CHAT",
80
- "template_format": "MUSTACHE",
81
- "invocation_parameters": {
82
- "type": "openai",
83
- "openai": {}
84
- },
85
- "id": "promptversionid1234"
86
- }`;
66
+ Expected return:
67
+ Prompt version object with template and configuration.`;
87
68
  const GET_PROMPT_VERSION_DESCRIPTION = `Get a specific version of a prompt using its version ID. Returns the prompt version with its template, model configuration, and invocation parameters.
88
69
 
89
- Example usage:
70
+ Example usage:
90
71
  Get a specific prompt version with ID 'promptversionid1234'
91
72
 
92
- Expected return:
93
- Prompt version object with template and configuration.
94
- Example: {
95
- "description": "Initial version",
96
- "model_provider": "OPENAI",
97
- "model_name": "gpt-3.5-turbo",
98
- "template": {
99
- "type": "chat",
100
- "messages": [
101
- {
102
- "role": "system",
103
- "content": "You are an expert summarizer. Create clear, concise bullet points highlighting the key information."
104
- },
105
- {
106
- "role": "user",
107
- "content": "Please summarize the following {{topic}} article:\n\n{{article}}"
108
- }
109
- ]
110
- },
111
- "template_type": "CHAT",
112
- "template_format": "MUSTACHE",
113
- "invocation_parameters": {
114
- "type": "openai",
115
- "openai": {}
116
- },
117
- "id": "promptversionid1234"
118
- }`;
73
+ Expected return:
74
+ Prompt version object with template and configuration.`;
75
+ const GET_PROMPT_DESCRIPTION = `Get a prompt using a single MCP-native interface.
76
+
77
+ Provide a prompt identifier to fetch the latest version, or add a tag or versionId to select a specific version.
78
+
79
+ Example usage:
80
+ Get prompt "article-summarizer"
81
+ Get prompt "article-summarizer" with tag "production"
82
+ Get prompt "article-summarizer" using version "promptversionid1234"
83
+
84
+ Expected return:
85
+ Prompt version object with template and configuration.`;
119
86
  const UPSERT_PROMPT_DESCRIPTION = `Create or update a prompt with its template and configuration. Creates a new prompt and its initial version with specified model settings.
120
87
 
121
- Example usage:
88
+ Example usage:
122
89
  Create a new prompt named 'email_generator' with a template for generating emails
123
90
 
124
- Expected return:
91
+ Expected return:
125
92
  A confirmation message of successful prompt creation`;
126
93
  const LIST_PROMPT_VERSIONS_DESCRIPTION = `Get a list of all versions for a specific prompt. Returns versions with pagination support.
127
94
 
128
- Example usage:
95
+ Example usage:
129
96
  List all versions of a prompt named 'article-summarizer'
130
97
 
131
- Expected return:
132
- Array of prompt version objects with IDs and configuration.
133
- Example: [
134
- {
135
- "description": "Initial version",
136
- "model_provider": "OPENAI",
137
- "model_name": "gpt-3.5-turbo",
138
- "template": {
139
- "type": "chat",
140
- "messages": [
141
- {
142
- "role": "system",
143
- "content": "You are an expert summarizer. Create clear, concise bullet points highlighting the key information."
144
- },
145
- {
146
- "role": "user",
147
- "content": "Please summarize the following {{topic}} article:\n\n{{article}}"
148
- }
149
- ]
150
- },
151
- "template_type": "CHAT",
152
- "template_format": "MUSTACHE",
153
- "invocation_parameters": {
154
- "type": "openai",
155
- "openai": {}
156
- },
157
- "id": "promptversionid1234"
158
- }
159
- ]`;
98
+ Expected return:
99
+ Array of prompt version objects with IDs and configuration.`;
160
100
  const GET_PROMPT_VERSION_BY_TAG_DESCRIPTION = `Get a prompt version by its tag name. Returns the prompt version with its template, model configuration, and invocation parameters.
161
101
 
162
- Example usage:
102
+ Example usage:
163
103
  Get the 'production' tagged version of prompt 'article-summarizer'
164
104
 
165
- Expected return:
166
- Prompt version object with template and configuration.
167
- Example: {
168
- "description": "Initial version",
169
- "model_provider": "OPENAI",
170
- "model_name": "gpt-3.5-turbo",
171
- "template": {
172
- "type": "chat",
173
- "messages": [
174
- {
175
- "role": "system",
176
- "content": "You are an expert summarizer. Create clear, concise bullet points highlighting the key information."
177
- },
178
- {
179
- "role": "user",
180
- "content": "Please summarize the following {{topic}} article:\n\n{{article}}"
181
- }
182
- ]
183
- },
184
- "template_type": "CHAT",
185
- "template_format": "MUSTACHE",
186
- "invocation_parameters": {
187
- "type": "openai",
188
- "openai": {}
189
- },
190
- "id": "promptversionid1234"
191
- }`;
105
+ Expected return:
106
+ Prompt version object with template and configuration.`;
192
107
  const LIST_PROMPT_VERSION_TAGS_DESCRIPTION = `Get a list of all tags for a specific prompt version. Returns tag objects with pagination support.
193
108
 
194
- Example usage:
109
+ Example usage:
195
110
  List all tags associated with prompt version 'promptversionid1234'
196
111
 
197
- Expected return:
198
- Array of tag objects with names and IDs.
199
- Example: [
200
- {
201
- "name": "staging",
202
- "description": "The version deployed to staging",
203
- "id": "promptversionid1234"
204
- },
205
- {
206
- "name": "development",
207
- "description": "The version deployed for development",
208
- "id": "promptversionid1234"
209
- }
210
- ]`;
112
+ Expected return:
113
+ Array of tag objects with names and IDs.`;
211
114
  const ADD_PROMPT_VERSION_TAG_DESCRIPTION = `Add a tag to a specific prompt version. The operation returns no content on success (204 status code).
212
115
 
213
- Example usage:
116
+ Example usage:
214
117
  Tag prompt version 'promptversionid1234' with the name 'production'
215
118
 
216
- Expected return:
119
+ Expected return:
217
120
  Confirmation message of successful tag addition`;
218
- export const initializePromptTools = ({ client, server, }) => {
219
- server.tool("list-prompts", LIST_PROMPTS_DESCRIPTION, listPromptsSchema.shape, async ({ limit }) => {
220
- const response = await client.GET("/v1/prompts", {
121
+ // ---------------------------------------------------------------------------
122
+ // Helpers
123
+ // ---------------------------------------------------------------------------
124
+ /**
125
+ * Fetch a prompt version by the most specific selector available:
126
+ * versionId > tag > latest.
127
+ */
128
+ async function fetchPromptVersionBySelection({ client, promptIdentifier, tag, versionId, }) {
129
+ if (versionId) {
130
+ const response = await client.GET("/v1/prompt_versions/{prompt_version_id}", {
221
131
  params: {
222
- query: {
223
- limit,
132
+ path: {
133
+ prompt_version_id: versionId,
224
134
  },
225
135
  },
226
136
  });
227
- return {
228
- content: [
229
- {
230
- type: "text",
231
- text: JSON.stringify(response.data?.data, null, 2),
232
- },
233
- ],
234
- };
235
- });
236
- server.tool("get-latest-prompt", GET_LATEST_PROMPT_DESCRIPTION, getLatestPromptSchema.shape, async ({ prompt_identifier }) => {
237
- const response = await client.GET("/v1/prompts/{prompt_identifier}/latest", {
137
+ return getResponseData({
138
+ response,
139
+ errorPrefix: `Failed to fetch prompt version "${versionId}"`,
140
+ }).data;
141
+ }
142
+ if (!promptIdentifier) {
143
+ throw new Error("promptIdentifier is required when versionId is not provided");
144
+ }
145
+ if (tag) {
146
+ const response = await client.GET("/v1/prompts/{prompt_identifier}/tags/{tag_name}", {
238
147
  params: {
239
148
  path: {
240
- prompt_identifier,
149
+ prompt_identifier: promptIdentifier,
150
+ tag_name: tag,
241
151
  },
242
152
  },
243
153
  });
244
- return {
245
- content: [
246
- {
247
- type: "text",
248
- text: JSON.stringify(response.data, null, 2),
249
- },
250
- ],
251
- };
154
+ return getResponseData({
155
+ response,
156
+ errorPrefix: `Failed to fetch prompt "${promptIdentifier}" with tag "${tag}"`,
157
+ }).data;
158
+ }
159
+ const response = await client.GET("/v1/prompts/{prompt_identifier}/latest", {
160
+ params: {
161
+ path: {
162
+ prompt_identifier: promptIdentifier,
163
+ },
164
+ },
252
165
  });
253
- server.tool("get-prompt-by-identifier", GET_PROMPT_BY_IDENTIFIER_DESCRIPTION, getPromptByIdentifierSchema.shape, async ({ prompt_identifier }) => {
254
- const response = await client.GET("/v1/prompts/{prompt_identifier}/latest", {
255
- params: {
256
- path: {
257
- prompt_identifier,
258
- },
166
+ return getResponseData({
167
+ response,
168
+ errorPrefix: `Failed to fetch prompt "${promptIdentifier}"`,
169
+ }).data;
170
+ }
171
+ /**
172
+ * Build a single-message chat template for prompt version creation.
173
+ */
174
+ function buildChatTemplate(templateText) {
175
+ return [
176
+ {
177
+ role: "user",
178
+ content: [{ type: "text", text: templateText }],
179
+ },
180
+ ];
181
+ }
182
+ // ---------------------------------------------------------------------------
183
+ // Tool registration
184
+ // ---------------------------------------------------------------------------
185
+ /**
186
+ * Register all prompt-related MCP tools on the given server.
187
+ */
188
+ export const initializePromptTools = ({ client, server, }) => {
189
+ server.tool("list-prompts", LIST_PROMPTS_DESCRIPTION, listPromptsSchema.shape, async ({ limit }) => {
190
+ const prompts = await fetchAllPages({
191
+ limit,
192
+ fetchPage: async (cursor, pageSize) => {
193
+ const response = await client.GET("/v1/prompts", {
194
+ params: { query: { cursor, limit: pageSize } },
195
+ });
196
+ const data = getResponseData({
197
+ response,
198
+ errorPrefix: "Failed to fetch prompts",
199
+ });
200
+ return { data: data.data, nextCursor: data.next_cursor || undefined };
259
201
  },
260
202
  });
261
- return {
262
- content: [
263
- {
264
- type: "text",
265
- text: JSON.stringify(response.data, null, 2),
266
- },
267
- ],
268
- };
203
+ return jsonResponse(prompts);
204
+ });
205
+ server.tool("get-prompt", GET_PROMPT_DESCRIPTION, getPromptSchema.shape, async ({ prompt_identifier, tag, version_id }) => {
206
+ const prompt = await fetchPromptVersionBySelection({
207
+ client,
208
+ promptIdentifier: prompt_identifier,
209
+ tag,
210
+ versionId: version_id,
211
+ });
212
+ return jsonResponse(prompt);
213
+ });
214
+ server.tool("get-latest-prompt", GET_LATEST_PROMPT_DESCRIPTION, getLatestPromptSchema.shape, async ({ prompt_identifier }) => {
215
+ const prompt = await fetchPromptVersionBySelection({
216
+ client,
217
+ promptIdentifier: prompt_identifier,
218
+ });
219
+ return jsonResponse(prompt);
220
+ });
221
+ server.tool("get-prompt-by-identifier", GET_PROMPT_BY_IDENTIFIER_DESCRIPTION, getPromptByIdentifierSchema.shape, async ({ prompt_identifier }) => {
222
+ const prompt = await fetchPromptVersionBySelection({
223
+ client,
224
+ promptIdentifier: prompt_identifier,
225
+ });
226
+ return jsonResponse(prompt);
269
227
  });
270
228
  server.tool("get-prompt-version", GET_PROMPT_VERSION_DESCRIPTION, getPromptVersionSchema.shape, async ({ prompt_version_id }) => {
271
- const response = await client.GET("/v1/prompt_versions/{prompt_version_id}", {
272
- params: {
273
- path: {
274
- prompt_version_id,
275
- },
276
- },
229
+ const prompt = await fetchPromptVersionBySelection({
230
+ client,
231
+ versionId: prompt_version_id,
277
232
  });
278
- return {
279
- content: [
280
- {
281
- type: "text",
282
- text: JSON.stringify(response.data, null, 2),
283
- },
284
- ],
285
- };
233
+ return jsonResponse(prompt);
286
234
  });
287
235
  server.tool("upsert-prompt", UPSERT_PROMPT_DESCRIPTION, createPromptSchema.shape, async ({ name, description, template, model_provider, model_name, temperature, }) => {
236
+ const chatTemplate = buildChatTemplate(template);
237
+ const versionDescription = description || "";
288
238
  let promptVersionData;
289
239
  switch (model_provider) {
290
240
  case "OPENAI":
291
241
  promptVersionData = promptVersion({
292
242
  modelProvider: "OPENAI",
293
243
  modelName: model_name,
294
- description: description || "",
295
- template: [
296
- {
297
- role: "user",
298
- content: [{ type: "text", text: template }],
299
- },
300
- ],
301
- invocationParameters: {
302
- temperature: temperature,
303
- },
244
+ description: versionDescription,
245
+ template: chatTemplate,
246
+ invocationParameters: { temperature },
304
247
  });
305
248
  break;
306
249
  case "AZURE_OPENAI":
307
250
  promptVersionData = promptVersion({
308
251
  modelProvider: "AZURE_OPENAI",
309
252
  modelName: model_name,
310
- description: description || "",
311
- template: [
312
- {
313
- role: "user",
314
- content: [{ type: "text", text: template }],
315
- },
316
- ],
317
- invocationParameters: {
318
- temperature: temperature,
319
- },
253
+ description: versionDescription,
254
+ template: chatTemplate,
255
+ invocationParameters: { temperature },
320
256
  });
321
257
  break;
322
258
  case "ANTHROPIC":
323
259
  promptVersionData = promptVersion({
324
260
  modelProvider: "ANTHROPIC",
325
261
  modelName: model_name,
326
- description: description || "",
327
- template: [
328
- {
329
- role: "user",
330
- content: [{ type: "text", text: template }],
331
- },
332
- ],
262
+ description: versionDescription,
263
+ template: chatTemplate,
333
264
  invocationParameters: {
334
- temperature: temperature,
335
- max_tokens: 1000, // Required for Anthropic
265
+ temperature,
266
+ max_tokens: ANTHROPIC_DEFAULT_MAX_TOKENS,
336
267
  },
337
268
  });
338
269
  break;
@@ -340,111 +271,61 @@ export const initializePromptTools = ({ client, server, }) => {
340
271
  promptVersionData = promptVersion({
341
272
  modelProvider: "GOOGLE",
342
273
  modelName: model_name,
343
- description: description || "",
344
- template: [
345
- {
346
- role: "user",
347
- content: [{ type: "text", text: template }],
348
- },
349
- ],
350
- invocationParameters: {
351
- temperature: temperature,
352
- },
274
+ description: versionDescription,
275
+ template: chatTemplate,
276
+ invocationParameters: { temperature },
353
277
  });
354
278
  break;
355
279
  }
356
280
  const response = await createPrompt({
357
- client: client,
358
- name: name,
359
- description: description || "",
281
+ client,
282
+ name,
283
+ description: versionDescription,
360
284
  version: promptVersionData,
361
285
  });
362
- return {
363
- content: [
364
- {
365
- type: "text",
366
- text: `Successfully created prompt "${name}":\n${JSON.stringify(response, null, 2)}`,
367
- },
368
- ],
369
- };
286
+ return textResponse(`Successfully created prompt "${name}":\n${JSON.stringify(response, null, 2)}`);
370
287
  });
371
288
  server.tool("list-prompt-versions", LIST_PROMPT_VERSIONS_DESCRIPTION, listPromptVersionsSchema.shape, async ({ prompt_identifier, limit }) => {
372
289
  const response = await client.GET("/v1/prompts/{prompt_identifier}/versions", {
373
290
  params: {
374
- path: {
375
- prompt_identifier,
376
- },
377
- query: {
378
- limit,
379
- },
291
+ path: { prompt_identifier },
292
+ query: { limit },
380
293
  },
381
294
  });
382
- return {
383
- content: [
384
- {
385
- type: "text",
386
- text: JSON.stringify(response.data, null, 2),
387
- },
388
- ],
389
- };
295
+ const promptVersions = getResponseData({
296
+ response,
297
+ errorPrefix: `Failed to fetch prompt versions for "${prompt_identifier}"`,
298
+ });
299
+ return jsonResponse(promptVersions);
390
300
  });
391
301
  server.tool("get-prompt-version-by-tag", GET_PROMPT_VERSION_BY_TAG_DESCRIPTION, getPromptVersionByTagSchema.shape, async ({ prompt_identifier, tag_name }) => {
392
- const response = await client.GET("/v1/prompts/{prompt_identifier}/tags/{tag_name}", {
393
- params: {
394
- path: {
395
- prompt_identifier,
396
- tag_name,
397
- },
398
- },
302
+ const prompt = await fetchPromptVersionBySelection({
303
+ client,
304
+ promptIdentifier: prompt_identifier,
305
+ tag: tag_name,
399
306
  });
400
- return {
401
- content: [
402
- {
403
- type: "text",
404
- text: JSON.stringify(response.data, null, 2),
405
- },
406
- ],
407
- };
307
+ return jsonResponse(prompt);
408
308
  });
409
309
  server.tool("list-prompt-version-tags", LIST_PROMPT_VERSION_TAGS_DESCRIPTION, listPromptVersionTagsSchema.shape, async ({ prompt_version_id, limit }) => {
410
310
  const response = await client.GET("/v1/prompt_versions/{prompt_version_id}/tags", {
411
311
  params: {
412
- path: {
413
- prompt_version_id,
414
- },
415
- query: {
416
- limit,
417
- },
312
+ path: { prompt_version_id },
313
+ query: { limit },
418
314
  },
419
315
  });
420
- return {
421
- content: [
422
- {
423
- type: "text",
424
- text: JSON.stringify(response.data, null, 2),
425
- },
426
- ],
427
- };
316
+ const tags = getResponseData({
317
+ response,
318
+ errorPrefix: `Failed to fetch tags for prompt version "${prompt_version_id}"`,
319
+ });
320
+ return jsonResponse(tags);
428
321
  });
429
322
  server.tool("add-prompt-version-tag", ADD_PROMPT_VERSION_TAG_DESCRIPTION, addPromptVersionTagSchema.shape, async ({ prompt_version_id, name, description }) => {
430
323
  await client.POST("/v1/prompt_versions/{prompt_version_id}/tags", {
431
324
  params: {
432
- path: {
433
- prompt_version_id,
434
- },
435
- },
436
- body: {
437
- name,
438
- description,
325
+ path: { prompt_version_id },
439
326
  },
327
+ body: { name, description },
440
328
  });
441
- return {
442
- content: [
443
- {
444
- type: "text",
445
- text: `Successfully added tag "${name}" to prompt version ${prompt_version_id}`,
446
- },
447
- ],
448
- };
329
+ return textResponse(`Successfully added tag "${name}" to prompt version ${prompt_version_id}`);
449
330
  });
450
331
  };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Unwrap a Phoenix API response, returning its data or throwing
3
+ * a descriptive error suitable for MCP tool consumers.
4
+ *
5
+ * @param options.response - The raw response from the Phoenix REST client.
6
+ * @param options.errorPrefix - A human-readable prefix prepended to any error message
7
+ * (e.g. `"Failed to fetch datasets"`).
8
+ * @returns The unwrapped response data.
9
+ * @throws When the response contains an error or no data.
10
+ */
11
+ export function getResponseData({ response, errorPrefix, }) {
12
+ if (response.error || response.data === undefined) {
13
+ throw new Error(`${errorPrefix}: ${response.error instanceof Error ? response.error.message : String(response.error || "Unknown error")}`);
14
+ }
15
+ return response.data;
16
+ }