ui: gate MCP prompt and resource capabilities on tool policy

hasPromptsCapability and hasResourcesCapability accept an optional
set of usable server ids; ChatFormActions resolves it from global
enablement minus the active conversation's policy. Restores the
per-chat gating the old mcpServerOverrides provided; callers without
arguments keep global behavior.

Assisted-by: pi
This commit is contained in:
Aleksander Grygier
2026-08-27 07:29:21 +02:00
committed by GitHub
parent ba419bf2f1
commit cd9d02c63f
2 changed files with 37 additions and 21 deletions
@@ -11,7 +11,7 @@
import { Button } from '$lib/components/ui/button';
import { ICON_CLASS_DEFAULT } from '$lib/constants';
import { setChatFormActionsContext } from '$lib/contexts';
import { FileTypeCategory, MessageRole } from '$lib/enums';
import { FileTypeCategory, MessageRole, ToolSource } from '$lib/enums';
import { ChatService } from '$lib/services';
import { chatStore, conversationsStore, mcpStore, settingsStore } from '$lib/stores';
import { getFileTypeCategory } from '$lib/utils';
@@ -58,9 +58,24 @@
let currentConfig = $derived(settingsStore.config);
let hasMcpPromptsSupport = $derived.by(() => mcpStore.hasPromptsCapability());
// usable MCP servers for this conversation: globally enabled and not
// disabled by the effective tool policy (category or server-scoped key)
let policyEnabledMcpServerIds = $derived.by(() => {
const prefs = conversationsStore.preferences;
let hasMcpResourcesSupport = $derived.by(() => mcpStore.hasResourcesCapability());
if (!prefs.isCategoryEnabled(ToolSource.MCP)) return new Set<string>();
return new Set(
mcpStore
.getServers()
.filter((s) => s.enabled && prefs.isServerToolsEnabled(s.id))
.map((s) => s.id)
);
});
let hasMcpPromptsSupport = $derived(mcpStore.hasPromptsCapability(policyEnabledMcpServerIds));
let hasMcpResourcesSupport = $derived(mcpStore.hasResourcesCapability(policyEnabledMcpServerIds));
let hasAudioModality = $state(false);
let hasVideoModality = $state(false);
+19 -18
View File
@@ -812,19 +812,15 @@ class MCPStore implements McpHealthHost {
* Uses health check state since servers may not have active connections until
* the user actually sends a message or uses prompts.
*/
hasPromptsCapability(): boolean {
const enabledServerIds = new Set(
this.getServers()
.filter((s) => s.enabled)
.map((s) => s.id)
);
hasPromptsCapability(enabledServerIds?: ReadonlySet<string>): boolean {
const ids = enabledServerIds ?? this.globalEnabledServerIds();
if (enabledServerIds.size === 0) {
if (ids.size === 0) {
return false;
}
for (const [serverId, state] of Object.entries(this.health.checks)) {
if (!enabledServerIds.has(serverId)) continue;
if (!ids.has(serverId)) continue;
if (
state.status === HealthCheckStatus.SUCCESS &&
@@ -835,7 +831,7 @@ class MCPStore implements McpHealthHost {
}
for (const [serverName, connection] of this.connections) {
if (!enabledServerIds.has(serverName)) continue;
if (!ids.has(serverName)) continue;
if (connection.serverCapabilities?.prompts) {
return true;
@@ -860,19 +856,15 @@ class MCPStore implements McpHealthHost {
* Uses health check state since servers may not have active connections until
* the user actually sends a message or uses prompts.
*/
hasResourcesCapability(): boolean {
const enabledServerIds = new Set(
this.getServers()
.filter((s) => s.enabled)
.map((s) => s.id)
);
hasResourcesCapability(enabledServerIds?: ReadonlySet<string>): boolean {
const ids = enabledServerIds ?? this.globalEnabledServerIds();
if (enabledServerIds.size === 0) {
if (ids.size === 0) {
return false;
}
for (const [serverId, state] of Object.entries(this.health.checks)) {
if (!enabledServerIds.has(serverId)) continue;
if (!ids.has(serverId)) continue;
if (
state.status === HealthCheckStatus.SUCCESS &&
@@ -883,7 +875,7 @@ class MCPStore implements McpHealthHost {
}
for (const [serverName, connection] of this.connections) {
if (!enabledServerIds.has(serverName)) continue;
if (!ids.has(serverName)) continue;
if (MCPService.supportsResources(connection)) {
return true;
@@ -1341,6 +1333,15 @@ class MCPStore implements McpHealthHost {
return `${MCP_SERVER_ID_PREFIX}-${index + 1}`;
}
/** Server ids that are usable right now: globally enabled ones. */
private globalEnabledServerIds(): Set<string> {
return new Set(
this.getServers()
.filter((s) => s.enabled)
.map((s) => s.id)
);
}
private handleToolsListChanged(serverName: string, tools: Tool[]): void {
const connection = this.connections.get(serverName);