Skip to content

Commit 330b728

Browse files
nhortonclaude
andauthored
Add workflow-level agent field for sub-agent delegation (#239)
* Add optional workflow-level `agent` field for sub-agent delegation When a workflow's `agent` field is set in job.yml, the `get_workflows` MCP response includes a `how_to_invoke` field instructing callers to delegate the entire workflow to a sub-agent of that type via the Task tool. This is distinct from the existing step-level `agent` field — this delegates the whole workflow, not individual steps. Changes: - Schema: added optional `agent` to `$defs/workflow` - Parser: `Workflow.agent` field + `from_dict` update - MCP: `WorkflowInfo.how_to_invoke` populated from `wf.agent` - Docs: updated mcp_interface.md with new field + changelog - Tests: 7 new tests across parser, validation, schemas, tools Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update flake.lock Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address review findings: specs, traceability, docs, lint fixes - Add JOBS-REQ-002.8.8 for optional workflow `agent` field - Add JOBS-REQ-001.2.6-8 for `how_to_invoke` in get_workflows response - Add traceability comments to all new tests - Update doc/architecture.md with workflow agent/how_to_invoke docs - Fix pre-existing mypy issues in test files (lint review) - Reformat f-string quoting in tools.py (ruff format) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Always populate how_to_invoke with invocation instructions Instead of returning null when no agent is set, how_to_invoke now always contains instructions: either to delegate via Task tool (when agent is set) or to call start_workflow MCP tool directly (default). Updated specs, docs, and tests to reflect the non-nullable field. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Use full MCP tool name in how_to_invoke instructions Reference mcp__plugin_deepwork_deepwork__start_workflow instead of generic "start_workflow DeepWork MCP tool" so agents can find the exact tool to call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b36f34f commit 330b728

14 files changed

Lines changed: 309 additions & 28 deletions

File tree

doc/architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,7 @@ Lists all available workflows from `.deepwork/jobs/`.
963963
964964
**Parameters**: None
965965
966-
**Returns**: List of jobs with their workflows, steps, and summaries
966+
**Returns**: List of jobs with their workflows, steps, and summaries. Each `WorkflowInfo` includes a `how_to_invoke` field with invocation instructions: when the workflow's `agent` field is set in job.yml, it directs callers to delegate via the Task tool; otherwise, it directs callers to use the `start_workflow` MCP tool directly.
967967
968968
#### 2. `start_workflow`
969969
Begins a new workflow session.

doc/mcp_interface.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ interface JobInfo {
4141
interface WorkflowInfo {
4242
name: string; // Workflow identifier
4343
summary: string; // Short description
44+
how_to_invoke: string; // Instructions for how to invoke this workflow
4445
}
4546
```
4647

@@ -397,6 +398,7 @@ Add to your `.mcp.json`:
397398

398399
| Version | Changes |
399400
|---------|---------|
401+
| 1.8.0 | Added `how_to_invoke` field to `WorkflowInfo` in `get_workflows` response. Always populated with invocation instructions: when a workflow's `agent` field is set, directs callers to delegate via the Task tool; otherwise, directs callers to use the `start_workflow` MCP tool directly. Also added optional `agent` field to workflow definitions in job.yml. |
400402
| 1.7.0 | Added `mark_review_as_passed` tool for review pass caching. Instruction files now include an "After Review" section with the review ID. Reviews with a `.passed` marker are automatically skipped by `get_review_instructions`. |
401403
| 1.6.0 | Added `get_configured_reviews` tool for listing configured review rules without running the full pipeline. Supports optional file-based filtering. |
402404
| 1.5.0 | Added `get_review_instructions` tool (originally named `review`) for running `.deepreview`-based code reviews via MCP. Added `--platform` CLI option to `serve` command. |

flake.lock

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

specs/deepwork/jobs/JOBS-REQ-001-mcp-workflow-tools.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ The DeepWork MCP server exposes four tools to AI agents via the Model Context Pr
2828
3. The tool MUST return a dictionary with a `jobs` key containing a list of job info objects.
2929
4. Each job info object MUST contain `name`, `summary`, and `workflows` fields.
3030
5. Each workflow info object MUST contain `name` and `summary` fields.
31-
6. The tool MUST also return an `errors` key containing a list of job load error objects for any jobs that failed to parse.
32-
7. Each job load error object MUST contain `job_name`, `job_dir`, and `error` fields.
33-
8. The tool MUST load jobs from all configured job folders (see JOBS-REQ-008).
31+
6. Each workflow info object MUST contain a `how_to_invoke` field (string) with invocation instructions.
32+
7. When a workflow's `agent` field is set in job.yml, `how_to_invoke` MUST contain instructions for delegating the workflow to a sub-agent of the specified type via the Task tool. The instructions MUST include the agent type, job name, and workflow name.
33+
8. When a workflow's `agent` field is not set, `how_to_invoke` MUST contain instructions to call the `start_workflow` MCP tool directly with the job name and workflow name.
34+
9. The tool MUST also return an `errors` key containing a list of job load error objects for any jobs that failed to parse.
35+
10. Each job load error object MUST contain `job_name`, `job_dir`, and `error` fields.
36+
11. The tool MUST load jobs from all configured job folders (see JOBS-REQ-008).
3437

3538
### JOBS-REQ-001.3: start_workflow Tool
3639

specs/deepwork/jobs/JOBS-REQ-002-job-definition-parsing.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Job definitions are YAML files (`job.yml`) that declare multi-step workflows for
8080
5. Each workflow step entry MUST be either a step ID string (sequential execution) or an array of step ID strings (concurrent execution).
8181
6. When a step entry is a string, it SHALL be parsed as a `WorkflowStepEntry` with `is_concurrent=False`.
8282
7. When a step entry is a list, it SHALL be parsed as a `WorkflowStepEntry` with `is_concurrent=True`.
83+
8. Workflows MAY have an optional `agent` field. When present, it MUST be a non-empty string specifying the agent type for delegating the entire workflow to a sub-agent.
8384

8485
### JOBS-REQ-002.9: Semantic Validation - Dependencies
8586

src/deepwork/jobs/job.schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
"items": {
8585
"$ref": "#/$defs/workflowStepEntry"
8686
}
87+
},
88+
"agent": {
89+
"type": "string",
90+
"minLength": 1,
91+
"description": "Agent type to run the entire workflow in (e.g., 'general-purpose'). When set, the workflow is designed to be delegated wholesale to a sub-agent of this type via the Task tool. This is for workflows where the entire execution should happen in a sub-agent — not for delegating individual steps within a workflow (use step-level 'agent' for that)."
8792
}
8893
}
8994
},

src/deepwork/jobs/mcp/schemas.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class WorkflowInfo(BaseModel):
5959

6060
name: str = Field(description="Workflow identifier")
6161
summary: str = Field(description="Short description of workflow")
62+
how_to_invoke: str = Field(
63+
description="Instructions for how to invoke this workflow (e.g., directly via MCP tools, or delegated to a sub-agent)",
64+
)
6265

6366

6467
class JobInfo(BaseModel):

src/deepwork/jobs/mcp/tools.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,29 @@ def _job_to_info(self, job: JobDefinition) -> JobInfo:
9797
JobInfo with workflow details
9898
"""
9999
# Convert workflows
100-
workflows = [
101-
WorkflowInfo(
102-
name=wf.name,
103-
summary=wf.summary,
100+
workflows = []
101+
for wf in job.workflows:
102+
if wf.agent:
103+
how_to_invoke = (
104+
f'Invoke as a Task using subagent_type="{wf.agent}" with a prompt '
105+
f"giving full context needed and instructions to call "
106+
f"`mcp__plugin_deepwork_deepwork__start_workflow` "
107+
f'(job_name="{job.name}", workflow_name="{wf.name}"). '
108+
f"If you do not have Task as an available tool, invoke the workflow directly."
109+
)
110+
else:
111+
how_to_invoke = (
112+
f"Call `mcp__plugin_deepwork_deepwork__start_workflow` with "
113+
f'job_name="{job.name}" and workflow_name="{wf.name}", '
114+
f"then follow the step instructions it returns."
115+
)
116+
workflows.append(
117+
WorkflowInfo(
118+
name=wf.name,
119+
summary=wf.summary,
120+
how_to_invoke=how_to_invoke,
121+
)
104122
)
105-
for wf in job.workflows
106-
]
107123

108124
return JobInfo(
109125
name=job.name,

src/deepwork/jobs/parser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ class Workflow:
240240
summary: str
241241
step_entries: list[WorkflowStepEntry] # List of step entries (sequential or concurrent)
242242

243+
# Agent type for this workflow (e.g., "general-purpose"). When set, the entire
244+
# workflow should be delegated to a sub-agent of this type via the Task tool.
245+
agent: str | None = None
246+
243247
@property
244248
def steps(self) -> list[str]:
245249
"""Get flattened list of all step IDs for backward compatibility."""
@@ -270,6 +274,7 @@ def from_dict(cls, data: dict[str, Any]) -> "Workflow":
270274
name=data["name"],
271275
summary=data["summary"],
272276
step_entries=step_entries,
277+
agent=data.get("agent"),
273278
)
274279

275280

tests/fixtures/jobs/concurrent_steps_job/job.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ common_job_info_provided_to_all_steps_at_runtime: |
99
workflows:
1010
- name: full_analysis
1111
summary: "Complete analysis workflow with parallel research phase"
12+
agent: "general-purpose"
1213
steps:
1314
- setup
1415
- [research_web, research_docs, research_interviews]

0 commit comments

Comments
 (0)