Skip to content

Commit 8137de1

Browse files
committed
fix(mcp): Always return inline content from start_workflow
- Remove caller_model parameter — start_workflow always inlines - Wrap content with buildInlineExecutionPrompt unconditionally - Return inline: true for both create-plan and run-autonomous - Update tool description to reflect always-inline behavior - Update tests to assert inline=true and INLINE EXECUTION wrapper - Update agent-workflow docs for start-workflow description
1 parent ffb7221 commit 8137de1

3 files changed

Lines changed: 58 additions & 39 deletions

File tree

docs/agent-workflow.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,12 @@ skills/
109109

110110
`start-workflow` has no skill file. It exists as both a **prompt** (slash
111111
command) and a **tool** (`start_workflow`). Both are server-side only: they fetch
112-
the card, inspect the `autonomous` flag, and route to `run-autonomous` or
113-
`create-plan`. The tool enables natural-language triggering — when a user writes
114-
"start workflow for ALPHA-001" (without a slash command), the agent sees the
115-
`start_workflow` tool and calls it to get routing instructions. If the card
116-
cannot be found, both paths return an error.
112+
the card, inspect the `autonomous` flag, and return the full skill content for
113+
`run-autonomous` or `create-plan`. The tool enables natural-language triggering
114+
— when a user writes "start workflow for ALPHA-001" (without a slash command),
115+
the agent sees the `start_workflow` tool and calls it to get the executable
116+
workflow content directly. If the card cannot be found, both paths return an
117+
error.
117118

118119
## Slash command interface
119120

internal/mcp/server_test.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,7 +1612,7 @@ func TestPrompt_StartWorkflow_Autonomous(t *testing.T) {
16121612
}
16131613

