Skip to content

Commit 86fc1f3

Browse files
authored
enhance: show upgrade required in chat & mcp server info (#4091)
1 parent b67451d commit 86fc1f3

File tree

7 files changed

+196
-55
lines changed

7 files changed

+196
-55
lines changed

ui/user/src/lib/components/chat/ChatSidebarMcpServer.svelte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
showDeleteConfirm = false;
2828
closeSidebarConfig(layout);
2929
}
30+
31+
async function refreshProjectMcps() {
32+
closeAll(layout);
33+
projectMcps.items = await ChatService.listProjectMCPs(project.assistantID, project.id);
34+
}
3035
</script>
3136

3237
<div class="flex h-fit w-full justify-center bg-gray-50 dark:bg-black">
@@ -74,6 +79,7 @@
7479
onProjectToolsUpdate={() => {
7580
closeAll(layout);
7681
}}
82+
onUpdate={refreshProjectMcps}
7783
{project}
7884
/>
7985
</div>

ui/user/src/lib/components/chat/McpServerSetup.svelte

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -243,26 +243,27 @@
243243
>
244244
{#snippet connectedServerCardAction(d: ConnectedServer)}
245245
{@const requiresUpdate = requiresUserUpdate(d)}
246-
<button
247-
disabled={requiresUpdate}
248-
class={twMerge(
249-
'icon-button hover:bg-surface1 dark:hover:bg-surface2 size-6 min-h-auto min-w-auto flex-shrink-0 p-1 hover:text-blue-500',
250-
requiresUpdate &&
251-
'hover:text-initial cursor-not-allowed opacity-50 hover:bg-transparent dark:hover:bg-transparent'
252-
)}
253-
onclick={() => {
254-
if (requiresUpdate) return;
255-
setupProjectMcp(d);
256-
}}
257-
use:tooltip={{
258-
text: 'Add To Chat',
259-
disablePortal: true,
260-
placement: 'top-end',
261-
classes: ['w-26.5']
262-
}}
263-
>
264-
<Import class="size-4" />
265-
</button>
246+
{#if !requiresUpdate}
247+
<button
248+
class={twMerge(
249+
'icon-button hover:bg-surface1 dark:hover:bg-surface2 size-6 min-h-auto min-w-auto flex-shrink-0 p-1 hover:text-blue-500',
250+
requiresUpdate &&
251+
'hover:text-initial cursor-not-allowed opacity-50 hover:bg-transparent dark:hover:bg-transparent'
252+
)}
253+
onclick={() => {
254+
if (requiresUpdate) return;
255+
setupProjectMcp(d);
256+
}}
257+
use:tooltip={{
258+
text: 'Add To Chat',
259+
disablePortal: true,
260+
placement: 'top-end',
261+
classes: ['w-26.5']
262+
}}
263+
>
264+
<Import class="size-4" />
265+
</button>
266+
{/if}
266267
{/snippet}
267268
</MyMcpServers>
268269
</div>

ui/user/src/lib/components/edit/McpServers.svelte

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@
5858
}
5959
6060
function shouldShowWarning(mcp: (typeof projectMCPs.items)[0]) {
61+
if (mcp.needsURL) {
62+
return true;
63+
}
64+
65+
if (typeof mcp.configured === 'boolean' && mcp.configured === false) {
66+
return true;
67+
}
68+
6169
if (typeof mcp.authenticated === 'boolean') {
6270
return !mcp.authenticated;
6371
}
@@ -111,7 +119,7 @@
111119
{mcpServer.alias || mcpServer.name || DEFAULT_CUSTOM_SERVER_NAME}
112120
{#if shouldShowWarning(mcpServer)}
113121
<span
114-
class="ml-1"
122+
class="ml-2"
115123
use:tooltip={mcpServer.authenticated
116124
? 'Configuration Required'
117125
: 'Authentication Required'}

ui/user/src/lib/components/mcp/McpServerInfoAndTools.svelte

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
<script lang="ts">
2-
import type { MCPCatalogEntry, MCPCatalogServer, Project, ProjectMCP } from '$lib/services';
2+
import {
3+
ChatService,
4+
type MCPCatalogEntry,
5+
type MCPCatalogServer,
6+
type Project,
7+
type ProjectMCP
8+
} from '$lib/services';
39
import { twMerge } from 'tailwind-merge';
410
import McpServerInfo from './McpServerInfo.svelte';
511
import McpServerTools from './McpServerTools.svelte';
612
import McpOauth from './McpOauth.svelte';
13+
import { AlertTriangle } from 'lucide-svelte';
14+
import CatalogConfigureForm, { type LaunchFormData } from './CatalogConfigureForm.svelte';
15+
import { convertEnvHeadersToRecord } from '$lib/services/chat/mcp';
716
817
interface Props {
918
entry?: MCPCatalogEntry | MCPCatalogServer | ProjectMCP;
@@ -13,6 +22,8 @@
1322
project?: Project;
1423
view?: 'overview' | 'tools';
1524
onProjectToolsUpdate?: (selected: string[]) => void;
25+
onUpdate?: () => void;
26+
onEditConfiguration?: () => void;
1627
}
1728
1829
let {
@@ -22,10 +33,11 @@
2233
onAuthenticate,
2334
project,
2435
view = 'overview',
25-
onProjectToolsUpdate
36+
onProjectToolsUpdate,
37+
onUpdate,
38+
onEditConfiguration
2639
}: Props = $props();
2740
let selected = $state<string>(view);
28-
2941
const tabs = [
3042
{ label: 'Overview', view: 'overview' },
3143
{ label: 'Tools', view: 'tools' }
@@ -34,6 +46,60 @@
3446
$effect(() => {
3547
selected = view;
3648
});
49+
50+
let configDialog = $state<ReturnType<typeof CatalogConfigureForm>>();
51+
let configureForm = $state<LaunchFormData>();
52+
let error = $state<string>();
53+
let saving = $state(false);
54+
let configuringServer = $state<MCPCatalogServer>();
55+
56+
async function handleInitConfigureForm() {
57+
if (!entry) return;
58+
if ('mcpID' in entry) {
59+
const response = await ChatService.getSingleOrRemoteMcpServer(entry.mcpID);
60+
61+
let values: Record<string, string>;
62+
try {
63+
values = await ChatService.revealSingleOrRemoteMcpServer(response.id);
64+
} catch (error) {
65+
if (error instanceof Error && !error.message.includes('404')) {
66+
console.error('Failed to reveal user server values due to unexpected error', error);
67+
}
68+
values = {};
69+
}
70+
configuringServer = response;
71+
configureForm = {
72+
envs: response.manifest.env?.map((env) => ({
73+
...env,
74+
value: values[env.key] ?? ''
75+
})),
76+
headers: response.manifest.remoteConfig?.headers?.map((header) => ({
77+
...header,
78+
value: values[header.key] ?? ''
79+
})),
80+
url: response.manifest.remoteConfig?.url
81+
};
82+
configDialog?.open();
83+
}
84+
}
85+
86+
async function handleConfigureFormUpdate() {
87+
if (!configuringServer || !configureForm) return;
88+
try {
89+
if (configuringServer.manifest.runtime === 'remote' && configureForm.url) {
90+
await ChatService.updateRemoteMcpServerUrl(configuringServer.id, configureForm.url.trim());
91+
}
92+
93+
const secretValues = convertEnvHeadersToRecord(configureForm.envs, configureForm.headers);
94+
await ChatService.configureSingleOrRemoteMcpServer(configuringServer.id, secretValues);
95+
96+
configDialog?.close();
97+
onUpdate?.();
98+
} catch (error) {
99+
console.error('Error during configuration:', error);
100+
configDialog?.close();
101+
}
102+
}
37103
</script>
38104

39105
<div class="flex h-full w-full flex-col gap-4">
@@ -65,8 +131,37 @@
65131
descriptionPlaceholder="Add a description for this MCP server in the Configuration tab"
66132
>
67133
{#snippet preContent()}
68-
{#if project}
69-
<McpOauth {entry} {onAuthenticate} {project} />
134+
{#if 'configured' in entry && typeof entry.configured === 'boolean' && entry.configured === false}
135+
<div class="notification-alert mb-4 flex gap-2">
136+
<div class="flex grow flex-col gap-2">
137+
<div class="flex items-center gap-2">
138+
<AlertTriangle class="size-6 flex-shrink-0 self-start text-yellow-500" />
139+
<p class="my-0.5 flex flex-col text-sm font-semibold">Update Required</p>
140+
</div>
141+
<span class="text-sm font-light break-all">
142+
Due to a recent update in the server, an update on this connector's
143+
configuration is required to continue using this server.
144+
</span>
145+
</div>
146+
<div class="flex flex-shrink-0 items-center">
147+
<button
148+
class="button-primary text-sm"
149+
onclick={() => {
150+
if (onEditConfiguration) {
151+
onEditConfiguration();
152+
} else {
153+
handleInitConfigureForm();
154+
}
155+
}}
156+
>
157+
Edit Configuration
158+
</button>
159+
</div>
160+
</div>
161+
{:else if project}
162+
<div class="mb-4 w-full">
163+
<McpOauth {entry} {onAuthenticate} {project} />
164+
</div>
70165
{/if}
71166
{/snippet}
72167
</McpServerInfo>
@@ -76,3 +171,14 @@
76171
{/if}
77172
</div>
78173
</div>
174+
175+
<CatalogConfigureForm
176+
bind:this={configDialog}
177+
bind:form={configureForm}
178+
{error}
179+
icon={configuringServer?.manifest.icon}
180+
name={configuringServer?.alias || configuringServer?.manifest.name}
181+
onSave={handleConfigureFormUpdate}
182+
submitText="Update"
183+
loading={saving}
184+
/>

ui/user/src/lib/components/mcp/MyMcpServers.svelte

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,14 @@
420420
secretValues
421421
);
422422
423+
const updatedServer = await ChatService.getSingleOrRemoteMcpServer(
424+
selectedEntryOrServer.server.id
425+
);
426+
selectedEntryOrServer = {
427+
...selectedEntryOrServer,
428+
server: updatedServer
429+
} as ConnectedServer;
430+
423431
configDialog?.close();
424432
onUpdateConfigure?.();
425433
} else {
@@ -442,6 +450,36 @@
442450
document.getElementsByTagName('main')[0].scrollTo({ top: 0, behavior: 'instant' });
443451
}
444452
453+
async function handleEditConfiguration(connectedServer: ConnectedServer) {
454+
if (!connectedServer?.server) {
455+
console.error('No user configured server for this entry found');
456+
return;
457+
}
458+
let values: Record<string, string>;
459+
try {
460+
values = await ChatService.revealSingleOrRemoteMcpServer(connectedServer.server.id);
461+
} catch (error) {
462+
if (error instanceof Error && !error.message.includes('404')) {
463+
console.error('Failed to reveal user server values due to unexpected error', error);
464+
}
465+
values = {};
466+
}
467+
selectedEntryOrServer = connectedServer;
468+
configureForm = {
469+
envs: connectedServer.server.manifest.env?.map((env) => ({
470+
...env,
471+
value: values[env.key] ?? ''
472+
})),
473+
headers: connectedServer.server.manifest.remoteConfig?.headers?.map((header) => ({
474+
...header,
475+
value: values[header.key] ?? ''
476+
})),
477+
url: connectedServer.server.manifest.remoteConfig?.url,
478+
hostname: connectedServer.parent?.manifest.remoteConfig?.hostname
479+
};
480+
configDialog?.open();
481+
}
482+
445483
const duration = PAGE_TRANSITION_DURATION;
446484
</script>
447485

@@ -733,6 +771,11 @@
733771
parent={selectedEntryOrServer && 'parent' in selectedEntryOrServer
734772
? selectedEntryOrServer.parent
735773
: undefined}
774+
onEditConfiguration={() => {
775+
if (selectedEntryOrServer && 'parent' in selectedEntryOrServer) {
776+
handleEditConfiguration(selectedEntryOrServer);
777+
}
778+
}}
736779
/>
737780
{/if}
738781
</div>
@@ -747,35 +790,7 @@
747790
'menu-button',
748791
requiresUpdate && 'bg-yellow-500/10 text-yellow-500 hover:bg-yellow-500/30'
749792
)}
750-
onclick={async () => {
751-
if (!connectedServer?.server) {
752-
console.error('No user configured server for this entry found');
753-
return;
754-
}
755-
let values: Record<string, string>;
756-
try {
757-
values = await ChatService.revealSingleOrRemoteMcpServer(connectedServer.server.id);
758-
} catch (error) {
759-
if (error instanceof Error && !error.message.includes('404')) {
760-
console.error('Failed to reveal user server values due to unexpected error', error);
761-
}
762-
values = {};
763-
}
764-
selectedEntryOrServer = connectedServer;
765-
configureForm = {
766-
envs: connectedServer.server.manifest.env?.map((env) => ({
767-
...env,
768-
value: values[env.key] ?? ''
769-
})),
770-
headers: connectedServer.server.manifest.remoteConfig?.headers?.map((header) => ({
771-
...header,
772-
value: values[header.key] ?? ''
773-
})),
774-
url: connectedServer.server.manifest.remoteConfig?.url,
775-
hostname: connectedServer.parent?.manifest.remoteConfig?.hostname
776-
};
777-
configDialog?.open();
778-
}}
793+
onclick={() => handleEditConfiguration(connectedServer)}
779794
>
780795
Edit Configuration
781796
</button>

ui/user/src/lib/context/projectMcps.svelte.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const Key = Symbol('mcps');
66
export type ProjectMcpItem = ProjectMCP & {
77
oauthURL?: string;
88
authenticated?: boolean;
9+
configured?: boolean;
10+
needsURL?: boolean;
911
};
1012

1113
export interface ProjectMCPContext {

ui/user/src/lib/services/chat/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ export interface ProjectMCP {
396396
name?: string;
397397
description?: string;
398398
icon?: string;
399+
configured?: boolean;
400+
needsUpdate?: boolean;
401+
needsURL?: boolean;
399402
}
400403

401404
export interface Credential {

0 commit comments

Comments
 (0)