Skip to content

Commit ff51efe

Browse files
committed
feat(inputs): support {{tmp}} resolution in input defaults
Enables workflow input defaults to use {{tmp}} variable which resolves to the workflow's scratch directory at runtime. Example usage: inputs: output_dir: type: str default: "{{tmp}}/output" # Resolves to scratch dir
1 parent b8c0964 commit ff51efe

3 files changed

Lines changed: 55 additions & 20 deletions

File tree

src/workflows_mcp/engine/workflow_runner.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from .metadata import Metadata
3232
from .orchestrator import BlockOrchestrator
3333
from .resolver import UnifiedVariableResolver
34-
from .schema import BlockDefinition, DependencySpec, WorkflowSchema
34+
from .schema import BlockDefinition, DependencySpec, InputType, WorkflowSchema
3535
from .secrets import EnvVarSecretProvider, SecretAuditLog, SecretRedactor
3636

3737
logger = logging.getLogger(__name__)
@@ -955,7 +955,7 @@ def _create_initial_execution_context(
955955
# Create execution context (workflow-level has no metadata - only blocks have metadata)
956956
workflow_start_time = datetime.now(UTC).isoformat()
957957
exec_context = Execution(
958-
inputs=self._merge_workflow_inputs(workflow, runtime_inputs),
958+
inputs=self._merge_workflow_inputs(workflow, runtime_inputs, scratch_dir),
959959
metadata=None, # Workflow-level Execution has no block metadata
960960
blocks={},
961961
depth=len(context.workflow_stack) if context else 0,
@@ -1076,37 +1076,41 @@ def _merge_workflow_inputs(
10761076
self,
10771077
workflow: WorkflowSchema,
10781078
runtime_inputs: dict[str, Any] | None,
1079+
scratch_dir: Path,
10791080
) -> dict[str, Any]:
1080-
"""Merge default and runtime inputs."""
1081+
"""Merge default and runtime inputs with {{tmp}} resolution and validation."""
10811082
merged = {}
10821083

1083-
# Apply defaults
1084+
# Apply defaults with {{tmp}} resolution
10841085
for input_name, input_decl in workflow.inputs.items():
10851086
if input_decl.default is not None:
1086-
merged[input_name] = input_decl.default
1087+
value = input_decl.default
1088+
if isinstance(value, str):
1089+
value = value.replace("{{tmp}}", str(scratch_dir))
1090+
merged[input_name] = value
10871091

10881092
# Override with runtime inputs
10891093
if runtime_inputs:
10901094
merged.update(runtime_inputs)
10911095

10921096
# Validate required inputs
1093-
missing_inputs = []
1094-
empty_string_inputs = []
1095-
for input_name, input_decl in workflow.inputs.items():
1096-
is_required = getattr(input_decl, "required", False)
1097-
if is_required:
1098-
if input_name not in merged:
1099-
missing_inputs.append(input_name)
1100-
elif input_decl.type.value == "str" and merged[input_name] == "":
1101-
empty_string_inputs.append(input_name)
1097+
missing = [
1098+
name for name, decl in workflow.inputs.items() if decl.required and name not in merged
1099+
]
1100+
empty = [
1101+
name
1102+
for name, decl in workflow.inputs.items()
1103+
if decl.required
1104+
and name in merged
1105+
and decl.type == InputType.STR
1106+
and merged[name] == ""
1107+
]
11021108

11031109
errors = []
1104-
if missing_inputs:
1105-
errors.append(f"Missing required inputs: {', '.join(missing_inputs)}")
1106-
if empty_string_inputs:
1107-
errors.append(
1108-
f"Required string inputs cannot be empty: {', '.join(empty_string_inputs)}"
1109-
)
1110+
if missing:
1111+
errors.append(f"Missing required inputs: {', '.join(missing)}")
1112+
if empty:
1113+
errors.append(f"Required string inputs cannot be empty: {', '.join(empty)}")
11101114

11111115
if errors:
11121116
raise ValueError("; ".join(errors))
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"outputs": {
3+
"resolved": true
4+
},
5+
"status": "success"
6+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: test-tmp-in-input-defaults
2+
description: Test that {{tmp}} is resolved in input defaults
3+
version: "1.0"
4+
tags: [test, tmp, input-defaults]
5+
6+
inputs:
7+
test_dir:
8+
type: str
9+
description: Output path with {{tmp}} default
10+
default: "{{tmp}}"
11+
required: false
12+
13+
blocks:
14+
- id: verify_tmp_resolved
15+
type: Shell
16+
inputs:
17+
working_dir: "{{tmp}}"
18+
command: |
19+
echo "marker" > test.txt
20+
[ -f "{{inputs.test_dir}}/test.txt" ]
21+
22+
outputs:
23+
resolved:
24+
value: "{{blocks.verify_tmp_resolved.succeeded}}"
25+
type: bool

0 commit comments

Comments
 (0)