diff --git a/client/dashboard/sites/settings-mcp/index.tsx b/client/dashboard/sites/settings-mcp/index.tsx
index 5530bb08ff28..3168a7c80c2c 100644
--- a/client/dashboard/sites/settings-mcp/index.tsx
+++ b/client/dashboard/sites/settings-mcp/index.tsx
@@ -1,6 +1,7 @@
import {
isAutomatticianQuery,
siteBySlugQuery,
+ siteSettingsQuery,
userSettingsQuery,
userSettingsMutation,
} from '@automattic/api-queries';
@@ -13,12 +14,14 @@ import {
ToggleControl,
ExternalLink,
__experimentalText as Text,
+ __experimentalHeading as Heading,
} from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
-import { useState, useMemo, useCallback, createElement } from 'react';
+import { useState, useMemo, useCallback } from 'react';
+import { ButtonStack } from '../../components/button-stack';
import PageLayout from '../../components/page-layout';
import SettingsPageHeader from '../settings-page-header';
import { getSiteMcpAbilities, createSiteSpecificApiPayload } from './utils';
@@ -29,6 +32,7 @@ function SettingsMcpComponent( { siteSlug }: { siteSlug: string } ) {
const { data: site } = useSuspenseQuery( siteBySlugQuery( siteSlug ) );
const { data: isAutomattician } = useQuery( isAutomatticianQuery() );
const { data: userSettings } = useSuspenseQuery( userSettingsQuery() );
+ const { data: siteSettings } = useQuery( siteSettingsQuery( site.ID ) );
// Use the standard userSettingsMutation (now supports mcp_abilities)
const saveMcpMutation = useMutation( {
...userSettingsMutation(),
@@ -42,18 +46,29 @@ function SettingsMcpComponent( { siteSlug }: { siteSlug: string } ) {
// Get tools from user settings using the new nested structure
const availableTools = useMemo( (): [ string, SiteMcpAbilities[ string ] ][] => {
- const abilities = getSiteMcpAbilities( userSettings, site.ID );
+ const abilities = getSiteMcpAbilities( userSettings, site.ID, siteSettings as any );
return Object.entries( abilities );
- }, [ userSettings, site.ID ] );
+ }, [ userSettings, site.ID, siteSettings ] );
const hasTools = availableTools.length > 0;
- const [ formData, setFormData ] = useState< SiteMcpAbilities >( () =>
- getSiteMcpAbilities( userSettings, site.ID )
- );
+ const [ formData, setFormData ] = useState< any >( () => {
+ const abilities = getSiteMcpAbilities( userSettings, site.ID, siteSettings as any );
+ return {
+ ...abilities,
+ };
+ } );
// Calculate if any tools are enabled in form data (for master toggle state)
- const anyToolsEnabled = hasTools && Object.values( formData ).some( ( tool ) => tool.enabled );
+ const anyToolsEnabled =
+ hasTools &&
+ Object.entries( formData ).some(
+ ( [ key, value ] ) =>
+ key !== 'accountToolsEnabled' &&
+ typeof value === 'object' &&
+ value &&
+ ( value as any ).enabled
+ );
const handleSubmit = useCallback(
( e: React.FormEvent ) => {
@@ -69,33 +84,36 @@ function SettingsMcpComponent( { siteSlug }: { siteSlug: string } ) {
createErrorNotice( __( 'Failed to save MCP tools.' ), { type: 'snackbar' } );
}
},
- [ formData, userSettings, site.ID, saveMcpMutation, createErrorNotice ]
+ [ formData, userSettings, site.ID, siteSettings, saveMcpMutation, createErrorNotice ]
);
const handleMasterToggle = useCallback(
( enabled: boolean ) => {
// Get the complete list of available tools from userSettings
- const currentAbilities = getSiteMcpAbilities( userSettings, site.ID );
- const updatedTools: SiteMcpAbilities = {};
+ const currentAbilities = getSiteMcpAbilities( userSettings, site.ID, siteSettings as any );
+ const updatedTools: any = {};
// Update all available tools to the same enabled state
Object.entries( currentAbilities ).forEach( ( [ toolId, tool ] ) => {
updatedTools[ toolId ] = {
- ...tool,
+ ...( tool as any ),
enabled,
};
} );
+ // Preserve the accountToolsEnabled setting
+ updatedTools.accountToolsEnabled = formData.accountToolsEnabled;
+
setFormData( updatedTools );
},
- [ userSettings, site.ID ]
+ [ userSettings, site.ID, formData.accountToolsEnabled ]
);
const handleToolChange = useCallback( ( toolId: string, enabled: boolean ) => {
- setFormData( ( prev ) => ( {
+ setFormData( ( prev: any ) => ( {
...prev,
[ toolId ]: {
- ...prev[ toolId ],
+ ...( prev[ toolId ] as any ),
enabled,
},
} ) );
@@ -107,7 +125,7 @@ function SettingsMcpComponent( { siteSlug }: { siteSlug: string } ) {
toolId,
{
...tool,
- enabled: formData[ toolId ]?.enabled ?? tool.enabled,
+ enabled: ( formData[ toolId ] as any )?.enabled ?? ( tool as any ).enabled,
},
] );
}, [ availableTools, formData ] );
@@ -117,48 +135,6 @@ function SettingsMcpComponent( { siteSlug }: { siteSlug: string } ) {
return null;
}
- // Group tools by type first, then by category
- const groupedByType: Record<
- string,
- Record< string, [ string, SiteMcpAbilities[ string ] ][] >
- > = tools.reduce(
- (
- typeGroups: Record< string, Record< string, [ string, SiteMcpAbilities[ string ] ][] > >,
- [ toolId, tool ]
- ) => {
- const type = tool.type || 'tool'; // Default to 'tool' instead of 'other'
- const category = tool.category || 'General';
-
- // Only include the three main types
- if ( ! [ 'tool', 'resource', 'prompt' ].includes( type ) ) {
- return typeGroups;
- }
-
- if ( ! typeGroups[ type ] ) {
- typeGroups[ type ] = {};
- }
- if ( ! typeGroups[ type ][ category ] ) {
- typeGroups[ type ][ category ] = [];
- }
- typeGroups[ type ][ category ].push( [ toolId, tool ] );
- return typeGroups;
- },
- {} as Record< string, Record< string, [ string, SiteMcpAbilities[ string ] ][] > >
- );
-
- // Type descriptions
- const typeDescriptions: Record< string, string > = {
- tool: __(
- 'Tools allow AI assistants to read and search your WordPress.com data. These are view-only capabilities that cannot modify your content or settings.'
- ),
- resource: __(
- 'Resources provide AI assistants with read-only access to your data, such as site statistics or user information.'
- ),
- prompt: __(
- 'Prompts help AI assistants understand context and provide better responses to your queries.'
- ),
- };
-
const renderContent = () => {
if ( ! hasTools ) {
return (
@@ -173,9 +149,9 @@ function SettingsMcpComponent( { siteSlug }: { siteSlug: string } ) {
}
return (
-
+ { hasTools && (
+
+
+
+ ) }
+
+
+
+
);
};
diff --git a/client/dashboard/sites/settings-mcp/utils.ts b/client/dashboard/sites/settings-mcp/utils.ts
index ee2c9422cd65..504543242725 100644
--- a/client/dashboard/sites/settings-mcp/utils.ts
+++ b/client/dashboard/sites/settings-mcp/utils.ts
@@ -13,8 +13,9 @@ export type McpAbilitiesApiStructure = {
*/
export function getSiteMcpAbilities(
userSettings: { mcp_abilities?: McpAbilities } | null | undefined,
- siteId: string | number
-): SiteMcpAbilities {
+ siteId: string | number,
+ siteSettings?: { default_tools?: boolean } | null | undefined
+): SiteMcpAbilities & { accountToolsEnabled?: boolean } {
const siteIdStr = String( siteId );
const mcpData = userSettings?.mcp_abilities;
@@ -50,7 +51,25 @@ export function getSiteMcpAbilities(
}
} );
- return mergedAbilities;
+ // Determine if account tools should be enabled
+ // Check if there are any enabled account-level abilities
+ const hasEnabledAccountTools =
+ mcpData.account && Object.values( mcpData.account ).some( ( ability ) => ability.enabled );
+
+ // Check if site has default tools enabled (from site settings)
+ const hasDefaultSiteTools = siteSettings?.default_tools === true;
+
+ // Set accountToolsEnabled to true if:
+ // 1. Explicitly set in site overrides, OR
+ // 2. There are enabled account-level tools, OR
+ // 3. Site has default tools available
+ const accountToolsEnabled =
+ siteOverrides.accountToolsEnabled ?? ( hasEnabledAccountTools || hasDefaultSiteTools );
+
+ return {
+ ...mergedAbilities,
+ accountToolsEnabled,
+ };
}
/**
@@ -165,7 +184,7 @@ export function convertAbilitiesFromApi(
export function createSiteSpecificApiPayload(
userSettings: { mcp_abilities?: McpAbilities } | null | undefined,
siteId: string | number,
- abilities: SiteMcpAbilities
+ abilities: SiteMcpAbilities & { accountToolsEnabled?: boolean }
): { mcp_abilities: McpAbilitiesApiStructure } {
const siteIdNum = Number( siteId );
const mcpData = userSettings?.mcp_abilities;
@@ -180,6 +199,11 @@ export function createSiteSpecificApiPayload(
// Find only the abilities that differ from defaults
const siteOverrides: Record< string, boolean > = {};
Object.entries( abilities ).forEach( ( [ abilityName, ability ] ) => {
+ // Skip the accountToolsEnabled field
+ if ( abilityName === 'accountToolsEnabled' ) {
+ return;
+ }
+
const defaultAbility = defaultSiteAbilities[ abilityName ];
// Only store if it differs from the default
@@ -188,6 +212,11 @@ export function createSiteSpecificApiPayload(
}
} );
+ // Add accountToolsEnabled if it's set (and not the default true)
+ if ( abilities.accountToolsEnabled !== undefined && abilities.accountToolsEnabled !== true ) {
+ siteOverrides.accountToolsEnabled = abilities.accountToolsEnabled;
+ }
+
// Create the optimized payload (only include site-specific overrides)
const payload: McpAbilitiesApiStructure = {};
diff --git a/client/me/mcp/main.jsx b/client/me/mcp/main.jsx
index d7f25851b18f..fd1f65b92c6f 100644
--- a/client/me/mcp/main.jsx
+++ b/client/me/mcp/main.jsx
@@ -4,14 +4,14 @@ import {
Button,
__experimentalVStack as VStack,
__experimentalText as Text,
+ __experimentalHeading as Heading,
Card,
CardBody,
- CardHeader,
ToggleControl,
} from '@wordpress/components';
import { createInterpolateElement } from '@wordpress/element';
import { useTranslate } from 'i18n-calypso';
-import { useState, useEffect, createElement } from 'react';
+import { useState, useEffect } from 'react';
import { connect, useDispatch as useReduxDispatch } from 'react-redux';
import DocumentHead from 'calypso/components/data/document-head';
import FormButton from 'calypso/components/forms/form-button';
@@ -22,6 +22,7 @@ import PageViewTracker from 'calypso/lib/analytics/page-view-tracker';
import getUserSettings from 'calypso/state/selectors/get-user-settings';
import { saveUserSettings } from 'calypso/state/user-settings/actions';
import { isUpdatingUserSettings } from 'calypso/state/user-settings/selectors';
+import { ButtonStack } from '../../dashboard/components/button-stack';
import { getAccountMcpAbilities, createAccountApiPayload } from './utils';
function McpComponent( { path, userSettings, isUpdating } ) {
@@ -114,62 +115,12 @@ function McpComponent( { path, userSettings, isUpdating } ) {
},
] );
- // Group tools by type first, then by category
- const groupedByType = tools.reduce( ( typeGroups, [ toolId, tool ] ) => {
- const type = tool.type || 'tool'; // Default to 'tool' instead of 'other'
- const category = tool.category || 'General';
-
- // Only include the three main types
- if ( ! [ 'tool', 'resource', 'prompt' ].includes( type ) ) {
- return typeGroups;
- }
-
- if ( ! typeGroups[ type ] ) {
- typeGroups[ type ] = {};
- }
- if ( ! typeGroups[ type ][ category ] ) {
- typeGroups[ type ][ category ] = [];
- }
- typeGroups[ type ][ category ].push( [ toolId, tool ] );
- return typeGroups;
- }, {} );
-
- // Type descriptions
- const typeDescriptions = {
- tool: translate(
- 'Tools allow AI assistants to read and search your WordPress.com data. These are view-only capabilities that cannot modify your content or settings.'
- ),
- resource: translate(
- 'Resources provide AI assistants with read-only access to your data, such as site statistics or user information.'
- ),
- prompt: translate(
- 'Prompts help AI assistants understand context and provide better responses to your queries.'
- ),
- };
-
- // Type display names
- const typeDisplayNames = {
- tool: translate( 'Tools' ),
- resource: translate( 'Resources' ),
- prompt: translate( 'Prompts' ),
- };
-
return (
-
-
-
-
- { translate( 'Account-level MCP tools' ) }
-
- { translate(
- 'These tools are available across all your sites. You can enable or disable them here to control access globally.'
- ) }
-
-
-
-
- { hasTools ? (
-
+
+
+