Skip to content

Commit b1eeb00

Browse files
authored
style: improve MCP configuration page style (#4475)
1 parent 1725b0d commit b1eeb00

File tree

9 files changed

+136
-77
lines changed

9 files changed

+136
-77
lines changed

packages/ai-native/src/browser/mcp/config/components/mcp-config.module.less

+61-30
Original file line numberDiff line numberDiff line change
@@ -45,34 +45,86 @@
4545
.serverItem {
4646
padding: 16px;
4747
border-radius: 8px;
48-
background-color: var(--editorWidget-background);
49-
border: 1px solid var(--border-color);
48+
background-color: var(--editor-background);
5049
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
5150
transition: all 0.2s ease;
52-
51+
.serverHeader {
52+
display: flex;
53+
justify-content: space-between;
54+
align-items: center;
55+
margin-bottom: 12px;
56+
.iconButton {
57+
padding: 2px 5px;
58+
margin-left: 5px;
59+
cursor: pointer;
60+
border-radius: 4px;
61+
line-height: 20px;
62+
font-size: 14px;
63+
&:hover {
64+
background-color: var(--badge-background);
65+
}
66+
}
67+
.serverActionButton {
68+
height: 24px;
69+
background-color: var(--badge-background);
70+
color: var(--badge-foreground);
71+
span {
72+
min-width: 52px;
73+
}
74+
&.active {
75+
i {
76+
color: var(--testing-iconPassed);
77+
}
78+
}
79+
i {
80+
color: var(--testing-iconErrored);
81+
margin-right: 3px;
82+
}
83+
}
84+
}
5385
&:hover {
5486
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
5587
border-color: var(--focusBorder);
5688
}
5789
}
5890

59-
.serverHeader {
91+
.serverTitleRow {
6092
display: flex;
61-
justify-content: space-between;
6293
align-items: center;
63-
margin-bottom: 12px;
94+
gap: 8px;
95+
text-indent: 12px;
6496
}
6597

66-
.serverTitleRow {
98+
.serverActions {
6799
display: flex;
68100
align-items: center;
69-
gap: 8px;
101+
flex: 1;
102+
justify-content: flex-end;
70103
}
71104

72105
.serverName {
73106
margin: 0;
74107
font-size: 14px;
75108
font-weight: 500;
109+
display: flex;
110+
align-items: center;
111+
justify-content: center;
112+
}
113+
114+
.serverStatusIcon {
115+
border-radius: 50%;
116+
width: 6px;
117+
height: 6px;
118+
margin-left: 12px;
119+
display: inline-block;
120+
&.active {
121+
background-color: var(--testing-iconPassed);
122+
box-shadow: 0 0 0 1px var(--testing-iconPassed);
123+
}
124+
&.inactive {
125+
background-color: var(--testing-iconErrored);
126+
box-shadow: 0 0 0 1px var(--testing-iconErrored);
127+
}
76128
}
77129

78130
.serverStatus,
@@ -104,26 +156,7 @@
104156
color: var(--notification-error-foreground);
105157
}
106158

107-
.serverActions {
108-
display: flex;
109-
gap: 4px;
110-
}
111-
112-
.iconButton {
113-
padding: 4px;
114-
border: none;
115-
background: none;
116-
color: var(--icon-foreground);
117-
cursor: pointer;
118-
border-radius: 4px;
119-
120-
&:hover {
121-
background-color: var(--list-hover-background);
122-
}
123-
}
124-
125159
.serverDetail {
126-
margin-top: 12px;
127160
padding: 12px;
128161
border-radius: 6px;
129162
background-color: var(--editor-background);
@@ -181,11 +214,9 @@
181214
display: inline-flex;
182215
align-items: center;
183216
padding: 2px 8px;
184-
background-color: var(--editorWidget-background);
185-
color: var(--button-secondary-foreground);
186217
border-radius: 12px;
187218
font-size: 12px;
188219
font-weight: 500;
189220
transition: all 0.2s ease;
190-
border: 1px solid var(--border-color);
221+
cursor: pointer;
191222
}

packages/ai-native/src/browser/mcp/config/components/mcp-config.view.tsx

+49-32
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import cls from 'classnames';
22
import React, { useCallback } from 'react';
33

4-
import { Badge } from '@opensumi/ide-components';
4+
import { Badge, Button, Popover, PopoverTriggerType } from '@opensumi/ide-components';
55
import { AINativeSettingSectionsId, ILogger, useInjectable } from '@opensumi/ide-core-browser';
66
import { PreferenceService } from '@opensumi/ide-core-browser/lib/preferences';
77
import { PreferenceScope, localize } from '@opensumi/ide-core-common';
@@ -178,43 +178,60 @@ export const MCPConfigView: React.FC = () => {
178178
<div key={server.name} className={styles.serverItem}>
179179
<div className={styles.serverHeader}>
180180
<div className={styles.serverTitleRow}>
181-
<h3 className={styles.serverName}>{server.name}</h3>
181+
<h3 className={styles.serverName}>
182+
{server.name}
183+
<span
184+
className={cls(styles.serverStatusIcon, server.isStarted ? styles.active : styles.inactive)}
185+
></span>
186+
</h3>
182187
</div>
183188
<div className={styles.serverActions}>
184-
{server.name !== BUILTIN_MCP_SERVER_NAME && (
185-
<button className={styles.iconButton} title='Edit' onClick={() => handleEditServer(server)}>
186-
<i className='codicon codicon-edit' />
187-
</button>
188-
)}
189-
<button
190-
className={styles.iconButton}
191-
title={server.isStarted ? 'Stop' : 'Start'}
192-
onClick={() => handleServerControl(server.name, !server.isStarted)}
189+
<Popover
190+
id='mcp-server-action-popover'
191+
trigger={PopoverTriggerType.hover}
192+
content={
193+
server.isStarted ? localize('ai.native.mcp.disable.title') : localize('ai.native.mcp.enable.title')
194+
}
193195
>
194-
<i
195-
className={`codicon ${
196-
loadingServer === server.name
197-
? 'codicon-loading kt-icon-loading'
198-
: server.isStarted
199-
? 'codicon-debug-stop'
200-
: 'codicon-debug-start'
201-
}`}
196+
<Button
197+
type='default'
198+
className={cls(styles.serverActionButton, server.isStarted && styles.active)}
199+
onClick={() => handleServerControl(server.name, !server.isStarted)}
200+
>
201+
<i
202+
className={`codicon ${
203+
loadingServer === server.name
204+
? 'codicon-loading kt-icon-loading'
205+
: server.isStarted
206+
? 'codicon-check'
207+
: 'codicon-circle'
208+
}`}
209+
/>
210+
<span>{localize(server.isStarted ? 'ai.native.mcp.enabled' : 'ai.native.mcp.disabled')}</span>
211+
</Button>
212+
</Popover>
213+
{server.name !== BUILTIN_MCP_SERVER_NAME && (
214+
<Button
215+
type='icon'
216+
iconClass='codicon codicon-edit'
217+
className={styles.iconButton}
218+
title='Edit'
219+
onClick={() => handleEditServer(server)}
202220
/>
203-
</button>
221+
)}
222+
204223
{server.name !== BUILTIN_MCP_SERVER_NAME && (
205-
<button className={styles.iconButton} title='Delete' onClick={() => handleDeleteServer(server.name)}>
206-
<i className='codicon codicon-trash' />
207-
</button>
224+
<Button
225+
type='icon'
226+
iconClass='codicon codicon-trash'
227+
className={styles.iconButton}
228+
title='Delete'
229+
onClick={() => handleDeleteServer(server.name)}
230+
/>
208231
)}
209232
</div>
210233
</div>
211234
<div className={styles.serverDetail}>
212-
<div className={styles.detailRow}>
213-
<span className={styles.detailLabel}>Status:</span>
214-
<span className={`${styles.serverStatus} ${server.isStarted ? styles.running : styles.stopped}`}>
215-
{server.isStarted ? localize('ai.native.mcp.running') : localize('ai.native.mcp.stopped')}
216-
</span>
217-
</div>
218235
{server.type && (
219236
<div className={styles.detailRow}>
220237
<span className={styles.detailLabel}>Type:</span>
@@ -228,9 +245,9 @@ export const MCPConfigView: React.FC = () => {
228245
<span className={styles.detailLabel}>Tools:</span>
229246
<span className={styles.detailContent}>
230247
{server.tools.map((tool, index) => (
231-
<span key={index} className={styles.toolTag}>
232-
{tool}
233-
</span>
248+
<Badge key={index} className={styles.toolTag} title={tool.description}>
249+
{tool.name}
250+
</Badge>
234251
))}
235252
</span>
236253
</div>

packages/ai-native/src/browser/mcp/config/components/mcp-server-form.module.less

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
gap: 8px;
4747
padding-top: 16px;
4848
border-top: 1px solid var(--border-color);
49+
.secondaryButton {
50+
opacity: 0.6;
51+
}
4952
}
5053

5154
.formRow {

packages/ai-native/src/browser/mcp/config/components/mcp-server-form.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export const MCPServerForm: FC<Props> = ({ visible, initialData, onSave, onCance
263263
</div>
264264
{renderFormItems()}
265265
<div className={styles.formActions}>
266-
<Button onClick={onCancel} type='ghost'>
266+
<Button onClick={onCancel} type='primary' className={styles.secondaryButton}>
267267
{localize('ai.native.mcp.buttonCancel')}
268268
</Button>
269269
<Button onClick={handleSubmit} type='primary'>

packages/ai-native/src/common/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const AI_CHAT_LOGO_AVATAR_ID = 'AI-Chat-Logo-Avatar';
3737
export const AI_MENU_BAR_DEBUG_TOOLBAR = 'AI_MENU_BAR_DEBUG_TOOLBAR';
3838

3939
// 内置 MCP 服务器名称
40-
export const BUILTIN_MCP_SERVER_NAME = 'builtin';
40+
export const BUILTIN_MCP_SERVER_NAME = 'Builtin';
4141

4242
/**
4343
* @deprecated Use {@link DESIGN_MENUBAR_CONTAINER_VIEW_ID} instead
@@ -134,7 +134,7 @@ export interface ISumiMCPServerBackend {
134134
initBuiltinMCPServer(enabled: boolean): void;
135135
initExternalMCPServers(servers: MCPServerDescription[]): void;
136136
getAllMCPTools(): Promise<MCPTool[]>;
137-
getServers(): Promise<Array<{ name: string; isStarted: boolean }>>;
137+
getServers(): Promise<Array<{ name: string; isStarted: boolean; tools: MCPTool[] }>>;
138138
startServer(serverName: string): Promise<void>;
139139
stopServer(serverName: string): Promise<void>;
140140
addOrUpdateServer(description: MCPServerDescription): void;

packages/ai-native/src/common/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export interface IMCPServerProxyService {
4646
export interface MCPServer {
4747
name: string;
4848
isStarted: boolean;
49-
tools?: string[];
49+
tools?: MCPTool[];
5050
command?: string;
5151
type?: string;
5252
serverHost?: string;

packages/ai-native/src/node/mcp/sumi-mcp-server.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,16 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
136136
const servers = Array.from(this.mcpServerManager.getServers().entries());
137137
const serverInfos = await Promise.all(
138138
servers.map(async ([serverName, server]) => {
139-
let toolNames: string[] = [];
139+
let tools: MCPTool[] = [];
140140
if (server.isStarted()) {
141141
// 只获取正在运行的 MCP Server 的工具列表
142142
const toolsResponse = await server.getTools();
143-
this.logger.log(`Server ${serverName} tools:`, toolsResponse.tools);
144-
toolNames = toolsResponse.tools.map((tool) => tool.name);
143+
tools = toolsResponse.tools.map((tool) => ({
144+
name: tool.name,
145+
description: tool.description || '',
146+
inputSchema: tool.inputSchema,
147+
providerName: serverName,
148+
}));
145149
}
146150

147151
// OpenSumi 内置的 MCP Server
@@ -150,7 +154,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
150154
name: server.getServerName(),
151155
isStarted: server.isStarted(),
152156
type: MCP_SERVER_TYPE.BUILTIN,
153-
tools: toolNames,
157+
tools,
154158
};
155159
}
156160

@@ -161,15 +165,15 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
161165
isStarted: server.isStarted(),
162166
type: MCP_SERVER_TYPE.STDIO,
163167
command: server.command + ' ' + (server.args?.join(' ') || ''),
164-
tools: toolNames,
168+
tools,
165169
};
166170
} else if (server instanceof SSEMCPServer) {
167171
return {
168172
name: server.getServerName(),
169173
isStarted: server.isStarted(),
170174
type: MCP_SERVER_TYPE.SSE,
171175
serverHost: server.serverHost,
172-
tools: toolNames,
176+
tools,
173177
};
174178
}
175179

@@ -178,7 +182,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
178182
isStarted: server.isStarted(),
179183
type: '[MOCK] stdio',
180184
command: '[MOCK] npx sumi-ide-mcp-server',
181-
tools: toolNames,
185+
tools,
182186
};
183187
}),
184188
);

packages/i18n/src/common/en-US.lang.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1621,8 +1621,10 @@ export const localizationBundle = {
16211621
'ai.native.mcp.command.isRequired': 'Command is required',
16221622
'ai.native.mcp.serverHost.isRequired': 'SSE URL is required',
16231623
'ai.native.mcp.manage.connections': 'Manage your MCP server connections',
1624-
'ai.native.mcp.running': 'Running',
1625-
'ai.native.mcp.stopped': 'Stopped',
1624+
'ai.native.mcp.enabled': 'Enabled',
1625+
'ai.native.mcp.disabled': 'Disabled',
1626+
'ai.native.mcp.enable.title': 'Start this service',
1627+
'ai.native.mcp.disable.title': 'Stop this service',
16261628

16271629
// MCP View
16281630
'ai.native.mcp.tool.arguments': 'Arguments',

packages/i18n/src/common/zh-CN.lang.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1384,8 +1384,10 @@ export const localizationBundle = {
13841384
'ai.native.mcp.command.isRequired': '命令不能为空',
13851385
'ai.native.mcp.serverHost.isRequired': 'SSE URL 不能为空',
13861386
'ai.native.mcp.manage.connections': '管理你的 MCP 服务器连接',
1387-
'ai.native.mcp.running': '运行中',
1388-
'ai.native.mcp.stopped': '已停止',
1387+
'ai.native.mcp.enabled': '已启用',
1388+
'ai.native.mcp.disabled': '已禁用',
1389+
'ai.native.mcp.enable.title': '启动该服务',
1390+
'ai.native.mcp.disable.title': '停止该服务',
13891391

13901392
// MCP View
13911393
'ai.native.mcp.tool.arguments': '参数',

0 commit comments

Comments
 (0)