get tools working
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ const App = () => {
|
|||||||
<Bell className="w-4 h-4 mr-2" />
|
<Bell className="w-4 h-4 mr-2" />
|
||||||
Notifications
|
Notifications
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="tools" disabled>
|
<TabsTrigger value="tools">
|
||||||
<Hammer className="w-4 h-4 mr-2" />
|
<Hammer className="w-4 h-4 mr-2" />
|
||||||
Tools
|
Tools
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
@@ -243,7 +243,10 @@ const App = () => {
|
|||||||
listTools={listTools}
|
listTools={listTools}
|
||||||
callTool={callTool}
|
callTool={callTool}
|
||||||
selectedTool={selectedTool}
|
selectedTool={selectedTool}
|
||||||
setSelectedTool={setSelectedTool}
|
setSelectedTool={(tool) => {
|
||||||
|
setSelectedTool(tool);
|
||||||
|
setToolResult("");
|
||||||
|
}}
|
||||||
toolResult={toolResult}
|
toolResult={toolResult}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { TabsContent } from "@/components/ui/tabs";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
export type Prompt = {
|
export type Prompt = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -98,7 +99,9 @@ const PromptsTab = ({
|
|||||||
)}
|
)}
|
||||||
{selectedPrompt.arguments?.map((arg) => (
|
{selectedPrompt.arguments?.map((arg) => (
|
||||||
<div key={arg.name}>
|
<div key={arg.name}>
|
||||||
|
<Label htmlFor={arg.name}>{arg.name}</Label>
|
||||||
<Input
|
<Input
|
||||||
|
id={arg.name}
|
||||||
placeholder={`Enter ${arg.name}`}
|
placeholder={`Enter ${arg.name}`}
|
||||||
value={promptArgs[arg.name] || ""}
|
value={promptArgs[arg.name] || ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Send, AlertCircle } from "lucide-react";
|
import { Send, AlertCircle } from "lucide-react";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
export type Tool = {
|
export type Tool = {
|
||||||
name: string;
|
name: string;
|
||||||
|
description: string;
|
||||||
|
inputSchema: {
|
||||||
|
type: string;
|
||||||
|
properties: Record<string, { type: string; description: string }>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const ToolsTab = ({
|
const ToolsTab = ({
|
||||||
@@ -26,7 +32,7 @@ const ToolsTab = ({
|
|||||||
toolResult: string;
|
toolResult: string;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
const [params, setParams] = useState("");
|
const [params, setParams] = useState<Record<string, unknown>>({});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabsContent value="tools" className="grid grid-cols-2 gap-4">
|
<TabsContent value="tools" className="grid grid-cols-2 gap-4">
|
||||||
@@ -46,6 +52,9 @@ const ToolsTab = ({
|
|||||||
onClick={() => setSelectedTool(tool)}
|
onClick={() => setSelectedTool(tool)}
|
||||||
>
|
>
|
||||||
<span className="flex-1">{tool.name}</span>
|
<span className="flex-1">{tool.name}</span>
|
||||||
|
<span className="text-sm text-gray-500">
|
||||||
|
{tool.description}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -67,22 +76,47 @@ const ToolsTab = ({
|
|||||||
</Alert>
|
</Alert>
|
||||||
) : selectedTool ? (
|
) : selectedTool ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Textarea
|
<p className="text-sm text-gray-600">
|
||||||
placeholder="Tool parameters (JSON)"
|
{selectedTool.description}
|
||||||
className="h-32 font-mono"
|
</p>
|
||||||
value={params}
|
{Object.entries(selectedTool.inputSchema.properties).map(
|
||||||
onChange={(e) => setParams(e.target.value)}
|
([key, value]) => (
|
||||||
/>
|
<div key={key}>
|
||||||
<Button
|
<Label
|
||||||
onClick={() => callTool(selectedTool.name, JSON.parse(params))}
|
htmlFor={key}
|
||||||
|
className="block text-sm font-medium text-gray-700"
|
||||||
>
|
>
|
||||||
|
{key}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
type={value.type === "number" ? "number" : "text"}
|
||||||
|
id={key}
|
||||||
|
name={key}
|
||||||
|
placeholder={value.description}
|
||||||
|
onChange={(e) =>
|
||||||
|
setParams({
|
||||||
|
...params,
|
||||||
|
[key]:
|
||||||
|
value.type === "number"
|
||||||
|
? Number(e.target.value)
|
||||||
|
: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
<Button onClick={() => callTool(selectedTool.name, params)}>
|
||||||
<Send className="w-4 h-4 mr-2" />
|
<Send className="w-4 h-4 mr-2" />
|
||||||
Run Tool
|
Run Tool
|
||||||
</Button>
|
</Button>
|
||||||
{toolResult && (
|
{toolResult && (
|
||||||
|
<>
|
||||||
|
<h4 className="font-semibold mb-2">Tool Result:</h4>
|
||||||
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
|
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
|
||||||
{JSON.stringify(toolResult, null, 2)}
|
{toolResult}
|
||||||
</pre>
|
</pre>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
24
client/src/components/ui/label.tsx
Normal file
24
client/src/components/ui/label.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const labelVariants = cva(
|
||||||
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Label = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Label }
|
||||||
@@ -499,6 +499,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||||
|
|
||||||
|
"@radix-ui/react-label@^2.1.0":
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.1.0.tgz#3aa2418d70bb242be37c51ff5e51a2adcbc372e3"
|
||||||
|
integrity sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-primitive" "2.0.0"
|
||||||
|
|
||||||
"@radix-ui/react-presence@1.1.1":
|
"@radix-ui/react-presence@1.1.1":
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1"
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export class McpClient {
|
|||||||
return await this.client.request(
|
return await this.client.request(
|
||||||
{
|
{
|
||||||
method: "tools/call",
|
method: "tools/call",
|
||||||
params: { name, ...params },
|
params: { name, arguments: params },
|
||||||
},
|
},
|
||||||
CallToolResultSchema,
|
CallToolResultSchema,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,11 +45,10 @@ wss.on("connection", (ws: WebSocket) => {
|
|||||||
command.name &&
|
command.name &&
|
||||||
command.params
|
command.params
|
||||||
) {
|
) {
|
||||||
const result = await mcpClient.callTool(
|
const result = await mcpClient.callTool(command.name, command.params);
|
||||||
command.name + "asdf",
|
ws.send(
|
||||||
command.params,
|
JSON.stringify({ type: "toolResult", data: result.toolResult }),
|
||||||
);
|
);
|
||||||
ws.send(JSON.stringify({ type: "toolResult", data: result }));
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
|
|||||||
Reference in New Issue
Block a user