Skip to content

Commit e7fa47d

Browse files
committed
Enables seamless MCP server initialization without reload
1 parent 4b22c99 commit e7fa47d

File tree

11 files changed

+160
-171
lines changed

11 files changed

+160
-171
lines changed

src/renderer/components/layouts/HeaderLayout.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,19 @@ watchEffect(() => {
4747
variant="text"
4848
base-color="white"
4949
>
50-
<v-btn data-testid="btn-menu-mcp" @click="handleRoute('/')">
50+
<v-btn :key="0" data-testid="btn-menu-mcp" @click="handleRoute('/')">
5151
<v-icon>mdi-view-dashboard</v-icon>
5252
</v-btn>
5353

54-
<v-btn data-testid="btn-menu-chat" @click="handleRoute('/chat')">
54+
<v-btn :key="1" data-testid="btn-menu-chat" @click="handleRoute('/chat')">
5555
<v-icon>mdi-comment-text-outline</v-icon>
5656
</v-btn>
5757

58-
<v-btn data-testid="btn-menu-agent" @click="handleRoute('/agent')">
58+
<v-btn :key="2" data-testid="btn-menu-agent" @click="handleRoute('/agent')">
5959
<v-icon>mdi-account-multiple</v-icon>
6060
</v-btn>
6161

62-
<v-btn data-testid="btn-menu-setting" @click="handleRoute('/setting')">
62+
<v-btn :key="3" data-testid="btn-menu-setting" @click="handleRoute('/setting')">
6363
<v-icon>mdi-cog-transfer-outline</v-icon>
6464
</v-btn>
6565
</v-btn-toggle>
@@ -76,7 +76,7 @@ watchEffect(() => {
7676
</v-tooltip>
7777
</v-btn> -->
7878
<!-- @click="mcpStore.listTools().then((tools) => console.log(tools))" -->
79-
<v-btn icon="mdi-clipboard-text" size="small" @click="console.log(mcpStore.getServers)">
79+
<v-btn icon="mdi-clipboard-text" size="small" @click="console.log(mcpStore.getServers())">
8080
</v-btn>
8181
</template>
8282
</v-app-bar>

src/renderer/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<!-- img-src 'self' data: -->
77
<meta
88
http-equiv="Content-Security-Policy"
9-
content="script-src 'self'; style-src 'self' 'unsafe-inline'; img-src *;"
9+
content="script-src 'self'; style-src 'self' 'unsafe-inline';"
1010
/>
1111
</head>
1212
<body>

src/renderer/locales/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"error": "Unknown Error"
99
},
1010
"mcp": {
11-
"init": "Init MCP Servers"
11+
"init": "Init MCP Servers",
12+
"updated": "MCP Severs Updated"
1213
},
1314
"menu": {
1415
"change-theme": "Change Theme",
@@ -35,6 +36,7 @@
3536
"config": "Agent Config",
3637
"prompt": "System Prompt",
3738
"tools": "List of tools",
39+
"no-tools": "No MCP servers with tools was connected.",
3840
"all": "All tools",
3941
"selected": "Selected Tools",
4042
"cancel": "Cancel",

src/renderer/locales/zh.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"error": "未知错误"
99
},
1010
"mcp": {
11-
"init": "初始化 MCP 服务端"
11+
"init": "初始化 MCP 服务端",
12+
"updated": "MCP 服务端已更新"
1213
},
1314
"menu": {
1415
"change-theme": "改变主题",
@@ -31,6 +32,7 @@
3132
"config": "智能体配置",
3233
"prompt": "提示指令",
3334
"tools": "工具列表",
35+
"no-tools": "尚未连接包含工具的 MCP 服务端",
3436
"all": "全部工具",
3537
"selected": "已选择工具",
3638
"cancel": "取消",

src/renderer/screens/agent/AgentCentralStage.vue

Lines changed: 77 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,12 @@ const mcpStore = useMcpStore()
88
const agentStore = useAgentStore()
99
const { t } = useI18n()
1010
11-
const allTools = ref([])
12-
// const selectedTree = ref([])
13-
1411
const selectedTree = computed({
1512
get() {
1613
return agentStore.getRevised?.selectedNode
1714
},
1815
set(value) {
19-
console.log(value)
16+
console.log('Selected tools', value)
2017
if (agentStore.getRevised) {
2118
agentStore.getRevised.selectedNode = value
2219
}
@@ -31,43 +28,49 @@ const items = ref([
3128
}
3229
])
3330
34-
watch(allTools, (val) => {
35-
console.log('Tool list updated')
36-
const flatChildren = []
37-
const children = val.map((type) => ({
38-
id: type.server,
39-
name: type.server,
40-
children: type.tools
41-
? type.tools.map((obj) => {
42-
const id = agentStore.genId(type.server, obj.name)
43-
const unit = {
44-
id: id,
45-
server: type.server,
46-
name: obj.name
47-
}
48-
flatChildren.push(id)
49-
return unit
50-
})
51-
: []
52-
}))
53-
const rootObj = items.value[0]
54-
rootObj.children = children
55-
items.value = [rootObj]
56-
57-
const newSelectedNode = agentStore.getRevised.selectedNode.filter((node) => {
58-
return flatChildren.includes(node)
59-
})
60-
agentStore.getRevised.selectedNode = newSelectedNode
61-
})
31+
watch(
32+
() => agentStore.allTools,
33+
(val) => {
34+
if (!agentStore.hasTools) {
35+
return
36+
}
37+
console.log('Tools Updated', val)
38+
const flatChildren = []
39+
const children = val.map((type) => ({
40+
id: type.server,
41+
name: type.server,
42+
children: type.tools
43+
? type.tools.map((obj) => {
44+
const id = agentStore.genId(type.server, obj.name)
45+
const unit = {
46+
id: id,
47+
server: type.server,
48+
name: obj.name
49+
}
50+
flatChildren.push(id)
51+
return unit
52+
})
53+
: []
54+
}))
55+
const rootObj = items.value[0]
56+
rootObj.children = children
57+
items.value = [rootObj]
58+
59+
agentStore.agents.forEach((agent) => {
60+
const newSelectedNode = agent.selectedNode.filter((node) => {
61+
return flatChildren.includes(node)
62+
})
63+
agent.selectedNode = newSelectedNode
64+
})
65+
}
66+
)
6267
6368
onMounted(() => {
6469
load()
65-
console.log(selectedTree)
66-
console.log(items.value)
6770
})
6871
6972
function load() {
70-
const mcpServers = mcpStore.getServers
73+
const mcpServers = mcpStore.getServers()
7174
const mcpKeys = Object.keys(mcpServers)
7275
// Create an array of Promises
7376
const toolPromises = mcpKeys.map((key) => {
@@ -82,7 +85,7 @@ function load() {
8285
// If toolsListFunction is not a function, return an object with content as null
8386
return Promise.resolve({
8487
name: key,
85-
content: null
88+
tools: []
8689
})
8790
}
8891
})
@@ -92,7 +95,8 @@ function load() {
9295
// Return a Promise that resolves when all toolPromises are resolved
9396
9497
return Promise.all(toolPromises).then((data) => {
95-
allTools.value = data
98+
console.log(data)
99+
agentStore.allTools = data
96100
})
97101
}
98102
@@ -112,6 +116,7 @@ function handleNameUpdate() {
112116
</script>
113117
114118
<template>
119+
<!-- <v-btn @click="console.log(agentStore.allTools)"></v-btn> -->
115120
<div v-if="agentStore.getRevised" :key="agentStore.getRevised">
116121
<v-card :title="$t('agent.config')">
117122
<v-divider></v-divider>
@@ -150,7 +155,17 @@ function handleNameUpdate() {
150155
</template>
151156
</v-confirm-edit>
152157
153-
<v-card v-if="selectedTree" class="mt-4" :title="$t('agent.tools')">
158+
<v-alert
159+
v-if="!agentStore.hasTools"
160+
border="top"
161+
type="warning"
162+
variant="outlined"
163+
prominent
164+
class="mt-4"
165+
>
166+
{{ $t('agent.no-tools') }}
167+
</v-alert>
168+
<v-card v-else class="mt-4" :title="$t('agent.tools')">
154169
<v-row dense>
155170
<v-divider></v-divider>
156171
<v-treeview
@@ -179,42 +194,34 @@ function handleNameUpdate() {
179194
</div>
180195
<div class="d-flex flex-wrap ga-1">
181196
<v-scroll-x-transition group hide-on-leave>
182-
<v-chip
183-
v-for="selection in selectedTree"
184-
:key="selection"
185-
:text="agentStore.getId(selection).name"
186-
color="grey"
187-
size="small"
188-
border
189-
closable
190-
label
191-
@click:close="onClickClose(selection)"
192-
>
193-
<template #prepend>
194-
<v-avatar
195-
:text="agentStore.getAbbr(agentStore.getId(selection).server)"
196-
:color="agentStore.getColor(selection)"
197-
start
198-
variant="plain"
199-
>
200-
</v-avatar>
201-
</template>
202-
</v-chip>
197+
<div v-for="(selection, index) in selectedTree" :key="index">
198+
<v-chip
199+
v-if="selection"
200+
:key="selection"
201+
:text="agentStore.getId(selection).name"
202+
color="grey"
203+
size="small"
204+
border
205+
closable
206+
label
207+
@click="console.log(selection, index)"
208+
@click:close="onClickClose(selection)"
209+
>
210+
<template #prepend>
211+
<v-avatar
212+
:text="agentStore.getAbbr(agentStore.getId(selection).server)"
213+
:color="agentStore.getColor(selection)"
214+
start
215+
variant="plain"
216+
>
217+
</v-avatar>
218+
</template>
219+
</v-chip>
220+
</div>
203221
</v-scroll-x-transition>
204222
</div>
205223
</v-card-text>
206224
</v-row>
207-
208-
<!-- <v-divider></v-divider>
209-
210-
<template v-slot:actions>
211-
<v-btn text="Reset" @click="selectedTree = []"></v-btn>
212-
213-
<v-spacer></v-spacer>
214-
215-
<v-btn append-icon="mdi-content-save" color="surface-light" text="Save" variant="flat" border></v-btn>
216-
<v-btn @click="console.log(agentStore.selected)"></v-btn>
217-
</template> -->
218225
</v-card>
219226
</div>
220227
</template>

src/renderer/screens/agent/AgentSideDrawer.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<script setup lang="ts">
22
import { useAgentStore } from '@/renderer/store/agent'
3+
import { useMcpStore } from '@/renderer/store/mcp'
4+
const mcpStore = useMcpStore()
35
const agentStore = useAgentStore()
46
57
function handleDelete(index, event) {
@@ -9,7 +11,7 @@ function handleDelete(index, event) {
911
</script>
1012

1113
<template>
12-
<v-list v-model:selected="agentStore.revised" nav mandatory>
14+
<v-list :key="mcpStore.version" v-model:selected="agentStore.revised" nav mandatory>
1315
<v-list-item
1416
v-for="(item, index) in agentStore.agents"
1517
:key="index"
@@ -23,7 +25,7 @@ function handleDelete(index, event) {
2325
<template #prepend>
2426
<v-badge
2527
class="mr-n3"
26-
color="primary"
28+
:color="agentStore.hasTools ? 'primary' : 'grey'"
2729
:content="item.selectedNode.length"
2830
inline
2931
:max="99"

src/renderer/screens/mcp/McpSideDock.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { ref } from 'vue'
3-
import { initAllMcpServers, windowReload } from '@/renderer/utils'
3+
import { initAllMcpServers } from '@/renderer/utils'
44
import { useMcpStore } from '@/renderer/store/mcp'
55
import { useSnackbarStore } from '@/renderer/store/snackbar'
66
const snackbarStore = useSnackbarStore()
@@ -12,17 +12,18 @@ const isLoading = ref(false)
1212
async function activeAllMcpServers() {
1313
isLoading.value = true
1414
try {
15-
const configs = mcpStore.getServers
15+
const configs = mcpStore.getServers()
1616
const result = await initAllMcpServers(configs)
1717
console.log(result)
1818
await mcpStore.updateServers()
1919
if (result.status == 'error') {
2020
snackbarStore.showErrorMessage(result.error.toString())
2121
} else {
22-
windowReload()
22+
snackbarStore.showSuccessMessage('mcp.updated')
2323
}
2424
} finally {
2525
isLoading.value = false
26+
mcpStore.version++
2627
}
2728
}
2829
</script>

src/renderer/screens/mcp/McpSideDrawer.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ const mcpStore = useMcpStore()
44
</script>
55

66
<template>
7-
<v-list v-model:selected="mcpStore.selected" nav mandatory>
7+
<v-list :key="mcpStore.version" v-model:selected="mcpStore.selected" nav mandatory>
88
<v-list-item
9-
v-for="(item, key) in mcpStore.getServers"
9+
v-for="(item, key) in mcpStore.getServers()"
1010
:key="key"
1111
two-line
1212
:value="key"
@@ -31,7 +31,7 @@ const mcpStore = useMcpStore()
3131
</template>
3232
<v-chip-group
3333
v-model="mcpStore.selectedChips[key]"
34-
:direction="mcpStore.selected[0] === key ? 'vertical' : undefined"
34+
:direction="mcpStore.selected?.[0] === key ? 'vertical' : undefined"
3535
selected-class="text-primary"
3636
mandatory
3737
>

0 commit comments

Comments
 (0)