@opensumi/ide-ai-native 3.8.1-next-1741262660.0 → 3.8.1-next-1741317422.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.
Files changed (55) hide show
  1. package/lib/browser/components/ChatInput.js +1 -1
  2. package/lib/browser/components/ChatInput.js.map +1 -1
  3. package/lib/browser/mcp/config/components/mcp-config.module.less +13 -0
  4. package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -1
  5. package/lib/browser/mcp/config/components/mcp-config.view.js +28 -7
  6. package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -1
  7. package/lib/browser/mcp/config/components/mcp-server-form.d.ts +5 -2
  8. package/lib/browser/mcp/config/components/mcp-server-form.d.ts.map +1 -1
  9. package/lib/browser/mcp/config/components/mcp-server-form.js +79 -33
  10. package/lib/browser/mcp/config/components/mcp-server-form.js.map +1 -1
  11. package/lib/browser/mcp/config/components/mcp-server-form.module.less +24 -1
  12. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +1 -1
  13. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
  14. package/lib/browser/mcp/mcp-server-proxy.service.js +1 -1
  15. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
  16. package/lib/browser/types.d.ts.map +1 -1
  17. package/lib/browser/types.js.map +1 -1
  18. package/lib/common/mcp-server-manager.d.ts +21 -3
  19. package/lib/common/mcp-server-manager.d.ts.map +1 -1
  20. package/lib/common/mcp-server-manager.js.map +1 -1
  21. package/lib/common/types.d.ts +6 -1
  22. package/lib/common/types.d.ts.map +1 -1
  23. package/lib/common/types.js +7 -1
  24. package/lib/common/types.js.map +1 -1
  25. package/lib/node/mcp/sumi-mcp-server.d.ts +12 -3
  26. package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -1
  27. package/lib/node/mcp/sumi-mcp-server.js +24 -12
  28. package/lib/node/mcp/sumi-mcp-server.js.map +1 -1
  29. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
  30. package/lib/node/mcp-server-manager-impl.js +22 -8
  31. package/lib/node/mcp-server-manager-impl.js.map +1 -1
  32. package/lib/node/mcp-server.sse.d.ts +192 -0
  33. package/lib/node/mcp-server.sse.d.ts.map +1 -0
  34. package/lib/node/mcp-server.sse.js +93 -0
  35. package/lib/node/mcp-server.sse.js.map +1 -0
  36. package/lib/node/{mcp-server.d.ts → mcp-server.stdio.d.ts} +2 -2
  37. package/lib/node/mcp-server.stdio.d.ts.map +1 -0
  38. package/lib/node/{mcp-server.js → mcp-server.stdio.js} +8 -6
  39. package/lib/node/mcp-server.stdio.js.map +1 -0
  40. package/package.json +24 -23
  41. package/src/browser/components/ChatInput.tsx +1 -1
  42. package/src/browser/mcp/config/components/mcp-config.module.less +13 -0
  43. package/src/browser/mcp/config/components/mcp-config.view.tsx +39 -9
  44. package/src/browser/mcp/config/components/mcp-server-form.module.less +24 -1
  45. package/src/browser/mcp/config/components/mcp-server-form.tsx +149 -63
  46. package/src/browser/mcp/mcp-server-proxy.service.ts +1 -1
  47. package/src/browser/types.ts +0 -1
  48. package/src/common/mcp-server-manager.ts +22 -3
  49. package/src/common/types.ts +7 -1
  50. package/src/node/mcp/sumi-mcp-server.ts +23 -14
  51. package/src/node/mcp-server-manager-impl.ts +20 -9
  52. package/src/node/mcp-server.sse.ts +103 -0
  53. package/src/node/{mcp-server.ts → mcp-server.stdio.ts} +4 -3
  54. package/lib/node/mcp-server.d.ts.map +0 -1
  55. package/lib/node/mcp-server.js.map +0 -1
@@ -1,15 +1,21 @@
1
- import React from 'react';
1
+ import cls from 'classnames';
2
+ import React, { useCallback } from 'react';
2
3
 
4
+ import { Select } from '@opensumi/ide-components';
3
5
  import { Button } from '@opensumi/ide-components/lib/button';
4
6
  import { Modal } from '@opensumi/ide-components/lib/modal';
