Skip to content

Commit dfcf638

Browse files
committed
add UI elements needed for lookup picker in custom mcp tools, add error notification to json element for custom tool static headers
1 parent 5b8e85c commit dfcf638

9 files changed

Lines changed: 168 additions & 20 deletions
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/1609.06f45799411bf730.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

dist/1609.3ddc6e7aa772e3f7.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
<body class="mat-typography">
1111
<df-root></df-root>
1212
<script type="text/javascript" src="https://assets.calendly.com/assets/external/widget.js"></script>
13-
<script src="runtime.240961e3e0f3a0fd.js" type="module"></script><script src="polyfills.def0190516b19e6b.js" type="module"></script><script src="main.9dba73d7b14cb3b1.js" type="module"></script></body>
13+
<script src="runtime.6ff9cce765026bb8.js" type="module"></script><script src="polyfills.def0190516b19e6b.js" type="module"></script><script src="main.9dba73d7b14cb3b1.js" type="module"></script></body>
1414
</html>
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/adf-services/df-service-details/df-service-details.component.html

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,27 @@ <h4>
10701070
matInput
10711071
formControlName="url"
10721072
placeholder="https://api.example.com/endpoint/{id}" />
1073-
<mat-hint>Use &#123;param&#125; for path parameters</mat-hint>
1073+
<button
1074+
mat-icon-button
1075+
matSuffix
1076+
type="button"
1077+
[matMenuTriggerFor]="urlLookupMenu"
1078+
[disabled]="availableLookups.length === 0"
1079+
matTooltip="Insert Lookup">
1080+
<fa-icon [icon]="faPlus"></fa-icon>
1081+
</button>
1082+
<mat-menu #urlLookupMenu="matMenu">
1083+
<button
1084+
mat-menu-item
1085+
*ngFor="let lookup of availableLookups"
1086+
(click)="insertLookup(lookup.name, 'url')">
1087+
{{ lookup.name }}
1088+
</button>
1089+
</mat-menu>
1090+
<mat-hint
1091+
>Use &#123;LOOKUP_NAME&#125; for secrets or
1092+
&#123;param&#125; for path parameters</mat-hint
1093+
>
10741094
</mat-form-field>
10751095

10761096
<mat-form-field appearance="outline" class="full-width">
@@ -1186,30 +1206,71 @@ <h5>Parameters</h5>
11861206
<div
11871207
class="editor-section"
11881208
*ngIf="customToolForm.get('toolType')?.value === 'function'">
1189-
<label class="editor-label"
1190-
>Function (JavaScript function body)</label
1191-
>
1209+
<div class="editor-label-row">
1210+
<label class="editor-label"
1211+
>Function (JavaScript function body)</label
1212+
>
1213+
<button
1214+
mat-stroked-button
1215+
type="button"
1216+
[matMenuTriggerFor]="fnLookupMenu"
1217+
[disabled]="availableLookups.length === 0">
1218+
<fa-icon [icon]="faPlus" class="btn-icon"></fa-icon>
1219+
Insert Lookup
1220+
</button>
1221+
<mat-menu #fnLookupMenu="matMenu">
1222+
<button
1223+
mat-menu-item
1224+
*ngFor="let lookup of availableLookups"
1225+
(click)="insertLookup(lookup.name, 'function')">
1226+
{{ lookup.name }}
1227+
</button>
1228+
</mat-menu>
1229+
</div>
11921230
<div class="editor-wrapper">
11931231
<df-ace-editor
1232+
#functionEditor
11941233
formControlName="function"
1195-
[mode]="functionEditorMode">
1234+
[mode]="functionEditorMode"
1235+
(valueChange)="onFunctionChange($event)">
11961236
</df-ace-editor>
11971237
</div>
11981238
<span class="editor-hint">
11991239
Write a JavaScript function body. Parameters are available as
1200-
variables by name.
1240+
variables by name. Use <code>secrets.LOOKUP_NAME</code> to
1241+
reference lookup values.
12011242
</span>
12021243
</div>
12031244

