Skip to main content
Glama
monostate

100ms Raydium Sniper MCP

by monostate
ToolsTab.tsx9.84 kB
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { TabsContent } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; import DynamicJsonForm, { JsonSchemaType, JsonValue } from "./DynamicJsonForm"; import { ListToolsResult, Tool, CallToolResultSchema, } from "@modelcontextprotocol/sdk/types.js"; import { AlertCircle, Send } from "lucide-react"; import { useEffect, useState } from "react"; import ListPane from "./ListPane"; import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js"; const ToolsTab = ({ tools, listTools, clearTools, callTool, selectedTool, setSelectedTool, toolResult, nextCursor, error, }: { tools: Tool[]; listTools: () => void; clearTools: () => void; callTool: (name: string, params: Record<string, unknown>) => void; selectedTool: Tool | null; setSelectedTool: (tool: Tool | null) => void; toolResult: CompatibilityCallToolResult | null; nextCursor: ListToolsResult["nextCursor"]; error: string | null; }) => { const [params, setParams] = useState<Record<string, unknown>>({}); useEffect(() => { setParams({}); }, [selectedTool]); const renderToolResult = () => { if (!toolResult) return null; if ("content" in toolResult) { const parsedResult = CallToolResultSchema.safeParse(toolResult); if (!parsedResult.success) { return ( <> <h4 className="font-semibold mb-2">Invalid Tool Result:</h4> <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64"> {JSON.stringify(toolResult, null, 2)} </pre> <h4 className="font-semibold mb-2">Errors:</h4> {parsedResult.error.errors.map((error, idx) => ( <pre key={idx} className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64" > {JSON.stringify(error, null, 2)} </pre> ))} </> ); } const structuredResult = parsedResult.data; const isError = structuredResult.isError ?? false; return ( <> <h4 className="font-semibold mb-2"> Tool Result: {isError ? "Error" : "Success"} </h4> {structuredResult.content.map((item, index) => ( <div key={index} className="mb-2"> {item.type === "text" && ( <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64"> {item.text} </pre> )} {item.type === "image" && ( <img src={`data:${item.mimeType};base64,${item.data}`} alt="Tool result image" className="max-w-full h-auto" /> )} {item.type === "resource" && (item.resource?.mimeType?.startsWith("audio/") ? ( <audio controls src={`data:${item.resource.mimeType};base64,${item.resource.blob}`} className="w-full" > <p>Your browser does not support audio playback</p> </audio> ) : ( <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 whitespace-pre-wrap break-words p-4 rounded text-sm overflow-auto max-h-64"> {JSON.stringify(item.resource, null, 2)} </pre> ))} </div> ))} </> ); } else if ("toolResult" in toolResult) { return ( <> <h4 className="font-semibold mb-2">Tool Result (Legacy):</h4> <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64"> {JSON.stringify(toolResult.toolResult, null, 2)} </pre> </> ); } }; return ( <TabsContent value="tools" className="grid grid-cols-2 gap-4"> <ListPane items={tools} listItems={listTools} clearItems={() => { clearTools(); setSelectedTool(null); }} setSelectedItem={setSelectedTool} renderItem={(tool) => ( <> <span className="flex-1">{tool.name}</span> <span className="text-sm text-gray-500 text-right"> {tool.description} </span> </> )} title="Tools" buttonText={nextCursor ? "List More Tools" : "List Tools"} isButtonDisabled={!nextCursor && tools.length > 0} /> <div className="bg-card rounded-lg shadow"> <div className="p-4 border-b border-gray-200"> <h3 className="font-semibold"> {selectedTool ? selectedTool.name : "Select a tool"} </h3> </div> <div className="p-4"> {error ? ( <Alert variant="destructive"> <AlertCircle className="h-4 w-4" /> <AlertTitle>Error</AlertTitle> <AlertDescription>{error}</AlertDescription> </Alert> ) : selectedTool ? ( <div className="space-y-4"> <p className="text-sm text-gray-600"> {selectedTool.description} </p> {Object.entries(selectedTool.inputSchema.properties ?? []).map( ([key, value]) => { const prop = value as JsonSchemaType; return ( <div key={key}> <Label htmlFor={key} className="block text-sm font-medium text-gray-700" > {key} </Label> {prop.type === "boolean" ? ( <div className="flex items-center space-x-2 mt-2"> <Checkbox id={key} name={key} checked={!!params[key]} onCheckedChange={(checked: boolean) => setParams({ ...params, [key]: checked, }) } /> <label htmlFor={key} className="text-sm font-medium text-gray-700 dark:text-gray-300" > {prop.description || "Toggle this option"} </label> </div> ) : prop.type === "string" ? ( <Textarea id={key} name={key} placeholder={prop.description} value={(params[key] as string) ?? ""} onChange={(e) => setParams({ ...params, [key]: e.target.value, }) } className="mt-1" /> ) : prop.type === "object" || prop.type === "array" ? ( <div className="mt-1"> <DynamicJsonForm schema={{ type: prop.type, properties: prop.properties, description: prop.description, items: prop.items, }} value={(params[key] as JsonValue) ?? {}} onChange={(newValue: JsonValue) => { setParams({ ...params, [key]: newValue, }); }} /> </div> ) : ( <Input type={prop.type === "number" ? "number" : "text"} id={key} name={key} placeholder={prop.description} onChange={(e) => setParams({ ...params, [key]: prop.type === "number" ? Number(e.target.value) : e.target.value, }) } className="mt-1" /> )} </div> ); }, )} <Button onClick={() => callTool(selectedTool.name, params)}> <Send className="w-4 h-4 mr-2" /> Run Tool </Button> {toolResult && renderToolResult()} </div> ) : ( <Alert> <AlertDescription> Select a tool from the list to view its details and run it </AlertDescription> </Alert> )} </div> </div> </TabsContent> ); }; export default ToolsTab;

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/monostate/100ms-SPL-Token-Sniper-MCP'

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