From e5522c8efec0b354281149d92d40cd98a5772ae2 Mon Sep 17 00:00:00 2001 From: Antoine Moreaux Date: Thu, 3 Jul 2025 21:23:58 +0200 Subject: [PATCH] feat(ai): add mcp integration (#13004) --- .../public/images/integrations/mcp.svg | 1 + .../src/generated-metadata/graphql.ts | 22 +- .../twenty-front/src/generated/graphql.ts | 22 +- .../SettingsIntegrationComponent.tsx | 14 + .../constants/SettingsIntegrationMcp.ts | 36 + .../constants/SettingsIntegrationWindmill.ts | 21 - .../hooks/useSettingsIntegrationCategories.ts | 8 +- .../integrations/types/SettingsIntegration.ts | 4 +- .../hooks/useAgentUpdateFormState.ts | 11 +- .../src/engine/core-modules/ai/ai.module.ts | 34 +- .../core-modules/ai/constants/mcp.const.ts | 7 + .../ai/controllers/ai.controller.spec.ts | 2 +- .../ai/controllers/ai.controller.ts | 2 +- .../ai/controllers/mcp.controller.spec.ts | 165 ++++ .../ai/controllers/mcp.controller.ts | 38 + .../ai/decorators/string-or-number.ts | 15 + .../engine/core-modules/ai/dtos/json-rpc.ts | 28 + .../ai/{ => services}/ai.service.ts | 0 .../ai/services/mcp.service.spec.ts | 392 +++++++++ .../core-modules/ai/services/mcp.service.ts | 181 ++++ .../core-modules/ai/services/tool.service.ts | 785 +++++++++++++++++ .../ai/utils/wrap-jsonrpc-response.ts | 23 + .../enums/feature-flag-key.enum.ts | 1 - .../agent/agent-tool.service.ts | 817 +----------------- .../metadata-modules/agent/agent.exception.ts | 6 - .../metadata-modules/agent/agent.resolver.ts | 25 - .../suites/agent/agent.integration-spec.ts | 367 +++----- .../utils/update-feature-flag-factory.util.ts | 26 - .../agent-tool.service.integration-spec.ts | 154 +--- .../agent/utils/agent-tool-test-utils.ts | 5 + .../types/ObjectRecordsPermissionsByRoleId.ts | 2 +- .../icon/providers/internal/AllIcons.ts | 4 - .../user-guide/functions/integrations.mdx | 2 +- 33 files changed, 1888 insertions(+), 1332 deletions(-) create mode 100644 packages/twenty-front/public/images/integrations/mcp.svg create mode 100644 packages/twenty-front/src/modules/settings/integrations/constants/SettingsIntegrationMcp.ts delete mode 100644 packages/twenty-front/src/modules/settings/integrations/constants/SettingsIntegrationWindmill.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/constants/mcp.const.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/controllers/mcp.controller.spec.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/controllers/mcp.controller.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/decorators/string-or-number.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/dtos/json-rpc.ts rename packages/twenty-server/src/engine/core-modules/ai/{ => services}/ai.service.ts (100%) create mode 100644 packages/twenty-server/src/engine/core-modules/ai/services/mcp.service.spec.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/services/mcp.service.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/services/tool.service.ts create mode 100644 packages/twenty-server/src/engine/core-modules/ai/utils/wrap-jsonrpc-response.ts delete mode 100644 packages/twenty-server/test/integration/graphql/utils/update-feature-flag-factory.util.ts diff --git a/packages/twenty-front/public/images/integrations/mcp.svg b/packages/twenty-front/public/images/integrations/mcp.svg new file mode 100644 index 000000000..7305c372a --- /dev/null +++ b/packages/twenty-front/public/images/integrations/mcp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index c23a8fcab..1a832ec55 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -435,14 +435,6 @@ export type ConnectionParametersOutput = { username: Scalars['String']; }; -export type CreateAgentInput = { - description?: InputMaybe; - modelId: Scalars['String']; - name: Scalars['String']; - prompt: Scalars['String']; - responseFormat?: InputMaybe; -}; - 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; - findManyAgents: Array; findManyRemoteServersByType: Array; findManyServerlessFunctions: Array; findOneAgent: Agent; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index e1f224b91..c587db4c7 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -435,14 +435,6 @@ export type ConnectionParametersOutput = { username: Scalars['String']; }; -export type CreateAgentInput = { - description?: InputMaybe; - modelId: Scalars['String']; - name: Scalars['String']; - prompt: Scalars['String']; - responseFormat?: InputMaybe; -}; - 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; findManyServerlessFunctions: Array; findOneAgent: Agent; findOneServerlessFunction: ServerlessFunction; diff --git a/packages/twenty-front/src/modules/settings/integrations/components/SettingsIntegrationComponent.tsx b/packages/twenty-front/src/modules/settings/integrations/components/SettingsIntegrationComponent.tsx index 1c0f10251..cf5d54739 100644 --- a/packages/twenty-front/src/modules/settings/integrations/components/SettingsIntegrationComponent.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/components/SettingsIntegrationComponent.tsx @@ -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 ( + ) : integration.type === 'Copy' ? ( +