feat(workflow-as-mcp): added ability to deploy workflows as mcp servers and mcp tools (#2415)

* added a workflow as mcp

* fixed the issue of UI rendering for deleted mcp servers

* fixing lint issues

* using mcn components

* fixing merge conflicts

* fix

* fix lint errors

* refactored code to use hasstartblock from the tirgger utils

* removing unecessary auth

* using official mcp sdk and added description fields

* using normalised input schema function

* ui fixes part 1

* remove migration before merge

* fix merge conflicts

* remove migration to prep merge

* re-add migration

* cleanup code to use mcp sdk types

* fix discovery calls

* add migration

* ui improvements

* fix lint

* fix types

* fix lint

* fix spacing

* remove migration to prep merge

* add migration back

* fix imports

* fix tool refresh ux

* fix test failures

* fix tests

* cleanup code

* styling improvements, ability to edit mcp server description, etc

* fixed ui in light mode api keys modal

* update docs

* deprecated unused input components, shifted to emcn

* updated playground, simplified components

* move images and videos

* updated more docs images

---------

Co-authored-by: priyanshu.solanki <priyanshu.solanki@saviynt.com>
Co-authored-by: Siddharth Ganesan <siddharthganesan@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: waleed <walif6@gmail.com>
This commit is contained in:
Priyanshu Solanki
2025-12-30 17:52:50 -07:00
committed by GitHub
parent 34bc115468
commit c77268c13d
100 changed files with 13379 additions and 1439 deletions

View File

@@ -0,0 +1,30 @@
CREATE TABLE "workflow_mcp_server" (
"id" text PRIMARY KEY NOT NULL,
"workspace_id" text NOT NULL,
"created_by" text NOT NULL,
"name" text NOT NULL,
"description" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "workflow_mcp_tool" (
"id" text PRIMARY KEY NOT NULL,
"server_id" text NOT NULL,
"workflow_id" text NOT NULL,
"tool_name" text NOT NULL,
"tool_description" text,
"parameter_schema" json DEFAULT '{}' NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "workflow_mcp_server" ADD CONSTRAINT "workflow_mcp_server_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "workflow_mcp_server" ADD CONSTRAINT "workflow_mcp_server_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "workflow_mcp_tool" ADD CONSTRAINT "workflow_mcp_tool_server_id_workflow_mcp_server_id_fk" FOREIGN KEY ("server_id") REFERENCES "public"."workflow_mcp_server"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "workflow_mcp_tool" ADD CONSTRAINT "workflow_mcp_tool_workflow_id_workflow_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflow"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "workflow_mcp_server_workspace_id_idx" ON "workflow_mcp_server" USING btree ("workspace_id");--> statement-breakpoint
CREATE INDEX "workflow_mcp_server_created_by_idx" ON "workflow_mcp_server" USING btree ("created_by");--> statement-breakpoint
CREATE INDEX "workflow_mcp_tool_server_id_idx" ON "workflow_mcp_tool" USING btree ("server_id");--> statement-breakpoint
CREATE INDEX "workflow_mcp_tool_workflow_id_idx" ON "workflow_mcp_tool" USING btree ("workflow_id");--> statement-breakpoint
CREATE UNIQUE INDEX "workflow_mcp_tool_server_workflow_unique" ON "workflow_mcp_tool" USING btree ("server_id","workflow_id");

File diff suppressed because it is too large Load Diff

View File

@@ -932,6 +932,13 @@
"when": 1766607372265,
"tag": "0133_smiling_cargill",
"breakpoints": true
},
{
"idx": 134,
"version": "7",
"when": 1766779827389,
"tag": "0134_parallel_galactus",
"breakpoints": true
}
]
}

View File

@@ -1688,7 +1688,61 @@ export const ssoProvider = pgTable(
})
)
// Usage logging for tracking individual billable operations
/**
* Workflow MCP Servers - User-created MCP servers that expose workflows as tools.
* These servers are accessible by external MCP clients via API key authentication.
*/
export const workflowMcpServer = pgTable(
'workflow_mcp_server',
{
id: text('id').primaryKey(),
workspaceId: text('workspace_id')
.notNull()
.references(() => workspace.id, { onDelete: 'cascade' }),
createdBy: text('created_by')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
description: text('description'),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
},
(table) => ({
workspaceIdIdx: index('workflow_mcp_server_workspace_id_idx').on(table.workspaceId),
createdByIdx: index('workflow_mcp_server_created_by_idx').on(table.createdBy),
})
)
/**
* Workflow MCP Tools - Workflows registered as tools within a Workflow MCP Server.
* Each tool maps to a deployed workflow's execute endpoint.
*/
export const workflowMcpTool = pgTable(
'workflow_mcp_tool',
{
id: text('id').primaryKey(),
serverId: text('server_id')
.notNull()
.references(() => workflowMcpServer.id, { onDelete: 'cascade' }),
workflowId: text('workflow_id')
.notNull()
.references(() => workflow.id, { onDelete: 'cascade' }),
toolName: text('tool_name').notNull(),
toolDescription: text('tool_description'),
parameterSchema: json('parameter_schema').notNull().default('{}'),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
},
(table) => ({
serverIdIdx: index('workflow_mcp_tool_server_id_idx').on(table.serverId),
workflowIdIdx: index('workflow_mcp_tool_workflow_id_idx').on(table.workflowId),
serverWorkflowUnique: uniqueIndex('workflow_mcp_tool_server_workflow_unique').on(
table.serverId,
table.workflowId
),
})
)
export const usageLogCategoryEnum = pgEnum('usage_log_category', ['model', 'fixed'])
export const usageLogSourceEnum = pgEnum('usage_log_source', ['workflow', 'wand', 'copilot'])
@@ -1700,38 +1754,26 @@ export const usageLog = pgTable(
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
// Charge category: 'model' (token-based) or 'fixed' (flat fee)
category: usageLogCategoryEnum('category').notNull(),
// What generated this charge: 'workflow', 'wand', 'copilot'
source: usageLogSourceEnum('source').notNull(),
// For model charges: model name (e.g., 'gpt-4o', 'claude-4.5-opus')
// For fixed charges: charge type (e.g., 'execution_fee', 'search_query')
description: text('description').notNull(),
// Category-specific metadata (e.g., tokens for 'model' category)
metadata: jsonb('metadata'),
// Cost in USD
cost: decimal('cost').notNull(),
// Optional context references
workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'set null' }),
workflowId: text('workflow_id').references(() => workflow.id, { onDelete: 'set null' }),
executionId: text('execution_id'),
// Timestamp
createdAt: timestamp('created_at').notNull().defaultNow(),
},
(table) => ({
// Index for querying user's usage history (most common query)
userCreatedAtIdx: index('usage_log_user_created_at_idx').on(table.userId, table.createdAt),
// Index for filtering by source
sourceIdx: index('usage_log_source_idx').on(table.source),
// Index for workspace-specific queries
workspaceIdIdx: index('usage_log_workspace_id_idx').on(table.workspaceId),
// Index for workflow-specific queries
workflowIdIdx: index('usage_log_workflow_id_idx').on(table.workflowId),
})
)