12041245
<!-- Headers (API tools only) -->
12051246
<div
12061247
class="editor-section"
12071248
*ngIf="customToolForm.get('toolType')?.value === 'api'">
1208-
<label class="editor-label">Static Headers (JSON)</label>
1249+
<div class="editor-label-row">
1250+
<label class="editor-label">Static Headers (JSON)</label>
1251+
<button
1252+
mat-stroked-button
1253+
type="button"
1254+
[matMenuTriggerFor]="hdrLookupMenu"
1255+
[disabled]="availableLookups.length === 0">
1256+
<fa-icon [icon]="faPlus" class="btn-icon"></fa-icon>
1257+
Insert Lookup
1258+
</button>
1259+
<mat-menu #hdrLookupMenu="matMenu">
1260+
<button
1261+
mat-menu-item
1262+
*ngFor="let lookup of availableLookups"
1263+
(click)="insertLookup(lookup.name, 'headers')">
1264+
{{ lookup.name }}
1265+
</button>
1266+
</mat-menu>
1267+
</div>
12091268
<div class="editor-wrapper editor-wrapper--compact">
12101269
<df-ace-editor
1270+
#headersEditor
12111271
formControlName="headers"
1212-
[mode]="headersEditorMode">
1272+
[mode]="headersEditorMode"
1273+
(valueChange)="onHeadersChange($event)">
12131274
</df-ace-editor>
12141275
</div>
12151276
</div>

src/app/adf-services/df-service-details/df-service-details.component.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,17 @@ mat-icon {
909909
margin-bottom: 8px;
910910
}
911911

