Skip to content

Commit ffa3592

Browse files
Merge pull request #49377 from phillip-kruger/dev-mcp-filter
Add a filter bar for Dev MCP Tools screen in Dev UI
2 parents d460b21 + fbdc4e3 commit ffa3592

File tree

1 file changed

+131
-68
lines changed

1 file changed

+131
-68
lines changed

extensions/devui/resources/src/main/resources/dev-ui/qwc/qwc-dev-mcp-tools.js

Lines changed: 131 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,19 @@ export class QwcDevMCPTools extends QwcHotReloadElement {
3535
vaadin-grid {
3636
height: 100%;
3737
}
38+
.filterText {
39+
width: 100%;
40+
}
3841
`;
3942

4043
static properties = {
4144
_tools: {state: true},
45+
_filtered: {state: true, type: Array},
4246
_showInputDialog: {state: true, type: Boolean},
4347
_selectedTool: {state: true},
4448
_inputvalues: { type: Object },
45-
_toolResult: {state: true}
49+
_toolResult: {state: true},
50+
_searchTerm: {state: true}
4651
}
4752

4853
constructor() {
@@ -51,12 +56,16 @@ export class QwcDevMCPTools extends QwcHotReloadElement {
5156
this._selectedTool = [];
5257
this._inputvalues = new Map();
5358
this._toolResult = null;
59+
this._tools = null;
60+
this._filtered = null;
5461
}
5562

5663
connectedCallback() {
5764
super.connectedCallback();
65+
this._filteredValue = null;
5866
this._loadTools();
5967
this._inputvalues.clear();
68+
6069
}
6170

6271
render() {
@@ -78,8 +87,96 @@ export class QwcDevMCPTools extends QwcHotReloadElement {
7887
}
7988

8089
_renderTools(){
81-
return html`
82-
<vaadin-dialog
90+
return html`${this._renderToolInvovationResultDialog()}
91+
${this._renderToolInvocationInputDialog()}
92+
${this._renderTabsheet()}`;
93+
}
94+
95+
_renderTabsheet(){
96+
return html`<vaadin-tabsheet>
97+
<vaadin-tabs slot="tabs">
98+
<vaadin-tab id="list-tab">List</vaadin-tab>
99+
<vaadin-tab id="raw-tab">Raw json</vaadin-tab>
100+
</vaadin-tabs>
101+
${this._renderGridTab()}
102+
${this._renderRawTab()}
103+
</vaadin-tabsheet>`;
104+
}
105+
106+
_renderGridTab(){
107+
return html`<div tab="list-tab">
108+
${this._renderFilterTextbar()}
109+
110+
<vaadin-grid .items="${this._filtered}" .selectedItems="${this._selectedTool}" theme="row-stripes" all-rows-visible
111+
@active-item-changed="${(e) => {
112+
const item = e.detail.value;
113+
this._selectedTool = item ? [item] : [];
114+
115+
if(this._selectedTool.length>0){
116+
let parameters = Object.keys(this._selectedTool[0].inputSchema.properties);
117+
const propertyCount = parameters.length;
118+
if(propertyCount>0) {
119+
this._showInputDialog = true;
120+
}else {
121+
this._testJsonRpcCall(this._selectedTool[0], null);
122+
}
123+
}
124+
}}">
125+
<vaadin-grid-sort-column
126+
header='Namespace'
127+
path="name"
128+
auto-width
129+
${columnBodyRenderer(this._namespaceRenderer, [])}
130+
>
131+
</vaadin-grid-sort-column>
132+
<vaadin-grid-sort-column
133+
header='Method'
134+
path="name"
135+
auto-width
136+
${columnBodyRenderer(this._nameRenderer, [])}
137+
>
138+
</vaadin-grid-sort-column>
139+
<vaadin-grid-sort-column
140+
header='Description'
141+
path="description"
142+
auto-width>
143+
</vaadin-grid-sort-column>
144+
<vaadin-grid-column
145+
header="Params"
146+
frozen-to-end
147+
auto-width
148+
flex-grow="0"
149+
${columnBodyRenderer(this._noOfParameterRenderer, [])}
150+
></vaadin-grid-column>
151+
</vaadin-grid>
152+
</div>`;
153+
}
154+
155+
_renderFilterTextbar(){
156+
return html`<vaadin-text-field class="filterText"
157+
placeholder="Filter"
158+
value="${this._filteredValue}"
159+
style="flex: 1;"
160+
@value-changed="${(e) => this._filterTextChanged(e)}">
161+
<vaadin-icon slot="prefix" icon="font-awesome-solid:filter"></vaadin-icon>
162+
<qui-badge slot="suffix"><span>${this._filtered?.length}</span></qui-badge>
163+
</vaadin-text-field>`;
164+
}
165+
166+
_renderRawTab(){
167+
return html`<div tab="raw-tab">
168+
<div class="codeBlock">
169+
<qui-themed-code-block
170+
mode='json'
171+
content='${JSON.stringify(this._tools, null, 2)}'
172+
showLineNumbers>
173+
</qui-themed-code-block>
174+
</div>
175+
</div>`;
176+
}
177+
178+
_renderToolInvovationResultDialog(){
179+
return html`<vaadin-dialog
83180
header-title="Tool invocation result"
84181
.opened="${this._toolResult!==null}"
85182
@opened-changed="${(event) => {
@@ -96,9 +193,11 @@ export class QwcDevMCPTools extends QwcHotReloadElement {
96193
[]
97194
)}
98195
${dialogRenderer(() => this._renderToolResult())}
99-
></vaadin-dialog>
100-
101-
<vaadin-dialog
196+
></vaadin-dialog>`;
197+
}
198+
199+
_renderToolInvocationInputDialog(){
200+
return html `<vaadin-dialog
102201
header-title="Input"
103202
.opened="${this._showInputDialog}"
104203
@opened-changed="${(event) => {
@@ -115,67 +214,7 @@ export class QwcDevMCPTools extends QwcHotReloadElement {
115214
[]
116215
)}
117216
${dialogRenderer(this._renderToolInput)}
118-
></vaadin-dialog>
119-
120-
<vaadin-tabsheet>
121-
<vaadin-tabs slot="tabs">
122-
<vaadin-tab id="list-tab">List</vaadin-tab>
123-
<vaadin-tab id="raw-tab">Raw json</vaadin-tab>
124-
</vaadin-tabs>
125-
<div tab="list-tab">
126-
<vaadin-grid .items="${this._tools.tools}" .selectedItems="${this._selectedTool}" theme="row-stripes" all-rows-visible
127-
@active-item-changed="${(e) => {
128-
const item = e.detail.value;
129-
this._selectedTool = item ? [item] : [];
130-
131-
if(this._selectedTool.length>0){
132-
let parameters = Object.keys(this._selectedTool[0].inputSchema.properties);
133-
const propertyCount = parameters.length;
134-
if(propertyCount>0) {
135-
this._showInputDialog = true;
136-
}else {
137-
this._testJsonRpcCall(this._selectedTool[0], null);
138-
}
139-
}
140-
}}">
141-
<vaadin-grid-sort-column
142-
header='Namespace'
143-
path="name"
144-
auto-width
145-
${columnBodyRenderer(this._namespaceRenderer, [])}
146-
>
147-
</vaadin-grid-sort-column>
148-
<vaadin-grid-sort-column
149-
header='Method'
150-
path="name"
151-
auto-width
152-
${columnBodyRenderer(this._nameRenderer, [])}
153-
>
154-
</vaadin-grid-sort-column>
155-
<vaadin-grid-sort-column
156-
header='Description'
157-
path="description"
158-
auto-width>
159-
</vaadin-grid-sort-column>
160-
<vaadin-grid-column
161-
header="Params"
162-
frozen-to-end
163-
auto-width
164-
flex-grow="0"
165-
${columnBodyRenderer(this._noOfParameterRenderer, [])}
166-
></vaadin-grid-column>
167-
</vaadin-grid>
168-
</div>
169-
<div tab="raw-tab">
170-
<div class="codeBlock">
171-
<qui-themed-code-block
172-
mode='json'
173-
content='${JSON.stringify(this._tools, null, 2)}'
174-
showLineNumbers>
175-
</qui-themed-code-block>
176-
</div>
177-
</vaadin-tabsheet>
178-
`;
217+
></vaadin-dialog>`;
179218
}
180219

181220
_renderToolResult(){
@@ -210,6 +249,29 @@ export class QwcDevMCPTools extends QwcHotReloadElement {
210249
}
211250
}
212251

252+
_filterTextChanged(e) {
253+
this._searchTerm = (e.detail.value || '').trim();
254+
return this._filterGrid();
255+
}
256+
257+
_filterGrid(){
258+
if (this._searchTerm === '') {
259+
this._filtered = this._tools;
260+
return;
261+
}
262+
263+
this._filtered = this._tools.filter((prop) => {
264+
return this._match(prop.name, this._searchTerm) || this._match(prop.description, this._searchTerm)
265+
});
266+
}
267+
268+
_match(value, term) {
269+
if (! value) {
270+
return false;
271+
}
272+
return value.toLowerCase().includes(term.toLowerCase());
273+
}
274+
213275
_closeDialog(){
214276
this._toolResult = null;
215277
this._showInputDialog = false;
@@ -279,7 +341,8 @@ export class QwcDevMCPTools extends QwcHotReloadElement {
279341

280342
_loadTools(){
281343
this.jsonRpc.list().then(jsonRpcResponse => {
282-
this._tools = jsonRpcResponse.result;
344+
this._tools = jsonRpcResponse.result.tools;
345+
this._filtered = this._tools;
283346
});
284347
}
285348

0 commit comments

Comments
 (0)