import React, { useState } from "react"; import { Wrench, Plus, Trash2, Play, Sparkles, Code2, Terminal, ArrowRight, RefreshCw, } from "lucide-react"; import { MCPToolDeclaration, TrainingDataSample } from "../types"; interface MCPHarnessStudioProps { mcpTools: MCPToolDeclaration[]; setMcpTools: React.Dispatch>; dataset: TrainingDataSample[]; setDataset: React.Dispatch>; onProceed: () => void; } export const MCPHarnessStudio: React.FC = ({ mcpTools, setMcpTools, dataset, setDataset, onProceed, }) => { const [selectedToolId, setSelectedToolId] = useState(mcpTools[0]?.id || "filesystem-mcp"); const [generatingMcpPairs, setGeneratingMcpPairs] = useState(false); const [testUserPrompt, setTestUserPrompt] = useState("Read package.json and summarize our frontend dependencies."); const [harnessOutput, setHarnessOutput] = useState(null); // New tool creator state const [newToolName, setNewToolName] = useState(""); const [newServerName, setNewServerName] = useState(""); const [newToolDesc, setNewToolDesc] = useState(""); const [newToolSchemaJson, setNewToolSchemaJson] = useState(`{ "type": "object", "properties": { "query": { "type": "string", "description": "Search query or input parameter" } }, "required": ["query"] }`); const activeTool = mcpTools.find((t) => t.id === selectedToolId) || mcpTools[0]; const handleAddTool = () => { if (!newToolName.trim()) return; try { const parsedSchema = JSON.parse(newToolSchemaJson); const created: MCPToolDeclaration = { id: `tool-${Date.now()}`, name: newToolName.trim(), serverName: newServerName.trim() || "Custom MCP", description: newToolDesc.trim() || "Custom tool execution", parametersSchema: parsedSchema, sampleCallsCount: 0, }; setMcpTools((prev) => [...prev, created]); setSelectedToolId(created.id); setNewToolName(""); setNewToolDesc(""); } catch (e: any) { console.error(e); } }; const handleDeleteTool = (id: string) => { setMcpTools((prev) => prev.filter((t) => t.id !== id)); if (selectedToolId === id && mcpTools.length > 1) { setSelectedToolId(mcpTools.find((t) => t.id !== id)!.id); } }; const handleGenerateMCPDataWithAI = async () => { setGeneratingMcpPairs(true); try { const res = await fetch("/api/dataset/generate-mcp", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ mcpServers: mcpTools, count: 6, complexity: "advanced", }), }); const data = await res.json(); if (data.success && Array.isArray(data.data)) { const newSamples: TrainingDataSample[] = data.data.map((item: any) => ({ id: `mcp-${Date.now()}-${Math.random().toString(36).substring(2, 6)}`, instruction: item.userQuery, output: `\n${JSON.stringify(item.toolCalls?.[0] || {}, null, 2)}\n\n\n${item.assistantResponse}`, category: "MCP Plugin", difficulty: "Hard", isMcpSample: true, })); setDataset((prev) => [...newSamples, ...prev]); } } catch (e: any) { console.error(e); } finally { setGeneratingMcpPairs(false); } }; const handleRunHarnessSimulation = () => { setHarnessOutput("Simulating MCP execution harness..."); setTimeout(() => { setHarnessOutput(`[MCP HARNESS] Matched Tool: ${activeTool.name} (${activeTool.serverName}) [PAYLOAD EMITTED] { "path": "package.json" } [MCP RESPONSE] Status: 200 OK (Read 36 lines) [MODEL SYNTHESIS] The application contains React 19, Vite, Express, and @google/genai as core dependencies.`); }, 600); }; return (
{/* Header Banner */}
MCP (MODEL CONTEXT PROTOCOL) HARNESS

Train Local Models for Flawless MCP Plugin Execution

Configure MCP server tool definitions (Filesystem, Postgres, Web Search, Terminal, GitHub) and auto-generate multi-turn function call datasets so your fine-tuned Ollama model executes tools in Cline, Cursor, Windsurf, and Claude Desktop.

{/* Main Grid: Tool Registry + Schema & Harness Playground */}
{/* Left: Registered MCP Tools */}

Active MCP Tools ({mcpTools.length})

JSON SCHEMA
{mcpTools.map((tool) => { const isSelected = selectedToolId === tool.id; return (
setSelectedToolId(tool.id)} className={`p-3 rounded-lg border transition-all cursor-pointer ${ isSelected ? "bg-[#18181b] border-blue-500/40 text-blue-400 ring-1 ring-blue-500/20" : "bg-[#121214] border-[#27272a] hover:border-zinc-700 hover:bg-[#18181b]/40 text-zinc-300" }`} >
{tool.name} {tool.serverName}

{tool.description}

); })}
{/* Add New Tool Card */}
Declare New MCP Tool
setNewToolName(e.target.value)} className="w-full bg-zinc-950 border border-zinc-800 rounded px-2.5 py-1.5 text-xs text-zinc-200 placeholder-zinc-600 focus:ring-1 focus:ring-blue-600 outline-none" /> setNewServerName(e.target.value)} className="w-full bg-zinc-950 border border-zinc-800 rounded px-2.5 py-1.5 text-xs text-zinc-200 placeholder-zinc-600 focus:ring-1 focus:ring-blue-600 outline-none" /> setNewToolDesc(e.target.value)} className="w-full bg-zinc-950 border border-zinc-800 rounded px-2.5 py-1.5 text-xs text-zinc-200 placeholder-zinc-600 focus:ring-1 focus:ring-blue-600 outline-none" />
{/* Right: Active Tool Schema & Test Harness Simulator */}
{activeTool && (
{activeTool.serverName}

{activeTool.name}

Docstring / Instructions: {activeTool.description}
{/* JSON Schema */}
Parameter JSON Schema
                  {JSON.stringify(activeTool.parametersSchema, null, 2)}
                
{/* Interactive MCP Test Harness Simulator */}
Interactive Harness Verification
setTestUserPrompt(e.target.value)} className="w-full bg-zinc-950 border border-zinc-800 rounded px-2.5 py-1.5 text-xs text-zinc-200 focus:ring-1 focus:ring-blue-600 outline-none font-mono" /> {harnessOutput && (
                    {harnessOutput}
                  
)}
)}
); };