style: improve MCP configuration page style (#4475)

This commit is contained in:
Dan 2025-03-14 10:39:33 +08:00 committed by GitHub
parent 1725b0d09a
commit b1eeb004c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 139 additions and 80 deletions

View File

@ -45,34 +45,86 @@
.serverItem {
padding: 16px;
border-radius: 8px;
background-color: var(--editorWidget-background);
border: 1px solid var(--border-color);
background-color: var(--editor-background);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
.serverHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.iconButton {
padding: 2px 5px;
margin-left: 5px;
cursor: pointer;
border-radius: 4px;
line-height: 20px;
font-size: 14px;
&:hover {
background-color: var(--badge-background);
}
}
.serverActionButton {
height: 24px;
background-color: var(--badge-background);
color: var(--badge-foreground);
span {
min-width: 52px;
}
&.active {
i {
color: var(--testing-iconPassed);
}
}
i {
color: var(--testing-iconErrored);
margin-right: 3px;
}
}
}
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-color: var(--focusBorder);
}
}
.serverHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.serverTitleRow {
display: flex;
align-items: center;
gap: 8px;
text-indent: 12px;
}
.serverActions {
display: flex;
align-items: center;
flex: 1;
justify-content: flex-end;
}
.serverName {
margin: 0;
font-size: 14px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
}
.serverStatusIcon {
border-radius: 50%;
width: 6px;
height: 6px;
margin-left: 12px;
display: inline-block;
&.active {
background-color: var(--testing-iconPassed);
box-shadow: 0 0 0 1px var(--testing-iconPassed);
}
&.inactive {
background-color: var(--testing-iconErrored);
box-shadow: 0 0 0 1px var(--testing-iconErrored);
}
}
.serverStatus,
@ -104,26 +156,7 @@
color: var(--notification-error-foreground);
}
.serverActions {
display: flex;
gap: 4px;
}
.iconButton {
padding: 4px;
border: none;
background: none;
color: var(--icon-foreground);
cursor: pointer;
border-radius: 4px;
&:hover {
background-color: var(--list-hover-background);
}
}
.serverDetail {
margin-top: 12px;
padding: 12px;
border-radius: 6px;
background-color: var(--editor-background);
@ -181,11 +214,9 @@
display: inline-flex;
align-items: center;
padding: 2px 8px;
background-color: var(--editorWidget-background);
color: var(--button-secondary-foreground);
border-radius: 12px;
font-size: 12px;
font-weight: 500;
transition: all 0.2s ease;
border: 1px solid var(--border-color);
cursor: pointer;
}

View File

