@opensumi/ide-ai-native 3.8.1-next-1740452092.0 → 3.8.1-next-1740463566.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +10 -3
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/chat/chat-manager.service.d.ts +5 -0
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +18 -1
- package/lib/browser/chat/chat-manager.service.js.map +1 -1
- package/lib/browser/chat/chat-model.d.ts +2 -0
- package/lib/browser/chat/chat-model.d.ts.map +1 -1
- package/lib/browser/chat/chat-model.js +8 -2
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +31 -8
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js +3 -0
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +4 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.module.less +178 -0
- package/lib/browser/mcp/config/components/mcp-config.view.d.ts +3 -0
- package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -0
- package/lib/browser/mcp/config/components/mcp-config.view.js +150 -0
- package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -0
- package/lib/browser/mcp/config/components/mcp-server-form.d.ts +16 -0
- package/lib/browser/mcp/config/components/mcp-server-form.d.ts.map +1 -0
- package/lib/browser/mcp/config/components/mcp-server-form.js +84 -0
- package/lib/browser/mcp/config/components/mcp-server-form.js.map +1 -0
- package/lib/browser/mcp/config/components/mcp-server-form.module.less +78 -0
- package/lib/browser/mcp/config/mcp-config.commands.d.ts +10 -0
- package/lib/browser/mcp/config/mcp-config.commands.d.ts.map +1 -0
- package/lib/browser/mcp/config/mcp-config.commands.js +35 -0
- package/lib/browser/mcp/config/mcp-config.commands.js.map +1 -0
- package/lib/browser/mcp/config/mcp-config.contribution.d.ts +16 -0
- package/lib/browser/mcp/config/mcp-config.contribution.d.ts.map +1 -0
- package/lib/browser/mcp/config/mcp-config.contribution.js +62 -0
- package/lib/browser/mcp/config/mcp-config.contribution.js.map +1 -0
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts +6 -0
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
- package/lib/browser/mcp/mcp-server-proxy.service.js +10 -1
- package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
- package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
- package/lib/browser/mcp/mcp-server.feature.registry.js +3 -2
- package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
- package/lib/browser/mcp/tools/handlers/RunCommand.js +2 -0
- package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
- package/lib/browser/preferences/schema.d.ts.map +1 -1
- package/lib/browser/preferences/schema.js +16 -0
- package/lib/browser/preferences/schema.js.map +1 -1
- package/lib/common/index.d.ts +8 -1
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +3 -1
- package/lib/common/index.js.map +1 -1
- package/lib/common/mcp-server-manager.d.ts +17 -1
- package/lib/common/mcp-server-manager.d.ts.map +1 -1
- package/lib/common/mcp-server-manager.js.map +1 -1
- package/lib/common/prompts/context-prompt-provider.d.ts +2 -3
- package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
- package/lib/common/prompts/context-prompt-provider.js +21 -22
- package/lib/common/prompts/context-prompt-provider.js.map +1 -1
- package/lib/common/tool-invocation-registry.d.ts +2 -2
- package/lib/common/tool-invocation-registry.d.ts.map +1 -1
- package/lib/common/tool-invocation-registry.js +1 -1
- package/lib/common/tool-invocation-registry.js.map +1 -1
- package/lib/common/types.d.ts +6 -0
- package/lib/common/types.d.ts.map +1 -1
- package/lib/common/utils.d.ts.map +1 -1
- package/lib/common/utils.js +2 -1
- package/lib/common/utils.js.map +1 -1
- package/lib/node/mcp/sumi-mcp-server.d.ts +17 -3
- package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -1
- package/lib/node/mcp/sumi-mcp-server.js +59 -6
- package/lib/node/mcp/sumi-mcp-server.js.map +1 -1
- package/lib/node/mcp-server-manager-impl.d.ts +4 -3
- package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
- package/lib/node/mcp-server-manager-impl.js +26 -6
- package/lib/node/mcp-server-manager-impl.js.map +1 -1
- package/lib/node/mcp-server.d.ts +5 -16
- package/lib/node/mcp-server.d.ts.map +1 -1
- package/lib/node/mcp-server.js +12 -6
- package/lib/node/mcp-server.js.map +1 -1
- package/lib/node/openai/openai-language-model.d.ts +4 -3
- package/lib/node/openai/openai-language-model.d.ts.map +1 -1
- package/lib/node/openai/openai-language-model.js +3 -2
- package/lib/node/openai/openai-language-model.js.map +1 -1
- package/package.json +27 -27
- package/src/browser/ai-core.contribution.ts +13 -4
- package/src/browser/chat/chat-manager.service.ts +17 -1
- package/src/browser/chat/chat-model.ts +18 -3
- package/src/browser/chat/chat.view.tsx +48 -8
- package/src/browser/context/llm-context.service.ts +4 -0
- package/src/browser/index.ts +4 -0
- package/src/browser/mcp/config/components/mcp-config.module.less +178 -0
- package/src/browser/mcp/config/components/mcp-config.view.tsx +215 -0
- package/src/browser/mcp/config/components/mcp-server-form.module.less +78 -0
- package/src/browser/mcp/config/components/mcp-server-form.tsx +144 -0
- package/src/browser/mcp/config/mcp-config.commands.ts +29 -0
- package/src/browser/mcp/config/mcp-config.contribution.ts +65 -0
- package/src/browser/mcp/mcp-server-proxy.service.ts +14 -2
- package/src/browser/mcp/mcp-server.feature.registry.ts +3 -2
- package/src/browser/mcp/tools/handlers/RunCommand.ts +2 -0
- package/src/browser/preferences/schema.ts +16 -0
- package/src/common/index.ts +7 -1
- package/src/common/mcp-server-manager.ts +17 -1
- package/src/common/prompts/context-prompt-provider.ts +26 -28
- package/src/common/tool-invocation-registry.ts +2 -2
- package/src/common/types.ts +6 -0
- package/src/common/utils.ts +3 -1
- package/src/node/mcp/sumi-mcp-server.ts +67 -9
- package/src/node/mcp-server-manager-impl.ts +30 -9
- package/src/node/mcp-server.ts +11 -14
- package/src/node/openai/openai-language-model.ts +7 -4
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
AI_CHAT_LOGO_AVATAR_ID,
|
|
73
73
|
AI_CHAT_VIEW_ID,
|
|
74
74
|
AI_MENU_BAR_DEBUG_TOOLBAR,
|
|
75
|
+
BUILTIN_MCP_SERVER_NAME,
|
|
75
76
|
ChatProxyServiceToken,
|
|
76
77
|
IChatInternalService,
|
|
77
78
|
IChatManagerService,
|
|
@@ -327,15 +328,23 @@ export class AINativeBrowserContribution
|
|
|
327
328
|
}
|
|
328
329
|
|
|
329
330
|
if (supportsMCP) {
|
|
330
|
-
// 初始化内置 MCP Server
|
|
331
|
-
this.sumiMCPServerBackendProxy.initBuiltinMCPServer();
|
|
332
|
-
|
|
333
331
|
// 从 preferences 获取并初始化外部 MCP Servers
|
|
334
332
|
const mcpServers = this.preferenceService.getValid<MCPServerDescription[]>(
|
|
335
333
|
AINativeSettingSectionsId.MCPServers,
|
|
336
334
|
);
|
|
335
|
+
|
|
336
|
+
// 查找内置 MCP Server 的配置
|
|
337
|
+
const builtinServer = mcpServers?.find((server) => server.name === BUILTIN_MCP_SERVER_NAME);
|
|
338
|
+
|
|
339
|
+
// 总是初始化内置服务器,根据配置决定是否启用
|
|
340
|
+
this.sumiMCPServerBackendProxy.initBuiltinMCPServer(builtinServer?.enabled ?? true);
|
|
341
|
+
|
|
342
|
+
// 初始化其他外部 MCP Servers
|
|
337
343
|
if (mcpServers && mcpServers.length > 0) {
|
|
338
|
-
|
|
344
|
+
const externalServers = mcpServers.filter((server) => server.name !== BUILTIN_MCP_SERVER_NAME);
|
|
345
|
+
if (externalServers.length > 0) {
|
|
346
|
+
this.sumiMCPServerBackendProxy.initExternalMCPServers(externalServers);
|
|
347
|
+
}
|
|
339
348
|
}
|
|
340
349
|
}
|
|
341
350
|
});
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
import { ChatMessageRole, IChatMessage, IHistoryChatMessage } from '@opensumi/ide-core-common/lib/types/ai-native';
|
|
14
14
|
|
|
15
15
|
import { IChatAgentService, IChatFollowup, IChatRequestMessage, IChatResponseErrorDetails } from '../../common';
|
|
16
|
+
import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
|
|
17
|
+
import { ChatAgentPromptProvider } from '../../common/prompts/context-prompt-provider';
|
|
16
18
|
import { MsgHistoryManager } from '../model/msg-history-manager';
|
|
17
19
|
|
|
18
20
|
import { ChatModel, ChatRequestModel, ChatResponseModel, IChatProgressResponseContent } from './chat-model';
|
|
@@ -49,6 +51,12 @@ export class ChatManagerService extends Disposable {
|
|
|
49
51
|
@Autowired(StorageProvider)
|
|
50
52
|
private storageProvider: StorageProvider;
|
|
51
53
|
|
|
54
|
+
@Autowired(ChatAgentPromptProvider)
|
|
55
|
+
protected readonly promptProvider: ChatAgentPromptProvider;
|
|
56
|
+
|
|
57
|
+
@Autowired(LLMContextServiceToken)
|
|
58
|
+
protected readonly contextService: LLMContextService;
|
|
59
|
+
|
|
52
60
|
private _chatStorage: IStorage;
|
|
53
61
|
|
|
54
62
|
protected fromJSON(data: ISessionModel[]) {
|
|
@@ -98,11 +106,19 @@ export class ChatManagerService extends Disposable {
|
|
|
98
106
|
}
|
|
99
107
|
|
|
100
108
|
startSession() {
|
|
101
|
-
const model = new ChatModel(
|
|
109
|
+
const model = new ChatModel({
|
|
110
|
+
provideContext: this.provideContextPrompt.bind(this),
|
|
111
|
+
});
|
|
102
112
|
this.#sessionModels.set(model.sessionId, model);
|
|
103
113
|
return model;
|
|
104
114
|
}
|
|
105
115
|
|
|
116
|
+
private provideContextPrompt(message: string) {
|
|
117
|
+
const context = this.contextService.serialize();
|
|
118
|
+
const fullMessage = this.promptProvider.provideContextPrompt(context, message);
|
|
119
|
+
return fullMessage;
|
|
120
|
+
}
|
|
121
|
+
|
|
106
122
|
getSession(sessionId: string): ChatModel | undefined {
|
|
107
123
|
return this.#sessionModels.get(sessionId);
|
|
108
124
|
}
|
|
@@ -274,13 +274,22 @@ export class ChatRequestModel implements IChatRequestModel {
|
|
|
274
274
|
export class ChatModel extends Disposable implements IChatModel {
|
|
275
275
|
private static requestIdPool = 0;
|
|
276
276
|
|
|
277
|
-
|
|
277
|
+
private provideContextPrompt?: (string) => string;
|
|
278
|
+
|
|
279
|
+
constructor(initParams?: {
|
|
280
|
+
sessionId?: string;
|
|
281
|
+
history?: MsgHistoryManager;
|
|
282
|
+
requests?: ChatRequestModel[];
|
|
283
|
+
provideContext?: (msg: string) => string;
|
|
284
|
+
}) {
|
|
278
285
|
super();
|
|
279
286
|
this.#sessionId = initParams?.sessionId ?? uuid();
|
|
280
287
|
this.history = initParams?.history ?? new MsgHistoryManager();
|
|
281
288
|
if (initParams?.requests) {
|
|
282
289
|
this.#requests = new Map(initParams.requests.map((r) => [r.requestId, r]));
|
|
283
290
|
}
|
|
291
|
+
|
|
292
|
+
this.provideContextPrompt = initParams?.provideContext;
|
|
284
293
|
}
|
|
285
294
|
|
|
286
295
|
#sessionId: string;
|
|
@@ -300,9 +309,15 @@ export class ChatModel extends Disposable implements IChatModel {
|
|
|
300
309
|
readonly history: MsgHistoryManager;
|
|
301
310
|
|
|
302
311
|
addRequest(message: IChatRequestMessage): ChatRequestModel {
|
|
312
|
+
const msg = message;
|
|
313
|
+
// first msg
|
|
314
|
+
if (ChatModel.requestIdPool === 0 && this.provideContextPrompt) {
|
|
315
|
+
msg.prompt = this.provideContextPrompt(msg.prompt);
|
|
316
|
+
}
|
|
317
|
+
|
|
303
318
|
const requestId = `${this.sessionId}_request_${ChatModel.requestIdPool++}`;
|
|
304
|
-
const response = new ChatResponseModel(requestId, this,
|
|
305
|
-
const request = new ChatRequestModel(requestId, this,
|
|
319
|
+
const response = new ChatResponseModel(requestId, this, msg.agentId);
|
|
320
|
+
const request = new ChatRequestModel(requestId, this, msg, response);
|
|
306
321
|
|
|
307
322
|
this.#requests.set(requestId, request);
|
|
308
323
|
return request;
|
|
@@ -3,6 +3,7 @@ import { MessageList } from 'react-chat-elements';
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
AINativeConfigService,
|
|
6
|
+
CommandService,
|
|
6
7
|
getIcon,
|
|
7
8
|
useEventEffect,
|
|
8
9
|
useInjectable,
|
|
@@ -41,8 +42,6 @@ import {
|
|
|
41
42
|
IChatMessageStructure,
|
|
42
43
|
TokenMCPServerProxyService,
|
|
43
44
|
} from '../../common';
|
|
44
|
-
import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
|
|
45
|
-
import { ChatAgentPromptProvider } from '../../common/prompts/context-prompt-provider';
|
|
46
45
|
import { ChatContext } from '../components/ChatContext';
|
|
47
46
|
import { CodeBlockWrapperInput } from '../components/ChatEditor';
|
|
48
47
|
import ChatHistory, { IChatHistoryItem } from '../components/ChatHistory';
|
|
@@ -52,6 +51,7 @@ import { ChatNotify, ChatReply } from '../components/ChatReply';
|
|
|
52
51
|
import { SlashCustomRender } from '../components/SlashCustomRender';
|
|
53
52
|
import { MessageData, createMessageByAI, createMessageByUser } from '../components/utils';
|
|
54
53
|
import { WelcomeMessage } from '../components/WelcomeMsg';
|
|
54
|
+
import { OPEN_MCP_CONFIG_COMMAND } from '../mcp/config/mcp-config.commands';
|
|
55
55
|
import { MCPServerProxyService } from '../mcp/mcp-server-proxy.service';
|
|
56
56
|
import { MCPToolsDialog } from '../mcp/mcp-tools-dialog.view';
|
|
57
57
|
import { ChatViewHeaderRender, TSlashCommandCustomRender } from '../types';
|
|
@@ -63,6 +63,7 @@ import { ChatFeatureRegistry } from './chat.feature.registry';
|
|
|
63
63
|
import { ChatInternalService } from './chat.internal.service';
|
|
64
64
|
import styles from './chat.module.less';
|
|
65
65
|
import { ChatRenderRegistry } from './chat.render.registry';
|
|
66
|
+
|
|
66
67
|
const SCROLL_CLASSNAME = 'chat_scroll';
|
|
67
68
|
|
|
68
69
|
interface TDispatchAction {
|
|
@@ -79,13 +80,15 @@ export const AIChatView = () => {
|
|
|
79
80
|
const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
|
|
80
81
|
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
|
|
81
82
|
const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
|
|
82
|
-
const
|
|
83
|
-
const promptProvider = useInjectable<ChatAgentPromptProvider>(ChatAgentPromptProvider);
|
|
83
|
+
const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
|
|
84
84
|
|
|
85
85
|
const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);
|
|
86
86
|
const msgHistoryManager = aiChatService.sessionModel.history;
|
|
87
87
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
88
88
|
const chatInputRef = React.useRef<{ setInputValue: (v: string) => void } | null>(null);
|
|
89
|
+
const dialogService = useInjectable<IDialogService>(IDialogService);
|
|
90
|
+
const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
|
|
91
|
+
const commandService = useInjectable<CommandService>(CommandService);
|
|
89
92
|
|
|
90
93
|
const [shortcutCommands, setShortcutCommands] = React.useState<ChatSlashCommandItemModel[]>([]);
|
|
91
94
|
|
|
@@ -107,6 +110,8 @@ export const AIChatView = () => {
|
|
|
107
110
|
const [defaultAgentId, setDefaultAgentId] = React.useState<string>('');
|
|
108
111
|
const [command, setCommand] = React.useState('');
|
|
109
112
|
const [theme, setTheme] = React.useState<string | null>(null);
|
|
113
|
+
const [mcpToolsCount, setMcpToolsCount] = React.useState<number>(0);
|
|
114
|
+
const [mcpServersCount, setMcpServersCount] = React.useState<number>(0);
|
|
110
115
|
|
|
111
116
|
React.useEffect(() => {
|
|
112
117
|
const featureSlashCommands = chatFeatureRegistry.getAllShortcutSlashCommand();
|
|
@@ -506,10 +511,7 @@ export const AIChatView = () => {
|
|
|
506
511
|
const { message, agentId, command, reportExtra } = value;
|
|
507
512
|
const { actionType, actionSource } = reportExtra || {};
|
|
508
513
|
|
|
509
|
-
const
|
|
510
|
-
const fullMessage = await promptProvider.provideContextPrompt(context, message);
|
|
511
|
-
|
|
512
|
-
const request = aiChatService.createRequest(fullMessage, agentId!, command);
|
|
514
|
+
const request = aiChatService.createRequest(message, agentId!, command);
|
|
513
515
|
if (!request) {
|
|
514
516
|
return;
|
|
515
517
|
}
|
|
@@ -658,6 +660,32 @@ export const AIChatView = () => {
|
|
|
658
660
|
};
|
|
659
661
|
}, [aiChatService.sessionModel]);
|
|
660
662
|
|
|
663
|
+
useEventEffect(
|
|
664
|
+
mcpServerProxyService.onChangeMCPServers,
|
|
665
|
+
() => {
|
|
666
|
+
mcpServerProxyService.getAllMCPTools().then((tools) => {
|
|
667
|
+
setMcpToolsCount(tools.length);
|
|
668
|
+
});
|
|
669
|
+
mcpServerProxyService.$getServers().then((servers) => {
|
|
670
|
+
setMcpServersCount(servers.length);
|
|
671
|
+
});
|
|
672
|
+
},
|
|
673
|
+
[mcpServerProxyService],
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
const handleShowMCPTools = React.useCallback(async () => {
|
|
677
|
+
const tools = await mcpServerProxyService.getAllMCPTools();
|
|
678
|
+
dialogService.open({
|
|
679
|
+
message: <MCPToolsDialog tools={tools} />,
|
|
680
|
+
type: MessageType.Empty,
|
|
681
|
+
buttons: ['关闭'],
|
|
682
|
+
});
|
|
683
|
+
}, [mcpServerProxyService, dialogService]);
|
|
684
|
+
|
|
685
|
+
const handleShowMCPConfig = React.useCallback(() => {
|
|
686
|
+
commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
|
|
687
|
+
}, [commandService]);
|
|
688
|
+
|
|
661
689
|
return (
|
|
662
690
|
<div id={styles.ai_chat_view}>
|
|
663
691
|
<div className={styles.header_container}>
|
|
@@ -697,6 +725,18 @@ export const AIChatView = () => {
|
|
|
697
725
|
</Popover>
|
|
698
726
|
))}
|
|
699
727
|
</div>
|
|
728
|
+
<div className={styles.header_operate_right}>
|
|
729
|
+
{aiNativeConfigService.capabilities.supportsMCP && (
|
|
730
|
+
<>
|
|
731
|
+
<div className={styles.tag} onClick={handleShowMCPConfig}>
|
|
732
|
+
{`MCP Servers: ${mcpServersCount}`}
|
|
733
|
+
</div>
|
|
734
|
+
<div className={styles.tag} onClick={handleShowMCPTools}>
|
|
735
|
+
{`MCP Tools: ${mcpToolsCount}`}
|
|
736
|
+
</div>
|
|
737
|
+
</>
|
|
738
|
+
)}
|
|
739
|
+
</div>
|
|
700
740
|
</div>
|
|
701
741
|
<ChatInputWrapperRender
|
|
702
742
|
onSend={(value, agentId, command) =>
|
|
@@ -55,6 +55,10 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
55
55
|
const targetList = isManual ? this.attachedFiles : this.recentlyViewFiles;
|
|
56
56
|
const maxLimit = isManual ? this.maxAttachFilesLimit : this.maxViewFilesLimit;
|
|
57
57
|
|
|
58
|
+
if (isManual) {
|
|
59
|
+
this.docModelManager.createModelReference(uri);
|
|
60
|
+
}
|
|
61
|
+
|
|
58
62
|
this.addFileToList(file, targetList, maxLimit);
|
|
59
63
|
this.notifyContextChange();
|
|
60
64
|
}
|
package/src/browser/index.ts
CHANGED
|
@@ -55,6 +55,8 @@ import { RenameCandidatesProviderRegistry } from './contrib/rename/rename.featur
|
|
|
55
55
|
import { TerminalAIContribution } from './contrib/terminal/terminal-ai.contributon';
|
|
56
56
|
import { TerminalFeatureRegistry } from './contrib/terminal/terminal.feature.registry';
|
|
57
57
|
import { LanguageParserService } from './languages/service';
|
|
58
|
+
import { MCPConfigCommandContribution } from './mcp/config/mcp-config.commands';
|
|
59
|
+
import { MCPConfigContribution } from './mcp/config/mcp-config.contribution';
|
|
58
60
|
import { MCPServerProxyService } from './mcp/mcp-server-proxy.service';
|
|
59
61
|
import { MCPServerRegistry } from './mcp/mcp-server.feature.registry';
|
|
60
62
|
import { CreateNewFileWithTextTool } from './mcp/tools/createNewFileWithText';
|
|
@@ -91,6 +93,8 @@ export class AINativeModule extends BrowserModule {
|
|
|
91
93
|
AICodeActionContribution,
|
|
92
94
|
AINativePreferencesContribution,
|
|
93
95
|
IntelligentCompletionsContribution,
|
|
96
|
+
MCPConfigContribution,
|
|
97
|
+
MCPConfigCommandContribution,
|
|
94
98
|
|
|
95
99
|
// MCP Server Contributions START
|
|
96
100
|
ListDirTool,
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
padding: 20px;
|
|
3
|
+
color: var(--foreground);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.header {
|
|
7
|
+
display: flex;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
align-items: flex-start;
|
|
10
|
+
margin-bottom: 24px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.title {
|
|
14
|
+
margin: 0 0 8px 0;
|
|
15
|
+
font-size: 24px;
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.description {
|
|
20
|
+
margin: 0;
|
|
21
|
+
color: var(--descriptionForeground);
|
|
22
|
+
font-size: 14px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.addButton {
|
|
26
|
+
padding: 8px 16px;
|
|
27
|
+
border-radius: 4px;
|
|
28
|
+
background-color: var(--button-primary-background);
|
|
29
|
+
color: var(--button-primary-foreground);
|
|
30
|
+
border: none;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
font-size: 13px;
|
|
33
|
+
|
|
34
|
+
&:hover {
|
|
35
|
+
background-color: var(--button-primary-hover-background);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.serversList {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
gap: 12px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.serverItem {
|
|
46
|
+
padding: 16px;
|
|
47
|
+
border-radius: 8px;
|
|
48
|
+
background-color: var(--editorWidget-background);
|
|
49
|
+
border: 1px solid var(--border-color);
|
|
50
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
51
|
+
transition: all 0.2s ease;
|
|
52
|
+
|
|
53
|
+
&:hover {
|
|
54
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
55
|
+
border-color: var(--focusBorder);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.serverHeader {
|
|
60
|
+
display: flex;
|
|
61
|
+
justify-content: space-between;
|
|
62
|
+
align-items: center;
|
|
63
|
+
margin-bottom: 12px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.serverTitleRow {
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
gap: 8px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.serverName {
|
|
73
|
+
margin: 0;
|
|
74
|
+
font-size: 14px;
|
|
75
|
+
font-weight: 500;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.serverStatus,
|
|
79
|
+
.serverType {
|
|
80
|
+
font-size: 12px;
|
|
81
|
+
padding: 2px 8px;
|
|
82
|
+
border-radius: 12px;
|
|
83
|
+
font-weight: 500;
|
|
84
|
+
display: inline-flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.serverType {
|
|
89
|
+
background-color: var(--editor-background);
|
|
90
|
+
color: var(--descriptionForeground);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.running {
|
|
94
|
+
background-color: var(--notification-info-background);
|
|
95
|
+
color: var(--notification-info-foreground);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.stopped {
|
|
99
|
+
background-color: var(--notification-error-background);
|
|
100
|
+
color: var(--notification-error-foreground);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.serverActions {
|
|
104
|
+
display: flex;
|
|
105
|
+
gap: 4px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.iconButton {
|
|
109
|
+
padding: 4px;
|
|
110
|
+
border: none;
|
|
111
|
+
background: none;
|
|
112
|
+
color: var(--icon-foreground);
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
border-radius: 4px;
|
|
115
|
+
|
|
116
|
+
&:hover {
|
|
117
|
+
background-color: var(--list-hover-background);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.serverDetail {
|
|
122
|
+
margin-top: 12px;
|
|
123
|
+
padding: 12px;
|
|
124
|
+
border-radius: 6px;
|
|
125
|
+
background-color: var(--editor-background);
|
|
126
|
+
font-size: 13px;
|
|
127
|
+
|
|
128
|
+
&:not(:last-child) {
|
|
129
|
+
margin-bottom: 8px;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.detailRow {
|
|
134
|
+
display: flex;
|
|
135
|
+
gap: 8px;
|
|
136
|
+
margin-bottom: 4px;
|
|
137
|
+
align-items: center;
|
|
138
|
+
|
|
139
|
+
&:last-child {
|
|
140
|
+
margin-bottom: 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.detailLabel {
|
|
145
|
+
color: var(--descriptionForeground);
|
|
146
|
+
min-width: 70px;
|
|
147
|
+
height: 24px;
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.detailContent {
|
|
153
|
+
color: var(--foreground);
|
|
154
|
+
word-break: break-all;
|
|
155
|
+
flex: 1;
|
|
156
|
+
background-color: var(--editor-background);
|
|
157
|
+
padding: 4px 8px;
|
|
158
|
+
border-radius: 4px;
|
|
159
|
+
font-family: var(--monaco-monospace-font);
|
|
160
|
+
min-height: 24px;
|
|
161
|
+
display: flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
gap: 8px;
|
|
164
|
+
flex-wrap: wrap;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.toolTag {
|
|
168
|
+
display: inline-flex;
|
|
169
|
+
align-items: center;
|
|
170
|
+
padding: 2px 8px;
|
|
171
|
+
background-color: var(--editorWidget-background);
|
|
172
|
+
color: var(--button-secondary-foreground);
|
|
173
|
+
border-radius: 12px;
|
|
174
|
+
font-size: 12px;
|
|
175
|
+
font-weight: 500;
|
|
176
|
+
transition: all 0.2s ease;
|
|
177
|
+
border: 1px solid var(--border-color);
|
|
178
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { AINativeSettingSectionsId, ILogger, useInjectable } from '@opensumi/ide-core-browser';
|
|
4
|
+
import { PreferenceService } from '@opensumi/ide-core-browser/lib/preferences';
|
|
5
|
+
|
|
6
|
+
import { BUILTIN_MCP_SERVER_NAME } from '../../../../common';
|
|
7
|
+
import { MCPServerDescription } from '../../../../common/mcp-server-manager';
|
|
8
|
+
import { MCPServerProxyService } from '../../mcp-server-proxy.service';
|
|
9
|
+
|
|
10
|
+
import styles from './mcp-config.module.less';
|
|
11
|
+
import { MCPServerForm, MCPServerFormData } from './mcp-server-form';
|
|
12
|
+
|
|
13
|
+
interface MCPServer {
|
|
14
|
+
name: string;
|
|
15
|
+
isStarted: boolean;
|
|
16
|
+
tools?: string[];
|
|
17
|
+
command?: string;
|
|
18
|
+
type?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const MCPConfigView: React.FC = () => {
|
|
22
|
+
const mcpServerProxyService = useInjectable<MCPServerProxyService>(MCPServerProxyService);
|
|
23
|
+
const preferenceService = useInjectable<PreferenceService>(PreferenceService);
|
|
24
|
+
const logger = useInjectable<ILogger>(ILogger);
|
|
25
|
+
const [servers, setServers] = React.useState<MCPServer[]>([]);
|
|
26
|
+
const [formVisible, setFormVisible] = React.useState(false);
|
|
27
|
+
const [editingServer, setEditingServer] = React.useState<MCPServerFormData | undefined>();
|
|
28
|
+
|
|
29
|
+
const loadServers = React.useCallback(async () => {
|
|
30
|
+
const allServers = await mcpServerProxyService.$getServers();
|
|
31
|
+
setServers(allServers);
|
|
32
|
+
}, [mcpServerProxyService]);
|
|
33
|
+
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
loadServers();
|
|
36
|
+
const disposer = mcpServerProxyService.onChangeMCPServers(() => {
|
|
37
|
+
loadServers();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
disposer.dispose();
|
|
42
|
+
};
|
|
43
|
+
}, [mcpServerProxyService, loadServers]);
|
|
44
|
+
|
|
45
|
+
const handleServerControl = async (serverName: string, start: boolean) => {
|
|
46
|
+
try {
|
|
47
|
+
if (start) {
|
|
48
|
+
await mcpServerProxyService.$startServer(serverName);
|
|
49
|
+
} else {
|
|
50
|
+
await mcpServerProxyService.$stopServer(serverName);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Update enabled state in preferences
|
|
54
|
+
const servers = preferenceService.get<MCPServerDescription[]>(AINativeSettingSectionsId.MCPServers, []);
|
|
55
|
+
let updatedServers = servers;
|
|
56
|
+
|
|
57
|
+
// 处理内置服务器的特殊情况
|
|
58
|
+
if (serverName === BUILTIN_MCP_SERVER_NAME) {
|
|
59
|
+
const builtinServerExists = servers.some((server) => server.name === BUILTIN_MCP_SERVER_NAME);
|
|
60
|
+
if (!builtinServerExists && !start) {
|
|
61
|
+
// 如果是停止内置服务器且之前没有配置,添加一个新的配置项
|
|
62
|
+
// 内置服务器不需要 command,因为它是直接集成在 IDE 中的
|
|
63
|
+
updatedServers = [
|
|
64
|
+
...servers,
|
|
65
|
+
{
|
|
66
|
+
name: BUILTIN_MCP_SERVER_NAME,
|
|
67
|
+
enabled: false,
|
|
68
|
+
command: '', // 内置服务器的 command 为空字符串
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
} else {
|
|
72
|
+
// 如果已经存在配置,更新 enabled 状态
|
|
73
|
+
updatedServers = servers.map((server) => {
|
|
74
|
+
if (server.name === BUILTIN_MCP_SERVER_NAME) {
|
|
75
|
+
return { ...server, enabled: start };
|
|
76
|
+
}
|
|
77
|
+
return server;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
// 处理其他外部服务器
|
|
82
|
+
updatedServers = servers.map((server) => {
|
|
83
|
+
if (server.name === serverName) {
|
|
84
|
+
return { ...server, enabled: start };
|
|
85
|
+
}
|
|
86
|
+
return server;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers);
|
|
91
|
+
await loadServers();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
logger.error(`Failed to ${start ? 'start' : 'stop'} server ${serverName}:`, error);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const handleAddServer = () => {
|
|
98
|
+
setEditingServer(undefined);
|
|
99
|
+
setFormVisible(true);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleEditServer = (server: MCPServer) => {
|
|
103
|
+
const servers = preferenceService.get<MCPServerFormData[]>(AINativeSettingSectionsId.MCPServers, []);
|
|
104
|
+
const serverConfig = servers.find((s) => s.name === server.name);
|
|
105
|
+
|
|
106
|
+
if (serverConfig) {
|
|
107
|
+
setEditingServer(serverConfig);
|
|
108
|
+
setFormVisible(true);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const handleDeleteServer = async (serverName: string) => {
|
|
113
|
+
const servers = preferenceService.get<MCPServerFormData[]>(AINativeSettingSectionsId.MCPServers, []);
|
|
114
|
+
const updatedServers = servers.filter((s) => s.name !== serverName);
|
|
115
|
+
await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers);
|
|
116
|
+
await loadServers();
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleSaveServer = async (data: MCPServerFormData) => {
|
|
120
|
+
const servers = preferenceService.get<MCPServerFormData[]>(AINativeSettingSectionsId.MCPServers, []);
|
|
121
|
+
const existingIndex = servers.findIndex((s) => s.name === data.name);
|
|
122
|
+
|
|
123
|
+
if (existingIndex >= 0) {
|
|
124
|
+
servers[existingIndex] = data;
|
|
125
|
+
} else {
|
|
126
|
+
servers.push(data);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await preferenceService.set(AINativeSettingSectionsId.MCPServers, servers);
|
|
130
|
+
setFormVisible(false);
|
|
131
|
+
await loadServers();
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div className={styles.container}>
|
|
136
|
+
<div className={styles.header}>
|
|
137
|
+
<div>
|
|
138
|
+
<h2 className={styles.title}>MCP Servers</h2>
|
|
139
|
+
<p className={styles.description}>Manage your MCP server connections.</p>
|
|
140
|
+
</div>
|
|
141
|
+
<button className={styles.addButton} onClick={handleAddServer}>
|
|
142
|
+
+ Add new MCP server
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
<div className={styles.serversList}>
|
|
146
|
+
{servers.map((server) => (
|
|
147
|
+
<div key={server.name} className={styles.serverItem}>
|
|
148
|
+
<div className={styles.serverHeader}>
|
|
149
|
+
<div className={styles.serverTitleRow}>
|
|
150
|
+
<h3 className={styles.serverName}>{server.name}</h3>
|
|
151
|
+
</div>
|
|
152
|
+
<div className={styles.serverActions}>
|
|
153
|
+
<button className={styles.iconButton} title='Edit' onClick={() => handleEditServer(server)}>
|
|
154
|
+
<i className='codicon codicon-edit' />
|
|
155
|
+
</button>
|
|
156
|
+
<button
|
|
157
|
+
className={styles.iconButton}
|
|
158
|
+
title={server.isStarted ? 'Stop' : 'Start'}
|
|
159
|
+
onClick={() => handleServerControl(server.name, !server.isStarted)}
|
|
160
|
+
>
|
|
161
|
+
<i className={`codicon ${server.isStarted ? 'codicon-debug-stop' : 'codicon-debug-start'}`} />
|
|
162
|
+
</button>
|
|
163
|
+
<button className={styles.iconButton} title='Delete' onClick={() => handleDeleteServer(server.name)}>
|
|
164
|
+
<i className='codicon codicon-trash' />
|
|
165
|
+
</button>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
<div className={styles.serverDetail}>
|
|
169
|
+
<div className={styles.detailRow}>
|
|
170
|
+
<span className={styles.detailLabel}>Status:</span>
|
|
171
|
+
<span className={`${styles.serverStatus} ${server.isStarted ? styles.running : styles.stopped}`}>
|
|
172
|
+
{server.isStarted ? 'Running' : 'Stopped'}
|
|
173
|
+
</span>
|
|
174
|
+
</div>
|
|
175
|
+
{server.type && (
|
|
176
|
+
<div className={styles.detailRow}>
|
|
177
|
+
<span className={styles.detailLabel}>Type:</span>
|
|
178
|
+
<span className={styles.serverType}>{server.type}</span>
|
|
179
|
+
</div>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
{server.tools && server.tools.length > 0 && (
|
|
183
|
+
<div className={styles.serverDetail}>
|
|
184
|
+
<div className={styles.detailRow}>
|
|
185
|
+
<span className={styles.detailLabel}>Tools:</span>
|
|
186
|
+
<span className={styles.detailContent}>
|
|
187
|
+
{server.tools.map((tool, index) => (
|
|
188
|
+
<span key={index} className={styles.toolTag}>
|
|
189
|
+
{tool}
|
|
190
|
+
</span>
|
|
191
|
+
))}
|
|
192
|
+
</span>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
)}
|
|
196
|
+
{server.command && (
|
|
197
|
+
<div className={styles.serverDetail}>
|
|
198
|
+
<div className={styles.detailRow}>
|
|
199
|
+
<span className={styles.detailLabel}>Command:</span>
|
|
200
|
+
<span className={styles.detailContent}>{server.command}</span>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
204
|
+
</div>
|
|
205
|
+
))}
|
|
206
|
+
</div>
|
|
207
|
+
<MCPServerForm
|
|
208
|
+
visible={formVisible}
|
|
209
|
+
initialData={editingServer}
|
|
210
|
+
onSave={handleSaveServer}
|
|
211
|
+
onCancel={() => setFormVisible(false)}
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
};
|