912+
.editor-label-row {
913+
display: flex;
914+
align-items: center;
915+
justify-content: space-between;
916+
margin-bottom: 4px;
917+
918+
.editor-label {
919+
margin-bottom: 0;
920+
}
921+
}
922+
912923
.editor-wrapper {
913924
border: 1px solid rgba(0, 0, 0, 0.15);
914925
border-radius: 8px;

src/app/adf-services/df-service-details/df-service-details.component.ts

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import { CommonModule } from '@angular/common';
7373
import { MatIconModule } from '@angular/material/icon';
7474
import { HttpClient } from '@angular/common/http';
7575
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
76+
import { MatMenuModule } from '@angular/material/menu';
7677
import { DfThemeService } from 'src/app/shared/services/df-theme.service';
7778
import { MatButtonToggleModule } from '@angular/material/button-toggle';
7879
import { readAsText } from '../../shared/utilities/file';
@@ -157,6 +158,7 @@ interface ServiceResponse {
157158
TitleCasePipe,
158159
MatDividerModule,
159160
DfSecurityConfigComponent,
161+
MatMenuModule,
160162
],
161163
})
162164
export class DfServiceDetailsComponent implements OnInit {
@@ -250,6 +252,11 @@ export class DfServiceDetailsComponent implements OnInit {
250252
customTools: any[] = [];
251253
editingToolIndex: number | null = null;
252254
customToolForm!: FormGroup;
255+
availableLookups: Array<{ name: string }> = [];
256+
@ViewChild('functionEditor') functionEditor: DfAceEditorComponent;
257+
@ViewChild('headersEditor') headersEditor: DfAceEditorComponent;
258+
private liveHeadersValue: string | null = null;
259+
private liveFunctionValue: string | null = null;
253260

254261
constructor(
255262
private activatedRoute: ActivatedRoute,
@@ -324,6 +331,13 @@ export class DfServiceDetailsComponent implements OnInit {
324331
.subscribe(images => {
325332
this.images = images;
326333
});
334+
this.http
335+
.get<any>(`${BASE_URL}/system/lookup`, {
336+
params: { fields: 'name', limit: '100' },
337+
})
338+
.subscribe(res => {
339+
this.availableLookups = res?.resource ?? [];
340+
});
327341
this.systemConfigDataService.environment$
328342
.pipe(
329343
switchMap(env =>
@@ -1037,21 +1051,26 @@ export class DfServiceDetailsComponent implements OnInit {
10371051
enabled: true,
10381052
});
10391053
this.customToolParameters.clear();
1054+
this.liveHeadersValue = null;
1055+
this.liveFunctionValue = null;
10401056
}
10411057

10421058
editCustomTool(index: number) {
10431059
const tool = this.customTools[index];
10441060
this.editingToolIndex = index;
1061+
const headersJson = JSON.stringify(tool.headers || {}, null, 2);
10451062
this.customToolForm.patchValue({
10461063
toolType: tool.toolType || 'api',
10471064
name: tool.name,
10481065
description: tool.description,
10491066
httpMethod: tool.httpMethod || 'GET',
10501067
url: tool.url || '',
1051-
headers: JSON.stringify(tool.headers || {}, null, 2),
1068+
headers: headersJson,
10521069
function: tool.function || '',
10531070
enabled: tool.enabled,
10541071
});
1072+
this.liveHeadersValue = headersJson;
1073+
this.liveFunctionValue = tool.function || '';
10551074
this.customToolParameters.clear();
10561075
(tool.parameters || []).forEach((p: any) => {
10571076
this.customToolParameters.push(this.createParameterGroup(p));
@@ -1062,15 +1081,52 @@ export class DfServiceDetailsComponent implements OnInit {
10621081
this.customTools.splice(index, 1);
10631082
}
10641083

1084+
onHeadersChange(value: string): void {
1085+
this.liveHeadersValue = value;
1086+
}
1087+
1088+
onFunctionChange(value: string): void {
1089+
this.liveFunctionValue = value;
1090+
}
1091+
1092+
insertLookup(
1093+
lookupName: string,
1094+
target: 'function' | 'headers' | 'url'
1095+
): void {
1096+
if (target === 'function') {
1097+
this.functionEditor?.insertAtCursor(`secrets.${lookupName}`);
1098+
} else if (target === 'headers') {
1099+
this.headersEditor?.insertAtCursor(`{${lookupName}}`);
1100+
} else if (target === 'url') {
1101+
const urlCtrl = this.customToolForm.get('url');
1102+
if (urlCtrl) {
1103+
const current = urlCtrl.value || '';
1104+
urlCtrl.setValue(current + `{${lookupName}}`);
1105+
}
1106+
}
1107+
}
1108+
10651109
saveCustomTool() {
10661110
if (this.customToolForm.invalid) return;
10671111

10681112
const formValue = this.customToolForm.getRawValue();
1113+
1114+
// Use live editor values captured via (valueChange) — the reactive form
1115+
// binding loses sync when *ngIf destroys/recreates the editor components.
1116+
const headersStr = this.liveHeadersValue ?? formValue.headers ?? '{}';
1117+
const functionStr = this.liveFunctionValue ?? formValue.function ?? '';
1118+
10691119
let headers: Record<string, string> = {};
1070-
try {
1071-
headers = JSON.parse(formValue.headers || '{}');
1072-
} catch {
1073-
headers = {};
1120+
if (formValue.toolType === 'api' && headersStr.trim() !== '') {
1121+
try {
1122+
headers = JSON.parse(headersStr);
1123+
} catch (e: any) {
1124+
this.snackbarService.openSnackBar(
1125+
`Invalid JSON in Static Headers: ${e.message}`,
1126+
'error'
1127+
);
1128+
return;
1129+
}
10741130
}
10751131

10761132
const tool: any = {
@@ -1081,7 +1137,7 @@ export class DfServiceDetailsComponent implements OnInit {
10811137
url: formValue.url,
10821138
parameters: formValue.parameters || [],
10831139
headers,
1084-
function: formValue.function || '',
1140+
function: functionStr,
10851141
enabled: formValue.enabled ?? true,
10861142
};
10871143

0 commit comments

Comments
 (0)