Always allow MCP for web (#6462)

* mcp and extension setting disabled + always allow mcp tools on web

* fix tests
This commit is contained in:
Dinh Long Nguyen 2025-09-15 20:13:46 +07:00 committed by GitHub
parent 59b2755810
commit 311a451005
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 49 additions and 137 deletions

View File

@ -104,7 +104,7 @@ const SettingsMenu = () => {
title: 'common:mcp-servers', title: 'common:mcp-servers',
route: route.settings.mcp_servers, route: route.settings.mcp_servers,
hasSubMenu: false, hasSubMenu: false,
isEnabled: PlatformFeatures[PlatformFeature.MCP_SERVERS], isEnabled: PlatformFeatures[PlatformFeature.MCP_SERVERS_SETTINGS],
}, },
{ {
title: 'common:local_api_server', title: 'common:local_api_server',
@ -122,7 +122,7 @@ const SettingsMenu = () => {
title: 'common:extensions', title: 'common:extensions',
route: route.settings.extensions, route: route.settings.extensions,
hasSubMenu: false, hasSubMenu: false,
isEnabled: PlatformFeatures[PlatformFeature.EXTENSION_MANAGEMENT], isEnabled: PlatformFeatures[PlatformFeature.EXTENSIONS_SETTINGS],
}, },
] ]

View File

