Skip to content

Commit 6c0cc7e

Browse files
authored
Merge pull request #157 from khawkins/workflow_customization
Making various workflow entities configurable
2 parents 9d0f88e + 5b9478f commit 6c0cc7e

File tree

11 files changed

+675
-226
lines changed

11 files changed

+675
-226
lines changed

docs/9_mcp_workflow_engine_extraction/design.md

Lines changed: 132 additions & 164 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/mcp-workflow/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@salesforce/magen-mcp-workflow",
3-
"version": "0.0.4",
3+
"version": "0.0.5",
44
"type": "module",
55
"files": [
66
"dist",

packages/mcp-workflow/src/common/metadata.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ export interface NodeGuidanceData<TResultSchema extends z.ZodObject<z.ZodRawShap
7777
* LLM compliance with the expected structure.
7878
*/
7979
exampleOutput?: string;
80+
/**
81+
* Optional custom guidance for the LLM to return results to the orchestrator.
82+
*
83+
* When provided, this replaces the orchestrator's default "return to orchestrator"
84+
* prompt. The function receives only `workflowStateData` (the runtime session state
85+
* that the producer doesn't have at construction time). The producer already owns
86+
* `resultSchema` and `exampleOutput` as sibling properties on this same struct,
87+
* so they can be captured in the closure if needed.
88+
*
89+
* Ensure this custom guidance properly instructs the LLM to return the workflow
90+
* to the orchestrator, or the workflow will likely be broken.
91+
*
92+
* @param workflowStateData - The workflow state data to round-trip back to the orchestrator
93+
* @returns The return guidance prompt string
94+
*/
95+
returnGuidance?: (workflowStateData: WorkflowStateData) => string;
8096
}
8197

