Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style: improve MCP configuration page style #4475

Merged
merged 1 commit into from
Mar 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
.serverTitleRow {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
gap: 8px;
text-indent: 12px;
}

.serverTitleRow {
.serverActions {
display: flex;
align-items: center;
gap: 8px;
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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
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
type='icon'
iconClass='codicon codicon-edit'
className={styles.iconButton}
title='Edit'
onClick={() => handleEditServer(server)}
/>
</button>
)}

{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-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>
Expand All @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
gap: 8px;
padding-top: 16px;
border-top: 1px solid var(--border-color);
.secondaryButton {
opacity: 0.6;
}
}

.formRow {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'>
Expand Down
4 changes: 2 additions & 2 deletions packages/ai-native/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/ai-native/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export interface IMCPServerProxyService {
export interface MCPServer {
name: string;
isStarted: boolean;
tools?: string[];
tools?: MCPTool[];
command?: string;
type?: string;
serverHost?: string;
Expand Down
18 changes: 11 additions & 7 deletions packages/ai-native/src/node/mcp/sumi-mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
};
}

Expand All @@ -161,15 +165,15 @@ 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 {
name: server.getServerName(),
isStarted: server.isStarted(),
type: MCP_SERVER_TYPE.SSE,
serverHost: server.serverHost,
tools: toolNames,
tools,
};
}

Expand All @@ -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,
};
}),
);
Expand Down
6 changes: 4 additions & 2 deletions packages/i18n/src/common/en-US.lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
6 changes: 4 additions & 2 deletions packages/i18n/src/common/zh-CN.lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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': '参数',
Expand Down
Loading