Merge pull request #3979 from jeremymcs/fix/mcp-tool-iserror-flag
fix(mcp): return isError flag on workflow tool execution failures
This commit is contained in:
commit
902c020590
2 changed files with 82 additions and 25 deletions
|
|
@ -256,6 +256,28 @@ test("executePlanSlice writes task planning state and rendered plan artifacts",
|
|||
}
|
||||
});
|
||||
|
||||
test("executePlanSlice marks validation failures with isError", async () => {
|
||||
const base = makeTmpBase();
|
||||
try {
|
||||
openTestDb(base);
|
||||
|
||||
const result = await inProjectDir(base, () => executePlanSlice({
|
||||
milestoneId: "M001",
|
||||
sliceId: "S01",
|
||||
goal: "Trigger validation failure for empty tasks.",
|
||||
tasks: [],
|
||||
}, base));
|
||||
|
||||
assert.equal(result.isError, true);
|
||||
assert.equal(result.details.operation, "plan_slice");
|
||||
assert.match(String(result.details.error), /validation failed: tasks must be a non-empty array/);
|
||||
assert.match(result.content[0].text, /Error planning slice:/);
|
||||
} finally {
|
||||
closeDatabase();
|
||||
cleanup(base);
|
||||
}
|
||||
});
|
||||
|
||||
test("executeSliceComplete coerces string enrichment entries and writes summary/UAT artifacts", async () => {
|
||||
const base = makeTmpBase();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export function isSupportedSummaryArtifactType(
|
|||
export interface ToolExecutionResult {
|
||||
content: Array<{ type: "text"; text: string }>;
|
||||
details: Record<string, unknown>;
|
||||
isError?: boolean;
|
||||
}
|
||||
|
||||
export interface SummarySaveParams {
|
||||
|
|
@ -57,13 +58,15 @@ export async function executeSummarySave(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot save artifact." }],
|
||||
details: { operation: "save_summary", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
if (!isSupportedSummaryArtifactType(params.artifact_type)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }],
|
||||
details: { operation: "save_summary", error: "invalid_artifact_type" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
const contextGuard = shouldBlockContextArtifactSaveInSnapshot(
|
||||
loadWriteGateSnapshot(basePath),
|
||||
|
|
@ -75,7 +78,8 @@ export async function executeSummarySave(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
|
||||
details: { operation: "save_summary", error: "context_write_blocked" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
let relativePath: string;
|
||||
|
|
@ -108,7 +112,8 @@ export async function executeSummarySave(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error saving artifact: ${msg}` }],
|
||||
details: { operation: "save_summary", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +168,8 @@ export async function executeTaskComplete(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete task." }],
|
||||
details: { operation: "complete_task", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const coerced = { ...params };
|
||||
|
|
@ -176,6 +182,7 @@ export async function executeTaskComplete(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error completing task: ${result.error}` }],
|
||||
details: { operation: "complete_task", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -194,7 +201,8 @@ export async function executeTaskComplete(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error completing task: ${msg}` }],
|
||||
details: { operation: "complete_task", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +215,8 @@ export async function executeSliceComplete(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete slice." }],
|
||||
details: { operation: "complete_slice", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const splitPair = (s: string): [string, string] => {
|
||||
|
|
@ -257,6 +266,7 @@ export async function executeSliceComplete(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error completing slice: ${result.error}` }],
|
||||
details: { operation: "complete_slice", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -275,7 +285,8 @@ export async function executeSliceComplete(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error completing slice: ${msg}` }],
|
||||
details: { operation: "complete_slice", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +299,8 @@ export async function executeCompleteMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete milestone." }],
|
||||
details: { operation: "complete_milestone", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const sanitized = sanitizeCompleteMilestoneParams(params);
|
||||
|
|
@ -297,6 +309,7 @@ export async function executeCompleteMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error completing milestone: ${result.error}` }],
|
||||
details: { operation: "complete_milestone", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -313,7 +326,8 @@ export async function executeCompleteMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error completing milestone: ${msg}` }],
|
||||
details: { operation: "complete_milestone", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -326,7 +340,8 @@ export async function executeValidateMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot validate milestone." }],
|
||||
details: { operation: "validate_milestone", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const result = await handleValidateMilestone(params, basePath);
|
||||
|
|
@ -334,6 +349,7 @@ export async function executeValidateMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error validating milestone: ${result.error}` }],
|
||||
details: { operation: "validate_milestone", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -351,7 +367,8 @@ export async function executeValidateMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error validating milestone: ${msg}` }],
|
||||
details: { operation: "validate_milestone", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +381,8 @@ export async function executeReassessRoadmap(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot reassess roadmap." }],
|
||||
details: { operation: "reassess_roadmap", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const result = await handleReassessRoadmap(params, basePath);
|
||||
|
|
@ -372,6 +390,7 @@ export async function executeReassessRoadmap(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error reassessing roadmap: ${result.error}` }],
|
||||
details: { operation: "reassess_roadmap", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -390,7 +409,8 @@ export async function executeReassessRoadmap(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error reassessing roadmap: ${msg}` }],
|
||||
details: { operation: "reassess_roadmap", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +423,8 @@ export async function executeSaveGateResult(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available." }],
|
||||
details: { operation: "save_gate_result", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"];
|
||||
|
|
@ -411,7 +432,8 @@ export async function executeSaveGateResult(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
|
||||
details: { operation: "save_gate_result", error: "invalid_gate_id" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const validVerdicts = ["pass", "flag", "omitted"];
|
||||
|
|
@ -419,7 +441,8 @@ export async function executeSaveGateResult(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }],
|
||||
details: { operation: "save_gate_result", error: "invalid_verdict" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -443,7 +466,8 @@ export async function executeSaveGateResult(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error saving gate result: ${msg}` }],
|
||||
details: { operation: "save_gate_result", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -456,7 +480,8 @@ export async function executePlanMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan milestone." }],
|
||||
details: { operation: "plan_milestone", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const result = await handlePlanMilestone(params, basePath);
|
||||
|
|
@ -464,6 +489,7 @@ export async function executePlanMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error planning milestone: ${result.error}` }],
|
||||
details: { operation: "plan_milestone", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -480,7 +506,8 @@ export async function executePlanMilestone(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error planning milestone: ${msg}` }],
|
||||
details: { operation: "plan_milestone", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -493,7 +520,8 @@ export async function executePlanSlice(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan slice." }],
|
||||
details: { operation: "plan_slice", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const result = await handlePlanSlice(params, basePath);
|
||||
|
|
@ -501,6 +529,7 @@ export async function executePlanSlice(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error planning slice: ${result.error}` }],
|
||||
details: { operation: "plan_slice", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -519,7 +548,8 @@ export async function executePlanSlice(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error planning slice: ${msg}` }],
|
||||
details: { operation: "plan_slice", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +562,8 @@ export async function executeReplanSlice(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available. Cannot replan slice." }],
|
||||
details: { operation: "replan_slice", error: "db_unavailable" },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const result = await handleReplanSlice(params, basePath);
|
||||
|
|
@ -540,6 +571,7 @@ export async function executeReplanSlice(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error replanning slice: ${result.error}` }],
|
||||
details: { operation: "replan_slice", error: result.error },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -558,7 +590,8 @@ export async function executeReplanSlice(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error replanning slice: ${msg}` }],
|
||||
details: { operation: "replan_slice", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -576,6 +609,7 @@ export async function executeMilestoneStatus(
|
|||
return {
|
||||
content: [{ type: "text", text: "Error: GSD database is not available." }],
|
||||
details: { operation: "milestone_status", error: "db_unavailable" },
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -624,6 +658,7 @@ export async function executeMilestoneStatus(
|
|||
return {
|
||||
content: [{ type: "text", text: `Error querying milestone status: ${msg}` }],
|
||||
details: { operation: "milestone_status", error: msg },
|
||||
};
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue