move MCP to settings

This commit is contained in:
Andrew Pareles 2025-05-21 21:24:46 -07:00
parent 72143608a1
commit 18d1f2e6b9
2 changed files with 125 additions and 123 deletions

View file

@ -1,113 +0,0 @@
import { VoidSwitch, VoidButtonBgDarken } from '../util/inputs.js';
import { MCPConfigFileParseErrorResponse, MCPServerEventType, MCPServerEventResponse, MCPServerObject, MCPServerOfName } from '../../../../common/mcpServiceTypes.js';
import { useEffect, useState } from 'react';
import { useAccessor, useMCPServiceState } from '../util/services.js';
// MCP Server component
const MCPServer = ({ name, server }: { name: string, server: MCPServerObject }) => {
const accessor = useAccessor();
const mcpService = accessor.get('IMCPService');
const handleChangeEvent = (e: boolean) => {
// Handle the change event
mcpService.toggleMCPServer(name, e);
}
return (
<div className="border-b border-gray-800 bg-gray-300/10 py-4 rounded-lg ">
<div className="flex items-center mx-4">
{/* Status indicator */}
<div className={`w-2 h-2 rounded-full mr-2
${server.status === 'success' ? 'green-500'
: server.status === 'error' ? 'red-500'
: server.status === 'loading' ? 'yellow-500'
: server.status === 'offline' ? 'gray-500'
: ''}
`}></div>
{/* Server name */}
<div className="text-sm font-medium mr-2">{name}</div>
{/* Power toggle switch */}
<div className="ml-auto">
<VoidSwitch
value={server.isOn ?? false}
disabled={server.status === 'error'}
onChange={handleChangeEvent}
/>
</div>
</div>
{/* Tools section */}
<div className="mt-1 mx-4">
<div className="flex flex-wrap gap-2 max-h-32 overflow-y-auto pb-1">
{server.isOn && server.tools.length > 0 ? (
server.tools.map((tool) => (
<span
key={tool.name}
className="px-2 py-0.5 bg-black/5 dark:bg-white/5 rounded text-xs"
title={tool.description || ''}
>
{tool.name}
</span>
))
) : (
<span className="text-xs text-gray-500">No tools available</span>
)}
</div>
</div>
{/* Command badge */}
{server.isOn && server.command && (
<div className="mt-2 mx-4">
<div className="text-xs text-gray-400">Command:</div>
<div className="px-2 py-1 bg-void-bg-3 text-xs font-mono overflow-x-auto whitespace-nowrap">
{server.command}
</div>
</div>
)}
{/* Error message if present */}
{server.error && (
<div className="mt-2 ml-4 text-red-500 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="mr-1">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
{server.error}
</div>
)}
</div>
);
};
// Main component that renders the list of servers
const MCPServersList = () => {
const mcpServiceState = useMCPServiceState()
const accessor = useAccessor();
const mcpService = accessor.get('IMCPService');
return (
<div className="text-white rounded-md py-4">
<div>
{!mcpServiceState.error && Object.entries(mcpServiceState.mcpServerOfName).map(([name, server]) => (
<div className="py-2" key={name}>
<MCPServer name={name} server={server} />
</div>
))}
{mcpServiceState.error && (
<div className="text-red-500 text-sm font-medium">
{mcpServiceState.error}
</div>
)}
</div>
</div>
);
};
export default MCPServersList;

View file

