From b00552a8a34731d18aeeb8c2c61fe477e74ce44d Mon Sep 17 00:00:00 2001 From: zerob13 Date: Wed, 24 Dec 2025 18:42:22 +0800 Subject: [PATCH 1/5] feat: add mcp passthrough for acp agent --- .../configPresenter/acpConfHelper.ts | 85 ++++++++++++- src/main/presenter/configPresenter/index.ts | 31 ++++- .../agent/acpProcessManager.ts | 9 ++ .../agent/acpSessionManager.ts | 54 +++++++- .../agent/mcpConfigConverter.ts | 59 +++++++++ .../agent/mcpTransportFilter.ts | 23 ++++ .../managers/agentLoopHandler.ts | 32 ++++- .../managers/toolCallProcessor.ts | 3 +- .../providers/acpProvider.ts | 3 +- .../presenter/mcpPresenter/agentMcpFilter.ts | 16 +++ .../presenter/mcpPresenter/toolManager.ts | 31 +++++ .../settings/components/AcpSettings.vue | 79 +++++++++++- src/renderer/src/components/McpToolsList.vue | 82 ++++++++++--- .../chat-input/composables/useAgentMcpData.ts | 47 +++++++ .../chat-input/composables/useMentionData.ts | 17 ++- .../mcp-config/AgentMcpSelector.vue | 115 ++++++++++++++++++ src/renderer/src/i18n/da-DK/mcp.json | 5 +- src/renderer/src/i18n/da-DK/settings.json | 5 +- src/renderer/src/i18n/en-US/mcp.json | 3 + src/renderer/src/i18n/en-US/settings.json | 3 + src/renderer/src/i18n/fa-IR/mcp.json | 5 +- src/renderer/src/i18n/fa-IR/settings.json | 5 +- src/renderer/src/i18n/fr-FR/mcp.json | 5 +- src/renderer/src/i18n/fr-FR/settings.json | 5 +- src/renderer/src/i18n/he-IL/mcp.json | 5 +- src/renderer/src/i18n/he-IL/settings.json | 5 +- src/renderer/src/i18n/ja-JP/mcp.json | 5 +- src/renderer/src/i18n/ja-JP/settings.json | 5 +- src/renderer/src/i18n/ko-KR/mcp.json | 5 +- src/renderer/src/i18n/ko-KR/settings.json | 5 +- src/renderer/src/i18n/pt-BR/mcp.json | 5 +- src/renderer/src/i18n/pt-BR/settings.json | 5 +- src/renderer/src/i18n/ru-RU/mcp.json | 5 +- src/renderer/src/i18n/ru-RU/settings.json | 5 +- src/renderer/src/i18n/zh-CN/mcp.json | 3 + src/renderer/src/i18n/zh-CN/settings.json | 3 + src/renderer/src/i18n/zh-HK/mcp.json | 5 +- src/renderer/src/i18n/zh-HK/settings.json | 5 +- src/renderer/src/i18n/zh-TW/mcp.json | 5 +- src/renderer/src/i18n/zh-TW/settings.json | 5 +- src/renderer/src/stores/chat.ts | 58 ++++++++- .../types/presenters/legacy.presenters.d.ts | 18 +++ test/main/presenter/acpMcpPassthrough.test.ts | 106 ++++++++++++++++ 43 files changed, 913 insertions(+), 67 deletions(-) create mode 100644 src/main/presenter/llmProviderPresenter/agent/mcpConfigConverter.ts create mode 100644 src/main/presenter/llmProviderPresenter/agent/mcpTransportFilter.ts create mode 100644 src/main/presenter/mcpPresenter/agentMcpFilter.ts create mode 100644 src/renderer/src/components/chat-input/composables/useAgentMcpData.ts create mode 100644 src/renderer/src/components/mcp-config/AgentMcpSelector.vue create mode 100644 test/main/presenter/acpMcpPassthrough.test.ts diff --git a/src/main/presenter/configPresenter/acpConfHelper.ts b/src/main/presenter/configPresenter/acpConfHelper.ts index 246bfb6c0..d30037f19 100644 --- a/src/main/presenter/configPresenter/acpConfHelper.ts +++ b/src/main/presenter/configPresenter/acpConfHelper.ts @@ -8,6 +8,7 @@ import type { AcpCustomAgent, AcpStoreData } from '@shared/presenter' +import { McpConfHelper } from './mcpConfHelper' const ACP_STORE_VERSION = '2' const DEFAULT_PROFILE_NAME = 'Default' @@ -64,8 +65,10 @@ const deepClone = (value: T): T => { export class AcpConfHelper { private store: ElectronStore + private readonly mcpConfHelper: McpConfHelper - constructor() { + constructor(options?: { mcpConfHelper?: McpConfHelper }) { + this.mcpConfHelper = options?.mcpConfHelper ?? new McpConfHelper() this.store = new ElectronStore({ name: 'acp_agents', defaults: { @@ -149,6 +152,57 @@ export class AcpConfHelper { return deepClone(this.getData().customs) } + async getAgentMcpSelections(agentId: string, isBuiltin?: boolean): Promise { + const builtin = typeof isBuiltin === 'boolean' ? isBuiltin : this.isBuiltinAgent(agentId) + if (builtin) { + const agent = this.getBuiltins().find((item) => item.id === agentId) + return this.normalizeMcpSelections(agent?.mcpSelections) ?? [] + } + + const agent = this.getCustoms().find((item) => item.id === agentId) + return this.normalizeMcpSelections(agent?.mcpSelections) ?? [] + } + + async setAgentMcpSelections( + agentId: string, + isBuiltin: boolean, + mcpIds: string[] + ): Promise { + const normalized = this.normalizeMcpSelections(mcpIds) ?? [] + const validated = await this.validateMcpSelections(normalized) + + if (isBuiltin) { + this.mutateBuiltins((builtins) => { + const target = builtins.find((agent) => agent.id === agentId) + if (!target) { + throw new Error(`ACP builtin agent not found: ${agentId}`) + } + target.mcpSelections = validated + }) + return + } + + this.mutateCustoms((customs) => { + const target = customs.find((agent) => agent.id === agentId) + if (!target) { + throw new Error(`ACP custom agent not found: ${agentId}`) + } + target.mcpSelections = validated + }) + } + + async addMcpToAgent(agentId: string, isBuiltin: boolean, mcpId: string): Promise { + const current = await this.getAgentMcpSelections(agentId, isBuiltin) + const next = Array.from(new Set([...current, mcpId])) + await this.setAgentMcpSelections(agentId, isBuiltin, next) + } + + async removeMcpFromAgent(agentId: string, isBuiltin: boolean, mcpId: string): Promise { + const current = await this.getAgentMcpSelections(agentId, isBuiltin) + const next = current.filter((id) => id !== mcpId) + await this.setAgentMcpSelections(agentId, isBuiltin, next) + } + addBuiltinProfile( agentId: AcpBuiltinAgentId, profile: Omit, @@ -585,7 +639,8 @@ export class AcpConfHelper { name: BUILTIN_TEMPLATES[id].name, enabled: false, activeProfileId: profile.id, - profiles: [profile] + profiles: [profile], + mcpSelections: undefined } } @@ -620,7 +675,8 @@ export class AcpConfHelper { name: template.name, enabled: Boolean(agent.enabled), activeProfileId, - profiles + profiles, + mcpSelections: this.normalizeMcpSelections(agent.mcpSelections) } } @@ -682,7 +738,8 @@ export class AcpConfHelper { command, args: this.normalizeArgs(agent.args), env: this.normalizeEnv(agent.env), - enabled + enabled, + mcpSelections: this.normalizeMcpSelections(agent.mcpSelections) } } @@ -722,6 +779,26 @@ export class AcpConfHelper { return Object.fromEntries(entries) } + private normalizeMcpSelections(value: unknown): string[] | undefined { + if (!Array.isArray(value)) return undefined + const cleaned = value + .map((item) => (typeof item === 'string' ? item.trim() : String(item).trim())) + .filter((item) => item.length > 0) + if (!cleaned.length) return undefined + return Array.from(new Set(cleaned)) + } + + private async validateMcpSelections(selections: string[]): Promise { + if (!selections.length) return [] + const servers = await this.mcpConfHelper.getMcpServers() + const valid = new Set( + Object.entries(servers) + .filter(([, config]) => config?.type !== 'inmemory') + .map(([name]) => name) + ) + return selections.filter((name) => valid.has(name)) + } + private isBuiltinAgent(id: string): id is AcpBuiltinAgentId { return BUILTIN_ORDER.includes(id as AcpBuiltinAgentId) } diff --git a/src/main/presenter/configPresenter/index.ts b/src/main/presenter/configPresenter/index.ts index 25b329a98..4e0c85780 100644 --- a/src/main/presenter/configPresenter/index.ts +++ b/src/main/presenter/configPresenter/index.ts @@ -190,13 +190,13 @@ export class ConfigPresenter implements IConfigPresenter { setSetting: this.setSetting.bind(this) }) - this.acpConfHelper = new AcpConfHelper() - this.syncAcpProviderEnabled(this.acpConfHelper.getGlobalEnabled()) - this.setupIpcHandlers() - // Initialize MCP configuration helper this.mcpConfHelper = new McpConfHelper() + this.acpConfHelper = new AcpConfHelper({ mcpConfHelper: this.mcpConfHelper }) + this.syncAcpProviderEnabled(this.acpConfHelper.getGlobalEnabled()) + this.setupIpcHandlers() + // Initialize model configuration helper this.modelConfigHelper = new ModelConfigHelper(this.currentAppVersion) @@ -1184,6 +1184,29 @@ export class ConfigPresenter implements IConfigPresenter { this.handleAcpAgentsMutated([agentId]) } + async getAgentMcpSelections(agentId: string, isBuiltin?: boolean): Promise { + return await this.acpConfHelper.getAgentMcpSelections(agentId, isBuiltin) + } + + async setAgentMcpSelections( + agentId: string, + isBuiltin: boolean, + mcpIds: string[] + ): Promise { + await this.acpConfHelper.setAgentMcpSelections(agentId, isBuiltin, mcpIds) + this.handleAcpAgentsMutated([agentId]) + } + + async addMcpToAgent(agentId: string, isBuiltin: boolean, mcpId: string): Promise { + await this.acpConfHelper.addMcpToAgent(agentId, isBuiltin, mcpId) + this.handleAcpAgentsMutated([agentId]) + } + + async removeMcpFromAgent(agentId: string, isBuiltin: boolean, mcpId: string): Promise { + await this.acpConfHelper.removeMcpFromAgent(agentId, isBuiltin, mcpId) + this.handleAcpAgentsMutated([agentId]) + } + private handleAcpAgentsMutated(agentIds?: string[]) { this.clearProviderModelStatusCache('acp') this.notifyAcpAgentsChanged() diff --git a/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts b/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts index 6639b1d0e..8e526bdc3 100644 --- a/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts +++ b/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts @@ -32,6 +32,7 @@ export interface AcpProcessHandle extends AgentProcessHandle { workdir: string availableModes?: Array<{ id: string; name: string; description: string }> currentModeId?: string + mcpCapabilities?: schema.McpCapabilities } interface AcpProcessManagerOptions { @@ -524,6 +525,14 @@ export class AcpProcessManager implements AgentProcessManager currentModeId?: string } + agentCapabilities?: { + mcpCapabilities?: schema.McpCapabilities + } + } + + if (resultData.agentCapabilities?.mcpCapabilities) { + handleSeed.mcpCapabilities = resultData.agentCapabilities.mcpCapabilities + console.info('[ACP] MCP capabilities:', resultData.agentCapabilities.mcpCapabilities) } if (resultData.sessionId) { diff --git a/src/main/presenter/llmProviderPresenter/agent/acpSessionManager.ts b/src/main/presenter/llmProviderPresenter/agent/acpSessionManager.ts index c02c31072..69f763bf7 100644 --- a/src/main/presenter/llmProviderPresenter/agent/acpSessionManager.ts +++ b/src/main/presenter/llmProviderPresenter/agent/acpSessionManager.ts @@ -1,5 +1,5 @@ import { app } from 'electron' -import type { AcpAgentConfig } from '@shared/presenter' +import type { AcpAgentConfig, IConfigPresenter } from '@shared/presenter' import type { AgentSessionState } from './types' import type { AcpProcessManager, @@ -9,11 +9,15 @@ import type { } from './acpProcessManager' import type { ClientSideConnection as ClientSideConnectionType } from '@agentclientprotocol/sdk' import { AcpSessionPersistence } from './acpSessionPersistence' +import { convertMcpConfigToAcpFormat } from './mcpConfigConverter' +import { filterMcpServersByTransportSupport } from './mcpTransportFilter' +import type * as schema from '@agentclientprotocol/sdk/dist/schema.js' interface AcpSessionManagerOptions { providerId: string processManager: AcpProcessManager sessionPersistence: AcpSessionPersistence + configPresenter: IConfigPresenter } interface SessionHooks { @@ -34,6 +38,7 @@ export class AcpSessionManager { private readonly providerId: string private readonly processManager: AcpProcessManager private readonly sessionPersistence: AcpSessionPersistence + private readonly configPresenter: IConfigPresenter private readonly sessionsByConversation = new Map() private readonly sessionsById = new Map() private readonly pendingSessions = new Map>() @@ -42,6 +47,7 @@ export class AcpSessionManager { this.providerId = options.providerId this.processManager = options.processManager this.sessionPersistence = options.sessionPersistence + this.configPresenter = options.configPresenter app.on('before-quit', () => { void this.clearAllSessions() @@ -267,9 +273,53 @@ export class AcpSessionManager { currentModeId?: string }> { try { + let mcpServers: schema.McpServer[] = [] + try { + const selections = await this.configPresenter.getAgentMcpSelections(agent.id) + if (selections.length > 0) { + const serverConfigs = await this.configPresenter.getMcpServers() + const converted = selections + .map((name) => { + const cfg = serverConfigs[name] + if (!cfg) return null + return convertMcpConfigToAcpFormat(name, cfg) + }) + .filter((item): item is schema.McpServer => Boolean(item)) + + mcpServers = filterMcpServersByTransportSupport(converted, handle.mcpCapabilities) + + if (converted.length !== mcpServers.length) { + console.info(`[ACP] Filtered MCP servers by transport support for agent ${agent.id}:`, { + selected: selections, + converted: converted.map((s) => + 'type' in s ? `${s.name}:${s.type}` : `${s.name}:stdio` + ), + passed: mcpServers.map((s) => + 'type' in s ? `${s.name}:${s.type}` : `${s.name}:stdio` + ) + }) + } else { + console.info(`[ACP] Passing MCP servers to agent ${agent.id}:`, { + selected: selections, + passed: mcpServers.map((s) => + 'type' in s ? `${s.name}:${s.type}` : `${s.name}:stdio` + ) + }) + } + } else { + console.info(`[ACP] No MCP selections for agent ${agent.id}; passing none.`) + } + } catch (error) { + console.warn( + `[ACP] Failed to resolve MCP servers for agent ${agent.id}; passing none.`, + error + ) + mcpServers = [] + } + const response = await handle.connection.newSession({ cwd: workdir, - mcpServers: [] + mcpServers }) // Extract modes from response if available diff --git a/src/main/presenter/llmProviderPresenter/agent/mcpConfigConverter.ts b/src/main/presenter/llmProviderPresenter/agent/mcpConfigConverter.ts new file mode 100644 index 000000000..d6ff4ab49 --- /dev/null +++ b/src/main/presenter/llmProviderPresenter/agent/mcpConfigConverter.ts @@ -0,0 +1,59 @@ +import type * as schema from '@agentclientprotocol/sdk/dist/schema.js' +import type { MCPServerConfig } from '@shared/presenter' + +const normalizeStringRecordToArray = ( + record: Record | undefined | null +): Array<{ name: string; value: string }> => { + if (!record || typeof record !== 'object') return [] + return Object.entries(record) + .map(([name, value]) => ({ + name: name?.toString().trim(), + value: typeof value === 'string' ? value : String(value ?? '') + })) + .filter((entry) => entry.name.length > 0) +} + +const normalizeHeaders = ( + record: Record | undefined | null +): Array<{ name: string; value: string }> => { + if (!record || typeof record !== 'object') return [] + return Object.entries(record) + .map(([name, value]) => ({ + name: name?.toString().trim(), + value: value?.toString() ?? '' + })) + .filter((entry) => entry.name.length > 0) +} + +export function convertMcpConfigToAcpFormat( + serverName: string, + config: MCPServerConfig +): schema.McpServer | null { + if (!config || !serverName) return null + + if (config.type === 'inmemory') { + return null + } + + if (config.type === 'stdio') { + return { + name: serverName, + command: config.command, + args: Array.isArray(config.args) ? config.args : [], + env: normalizeStringRecordToArray(config.env) + } + } + + if (config.type === 'http' || config.type === 'sse') { + const url = config.baseUrl?.toString().trim() + if (!url) return null + return { + type: config.type, + name: serverName, + url, + headers: normalizeHeaders(config.customHeaders) + } + } + + return null +} diff --git a/src/main/presenter/llmProviderPresenter/agent/mcpTransportFilter.ts b/src/main/presenter/llmProviderPresenter/agent/mcpTransportFilter.ts new file mode 100644 index 000000000..f2cda9838 --- /dev/null +++ b/src/main/presenter/llmProviderPresenter/agent/mcpTransportFilter.ts @@ -0,0 +1,23 @@ +import type * as schema from '@agentclientprotocol/sdk/dist/schema.js' + +export function filterMcpServersByTransportSupport( + servers: schema.McpServer[], + mcpCapabilities?: schema.McpCapabilities +): schema.McpServer[] { + if (!Array.isArray(servers) || servers.length === 0) return [] + + return servers.filter((server) => { + if ('type' in server) { + if (server.type === 'http') { + return Boolean(mcpCapabilities?.http) + } + if (server.type === 'sse') { + return Boolean(mcpCapabilities?.sse) + } + return false + } + + // Stdio transport: all agents must support it. + return true + }) +} diff --git a/src/main/presenter/llmProviderPresenter/managers/agentLoopHandler.ts b/src/main/presenter/llmProviderPresenter/managers/agentLoopHandler.ts index 8d6e5ed95..107ca26fc 100644 --- a/src/main/presenter/llmProviderPresenter/managers/agentLoopHandler.ts +++ b/src/main/presenter/llmProviderPresenter/managers/agentLoopHandler.ts @@ -7,6 +7,7 @@ import { StreamState } from '../types' import { RateLimitManager } from './rateLimitManager' import { ToolCallProcessor } from './toolCallProcessor' import { ToolPresenter } from '../../toolPresenter' +import { getAgentFilteredTools } from '../../mcpPresenter/agentMcpFilter' import fs from 'fs' import path from 'path' import { app } from 'electron' @@ -45,12 +46,14 @@ export class AgentLoopHandler { modelId ) - return await this.getToolPresenter().getAllToolDefinitions({ + const toolDefs = await this.getToolPresenter().getAllToolDefinitions({ enabledMcpTools: context.enabledMcpTools, chatMode, supportsVision: this.currentSupportsVision, agentWorkspacePath }) + + return await this.filterToolsForChatMode(toolDefs, chatMode, modelId) }, callTool: async (request: MCPToolCall) => { return await this.getToolPresenter().callTool(request) @@ -188,6 +191,30 @@ export class AgentLoopHandler { return lower.includes('deepseek-reasoner') || lower.includes('kimi-k2-thinking') } + private isAgentToolDefinition(tool: { server?: { name: string } }): boolean { + const name = tool.server?.name + return Boolean(name && (name === 'yo-browser' || name.startsWith('agent-'))) + } + + private async filterToolsForChatMode( + tools: Awaited>, + chatMode: 'chat' | 'agent' | 'acp agent', + agentId?: string + ): Promise>> { + if (chatMode !== 'acp agent') return tools + if (!agentId) return [] + + const agentTools = tools.filter((tool) => this.isAgentToolDefinition(tool)) + const mcpTools = tools.filter((tool) => !this.isAgentToolDefinition(tool)) + const filteredMcp = await getAgentFilteredTools( + agentId, + undefined, + mcpTools, + this.options.configPresenter + ) + return [...filteredMcp, ...agentTools] + } + async *startStreamCompletion( providerId: string, initialMessages: ChatMessage[], @@ -313,6 +340,7 @@ export class AgentLoopHandler { supportsVision: this.currentSupportsVision, agentWorkspacePath }) + const filteredToolDefs = await this.filterToolsForChatMode(toolDefs, chatMode, modelId) const canExecute = this.options.rateLimitManager.canExecuteImmediately(providerId) if (!canExecute) { @@ -346,7 +374,7 @@ export class AgentLoopHandler { modelConfig, temperature, maxTokens, - toolDefs + filteredToolDefs ) // Process the standardized stream events diff --git a/src/main/presenter/llmProviderPresenter/managers/toolCallProcessor.ts b/src/main/presenter/llmProviderPresenter/managers/toolCallProcessor.ts index 656e1d584..a8af6c92d 100644 --- a/src/main/presenter/llmProviderPresenter/managers/toolCallProcessor.ts +++ b/src/main/presenter/llmProviderPresenter/managers/toolCallProcessor.ts @@ -123,7 +123,8 @@ export class ToolCallProcessor { name: toolCall.name, arguments: toolCall.arguments }, - server: toolDef.server + server: toolDef.server, + conversationId: context.conversationId } yield { diff --git a/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts b/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts index 1f331ad7e..86c231a22 100644 --- a/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts +++ b/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts @@ -92,7 +92,8 @@ export class AcpProvider extends BaseAgentProvider< this.sessionManager = new AcpSessionManager({ providerId: provider.id, processManager: this.processManager, - sessionPersistence: this.sessionPersistence + sessionPersistence: this.sessionPersistence, + configPresenter }) void this.initWhenEnabled() diff --git a/src/main/presenter/mcpPresenter/agentMcpFilter.ts b/src/main/presenter/mcpPresenter/agentMcpFilter.ts new file mode 100644 index 000000000..5a518ffe0 --- /dev/null +++ b/src/main/presenter/mcpPresenter/agentMcpFilter.ts @@ -0,0 +1,16 @@ +import type { IConfigPresenter, MCPToolDefinition } from '@shared/presenter' + +export async function getAgentFilteredTools( + agentId: string, + isBuiltin: boolean | undefined, + allTools: MCPToolDefinition[], + configPresenter: IConfigPresenter +): Promise { + if (!agentId) return [] + + const selections = await configPresenter.getAgentMcpSelections(agentId, isBuiltin) + if (!selections?.length) return [] + + const selectionSet = new Set(selections) + return allTools.filter((tool) => selectionSet.has(tool.server?.name)) +} diff --git a/src/main/presenter/mcpPresenter/toolManager.ts b/src/main/presenter/mcpPresenter/toolManager.ts index ccdad017c..bbd6ed5b5 100644 --- a/src/main/presenter/mcpPresenter/toolManager.ts +++ b/src/main/presenter/mcpPresenter/toolManager.ts @@ -13,6 +13,7 @@ import { ServerManager } from './serverManager' import { McpClient } from './mcpClient' import { jsonrepair } from 'jsonrepair' import { getErrorMessageLabels } from '@shared/i18n' +import { presenter } from '@/presenter' export class ToolManager { private configPresenter: IConfigPresenter @@ -339,6 +340,36 @@ export class ToolManager { const { client: targetClient, originalName } = targetInfo const toolServerName = targetClient.serverName + // ACP agent-level MCP access control (only applies in "acp agent" chat mode) + if (toolCall.conversationId) { + const chatMode = this.configPresenter.getSetting<'chat' | 'agent' | 'acp agent'>( + 'input_chatMode' + ) + if (chatMode === 'acp agent') { + try { + const conversation = await presenter.threadPresenter.getConversation( + toolCall.conversationId + ) + const agentId = conversation?.settings?.modelId + if (typeof agentId === 'string' && agentId.trim().length > 0) { + const selections = await this.configPresenter.getAgentMcpSelections(agentId) + if (!selections?.length || !selections.includes(toolServerName)) { + return { + toolCallId: toolCall.id, + content: `MCP server '${toolServerName}' is not allowed for ACP agent '${agentId}'. Configure MCP access in ACP settings.`, + isError: true + } + } + } + } catch (error) { + console.warn( + '[ToolManager] Failed to resolve ACP agent context for MCP access control:', + error + ) + } + } + } + // Log the call details including original name console.info('[MCP] ToolManager calling tool', { requestedName: finalName, diff --git a/src/renderer/settings/components/AcpSettings.vue b/src/renderer/settings/components/AcpSettings.vue index 5e515b1b0..0ba45054e 100644 --- a/src/renderer/settings/components/AcpSettings.vue +++ b/src/renderer/settings/components/AcpSettings.vue @@ -62,6 +62,22 @@
{{ agent.name }} + + + + + {{ + t('settings.acp.mcpAccessBadge', { + count: getMcpSelectionCount(agent) + }) + }} + + + +

{{ getMcpSelectionTooltip(agent) }}

+
+
+
{{ t('settings.acp.disabledBadge') }} @@ -118,6 +134,11 @@
+
-

{{ t('mcp.tools.disabled') }}

-

{{ t('mcp.tools.loading') }}

-

{{ t('mcp.tools.error') }}

-

- {{ t('mcp.tools.available', { count: getTotalEnabledToolCount() }) }} -

-

{{ t('mcp.tools.none') }}

+ +
+
+ {{ t('mcp.tools.acpManagedHint') }} +
-
+
{{ t('mcp.tools.enabled') }}
@@ -154,7 +194,7 @@ const getLocalizedServerName = (serverName: string) => {
-
+
{{ t('mcp.tools.enableToUse') }}