@opensumi/ide-ai-native 3.8.3-next-1745568063.0 → 3.8.3-next-1745835516.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 +2 -0
- package/lib/browser/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +58 -12
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/components/ChatInput.js +1 -1
- package/lib/browser/components/ChatInput.js.map +1 -1
- package/lib/browser/components/ChatMentionInput.js +1 -1
- package/lib/browser/components/ChatMentionInput.js.map +1 -1
- package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
- package/lib/browser/components/ChatToolRender.js +2 -1
- package/lib/browser/components/ChatToolRender.js.map +1 -1
- package/lib/browser/components/ChatToolResult.d.ts +8 -0
- package/lib/browser/components/ChatToolResult.d.ts.map +1 -0
- package/lib/browser/components/ChatToolResult.js +38 -0
- package/lib/browser/components/ChatToolResult.js.map +1 -0
- package/lib/browser/components/ChatToolResult.module.less +24 -0
- package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
- package/lib/browser/components/mention-input/mention-input.js +9 -7
- package/lib/browser/components/mention-input/mention-input.js.map +1 -1
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +15 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.module.less +4 -1
- package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.view.js +27 -119
- package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -1
- package/lib/browser/mcp/config/components/mcp-server-form.d.ts +1 -1
- package/lib/browser/mcp/config/components/mcp-server-form.d.ts.map +1 -1
- package/lib/browser/mcp/config/components/mcp-server-form.js +8 -8
- package/lib/browser/mcp/config/components/mcp-server-form.js.map +1 -1
- package/lib/browser/mcp/config/mcp-config.commands.d.ts +11 -4
- package/lib/browser/mcp/config/mcp-config.commands.d.ts.map +1 -1
- package/lib/browser/mcp/config/mcp-config.commands.js +23 -6
- package/lib/browser/mcp/config/mcp-config.commands.js.map +1 -1
- package/lib/browser/mcp/config/mcp-config.contribution.d.ts +4 -1
- package/lib/browser/mcp/config/mcp-config.contribution.d.ts.map +1 -1
- package/lib/browser/mcp/config/mcp-config.contribution.js +20 -1
- package/lib/browser/mcp/config/mcp-config.contribution.js.map +1 -1
- package/lib/browser/mcp/config/mcp-config.service.d.ts +30 -0
- package/lib/browser/mcp/config/mcp-config.service.d.ts.map +1 -0
- package/lib/browser/mcp/config/mcp-config.service.js +236 -0
- package/lib/browser/mcp/config/mcp-config.service.js.map +1 -0
- package/lib/browser/mcp/mcp-folder-preference-provider.d.ts +6 -0
- package/lib/browser/mcp/mcp-folder-preference-provider.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-folder-preference-provider.js +29 -0
- package/lib/browser/mcp/mcp-folder-preference-provider.js.map +1 -0
- package/lib/browser/mcp/mcp-preferences-contribution.d.ts +15 -0
- package/lib/browser/mcp/mcp-preferences-contribution.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-preferences-contribution.js +49 -0
- package/lib/browser/mcp/mcp-preferences-contribution.js.map +1 -0
- package/lib/browser/mcp/mcp-preferences.d.ts +6 -0
- package/lib/browser/mcp/mcp-preferences.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-preferences.js +47 -0
- package/lib/browser/mcp/mcp-preferences.js.map +1 -0
- package/lib/browser/preferences/schema.d.ts.map +1 -1
- package/lib/browser/preferences/schema.js +3 -0
- package/lib/browser/preferences/schema.js.map +1 -1
- package/lib/common/mcp-server-manager.d.ts +2 -1
- package/lib/common/mcp-server-manager.d.ts.map +1 -1
- package/lib/common/mcp-server-manager.js +2 -1
- package/lib/common/mcp-server-manager.js.map +1 -1
- package/lib/common/types.d.ts +1 -1
- package/lib/common/types.d.ts.map +1 -1
- package/lib/node/mcp/sumi-mcp-server.d.ts +3 -3
- package/lib/node/mcp/sumi-mcp-server.js +1 -1
- package/lib/node/mcp/sumi-mcp-server.js.map +1 -1
- package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
- package/lib/node/mcp-server-manager-impl.js +9 -4
- package/lib/node/mcp-server-manager-impl.js.map +1 -1
- package/lib/node/mcp-server.sse.d.ts +10 -37
- package/lib/node/mcp-server.sse.d.ts.map +1 -1
- package/lib/node/mcp-server.sse.js +43 -15
- package/lib/node/mcp-server.sse.js.map +1 -1
- package/lib/node/mcp-server.stdio.d.ts +7 -34
- package/lib/node/mcp-server.stdio.d.ts.map +1 -1
- package/lib/node/mcp-server.stdio.js +27 -2
- package/lib/node/mcp-server.stdio.js.map +1 -1
- package/package.json +24 -24
- package/src/browser/ai-core.contribution.ts +71 -16
- package/src/browser/components/ChatInput.tsx +2 -2
- package/src/browser/components/ChatMentionInput.tsx +2 -2
- package/src/browser/components/ChatToolRender.tsx +3 -1
- package/src/browser/components/ChatToolResult.module.less +24 -0
- package/src/browser/components/ChatToolResult.tsx +64 -0
- package/src/browser/components/mention-input/mention-input.tsx +27 -23
- package/src/browser/index.ts +16 -0
- package/src/browser/mcp/config/components/mcp-config.module.less +4 -1
- package/src/browser/mcp/config/components/mcp-config.view.tsx +37 -125
- package/src/browser/mcp/config/components/mcp-server-form.tsx +10 -10
- package/src/browser/mcp/config/mcp-config.commands.ts +22 -6
- package/src/browser/mcp/config/mcp-config.contribution.ts +24 -2
- package/src/browser/mcp/config/mcp-config.service.ts +285 -0
- package/src/browser/mcp/mcp-folder-preference-provider.ts +23 -0
- package/src/browser/mcp/mcp-preferences-contribution.ts +62 -0
- package/src/browser/mcp/mcp-preferences.ts +48 -0
- package/src/browser/preferences/schema.ts +3 -0
- package/src/common/mcp-server-manager.ts +3 -1
- package/src/common/types.ts +1 -1
- package/src/node/mcp/sumi-mcp-server.ts +1 -1
- package/src/node/mcp-server-manager-impl.ts +8 -4
- package/src/node/mcp-server.sse.ts +41 -14
- package/src/node/mcp-server.stdio.ts +26 -2
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { Autowired, Injectable } from '@opensumi/di';
|
|
2
|
+
import { ILogger } from '@opensumi/ide-core-browser';
|
|
3
|
+
import { PreferenceService } from '@opensumi/ide-core-browser/lib/preferences';
|
|
4
|
+
import {
|
|
5
|
+
Deferred,
|
|
6
|
+
Disposable,
|
|
7
|
+
Emitter,
|
|
8
|
+
IStorage,
|
|
9
|
+
PreferenceScope,
|
|
10
|
+
STORAGE_NAMESPACE,
|
|
11
|
+
StorageProvider,
|
|
12
|
+
localize,
|
|
13
|
+
} from '@opensumi/ide-core-common';
|
|
14
|
+
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
15
|
+
import { IMessageService } from '@opensumi/ide-overlay';
|
|
16
|
+
|
|
17
|
+
import { BUILTIN_MCP_SERVER_NAME, ISumiMCPServerBackend, SumiMCPServerProxyServicePath } from '../../../common';
|
|
18
|
+
import {
|
|
19
|
+
MCPServerDescription,
|
|
20
|
+
MCPServersEnabledKey,
|
|
21
|
+
SSEMCPServerDescription,
|
|
22
|
+
StdioMCPServerDescription,
|
|
23
|
+
} from '../../../common/mcp-server-manager';
|
|
24
|
+
import { MCPServer, MCP_SERVER_TYPE } from '../../../common/types';
|
|
25
|
+
import { MCPServerProxyService } from '../mcp-server-proxy.service';
|
|
26
|
+
|
|
27
|
+
import { MCPServerFormData } from './components/mcp-server-form';
|
|
28
|
+
|
|
29
|
+
@Injectable()
|
|
30
|
+
export class MCPConfigService extends Disposable {
|
|
31
|
+
@Autowired(SumiMCPServerProxyServicePath)
|
|
32
|
+
private readonly sumiMCPServerBackendProxy: ISumiMCPServerBackend;
|
|
33
|
+
|
|
34
|
+
@Autowired(MCPServerProxyService)
|
|
35
|
+
private readonly mcpServerProxyService: MCPServerProxyService;
|
|
36
|
+
|
|
37
|
+
@Autowired(PreferenceService)
|
|
38
|
+
private readonly preferenceService: PreferenceService;
|
|
39
|
+
|
|
40
|
+
@Autowired(IMessageService)
|
|
41
|
+
private readonly messageService: IMessageService;
|
|
42
|
+
|
|
43
|
+
@Autowired(StorageProvider)
|
|
44
|
+
private readonly storageProvider: StorageProvider;
|
|
45
|
+
|
|
46
|
+
@Autowired(WorkbenchEditorService)
|
|
47
|
+
private readonly workbenchEditorService: WorkbenchEditorService;
|
|
48
|
+
|
|
49
|
+
@Autowired(ILogger)
|
|
50
|
+
private readonly logger: ILogger;
|
|
51
|
+
|
|
52
|
+
private chatStorage: IStorage;
|
|
53
|
+
private whenReadyDeferred = new Deferred<void>();
|
|
54
|
+
|
|
55
|
+
private readonly mcpServersChangeEventEmitter = new Emitter<boolean>();
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
super();
|
|
59
|
+
|
|
60
|
+
this.init();
|
|
61
|
+
this.disposables.push(
|
|
62
|
+
this.mcpServerProxyService.onChangeMCPServers(() => {
|
|
63
|
+
this.fireMCPServersChange();
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
this.disposables.push(
|
|
67
|
+
this.preferenceService.onSpecificPreferenceChange('mcp', () => {
|
|
68
|
+
this.fireMCPServersChange();
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private async init() {
|
|
74
|
+
this.chatStorage = await this.storageProvider(STORAGE_NAMESPACE.CHAT);
|
|
75
|
+
this.whenReadyDeferred.resolve();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get whenReady() {
|
|
79
|
+
return this.whenReadyDeferred.promise;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get onMCPServersChange() {
|
|
83
|
+
return this.mcpServersChangeEventEmitter.event;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
fireMCPServersChange(isInit: boolean = false) {
|
|
87
|
+
this.mcpServersChangeEventEmitter.fire(isInit);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async getServers(): Promise<MCPServer[]> {
|
|
91
|
+
// Get workspace MCP server configurations
|
|
92
|
+
const { value: mcpConfig } = this.preferenceService.resolve<{ mcpServers: Record<string, any> }>(
|
|
93
|
+
'mcp',
|
|
94
|
+
{ mcpServers: {} },
|
|
95
|
+
undefined,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (!mcpConfig?.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
|
|
99
|
+
const runningServers = await this.mcpServerProxyService.$getServers();
|
|
100
|
+
const builtinServer = runningServers.find((server) => server.name === BUILTIN_MCP_SERVER_NAME);
|
|
101
|
+
return builtinServer ? [builtinServer] : [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const userServers = mcpConfig.mcpServers.map((server) => {
|
|
105
|
+
const name = Object.keys(server)[0];
|
|
106
|
+
const serverConfig = server[name];
|
|
107
|
+
if (serverConfig.url) {
|
|
108
|
+
return {
|
|
109
|
+
name,
|
|
110
|
+
type: MCP_SERVER_TYPE.SSE,
|
|
111
|
+
url: serverConfig.url,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
name,
|
|
116
|
+
type: MCP_SERVER_TYPE.STDIO,
|
|
117
|
+
command: serverConfig.command,
|
|
118
|
+
args: serverConfig.args,
|
|
119
|
+
env: serverConfig.env,
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const runningServers = await this.mcpServerProxyService.$getServers();
|
|
124
|
+
const builtinServer = runningServers.find((server) => server.name === BUILTIN_MCP_SERVER_NAME);
|
|
125
|
+
|
|
126
|
+
// Merge server configs with running status
|
|
127
|
+
const allServers = userServers.map((server) => {
|
|
128
|
+
const runningServer = runningServers.find((s) => s.name === server.name);
|
|
129
|
+
return {
|
|
130
|
+
...server,
|
|
131
|
+
isStarted: runningServer?.isStarted || false,
|
|
132
|
+
tools: runningServer?.tools || [],
|
|
133
|
+
};
|
|
134
|
+
}) as MCPServer[];
|
|
135
|
+
|
|
136
|
+
// Add built-in server at the beginning if it exists
|
|
137
|
+
if (builtinServer) {
|
|
138
|
+
allServers.unshift(builtinServer);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return allServers;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async controlServer(serverName: string, start: boolean): Promise<void> {
|
|
145
|
+
try {
|
|
146
|
+
if (start) {
|
|
147
|
+
await this.mcpServerProxyService.$startServer(serverName);
|
|
148
|
+
} else {
|
|
149
|
+
await this.mcpServerProxyService.$stopServer(serverName);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const enabledMCPServers = this.chatStorage.get<string[]>(MCPServersEnabledKey, [BUILTIN_MCP_SERVER_NAME]);
|
|
153
|
+
const enabledMCPServersSet = new Set(enabledMCPServers);
|
|
154
|
+
|
|
155
|
+
if (start) {
|
|
156
|
+
enabledMCPServersSet.add(serverName);
|
|
157
|
+
} else {
|
|
158
|
+
enabledMCPServersSet.delete(serverName);
|
|
159
|
+
}
|
|
160
|
+
this.chatStorage.set(MCPServersEnabledKey, Array.from(enabledMCPServersSet));
|
|
161
|
+
} catch (error) {
|
|
162
|
+
const msg = error.message || error;
|
|
163
|
+
this.logger.error(`Failed to ${start ? 'start' : 'stop'} server ${serverName}:`, msg);
|
|
164
|
+
this.messageService.error(msg);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async saveServer(data: MCPServerFormData): Promise<void> {
|
|
170
|
+
await this.whenReady;
|
|
171
|
+
const { value: mcpConfig } = this.preferenceService.resolve<{ mcpServers: Record<string, any>[] }>(
|
|
172
|
+
'mcp',
|
|
173
|
+
{ mcpServers: [] },
|
|
174
|
+
undefined,
|
|
175
|
+
);
|
|
176
|
+
const servers = mcpConfig!.mcpServers;
|
|
177
|
+
const existingIndex = servers?.findIndex((s) => s.name === data.name);
|
|
178
|
+
|
|
179
|
+
let serverConfig;
|
|
180
|
+
if (data.type === MCP_SERVER_TYPE.SSE) {
|
|
181
|
+
serverConfig = { [data.name]: { url: (data as SSEMCPServerDescription).url } };
|
|
182
|
+
} else {
|
|
183
|
+
serverConfig = {
|
|
184
|
+
[data.name]: {
|
|
185
|
+
command: (data as StdioMCPServerDescription).command,
|
|
186
|
+
args: (data as StdioMCPServerDescription).args,
|
|
187
|
+
env: (data as StdioMCPServerDescription).env,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (existingIndex !== undefined && existingIndex >= 0) {
|
|
192
|
+
servers[existingIndex] = serverConfig;
|
|
193
|
+
} else {
|
|
194
|
+
servers.push(serverConfig);
|
|
195
|
+
}
|
|
196
|
+
await this.sumiMCPServerBackendProxy.$addOrUpdateServer(data as MCPServerDescription);
|
|
197
|
+
await this.preferenceService.set('mcp', { mcpServers: servers });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async deleteServer(serverName: string): Promise<void> {
|
|
201
|
+
const { value: mcpConfig } = this.preferenceService.resolve<{ mcpServers: Record<string, any>[] }>(
|
|
202
|
+
'mcp',
|
|
203
|
+
{ mcpServers: [] },
|
|
204
|
+
undefined,
|
|
205
|
+
);
|
|
206
|
+
const servers = mcpConfig?.mcpServers;
|
|
207
|
+
const serverIndex = servers?.findIndex((s) => Object.keys(s)[0] === serverName);
|
|
208
|
+
if (serverIndex !== undefined && serverIndex >= 0) {
|
|
209
|
+
servers?.splice(serverIndex, 1);
|
|
210
|
+
}
|
|
211
|
+
await this.sumiMCPServerBackendProxy.$removeServer(serverName);
|
|
212
|
+
await this.preferenceService.set('mcp', { mcpServers: servers });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async syncServer(serverName: string): Promise<void> {
|
|
216
|
+
await this.sumiMCPServerBackendProxy.$syncServer(serverName);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async getServerConfigByName(serverName: string): Promise<MCPServerDescription | undefined> {
|
|
220
|
+
const { value: mcpConfig } = this.preferenceService.resolve<{ mcpServers: Record<string, any>[] }>(
|
|
221
|
+
'mcp',
|
|
222
|
+
{ mcpServers: [] },
|
|
223
|
+
undefined,
|
|
224
|
+
);
|
|
225
|
+
await this.whenReady;
|
|
226
|
+
const enabledMCPServers = this.chatStorage.get<string[]>(MCPServersEnabledKey, [BUILTIN_MCP_SERVER_NAME]);
|
|
227
|
+
const servers = mcpConfig?.mcpServers;
|
|
228
|
+
const server = servers?.find((s) => Object.keys(s)[0] === serverName);
|
|
229
|
+
if (server) {
|
|
230
|
+
if (server[serverName].url) {
|
|
231
|
+
return {
|
|
232
|
+
name: serverName,
|
|
233
|
+
type: MCP_SERVER_TYPE.SSE,
|
|
234
|
+
url: server[serverName].url,
|
|
235
|
+
enabled: enabledMCPServers.includes(serverName),
|
|
236
|
+
};
|
|
237
|
+
} else {
|
|
238
|
+
return {
|
|
239
|
+
name: serverName,
|
|
240
|
+
type: MCP_SERVER_TYPE.STDIO,
|
|
241
|
+
command: server[serverName].command,
|
|
242
|
+
args: server[serverName].args,
|
|
243
|
+
env: server[serverName].env,
|
|
244
|
+
enabled: enabledMCPServers.includes(serverName),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getReadableServerType(type: string): string {
|
|
252
|
+
switch (type) {
|
|
253
|
+
case MCP_SERVER_TYPE.STDIO:
|
|
254
|
+
return localize('ai.native.mcp.type.stdio');
|
|
255
|
+
case MCP_SERVER_TYPE.SSE:
|
|
256
|
+
return localize('ai.native.mcp.type.sse');
|
|
257
|
+
case MCP_SERVER_TYPE.BUILTIN:
|
|
258
|
+
return localize('ai.native.mcp.type.builtin');
|
|
259
|
+
default:
|
|
260
|
+
return type;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async openConfigFile(): Promise<void> {
|
|
265
|
+
let config = this.preferenceService.resolve<{ mcpServers: Record<string, any>[] }>(
|
|
266
|
+
'mcp',
|
|
267
|
+
{ mcpServers: [] },
|
|
268
|
+
undefined,
|
|
269
|
+
);
|
|
270
|
+
if (config.scope === PreferenceScope.Default) {
|
|
271
|
+
await this.preferenceService.set('mcp', { mcpServers: [] }, PreferenceScope.Workspace);
|
|
272
|
+
config = this.preferenceService.resolve<{ mcpServers: Record<string, any>[] }>(
|
|
273
|
+
'mcp',
|
|
274
|
+
{ mcpServers: [] },
|
|
275
|
+
undefined,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const uri = config.configUri;
|
|
279
|
+
if (uri) {
|
|
280
|
+
this.workbenchEditorService.open(uri, {
|
|
281
|
+
preview: false,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Injectable } from '@opensumi/di';
|
|
2
|
+
import { FolderFilePreferenceProvider } from '@opensumi/ide-preferences/lib/browser/folder-file-preference-provider';
|
|
3
|
+
|
|
4
|
+
@Injectable()
|
|
5
|
+
export class MCPFolderPreferenceProvider extends FolderFilePreferenceProvider {
|
|
6
|
+
protected parse(content: string): any {
|
|
7
|
+
const mcp = super.parse(content);
|
|
8
|
+
if (mcp === undefined) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return { mcp: { ...mcp } };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
protected getPath(preferenceName: string): string[] | undefined {
|
|
15
|
+
if (preferenceName === 'mcp') {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
if (preferenceName.startsWith('mcp.')) {
|
|
19
|
+
return [preferenceName.substr('mcp.'.length)];
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Autowired, Injectable } from '@opensumi/di';
|
|
2
|
+
import {
|
|
3
|
+
CodeSchemaId,
|
|
4
|
+
Domain,
|
|
5
|
+
IJSONSchema,
|
|
6
|
+
IJSONSchemaRegistry,
|
|
7
|
+
JsonSchemaContribution,
|
|
8
|
+
MaybePromise,
|
|
9
|
+
PreferenceConfiguration,
|
|
10
|
+
PreferenceContribution,
|
|
11
|
+
PreferenceSchema,
|
|
12
|
+
URI,
|
|
13
|
+
getIcon,
|
|
14
|
+
localize,
|
|
15
|
+
} from '@opensumi/ide-core-browser';
|
|
16
|
+
import {
|
|
17
|
+
BrowserEditorContribution,
|
|
18
|
+
IResource,
|
|
19
|
+
IResourceProvider,
|
|
20
|
+
ResourceService,
|
|
21
|
+
} from '@opensumi/ide-editor/lib/browser';
|
|
22
|
+
|
|
23
|
+
import { MCPPreferencesSchema, MCPSchema, MCPSchemaUri } from './mcp-preferences';
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
export class MCPResourceProvider implements IResourceProvider {
|
|
27
|
+
provideResource(uri: URI): MaybePromise<IResource<any>> {
|
|
28
|
+
return {
|
|
29
|
+
supportsRevive: true,
|
|
30
|
+
name: localize('menu-bar.title.debug'),
|
|
31
|
+
icon: getIcon('debug'),
|
|
32
|
+
uri,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
provideResourceSubname(): string | null {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async shouldCloseResource(): Promise<boolean> {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Domain(PreferenceContribution, PreferenceConfiguration, BrowserEditorContribution, JsonSchemaContribution)
|
|
46
|
+
export class MCPPreferencesContribution
|
|
47
|
+
implements PreferenceContribution, PreferenceConfiguration, BrowserEditorContribution, JsonSchemaContribution
|
|
48
|
+
{
|
|
49
|
+
@Autowired(MCPResourceProvider)
|
|
50
|
+
private readonly prefResourceProvider: MCPResourceProvider;
|
|
51
|
+
|
|
52
|
+
schema: PreferenceSchema = MCPPreferencesSchema;
|
|
53
|
+
name = 'mcp';
|
|
54
|
+
|
|
55
|
+
registerResource(resourceService: ResourceService): void {
|
|
56
|
+
resourceService.registerResourceProvider(this.prefResourceProvider);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
registerSchema(registry: IJSONSchemaRegistry) {
|
|
60
|
+
registry.registerSchema(MCPSchemaUri, MCPSchema, ['mcp.json']);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PreferenceSchema } from '@opensumi/ide-core-browser';
|
|
2
|
+
import { CodeSchemaId, IJSONSchema } from '@opensumi/ide-core-common';
|
|
3
|
+
|
|
4
|
+
export const MCPSchemaUri = `${CodeSchemaId.mcp}/user`;
|
|
5
|
+
|
|
6
|
+
export const MCPPreferencesSchema: PreferenceSchema = {
|
|
7
|
+
type: 'object',
|
|
8
|
+
scope: 'resource',
|
|
9
|
+
properties: {
|
|
10
|
+
mcp: {
|
|
11
|
+
$ref: MCPSchemaUri,
|
|
12
|
+
description: 'MCP configuration for Workspace.',
|
|
13
|
+
defaultValue: { mcpServers: [] },
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const MCPSchema: IJSONSchema = {
|
|
19
|
+
$id: MCPSchemaUri,
|
|
20
|
+
type: 'object',
|
|
21
|
+
title: 'MCP',
|
|
22
|
+
required: [],
|
|
23
|
+
default: { mcpServers: [] },
|
|
24
|
+
properties: {
|
|
25
|
+
mcpServers: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
description: 'List of MCP Servers. Add new servers or edit existing ones by using IntelliSense.',
|
|
28
|
+
properties: {
|
|
29
|
+
command: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'The command to start the MCP server.',
|
|
32
|
+
},
|
|
33
|
+
args: {
|
|
34
|
+
type: 'array',
|
|
35
|
+
description: 'The arguments for the command to start the MCP server.',
|
|
36
|
+
},
|
|
37
|
+
env: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
description: 'The environment variables for the command to start the MCP server.',
|
|
40
|
+
},
|
|
41
|
+
url: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'The SSE URL for the MCP server.',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -105,6 +105,9 @@ export const aiNativePreferenceSchema: PreferenceSchema = {
|
|
|
105
105
|
type: 'number',
|
|
106
106
|
description: localize('preference.ai.native.maxTokens.description'),
|
|
107
107
|
},
|
|
108
|
+
/**
|
|
109
|
+
* @deprecated This configuration will be removed in the future. Please use `mcp.json` instead.
|
|
110
|
+
*/
|
|
108
111
|
[AINativeSettingSectionsId.MCPServers]: {
|
|
109
112
|
type: 'array',
|
|
110
113
|
default: [],
|
|
@@ -77,7 +77,7 @@ export interface SSEMCPServerDescription extends BaseMCPServerDescription {
|
|
|
77
77
|
/**
|
|
78
78
|
* The host of the MCP server.
|
|
79
79
|
*/
|
|
80
|
-
|
|
80
|
+
url: string;
|
|
81
81
|
transportOptions?: SSEClientTransportOptions;
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -99,3 +99,5 @@ export type MCPServerDescription =
|
|
|
99
99
|
|
|
100
100
|
export const MCPServerManager = Symbol('MCPServerManager');
|
|
101
101
|
export const MCPServerManagerPath = 'ServicesMCPServerManager';
|
|
102
|
+
|
|
103
|
+
export const MCPServersEnabledKey = 'mcp_servers_enabled';
|
package/src/common/types.ts
CHANGED
|
@@ -176,7 +176,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
|
|
|
176
176
|
name: server.getServerName(),
|
|
177
177
|
isStarted: server.isStarted(),
|
|
178
178
|
type: MCP_SERVER_TYPE.SSE,
|
|
179
|
-
|
|
179
|
+
url: server.url,
|
|
180
180
|
tools,
|
|
181
181
|
};
|
|
182
182
|
}
|
|
@@ -157,11 +157,11 @@ export class MCPServerManagerImpl implements MCPServerManager {
|
|
|
157
157
|
this.servers.set(name, newServer);
|
|
158
158
|
}
|
|
159
159
|
} else if (description.type === MCP_SERVER_TYPE.SSE) {
|
|
160
|
-
const { name,
|
|
160
|
+
const { name, url, transportOptions } = description;
|
|
161
161
|
if (existingServer) {
|
|
162
|
-
existingServer.update(
|
|
162
|
+
existingServer.update(url);
|
|
163
163
|
} else {
|
|
164
|
-
const newServer = new SSEMCPServer(name,
|
|
164
|
+
const newServer = new SSEMCPServer(name, url, this.logger, transportOptions);
|
|
165
165
|
this.servers.set(name, newServer);
|
|
166
166
|
}
|
|
167
167
|
}
|
|
@@ -188,7 +188,11 @@ export class MCPServerManagerImpl implements MCPServerManager {
|
|
|
188
188
|
// 如果是 enabled 为 false 的 server,则不进行启动
|
|
189
189
|
continue;
|
|
190
190
|
}
|
|
191
|
-
|
|
191
|
+
try {
|
|
192
|
+
await this.startServer(server.name);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
this.logger.error(`Error in addExternalMCPServers for ${server.name}:`, error);
|
|
195
|
+
}
|
|
192
196
|
}
|
|
193
197
|
}
|
|
194
198
|
|
|
@@ -11,19 +11,20 @@ import { SSEClientTransportOptions } from '../common/types';
|
|
|
11
11
|
global.EventSource = EventSource as any;
|
|
12
12
|
export class SSEMCPServer implements IMCPServer {
|
|
13
13
|
private name: string;
|
|
14
|
-
public
|
|
14
|
+
public url: string;
|
|
15
15
|
private transportOptions?: SSEClientTransportOptions;
|
|
16
16
|
private client: Client;
|
|
17
17
|
private started: boolean = false;
|
|
18
|
+
private toolNameMap: Map<string, string> = new Map(); // Map sanitized tool names to original names
|
|
18
19
|
|
|
19
20
|
constructor(
|
|
20
21
|
name: string,
|
|
21
|
-
|
|
22
|
+
url: string,
|
|
22
23
|
private readonly logger?: ILogger,
|
|
23
24
|
private readonly options?: SSEClientTransportOptions,
|
|
24
25
|
) {
|
|
25
26
|
this.name = name;
|
|
26
|
-
this.
|
|
27
|
+
this.url = url;
|
|
27
28
|
this.transportOptions = options;
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -43,11 +44,11 @@ export class SSEMCPServer implements IMCPServer {
|
|
|
43
44
|
if (this.started) {
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
|
-
this.logger?.log(`Starting server "${this.name}" with
|
|
47
|
+
this.logger?.log(`Starting server "${this.name}" with url: ${this.url}`);
|
|
47
48
|
|
|
48
49
|
const SSEClientTransport = (await import('@modelcontextprotocol/sdk/client/sse.js')).SSEClientTransport;
|
|
49
50
|
|
|
50
|
-
const transport = new SSEClientTransport(new URL(this.
|
|
51
|
+
const transport = new SSEClientTransport(new URL(this.url), this.transportOptions);
|
|
51
52
|
|
|
52
53
|
transport.onerror = (error) => {
|
|
53
54
|
this.logger?.error('Transport Error:', error);
|
|
@@ -66,8 +67,13 @@ export class SSEMCPServer implements IMCPServer {
|
|
|
66
67
|
this.logger?.error('Error in MCP client:', error);
|
|
67
68
|
};
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
try {
|
|
71
|
+
await this.client.connect(transport);
|
|
72
|
+
this.started = true;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
this.logger?.error(`Error in startServer for ${this.name}:`, error);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
async callTool(toolName: string, toolCallId: string, arg_string: string) {
|
|
@@ -76,13 +82,15 @@ export class SSEMCPServer implements IMCPServer {
|
|
|
76
82
|
args = JSON.parse(arg_string);
|
|
77
83
|
} catch (error) {
|
|
78
84
|
this.logger?.error(
|
|
79
|
-
`Failed to parse arguments for calling tool "${toolName}" in MCP server "${this.name}" with
|
|
85
|
+
`Failed to parse arguments for calling tool "${toolName}" in MCP server "${this.name}" with url "${this.url}".
|
|
80
86
|
Invalid JSON: ${arg_string}`,
|
|
81
87
|
error,
|
|
82
88
|
);
|
|
83
89
|
}
|
|
90
|
+
// Convert sanitized tool name back to original name if it exists in the map
|
|
91
|
+
const originalToolName = this.toolNameMap.get(toolName) || toolName;
|
|
84
92
|
const params = {
|
|
85
|
-
name:
|
|
93
|
+
name: originalToolName,
|
|
86
94
|
arguments: args,
|
|
87
95
|
toolCallId,
|
|
88
96
|
};
|
|
@@ -90,13 +98,32 @@ export class SSEMCPServer implements IMCPServer {
|
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
async getTools() {
|
|
93
|
-
const
|
|
94
|
-
this.
|
|
95
|
-
|
|
101
|
+
const originalTools = await this.client.listTools();
|
|
102
|
+
this.toolNameMap.clear();
|
|
103
|
+
const toolsArray = originalTools.tools || [];
|
|
104
|
+
const sanitizedToolsArray = toolsArray.map((tool) => {
|
|
105
|
+
const originalName = tool.name;
|
|
106
|
+
// Remove Chinese characters from the tool name
|
|
107
|
+
// Claude 3.5+ Sonnet 不支持中文 Tool Name
|
|
108
|
+
const sanitizedName = originalName.replace(/[\u4e00-\u9fa5]/g, '');
|
|
109
|
+
// If the name changed, store the mapping
|
|
110
|
+
if (sanitizedName !== originalName) {
|
|
111
|
+
this.toolNameMap.set(sanitizedName, originalName);
|
|
112
|
+
return { ...tool, name: sanitizedName };
|
|
113
|
+
}
|
|
114
|
+
return tool;
|
|
115
|
+
});
|
|
116
|
+
const sanitizedTools = {
|
|
117
|
+
...originalTools,
|
|
118
|
+
tools: sanitizedToolsArray,
|
|
119
|
+
};
|
|
120
|
+
this.logger?.log(`Got tools from MCP server "${this.name}" with url "${this.url}":`, sanitizedTools);
|
|
121
|
+
this.logger?.log('Tool name mapping: ', Object.fromEntries(this.toolNameMap));
|
|
122
|
+
return sanitizedTools;
|
|
96
123
|
}
|
|
97
124
|
|
|
98
|
-
update(
|
|
99
|
-
this.
|
|
125
|
+
update(url: string): void {
|
|
126
|
+
this.url = url;
|
|
100
127
|
}
|
|
101
128
|
|
|
102
129
|
async stop(): Promise<void> {
|
|
@@ -14,6 +14,7 @@ export class StdioMCPServer implements IMCPServer {
|
|
|
14
14
|
private client: Client;
|
|
15
15
|
private env?: { [key: string]: string };
|
|
16
16
|
private started: boolean = false;
|
|
17
|
+
private toolNameMap: Map<string, string> = new Map(); // Map sanitized tool names to original names
|
|
17
18
|
|
|
18
19
|
constructor(
|
|
19
20
|
name: string,
|
|
@@ -95,8 +96,10 @@ export class StdioMCPServer implements IMCPServer {
|
|
|
95
96
|
error,
|
|
96
97
|
);
|
|
97
98
|
}
|
|
99
|
+
// Convert sanitized tool name back to original name if it exists in the map
|
|
100
|
+
const originalToolName = this.toolNameMap.get(toolName) || toolName;
|
|
98
101
|
const params = {
|
|
99
|
-
name:
|
|
102
|
+
name: originalToolName,
|
|
100
103
|
arguments: args,
|
|
101
104
|
toolCallId,
|
|
102
105
|
};
|
|
@@ -104,7 +107,28 @@ export class StdioMCPServer implements IMCPServer {
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
async getTools() {
|
|
107
|
-
|
|
110
|
+
const originalTools = await this.client.listTools();
|
|
111
|
+
this.toolNameMap.clear();
|
|
112
|
+
// Process tool names to remove Chinese characters and create mapping
|
|
113
|
+
const toolsArray = originalTools.tools || [];
|
|
114
|
+
const sanitizedToolsArray = toolsArray.map((tool) => {
|
|
115
|
+
const originalName = tool.name;
|
|
116
|
+
// Remove Chinese characters from the tool name
|
|
117
|
+
// Claude 3.5+ Sonnet 不支持中文 Tool Name
|
|
118
|
+
const sanitizedName = originalName.replace(/[\u4e00-\u9fa5]/g, '');
|
|
119
|
+
if (sanitizedName !== originalName) {
|
|
120
|
+
this.toolNameMap.set(sanitizedName, originalName);
|
|
121
|
+
return { ...tool, name: sanitizedName };
|
|
122
|
+
}
|
|
123
|
+
return tool;
|
|
124
|
+
});
|
|
125
|
+
const sanitizedTools = {
|
|
126
|
+
...originalTools,
|
|
127
|
+
tools: sanitizedToolsArray,
|
|
128
|
+
};
|
|
129
|
+
this.logger?.log(`Got tools from MCP server "${this.name}":`, sanitizedTools);
|
|
130
|
+
this.logger?.log('Tool name mapping: ', Object.fromEntries(this.toolNameMap));
|
|
131
|
+
return sanitizedTools;
|
|
108
132
|
}
|
|
109
133
|
|
|
110
134
|
update(command: string, args?: string[], env?: { [key: string]: string }): void {
|