Skip to main content
Glama

@arizeai/phoenix-mcp

Official
by Arize-ai
toolSchemas.ts12.3 kB
import { z } from "zod"; import zodToJsonSchema from "zod-to-json-schema"; import { assertUnreachable, isObject } from "@phoenix/typeUtils"; import { JSONLiteral, jsonLiteralSchema } from "./jsonLiteralSchema"; const jsonSchemaPropertiesSchema = z .object({ type: z .enum([ "string", "number", "boolean", "object", "array", "null", "integer", ]) .describe("The type of the parameter"), description: z .string() .optional() .describe("A description of the parameter"), enum: z.array(z.string()).optional().describe("The allowed values"), }) .passthrough() .describe("A map of parameter names to their definitions"); export const jsonSchemaZodSchema = z .object({ type: z.literal("object"), properties: z .record( z.union([ jsonSchemaPropertiesSchema, z .object({ anyOf: z.array(jsonSchemaPropertiesSchema) }) .describe( "A list of possible parameter names to their definitions" ), ]) ) .optional(), required: z .array(z.string()) .optional() .describe("The required parameters"), additionalProperties: z .boolean() .optional() .describe( "Whether or not additional properties are allowed in the schema" ), }) .passthrough(); /** * The schema for an OpenAI tool definition * @see https://platform.openai.com/docs/guides/structured-outputs/supported-schemas * * Note: The nested passThrough's are used to allow for extra keys in JSON schema, however, they do not actually * allow for extra keys when the zod schema is used for parsing. This is to allow more flexibility for users * to define their own tools according */ export const openAIToolDefinitionSchema = z .object({ type: z.literal("function").describe("The type of the tool"), function: z .object({ name: z.string().describe("The name of the function"), description: z .string() .optional() .describe("A description of the function"), parameters: jsonSchemaZodSchema .extend({ strict: z .boolean() .optional() .describe( "Whether or not the arguments should exactly match the function definition, only supported for OpenAI models" ), }) .describe("The parameters that the function accepts"), }) .passthrough() .describe("The function definition"), }) .passthrough(); /** * The type of an OpenAI tool definition * @see https://platform.openai.com/docs/guides/structured-outputs/supported-schemas */ export type OpenAIToolDefinition = z.infer<typeof openAIToolDefinitionSchema>; /** * The JSON schema for an OpenAI tool definition */ export const openAIToolDefinitionJSONSchema = zodToJsonSchema( openAIToolDefinitionSchema, { removeAdditionalStrategy: "passthrough", } ); /** * The zod schema for an anthropic tool definition */ export const anthropicToolDefinitionSchema = z.object({ name: z.string(), description: z.string(), input_schema: jsonSchemaZodSchema, }); /** * The type of an anthropic tool definition */ export type AnthropicToolDefinition = z.infer< typeof anthropicToolDefinitionSchema >; /** * The JSON schema for an anthropic tool definition */ export const anthropicToolDefinitionJSONSchema = zodToJsonSchema( anthropicToolDefinitionSchema, { removeAdditionalStrategy: "passthrough", } ); export const awsToolDefinitionSchema = z.object({ toolSpec: z.object({ name: z.string(), description: z.string().min(1), inputSchema: z.object({ json: jsonSchemaZodSchema, }), }), }); export type AwsToolDefinition = z.infer<typeof awsToolDefinitionSchema>; export const awsToolDefinitionJSONSchema = zodToJsonSchema( awsToolDefinitionSchema, { removeAdditionalStrategy: "passthrough", } ); /** * -------------------------------- * Conversion Schemas * -------------------------------- */ export const awsToolToOpenAI = awsToolDefinitionSchema.transform( (aws): OpenAIToolDefinition => ({ type: "function", function: { name: aws.toolSpec.name, description: aws.toolSpec.description, parameters: aws.toolSpec.inputSchema.json, }, }) ); export const openAIToolToAws = openAIToolDefinitionSchema.transform( (openai): AwsToolDefinition => ({ toolSpec: { name: openai.function.name, description: openai.function.description ?? openai.function.name, inputSchema: { json: openai.function.parameters, }, }, }) ); /** * Parse incoming object as an Anthropic tool call and immediately convert to OpenAI format */ export const anthropicToolToOpenAI = anthropicToolDefinitionSchema.transform( (anthropic): OpenAIToolDefinition => ({ type: "function", function: { name: anthropic.name, description: anthropic.description, parameters: anthropic.input_schema, }, }) ); /** * Parse incoming object as an OpenAI tool call and immediately convert to Anthropic format */ export const openAIToolToAnthropic = openAIToolDefinitionSchema.transform( (openai): AnthropicToolDefinition => ({ name: openai.function.name, description: openai.function.description ?? openai.function.name, input_schema: openai.function.parameters, }) ); /** * -------------------------------- * Conversion Helpers * -------------------------------- */ /** * Union of all tool call formats * * This is useful for functions that need to accept any tool definition format */ export const llmProviderToolDefinitionSchema = z.union([ openAIToolDefinitionSchema, anthropicToolDefinitionSchema, awsToolDefinitionSchema, jsonLiteralSchema, ]); export type LlmProviderToolDefinition = z.infer< typeof llmProviderToolDefinitionSchema >; type ToolDefinitionWithProvider = | { provider: Extract<ModelProvider, "OPENAI" | "AZURE_OPENAI">; validatedToolDefinition: OpenAIToolDefinition; } | { provider: Extract<ModelProvider, "ANTHROPIC">; validatedToolDefinition: AnthropicToolDefinition; } | { provider: Extract<ModelProvider, "AWS">; validatedToolDefinition: AwsToolDefinition; } | { provider: "UNKNOWN"; validatedToolDefinition: null; }; /** * Detect the provider of a tool call object */ export const detectToolDefinitionProvider = ( toolDefinition: unknown ): ToolDefinitionWithProvider => { const { success: openaiSuccess, data: openaiData } = openAIToolDefinitionSchema.safeParse(toolDefinition); if (openaiSuccess) { return { // we cannot disambiguate between azure openai and openai here provider: "OPENAI", validatedToolDefinition: openaiData, }; } const { success: anthropicSuccess, data: anthropicData } = anthropicToolDefinitionSchema.safeParse(toolDefinition); if (anthropicSuccess) { return { provider: "ANTHROPIC", validatedToolDefinition: anthropicData, }; } const { success: awsSuccess, data: awsData } = awsToolDefinitionSchema.safeParse(toolDefinition); if (awsSuccess) { return { provider: "AWS", validatedToolDefinition: awsData, }; } return { provider: "UNKNOWN", validatedToolDefinition: null }; }; type ProviderToToolDefinitionMap = { OPENAI: OpenAIToolDefinition; AZURE_OPENAI: OpenAIToolDefinition; ANTHROPIC: AnthropicToolDefinition; // Use generic JSON type for unknown tool formats / new providers GOOGLE: JSONLiteral; DEEPSEEK: OpenAIToolDefinition; XAI: OpenAIToolDefinition; OLLAMA: OpenAIToolDefinition; AWS: AwsToolDefinition; }; /** * Convert from any tool call format to OpenAI format if possible */ export const toOpenAIToolDefinition = ( toolDefinition: LlmProviderToolDefinition ): OpenAIToolDefinition | null => { const { provider, validatedToolDefinition } = detectToolDefinitionProvider(toolDefinition); switch (provider) { case "AZURE_OPENAI": case "OPENAI": return validatedToolDefinition; case "ANTHROPIC": return anthropicToolToOpenAI.parse(validatedToolDefinition); case "AWS": return awsToolToOpenAI.parse(validatedToolDefinition); case "UNKNOWN": return null; default: assertUnreachable(provider); } }; /** * Convert from OpenAI tool call format to any other format */ export const fromOpenAIToolDefinition = <T extends ModelProvider>({ toolDefinition, targetProvider, }: { toolDefinition: OpenAIToolDefinition; targetProvider: T; }): ProviderToToolDefinitionMap[T] => { switch (targetProvider) { case "AZURE_OPENAI": case "OPENAI": case "DEEPSEEK": case "XAI": case "OLLAMA": return toolDefinition as ProviderToToolDefinitionMap[T]; case "ANTHROPIC": return openAIToolToAnthropic.parse( toolDefinition ) as ProviderToToolDefinitionMap[T]; case "AWS": return openAIToolToAws.parse( toolDefinition ) as ProviderToToolDefinitionMap[T]; // TODO(apowell): #5348 Add Google tool calls schema - https://github.com/Arize-ai/phoenix/issues/5348 case "GOOGLE": return toolDefinition as ProviderToToolDefinitionMap[T]; default: assertUnreachable(targetProvider); } }; /** * Creates an OpenAI tool definition * @param toolNumber the number of the tool in that instance for example instance.tools.length + 1 to be used to fill in the name * @returns an OpenAI tool definition */ export function createOpenAIToolDefinition( toolNumber: number ): OpenAIToolDefinition { return { type: "function", function: { name: `new_function_${toolNumber}`, description: "a description", parameters: { type: "object", properties: { new_arg: { type: "string", }, }, required: [], }, }, }; } /** * Creates an Anthropic tool definition * @param toolNumber the number of the tool in that instance for example instance.tools.length + 1 to be used to fill in the name * @returns an Anthropic tool definition */ export function createAnthropicToolDefinition( toolNumber: number ): AnthropicToolDefinition { return { name: `new_function_${toolNumber}`, description: "a description", input_schema: { type: "object", properties: { new_arg: { type: "string", }, }, required: [], }, }; } export function createAwsToolDefinition(toolNumber: number): AwsToolDefinition { return { toolSpec: { name: `new_function_${toolNumber}`, description: "a description", inputSchema: { json: { type: "object", properties: { new_arg: { type: "string", }, }, required: [], }, }, }, }; } export const findToolDefinitionName = (toolDefinition: unknown) => { const parsed = llmProviderToolDefinitionSchema.safeParse(toolDefinition); if (!parsed.success || parsed.data === null || !isObject(parsed.data)) { return null; } if ( "function" in parsed.data && isObject(parsed.data.function) && "name" in parsed.data.function && typeof parsed.data.function.name === "string" ) { return parsed.data.function.name; } if ("name" in parsed.data && typeof parsed.data.name === "string") { return parsed.data.name; } return null; }; export const findToolDefinitionDescription = (toolDefinition: unknown) => { const parsed = llmProviderToolDefinitionSchema.safeParse(toolDefinition); if (!parsed.success || parsed.data === null || !isObject(parsed.data)) { return null; } if ( "function" in parsed.data && isObject(parsed.data.function) && "description" in parsed.data.function && typeof parsed.data.function.description === "string" ) { return parsed.data.function.description; } if ( "description" in parsed.data && typeof parsed.data.description === "string" ) { return parsed.data.description; } return null; };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Arize-ai/phoenix'

If you have feedback or need assistance with the MCP directory API, please join our Discord server