@ -1,6 +1,8 @@
import { create } from 'zustand' import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware' import { persist, createJSONStorage } from 'zustand/middleware'
import { localStorageKey } from '@/constants/localStorage' import { localStorageKey } from '@/constants/localStorage'
import { PlatformFeatures } from '@/lib/platform/const'
import { PlatformFeature } from '@/lib/platform/types'
export type ToolApprovalModalProps = { export type ToolApprovalModalProps = {
toolName: string toolName: string
@ -32,7 +34,7 @@ export const useToolApproval = create<ToolApprovalState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
approvedTools: {}, approvedTools: {},
allowAllMCPPermissions: false, allowAllMCPPermissions: PlatformFeatures[PlatformFeature.MCP_AUTO_APPROVE_TOOLS],
isModalOpen: false, isModalOpen: false,
modalProps: null, modalProps: null,
@ -55,6 +57,12 @@ export const useToolApproval = create<ToolApprovalState>()(
showApprovalModal: (toolName: string, threadId: string, toolParameters?: object) => { showApprovalModal: (toolName: string, threadId: string, toolParameters?: object) => {
return new Promise<boolean>((resolve) => { return new Promise<boolean>((resolve) => {
// Auto-approve MCP tools when feature is enabled
if (PlatformFeatures[PlatformFeature.MCP_AUTO_APPROVE_TOOLS]) {
resolve(true)
return
}
// Check if tool is already approved for this thread // Check if tool is already approved for this thread
const state = get() const state = get()
if (state.isToolApproved(threadId, toolName)) { if (state.isToolApproved(threadId, toolName)) {

View File

@ -14,15 +14,9 @@ export const PlatformFeatures: Record<PlatformFeature, boolean> = {
// Hardware monitoring and GPU usage // Hardware monitoring and GPU usage
[PlatformFeature.HARDWARE_MONITORING]: isPlatformTauri(), [PlatformFeature.HARDWARE_MONITORING]: isPlatformTauri(),
// Extension installation/management
[PlatformFeature.EXTENSION_MANAGEMENT]: true,
// Local model inference (llama.cpp) // Local model inference (llama.cpp)
[PlatformFeature.LOCAL_INFERENCE]: isPlatformTauri(), [PlatformFeature.LOCAL_INFERENCE]: isPlatformTauri(),
// MCP (Model Context Protocol) servers
[PlatformFeature.MCP_SERVERS]: true,
// Local API server // Local API server
[PlatformFeature.LOCAL_API_SERVER]: isPlatformTauri(), [PlatformFeature.LOCAL_API_SERVER]: isPlatformTauri(),
@ -46,4 +40,13 @@ export const PlatformFeatures: Record<PlatformFeature, boolean> = {
// Model provider settings page management - disabled for web only // Model provider settings page management - disabled for web only
[PlatformFeature.MODEL_PROVIDER_SETTINGS]: isPlatformTauri(), [PlatformFeature.MODEL_PROVIDER_SETTINGS]: isPlatformTauri(),
// Auto-enable MCP tool permissions - enabled for web platform
[PlatformFeature.MCP_AUTO_APPROVE_TOOLS]: !isPlatformTauri(),
// MCP servers settings page - disabled for web
[PlatformFeature.MCP_SERVERS_SETTINGS]: isPlatformTauri(),
// Extensions settings page - disabled for web
[PlatformFeature.EXTENSIONS_SETTINGS]: isPlatformTauri(),
} }

View File

@ -16,15 +16,9 @@ export enum PlatformFeature {
// Hardware monitoring and GPU usage // Hardware monitoring and GPU usage
HARDWARE_MONITORING = 'hardwareMonitoring', HARDWARE_MONITORING = 'hardwareMonitoring',
// Extension installation/management
EXTENSION_MANAGEMENT = 'extensionManagement',
// Local model inference (llama.cpp) // Local model inference (llama.cpp)
LOCAL_INFERENCE = 'localInference', LOCAL_INFERENCE = 'localInference',
// MCP (Model Context Protocol) servers
MCP_SERVERS = 'mcpServers',
// Local API server // Local API server
LOCAL_API_SERVER = 'localApiServer', LOCAL_API_SERVER = 'localApiServer',
@ -48,4 +42,13 @@ export enum PlatformFeature {
// Model provider settings page management // Model provider settings page management
MODEL_PROVIDER_SETTINGS = 'modelProviderSettings', MODEL_PROVIDER_SETTINGS = 'modelProviderSettings',
// Auto-enable MCP tool permissions without approval
MCP_AUTO_APPROVE_TOOLS = 'mcpAutoApproveTools',
// MCP servers settings page management
MCP_SERVERS_SETTINGS = 'mcpServersSettings',
// Extensions settings page management
EXTENSIONS_SETTINGS = 'extensionsSettings',
} }

View File

@ -18,7 +18,7 @@ export const Route = createFileRoute(route.settings.extensions as any)({
function Extensions() { function Extensions() {
return ( return (
<PlatformGuard feature={PlatformFeature.EXTENSION_MANAGEMENT}> <PlatformGuard feature={PlatformFeature.EXTENSIONS_SETTINGS}>
<ExtensionsContent /> <ExtensionsContent />
</PlatformGuard> </PlatformGuard>
) )

View File

@ -23,8 +23,6 @@ import { useTranslation } from '@/i18n/react-i18next-compat'
import { useAppState } from '@/hooks/useAppState' import { useAppState } from '@/hooks/useAppState'
import { PlatformGuard } from '@/lib/platform/PlatformGuard' import { PlatformGuard } from '@/lib/platform/PlatformGuard'
import { PlatformFeature } from '@/lib/platform' import { PlatformFeature } from '@/lib/platform'
import { isPlatformTauri } from '@/lib/platform/utils'
import { MCPTool } from '@janhq/core'
// Function to mask sensitive values // Function to mask sensitive values
const maskSensitiveValue = (value: string) => { const maskSensitiveValue = (value: string) => {
@ -92,118 +90,12 @@ export const Route = createFileRoute(route.settings.mcp_servers as any)({
function MCPServers() { function MCPServers() {
return ( return (
<PlatformGuard feature={PlatformFeature.MCP_SERVERS}> <PlatformGuard feature={PlatformFeature.MCP_SERVERS_SETTINGS}>
{isPlatformTauri() ? <MCPServersDesktop /> : <MCPServersWeb />} <MCPServersDesktop />
</PlatformGuard> </PlatformGuard>
) )
} }
// Web version of MCP servers - simpler UI without server management
function MCPServersWeb() {
const { t } = useTranslation()
const serviceHub = useServiceHub()
const { allowAllMCPPermissions, setAllowAllMCPPermissions } = useToolApproval()
const [webMcpTools, setWebMcpTools] = useState<MCPTool[]>([])
const [webMcpServers, setWebMcpServers] = useState<string[]>([])
const [webMcpLoading, setWebMcpLoading] = useState(true)
useEffect(() => {
async function loadWebMcpData() {
setWebMcpLoading(true)
try {
const [tools, servers] = await Promise.all([
serviceHub.mcp().getTools(),
serviceHub.mcp().getConnectedServers(),
])
setWebMcpTools(tools)
setWebMcpServers(servers)
} catch (error) {
console.error('Failed to load web MCP data:', error)
setWebMcpTools([])
setWebMcpServers([])
} finally {
setWebMcpLoading(false)
}
}
loadWebMcpData()
}, [serviceHub])
return (
<div className="flex flex-col h-full">
<HeaderPage>
<h1 className="font-medium">{t('common:settings')}</h1>
</HeaderPage>
<div className="flex h-full w-full">
<SettingsMenu />
<div className="p-4 w-full h-[calc(100%-32px)] overflow-y-auto">
<div className="flex flex-col justify-between gap-4 gap-y-3 w-full">
<Card
header={
<div className="flex flex-col mb-4">
<h1 className="text-main-view-fg font-medium text-base">
{t('mcp-servers:title')}
</h1>
<p className="text-sm text-main-view-fg/70 mt-1">
MCP tools are automatically available in your chat sessions
</p>
</div>
}
>
<CardItem
title={t('mcp-servers:allowPermissions')}
description={t('mcp-servers:allowPermissionsDesc')}
actions={
<div className="flex-shrink-0 ml-4">
<Switch
checked={allowAllMCPPermissions}
onCheckedChange={setAllowAllMCPPermissions}
/>
</div>
}
/>
</Card>
<Card>
<CardItem
title="MCP Service Status"
description={
webMcpLoading
? "Loading MCP service status..."
: webMcpServers.length > 0
? `Connected to ${webMcpServers.join(', ')}. ${webMcpTools.length} tools available.`
: "MCP service not connected"
}
descriptionOutside={
webMcpTools.length > 0 && !webMcpLoading && (
<div className="mt-2">
<h4 className="text-sm font-medium text-main-view-fg/80 mb-2">Available Tools:</h4>
<div className="grid grid-cols-1 gap-2">
{webMcpTools.map((tool) => (
<div key={tool.name} className="flex items-start gap-2 p-2 bg-main-view-fg/5 rounded">
<div className="flex-1">
<div className="font-medium text-sm">{tool.name}</div>
<div className="text-xs text-main-view-fg/70">{tool.description}</div>
{tool.server && (
<div className="text-xs text-main-view-fg/50 mt-1">Server: {tool.server}</div>
)}
</div>
</div>
))}
</div>
</div>
)
}
/>
</Card>
</div>
</div>
</div>
</div>
)
}
// Desktop version of MCP servers - full server management capabilities
function MCPServersDesktop() { function MCPServersDesktop() {
const { t } = useTranslation() const { t } = useTranslation()
const serviceHub = useServiceHub() const serviceHub = useServiceHub()
@ -351,7 +243,9 @@ function MCPServersDesktop() {
setLoadingServers((prev) => ({ ...prev, [serverKey]: true })) setLoadingServers((prev) => ({ ...prev, [serverKey]: true }))
const config = getServerConfig(serverKey) const config = getServerConfig(serverKey)
if (active && config) { if (active && config) {
serviceHub.mcp().activateMCPServer(serverKey, { serviceHub
.mcp()
.activateMCPServer(serverKey, {
...(config ?? (mcpServers[serverKey] as MCPServerConfig)), ...(config ?? (mcpServers[serverKey] as MCPServerConfig)),
active, active,
}) })
@ -388,7 +282,10 @@ function MCPServersDesktop() {
active, active,
}) })
syncServers() syncServers()
serviceHub.mcp().deactivateMCPServer(serverKey).finally(() => { serviceHub
.mcp()
.deactivateMCPServer(serverKey)
.finally(() => {
serviceHub.mcp().getConnectedServers().then(setConnectedServers) serviceHub.mcp().getConnectedServers().then(setConnectedServers)
setLoadingServers((prev) => ({ ...prev, [serverKey]: false })) setLoadingServers((prev) => ({ ...prev, [serverKey]: false }))
}) })

View File

@ -11,17 +11,18 @@ expect.extend(matchers)
vi.mock('@/lib/platform/const', () => ({ vi.mock('@/lib/platform/const', () => ({
PlatformFeatures: { PlatformFeatures: {
hardwareMonitoring: true, hardwareMonitoring: true,
extensionManagement: true,
localInference: true, localInference: true,
mcpServers: true,
localApiServer: true, localApiServer: true,
modelHub: true, modelHub: true,
systemIntegrations: true, systemIntegrations: true,
httpsProxy: true, httpsProxy: true,
defaultProviders: true, defaultProviders: true,
analytics: true, analytics: true,
webAutoModelSelection: true, webAutoModelSelection: false,
modelProviderSettings: true, modelProviderSettings: true,
mcpAutoApproveTools: false,
mcpServersSettings: true,
extensionsSettings: true,
} }
})) }))