16141614
// TestTool_StartWorkflow_NonAutonomous verifies the start_workflow tool returns
1615-
// create-plan for non-autonomous cards.
1615+
// full create-plan skill content for non-autonomous cards.
16161616
func TestTool_StartWorkflow_NonAutonomous(t *testing.T) {
16171617
env := setupMCP(t)
16181618

@@ -1626,14 +1626,17 @@ func TestTool_StartWorkflow_NonAutonomous(t *testing.T) {
16261626
var out startWorkflowOutput
16271627
unmarshalResult(t, result, &out)
16281628

1629-
assert.Equal(t, "TEST-001", out.CardID)
1630-
assert.Equal(t, "create-plan", out.Workflow)
1631-
assert.Contains(t, out.Prompt, "create-plan")
1632-
assert.Contains(t, out.Prompt, "TEST-001")
1629+
assert.Equal(t, "create-plan", out.SkillName)
1630+
assert.True(t, out.Inline, "start_workflow should always return inline=true")
1631+
assert.Contains(t, out.Content, "INLINE EXECUTION")
1632+
assert.Contains(t, out.Content, "create-plan.md")
1633+
assert.Contains(t, out.Content, "Skill instructions here.")
1634+
assert.Contains(t, out.Content, "TEST-001")
1635+
assert.NotContains(t, out.Content, "## Agent Configuration")
16331636
}
16341637

16351638
// TestTool_StartWorkflow_Autonomous verifies the start_workflow tool returns
1636-
// run-autonomous for autonomous cards.
1639+
// full run-autonomous skill content for autonomous cards.
16371640
func TestTool_StartWorkflow_Autonomous(t *testing.T) {
16381641
env := setupMCP(t)
16391642

@@ -1652,10 +1655,13 @@ func TestTool_StartWorkflow_Autonomous(t *testing.T) {
16521655
var out startWorkflowOutput
16531656
unmarshalResult(t, result, &out)
16541657

1655-
assert.Equal(t, "TEST-001", out.CardID)
1656-
assert.Equal(t, "run-autonomous", out.Workflow)
1657-
assert.Contains(t, out.Prompt, "run-autonomous")
1658-
assert.Contains(t, out.Prompt, "TEST-001")
1658+
assert.Equal(t, "run-autonomous", out.SkillName)
1659+
assert.True(t, out.Inline, "start_workflow should always return inline=true")
1660+
assert.Contains(t, out.Content, "INLINE EXECUTION")
1661+
assert.Contains(t, out.Content, "run-autonomous.md")
1662+
assert.Contains(t, out.Content, "TEST-001")
1663+
assert.Contains(t, out.Content, "**Autonomous:** true")
1664+
assert.NotContains(t, out.Content, "## Agent Configuration")
16591665
}
16601666

16611667
func TestUpdateCard_Priority(t *testing.T) {

internal/mcp/tools.go

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func registerTools(server *mcp.Server, svc *service.CardService, skillsDir strin
3636
registerCreateProject(server, svc)
3737
registerUpdateProject(server, svc)
3838
registerDeleteProject(server, svc)
39-
registerStartWorkflow(server, svc)
39+
registerStartWorkflow(server, svc, skillsDir)
4040
registerGetSkill(server, svc, skillsDir)
4141
registerReportPush(server, svc)
4242
registerIncrementReviewAttempts(server, svc)
@@ -58,10 +58,12 @@ func resolveProject(ctx context.Context, svc *service.CardService, project, card
5858

5959
// --- Input/Output types ---
6060

61-
type listProjectsInput struct{}
62-
type listProjectsOutput struct {
63-
Projects []board.ProjectConfig `json:"projects"`
64-
}
61+
type (
62+
listProjectsInput struct{}
63+
listProjectsOutput struct {
64+
Projects []board.ProjectConfig `json:"projects"`
65+
}
66+
)
6567

6668
type listCardsInput struct {
6769
Project string `json:"project" jsonschema:"required,project name"`
@@ -845,42 +847,52 @@ func registerDeleteProject(server *mcp.Server, svc *service.CardService) {
845847
// --- start_workflow tool ---
846848

847849
type startWorkflowInput struct {
848-
CardID string `json:"card_id" jsonschema:"required,card ID to start the workflow for (e.g. ALPHA-001)"`
850+
CardID string `json:"card_id" jsonschema:"required,card ID to start the workflow for (e.g. ALPHA-001)"`
851+
IncludePreamble *bool `json:"include_preamble,omitempty" jsonschema:"include workflow rules preamble (default true, pass false to skip on subsequent calls when you already have it)"`
849852
}
850853
type startWorkflowOutput struct {
851-
CardID string `json:"card_id"`
852-
Workflow string `json:"workflow"`
853-
Prompt string `json:"prompt"`
854-
Instruction string `json:"instruction"`
854+
SkillName string `json:"skill_name"`
855+
Model string `json:"model,omitempty"`
856+
Content string `json:"content"`
857+
Inline bool `json:"inline,omitempty"`
855858
}
856859

857-
func registerStartWorkflow(server *mcp.Server, svc *service.CardService) {
860+
func registerStartWorkflow(server *mcp.Server, svc *service.CardService, skillsDir string) {
858861
mcp.AddTool(server, &mcp.Tool{
859862
Name: "start_workflow",
860863
Description: "Start the workflow for a card. Call this when a user asks to " +
861-
"'start workflow', 'plan', 'work on', 'begin', or 'run' a card. " +
862-
"Inspects the card's autonomous flag and returns which workflow prompt to invoke: " +
863-
"run-autonomous (for autonomous cards) or create-plan (for human-in-the-loop cards).",
864+
"'start workflow', 'start', 'plan', 'work on', 'begin', or 'run' a card. " +
865+
"Inspects the card's autonomous flag and returns the full workflow skill content: " +
866+
"run-autonomous (for autonomous cards) or create-plan (for human-in-the-loop cards). " +
867+
"Always returns inline: true — execute the content directly.",
864868
}, func(ctx context.Context, _ *mcp.CallToolRequest, input startWorkflowInput) (*mcp.CallToolResult, startWorkflowOutput, error) {
865869
card, _, err := findCard(ctx, svc, input.CardID)
866870
if err != nil {
867871
return nil, startWorkflowOutput{}, fmt.Errorf("start workflow: %w", err)
868872
}
869873

874+
skill := "create-plan"
870875
if card.Autonomous {
871-
return nil, startWorkflowOutput{
872-
CardID: input.CardID,
873-
Workflow: "run-autonomous",
874-
Prompt: "/contextmatrix:run-autonomous " + input.CardID,
875-
Instruction: "This card has autonomous mode enabled. Run the prompt shown above to drive it through the full lifecycle without human approval gates.",
876-
}, nil
876+
skill = "run-autonomous"
877+
}
878+
879+
includePreamble := input.IncludePreamble == nil || *input.IncludePreamble
880+
result, err := buildSkillContent(ctx, svc, skillsDir, skill, skillArgs{
881+
CardID: input.CardID,
882+
}, includePreamble)
883+
if err != nil {
884+
return nil, startWorkflowOutput{}, fmt.Errorf("start workflow: %w", err)
877885
}
886+
content := stripAgentConfig(result.Content)
887+
888+
// start_workflow always returns inline content — both create-plan
889+
// and run-autonomous are executed directly by the orchestrator.
890+
content = buildInlineExecutionPrompt(content, input.CardID, skill)
878891

879892
return nil, startWorkflowOutput{
880-
CardID: input.CardID,
881-
Workflow: "create-plan",
882-
Prompt: "/contextmatrix:create-plan " + input.CardID,
883-
Instruction: "This card uses the human-in-the-loop workflow. Run the prompt shown above to start planning with approval gates.",
893+
SkillName: skill,
894+
Content: content,
895+
Inline: true,
884896
}, nil
885897
})
886898
}

0 commit comments

Comments
 (0)