Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions apps/sim/app/api/mcp/copilot/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { validateOAuthAccessToken } from '@/lib/auth/oauth-token'
import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription'
import { generateWorkspaceContext } from '@/lib/copilot/chat/workspace-context'
import { ORCHESTRATION_TIMEOUT_MS, SIM_AGENT_API_URL } from '@/lib/copilot/constants'
import { runHeadlessCopilotLifecycle } from '@/lib/copilot/request/lifecycle/headless'
import { orchestrateSubagentStream } from '@/lib/copilot/request/subagent'
Expand Down Expand Up @@ -136,14 +137,14 @@ When the user refers to a workflow by name or description ("the email one", "my
### Organization

- \`rename_workflow\` — rename a workflow
- \`move_workflow\` — move a workflow into a folder (or root with null)
- \`move_folder\` — nest a folder inside another (or root with null)
- \`move_workflow\` — move a workflow into a folder (or back to root by clearing the folder id)
- \`move_folder\` — nest a folder inside another (or move it back to root by clearing the parent id)
- \`create_folder(name, parentId)\` — create nested folder hierarchies

### Key Rules

- You can test workflows immediately after building — deployment is only needed for external access (API, chat, MCP).
- All workflow-scoped copilot tools require \`workflowId\`.
- Tools that operate on a specific workflow such as \`sim_workflow\`, \`sim_test\`, \`sim_deploy\`, and workflow-scoped \`sim_info\` requests require \`workflowId\`.
- If the user reports errors, route through \`sim_workflow\` and ask it to reproduce, inspect logs, and fix the issue end to end.
- Variable syntax: \`<blockname.field>\` for block outputs, \`{{ENV_VAR}}\` for env vars.
`
Expand Down Expand Up @@ -667,10 +668,10 @@ async function handleDirectToolCall(
}

/**
* Build mode uses the main chat orchestrator with the 'fast' command instead of
* the subagent endpoint. In Go, 'workflow' is not a registered subagent — it's a mode
* (ModeFast) on the main chat processor that bypasses subagent orchestration and
* executes all tools directly.
* Build mode uses the main /api/mcp orchestrator instead of /api/subagent/workflow.
* The main agent still delegates workflow work to the workflow subagent inside Go;
* this helper simply uses the full headless lifecycle so build requests behave like
* the primary MCP chat flow.
*/
async function handleBuildToolCall(
args: Record<string, unknown>,
Expand All @@ -680,6 +681,8 @@ async function handleBuildToolCall(
try {
const requestText = (args.request as string) || JSON.stringify(args)
const workflowId = args.workflowId as string | undefined
let resolvedWorkflowName: string | undefined
let resolvedWorkspaceId: string | undefined

const resolved = workflowId
? await (async () => {
Expand All @@ -688,15 +691,21 @@ async function handleBuildToolCall(
userId,
action: 'read',
})
resolvedWorkflowName = authorization.workflow?.name || undefined
resolvedWorkspaceId = authorization.workflow?.workspaceId || undefined
return authorization.allowed
? { status: 'resolved' as const, workflowId }
? { status: 'resolved' as const, workflowId, workflowName: resolvedWorkflowName }
: {
status: 'not_found' as const,
message: 'workflowId is required for build. Call create_workflow first.',
}
})()
: await resolveWorkflowIdForUser(userId)

if (resolved.status === 'resolved') {
resolvedWorkflowName ||= resolved.workflowName
}

if (!resolved || resolved.status !== 'resolved') {
return {
content: [
Expand All @@ -719,10 +728,29 @@ async function handleBuildToolCall(
}

const chatId = generateId()
const executionContext = await prepareExecutionContext(userId, resolved.workflowId, chatId, {
workspaceId: resolvedWorkspaceId,
})
resolvedWorkspaceId = executionContext.workspaceId
let workspaceContext: string | undefined
if (resolvedWorkspaceId) {
try {
workspaceContext = await generateWorkspaceContext(resolvedWorkspaceId, userId)
} catch (error) {
logger.warn('Failed to generate workspace context for build tool call', {
workflowId: resolved.workflowId,
workspaceId: resolvedWorkspaceId,
error: error instanceof Error ? error.message : String(error),
})
}
}

const requestPayload = {
message: requestText,
workflowId: resolved.workflowId,
...(resolvedWorkflowName ? { workflowName: resolvedWorkflowName } : {}),
...(resolvedWorkspaceId ? { workspaceId: resolvedWorkspaceId } : {}),
...(workspaceContext ? { workspaceContext } : {}),
userId,
model: DEFAULT_COPILOT_MODEL,
mode: 'agent',
Expand All @@ -734,8 +762,10 @@ async function handleBuildToolCall(
const result = await runHeadlessCopilotLifecycle(requestPayload, {
userId,
workflowId: resolved.workflowId,
workspaceId: resolvedWorkspaceId,
chatId,
goRoute: '/api/mcp',
executionContext,
autoExecuteTools: true,
timeout: ORCHESTRATION_TIMEOUT_MS,
interactive: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ComponentType, SVGProps } from 'react'
import {
Asterisk,
Blimp,
Bug,
Calendar,
Database,
Eye,
Expand Down Expand Up @@ -44,6 +45,7 @@ const TOOL_ICONS: Record<string, IconComponent> = {
create_workflow: Layout,
edit_workflow: Pencil,
workflow: Hammer,
debug: Bug,
run: PlayOutline,
deploy: Rocket,
auth: Integration,
Expand Down
3 changes: 3 additions & 0 deletions apps/sim/app/workspace/[workspaceId]/home/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Agent,
Auth,
CreateWorkflow,
Debug,
Deploy,
EditWorkflow,
FunctionExecute,
Expand Down Expand Up @@ -161,6 +162,7 @@ export interface ChatMessage {

export const SUBAGENT_LABELS: Record<string, string> = {
workflow: 'Workflow Agent',
debug: 'Debug Agent',
deploy: 'Deploy Agent',
auth: 'Auth Agent',
research: 'Research Agent',
Expand Down Expand Up @@ -200,6 +202,7 @@ export const TOOL_UI_METADATA: Record<string, ToolTitleMetadata> = {
[CreateWorkflow.id]: { title: 'Creating workflow' },
[EditWorkflow.id]: { title: 'Editing workflow' },
[Workflow.id]: { title: 'Workflow Agent' },
[Debug.id]: { title: 'Debug Agent' },
[RUN_SUBAGENT_ID]: { title: 'Run Agent' },
[Deploy.id]: { title: 'Deploy Agent' },
[Auth.id]: { title: 'Auth Agent' },
Expand Down
56 changes: 55 additions & 1 deletion apps/sim/lib/copilot/generated/tool-catalog-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface ToolCatalogEntry {
| 'create_job'
| 'create_workflow'
| 'create_workspace_mcp_server'
| 'debug'
| 'delete_file'
| 'delete_folder'
| 'delete_workflow'
Expand Down Expand Up @@ -70,6 +71,7 @@ export interface ToolCatalogEntry {
| 'respond'
| 'restore_resource'
| 'revert_to_version'
| 'run'
| 'run_block'
| 'run_from_block'
| 'run_workflow'
Expand Down Expand Up @@ -105,6 +107,7 @@ export interface ToolCatalogEntry {
| 'create_job'
| 'create_workflow'
| 'create_workspace_mcp_server'
| 'debug'
| 'delete_file'
| 'delete_folder'
| 'delete_workflow'
Expand Down Expand Up @@ -158,6 +161,7 @@ export interface ToolCatalogEntry {
| 'respond'
| 'restore_resource'
| 'revert_to_version'
| 'run'
| 'run_block'
| 'run_from_block'
| 'run_workflow'
Expand Down Expand Up @@ -187,11 +191,13 @@ export interface ToolCatalogEntry {
subagentId?:
| 'agent'
| 'auth'
| 'debug'
| 'deploy'
| 'file'
| 'job'
| 'knowledge'
| 'research'
| 'run'
| 'superagent'
| 'table'
| 'workflow'
Expand Down Expand Up @@ -444,6 +450,31 @@ export const CreateWorkspaceMcpServer: ToolCatalogEntry = {
requiredPermission: 'admin',
}

export const Debug: ToolCatalogEntry = {
id: 'debug',
name: 'debug',
route: 'subagent',
mode: 'async',
parameters: {
properties: {
context: {
description:
'Pre-gathered context: workflow state JSON, block schemas, error logs. The debug agent will skip re-reading anything included here.',
type: 'string',
},
request: {
description:
'What to debug. Include error messages, block IDs, and any context about the failure.',
type: 'string',
},
},
required: ['request'],
type: 'object',
},
subagentId: 'debug',
internal: true,
}

export const DeleteFile: ToolCatalogEntry = {
id: 'delete_file',
name: 'delete_file',
Expand Down Expand Up @@ -2039,7 +2070,8 @@ export const Read: ToolCatalogEntry = {
},
path: {
type: 'string',
description: "Path to the file to read (e.g. 'workflows/My Workflow/state.json').",
description:
"Path to the file to read (e.g. 'workflows/My Workflow/state.json' or 'workflows/Projects/Q1/My Workflow/state.json').",
},
},
required: ['path'],
Expand Down Expand Up @@ -2231,6 +2263,26 @@ export const RevertToVersion: ToolCatalogEntry = {
requiredPermission: 'admin',
}

export const Run: ToolCatalogEntry = {
id: 'run',
name: 'run',
route: 'subagent',
mode: 'async',
parameters: {
properties: {
context: {
description: 'Pre-gathered context: workflow state, block IDs, input requirements.',
type: 'string',
},
request: { description: 'What to run or what logs to check.', type: 'string' },
},
required: ['request'],
type: 'object',
},
subagentId: 'run',
internal: true,
}

export const RunBlock: ToolCatalogEntry = {
id: 'run_block',
name: 'run_block',
Expand Down Expand Up @@ -3264,6 +3316,7 @@ export const TOOL_CATALOG: Record<string, ToolCatalogEntry> = {
[CreateJob.id]: CreateJob,
[CreateWorkflow.id]: CreateWorkflow,
[CreateWorkspaceMcpServer.id]: CreateWorkspaceMcpServer,
[Debug.id]: Debug,
[DeleteFile.id]: DeleteFile,
[DeleteFolder.id]: DeleteFolder,
[DeleteWorkflow.id]: DeleteWorkflow,
Expand Down Expand Up @@ -3317,6 +3370,7 @@ export const TOOL_CATALOG: Record<string, ToolCatalogEntry> = {
[Respond.id]: Respond,
[RestoreResource.id]: RestoreResource,
[RevertToVersion.id]: RevertToVersion,
[Run.id]: Run,
[RunBlock.id]: RunBlock,
[RunFromBlock.id]: RunFromBlock,
[RunWorkflow.id]: RunWorkflow,
Expand Down
39 changes: 38 additions & 1 deletion apps/sim/lib/copilot/generated/tool-schemas-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,25 @@ export const TOOL_RUNTIME_SCHEMAS: Record<string, ToolRuntimeSchemaEntry> = {
},
resultSchema: undefined,
},
debug: {
parameters: {
properties: {
context: {
description:
'Pre-gathered context: workflow state JSON, block schemas, error logs. The debug agent will skip re-reading anything included here.',
type: 'string',
},
request: {
description:
'What to debug. Include error messages, block IDs, and any context about the failure.',
type: 'string',
},
},
required: ['request'],
type: 'object',
},
resultSchema: undefined,
},
delete_file: {
parameters: {
type: 'object',
Expand Down Expand Up @@ -1872,7 +1891,8 @@ export const TOOL_RUNTIME_SCHEMAS: Record<string, ToolRuntimeSchemaEntry> = {
},
path: {
type: 'string',
description: "Path to the file to read (e.g. 'workflows/My Workflow/state.json').",
description:
"Path to the file to read (e.g. 'workflows/My Workflow/state.json' or 'workflows/Projects/Q1/My Workflow/state.json').",
},
},
required: ['path'],
Expand Down Expand Up @@ -2070,6 +2090,23 @@ export const TOOL_RUNTIME_SCHEMAS: Record<string, ToolRuntimeSchemaEntry> = {
},
resultSchema: undefined,
},
run: {
parameters: {
properties: {
context: {
description: 'Pre-gathered context: workflow state, block IDs, input requirements.',
type: 'string',
},
request: {
description: 'What to run or what logs to check.',
type: 'string',
},
},
required: ['request'],
type: 'object',
},
resultSchema: undefined,
},
run_block: {
parameters: {
type: 'object',
Expand Down
Loading
Loading