Skip to content

Commit 74f73aa

Browse files
committed
Supports manual shutdown of MCP servers
1 parent 15872ff commit 74f73aa

File tree

8 files changed

+65
-14
lines changed

8 files changed

+65
-14
lines changed

src/main/IPCs.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ interface ManifestResponse {
4040
* */
4141
export default class IPCs {
4242
static clients: ClientObj[] = []
43-
static currentFeatures: any[] = []
43+
static currentFeatures: FeatureObj[] = []
4444

4545
static initialize(): void {
4646
// Get application version
@@ -56,16 +56,24 @@ export default class IPCs {
5656
return pathToFileURL(normalize(resolve(Constants.ASSETS_PATH.dxt))).toString()
5757
})
5858

59+
ipcMain.handle('msgMcpServersStop', async () => {
60+
IPCs.stopAllServers()
61+
62+
const configs = await loadConfig()
63+
64+
const features = configs.map((params) => {
65+
return registerIpcHandlers(params)
66+
})
67+
68+
this.currentFeatures = features
69+
70+
return true
71+
})
72+
5973
ipcMain.handle(
6074
'msgMcpServersInit',
6175
async (event: IpcMainEvent, metadata: ConfigMcpMetadata) => {
62-
this.clients.forEach((client: ClientObj) => {
63-
if (client.connection?.transport) {
64-
disconnect(client.connection.transport)
65-
}
66-
})
67-
68-
IPCs.removeAllHandlers()
76+
IPCs.stopAllServers()
6977

7078
const progressCallback: McpProgressCallback = (name, message, status) => {
7179
mcpServersCallback({ name, message, status })
@@ -249,6 +257,17 @@ export default class IPCs {
249257
this.currentFeatures = newFeatures
250258
}
251259

260+
static stopAllServers() {
261+
this.clients.forEach((client: ClientObj) => {
262+
if (client.connection?.transport) {
263+
disconnect(client.connection.transport)
264+
delete client.connection.transport
265+
}
266+
})
267+
268+
IPCs.removeAllHandlers()
269+
}
270+
252271
static removeAllHandlers() {
253272
for (const [eventName] of handlerRegistry) {
254273
ipcMain.removeHandler(eventName)

src/preload/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const mainAvailChannels: string[] = [
2525
'msgCommandSelectionNotify',
2626
'msgGetApiToken',
2727
'msgMcpServersInit',
28+
'msgMcpServersStop',
2829
'msgWindowReload'
2930
]
3031

src/renderer/components/pages/McpProcessPage.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,16 @@ const allSuccess = computed(() => {
4343
return Object.values(progressMap.value).every((item) => item.status === 'success')
4444
})
4545
46-
watch(isLoading, (newVal) => {
46+
watch(isLoading, (newVal, oldVal) => {
4747
if (newVal) {
4848
progressMap.value = {}
4949
mcpDialog.value = true
5050
} else {
5151
setTimeout(() => {
52-
if (allSuccess.value) {
52+
console.log(progressMap.value)
53+
if (allSuccess.value || oldVal === 'stop') {
54+
// If all server are started successful, or all servers are forced to stop
55+
// no need to stick the dialog for process visualization
5356
mcpDialog.value = false
5457
}
5558
}, 500)

src/renderer/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
"config": "Server Config",
4545
"file": "Archive File",
4646
"new": "New MCP Server",
47-
"init": "Init MCP Servers",
47+
"init": "Start MCP Servers",
48+
"stop": "Stop MCP Servers",
4849
"read": "Read",
4950
"minutes": "minutes",
5051
"open": "Reveal in File Explorer",

src/renderer/locales/zh.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
"config": "服务端配置",
4545
"file": "文件压缩包",
4646
"new": "新的 MCP 服务端",
47-
"init": "初始化 MCP 服务端",
47+
"init": "启动 MCP 服务端",
48+
"stop": "停止 MCP 服务端",
4849
"read": "阅读",
4950
"minutes": "分钟",
5051
"open": "在文件管理器中打开",

src/renderer/screens/mcp/McpSideDock.vue

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,19 @@ const addDialog = ref(false)
1919
2020
const isDragActive = ref(false)
2121
22+
async function stopAllMcpServers() {
23+
layoutStore.mcpLoading = 'stop'
24+
try {
25+
await McpEvent.stop()
26+
await mcpStore.updateServers()
27+
} finally {
28+
layoutStore.mcpLoading = false
29+
mcpStore.version++
30+
}
31+
}
32+
2233
async function activeAllMcpServers() {
23-
layoutStore.mcpLoading = true
34+
layoutStore.mcpLoading = 'start'
2435
try {
2536
const configs = getServers()
2637
const result = await McpEvent.init(configs)
@@ -76,6 +87,14 @@ const items = [
7687
@click="activeAllMcpServers()"
7788
>
7889
</v-btn>
90+
<v-btn
91+
v-tooltip:top="$t('mcp.stop')"
92+
icon="mdi-power-off"
93+
color="error"
94+
:loading="layoutStore.mcpLoading"
95+
@click="stopAllMcpServers()"
96+
>
97+
</v-btn>
7998
<v-btn
8099
v-tooltip:top="$t('mcp.open')"
81100
color="primary"

src/renderer/store/layout.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const PATH_TO_SCREEN = {
1212
type ScreenKey = keyof typeof PATH_TO_SCREEN
1313
type ScreenValue = (typeof PATH_TO_SCREEN)[ScreenKey]
1414

15+
type LoadingStatus = 'start' | 'stop' | false
16+
1517
export const getScreenFromPath = (path: string): ScreenValue => {
1618
return PATH_TO_SCREEN[path as ScreenKey] ?? 0
1719
}
@@ -20,7 +22,7 @@ export const useLayoutStore = defineStore('layoutStore', {
2022
state: () => ({
2123
sidebar: true,
2224
apiKeyShow: false,
23-
mcpLoading: false,
25+
mcpLoading: false as LoadingStatus,
2426
screen: 0 // The selected screen is a list: 0,1,2,...
2527
})
2628
})

src/renderer/utils/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,17 @@ class Mcp {
184184
return window.mainApi.invoke('msgMcpServersInit', filteredConfigs)
185185
}
186186

187+
static async msgMcpServersStop(): Promise<any> {
188+
return window.mainApi.invoke('msgMcpServersStop')
189+
}
190+
187191
static async msgMcpServersWatch(callback: any): Promise<any> {
188192
return window.mainApi.on('msgMcpServersWatch', callback)
189193
}
190194
}
191195

192196
export const McpEvent = {
193197
init: Mcp.msgMcpServersInit,
198+
stop: Mcp.msgMcpServersStop,
194199
watch: Mcp.msgMcpServersWatch
195200
}

0 commit comments

Comments
 (0)