@opensumi/ide-ai-native 3.8.1-next-1740556231.0 → 3.8.1-next-1740625266.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 (123) hide show
  1. package/lib/browser/ai-core.contextkeys.d.ts +1 -1
  2. package/lib/browser/ai-core.contextkeys.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contextkeys.js +1 -1
  4. package/lib/browser/ai-core.contextkeys.js.map +1 -1
  5. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  6. package/lib/browser/ai-core.contribution.js +10 -3
  7. package/lib/browser/ai-core.contribution.js.map +1 -1
  8. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  9. package/lib/browser/chat/chat.view.js +30 -1
  10. package/lib/browser/chat/chat.view.js.map +1 -1
  11. package/lib/browser/contrib/inline-completions/inline-completions.controller.js +1 -1
  12. package/lib/browser/contrib/inline-completions/inline-completions.controller.js.map +1 -1
  13. package/lib/browser/contrib/intelligent-completions/index.d.ts +2 -1
  14. package/lib/browser/contrib/intelligent-completions/index.d.ts.map +1 -1
  15. package/lib/browser/contrib/intelligent-completions/index.js +4 -1
  16. package/lib/browser/contrib/intelligent-completions/index.js.map +1 -1
  17. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js +2 -2
  18. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js.map +1 -1
  19. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  20. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +5 -4
  21. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  22. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.d.ts.map +1 -1
  23. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.js +4 -2
  24. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.js.map +1 -1
  25. package/lib/browser/contrib/intelligent-completions/view/default.d.ts.map +1 -1
  26. package/lib/browser/contrib/intelligent-completions/view/default.js +17 -11
  27. package/lib/browser/contrib/intelligent-completions/view/default.js.map +1 -1
  28. package/lib/browser/index.d.ts.map +1 -1
  29. package/lib/browser/index.js +4 -0
  30. package/lib/browser/index.js.map +1 -1
  31. package/lib/browser/mcp/config/components/mcp-config.module.less +178 -0
  32. package/lib/browser/mcp/config/components/mcp-config.view.d.ts +3 -0
  33. package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -0
  34. package/lib/browser/mcp/config/components/mcp-config.view.js +150 -0
  35. package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -0
  36. package/lib/browser/mcp/config/components/mcp-server-form.d.ts +16 -0
  37. package/lib/browser/mcp/config/components/mcp-server-form.d.ts.map +1 -0
  38. package/lib/browser/mcp/config/components/mcp-server-form.js +84 -0
  39. package/lib/browser/mcp/config/components/mcp-server-form.js.map +1 -0
  40. package/lib/browser/mcp/config/components/mcp-server-form.module.less +78 -0
  41. package/lib/browser/mcp/config/mcp-config.commands.d.ts +10 -0
  42. package/lib/browser/mcp/config/mcp-config.commands.d.ts.map +1 -0
  43. package/lib/browser/mcp/config/mcp-config.commands.js +35 -0
  44. package/lib/browser/mcp/config/mcp-config.commands.js.map +1 -0
  45. package/lib/browser/mcp/config/mcp-config.contribution.d.ts +16 -0
  46. package/lib/browser/mcp/config/mcp-config.contribution.d.ts.map +1 -0
  47. package/lib/browser/mcp/config/mcp-config.contribution.js +62 -0
  48. package/lib/browser/mcp/config/mcp-config.contribution.js.map +1 -0
  49. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +6 -0
  50. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
  51. package/lib/browser/mcp/mcp-server-proxy.service.js +10 -1
  52. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
  53. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
  54. package/lib/browser/mcp/mcp-server.feature.registry.js +3 -2
  55. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
  56. package/lib/browser/preferences/schema.d.ts.map +1 -1
  57. package/lib/browser/preferences/schema.js +16 -0
  58. package/lib/browser/preferences/schema.js.map +1 -1
  59. package/lib/common/index.d.ts +8 -1
  60. package/lib/common/index.d.ts.map +1 -1
  61. package/lib/common/index.js +3 -1
  62. package/lib/common/index.js.map +1 -1
  63. package/lib/common/mcp-server-manager.d.ts +17 -1
  64. package/lib/common/mcp-server-manager.d.ts.map +1 -1
  65. package/lib/common/mcp-server-manager.js.map +1 -1
  66. package/lib/common/tool-invocation-registry.d.ts +2 -2
  67. package/lib/common/tool-invocation-registry.d.ts.map +1 -1
  68. package/lib/common/tool-invocation-registry.js +1 -1
  69. package/lib/common/tool-invocation-registry.js.map +1 -1
  70. package/lib/common/types.d.ts +6 -0
  71. package/lib/common/types.d.ts.map +1 -1
  72. package/lib/common/utils.d.ts.map +1 -1
  73. package/lib/common/utils.js +2 -1
  74. package/lib/common/utils.js.map +1 -1
  75. package/lib/node/base-language-model.d.ts.map +1 -1
  76. package/lib/node/base-language-model.js +5 -4
  77. package/lib/node/base-language-model.js.map +1 -1
  78. package/lib/node/mcp/sumi-mcp-server.d.ts +17 -3
  79. package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -1
  80. package/lib/node/mcp/sumi-mcp-server.js +59 -6
  81. package/lib/node/mcp/sumi-mcp-server.js.map +1 -1
  82. package/lib/node/mcp-server-manager-impl.d.ts +4 -3
  83. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
  84. package/lib/node/mcp-server-manager-impl.js +26 -6
  85. package/lib/node/mcp-server-manager-impl.js.map +1 -1
  86. package/lib/node/mcp-server.d.ts +5 -16
  87. package/lib/node/mcp-server.d.ts.map +1 -1
  88. package/lib/node/mcp-server.js +12 -6
  89. package/lib/node/mcp-server.js.map +1 -1
  90. package/lib/node/openai/openai-language-model.d.ts +4 -3
  91. package/lib/node/openai/openai-language-model.d.ts.map +1 -1
  92. package/lib/node/openai/openai-language-model.js +3 -2
  93. package/lib/node/openai/openai-language-model.js.map +1 -1
  94. package/package.json +27 -27
  95. package/src/browser/ai-core.contextkeys.ts +3 -3
  96. package/src/browser/ai-core.contribution.ts +13 -4
  97. package/src/browser/chat/chat.view.tsx +47 -0
  98. package/src/browser/contrib/inline-completions/inline-completions.controller.ts +1 -1
  99. package/src/browser/contrib/intelligent-completions/index.ts +5 -1
  100. package/src/browser/contrib/intelligent-completions/intelligent-completions.contribution.ts +3 -3
  101. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +6 -5
  102. package/src/browser/contrib/intelligent-completions/view/code-edits-previewer.ts +4 -2
  103. package/src/browser/contrib/intelligent-completions/view/default.ts +27 -19
  104. package/src/browser/index.ts +4 -0
  105. package/src/browser/mcp/config/components/mcp-config.module.less +178 -0
  106. package/src/browser/mcp/config/components/mcp-config.view.tsx +215 -0
  107. package/src/browser/mcp/config/components/mcp-server-form.module.less +78 -0
  108. package/src/browser/mcp/config/components/mcp-server-form.tsx +144 -0
  109. package/src/browser/mcp/config/mcp-config.commands.ts +29 -0
  110. package/src/browser/mcp/config/mcp-config.contribution.ts +65 -0
  111. package/src/browser/mcp/mcp-server-proxy.service.ts +14 -2
  112. package/src/browser/mcp/mcp-server.feature.registry.ts +3 -2
  113. package/src/browser/preferences/schema.ts +16 -0
  114. package/src/common/index.ts +7 -1
  115. package/src/common/mcp-server-manager.ts +17 -1
  116. package/src/common/tool-invocation-registry.ts +2 -2
  117. package/src/common/types.ts +6 -0
  118. package/src/common/utils.ts +3 -1
  119. package/src/node/base-language-model.ts +7 -4
  120. package/src/node/mcp/sumi-mcp-server.ts +67 -9
  121. package/src/node/mcp-server-manager-impl.ts +30 -9
  122. package/src/node/mcp-server.ts +11 -14
  123. package/src/node/openai/openai-language-model.ts +7 -4
