feat(ai): add mcp integration (#13004)

This commit is contained in:
Antoine Moreaux
2025-07-03 21:23:58 +02:00
committed by GitHub
parent bc94d58af7
commit e5522c8efe
33 changed files with 1888 additions and 1332 deletions

View File

@ -8,10 +8,12 @@ import { Button } from 'twenty-ui/input';
import {
IconArrowUpRight,
IconBolt,
IconCopy,
IconPlus,
Status,
} from 'twenty-ui/display';
import { Pill } from 'twenty-ui/components';
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
interface SettingsIntegrationComponentProps {
integration: SettingsIntegration;
@ -64,6 +66,7 @@ const StyledLogo = styled.img`
export const SettingsIntegrationComponent = ({
integration,
}: SettingsIntegrationComponentProps) => {
const { copyToClipboard } = useCopyToClipboard();
return (
<StyledContainer
to={integration.type === 'Active' ? integration.link : undefined}
@ -100,6 +103,17 @@ export const SettingsIntegrationComponent = ({
title="Use"
size="small"
/>
) : integration.type === 'Copy' ? (
<Button
onClick={() => {
if (isDefined(integration.content)) {
copyToClipboard(integration.content);
}
}}
Icon={IconCopy}
title={integration.linkText}
size="small"
/>
) : (
<Button
to={integration.link}

View File

@ -0,0 +1,36 @@
import { SettingsIntegrationCategory } from '@/settings/integrations/types/SettingsIntegrationCategory';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
export const SETTINGS_INTEGRATION_AI_CATEGORY: SettingsIntegrationCategory = {
key: 'ai',
title: 'With AI',
hyperlink: null,
integrations: [
{
from: {
key: 'mcp',
image: '/images/integrations/mcp.svg',
},
to: null,
type: 'Copy',
content: JSON.stringify(
{
mcpServers: {
twenty: {
type: 'remote',
url: `${REACT_APP_SERVER_BASE_URL}/mcp`,
headers: {
Authorization: 'Bearer [API_KEY]',
},
},
},
},
null,
2,
),
text: 'Connect MCP Client',
link: '#',
linkText: 'Copy',
},
],
};

View File

@ -1,21 +0,0 @@
import { SettingsIntegrationCategory } from '@/settings/integrations/types/SettingsIntegrationCategory';
export const SETTINGS_INTEGRATION_WINDMILL_CATEGORY: SettingsIntegrationCategory =
{
key: 'windmill',
title: 'With Windmill',
hyperlink: null,
integrations: [
{
from: {
key: 'windmill',
image: '/images/integrations/windmill-logo.png',
},
to: null,
type: 'Goto',
text: 'Create a workflow with Windmill',
link: 'https://www.windmill.dev',
linkText: 'Go to Windmill',
},
],
};

View File

@ -1,6 +1,6 @@
import { MOCK_REMOTE_DATABASES } from '@/settings/integrations/constants/MockRemoteDatabases';
import { SETTINGS_INTEGRATION_AI_CATEGORY } from '@/settings/integrations/constants/SettingsIntegrationMcp';
import { SETTINGS_INTEGRATION_REQUEST_CATEGORY } from '@/settings/integrations/constants/SettingsIntegrationRequest';
import { SETTINGS_INTEGRATION_WINDMILL_CATEGORY } from '@/settings/integrations/constants/SettingsIntegrationWindmill';
import { SETTINGS_INTEGRATION_ZAPIER_CATEGORY } from '@/settings/integrations/constants/SettingsIntegrationZapier';
import { SettingsIntegrationCategory } from '@/settings/integrations/types/SettingsIntegrationCategory';
import { getSettingsIntegrationAll } from '@/settings/integrations/utils/getSettingsIntegrationAll';
@ -30,6 +30,10 @@ export const useSettingsIntegrationCategories =
({ name }) => name === 'stripe',
)?.isActive;
const isAiIntegrationEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_AI_ENABLED,
);
const allIntegrations = getSettingsIntegrationAll({
isAirtableIntegrationEnabled,
isAirtableIntegrationActive,
@ -42,7 +46,7 @@ export const useSettingsIntegrationCategories =
return [
...(allIntegrations.integrations.length > 0 ? [allIntegrations] : []),
SETTINGS_INTEGRATION_ZAPIER_CATEGORY,
SETTINGS_INTEGRATION_WINDMILL_CATEGORY,
...(isAiIntegrationEnabled ? [SETTINGS_INTEGRATION_AI_CATEGORY] : []),
SETTINGS_INTEGRATION_REQUEST_CATEGORY,
];
};

View File

@ -3,13 +3,15 @@ export type SettingsIntegrationType =
| 'Add'
| 'Goto'
| 'Soon'
| 'Use';
| 'Use'
| 'Copy';
export type SettingsIntegration = {
from: { key: string; image: string };
to?: { key: string; image: string } | null;
type: SettingsIntegrationType;
linkText?: string;
content?: string;
link: string;
text: string;
};

View File

@ -1,9 +1,10 @@
import { useMutation } from '@apollo/client';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useDebouncedCallback } from 'use-debounce';
import { useFindOneAgentQuery } from '~/generated-metadata/graphql';
import { UPDATE_ONE_AGENT } from '../graphql/mutations/updateOneAgent';
import {
useFindOneAgentQuery,
useUpdateOneAgentMutation,
} from '~/generated-metadata/graphql';
type AgentFormValues = {
name: string;
@ -39,9 +40,9 @@ export const useAgentUpdateFormState = ({
},
});
const [updateAgent] = useMutation(UPDATE_ONE_AGENT);
const [updateAgent] = useUpdateOneAgentMutation();
const updateAgentMutation = async (updates: Partial<AgentFormValues>) => {
const updateAgentMutation = async (updates: AgentFormValues) => {
if (!agentId) {
return;
}