Add mock data and gui

This commit is contained in:
Saoud Rizwan
2024-12-01 14:41:06 -08:00
parent 5052944efe
commit bee3df5319

View File

@@ -1,10 +1,120 @@
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import {
VSCodeButton,
VSCodeDivider,
VSCodeTextArea,
VSCodeTextField,
VSCodeTag,
VSCodePanelTab,
VSCodePanelView,
VSCodeDataGrid,
VSCodeDataGridRow,
VSCodeDataGridCell,
VSCodePanels,
} from "@vscode/webview-ui-toolkit/react"
import { useState } from "react"
type McpServer = {
name: string
config: string // JSON config
status: "connected" | "connecting" | "disconnected"
error?: string
tools?: any[] // We'll type this properly later
resources?: any[] // We'll type this properly later
}
type McpViewProps = {
onDone: () => void
}
const McpView = ({ onDone }: McpViewProps) => {
const [isAdding, setIsAdding] = useState(false)
const [servers, setServers] = useState<McpServer[]>([
// Add some mock servers for testing
{
name: "local-tools",
config: JSON.stringify({
mcpServers: {
"local-tools": {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-tools"],
},
},
}),
status: "connected",
tools: [
{
name: "execute_command",
description: "Run a shell command on the local system",
},
{
name: "read_file",
description: "Read contents of a file from the filesystem",
},
],
},
{
name: "postgres-db",
config: JSON.stringify({
mcpServers: {
"postgres-db": {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"],
},
},
}),
status: "disconnected",
error: "Failed to connect to database: Connection refused",
},
{
name: "github-tools",
config: JSON.stringify({
mcpServers: {
"github-tools": {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
},
},
}),
status: "connecting",
resources: [
{
uri: "github://repo/issues",
name: "Repository Issues",
},
{
uri: "github://repo/pulls",
name: "Pull Requests",
},
],
},
])
const [configInput, setConfigInput] = useState("")
const handleAddServer = () => {
try {
const config = JSON.parse(configInput)
const serverName = Object.keys(config.mcpServers)[0]
setServers((prev) => [
...prev,
{
name: serverName,
config: configInput,
status: "connecting",
},
])
setIsAdding(false)
setConfigInput("")
// Here you would trigger the actual server connection
// and update its status/tools/resources accordingly
} catch (e) {
// Handle invalid JSON
console.error("Invalid server configuration:", e)
}
}
return (
<div
style={{
@@ -24,14 +134,256 @@ const McpView = ({ onDone }: McpViewProps) => {
alignItems: "center",
padding: "10px 17px 10px 20px",
}}>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>MCP</h3>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>MCP Servers</h3>
<VSCodeButton onClick={onDone}>Done</VSCodeButton>
</div>
<div style={{ padding: "20px", display: "flex", justifyContent: "center" }}>
<VSCodeButton>Add Server</VSCodeButton>
<p style={{ padding: "0 20px", color: "var(--vscode-foreground)", fontSize: "13px" }}>
MCP (Model Context Protocol) enables AI models to access external tools and data through standardized
interfaces. Add MCP servers to extend Claude's capabilities with custom functionality and real-time data
access.
</p>
{/* Server List */}
<div style={{ flex: 1, overflow: "auto", padding: "0 20px" }}>
{servers.map((server) => (
<ServerRow key={server.name} server={server} />
))}
</div>
{/* Add Server UI */}
<div style={{ padding: "20px" }}>
{isAdding ? (
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
<VSCodeTextArea
rows={4}
placeholder='{"mcpServers": {"server-name": {"command": "...", "args": [...]}}}'
value={configInput}
onChange={(e) => setConfigInput((e.target as HTMLTextAreaElement).value)}
/>
<div style={{ display: "flex", gap: "10px" }}>
<VSCodeButton style={{ flex: 1 }} onClick={handleAddServer}>
Add Server
</VSCodeButton>
<VSCodeButton style={{ flex: 1 }} appearance="secondary" onClick={() => setIsAdding(false)}>
Cancel
</VSCodeButton>
</div>
</div>
) : (
<VSCodeButton style={{ width: "100%" }} onClick={() => setIsAdding(true)}>
<span className="codicon codicon-add" style={{ marginRight: "6px" }}></span>
Add MCP Server
</VSCodeButton>
)}
</div>
</div>
)
}
// Server Row Component
const ServerRow = ({ server }: { server: McpServer }) => {
const [isExpanded, setIsExpanded] = useState(false)
const [isEditing, setIsEditing] = useState(false)
const [editConfig, setEditConfig] = useState(server.config)
const getStatusColor = () => {
switch (server.status) {
case "connected":
return "var(--vscode-testing-iconPassed)"
case "connecting":
return "var(--vscode-charts-yellow)"
case "disconnected":
return "var(--vscode-testing-iconFailed)"
}
}
const handleSaveConfig = () => {
try {
JSON.parse(editConfig) // Validate JSON
// Here you would update the server config
setIsEditing(false)
} catch (e) {
console.error("Invalid JSON config:", e)
}
}
// Don't allow expansion if server has error
const handleRowClick = () => {
if (!server.error) {
setIsExpanded(!isExpanded)
}
}
return (
<div style={{ marginBottom: "10px" }}>
<div
style={{
display: "flex",
alignItems: "center",
padding: "8px",
background: "var(--vscode-list-hoverBackground)",
cursor: server.error ? "default" : "pointer",
borderRadius: isExpanded || server.error ? "4px 4px 0 0" : "4px",
}}
onClick={handleRowClick}>
{!server.error && (
<span
className={`codicon codicon-chevron-${isExpanded ? "down" : "right"}`}
style={{ marginRight: "8px" }}
/>
)}
<span style={{ flex: 1 }}>{server.name}</span>
<div
style={{
width: "8px",
height: "8px",
borderRadius: "50%",
background: getStatusColor(),
marginLeft: "8px",
}}
/>
</div>
{server.error ? (
<div
style={{
padding: "8px",
fontSize: "13px",
color: "var(--vscode-testing-iconFailed)",
background: "var(--vscode-list-hoverBackground)",
borderRadius: "0 0 4px 4px",
}}>
{server.error}
</div>
) : (
isExpanded && (
<div
style={{
background: "var(--vscode-list-hoverBackground)",
padding: "0 12px 12px 12px",
fontSize: "13px",
borderRadius: "0 0 4px 4px",
}}>
<VSCodePanels>
<VSCodePanelTab id="tools">Tools ({server.tools?.length || 0})</VSCodePanelTab>
<VSCodePanelTab id="resources">Resources ({server.resources?.length || 0})</VSCodePanelTab>
<VSCodePanelView id="tools-view">
{server.tools && server.tools.length > 0 ? (
<div style={{ display: "flex", flexDirection: "column", gap: "3px" }}>
{server.tools.map((tool) => (
<div
key={tool.name}
style={{
padding: "8px 0",
}}>
<div style={{ display: "flex" }}>
<span
className="codicon codicon-symbol-method"
style={{ marginRight: "6px" }}></span>
<span style={{ fontWeight: 500 }}>{tool.name}</span>
</div>
<div
style={{
marginLeft: "0px",
marginTop: "4px",
opacity: 0.8,
fontSize: "12px",
}}>
{tool.description}
</div>
</div>
))}
</div>
) : (
<div style={{ padding: "10px 0", color: "var(--vscode-descriptionForeground)" }}>
No tools found
</div>
)}
</VSCodePanelView>
{/* Resources Panel View */}
<VSCodePanelView id="resources-view">
{server.resources && server.resources.length > 0 ? (
<div style={{ display: "flex", flexDirection: "column", gap: "3px" }}>
{server.resources.map((resource) => (
<div
key={resource.uri}
style={{
padding: "8px 0",
}}>
<div style={{ display: "flex" }}>
<span
className="codicon codicon-symbol-file"
style={{ marginRight: "6px" }}></span>
<span style={{ fontWeight: 500 }}>{resource.name}</span>
</div>
<div style={{ marginTop: "6px", fontSize: "12px" }}>
<code
style={{
color: "var(--vscode-textPreformat-foreground)",
background: "var(--vscode-textPreformat-background)",
padding: "2px 4px",
borderRadius: "3px",
}}>
{resource.uri}
</code>
</div>
</div>
))}
</div>
) : (
<div style={{ padding: "10px 0", color: "var(--vscode-descriptionForeground)" }}>
No resources found
</div>
)}
</VSCodePanelView>
</VSCodePanels>
{/* Edit/Remove Buttons */}
<div style={{ display: "flex", flexDirection: "column", gap: "8px", marginTop: "0px" }}>
{isEditing ? (
<>
<VSCodeTextArea
value={editConfig}
onChange={(e) => setEditConfig((e.target as HTMLTextAreaElement).value)}
style={{ width: "100%" }}
/>
<div style={{ display: "flex", gap: "8px" }}>
<VSCodeButton onClick={handleSaveConfig} style={{ flex: 1 }}>
Save
</VSCodeButton>
<VSCodeButton
appearance="secondary"
onClick={() => setIsEditing(false)}
style={{ flex: 1 }}>
Cancel
</VSCodeButton>
</div>
</>
) : (
<div style={{ display: "flex", gap: "8px" }}>
<VSCodeButton
appearance="secondary"
onClick={() => setIsEditing(true)}
style={{ flex: 1 }}>
Edit
</VSCodeButton>
<VSCodeButton
appearance="secondary"
style={{
flex: 1,
}}>
Remove
</VSCodeButton>
</div>
)}
</div>
</div>
)
)}
</div>
)
}
export default McpView