@@ -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
+ };
@@ -0,0 +1,78 @@
1
+ .form {
2
+ padding: 16px;
3
+ }
4
+
5
+ .formItem {
6
+ margin-bottom: 16px;
7
+
8
+ label {
9
+ display: block;
10
+ margin-bottom: 8px;
11
+ color: var(--foreground);
12
+ font-size: 13px;
13
+ }
14
+
15
+ input,
16
+ textarea {
17
+ width: 100%;
18
+ padding: 8px;
19
+ border: 1px solid var(--input-border);
20
+ border-radius: 4px;
21
+ background-color: var(--input-background);
22
+ color: var(--input-foreground);
23
+ font-size: 13px;
24
+ font-family: var(--monaco-monospace-font);
25
+
26
+ &:focus {
27
+ border-color: var(--focusBorder);
28
+ outline: none;
29
+ }
30
+
31
+ &::placeholder {
32
+ color: var(--descriptionForeground);
33
+ opacity: 0.6;
34
+ }
35
+ }
36
+
37
+ textarea {
38
+ resize: vertical;
39
+ min-height: 60px;
40
+ }
41
+ }
42
+
43
+ .formActions {
44
+ display: flex;
45
+ justify-content: flex-end;
46
+ gap: 8px;
47
+ margin-top: 24px;
48
+ padding-top: 16px;
49
+ border-top: 1px solid var(--border-color);
50
+ }
51
+
52
+ .cancelButton {
53
+ padding: 6px 12px;
54
+ border: 1px solid var(--button-border);
55
+ border-radius: 4px;
56
+ background-color: var(--button-secondary-background);
57
+ color: var(--button-secondary-foreground);
58
+ cursor: pointer;
59
+ font-size: 13px;
60
+
61
+ &:hover {
62
+ background-color: var(--button-secondary-hover-background);
63
+ }
64
+ }
65
+
66
+ .submitButton {
67
+ padding: 6px 12px;
68
+ border: none;
69
+ border-radius: 4px;
70
+ background-color: var(--button-primary-background);
71
+ color: var(--button-primary-foreground);
72
+ cursor: pointer;
73
+ font-size: 13px;
74
+
75
+ &:hover {
76
+ background-color: var(--button-primary-hover-background);
77
+ }
78
+ }
@@ -0,0 +1,144 @@
1
+ import React from 'react';
2
+
3
+ import { Button } from '@opensumi/ide-components/lib/button';
4
+ import { Modal } from '@opensumi/ide-components/lib/modal';
5
+
6
+ import styles from './mcp-server-form.module.less';
7
+
8
+ export interface MCPServerFormData {
9
+ name: string;
10
+ command: string;
11
+ args: string[];
12
+ env?: Record<string, string>;
13
+ }
14
+
15
+ interface Props {
16
+ visible: boolean;
17
+ initialData?: MCPServerFormData;
18
+ onSave: (data: MCPServerFormData) => void;
19
+ onCancel: () => void;
20
+ }
21
+
22
+ export const MCPServerForm: React.FC<Props> = ({ visible, initialData, onSave, onCancel }) => {
23
+ const [formData, setFormData] = React.useState<MCPServerFormData>(() => ({
24
+ name: '',
25
+ command: '',
26
+ args: [],
27
+ env: {},
28
+ type: 'stdio', // TODO: 支持 SSE
29
+ ...initialData,
30
+ }));
31
+
32
+ const [argsText, setArgsText] = React.useState(() => initialData?.args?.join(' ') || '');
33
+ const [envText, setEnvText] = React.useState(() => {
34
+ if (!initialData?.env) {
35
+ return '';
36
+ }
37
+ return Object.entries(initialData.env)
38
+ .map(([key, value]) => `${key}=${value}`)
39
+ .join('\n');
40
+ });
41
+
42
+ // Update form data when initialData changes
43
+ React.useEffect(() => {
44
+ setFormData({
45
+ name: '',
46
+ command: '',
47
+ args: [],
48
+ env: {},
49
+ ...initialData,
50
+ });
51
+ setArgsText(initialData?.args?.join(' ') || '');
52
+ setEnvText(
53
+ initialData?.env
54
+ ? Object.entries(initialData.env)
55
+ .map(([key, value]) => `${key}=${value}`)
56
+ .join('\n')
57
+ : '',
58
+ );
59
+ }, [initialData]);
60
+
61
+ const handleSubmit = (e: React.FormEvent) => {
62
+ 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({
76
+ ...formData,
77
+ args,
78
+ env,
79
+ });
80
+ };
81
+
82
+ return (
83
+ <Modal
84
+ title={initialData ? 'Edit MCP Server' : 'Add New MCP Server'}
85
+ visible={visible}
86
+ onCancel={onCancel}
87
+ centered
88
+ width={600}
89
+ footer={null}
90
+ style={{
91
+ background: 'var(--editor-background)',
92
+ }}
93
+ >
94
+ <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
+ />
132
+ </div>
133
+ <div className={styles.formActions}>
134
+ <Button onClick={onCancel} type='secondary'>
135
+ Cancel
136
+ </Button>
137
+ <Button onClick={handleSubmit} type='primary'>
138
+ Save
139
+ </Button>
140
+ </div>
141
+ </form>
142
+ </Modal>
143
+ );
144
+ };
@@ -0,0 +1,29 @@
1
+ import { Autowired } from '@opensumi/di';
2
+ import { CommandContribution, CommandRegistry, URI } from '@opensumi/ide-core-browser';
3
+ import { Domain } from '@opensumi/ide-core-common';
4
+ import { WorkbenchEditorService } from '@opensumi/ide-editor';
5
+
6
+ import { MCP_CONFIG_COMPONENTS_SCHEME_ID } from './mcp-config.contribution';
7
+
8
+ export const OPEN_MCP_CONFIG_COMMAND = {
9
+ id: 'opensumi-mcp.openConfig',
10
+ label: 'Open MCP Configuration',
11
+ };
12
+
13
+ @Domain(CommandContribution)
14
+ export class MCPConfigCommandContribution implements CommandContribution {
15
+ @Autowired(WorkbenchEditorService)
16
+ private readonly editorService: WorkbenchEditorService;
17
+
18
+ registerCommands(registry: CommandRegistry) {
19
+ registry.registerCommand(OPEN_MCP_CONFIG_COMMAND, {
20
+ execute: () => {
21
+ const uri = new URI().withScheme(MCP_CONFIG_COMPONENTS_SCHEME_ID);
22
+ this.editorService.open(uri, {
23
+ preview: false,
24
+ focus: true,
25
+ });
26
+ },
27
+ });
28
+ }
29
+ }
@@ -0,0 +1,65 @@
1
+ import { Autowired } from '@opensumi/di';
2
+ import { LabelService } from '@opensumi/ide-core-browser/lib/services';
3
+ import { Domain, Schemes, URI } from '@opensumi/ide-core-common';
4
+ import {
5
+ BrowserEditorContribution,
6
+ EditorComponentRegistry,
7
+ EditorComponentRenderMode,
8
+ IResource,
9
+ ResourceService,
10
+ } from '@opensumi/ide-editor/lib/browser/types';
11
+ import { IconService } from '@opensumi/ide-theme/lib/browser';
12
+ import { IWorkspaceService } from '@opensumi/ide-workspace/lib/common';
13
+
14
+ import { MCPConfigView } from './components/mcp-config.view';
15
+
16
+ const COMPONENTS_ID = 'opensumi-mcp-config-viewer';
17
+ export const MCP_CONFIG_COMPONENTS_SCHEME_ID = 'mcp-config';
18
+
19
+ export type IMCPConfigResource = IResource<{ configType: string }>;
20
+
21
+ @Domain(BrowserEditorContribution)
22
+ export class MCPConfigContribution implements BrowserEditorContribution {
23
+ @Autowired(IWorkspaceService)
24
+ protected readonly workspaceService: IWorkspaceService;
25
+
26
+ @Autowired(IconService)
27
+ protected readonly iconService: IconService;
28
+
29
+ @Autowired()
30
+ labelService: LabelService;
31
+
32
+ registerEditorComponent(registry: EditorComponentRegistry) {
33
+ registry.registerEditorComponent({
34
+ uid: COMPONENTS_ID,
35
+ scheme: MCP_CONFIG_COMPONENTS_SCHEME_ID,
36
+ component: MCPConfigView,
37
+ renderMode: EditorComponentRenderMode.ONE_PER_WORKBENCH,
38
+ });
39
+
40
+ registry.registerEditorComponentResolver(MCP_CONFIG_COMPONENTS_SCHEME_ID, (resource, results) => {
41
+ results.push({
42
+ type: 'component',
43
+ componentId: COMPONENTS_ID,
44
+ });
45
+ });
46
+ }
47
+
48
+ registerResource(service: ResourceService) {
49
+ service.registerResourceProvider({
50
+ scheme: MCP_CONFIG_COMPONENTS_SCHEME_ID,
51
+ provideResource: async (uri: URI): Promise<IMCPConfigResource> => {
52
+ const { configType } = uri.getParsedQuery();
53
+
54
+ return {
55
+ uri,
56
+ name: 'MCP Configuration',
57
+ icon: 'settings',
58
+ metadata: {
59
+ configType,
60
+ },
61
+ };
62
+ },
63
+ });
64
+ }
65
+ }
@@ -4,7 +4,7 @@ import { Autowired, Injectable } from '@opensumi/di';
4
4
  import { ILogger } from '@opensumi/ide-core-browser';
