Skip to main content
Glama

@arizeai/phoenix-mcp

Official
by Arize-ai
PlaygroundTool.tsx5.87 kB
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { JSONSchema7 } from "json-schema"; import { Button, Card, CopyToClipboardButton, Flex, Icon, Icons, Text, } from "@phoenix/components"; import { JSONEditor } from "@phoenix/components/code"; import { LazyEditorWrapper } from "@phoenix/components/code/LazyEditorWrapper"; import { SpanKindIcon } from "@phoenix/components/trace"; import { usePlaygroundContext, usePlaygroundStore, } from "@phoenix/contexts/PlaygroundContext"; import { anthropicToolDefinitionJSONSchema, awsToolDefinitionJSONSchema, openAIToolDefinitionJSONSchema, } from "@phoenix/schemas"; import { findToolChoiceName } from "@phoenix/schemas/toolChoiceSchemas"; import { Tool } from "@phoenix/store"; import { isJSONString, safelyParseJSON } from "@phoenix/utils/jsonUtils"; import { getToolName } from "./playgroundUtils"; import { PlaygroundInstanceProps } from "./types"; /** * The minimum height for the editor before it is initialized. * This is to ensure that the editor is properly initialized when it is rendered outside of the viewport. */ const TOOL_EDITOR_PRE_INIT_HEIGHT = 400; /** * A tool editor that is used to edit the definition of a tool. * * This is a mostly un-controlled editor that re-mounts when the tool definition changes externally. * This is necessary because controlled react-codemirror editors incessantly remount and reset * cursor position when value is updated. */ export function PlaygroundTool({ playgroundInstanceId, toolId, }: PlaygroundInstanceProps & { toolId: Tool["id"]; }) { const store = usePlaygroundStore(); const updateInstance = usePlaygroundContext((state) => state.updateInstance); const instance = usePlaygroundContext((state) => state.instances.find((instance) => instance.id === playgroundInstanceId) ); if (instance == null) { throw new Error(`Playground instance ${playgroundInstanceId} not found`); } const instanceProvider = instance.model.provider; const instanceTools = instance.tools; const tool = instanceTools.find((t) => t.id === toolId); if (tool == null) { throw new Error(`Tool ${toolId} not found`); } const [initialEditorValue, setInitialEditorValue] = useState(() => JSON.stringify(tool.definition, null, 2) ); const editorValueRef = useRef(initialEditorValue); // when the instance provider changes, we need to update the editor value // to reflect the new tool definition schema useEffect(() => { const state = store.getState(); const instance = state.instances.find((i) => i.id === playgroundInstanceId); if (instance == null) { return; } const tool = instance.tools.find((t) => t.id === toolId); if (tool == null) { return; } const newDefinition = JSON.stringify(tool.definition, null, 2); if (isJSONString({ str: newDefinition, excludeNull: true })) { setInitialEditorValue(newDefinition); } }, [instanceProvider, store, playgroundInstanceId, toolId]); const onChange = useCallback( (value: string) => { editorValueRef.current = value; const { json: definition } = safelyParseJSON(value); updateInstance({ instanceId: playgroundInstanceId, patch: { tools: instanceTools.map((t) => t.id === tool.id ? { ...t, definition, } : t ), }, dirty: true, }); }, [instanceTools, playgroundInstanceId, tool.id, updateInstance] ); const toolName = useMemo(() => { return getToolName(tool); }, [tool]); const toolDefinitionJSONSchema = useMemo((): JSONSchema7 | null => { switch (instance.model.provider) { case "OPENAI": case "AZURE_OPENAI": case "DEEPSEEK": case "XAI": case "OLLAMA": return openAIToolDefinitionJSONSchema as JSONSchema7; case "ANTHROPIC": return anthropicToolDefinitionJSONSchema as JSONSchema7; case "AWS": return awsToolDefinitionJSONSchema as JSONSchema7; case "GOOGLE": return null; } }, [instance.model.provider]); return ( <Card collapsible backgroundColor={"yellow-100"} borderColor={"yellow-700"} title={ <Flex direction="row" gap="size-100"> <SpanKindIcon spanKind="tool" /> <Text>{toolName ?? "Tool"}</Text> </Flex> } extra={ <Flex direction="row" gap="size-100"> <CopyToClipboardButton text={editorValueRef} /> <Button aria-label="Delete tool" leadingVisual={<Icon svg={<Icons.TrashOutline />} />} size="S" onPress={() => { const newTools = instanceTools.filter((t) => t.id !== tool.id); const deletingToolChoice = typeof instance.toolChoice === "object" && toolName != null && findToolChoiceName(instance.toolChoice) === toolName; let toolChoice = instance.toolChoice; if (newTools.length === 0) { toolChoice = undefined; } else if (deletingToolChoice) { toolChoice = "auto"; } updateInstance({ instanceId: playgroundInstanceId, patch: { tools: newTools, toolChoice, }, dirty: true, }); }} /> </Flex> } > <LazyEditorWrapper preInitializationMinHeight={TOOL_EDITOR_PRE_INIT_HEIGHT} > <JSONEditor value={initialEditorValue} onChange={onChange} jsonSchema={toolDefinitionJSONSchema} /> </LazyEditorWrapper> </Card> ); }

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