7
+ import { localize } from '@opensumi/ide-core-common';
5
8
 
6
- import styles from './mcp-server-form.module.less';
9
+ import { MCP_SERVER_TYPE } from '../../../../common/types';
7
10
 
11
+ import styles from './mcp-server-form.module.less';
8
12
  export interface MCPServerFormData {
9
13
  name: string;
10
- command: string;
11
- args: string[];
14
+ command?: string;
15
+ args?: string[];
12
16
  env?: Record<string, string>;
17
+ type: MCP_SERVER_TYPE;
18
+ serverHost?: string;
13
19
  }
14
20
 
15
21
  interface Props {
@@ -25,7 +31,7 @@ export const MCPServerForm: React.FC<Props> = ({ visible, initialData, onSave, o
25
31
  command: '',
26
32
  args: [],
27
33
  env: {},
28
- type: 'stdio', // TODO: 支持 SSE
34
+ type: MCP_SERVER_TYPE.STDIO,
29
35
  ...initialData,
30
36
  }));
31
37
 
@@ -39,13 +45,13 @@ export const MCPServerForm: React.FC<Props> = ({ visible, initialData, onSave, o
39
45
  .join('\n');
40
46
  });
41
47
 
42
- // Update form data when initialData changes
43
48
  React.useEffect(() => {
44
49
  setFormData({
45
50
  name: '',
46
51
  command: '',
47
52
  args: [],
48
53
  env: {},
54
+ type: MCP_SERVER_TYPE.STDIO,
49
55
  ...initialData,
50
56
  });
51
57
  setArgsText(initialData?.args?.join(' ') || '');
@@ -60,28 +66,120 @@ export const MCPServerForm: React.FC<Props> = ({ visible, initialData, onSave, o
60
66
 
61
67
  const handleSubmit = (e: React.FormEvent) => {
62
68
  e.preventDefault();
63
- const args = argsText.split(' ').filter(Boolean);
64
- const env = envText
65
- .split('\n')
66
- .filter(Boolean)
67
- .reduce((acc, line) => {
68
- const [key, value] = line.split('=');
69
- if (key && value) {
70
- acc[key.trim()] = value.trim();
71
- }
72
- return acc;
73
- }, {} as Record<string, string>);
74
-
75
- onSave({
69
+ const form = {
76
70
  ...formData,
77
- args,
78
- env,
79
- });
71
+ };
72
+ if (formData.type === MCP_SERVER_TYPE.SSE) {
73
+ form.serverHost = form.serverHost?.trim();
74
+ } else {
75
+ const args = argsText.split(' ').filter(Boolean);
76
+ const env = envText
77
+ .split('\n')
78
+ .filter(Boolean)
79
+ .reduce((acc, line) => {
80
+ const [key, value] = line.split('=');
81
+ if (key && value) {
82
+ acc[key.trim()] = value.trim();
83
+ }
84
+ return acc;
85
+ }, {} as Record<string, string>);
86
+ form.args = args;
87
+ form.env = env;
88
+ }
89
+
90
+ onSave(form);
80
91
  };
81
92
 
93
+ const handleCommandChange = useCallback(
94
+ (e: React.ChangeEvent<HTMLInputElement>) => {
95
+ setFormData({ ...formData, command: e.target.value });
96
+ setArgsText(e.target.value.split(' ').filter(Boolean).join(' '));
97
+ },
98
+ [formData, argsText],
99
+ );
100
+
101
+ const handleArgsChange = useCallback(
102
+ (e: React.ChangeEvent<HTMLTextAreaElement>) => {
103
+ setArgsText(e.target.value);
104
+ },
105
+ [argsText],
106
+ );
107
+
108
+ const handleEnvChange = useCallback(
109
+ (e: React.ChangeEvent<HTMLTextAreaElement>) => {
110
+ setEnvText(e.target.value);
111
+ },
112
+ [envText],
113
+ );
114
+
115
+ const handleServerHostChange = useCallback(
116
+ (e: React.ChangeEvent<HTMLTextAreaElement>) => {
117
+ setFormData({ ...formData, serverHost: e.target.value });
118
+ },
119
+ [formData],
120
+ );
121
+
122
+ const handleTypeChange = useCallback(
123
+ (value: MCP_SERVER_TYPE) => {
124
+ setFormData({ ...formData, type: value });
125
+ },
126
+ [formData],
127
+ );
128
+
129
+ const renderFormItems = useCallback(() => {
130
+ if (formData?.type === MCP_SERVER_TYPE.STDIO) {
131
+ return (
132
+ <>
133
+ <div className={styles.formItem}>
134
+ <label>{localize('ai.native.mcp.command')}</label>
135
+ <input
136
+ type='text'
137
+ value={formData.command}
138
+ onChange={handleCommandChange}
139
+ placeholder={localize('ai.native.mcp.command.placeHolder')}
140
+ required
141
+ />
142
+ </div>
143
+ <div className={styles.formItem}>
144
+ <label>{localize('ai.native.mcp.args')}</label>
145
+ <textarea
146
+ value={argsText}
147
+ onChange={handleArgsChange}
148
+ placeholder={localize('ai.native.mcp.args.placeHolder')}
149
+ rows={3}
150
+ />
151
+ </div>
152
+ <div className={styles.formItem}>
153
+ <label>{localize('ai.native.mcp.env')}</label>
154
+ <textarea
155
+ value={envText}
156
+ onChange={handleEnvChange}
157
+ placeholder={localize('ai.native.mcp.env.placeHolder')}
158
+ rows={3}
159
+ />
160
+ </div>
161
+ </>
162
+ );
163
+ } else {
164
+ return (
165
+ <>
166
+ <div className={styles.formItem}>
167
+ <label>{localize('ai.native.mcp.serverHost')}</label>
168
+ <textarea
169
+ value={formData.serverHost}
170
+ onChange={handleServerHostChange}
171
+ placeholder={localize('ai.native.mcp.serverHost.placeHolder')}
172
+ rows={3}
173
+ />
174
+ </div>
175
+ </>
176
+ );
177
+ }
178
+ }, [formData, argsText, envText]);
179
+
82
180
  return (
83
181
  <Modal
84
- title={initialData ? 'Edit MCP Server' : 'Add New MCP Server'}
182
+ title={initialData ? localize('ai.native.mcp.editMCPServer.title') : localize('ai.native.mcp.addMCPServer.title')}
85
183
  visible={visible}
86
184
  onCancel={onCancel}
87
185
  centered
@@ -92,50 +190,38 @@ export const MCPServerForm: React.FC<Props> = ({ visible, initialData, onSave, o
92
190
  }}
93
191
  >
94
192
  <form className={styles.form} onSubmit={(e) => e.preventDefault()}>
95
- <div className={styles.formItem}>
96
- <label>Name:</label>
97
- <input
98
- type='text'
99
- value={formData.name}
100
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
101
- placeholder='Enter server name'
102
- required
103
- />
104
- </div>
105
- <div className={styles.formItem}>
106
- <label>Command:</label>
107
- <input
108
- type='text'
109
- value={formData.command}
110
- onChange={(e) => setFormData({ ...formData, command: e.target.value })}
111
- placeholder='Enter command (e.g., npx)'
112
- required
113
- />
114
- </div>
115
- <div className={styles.formItem}>
116
- <label>Arguments:</label>
117
- <textarea
118
- value={argsText}
119
- onChange={(e) => setArgsText(e.target.value)}
120
- placeholder='Enter arguments separated by space'
121
- rows={3}
122
- />
123
- </div>
124
- <div className={styles.formItem}>
125
- <label>Environment Variables:</label>
126
- <textarea
127
- value={envText}
128
- onChange={(e) => setEnvText(e.target.value)}
129
- placeholder='KEY=value (one per line)'
130
- rows={3}
131
- />
193
+ <div className={styles.formRow}>
194
+ <div className={cls(styles.formItem, styles.formItemName)}>
195
+ <label>{localize('ai.native.mcp.name')}</label>
196
+ <input
197
+ type='text'
198
+ value={formData.name}
199
+ onChange={(e) => setFormData({ ...formData, name: e.target.value })}
200
+ placeholder={localize('ai.native.mcp.name.placeHolder')}
201
+ required
202
+ />
203
+ </div>
204
+ <div className={cls(styles.formItem, styles.formItemType)}>
205
+ <label>{localize('ai.native.mcp.type')}</label>
206
+ <Select
207
+ size='large'
208
+ value={formData.type}
209
+ options={[
210
+ { label: localize('ai.native.mcp.stdio'), value: MCP_SERVER_TYPE.STDIO },
211
+ { label: localize('ai.native.mcp.sse'), value: MCP_SERVER_TYPE.SSE },
212
+ ]}
213
+ className={styles.formItemSelect}
214
+ onChange={handleTypeChange}
215
+ />
216
+ </div>
132
217
  </div>
218
+ {renderFormItems()}
133
219
  <div className={styles.formActions}>
134
- <Button onClick={onCancel} type='secondary'>
135
- Cancel
220
+ <Button onClick={onCancel} type='ghost'>
221
+ {localize('ai.native.mcp.buttonCancel')}
136
222
  </Button>
137
223
  <Button onClick={handleSubmit} type='primary'>
138
- Save
224
+ {initialData ? localize('ai.native.mcp.buttonUpdate') : localize('ai.native.mcp.buttonSave')}
139
225
  </Button>
140
226
  </div>
141
227
  </form>
@@ -28,7 +28,7 @@ export class MCPServerProxyService implements IMCPServerProxyService {
28
28
  }
29
29
 
30
30
  // 获取 OpenSumi 内部注册的 MCP tools
31
- async $getMCPTools() {
31
+ async $getBuiltinMCPTools() {
32
32
  const tools = await this.mcpServerRegistry.getMCPTools().map((tool) =>
33
33
  // 不要传递 handler
34
34
  ({
@@ -27,7 +27,6 @@ import { SumiReadableStream } from '@opensumi/ide-utils/lib/stream';
27
27
  import { IMarker } from '@opensumi/monaco-editor-core/esm/vs/platform/markers/common/markers';
28
28
 
29
29
  import { IChatWelcomeMessageContent, ISampleQuestions, ITerminalCommandSuggestionDesc } from '../common';
30
- import { SerializedContext } from '../common/llm-context';
31
30
 
32
31
  import {
33
32
  ICodeEditsContextBean,
@@ -1,5 +1,7 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
2
 
3
+ import { MCP_SERVER_TYPE } from './types';
4
+
3
5
  export interface IMCPServer {
4
6
  isStarted(): boolean;
5
7
  start(): Promise<void>;
@@ -36,7 +38,18 @@ export type MCPTool = Awaited<ReturnType<MCPServerManager['getTools']>>['tools']
36
38
 
37
39
  export type MCPToolParameter = Awaited<ReturnType<MCPServerManager['getTools']>>['tools'][number]['inputSchema'];
38
40
 
39
- export interface MCPServerDescription {
41
+ export interface BaseMCPServerDescription {
42
+ /**
43
+ * The unique name of the MCP server.
44
+ */
45
+ name: string;
46
+ /**
47
+ * Whether to enable the MCP server.
48
+ */
49
+ enabled?: boolean;
50
+ }
51
+
52
+ export interface StdioMCPServerDescription extends BaseMCPServerDescription {
40
53
  /**
41
54
  * The unique name of the MCP server.
42
55
  */
@@ -56,12 +69,18 @@ export interface MCPServerDescription {
56
69
  * Optional environment variables to set when starting the server.
57
70
  */
58
71
  env?: { [key: string]: string };
72
+ }
59
73
 
74
+ export interface SSEMCPServerDescription extends BaseMCPServerDescription {
60
75
  /**
61
- * Whether to enable the MCP server.
76
+ * The host of the MCP server.
62
77
  */
63
- enabled?: boolean;
78
+ serverHost: string;
64
79
  }
65
80
 
81
+ export type MCPServerDescription =
82
+ | ({ type: MCP_SERVER_TYPE.STDIO } & StdioMCPServerDescription)
83
+ | ({ type: MCP_SERVER_TYPE.SSE } & SSEMCPServerDescription);
84
+
66
85
  export const MCPServerManager = Symbol('MCPServerManager');
67
86
  export const MCPServerManagerPath = 'ServicesMCPServerManager';
@@ -31,7 +31,7 @@ export interface IMCPServerProxyService {
31
31
  isError?: boolean;
32
32
  }>;
33
33
  // 获取 browser 层注册的 MCP 工具列表 (Browser tab 维度)
34
- $getMCPTools(): Promise<MCPTool[]>;
34
+ $getBuiltinMCPTools(): Promise<MCPTool[]>;
35
35
  // 通知前端 MCP 服务注册表发生了变化
36
36
  $updateMCPServers(): Promise<void>;
37
37
  // 获取所有 MCP 服务器列表
@@ -49,6 +49,12 @@ export interface MCPTool {
49
49
  providerName: string;
50
50
  }
51
51
 
52
+ export enum MCP_SERVER_TYPE {
53
+ STDIO = 'stdio',
54
+ SSE = 'sse',
55
+ BUILTIN = 'builtin',
56
+ }
57
+
52
58
  export interface CodeBlockData {
53
59
  messageId: string;
54
60
  toolCallId: string;
@@ -10,12 +10,14 @@ import { RPCService } from '@opensumi/ide-connection';
10
10
  import { ILogger } from '@opensumi/ide-core-common';
11
11
  import { INodeLogger } from '@opensumi/ide-core-node';
12
12
 
13
+ import pkg from '../../../package.json';
13
14
  import { BUILTIN_MCP_SERVER_NAME, ISumiMCPServerBackend } from '../../common';
14
15
  import { IMCPServer, MCPServerDescription } from '../../common/mcp-server-manager';
15
16
  import { IToolInvocationRegistryManager, ToolInvocationRegistryManager } from '../../common/tool-invocation-registry';
16
- import { IMCPServerProxyService, MCPTool } from '../../common/types';
17
- import { StdioMCPServerImpl } from '../mcp-server';
17
+ import { IMCPServerProxyService, MCPTool, MCP_SERVER_TYPE } from '../../common/types';
18
18
  import { MCPServerManagerImpl } from '../mcp-server-manager-impl';
19
+ import { SSEMCPServer } from '../mcp-server.sse';
20
+ import { StdioMCPServer } from '../mcp-server.stdio';
19
21
 
20
22
  // 每个 BrowserTab 都对应了一个 SumiMCPServerBackend 实例
21
23
  // SumiMCPServerBackend 需要做的事情:
@@ -49,12 +51,12 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
49
51
  this.mcpServerManager.setClientId(clientId);
50
52
  }
51
53
 
52
- async getMCPTools() {
54
+ async $getMCPTools() {
53
55
  if (!this.client) {
54
56
  throw new Error('SUMI MCP RPC Client not initialized');
55
57
  }
56
- // 获取 MCP 工具
57
- const tools = await this.client.$getMCPTools();
58
+ // 获取 MCP 工具,这里的 client 为 MCPServerProxyService
59
+ const tools = await this.client.$getBuiltinMCPTools();
58
60
  return tools;
59
61
  }
60
62
 
@@ -98,7 +100,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
98
100
  this.server = new Server(
99
101
  {
100
102
  name: 'sumi-ide-mcp-server',
101
- version: '0.2.0',
103
+ version: pkg.version,
102
104
  },
103
105
  {
104
106
  capabilities: {
@@ -108,8 +110,8 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
108
110
  );
109
111
 
110
112
  // 设置工具列表请求处理器
111
- this.server.setRequestHandler(ListToolsRequestSchema, async () => {
112
- const tools = await this.getMCPTools();
113
+ this.server.setRequestHandler(ListToolsRequestSchema, async (event) => {
114
+ const tools = await this.$getMCPTools();
113
115
  return { tools };
114
116
  });
115
117
 
@@ -138,6 +140,7 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
138
140
  if (server.isStarted()) {
139
141
  // 只获取正在运行的 MCP Server 的工具列表
140
142
  const toolsResponse = await server.getTools();
143
+ this.logger.log(`Server ${serverName} tools:`, toolsResponse.tools);
141
144
  toolNames = toolsResponse.tools.map((tool) => tool.name);
142
145
  }
143
146
 
@@ -146,24 +149,30 @@ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> imp
146
149
  return {
147
150
  name: server.getServerName(),
148
151
  isStarted: server.isStarted(),
149
- type: 'builtin rpc',
152
+ type: MCP_SERVER_TYPE.BUILTIN,
150
153
  tools: toolNames,
151
154
  };
152
155
  }
153
156
 
154
157
  // 第三方 Stdio 类型的 MCP Server
155
- if (server instanceof StdioMCPServerImpl) {
158
+ if (server instanceof StdioMCPServer) {
156
159
  return {
157
160
  name: server.getServerName(),
158
161
  isStarted: server.isStarted(),
159
- type: 'stdio',
162
+ type: MCP_SERVER_TYPE.STDIO,
160
163
  command: server.command + ' ' + (server.args?.join(' ') || ''),
161
164
  tools: toolNames,
162
165
  };
166
+ } else if (server instanceof SSEMCPServer) {
167
+ return {
168
+ name: server.getServerName(),
169
+ isStarted: server.isStarted(),
170
+ type: MCP_SERVER_TYPE.SSE,
171
+ serverHost: server.serverHost,
172
+ tools: toolNames,
173
+ };
163
174
  }
164
175
 
165
- // TODO SSE 类型的 MCP Server
166
-
167
176
  return {
168
177
  name: server.getServerName(),
169
178
  isStarted: server.isStarted(),
@@ -240,7 +249,7 @@ export class BuiltinMCPServer implements IMCPServer {
240
249
  if (!this.started) {
241
250
  throw new Error('MCP Server not started');
242
251
  }
243
- const tools = await this.sumiMCPServer.getMCPTools();
252
+ const tools = await this.sumiMCPServer.$getMCPTools();
244
253
  return { tools } as any;
245
254
  }
246
255
 
@@ -4,10 +4,12 @@ import { ILogger } from '@opensumi/ide-core-common';
4
4
 
5
5
  import { IMCPServer, MCPServerDescription, MCPServerManager, MCPTool } from '../common/mcp-server-manager';
6
6
  import { IToolInvocationRegistryManager, ToolRequest } from '../common/tool-invocation-registry';
7
+ import { MCP_SERVER_TYPE } from '../common/types';
7
8
  import { getToolName } from '../common/utils';
8
9
 
9
10
  import { BuiltinMCPServer } from './mcp/sumi-mcp-server';
10
- import { StdioMCPServerImpl } from './mcp-server';
11
+ import { SSEMCPServer } from './mcp-server.sse';
12
+ import { StdioMCPServer } from './mcp-server.stdio';
11
13
  // 这应该是 Browser Tab 维度的,每个 Tab 对应一个 MCPServerManagerImpl
12
14
  export class MCPServerManagerImpl implements MCPServerManager {
13
15
  protected servers: Map<string, IMCPServer> = new Map();
@@ -127,14 +129,23 @@ export class MCPServerManagerImpl implements MCPServerManager {
127
129
  }
128
130
 
129
131
  addOrUpdateServer(description: MCPServerDescription): void {
130
- const { name, command, args, env } = description;
131
- const existingServer = this.servers.get(name);
132
-
133
- if (existingServer) {
134
- existingServer.update(command, args, env);
135
- } else {
136
- const newServer = new StdioMCPServerImpl(name, command, args, env, this.logger);
137
- this.servers.set(name, newServer);
132
+ const existingServer = this.servers.get(description.name);
133
+ if (description.type === MCP_SERVER_TYPE.STDIO) {
134
+ const { name, command, args, env } = description;
135
+ if (existingServer) {
136
+ existingServer.update(command, args, env);
137
+ } else {
138
+ const newServer = new StdioMCPServer(name, command, args, env, this.logger);
139
+ this.servers.set(name, newServer);
140
+ }
141
+ } else if (description.type === MCP_SERVER_TYPE.SSE) {
142
+ const { name, serverHost } = description;
143
+ if (existingServer) {
144
+ existingServer.update(serverHost);
145
+ } else {
146
+ const newServer = new SSEMCPServer(name, serverHost, this.logger);
147
+ this.servers.set(name, newServer);
148
+ }
138
149
  }
139
150
  }
140
151
 
@@ -0,0 +1,103 @@
1
+ // have to import with extension since the exports map is ./* -> ./dist/cjs/*
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
4
+ import { EventSource } from 'eventsource';
5
+
6
+ import { ILogger } from '@opensumi/ide-core-common';
7
+
8
+ import pkg from '../../package.json';
9
+ import { IMCPServer } from '../common/mcp-server-manager';
10
+
11
+ global.EventSource = EventSource as any;
12
+ export class SSEMCPServer implements IMCPServer {
13
+ private name: string;
14
+ public serverHost: string;
15
+ private client: Client;
16
+ private started: boolean = false;
17
+
18
+ constructor(name: string, serverHost: string, private readonly logger?: ILogger) {
19
+ this.name = name;
20
+ this.serverHost = serverHost;
21
+ }
22
+
23
+ isStarted(): boolean {
24
+ return this.started;
25
+ }
26
+
27
+ getServerName(): string {
28
+ return this.name;
29
+ }
30
+
31
+ async start(): Promise<void> {
32
+ if (this.started) {
33
+ return;
34
+ }
35
+ this.logger?.log(`Starting server "${this.name}" with serverHost: ${this.serverHost}`);
36
+
37
+ const transport = new SSEClientTransport(new URL(this.serverHost), {
38
+ eventSourceInit: {},
39
+ });
40
+ transport.onerror = (error) => {
41
+ this.logger?.error('Transport Error:', error);
42
+ };
43
+
44
+ this.client = new Client(
45
+ {
46
+ name: 'sumi-ide-sse-mcp-client',
47
+ version: pkg.version,
48
+ },
49
+ {
50
+ capabilities: {},
51
+ },
52
+ );
53
+ this.client.onerror = (error) => {
54
+ this.logger?.error('Error in MCP client:', error);
55
+ };
56
+
57
+ await this.client.connect(transport);
58
+ this.started = true;
59
+ }
60
+
61
+ async callTool(toolName: string, toolCallId: string, arg_string: string) {
62
+ let args;
63
+ try {
64
+ args = JSON.parse(arg_string);
65
+ } catch (error) {
66
+ this.logger?.error(
67
+ `Failed to parse arguments for calling tool "${toolName}" in MCP server "${this.name}" with serverHost "${this.serverHost}".
68
+ Invalid JSON: ${arg_string}`,
69
+ error,
70
+ );
71
+ }
72
+ const params = {
73
+ name: toolName,
74
+ arguments: args,
75
+ toolCallId,
76
+ };
77
+ return this.client.callTool(params);
78
+ }
79
+
80
+ async getTools() {
81
+ const tools = await this.client.listTools();
82
+ this.logger?.log(`Got tools from MCP server "${this.name}" with serverHost "${this.serverHost}":`, tools);
83
+ return tools;
84
+ }
85
+
86
+ update(serverHost: string): void {
87
+ this.serverHost = serverHost;
88
+ }
89
+
90
+ async stop(): Promise<void> {
91
+ if (!this.started || !this.client) {
92
+ return;
93
+ }
94
+ this.logger?.log(`Stopping MCP server "${this.name}"`);
95
+ try {
96
+ await this.client.close();
97
+ } catch (error) {
98
+ this.logger?.error(`Failed to stop MCP server "${this.name}":`, error);
99
+ }
100
+ this.logger?.log(`MCP server "${this.name}" stopped`);
101
+ this.started = false;
102
+ }
103
+ }
@@ -4,9 +4,10 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
4
4
 
5
5
  import { ILogger } from '@opensumi/ide-core-common';
6
6
 
7
+ import pkg from '../../package.json';
7
8
  import { IMCPServer } from '../common/mcp-server-manager';
8
9
 
9
- export class StdioMCPServerImpl implements IMCPServer {
10
+ export class StdioMCPServer implements IMCPServer {
10
11
  private name: string;
11
12
  public command: string;
12
13
  public args?: string[];
@@ -64,8 +65,8 @@ export class StdioMCPServerImpl implements IMCPServer {
64
65
 
65
66
  this.client = new Client(
66
67
  {
67
- name: 'opensumi-mcp-client',
68
- version: '1.0.0',
68
+ name: 'sumi-ide-stdio-mcp-client',
69
+ version: pkg.version,
69
70
  },
70
71
  {
71
72
  capabilities: {},
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/node/mcp-server.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../src/node/mcp-server.ts"],"names":[],"mappings":"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"}