5
5
  import { Emitter, Event } from '@opensumi/ide-core-common';
6
6
 
7
- import { ISumiMCPServerBackend, SumiMCPServerProxyServicePath } from '../../common';
7
+ import { BUILTIN_MCP_SERVER_NAME, ISumiMCPServerBackend, SumiMCPServerProxyServicePath } from '../../common';
8
8
  import { IMCPServerProxyService } from '../../common/types';
9
9
  import { IMCPServerRegistry, TokenMCPServerRegistry } from '../types';
10
10
 
@@ -35,7 +35,7 @@ export class MCPServerProxyService implements IMCPServerProxyService {
35
35
  name: tool.name,
36
36
  description: tool.description,
37
37
  inputSchema: zodToJsonSchema(tool.inputSchema),
38
- providerName: 'sumi-builtin',
38
+ providerName: BUILTIN_MCP_SERVER_NAME,
39
39
  }),
40
40
  );
41
41
 
@@ -52,4 +52,16 @@ export class MCPServerProxyService implements IMCPServerProxyService {
52
52
  async getAllMCPTools() {
53
53
  return this.sumiMCPServerProxyService.getAllMCPTools();
54
54
  }
55
+
56
+ async $getServers() {
57
+ return this.sumiMCPServerProxyService.getServers();
58
+ }
59
+
60
+ async $startServer(serverName: string) {
61
+ await this.sumiMCPServerProxyService.startServer(serverName);
62
+ }
63
+
64
+ async $stopServer(serverName: string) {
65
+ await this.sumiMCPServerProxyService.stopServer(serverName);
66
+ }
55
67
  }
