Allow workflows listing in MCP (#22013)
This resolves https://github.com/twentyhq/twenty/issues/21986 Add `list_workflows `MCP tool Workflow objects are excluded from the generic database CRUD tools exposed via MCP, which meant the only way to list workflows was through a direct API call. This adds a `list_workflows `tool to the `WorkflowToolProvider`, making it available via MCP alongside the existing workflow builder tools. It supports optional filtering by status (`DRAFT`, `ACTIVE`, `DEACTIVATED`) and pagination (`limit`/`offset`). The status filter uses an array-membership predicate (`ANY`) since `statuses `is a multi-value field. --------- Co-authored-by: Souheyl Gouadria <souheyl.gouadria@medius.com> Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -24,7 +24,7 @@ export const buildMcpServerInstructions = (
|
||||
``,
|
||||
`Non-CRUD tools — use learn_tools for schemas:`,
|
||||
` ACTION: http_request | send_email | draft_email | navigate_app | code_interpreter | search_help_center`,
|
||||
` WORKFLOW: create_complete_workflow | create/update/delete_workflow_version_step | activate/deactivate_workflow_version`,
|
||||
` WORKFLOW: list_workflows | create_complete_workflow | create/update/delete_workflow_version_step | activate/deactivate_workflow_version | list_workflow_runs | get_workflow_run | get_workflow_current_version`,
|
||||
` METADATA: get/create/update/delete_object_metadata | get/create/update/delete_field_metadata`,
|
||||
` Both GET tools return system items as compact summaries by default — keep that default for listing/inspecting; only set includeFullSystemObjects / includeFullSystemFields=true when you specifically need a system item's full configuration`,
|
||||
` VIEW: get_views | get_view_query_parameters | create/update/delete_view | manage view fields, filters, sorts`,
|
||||
|
||||
+4
@@ -68,6 +68,10 @@ LOGIC_FUNCTION steps execute logic functions provided by installed applications.
|
||||
{ "stepType": "LOGIC_FUNCTION", "workflowVersionId": "<version-id>", "defaultSettings": { "input": { "logicFunctionId": "<logic-function-id>" } } }
|
||||
3. Or when using \`create_complete_workflow\`, include a step with type "LOGIC_FUNCTION" and settings.input.logicFunctionId.
|
||||
|
||||
## Listing Workflows
|
||||
|
||||
To discover existing workflows in the workspace, use \`list_workflows\`. Use this before modifying a workflow when the user refers to it by name rather than id — resolve the \`id\` here first, then call \`get_workflow_current_version\` with it.
|
||||
|
||||
## Troubleshooting Workflow Runs
|
||||
|
||||
When a user reports a failing or misbehaving workflow, diagnose it with two read-only tools:
|
||||
|
||||
+6
@@ -27,6 +27,7 @@ import { createGetWorkflowCurrentVersionTool } from 'src/modules/workflow/workfl
|
||||
import { createGetWorkflowRunTool } from 'src/modules/workflow/workflow-tools/tools/get-workflow-run.tool';
|
||||
import { createListLogicFunctionToolsTool } from 'src/modules/workflow/workflow-tools/tools/list-logic-function-tools.tool';
|
||||
import { createListWorkflowRunsTool } from 'src/modules/workflow/workflow-tools/tools/list-workflow-runs.tool';
|
||||
import { createListWorkflowsTool } from 'src/modules/workflow/workflow-tools/tools/list-workflows.tool';
|
||||
import { createUpdateAgentTool } from 'src/modules/workflow/workflow-tools/tools/update-agent.tool';
|
||||
import { createUpdateLogicFunctionSourceTool } from 'src/modules/workflow/workflow-tools/tools/update-logic-function-source.tool';
|
||||
import { createUpdateWorkflowVersionPositionsTool } from 'src/modules/workflow/workflow-tools/tools/update-workflow-version-positions.tool';
|
||||
@@ -134,6 +135,10 @@ export class WorkflowToolWorkspaceService {
|
||||
this.deps,
|
||||
contextWithPermissions,
|
||||
);
|
||||
const listWorkflows = createListWorkflowsTool(
|
||||
this.deps,
|
||||
contextWithPermissions,
|
||||
);
|
||||
const updateLogicFunctionSource = createUpdateLogicFunctionSourceTool(
|
||||
this.deps,
|
||||
context,
|
||||
@@ -159,6 +164,7 @@ export class WorkflowToolWorkspaceService {
|
||||
[deactivateWorkflowVersion.name]: deactivateWorkflowVersion,
|
||||
[computeStepOutputSchema.name]: computeStepOutputSchema,
|
||||
[getWorkflowCurrentVersion.name]: getWorkflowCurrentVersion,
|
||||
[listWorkflows.name]: listWorkflows,
|
||||
[getWorkflowRun.name]: getWorkflowRun,
|
||||
[listWorkflowRuns.name]: listWorkflowRuns,
|
||||
[updateLogicFunctionSource.name]: updateLogicFunctionSource,
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import {
|
||||
WorkflowStatus,
|
||||
type WorkflowWorkspaceEntity,
|
||||
} from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
import {
|
||||
type WorkflowToolContext,
|
||||
type WorkflowToolDependencies,
|
||||
} from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
|
||||
|
||||
type ListWorkflowsToolContext = WorkflowToolContext & {
|
||||
rolePermissionConfig: RolePermissionConfig;
|
||||
};
|
||||
|
||||
const listWorkflowsSchema = z.object({
|
||||
status: z
|
||||
.nativeEnum(WorkflowStatus)
|
||||
.optional()
|
||||
.describe('Filter by status (DRAFT, ACTIVE, DEACTIVATED)'),
|
||||
limit: z.number().int().min(1).max(100).optional().default(50),
|
||||
offset: z.number().int().min(0).optional().default(0),
|
||||
});
|
||||
|
||||
type ListWorkflowsInput = z.infer<typeof listWorkflowsSchema>;
|
||||
|
||||
export const createListWorkflowsTool = (
|
||||
deps: Pick<WorkflowToolDependencies, 'globalWorkspaceOrmManager'>,
|
||||
context: ListWorkflowsToolContext,
|
||||
) => ({
|
||||
name: 'list_workflows' as const,
|
||||
description:
|
||||
'List all workflows in the workspace. Supports filtering by status and pagination.',
|
||||
inputSchema: listWorkflowsSchema,
|
||||
execute: async (parameters: ListWorkflowsInput) => {
|
||||
try {
|
||||
const authContext = buildSystemAuthContext(context.workspaceId);
|
||||
|
||||
return await deps.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
async () => {
|
||||
const workflowRepository =
|
||||
await deps.globalWorkspaceOrmManager.getRepository<WorkflowWorkspaceEntity>(
|
||||
context.workspaceId,
|
||||
'workflow',
|
||||
context.rolePermissionConfig,
|
||||
);
|
||||
|
||||
const queryBuilder =
|
||||
workflowRepository.createQueryBuilder('workflow');
|
||||
|
||||
if (parameters.status) {
|
||||
queryBuilder.where(':status = ANY(workflow.statuses)', {
|
||||
status: parameters.status,
|
||||
});
|
||||
}
|
||||
|
||||
queryBuilder
|
||||
.orderBy('workflow.createdAt', 'DESC')
|
||||
.take(parameters.limit)
|
||||
.skip(parameters.offset);
|
||||
|
||||
const [workflows, totalCount] = await queryBuilder.getManyAndCount();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
workflows: workflows.map((workflow) => ({
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
statuses: workflow.statuses,
|
||||
lastPublishedVersionId: workflow.lastPublishedVersionId,
|
||||
createdAt: workflow.createdAt,
|
||||
updatedAt: workflow.updatedAt,
|
||||
})),
|
||||
totalCount,
|
||||
};
|
||||
},
|
||||
authContext,
|
||||
);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
message: `Failed to list workflows: ${errorMessage}`,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user