8298
/**

packages/mcp-workflow/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export { type BaseGraphConfig, type WorkflowRunnableConfig } from './common/grap
6868
export {
6969
BaseNode,
7070
AbstractToolNode,
71+
UserInputExtractionNode,
7172
createGetUserInputNode,
7273
createUserInputExtractionNode,
7374
type GetUserInputNodeOptions,
@@ -78,7 +79,7 @@ export {
7879
export { CheckPropertiesFulfilledRouter } from './routers/index.js';
7980

8081
// Base Service Classes
81-
export { AbstractService } from './services/index.js';
82+
export { AbstractService, InputExtractionService } from './services/index.js';
8283

8384
// Checkpointing Infrastructure
8485
export {
@@ -93,6 +94,7 @@ export {
9394
export {
9495
OrchestratorTool,
9596
createOrchestratorToolMetadata,
97+
type DefaultOrchestratorInputSchema,
9698
type OrchestratorConfig,
9799
type OrchestratorInput,
98100
type OrchestratorOutput,

packages/mcp-workflow/src/services/inputExtractionService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ export class InputExtractionService
204204
* @param propertiesToExtract - Array of properties to extract
205205
* @returns The guidance prompt string
206206
*/
207-
private generateTaskGuidance(
207+
protected generateTaskGuidance(
208208
userUtterance: unknown,
209209
propertiesToExtract: Array<{ propertyName: string; description: string }>
210210
): string {

packages/mcp-workflow/src/tools/orchestrator/config.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
66
*/
77

8+
import z from 'zod';
89
import { StateGraph } from '@langchain/langgraph';
910
import { Logger } from '../../logging/logger.js';
1011
import { WorkflowStateManager } from '../../checkpointing/workflowStateManager.js';
12+
import type { DefaultOrchestratorInputSchema } from './metadata.js';
1113

1214
/**
1315
* Orchestrator configuration interface
1416
*
15-
* Example usage:
17+
* @template TInputSchema - The Zod input schema type for the orchestrator MCP tool.
18+
* Defaults to the standard ORCHESTRATOR_INPUT_SCHEMA. When providing a custom schema,
19+
* the OrchestratorTool subclass MUST also override extractUserInput() and
20+
* extractWorkflowStateData() to map the custom schema's properties to the
21+
* orchestrator's semantic needs.
22+
*
23+
* Example usage (default schema):
1624
* ```
1725
* const MyWorkflowState = Annotation.Root({ messages: Annotation<string[]> });
1826
* const workflow = new StateGraph(MyWorkflowState)
@@ -27,8 +35,26 @@ import { WorkflowStateManager } from '../../checkpointing/workflowStateManager.j
2735
* workflow,
2836
* };
2937
* ```
38+
*
39+
* Example usage (custom schema):
40+
* ```
41+
* const MY_CUSTOM_SCHEMA = z.object({
42+
* payload: z.unknown().optional(),
43+
* sessionState: z.object({ thread_id: z.string() }).default({ thread_id: '' }),
44+
* });
45+
*
46+
* const config: OrchestratorConfig<typeof MY_CUSTOM_SCHEMA> = {
47+
* toolId: 'my-orchestrator',
48+
* title: 'My Orchestrator',
49+
* description: 'Orchestrates my workflow',
50+
* workflow,
51+
* inputSchema: MY_CUSTOM_SCHEMA,
52+
* };
53+
* ```
3054
*/
31-
export interface OrchestratorConfig {
55+
export interface OrchestratorConfig<
56+
TInputSchema extends z.ZodObject<z.ZodRawShape> = DefaultOrchestratorInputSchema,
57+
> {
3258
/** Unique tool identifier for MCP registration */
3359
toolId: string;
3460

@@ -48,6 +74,18 @@ export interface OrchestratorConfig {
4874
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4975
workflow: StateGraph<any, any, any, any, any, any, any, any>;
5076

77+
/**
78+
* Custom Zod input schema for the orchestrator MCP tool.
79+
*
80+
* Optional - defaults to ORCHESTRATOR_INPUT_SCHEMA, which provides the standard
81+
* `userInput` and `workflowStateData` properties.
82+
*
83+
* When providing a custom schema, the OrchestratorTool subclass MUST also override
84+
* `extractUserInput()` and `extractWorkflowStateData()` to map the custom schema's
85+
* properties to the orchestrator's semantic needs.
86+
*/
87+
inputSchema?: TInputSchema;
88+
5189
/**
5290
* Workflow state manager for checkpointing and persistence
5391
*

packages/mcp-workflow/src/tools/orchestrator/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
export { type OrchestratorConfig } from './config.js';
99
export {
10+
type DefaultOrchestratorInputSchema,
1011
type OrchestratorInput,
1112
type OrchestratorOutput,
1213
type OrchestratorToolMetadata,

packages/mcp-workflow/src/tools/orchestrator/metadata.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,31 +74,49 @@ export const ORCHESTRATOR_OUTPUT_SCHEMA = z.object({
7474

7575
export type OrchestratorOutput = z.infer<typeof ORCHESTRATOR_OUTPUT_SCHEMA>;
7676

77+
/**
78+
* Type alias for the default orchestrator input schema type.
79+
*
80+
* Used as the default generic parameter for OrchestratorConfig, OrchestratorToolMetadata,
81+
* and OrchestratorTool to avoid circular imports between config.ts and metadata.ts.
82+
*/
83+
export type DefaultOrchestratorInputSchema = typeof ORCHESTRATOR_INPUT_SCHEMA;
84+
7785
/**
7886
* Orchestrator tool metadata type
7987
* The metadata for the orchestrator tool (inputs/outputs)
88+
*
89+
* @template TInputSchema - The Zod input schema type. Defaults to ORCHESTRATOR_INPUT_SCHEMA.
8090
*/
81-
export type OrchestratorToolMetadata = ToolMetadata<
82-
typeof ORCHESTRATOR_INPUT_SCHEMA,
83-
typeof ORCHESTRATOR_OUTPUT_SCHEMA
84-
>;
91+
export type OrchestratorToolMetadata<
92+
TInputSchema extends z.ZodObject<z.ZodRawShape> = DefaultOrchestratorInputSchema,
93+
> = ToolMetadata<TInputSchema, typeof ORCHESTRATOR_OUTPUT_SCHEMA>;
8594

8695
/**
87-
* Factory function to create orchestrator tool metadata from configuration
96+
* Factory function to create orchestrator tool metadata from configuration.
8897
* Takes the consumer-provided config and creates the tool metadata with
89-
* standardized input/output schemas.
98+
* the appropriate input/output schemas.
99+
*
100+
* When a custom inputSchema is provided in the config, it is used instead of
101+
* the default ORCHESTRATOR_INPUT_SCHEMA.
90102
*
103+
* @template TInputSchema - The Zod input schema type. Defaults to ORCHESTRATOR_INPUT_SCHEMA.
91104
* @param config - The orchestrator configuration
92-
* @returns Tool metadata with the specified toolId, title, description
105+
* @returns Tool metadata with the specified toolId, title, description, and schemas
93106
*/
94-
export function createOrchestratorToolMetadata(
95-
config: OrchestratorConfig
96-
): OrchestratorToolMetadata {
107+
export function createOrchestratorToolMetadata<
108+
TInputSchema extends z.ZodObject<z.ZodRawShape> = DefaultOrchestratorInputSchema,
109+
>(config: OrchestratorConfig<TInputSchema>): OrchestratorToolMetadata<TInputSchema> {
110+
// The cast is necessary because TypeScript cannot prove that when config.inputSchema
111+
// is undefined, TInputSchema must be DefaultOrchestratorInputSchema (i.e. the generic
112+
// default and the runtime default independently track the same fallback). The cast is
113+
// safe: when inputSchema is omitted, TInputSchema defaults to typeof ORCHESTRATOR_INPUT_SCHEMA.
114+
const effectiveInputSchema = (config.inputSchema ?? ORCHESTRATOR_INPUT_SCHEMA) as TInputSchema;
97115
return {
98116
toolId: config.toolId,
99117
title: config.title,
100118
description: config.description,
101-
inputSchema: ORCHESTRATOR_INPUT_SCHEMA,
119+
inputSchema: effectiveInputSchema,
102120
outputSchema: ORCHESTRATOR_OUTPUT_SCHEMA,
103121
};
104122
}

0 commit comments

Comments
 (0)