Skip to content

Commit 1a42161

Browse files
committed
Remove MCP changes — extracted to PR #394
The MCP workflow runtime changes from this branch have been extracted into a separate draft PR (#394) for independent review. This branch now contains only the OpenClaw bundle content. Note: with MCP hardening removed, `uvx deepwork serve --platform openclaw` will reject "openclaw" as an unsupported platform (FORMATTERS registry only knows "claude" on main). That is expected during this review split — determines whether the MCP changes are actually required to ship OpenClaw support, or whether the bundle can work on the main-branch runtime alone.
1 parent 3d9acd0 commit 1a42161

19 files changed

Lines changed: 88 additions & 882 deletions

doc/mcp_interface.md

Lines changed: 12 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This document describes the Model Context Protocol (MCP) tools exposed by the De
1010

1111
## Tools
1212

13-
DeepWork exposes thirteen MCP tools:
13+
DeepWork exposes eleven MCP tools:
1414

1515
### 1. `get_workflows`
1616

@@ -54,64 +54,7 @@ interface WorkflowInfo {
5454

5555
---
5656

57-
### 2. `get_active_workflow`
58-
59-
Return the currently active workflow for a session, if one exists. This is useful after compaction, reset, or any host-specific session restore flow.
60-
61-
#### Parameters
62-
63-
| Parameter | Type | Required | Description |
64-
|-----------|------|----------|-------------|
65-
| `session_id` | `string` | Yes | The persistent DeepWork session ID for the current host session. In Claude Code this is `CLAUDE_CODE_SESSION_ID`. |
66-
| `agent_id` | `string \| null` | No | Optional host-specific agent identifier for agent-scoped workflow state. In Claude Code this is `CLAUDE_CODE_AGENT_ID`. |
67-
68-
#### Returns
69-
70-
```typescript
71-
{
72-
has_active_workflow: boolean;
73-
stack: StackEntry[];
74-
active_workflow?: {
75-
job_name: string;
76-
workflow_name: string;
77-
goal: string;
78-
started_at: string;
79-
step_number: number;
80-
total_steps: number;
81-
completed_steps: string[];
82-
current_step: ActiveStepInfo;
83-
} | null;
84-
}
85-
```
86-
87-
---
88-
89-
### 3. `validate_step_outputs`
90-
91-
Validate a planned `finished_step` payload against the active step without advancing the workflow or running quality reviews. Use this as a dry run when you want to catch wrong output names, missing required outputs, bad types, or missing files before calling `finished_step`.
92-
93-
#### Parameters
94-
95-
| Parameter | Type | Required | Description |
96-
|-----------|------|----------|-------------|
97-
| `outputs` | `Record<string, string \| string[]>` | Yes | Map of planned step output names to values. Validation uses the active step's declared output contract without advancing the workflow. |
98-
| `session_id` | `string` | Yes | The persistent DeepWork session ID for the current host session. In Claude Code this is `CLAUDE_CODE_SESSION_ID`. |
99-
| `agent_id` | `string \| null` | No | Optional host-specific agent identifier for agent-scoped workflow state. In Claude Code this is `CLAUDE_CODE_AGENT_ID`. |
100-
101-
#### Returns
102-
103-
```typescript
104-
{
105-
valid: boolean;
106-
errors: string[];
107-
current_step: ActiveStepInfo;
108-
stack: StackEntry[];
109-
}
110-
```
111-
112-
---
113-
114-
### 4. `start_workflow`
57+
### 2. `start_workflow`
11558

11659
Start a new workflow session. Initializes state tracking and returns the first step's instructions. Supports nested workflows — starting a workflow while one is active pushes onto a stack.
11760

@@ -139,7 +82,7 @@ Start a new workflow session. Initializes state tracking and returns the first s
13982

14083
---
14184

142-
### 5. `finished_step`
85+
### 3. `finished_step`
14386

14487
Report that you've finished a workflow step. Validates outputs and runs quality reviews (from step definitions and .deepreview rules), then returns the next action.
14588

@@ -178,7 +121,7 @@ Report that you've finished a workflow step. Validates outputs and runs quality
178121

179122
---
180123

181-
### 6. `abort_workflow`
124+
### 4. `abort_workflow`
182125

183126
Abort the current workflow and return to the parent workflow (if nested). Use this when a workflow cannot be completed.
184127

@@ -206,7 +149,7 @@ Abort the current workflow and return to the parent workflow (if nested). Use th
206149

207150
---
208151

209-
### 7. `go_to_step`
152+
### 5. `go_to_step`
210153

211154
Navigate back to a prior step in the current workflow. Clears all progress from the target step onward, forcing re-execution of subsequent steps to ensure consistency. Use this when earlier outputs need revision or quality issues are discovered in later steps.
212155

@@ -238,7 +181,7 @@ Navigate back to a prior step in the current workflow. Clears all progress from
238181

239182
---
240183

241-
### 8. `get_review_instructions`
184+
### 6. `get_review_instructions`
242185

243186
Run a review of changed files based on `.deepreview` configuration files and DeepSchema-generated synthetic review rules. Returns a list of review tasks to invoke in parallel. Each task has `description`, `subagent_type`, and `prompt` fields for the Agent tool.
244187

@@ -258,7 +201,7 @@ A plain string with one of:
258201

259202
---
260203

261-
### 9. `get_configured_reviews`
204+
### 7. `get_configured_reviews`
262205

263206
List all configured review rules from `.deepreview` files and DeepSchema-generated synthetic rules. Returns each rule's name, description, and defining file location. Optionally filters to rules matching specific files.
264207

@@ -282,7 +225,7 @@ Array<{
282225

283226
---
284227

285-
### 10. `mark_review_as_passed`
228+
### 8. `mark_review_as_passed`
286229

287230
Mark a review as passed so it won't be re-run while reviewed files remain unchanged. Call this when a review has no findings, when all findings have been fixed, or when remaining findings have been explicitly dismissed by the user. The `review_id` is provided in the instruction file's "After Review" section.
288231

@@ -302,7 +245,7 @@ A plain string with either:
302245

303246
---
304247

305-
### 11. `get_named_schemas`
248+
### 9. `get_named_schemas`
306249

307250
List all named DeepSchemas discovered across all schema sources (project-local, standard, and env var). Returns each schema's name, summary, and matcher patterns.
308251

@@ -320,7 +263,7 @@ Array<{
320263
}>
321264
```
322265

323-
### 12. `register_session_job`
266+
### 10. `register_session_job`
324267

325268
Register a transient job definition scoped to the current session. The job is validated against the job schema and stored so that `start_workflow` can discover it. Can be called multiple times to overwrite.
326269

@@ -345,7 +288,7 @@ Register a transient job definition scoped to the current session. The job is va
345288

346289
On validation failure, returns `{ error: string }` with details about what failed.
347290

348-
### 13. `get_session_job`
291+
### 11. `get_session_job`
349292

350293
Retrieve the YAML content of a session-scoped job definition previously registered with `register_session_job`.
351294

@@ -432,9 +375,7 @@ The `finished_step` tool returns one of three statuses:
432375
|
433376
3. Execute step instructions, create outputs
434377
|
435-
4. validate_step_outputs(outputs, session_id) // optional dry run
436-
|
437-
5. finished_step(outputs, session_id)
378+
4. finished_step(outputs, session_id)
438379
|
439380
+-- status = "needs_work" -> Fix issues, goto 4
440381
+-- status = "next_step" -> Execute new instructions, goto 4

src/deepwork/jobs/mcp/quality_gate.py

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
)
2323
from deepwork.review.config import ReviewRule, ReviewTask
2424
from deepwork.review.discovery import load_all_rules
25-
from deepwork.review.formatter import FORMATTERS, format_for_claude
25+
from deepwork.review.formatter import format_for_claude
2626
from deepwork.review.instructions import (
2727
write_instruction_files,
2828
)
@@ -461,35 +461,16 @@ def run_quality_gate(
461461
return None
462462

463463
# 9. Format as review instructions
464-
formatter = FORMATTERS.get(platform, format_for_claude)
465-
review_output = formatter(task_files, project_root)
464+
review_output = format_for_claude(task_files, project_root)
466465

467466
# 10. Build complete response with guidance
468-
guidance = _build_review_guidance(review_output, platform)
467+
guidance = _build_review_guidance(review_output)
469468

470469
return guidance
471470

472471

473-
def _build_review_guidance(review_output: str, platform: str = "claude") -> str:
472+
def _build_review_guidance(review_output: str) -> str:
474473
"""Build the complete review guidance including /review skill instructions."""
475-
if platform == "openclaw":
476-
return f"""Quality reviews are required before this step can advance.
477-
478-
{review_output}
479-
480-
## How to Run Reviews
481-
482-
For each review task listed above, launch it as a parallel OpenClaw sub-agent with `sessions_spawn`.
483-
484-
- Spawn every listed review before waiting for any completion event.
485-
- Use each instruction path exactly as written, relative to the workspace root. Do not rewrite it as an absolute host path.
486-
- Do not set `timeoutSeconds` on these review spawns; let the runtime default apply. If the tool requires a timeout value, use `0`.
487-
- After all spawns are accepted, use `sessions_yield` to wait for completion events before continuing.
488-
489-
## After Reviews
490-
491-
For any failing reviews, if you believe the issue is invalid, then you can call `mark_review_as_passed` on it. Otherwise, you should act on any feedback from the review to fix the issues. Once done, call `finished_step` again to see if you will pass now."""
492-
493474
return f"""Quality reviews are required before this step can advance.
494475
495476
{review_output}

src/deepwork/jobs/mcp/roots.py

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
"""MCP root resolution via listRoots client capability.
22
33
Resolves the project root dynamically by asking the MCP client for its
4-
filesystem roots. When ``--path`` is explicitly passed on the CLI the
5-
resolver always returns that path. Otherwise it calls ``ctx.list_roots()``
4+
filesystem roots. When ``--path`` is explicitly passed on the CLI the
5+
resolver always returns that path. Otherwise it calls ``ctx.list_roots()``
66
on every tool invocation so it tracks workspace changes (e.g. git worktree
77
switches) without caching stale values.
8-
9-
For OpenClaw bundle installs, the MCP server can be launched from the plugin
10-
bundle directory itself (for example ``plugins/openclaw``) when the host does
11-
not expose a usable ``listRoots`` capability. In that case we normalize the
12-
bundle directory back to the enclosing workspace root when we can detect
13-
OpenClaw workspace markers.
148
"""
159

1610
from __future__ import annotations
@@ -25,9 +19,6 @@
2519

2620
logger = logging.getLogger("deepwork.jobs.mcp")
2721

28-
_OPENCLAW_PLUGIN_MARKER = Path(".codex-plugin") / "plugin.json"
29-
_OPENCLAW_WORKSPACE_MARKER = Path(".openclaw") / "workspace-state.json"
30-
3122

3223
async def resolve_project_root(ctx: Context, fallback: Path) -> Path:
3324
"""Ask the MCP client for its filesystem root.
@@ -87,24 +78,4 @@ async def get_root(self, ctx: Context) -> Path:
8778
"""
8879
if self._explicit:
8980
return self._fallback
90-
candidate = await resolve_project_root(ctx, self._fallback)
91-
return _normalize_openclaw_bundle_root(candidate)
92-
93-
94-
def _normalize_openclaw_bundle_root(candidate: Path) -> Path:
95-
"""Map an OpenClaw plugin bundle path back to the workspace root."""
96-
97-
resolved = candidate.resolve()
98-
if not (resolved / _OPENCLAW_PLUGIN_MARKER).exists():
99-
return resolved
100-
101-
for ancestor in (resolved, *resolved.parents):
102-
if (ancestor / _OPENCLAW_WORKSPACE_MARKER).exists():
103-
logger.debug(
104-
"Normalized OpenClaw plugin bundle root %s to workspace root %s",
105-
resolved,
106-
ancestor,
107-
)
108-
return ancestor
109-
110-
return resolved
81+
return await resolve_project_root(ctx, self._fallback)

src/deepwork/jobs/mcp/schemas.py

Lines changed: 0 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -144,31 +144,6 @@ class FinishedStepInput(BaseModel):
144144
)
145145

146146

147-
class ValidateStepOutputsInput(BaseModel):
148-
"""Input for validate_step_outputs tool."""
149-
150-
outputs: dict[str, ArgumentValue] = Field(
151-
description=(
152-
"Map of planned step output names to values. "
153-
"Validation uses the active step's declared output contract without "
154-
"advancing the workflow or running quality reviews."
155-
)
156-
)
157-
session_id: str = Field(
158-
description=(
159-
"The persistent DeepWork session ID for the current host session. "
160-
"In Claude Code this is CLAUDE_CODE_SESSION_ID."
161-
),
162-
)
163-
agent_id: str | None = Field(
164-
default=None,
165-
description=(
166-
"Optional host-specific agent identifier for agent-scoped workflow state. "
167-
"In Claude Code this is CLAUDE_CODE_AGENT_ID."
168-
),
169-
)
170-
171-
172147
class AbortWorkflowInput(BaseModel):
173148
"""Input for abort_workflow tool."""
174149

@@ -207,24 +182,6 @@ class GoToStepInput(BaseModel):
207182
)
208183

209184

210-
class GetActiveWorkflowInput(BaseModel):
211-
"""Input for get_active_workflow tool."""
212-
213-
session_id: str = Field(
214-
description=(
215-
"The persistent DeepWork session ID for the current host session. "
216-
"In Claude Code this is CLAUDE_CODE_SESSION_ID."
217-
),
218-
)
219-
agent_id: str | None = Field(
220-
default=None,
221-
description=(
222-
"Optional host-specific agent identifier for agent-scoped workflow state. "
223-
"In Claude Code this is CLAUDE_CODE_AGENT_ID."
224-
),
225-
)
226-
227-
228185
# =============================================================================
229186
# Tool Output Models
230187
# NOTE: Changes to these models affect MCP tool return types.
@@ -363,23 +320,6 @@ class FinishedStepResponse(BaseModel):
363320
)
364321

365322

366-
class ValidateStepOutputsResponse(BaseModel):
367-
"""Response from validate_step_outputs tool."""
368-
369-
valid: bool = Field(description="Whether the submitted outputs satisfy the active step contract")
370-
errors: list[str] = Field(
371-
default_factory=list,
372-
description="Validation errors that must be fixed before calling finished_step",
373-
)
374-
current_step: ActiveStepInfo = Field(
375-
description="The current step, including the declared expected outputs",
376-
)
377-
stack: list[StackEntry] = Field(
378-
default_factory=list,
379-
description="Current workflow stack after validation",
380-
)
381-
382-
383323
class AbortWorkflowResponse(BaseModel):
384324
"""Response from abort_workflow tool."""
385325

@@ -409,40 +349,6 @@ class GoToStepResponse(BaseModel):
409349
)
410350

411351

412-
class ActiveWorkflowState(BaseModel):
413-
"""Current active workflow session details."""
414-
415-
job_name: str = Field(description="Name of the active job")
416-
workflow_name: str = Field(description="Name of the active workflow")
417-
goal: str = Field(description="Goal originally supplied when the workflow started")
418-
started_at: str = Field(description="ISO timestamp when the workflow started")
419-
step_number: int = Field(description="1-based index of the current step")
420-
total_steps: int = Field(description="Total number of steps in the workflow")
421-
completed_steps: list[str] = Field(
422-
default_factory=list,
423-
description="Step IDs already completed in this workflow session",
424-
)
425-
current_step: ActiveStepInfo = Field(
426-
description="The active step and its current resolved instructions",
427-
)
428-
429-
430-
class GetActiveWorkflowResponse(BaseModel):
431-
"""Response from get_active_workflow tool."""
432-
433-
has_active_workflow: bool = Field(
434-
description="Whether the given session currently has an active workflow"
435-
)
436-
stack: list[StackEntry] = Field(
437-
default_factory=list,
438-
description="Current workflow stack visible to this session/agent",
439-
)
440-
active_workflow: ActiveWorkflowState | None = Field(
441-
default=None,
442-
description="Details of the active workflow when one exists",
443-
)
444-
445-
446352
# =============================================================================
447353
# Session Job Models
448354
# NOTE: These models support register_session_job / get_session_job tools.

0 commit comments

Comments
 (0)