@ -1,7 +1,7 @@
import cls from 'classnames';
import React, { useCallback } from 'react';
import { Badge } from '@opensumi/ide-components';
import { Badge, Button, Popover, PopoverTriggerType } from '@opensumi/ide-components';
import { AINativeSettingSectionsId, ILogger, useInjectable } from '@opensumi/ide-core-browser';
import { PreferenceService } from '@opensumi/ide-core-browser/lib/preferences';
import { PreferenceScope, localize } from '@opensumi/ide-core-common';
@ -178,43 +178,60 @@ export const MCPConfigView: React.FC = () => {
<div key={server.name} className={styles.serverItem}>
<div className={styles.serverHeader}>
<div className={styles.serverTitleRow}>
<h3 className={styles.serverName}>{server.name}</h3>
<h3 className={styles.serverName}>
{server.name}
<span
className={cls(styles.serverStatusIcon, server.isStarted ? styles.active : styles.inactive)}
></span>
</h3>
</div>
<div className={styles.serverActions}>
{server.name !== BUILTIN_MCP_SERVER_NAME && (
<button className={styles.iconButton} title='Edit' onClick={() => handleEditServer(server)}>
<i className='codicon codicon-edit' />
</button>
)}
<button
className={styles.iconButton}
title={server.isStarted ? 'Stop' : 'Start'}
onClick={() => handleServerControl(server.name, !server.isStarted)}
<Popover
id='mcp-server-action-popover'
trigger={PopoverTriggerType.hover}
content={
server.isStarted ? localize('ai.native.mcp.disable.title') : localize('ai.native.mcp.enable.title')
}
>
<i
className={`codicon ${
loadingServer === server.name
? 'codicon-loading kt-icon-loading'
: server.isStarted
? 'codicon-debug-stop'
: 'codicon-debug-start'
}`}
/>
</button>
<Button
type='default'
className={cls(styles.serverActionButton, server.isStarted && styles.active)}
onClick={() => handleServerControl(server.name, !server.isStarted)}
>
<i
className={`codicon ${
loadingServer === server.name
? 'codicon-loading kt-icon-loading'
: server.isStarted
? 'codicon-check'
: 'codicon-circle'
}`}
/>
<span>{localize(server.isStarted ? 'ai.native.mcp.enabled' : 'ai.native.mcp.disabled')}</span>
</Button>
</Popover>
{server.name !== BUILTIN_MCP_SERVER_NAME && (
<button className={styles.iconButton} title='Delete' onClick={() => handleDeleteServer(server.name)}>
<i className='codicon codicon-trash' />
</button>
<Button
type='icon'
iconClass='codicon codicon-edit'
className={styles.iconButton}
title='Edit'
onClick={() => handleEditServer(server)}
/>
)}
{server.name !== BUILTIN_MCP_SERVER_NAME && (
<Button
type='icon'
iconClass='codicon codicon-trash'
className={styles.iconButton}
title='Delete'
onClick={() => handleDeleteServer(server.name)}
/>
)}
</div>
</div>
<div className={styles.serverDetail}>
<div className={styles.detailRow}>
<span className={styles.detailLabel}>Status:</span>
<span className={`${styles.serverStatus} ${server.isStarted ? styles.running : styles.stopped}`}>
{server.isStarted ? localize('ai.native.mcp.running') : localize('ai.native.mcp.stopped')}
</span>
</div>
{server.type && (
<div className={styles.detailRow}>
<span className={styles.detailLabel}>Type:</span>
@ -228,9 +245,9 @@ export const MCPConfigView: React.FC = () => {
<span className={styles.detailLabel}>Tools:</span>
<span className={styles.detailContent}>
{server.tools.map((tool, index) => (
<span key={index} className={styles.toolTag}>
{tool}
</span>
<Badge key={index} className={styles.toolTag} title={tool.description}>
{tool.name}
</Badge>
))}
</span>
</div>

View File

@ -46,6 +46,9 @@
gap: 8px;
padding-top: 16px;
border-top: 1px solid var(--border-color);
.secondaryButton {
opacity: 0.6;
}
}
.formRow {

View File

@ -263,7 +263,7 @@ export const MCPServerForm: FC<Props> = ({ visible, initialData, onSave, onCance
</div>
{renderFormItems()}
<div className={styles.formActions}>
<Button onClick={onCancel} type='ghost'>
<Button onClick={onCancel} type='primary' className={styles.secondaryButton}>
{localize('ai.native.mcp.buttonCancel')}
</Button>
<Button onClick={handleSubmit} type='primary'>

View File

@ -37,7 +37,7 @@ export const AI_CHAT_LOGO_AVATAR_ID = 'AI-Chat-Logo-Avatar';
export const AI_MENU_BAR_DEBUG_TOOLBAR = 'AI_MENU_BAR_DEBUG_TOOLBAR';
// 内置 MCP 服务器名称
export const BUILTIN_MCP_SERVER_NAME = 'builtin';
export const BUILTIN_MCP_SERVER_NAME = 'Builtin';
/**
* @deprecated Use {@link DESIGN_MENUBAR_CONTAINER_VIEW_ID} instead
@ -134,7 +134,7 @@ export interface ISumiMCPServerBackend {
initBuiltinMCPServer(enabled: boolean): void;
initExternalMCPServers(servers: MCPServerDescription[]): void;
getAllMCPTools(): Promise<MCPTool[]>;
getServers(): Promise<Array<{ name: string; isStarted: boolean }>>;
getServers(): Promise<Array<{ name: string; isStarted: boolean; tools: MCPTool[] }>>;
startServer(serverName: string): Promise<void>;
stopServer(serverName: string): Promise<void>;
addOrUpdateServer(description: MCPServerDescription): void;

View File

@ -46,7 +46,7 @@ export interface IMCPServerProxyService {
export interface MCPServer {
name: string;
isStarted: boolean;
tools?: string[];
tools?: MCPTool[];
command?: string;
type?: string;
serverHost?: string;

View File

@ -136,12 +136,16 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
const servers = Array.from(this.mcpServerManager.getServers().entries());
const serverInfos = await Promise.all(
servers.map(async ([serverName, server]) => {
let toolNames: string[] = [];
let tools: MCPTool[] = [];
if (server.isStarted()) {
// 只获取正在运行的 MCP Server 的工具列表
const toolsResponse = await server.getTools();
this.logger.log(`Server ${serverName} tools:`, toolsResponse.tools);
toolNames = toolsResponse.tools.map((tool) => tool.name);
tools = toolsResponse.tools.map((tool) => ({
name: tool.name,
description: tool.description || '',
inputSchema: tool.inputSchema,
providerName: serverName,
}));
}
// OpenSumi 内置的 MCP Server
@ -150,7 +154,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
name: server.getServerName(),
isStarted: server.isStarted(),
type: MCP_SERVER_TYPE.BUILTIN,
tools: toolNames,
tools,
};
}
@ -161,7 +165,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
isStarted: server.isStarted(),
type: MCP_SERVER_TYPE.STDIO,
command: server.command + ' ' + (server.args?.join(' ') || ''),
tools: toolNames,
tools,
};
} else if (server instanceof SSEMCPServer) {
return {
@ -169,7 +173,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
isStarted: server.isStarted(),
type: MCP_SERVER_TYPE.SSE,
serverHost: server.serverHost,
tools: toolNames,
tools,
};
}
@ -178,7 +182,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
isStarted: server.isStarted(),
type: '[MOCK] stdio',
command: '[MOCK] npx sumi-ide-mcp-server',
tools: toolNames,
tools,
};
}),
);

View File

@ -1621,8 +1621,10 @@ export const localizationBundle = {
'ai.native.mcp.command.isRequired': 'Command is required',
'ai.native.mcp.serverHost.isRequired': 'SSE URL is required',
'ai.native.mcp.manage.connections': 'Manage your MCP server connections',
'ai.native.mcp.running': 'Running',
'ai.native.mcp.stopped': 'Stopped',
'ai.native.mcp.enabled': 'Enabled',
'ai.native.mcp.disabled': 'Disabled',
'ai.native.mcp.enable.title': 'Start this service',
'ai.native.mcp.disable.title': 'Stop this service',
// MCP View
'ai.native.mcp.tool.arguments': 'Arguments',

View File

@ -1384,8 +1384,10 @@ export const localizationBundle = {
'ai.native.mcp.command.isRequired': '命令不能为空',
'ai.native.mcp.serverHost.isRequired': 'SSE URL 不能为空',
'ai.native.mcp.manage.connections': '管理你的 MCP 服务器连接',
'ai.native.mcp.running': '运行中',
'ai.native.mcp.stopped': '已停止',
'ai.native.mcp.enabled': '已启用',
'ai.native.mcp.disabled': '已禁用',
'ai.native.mcp.enable.title': '启动该服务',
'ai.native.mcp.disable.title': '停止该服务',
// MCP View
'ai.native.mcp.tool.arguments': '参数',