feat: add semantic draft authoring operations
This commit is contained in:
@@ -187,6 +187,12 @@ class ValidateDraftWorkspaceRequest(BaseModel):
|
||||
workspace_id: WorkspaceId
|
||||
|
||||
|
||||
class CompileDraftWorkspaceRequest(BaseModel):
|
||||
"""Typed MCP request for compiling one workspace draft without mutation."""
|
||||
|
||||
workspace_id: WorkspaceId
|
||||
|
||||
|
||||
class SetDraftNameRequest(BaseModel):
|
||||
"""Typed MCP request for changing the workflow draft name."""
|
||||
|
||||
@@ -237,20 +243,6 @@ class SetStepOutputMapRequest(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class AddStateFromOutputRequest(BaseModel):
|
||||
"""Typed MCP request for declaring a state field from a step output schema."""
|
||||
|
||||
workspace_id: WorkspaceId
|
||||
revision: int = Field(ge=1, description="Expected current workspace revision.")
|
||||
step_id: str = Field(description="Draft step id whose capability output is used.")
|
||||
output_field: str = Field(
|
||||
description="Top-level output field to copy, for example after."
|
||||
)
|
||||
state_path: str = Field(
|
||||
description="Root state path to declare, for example state.after."
|
||||
)
|
||||
|
||||
|
||||
class BindOutputToStateRequest(BaseModel):
|
||||
"""Typed MCP request for binding one step output to one root state field."""
|
||||
|
||||
@@ -280,13 +272,13 @@ class AddStepFromCapabilityRequest(BaseModel):
|
||||
default="ok",
|
||||
description="Outcome on route_from_step that should route to the new step.",
|
||||
)
|
||||
route_outcome: str = Field(
|
||||
default="ok",
|
||||
description="Outcome emitted by the new step.",
|
||||
)
|
||||
route_to: str = Field(
|
||||
default="__end__",
|
||||
description="Target step id or __end__ for the new step outcome.",
|
||||
routes: dict[str, str] | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Outcome to target route map for the new step. If omitted, a single "
|
||||
"declared outcome routes to __end__; multiple declared outcomes "
|
||||
"require explicit routes."
|
||||
),
|
||||
)
|
||||
input_map: dict[str, str] = Field(
|
||||
default_factory=dict,
|
||||
@@ -298,6 +290,31 @@ class AddStepFromCapabilityRequest(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class BranchDraftRequest(BaseModel):
|
||||
"""Typed MCP request for branching routes on a draft step."""
|
||||
|
||||
workspace_id: WorkspaceId
|
||||
revision: int = Field(ge=1, description="Expected workspace revision.")
|
||||
step_id: str = Field(description="Draft step id whose routes should be branched.")
|
||||
routes: dict[str, str] = Field(description="Outcome to target step map.")
|
||||
|
||||
|
||||
class HandleDraftBranchItem(BaseModel):
|
||||
"""Single branch item for handle_draft."""
|
||||
|
||||
step_id: str = Field(description="Draft step id.")
|
||||
outcome: str = Field(description="Outcome label.")
|
||||
|
||||
|
||||
class HandleDraftRequest(BaseModel):
|
||||
"""Typed MCP request for handling draft branches."""
|
||||
|
||||
workspace_id: WorkspaceId
|
||||
revision: int = Field(ge=1, description="Expected workspace revision.")
|
||||
branches: list[HandleDraftBranchItem] = Field(description="Branch items to handle.")
|
||||
target: str = Field(description="Target step id or __end__.")
|
||||
|
||||
|
||||
class DeleteDraftWorkspaceRequest(BaseModel):
|
||||
"""Typed MCP request payload for deleting one draft workspace."""
|
||||
|
||||
|
||||
@@ -12,10 +12,11 @@ from wf_mcp.broker.service import WfMcpService
|
||||
from wf_mcp.broker.service.workflow_operation_context import context_from_service
|
||||
|
||||
from .models import (
|
||||
AddStateFromOutputRequest,
|
||||
AddStepFromCapabilityRequest,
|
||||
BindOutputToStateRequest,
|
||||
BranchDraftRequest,
|
||||
CallCapabilityResult,
|
||||
CompileDraftWorkspaceRequest,
|
||||
CreateArtifactFromWorkspaceRequest,
|
||||
CreateDraftWorkspaceFromCapabilityRequest,
|
||||
CreateDraftWorkspaceFromCapabilityResult,
|
||||
@@ -26,6 +27,7 @@ from .models import (
|
||||
DeleteDraftWorkspaceResult,
|
||||
DraftWorkspaceListResult,
|
||||
DraftWorkspaceResult,
|
||||
HandleDraftRequest,
|
||||
PatchDraftWorkspaceRequest,
|
||||
RunDeploymentResult,
|
||||
SetDraftNameRequest,
|
||||
@@ -368,6 +370,22 @@ def register_workflow_tools(server: FastMCP[Any], service: WfMcpService) -> None
|
||||
await handlers.validate_draft_workspace(workspace_id=request.workspace_id)
|
||||
)
|
||||
|
||||
@server.tool(
|
||||
name="wf.workflow.compile_draft_workspace",
|
||||
title="Compile Draft Workspace",
|
||||
description=(
|
||||
"Compile a stored draft workspace without mutating it. Returns "
|
||||
"compiled_plan and required_capabilities when valid, or diagnostics "
|
||||
"when invalid."
|
||||
),
|
||||
)
|
||||
async def compile_draft_workspace(
|
||||
request: CompileDraftWorkspaceRequest,
|
||||
) -> dict[str, Any]:
|
||||
return await handlers.compile_draft_workspace(
|
||||
workspace_id=request.workspace_id,
|
||||
)
|
||||
|
||||
@server.tool(
|
||||
name="wf.workflow.set_draft_name",
|
||||
title="Set Draft Name",
|
||||
@@ -442,27 +460,6 @@ def register_workflow_tools(server: FastMCP[Any], service: WfMcpService) -> None
|
||||
)
|
||||
)
|
||||
|
||||
@server.tool(
|
||||
name="wf.workflow.add_state_from_output",
|
||||
title="Add State From Output",
|
||||
description=(
|
||||
"Declare one root state field by copying a draft step capability output "
|
||||
"field schema, including local $defs/definitions when present."
|
||||
),
|
||||
)
|
||||
async def add_state_from_output(
|
||||
request: AddStateFromOutputRequest,
|
||||
) -> DraftWorkspaceResult:
|
||||
return DraftWorkspaceResult.model_validate(
|
||||
await handlers.add_state_schema_from_output(
|
||||
workspace_id=request.workspace_id,
|
||||
revision=request.revision,
|
||||
step_id=request.step_id,
|
||||
output_field=request.output_field,
|
||||
state_path=request.state_path,
|
||||
)
|
||||
)
|
||||
|
||||
@server.tool(
|
||||
name="wf.workflow.bind_output_to_state",
|
||||
title="Bind Output To State",
|
||||
@@ -503,13 +500,48 @@ def register_workflow_tools(server: FastMCP[Any], service: WfMcpService) -> None
|
||||
capability_name=request.capability_name,
|
||||
route_from_step=request.route_from_step,
|
||||
route_from_outcome=request.route_from_outcome,
|
||||
route_outcome=request.route_outcome,
|
||||
route_to=request.route_to,
|
||||
routes=request.routes,
|
||||
input_map=request.input_map,
|
||||
bind_outputs=request.bind_outputs,
|
||||
)
|
||||
)
|
||||
|
||||
@server.tool(
|
||||
name="wf.workflow.branch_draft",
|
||||
title="Branch Draft",
|
||||
description=(
|
||||
"Branch multiple outcome routes on a single draft step atomically. "
|
||||
"Provided outcomes are updated while unspecified outcomes are preserved."
|
||||
),
|
||||
)
|
||||
async def branch_draft(request: BranchDraftRequest) -> DraftWorkspaceResult:
|
||||
return DraftWorkspaceResult.model_validate(
|
||||
await handlers.branch_draft(
|
||||
workspace_id=request.workspace_id,
|
||||
revision=request.revision,
|
||||
step_id=request.step_id,
|
||||
routes=request.routes,
|
||||
)
|
||||
)
|
||||
|
||||
@server.tool(
|
||||
name="wf.workflow.handle_draft",
|
||||
title="Handle Draft",
|
||||
description=("Set a common target for multiple step/outcome pairs atomically."),
|
||||
)
|
||||
async def handle_draft(request: HandleDraftRequest) -> DraftWorkspaceResult:
|
||||
return DraftWorkspaceResult.model_validate(
|
||||
await handlers.handle_draft(
|
||||
workspace_id=request.workspace_id,
|
||||
revision=request.revision,
|
||||
branches=[
|
||||
{"step_id": b.step_id, "outcome": b.outcome}
|
||||
for b in request.branches
|
||||
],
|
||||
target=request.target,
|
||||
)
|
||||
)
|
||||
|
||||
@server.tool(
|
||||
name="wf.workflow.create_minimal_draft_workspace",
|
||||
title="Create Minimal Draft Workspace",
|
||||
|
||||
Reference in New Issue
Block a user