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

@ -435,14 +435,6 @@ export type ConnectionParametersOutput = {
username: Scalars['String'];
};
export type CreateAgentInput = {
description?: InputMaybe<Scalars['String']>;
modelId: Scalars['String'];
name: Scalars['String'];
prompt: Scalars['String'];
responseFormat?: InputMaybe<Scalars['JSON']>;
};
export type CreateAppTokenInput = {
expiresAt: Scalars['DateTime'];
};
@ -688,6 +680,7 @@ export enum FeatureFlagKey {
IS_IMAP_ENABLED = 'IS_IMAP_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
IS_RELATION_CONNECT_ENABLED = 'IS_RELATION_CONNECT_ENABLED',
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
IS_WORKFLOW_FILTERING_ENABLED = 'IS_WORKFLOW_FILTERING_ENABLED'
@ -1007,7 +1000,6 @@ export type Mutation = {
createDraftFromWorkflowVersion: WorkflowVersion;
createOIDCIdentityProvider: SetupSsoOutput;
createObjectEvent: Analytics;
createOneAgent: Agent;
createOneAppToken: AppToken;
createOneField: Field;
createOneObject: Object;
@ -1020,7 +1012,6 @@ export type Mutation = {
deleteApprovedAccessDomain: Scalars['Boolean'];
deleteCurrentWorkspace: Workspace;
deleteDatabaseConfigVariable: Scalars['Boolean'];
deleteOneAgent: Agent;
deleteOneField: Field;
deleteOneObject: Object;
deleteOneRemoteServer: RemoteServer;
@ -1154,11 +1145,6 @@ export type MutationCreateObjectEventArgs = {
};
export type MutationCreateOneAgentArgs = {
input: CreateAgentInput;
};
export type MutationCreateOneAppTokenArgs = {
input: CreateOneAppTokenInput;
};
@ -1214,11 +1200,6 @@ export type MutationDeleteDatabaseConfigVariableArgs = {
};
export type MutationDeleteOneAgentArgs = {
input: AgentIdInput;
};
export type MutationDeleteOneFieldArgs = {
input: DeleteOneFieldInput;
};
@ -1745,7 +1726,6 @@ export type Query = {
field: Field;
fields: FieldConnection;
findDistantTablesWithStatus: Array<RemoteTable>;
findManyAgents: Array<Agent>;
findManyRemoteServersByType: Array<RemoteServer>;
findManyServerlessFunctions: Array<ServerlessFunction>;
findOneAgent: Agent;

View File

@ -435,14 +435,6 @@ export type ConnectionParametersOutput = {
username: Scalars['String'];
};
export type CreateAgentInput = {
description?: InputMaybe<Scalars['String']>;
modelId: Scalars['String'];
name: Scalars['String'];
prompt: Scalars['String'];
responseFormat?: InputMaybe<Scalars['JSON']>;
};
export type CreateApprovedAccessDomainInput = {
domain: Scalars['String'];
email: Scalars['String'];
@ -652,6 +644,7 @@ export enum FeatureFlagKey {
IS_IMAP_ENABLED = 'IS_IMAP_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
IS_RELATION_CONNECT_ENABLED = 'IS_RELATION_CONNECT_ENABLED',
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
IS_WORKFLOW_FILTERING_ENABLED = 'IS_WORKFLOW_FILTERING_ENABLED'
@ -964,7 +957,6 @@ export type Mutation = {
createDraftFromWorkflowVersion: WorkflowVersion;
createOIDCIdentityProvider: SetupSsoOutput;
createObjectEvent: Analytics;
createOneAgent: Agent;
createOneAppToken: AppToken;
createOneField: Field;
createOneObject: Object;
@ -976,7 +968,6 @@ export type Mutation = {
deleteApprovedAccessDomain: Scalars['Boolean'];
deleteCurrentWorkspace: Workspace;
deleteDatabaseConfigVariable: Scalars['Boolean'];
deleteOneAgent: Agent;
deleteOneField: Field;
deleteOneObject: Object;
deleteOneRole: Scalars['String'];
@ -1105,11 +1096,6 @@ export type MutationCreateObjectEventArgs = {
};
export type MutationCreateOneAgentArgs = {
input: CreateAgentInput;
};
export type MutationCreateOneFieldArgs = {
input: CreateOneFieldMetadataInput;
};
@ -1150,11 +1136,6 @@ export type MutationDeleteDatabaseConfigVariableArgs = {
};
export type MutationDeleteOneAgentArgs = {
input: AgentIdInput;
};
export type MutationDeleteOneFieldArgs = {
input: DeleteOneFieldInput;
};
@ -1655,7 +1636,6 @@ export type Query = {
currentWorkspace: Workspace;
field: Field;
fields: FieldConnection;
findManyAgents: Array<Agent>;
findManyServerlessFunctions: Array<ServerlessFunction>;
findOneAgent: Agent;
findOneServerlessFunction: ServerlessFunction;

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;
}