Skip to content

Commit b763767

Browse files
committed
Refactor ElicitationCard and update README highlights
Enhanced ElicitationCard.vue to improve property normalization, dynamic model handling, and input rendering for various schema types. Updated README.md to clarify feature highlights and added a detailed MCP features table.
1 parent 3bbc740 commit b763767

File tree

2 files changed

+137
-60
lines changed

2 files changed

+137
-60
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This repository is essentially an **LLM chat desktop application based on MCP**.
1414

1515
Given the considerations regarding the quality and safety of AI-generated content, this project employs strict syntax checks and naming conventions. Therefore, for any further development, please ensure that you use the linting tools I've set up to check and automatically fix syntax issues.
1616

17-
## Features
17+
## Highlights
1818

1919
- ✨ Accelerate AI tool integration via MCP
2020
- ✨ Orchestrate cross-vendor LLM APIs through dynamic configuring
@@ -25,6 +25,19 @@ Given the considerations regarding the quality and safety of AI-generated conten
2525
- ✨ Global state management through the Pinia store
2626
- ✨ Quick support through the GitHub community and official documentation
2727

28+
## 🔥 MCP features
29+
30+
| Status | Feature | Category | Note |
31+
| --- | --- | --- | --- |
32+
|| [Tools](https://modelcontextprotocol.io/specification/latest/server/tools) | Server | |
33+
|| [Prompts](https://modelcontextprotocol.io/specification/latest/server/prompts) | Server | |
34+
|| [Resources](https://modelcontextprotocol.io/specification/latest/server/resources) | Server | |
35+
| 🔲 | [Roots](https://modelcontextprotocol.io/specification/latest/client/roots) | Client | This is generally only used for the Vibe Coding IDE and can typically be configured through server environment variables. |
36+
|| [Sampling](https://modelcontextprotocol.io/specification/latest/server/sampling) | Client | |
37+
|| [Elicitation](https://modelcontextprotocol.io/specification/latest/server/elicitation) | Client | |
38+
|| [Discovery](https://github.com/modelcontextprotocol/registry) | Registry | Provides real-time MCP server discovery on the MCP registry |
39+
|| [MCPB](https://github.com/modelcontextprotocol/mcpb) | Extension | MCP Bundles (.mcpb) is the new name for what was previously known as Desktop Extensions (.dxt) |
40+
2841
## 🚀 Getting Started
2942

3043
You can quickly get started with the project through a variety of options tailored to your role and needs:

src/renderer/components/common/ElicitationCard.vue

Lines changed: 123 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ import { validateNumberRange } from '@/renderer/store/dxt'
66
import { useI18n } from 'vue-i18n'
77
const { t } = useI18n()
88
9-
type ElicitRequestParams = ElicitRequest['params']
9+
type RemoveIndexSignature<T> = {
10+
[K in keyof T as string extends K ? never : K]: T[K]
11+
}
12+
13+
type ElicitRequestParams = RemoveIndexSignature<ElicitRequest['params']>
1014
1115
// type ElicitationProperty = ElicitRequestParams['requestedSchema']['properties'][string]['type']
1216
13-
type ElicitationProperty = string | number | boolean | null | ElicitationEnum[] | string[]
17+
type ElicitationProperty = string | number | boolean | string[]
1418
1519
type ElicitationEnumKey = 'const' | 'title'
1620
@@ -25,24 +29,78 @@ const elicitationParams = ref<ElicitRequestParams | {}>({})
2529
const elicitationChannel = ref('')
2630
2731
const normalizedProperties = computed(() => {
28-
const props = (elicitationParams.value as ElicitRequestParams).requestedSchema.properties
29-
return Object.keys(props).map((key) => ({
30-
key,
31-
para: props[key]
32-
}))
32+
const params = elicitationParams.value
33+
if (!params || !('requestedSchema' in params)) return []
34+
35+
const props = params.requestedSchema.properties
36+
return Object.keys(props).map((key) => {
37+
const para = props[key]
38+
return {
39+
key,
40+
para,
41+
42+
hasEnum: 'enum' in para && para.enum !== undefined,
43+
enums: ('enum' in para ? para.enum : []) as string[],
44+
45+
hasEnumItems: 'oneOf' in para && para.oneOf !== undefined,
46+
enumItems: ('oneOf' in para ? para.oneOf : []) as ElicitationEnum[],
47+
48+
hasMultiEnum: 'items' in para && 'enum' in para.items,
49+
multiEnums: ('items' in para && 'enum' in para.items ? para.items.enum : []) as string[],
50+
51+
hasMultiEnumItems: 'items' in para && 'anyOf' in para.items,
52+
multiEnumItems: ('items' in para && 'anyOf' in para.items
53+
? para.items.anyOf
54+
: []) as ElicitationEnum[],
55+
56+
isString: para.type === 'string',
57+
isNumber: para.type === 'number',
58+
isInteger: para.type === 'integer',
59+
isBoolean: para.type === 'boolean',
60+
61+
minLength: 'minLength' in para ? para.minLength : undefined,
62+
maxLength: 'maxLength' in para ? para.maxLength : undefined,
63+
64+
minItems: 'minItems' in para ? para.minItems : undefined,
65+
maxItems: 'maxItems' in para ? para.maxItems : undefined,
66+
67+
minimum: 'minimum' in para ? para.minimum : undefined,
68+
maximum: 'maximum' in para ? para.maximum : undefined,
69+
70+
title: para.title,
71+
description: para.description,
72+
label: para.description || para.title,
73+
default: 'default' in para ? para.default : undefined
74+
}
75+
})
3376
})
3477
35-
const getConfigAttribute = (name: string) => {
36-
return elicitationResults.value[name] ?? null
78+
const configExist = (name: string) => {
79+
const exists = name in elicitationResults.value
80+
return {
81+
exists,
82+
configValue: exists ? elicitationResults.value[name] : undefined
83+
}
3784
}
3885
39-
const updateConfigAttribute = (name: string, value: ElicitationProperty) => {
40-
elicitationResults.value[name] = value
86+
const updateConfigAttribute = (name: string, value: ElicitationProperty | null) => {
87+
if (value === null) {
88+
elicitationResults.value[name] = ''
89+
} else {
90+
elicitationResults.value[name] = value
91+
}
4192
}
4293
4394
const dynamicModel = (name: string) => ({
44-
get: () => getConfigAttribute(name),
45-
set: (val: ElicitationProperty) => updateConfigAttribute(name, val)
95+
get: (defaultVal: ElicitationProperty | undefined) => {
96+
const { exists, configValue } = configExist(name)
97+
if (defaultVal && !exists) {
98+
updateConfigAttribute(name, defaultVal)
99+
return defaultVal
100+
}
101+
return configValue
102+
},
103+
set: (val: ElicitationProperty | null) => updateConfigAttribute(name, val)
46104
})
47105
48106
const declineElicitation = () => {
@@ -82,6 +140,11 @@ const clearSampling = () => {
82140
}
83141
84142
const getErrorState = (key: string): boolean => {
143+
const { exists } = configExist(key)
144+
if (exists) {
145+
return false
146+
}
147+
85148
const value = elicitationParams.value
86149
87150
if (value && typeof value === 'object' && 'requestedSchema' in value) {
@@ -170,98 +233,99 @@ ElicitationTransfer.request(handleProgress)
170233
'properties' in elicitationParams.requestedSchema
171234
"
172235
>
173-
<v-row v-for="{ para, key } in normalizedProperties" :key="key" class="mx-3 mb-2 mt-1">
236+
<v-row v-for="item in normalizedProperties" :key="item.key" class="mx-3 mb-2 mt-1">
174237
<!-- Single-select enum (without titles) -->
175238
<v-select
176-
v-if="para.enum"
239+
v-if="item.hasEnum"
177240
prepend-icon="mdi-list-box-outline"
178-
:label="para.title || para.description"
241+
:label="item.label"
179242
variant="outlined"
180243
density="compact"
181-
:items="para.enum as string[]"
182-
:model-value="dynamicModel(key).get() as string"
244+
:items="item.enums"
245+
:model-value="dynamicModel(item.key).get(item.default) as string"
183246
clearable
184-
@update:model-value="dynamicModel(key).set($event)"
247+
@update:model-value="dynamicModel(item.key).set($event)"
185248
></v-select>
186249
<!-- Single-select enum (with titles) -->
187250
<v-select
188-
v-else-if="para.oneOf"
251+
v-else-if="item.hasEnumItems"
189252
prepend-icon="mdi-list-box-outline"
190-
:label="para.title || para.description"
253+
:label="item.label"
191254
variant="outlined"
192255
density="compact"
193-
:items="para.oneOf as ElicitationEnum[]"
256+
:items="item.enumItems"
194257
item-value="const"
195-
:model-value="dynamicModel(key).get() as ElicitationEnum[]"
258+
:model-value="dynamicModel(item.key).get(item.default)"
196259
clearable
197-
@update:model-value="dynamicModel(key).set($event)"
260+
@update:model-value="dynamicModel(item.key).set($event)"
198261
></v-select>
199262
<!-- Multi-select enum (without titles) -->
200263
<v-select
201-
v-else-if="para.items && para.items.enum"
264+
v-else-if="item.hasMultiEnum"
202265
prepend-icon="mdi-list-box-outline"
203-
:label="para.title || para.description"
266+
:label="item.label"
204267
variant="outlined"
205268
density="compact"
206-
:items="para.items.enum as string[]"
269+
:items="item.multiEnums"
207270
:multiple="true"
208-
:rules="[validateEnumLength(para.minItems, para.maxItems)]"
271+
:rules="[validateEnumLength(item.minItems, item.maxItems)]"
209272
clearable
210-
:model-value="dynamicModel(key).get() as string[]"
211-
@update:model-value="dynamicModel(key).set($event)"
273+
:model-value="dynamicModel(item.key).get(item.default) as string[]"
274+
@update:model-value="dynamicModel(item.key).set($event)"
212275
></v-select>
213276
<!-- Multi-select enum (with titles) -->
214277
<v-select
215-
v-else-if="para.items && para.items.anyOf"
278+
v-else-if="item.hasMultiEnumItems"
216279
prepend-icon="mdi-list-box-outline"
217-
:label="para.title || para.description"
280+
:label="item.label"
218281
variant="outlined"
219282
density="compact"
220-
:items="para.items.anyOf as ElicitationEnum[]"
283+
:items="item.multiEnumItems"
221284
:multiple="true"
222285
item-value="const"
223-
:rules="[validateEnumLength(para.minItems, para.maxItems)]"
286+
:rules="[validateEnumLength(item.minItems, item.maxItems)]"
224287
clearable
225-
:model-value="dynamicModel(key).get() as ElicitationEnum[]"
226-
@update:model-value="dynamicModel(key).set($event)"
288+
:model-value="dynamicModel(item.key).get(item.default) as string[]"
289+
@update:model-value="dynamicModel(item.key).set($event)"
227290
></v-select>
228291
<v-text-field
229-
v-else-if="para.type === 'string'"
292+
v-else-if="item.isString"
230293
prepend-icon="mdi-alphabetical"
231-
:label="para.title || para.description"
294+
:label="item.label"
232295
density="compact"
233296
variant="outlined"
234-
:placeholder="para.description"
235-
:rules="[validateStringLength(para.minLength, para.maxLength)]"
236-
:model-value="dynamicModel(key).get()"
297+
:placeholder="item.title"
298+
:rules="[validateStringLength(item.minLength, item.maxLength)]"
299+
:model-value="dynamicModel(item.key).get(item.default)"
237300
clearable
238-
:error="getErrorState(key)"
239-
:error-messages="getErrorMessages(key)"
240-
@update:model-value="dynamicModel(key).set($event)"
301+
:error="getErrorState(item.key)"
302+
:error-messages="getErrorMessages(item.key)"
303+
@update:model-value="dynamicModel(item.key).set($event)"
241304
>
242305
</v-text-field>
243306
<v-number-input
244-
v-else-if="para.type === 'integer'"
307+
v-else-if="item.isInteger"
245308
prepend-icon="mdi-numeric"
246-
:model-value="dynamicModel(key).get() as number"
247-
:label="para.title || para.description"
309+
:model-value="dynamicModel(item.key).get(item.default) as number"
310+
:label="item.label"
248311
density="compact"
249312
variant="outlined"
250-
:placeholder="para.description"
251-
:max="para.maximum"
252-
:min="para.minimum"
253-
:hint="validateNumberRange(para.minimum, para.maximum)"
313+
:placeholder="item.title"
314+
:max="item.maximum"
315+
:min="item.minimum"
316+
:hint="validateNumberRange(item.minimum, item.maximum)"
254317
clearable
255-
:error="getErrorState(key)"
256-
:error-messages="getErrorMessages(key)"
257-
@update:model-value="dynamicModel(key).set($event)"
318+
:error="getErrorState(item.key)"
319+
:error-messages="getErrorMessages(item.key)"
320+
@update:model-value="dynamicModel(item.key).set($event)"
258321
></v-number-input>
259322
<v-checkbox
260-
v-else-if="para.type === 'boolean'"
261-
v-tooltip:top="para.description"
262-
:model-value="dynamicModel(key).get()"
263-
:label="para.title"
264-
@update:model-value="dynamicModel(key).set($event)"
323+
v-else-if="item.isBoolean"
324+
v-tooltip:bottom="item.title"
325+
color="secondary"
326+
:model-value="dynamicModel(item.key).get(item.default)"
327+
:label="item.label"
328+
@update:model-value="dynamicModel(item.key).set($event)"
265329
hide-details
266330
></v-checkbox>
267331
</v-row>

0 commit comments

Comments
 (0)