Skip to content

Commit dd5f536

Browse files
committed
Support selective startup of MCP servers
1 parent a9a498b commit dd5f536

File tree

7 files changed

+85
-40
lines changed

7 files changed

+85
-40
lines changed

src/main/IPCs.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,23 @@ export default class IPCs {
7979
mcpServersCallback({ name, message, status })
8080
}
8181

82+
const configs = await loadConfig()
83+
8284
try {
8385
const newClients = await initClients(metadata, progressCallback)
84-
const features = newClients.map((params) => {
85-
return registerIpcHandlers(params)
86-
})
87-
86+
const activeClientNames = newClients.map((client) => client.name)
87+
const inactiveConfigs = configs.filter(
88+
(config) => !activeClientNames.includes(config.name)
89+
)
90+
91+
const features = [
92+
...newClients.map((params) => registerIpcHandlers(params)),
93+
...inactiveConfigs.map((params) => registerIpcHandlers(params))
94+
]
8895
IPCs.updateMCP(features)
8996
this.clients = newClients
9097
return features
9198
} catch (error) {
92-
const configs = await loadConfig()
93-
9499
const features = configs.map((params) => {
95100
return registerIpcHandlers(params)
96101
})

src/renderer/components/layouts/HeaderLayout.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<script setup lang="tsx">
1+
<script setup lang="ts">
22
import { watchEffect, computed, ref } from 'vue'
33
import { useRoute, useRouter } from 'vue-router'
44
import { useLayoutStore, getScreenFromPath } from '@/renderer/store/layout'

src/renderer/components/pages/McpProcessPage.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<script setup lang="ts">
22
import { McpEvent } from '@/renderer/utils'
3+
import { useMcpStore } from '@/renderer/store/mcp'
34
import { useLayoutStore } from '@/renderer/store/layout'
45
import { ref, watch, computed } from 'vue'
56
67
const layoutStore = useLayoutStore()
8+
const mcpStore = useMcpStore()
79
810
interface ProgressItem {
911
messages: string[]
@@ -40,7 +42,9 @@ McpEvent.watch(handleProgress)
4042
const isLoading = computed(() => layoutStore.mcpLoading)
4143
4244
const allSuccess = computed(() => {
43-
return Object.values(progressMap.value).every((item) => item.status === 'success')
45+
return Object.entries(progressMap.value).every(
46+
([key, item]) => item.status === 'success' || !mcpStore.checkList.includes(key)
47+
)
4448
})
4549
4650
watch(isLoading, (newVal, oldVal) => {

src/renderer/screens/mcp/McpSideDock.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useLayoutStore } from '@/renderer/store/layout'
66
import { useSnackbarStore } from '@/renderer/store/snackbar'
77
import McpDxtPage from '@/renderer/components/pages/McpDxtPage.vue'
88
import McpAddPage from '@/renderer/components/pages/McpAddPage.vue'
9+
import { pick } from 'lodash'
910
1011
const snackbarStore = useSnackbarStore()
1112
@@ -34,8 +35,11 @@ async function stopAllMcpServers() {
3435
async function activeAllMcpServers() {
3536
layoutStore.mcpLoading = 'start'
3637
try {
37-
const configs = getServers()
38-
const result = await McpEvent.init(configs)
38+
const configs = getServers() ?? {}
39+
40+
const filteredConfigs = pick(configs, mcpStore.checkList)
41+
42+
const result = await McpEvent.init(filteredConfigs)
3943
console.log(result)
4044
await mcpStore.updateServers()
4145
if (result.status == 'error') {

src/renderer/screens/mcp/McpSideDrawer.vue

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
11
<script setup lang="ts">
22
import { useMcpStore, getAllowedPrimitive, getServers } from '@/renderer/store/mcp'
33
const mcpStore = useMcpStore()
4+
import { onMounted } from 'vue'
5+
6+
onMounted(() => {
7+
mcpStore.watchServerUpdate()
8+
})
49
</script>
510

611
<template>
7-
<v-list :key="mcpStore.version" v-model:selected="mcpStore.selected" nav mandatory>
8-
<v-list-item
9-
v-for="(item, key) in getServers()"
10-
:key="key"
11-
two-line
12-
:value="key"
13-
link
14-
:ripple="false"
15-
>
12+
<v-list
13+
:key="mcpStore.version"
14+
v-model:selected="mcpStore.selected"
15+
nav
16+
mandatory
17+
density="compact"
18+
>
19+
<v-list-item v-for="(item, key) in getServers()" :key="key" :value="key" :ripple="false">
1620
<template #title>
1721
<div class="d-flex align-center">
18-
<v-list-item-title class="pt-1">
22+
<v-checkbox
23+
v-model="mcpStore.checkList"
24+
class="pr-1"
25+
density="compact"
26+
glow
27+
hide-details
28+
color="secondary"
29+
:value="key"
30+
></v-checkbox>
31+
<v-list-item-title>
1932
{{ key }}
2033
</v-list-item-title>
2134
<v-spacer></v-spacer>
2235
<v-btn
23-
class="mt-1"
2436
size="small"
2537
color="primary"
2638
icon="mdi-cog"
@@ -29,22 +41,25 @@ const mcpStore = useMcpStore()
2941
></v-btn>
3042
</div>
3143
</template>
32-
<v-chip-group
33-
v-model="mcpStore.selectedChips[key]"
34-
:direction="mcpStore.selected?.[0] === key ? 'vertical' : undefined"
35-
mandatory
36-
>
37-
<v-chip
38-
v-for="name in getAllowedPrimitive(item)"
39-
:key="`${key}-${name}`"
40-
class="mr-1 my-1"
41-
label
42-
color="secondary"
43-
size="small"
44+
<div v-if="getAllowedPrimitive(item).length > 0">
45+
<v-divider></v-divider>
46+
<v-chip-group
47+
v-model="mcpStore.selectedChips[key]"
48+
:direction="mcpStore.selected?.[0] === key ? 'vertical' : undefined"
49+
mandatory
4450
>
45-
{{ name }}
46-
</v-chip>
47-
</v-chip-group>
51+
<v-chip
52+
v-for="name in getAllowedPrimitive(item)"
53+
:key="`${key}-${name}`"
54+
class="ma-1"
55+
label
56+
color="secondary"
57+
size="small"
58+
>
59+
{{ name }}
60+
</v-chip>
61+
</v-chip-group>
62+
</div>
4863
</v-list-item>
4964
</v-list>
5065
</template>

src/renderer/store/mcp.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { defineStore } from 'pinia'
2+
import { watch } from 'vue'
23
import type {
34
ChatCompletionRequestContent,
45
ChatCompletionPromptMessage
@@ -18,6 +19,7 @@ type McpMethodType =
1819
| { type: 'call'; fn: () => any }
1920
| { type: 'templates/list'; fn: () => any }
2021
| string
22+
export type McpServerApi = MCPAPI | undefined
2123

2224
export function getAllowedPrimitive(item: McpObject): AllowedPrimitive[] {
2325
if (!item) return []
@@ -27,11 +29,11 @@ export function getAllowedPrimitive(item: McpObject): AllowedPrimitive[] {
2729
) as AllowedPrimitive[]
2830
}
2931

30-
export function getRawServers(): MCPAPI | undefined {
32+
export function getRawServers(): McpServerApi {
3133
return window.mcpServers?.get()
3234
}
3335

34-
export function getServers(): MCPAPI | undefined {
36+
export function getServers(): McpServerApi {
3537
const mcpServers = getRawServers()
3638
const stdioServers = useStdioStore().configValues
3739

@@ -65,12 +67,17 @@ export interface McpCoreType {
6567
method: McpMethodType
6668
}
6769

70+
function getObjectKeys(o: unknown) {
71+
return o && typeof o === 'object' && !Array.isArray(o) ? Object.keys(o) : []
72+
}
73+
6874
export const useMcpStore = defineStore('mcpStore', {
6975
// TODO: fix any to type
7076
state: (): any => ({
7177
version: 1,
7278
serverTools: [],
7379
loading: true,
80+
checkList: getObjectKeys(getServers()) as string[],
7481
selected: undefined as string[] | undefined,
7582
selectedChips: {} // { key : 0 | 1 | 2}
7683
}),
@@ -108,6 +115,16 @@ export const useMcpStore = defineStore('mcpStore', {
108115
},
109116

110117
actions: {
118+
watchServerUpdate() {
119+
watch(getServers, (newVal: McpServerApi, oldVal: McpServerApi) => {
120+
const newKeys = getObjectKeys(newVal)
121+
const oldKeys = getObjectKeys(oldVal)
122+
const retainedKeys = this.checkList.filter((key: string) => newKeys.includes(key))
123+
const addedKeys = newKeys.filter((key) => !oldKeys.includes(key))
124+
this.checkList = [...retainedKeys, ...addedKeys]
125+
})
126+
},
127+
111128
getAllByServer: function (serverName: string): McpCoreType[] {
112129
const mcpServers = getServers()
113130
if (!mcpServers) {

src/renderer/utils/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { MCPAPI } from '@/preload/mcp'
1+
import type { McpServerApi } from '@/renderer/store/mcp'
22
import { useDxtStore } from '@/renderer/store/dxt'
33

44
function isValidValue(value: any): boolean {
@@ -139,7 +139,7 @@ export const CommandEvent = {
139139
}
140140

141141
class Mcp {
142-
static async msgMcpServersInit(configs: MCPAPI | undefined): Promise<any> {
142+
static async msgMcpServersInit(configs: McpServerApi): Promise<any> {
143143
if (!configs) return
144144
const dxtStore = useDxtStore()
145145
const filteredConfigs = Object.fromEntries(

0 commit comments

Comments
 (0)