@@ -2,6 +2,7 @@
2
2
  import { Autowired, Injectable } from '@opensumi/di';
3
3
  import { ILogger } from '@opensumi/ide-core-common';
4
4
 
5
+ import { BUILTIN_MCP_SERVER_NAME } from '../../common';
5
6
  import { getToolName } from '../../common/utils';
6
7
  import { IMCPServerRegistry, IMCPServerToolComponentProps, MCPLogger, MCPToolDefinition } from '../types';
7
8
 
@@ -25,7 +26,7 @@ export class MCPServerRegistry implements IMCPServerRegistry {
25
26
  return new LoggerAdapter(this.baseLogger);
26
27
  }
27
28
 
28
- getMCPTool(name: string, serverName = 'sumi-builtin'): MCPToolDefinition | undefined {
29
+ getMCPTool(name: string, serverName = BUILTIN_MCP_SERVER_NAME): MCPToolDefinition | undefined {
29
30
  return this.tools.find((tool) => getToolName(tool.name, serverName) === name);
30
31
  }
31
32
 
@@ -36,7 +37,7 @@ export class MCPServerRegistry implements IMCPServerRegistry {
36
37
  registerToolComponent(
37
38
  name: string,
38
39
  component: React.FC<IMCPServerToolComponentProps>,
39
- serverName = 'sumi-builtin',
40
+ serverName = BUILTIN_MCP_SERVER_NAME,
40
41
  ): void {
41
42
  this.toolComponents[getToolName(name, serverName)] = component;
42
43
  }
@@ -108,11 +108,27 @@ export const aiNativePreferenceSchema: PreferenceSchema = {
108
108
  type: 'string',
109
109
  description: localize('preference.ai.native.mcp.servers.command.description'),
110
110
  },
111
+ type: {
112
+ type: 'string',
113
+ enum: ['stdio', 'sse'],
114
+ enumDescriptions: [
115
+ localize('preference.ai.native.mcp.servers.type.stdio'),
116
+ localize('preference.ai.native.mcp.servers.type.sse'),
117
+ ],
118
+ description: localize('preference.ai.native.mcp.servers.type.description'),
119
+ default: 'stdio',
120
+ },
121
+ enabled: {
122
+ type: 'boolean',
123
+ description: localize('preference.ai.native.mcp.servers.enabled.description'),
124
+ default: true,
125
+ },
111
126
  args: {
112
127
  type: 'array',
113
128
  items: {
114
129
  type: 'string',
115
130
  },
131
+ default: [],
116
132
  description: localize('preference.ai.native.mcp.servers.args.description'),
117
133
  },
118
134
  env: {
@@ -31,6 +31,9 @@ export const AI_CHAT_CONTAINER_ID = 'AI-Chat-Container';
31
31
  export const AI_CHAT_LOGO_AVATAR_ID = 'AI-Chat-Logo-Avatar';
32
32
  export const AI_MENU_BAR_DEBUG_TOOLBAR = 'AI_MENU_BAR_DEBUG_TOOLBAR';
33
33
 
34
+ // 内置 MCP 服务器名称
35
+ export const BUILTIN_MCP_SERVER_NAME = 'sumi-builtin';
36
+
34
37
  /**
35
38
  * @deprecated Use {@link DESIGN_MENUBAR_CONTAINER_VIEW_ID} instead
36
39
  */
@@ -123,9 +126,12 @@ export const ChatProxyServiceToken = Symbol('ChatProxyServiceToken');
123
126
  export const TokenMCPServerProxyService = Symbol('TokenMCPServerProxyService');
124
127
 
125
128
  export interface ISumiMCPServerBackend {
126
- initBuiltinMCPServer(): void;
129
+ initBuiltinMCPServer(enabled: boolean): void;
127
130
  initExternalMCPServers(servers: MCPServerDescription[]): void;
128
131
  getAllMCPTools(): Promise<MCPTool[]>;
132
+ getServers(): Promise<Array<{ name: string; isStarted: boolean }>>;
133
+ startServer(serverName: string): Promise<void>;
134
+ stopServer(serverName: string): Promise<void>;
129
135
  }
130
136
 
131
137
  export const SumiMCPServerProxyServicePath = 'SumiMCPServerProxyServicePath';
@@ -1,5 +1,15 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
2
 
3
+ export interface IMCPServer {
4
+ isStarted(): boolean;
5
+ start(): Promise<void>;
6
+ getServerName(): string;
7
+ callTool(toolName: string, toolCallId: string, arg_string: string): ReturnType<Client['callTool']>;
8
+ getTools(): ReturnType<Client['listTools']>;
9
+ update(command: string, args?: string[], env?: { [key: string]: string }): void;
10
+ stop(): void;
11
+ }
12
+
3
13
  export interface MCPServerManager {
4
14
  callTool(
5
15
  serverName: string,
@@ -11,7 +21,7 @@ export interface MCPServerManager {
11
21
  addOrUpdateServer(description: MCPServerDescription): void;
12
22
  // invoke in node.js only
13
23
  addOrUpdateServerDirectly(server: any): void;
14
- initBuiltinServer(builtinMCPServer: any): void;
24
+ initBuiltinServer(builtinMCPServer: any, enabled: boolean): void;
15
25
  getTools(serverName: string): ReturnType<Client['listTools']>;
16
26
  getServerNames(): Promise<string[]>;
17
27
  startServer(serverName: string): Promise<void>;
@@ -19,6 +29,7 @@ export interface MCPServerManager {
19
29
  getStartedServers(): Promise<string[]>;
20
30
  registerTools(serverName: string): Promise<void>;
21
31
  addExternalMCPServers(servers: MCPServerDescription[]): void;
32
+ getServers(): Map<string, IMCPServer>;
22
33
  }
23
34
 
24
35
  export type MCPTool = Awaited<ReturnType<MCPServerManager['getTools']>>['tools'][number];
@@ -45,6 +56,11 @@ export interface MCPServerDescription {
45
56
  * Optional environment variables to set when starting the server.
46
57
  */
47
58
  env?: { [key: string]: string };
59
+
60
+ /**
61
+ * Whether to enable the MCP server.
62
+ */
63
+ enabled?: boolean;
48
64
  }
49
65
 
50
66
  export const MCPServerManager = Symbol('MCPServerManager');