@ -19,7 +19,8 @@ import { ToolApprovalType, toolApprovalTypes } from '../../../../common/toolsSer
import Severity from '../../../../../../../base/common/severity.js'
import { getModelCapabilities, modelOverrideKeys, ModelOverrides } from '../../../../common/modelCapabilities.js';
import { TransferEditorType, TransferFilesInfo } from '../../../extensionTransferTypes.js';
import MCPServersList from './MCPServersList.js';
import { MCPServerObject } from '../../../../common/mcpServiceTypes.js';
import { useMCPServiceState } from '../util/services.js';
const ButtonLeftTextRightOption = ({ text, leftButton }: { text: string, leftButton?: React.ReactNode }) => {
@ -899,6 +900,118 @@ export const OneClickSwitchButton = ({ fromEditor = 'VS Code', className = '' }:
// full settings
// MCP Server component
const MCPServer = ({ name, server }: { name: string, server: MCPServerObject }) => {
const accessor = useAccessor();
const mcpService = accessor.get('IMCPService');
const handleChangeEvent = (e: boolean) => {
// Handle the change event
mcpService.toggleMCPServer(name, e);
}
return (
<div className="border-b border-gray-800 bg-gray-300/10 py-4 rounded-lg ">
<div className="flex items-center mx-4">
{/* Status indicator */}
<div className={`w-2 h-2 rounded-full mr-2
${server.status === 'success' ? 'green-500'
: server.status === 'error' ? 'red-500'
: server.status === 'loading' ? 'yellow-500'
: server.status === 'offline' ? 'gray-500'
: ''}
`}></div>
{/* Server name */}
<div className="text-sm font-medium mr-2">{name}</div>
{/* Power toggle switch */}
<div className="ml-auto">
<VoidSwitch
value={server.isOn ?? false}
disabled={server.status === 'error'}
onChange={handleChangeEvent}
/>
</div>
</div>
{/* Tools section */}
<div className="mt-1 mx-4">
<div className="flex flex-wrap gap-2 max-h-32 overflow-y-auto pb-1">
{server.isOn && server.tools.length > 0 ? (
server.tools.map((tool: { name: string; description?: string }) => (
<span
key={tool.name}
className="px-2 py-0.5 bg-black/5 dark:bg-white/5 rounded text-xs"
title={tool.description || ''}
>
{tool.name}
</span>
))
) : (
<span className="text-xs text-gray-500">No tools available</span>
)}
</div>
</div>
{/* Command badge */}
{server.isOn && server.command && (
<div className="mt-2 mx-4">
<div className="text-xs text-gray-400">Command:</div>
<div className="px-2 py-1 bg-void-bg-3 text-xs font-mono overflow-x-auto whitespace-nowrap">
{server.command}
</div>
</div>
)}
{/* Error message if present */}
{server.error && (
<div className="mt-2 ml-4 text-red-500 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="mr-1">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
{server.error}
</div>
)}
</div>
);
};
// Main component that renders the list of servers
const MCPServersList = () => {
const mcpServiceState = useMCPServiceState()
const accessor = useAccessor();
const mcpService = accessor.get('IMCPService');
let content: React.ReactNode
if (mcpServiceState.error) {
content = <div className="text-red-500 text-sm font-medium">
{mcpServiceState.error}
</div>
}
else {
const entries = Object.entries(mcpServiceState.mcpServerOfName)
if (entries.length === 0) {
content = <div className="text-red-500 text-sm font-medium">
No servers found
</div>
}
else {
content = entries.map(([name, server]) => (
<div className="py-2" key={name}>
<MCPServer name={name} server={server} />
</div>
))
}
}
return content
};
export const Settings = () => {
const isDark = useIsDark()
const accessor = useAccessor()
@ -1235,6 +1348,7 @@ export const Settings = () => {
<div className='mt-12 max-w-[600px]'>
<h2 className={`text-3xl mb-2`}>AI Instructions</h2>
<h4 className={`text-void-fg-3 mb-4`}>
<ChatMarkdownRender inPTag={true} string={`
System instructions to include with all AI requests.
@ -1247,21 +1361,22 @@ Alternatively, place a \`.voidrules\` file in the root of your workspace.
</div>
<div className='mt-12 max-w-[600px]'>
<div className='flex items-center justify-between mb-2'>
<h2 className={`text-3xl`}>MCP</h2>
<VoidButtonBgDarken className='px-4 py-1' onClick={async () => { await mcpService.revealMCPConfigFile() }}>
Add MCP Server
</VoidButtonBgDarken>
</div>
<h2 className='text-3xl mb-2'>MCP</h2>
<h4 className={`text-void-fg-3 mb-4`}>
<ChatMarkdownRender inPTag={true} string={`
Use Model Context Protocol to provide Agent mode with more tools.
`} chatMessageLocation={undefined} />
</h4>
<ErrorBoundary>
<MCPServersList />
</ErrorBoundary>
<div>
<VoidButtonBgDarken className='px-4 py-1 mb-2' onClick={async () => { await mcpService.revealMCPConfigFile() }}>
Add MCP Server
</VoidButtonBgDarken>
</div>
</div>
<ErrorBoundary>
<MCPServersList />
</ErrorBoundary>
</div>
</div>
</div>