Skip to main content
Glama
smatiolids
by smatiolids
ToolEditor.tsx18 kB
import { useState, useEffect } from 'react'; import { Tool, ToolParameter } from '../utils/astraClient'; interface ToolEditorProps { tool: Tool | null; } export default function ToolEditor({ tool }: ToolEditorProps) { const [formData, setFormData] = useState<Tool | null>(null); useEffect(() => { if (tool) { setFormData({ ...tool }); } else { setFormData(null); } }, [tool]); if (!formData) { return ( <div className="flex-1 flex items-center justify-center bg-white dark:bg-gray-800"> <div className="text-center text-gray-500 dark:text-gray-400"> <p className="text-lg">Select a tool from the list to view and edit</p> </div> </div> ); } const updateField = (field: keyof Tool, value: any) => { setFormData({ ...formData, [field]: value }); }; const updateParameter = (index: number, field: keyof ToolParameter, value: any) => { if (!formData.parameters) return; const newParameters = [...formData.parameters]; newParameters[index] = { ...newParameters[index], [field]: value }; setFormData({ ...formData, parameters: newParameters }); }; const addParameter = () => { const newParam: ToolParameter = { param: '', description: '', type: 'string', required: false, }; setFormData({ ...formData, parameters: [...(formData.parameters || []), newParam], }); }; const removeParameter = (index: number) => { if (!formData.parameters) return; const newParameters = formData.parameters.filter((_, i) => i !== index); setFormData({ ...formData, parameters: newParameters }); }; const updateTags = (tagsString: string) => { const tags = tagsString.split(',').map(t => t.trim()).filter(t => t); updateField('tags', tags); }; return ( <div className="flex-1 overflow-y-auto bg-white dark:bg-gray-800"> <div className="max-w-4xl mx-auto p-6"> <h1 className="text-2xl font-bold text-gray-800 dark:text-gray-200 mb-6">Edit Tool</h1> <div className="space-y-6"> {/* Basic Information */} <div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4"> <h2 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Basic Information</h2> <div className="space-y-4"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Name * </label> <input type="text" value={formData.name || ''} onChange={(e) => updateField('name', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Description </label> <textarea value={formData.description || ''} onChange={(e) => updateField('description', e.target.value)} rows={3} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Tags (comma-separated) </label> <input type="text" value={formData.tags?.join(', ') || ''} onChange={(e) => updateTags(e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" placeholder="products, ecommerce, clothing" /> </div> <div className="grid grid-cols-2 gap-4"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Type </label> <input type="text" value={formData.type || ''} onChange={(e) => updateField('type', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Limit </label> <input type="number" value={formData.limit || ''} onChange={(e) => updateField('limit', e.target.value ? parseInt(e.target.value) : undefined)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> </div> </div> </div> {/* Method & Collection */} <div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4"> <h2 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Database Configuration</h2> <div className="grid grid-cols-2 gap-4"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Method </label> <input type="text" value={formData.method || ''} onChange={(e) => updateField('method', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" placeholder="find, find_documents" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Collection Name </label> <input type="text" value={formData.collection_name || ''} onChange={(e) => updateField('collection_name', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Table Name </label> <input type="text" value={formData.table_name || ''} onChange={(e) => updateField('table_name', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> DB Name </label> <input type="text" value={formData.db_name || ''} onChange={(e) => updateField('db_name', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> </div> </div> {/* Projection */} {formData.projection && ( <div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4"> <h2 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Projection</h2> <textarea value={JSON.stringify(formData.projection, null, 2)} onChange={(e) => { try { const parsed = JSON.parse(e.target.value); updateField('projection', parsed); } catch { // Invalid JSON, ignore } }} rows={6} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 font-mono text-sm" /> </div> )} {/* Parameters */} <div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4"> <div className="flex justify-between items-center mb-4"> <h2 className="text-lg font-semibold text-gray-800 dark:text-gray-200">Parameters</h2> <button onClick={addParameter} className="px-3 py-1 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm" > Add Parameter </button> </div> {formData.parameters && formData.parameters.length > 0 ? ( <div className="space-y-4"> {formData.parameters.map((param, index) => ( <div key={index} className="border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-white dark:bg-gray-800"> <div className="flex justify-between items-start mb-3"> <h3 className="font-medium text-gray-800 dark:text-gray-200">Parameter {index + 1}</h3> <button onClick={() => removeParameter(index)} className="text-red-600 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 text-sm" > Remove </button> </div> <div className="grid grid-cols-2 gap-4"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Param Name * </label> <input type="text" value={param.param || ''} onChange={(e) => updateParameter(index, 'param', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Type </label> <select value={param.type || 'string'} onChange={(e) => updateParameter(index, 'type', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" > <option value="string">string</option> <option value="number">number</option> <option value="boolean">boolean</option> <option value="text">text</option> <option value="timestamp">timestamp</option> <option value="float">float</option> <option value="vector">vector</option> </select> </div> </div> <div className="mt-3"> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Description </label> <textarea value={param.description || ''} onChange={(e) => updateParameter(index, 'description', e.target.value)} rows={2} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> <div className="grid grid-cols-2 gap-4 mt-3"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Attribute </label> <input type="text" value={param.attribute || ''} onChange={(e) => updateParameter(index, 'attribute', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" placeholder="$vectorize, $vector, or column name" /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Operator </label> <select value={param.operator || '$eq'} onChange={(e) => updateParameter(index, 'operator', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" > <option value="$eq">$eq</option> <option value="$gt">$gt</option> <option value="$gte">$gte</option> <option value="$lt">$lt</option> <option value="$lte">$lte</option> <option value="$in">$in</option> <option value="$ne">$ne</option> </select> </div> </div> <div className="grid grid-cols-2 gap-4 mt-3"> <div> <label className="flex items-center space-x-2"> <input type="checkbox" checked={!!param.required} onChange={(e) => updateParameter(index, 'required', e.target.checked)} className="rounded border-gray-300 dark:border-gray-600" /> <span className="text-sm font-medium text-gray-700 dark:text-gray-300">Required</span> </label> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Embedding Model </label> <input type="text" value={param.embedding_model || ''} onChange={(e) => updateParameter(index, 'embedding_model', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" placeholder="text-embedding-3-small" /> </div> </div> {param.enum && ( <div className="mt-3"> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Enum Values (comma-separated) </label> <input type="text" value={Array.isArray(param.enum) ? param.enum.join(', ') : param.enum} onChange={(e) => { const enumValues = e.target.value.split(',').map(v => v.trim()).filter(v => v); updateParameter(index, 'enum', enumValues.length > 0 ? enumValues : undefined); }} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" /> </div> )} <div className="mt-3"> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> Info </label> <input type="text" value={param.info || ''} onChange={(e) => updateParameter(index, 'info', e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200" placeholder="Partition key, sorting key, indexed column, etc." /> </div> </div> ))} </div> ) : ( <div className="text-center py-8 text-gray-500 dark:text-gray-400"> <p>No parameters defined. Click "Add Parameter" to add one.</p> </div> )} </div> </div> </div> </div> ); }

Latest Blog Posts

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/smatiolids/astra-mcp-server'

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