Skip to main content
Glama

relay_workflow_run

Execute multi-step AI workflows with reduced context usage by keeping intermediate results in the workflow engine, supporting multiple model calls and tool integrations.

Instructions

Execute a multi-step AI workflow. Intermediate results stay in the workflow engine (not your context), providing 90%+ context reduction on complex pipelines. Use for any task requiring multiple model calls or tool integrations. Cost tracks your provider bills, not RelayPlane fees - we're BYOK.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesWorkflow name for tracing
stepsYesWorkflow steps
inputYesInput data (accessible via {{input.field}})

Implementation Reference

  • Core handler function that executes the relay_workflow_run tool: performs topological sort on steps, interpolates prompts, executes AI steps via relayRun or MCP, handles budget/costs, stores run results, computes context reduction percentage.
    export async function relayWorkflowRun(input: RelayWorkflowRunInput): Promise<RelayWorkflowRunResponse> {
      const startTime = Date.now();
      const runId = generateRunId();
      const config = getConfig();
    
      const stepResults: Record<string, WorkflowStepResult> = {};
      let totalTokens = 0;
      let totalCost = 0;
    
      try {
        // Validate and sort steps
        const sortedSteps = topologicalSort(input.steps);
    
        // Estimate cost for AI steps and check budget
        const aiSteps = sortedSteps.filter(s => s.model && s.prompt);
        const estimatedCost = estimateWorkflowCost(aiSteps);
        const budgetCheck = checkBudget(estimatedCost);
    
        if (!budgetCheck.allowed) {
          throw new Error(budgetCheck.error);
        }
    
        // Check all providers are configured
        for (const step of aiSteps) {
          const provider = step.model!.split(':')[0];
          if (!isProviderConfigured(provider)) {
            throw new Error(
              `Provider "${provider}" (step "${step.name}") is not configured. Set ${provider.toUpperCase()}_API_KEY environment variable.`
            );
          }
        }
    
        // Execute steps in order
        const context = { input: input.input, steps: {} as Record<string, any> };
    
        for (const step of sortedSteps) {
          const stepStart = Date.now();
    
          try {
            if (step.model && step.prompt) {
              // AI step - use relay_run
              const interpolatedPrompt = interpolate(step.prompt, context);
              const interpolatedSystemPrompt = step.systemPrompt
                ? interpolate(step.systemPrompt, context)
                : undefined;
    
              const result = await relayRun({
                model: step.model,
                prompt: interpolatedPrompt,
                systemPrompt: interpolatedSystemPrompt,
                schema: step.schema,
              });
    
              if (!result.success) {
                throw new Error(result.error?.message || 'Step execution failed');
              }
    
              context.steps[step.name] = { output: result.output };
              stepResults[step.name] = {
                success: true,
                output: result.output,
                durationMs: result.durationMs,
                usage: {
                  promptTokens: result.usage.promptTokens,
                  completionTokens: result.usage.completionTokens,
                  estimatedProviderCostUsd: result.usage.estimatedProviderCostUsd,
                },
              };
    
              totalTokens += result.usage.totalTokens;
              totalCost += result.usage.estimatedProviderCostUsd;
            } else if (step.mcp) {
              // MCP step - placeholder for now
              // In a full implementation, this would call the MCP tool
              const stepDuration = Date.now() - stepStart;
              context.steps[step.name] = { output: { message: 'MCP step executed (placeholder)' } };
              stepResults[step.name] = {
                success: true,
                output: { message: 'MCP step executed (placeholder)' },
                durationMs: stepDuration,
              };
            } else {
              // Transform step or other type
              const stepDuration = Date.now() - stepStart;
              context.steps[step.name] = { output: null };
              stepResults[step.name] = {
                success: true,
                output: null,
                durationMs: stepDuration,
              };
            }
          } catch (error) {
            const stepDuration = Date.now() - stepStart;
            const errorMessage = error instanceof Error ? error.message : String(error);
    
            stepResults[step.name] = {
              success: false,
              output: null,
              durationMs: stepDuration,
              error: {
                code: 'STEP_EXECUTION_ERROR',
                message: errorMessage,
              },
            };
    
            throw new Error(`Step "${step.name}" failed: ${errorMessage}`);
          }
        }
    
        const totalDurationMs = Date.now() - startTime;
        const lastStep = sortedSteps[sortedSteps.length - 1];
        const finalOutput = stepResults[lastStep?.name]?.output;
    
        const response: RelayWorkflowRunResponse = {
          success: true,
          steps: stepResults,
          finalOutput,
          totalUsage: {
            totalTokens,
            estimatedProviderCostUsd: totalCost,
          },
          totalDurationMs,
          runId,
          traceUrl: `${config.traceUrlBase}/${runId}`,
          contextReduction: estimateContextReduction(sortedSteps, stepResults),
        };
    
        // Store run
        addRun({
          runId,
          type: 'workflow',
          workflowName: input.name,
          success: true,
          startTime: new Date(startTime),
          endTime: new Date(),
          durationMs: totalDurationMs,
          usage: {
            promptTokens: 0, // Aggregated in steps
            completionTokens: 0,
            totalTokens,
            estimatedProviderCostUsd: totalCost,
          },
          input: input.input,
          output: finalOutput,
          steps: stepResults,
          contextReduction: response.contextReduction,
        });
    
        return response;
      } catch (error) {
        const totalDurationMs = Date.now() - startTime;
        const errorMessage = error instanceof Error ? error.message : String(error);
    
        const response: RelayWorkflowRunResponse = {
          success: false,
          steps: stepResults,
          finalOutput: null,
          totalUsage: {
            totalTokens,
            estimatedProviderCostUsd: totalCost,
          },
          totalDurationMs,
          runId,
          traceUrl: `${config.traceUrlBase}/${runId}`,
          contextReduction: 'N/A (workflow failed)',
          error: {
            code: 'WORKFLOW_ERROR',
            message: errorMessage,
          },
        };
    
        // Store failed run
        addRun({
          runId,
          type: 'workflow',
          workflowName: input.name,
          success: false,
          startTime: new Date(startTime),
          endTime: new Date(),
          durationMs: totalDurationMs,
          usage: {
            promptTokens: 0,
            completionTokens: 0,
            totalTokens,
            estimatedProviderCostUsd: totalCost,
          },
          input: input.input,
          steps: stepResults,
          error: errorMessage,
        });
    
        return response;
      }
    }
  • MCP tool definition object with name, description, and detailed inputSchema for validation in MCP protocol.
    export const relayWorkflowRunDefinition = {
      name: 'relay_workflow_run',
      description:
        "Execute a multi-step AI workflow. Intermediate results stay in the workflow engine (not your context), providing 90%+ context reduction on complex pipelines. Use for any task requiring multiple model calls or tool integrations. Cost tracks your provider bills, not RelayPlane fees - we're BYOK.",
      inputSchema: {
        type: 'object' as const,
        properties: {
          name: {
            type: 'string',
            description: 'Workflow name for tracing',
          },
          steps: {
            type: 'array',
            description: 'Workflow steps',
            items: {
              type: 'object',
              properties: {
                name: { type: 'string', description: 'Step name' },
                model: { type: 'string', description: 'Model in provider:model format' },
                prompt: { type: 'string', description: 'Prompt template (supports {{input.field}} and {{steps.stepName.output}})' },
                systemPrompt: { type: 'string', description: 'Optional system prompt' },
                depends: { type: 'array', items: { type: 'string' }, description: 'Steps this step depends on' },
                mcp: { type: 'string', description: 'MCP tool in server:tool format' },
                params: { type: 'object', description: 'MCP tool parameters' },
                schema: { type: 'object', description: 'JSON schema for structured output' },
              },
              required: ['name'],
            },
          },
          input: {
            type: 'object',
            description: 'Input data (accessible via {{input.field}})',
          },
        },
        required: ['name', 'steps', 'input'],
      },
    };
  • Zod schema for input validation of relay_workflow_run parameters.
    export const relayWorkflowRunSchema = z.object({
      name: z.string().describe('Workflow name for tracing'),
      steps: z.array(workflowStepSchema).describe('Workflow steps'),
      input: z.object({}).passthrough().describe('Input data (accessible via {{input.field}})'),
    });
  • src/server.ts:59-67 (registration)
    Registration of relayWorkflowRunDefinition in the TOOLS array provided to MCP listTools handler.
    const TOOLS = [
      relayModelsListDefinition,
      relayRunDefinition,
      relayWorkflowRunDefinition,
      relayWorkflowValidateDefinition,
      relaySkillsListDefinition,
      relayRunsListDefinition,
      relayRunGetDefinition,
    ];
  • src/server.ts:119-122 (registration)
    Dispatch case in MCP callTool handler that invokes the relay_workflow_run tool implementation.
    case 'relay_workflow_run': {
      const parsed = relayWorkflowRunSchema.parse(args);
      result = await relayWorkflowRun(parsed);
      break;
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes key behaviors: intermediate results stay in the workflow engine (not the agent's context), provides 90%+ context reduction, and clarifies cost tracking (provider bills, not RelayPlane fees). However, it doesn't mention error handling, timeouts, or output format details, leaving some behavioral aspects unclear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, with the core purpose in the first sentence. All sentences add value: context reduction benefits, usage guidelines, and cost clarification. However, the cost explanation could be slightly more concise, and the structure might benefit from clearer separation of key points.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (multi-step workflows with nested objects) and no annotations or output schema, the description does well to cover purpose, usage, and key behavioral traits. It addresses context reduction and cost tracking, which are critical for this type of tool. However, without an output schema, it doesn't describe return values or error formats, leaving some gaps in completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds minimal parameter semantics beyond the schema—it mentions 'multi-step AI workflow' which aligns with the steps parameter, and references input accessibility via {{input.field}}, but doesn't provide additional syntax or usage details. Baseline 3 is appropriate when the schema does most of the work.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Execute a multi-step AI workflow' with specific details about intermediate results staying in the workflow engine and providing 90%+ context reduction. It distinguishes from siblings by emphasizing multi-step workflows requiring multiple model calls or tool integrations, unlike simpler run or list tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage guidance: 'Use for any task requiring multiple model calls or tool integrations.' This clearly indicates when to use this tool versus simpler alternatives like relay_run (likely for single-step execution) or list tools. It also clarifies cost implications, helping users decide based on billing considerations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/RelayPlane/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server