@thehammer/schema-mcp-server 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,5 +38,16 @@ export declare function getTemplatePreviewHtml(templateId: number, teamObjectId?
38
38
  export declare function getStyleList(): Promise<ApiResponse>;
39
39
  export declare function listQualityGateItems(): Promise<ApiResponse>;
40
40
  export declare function submitQualityGateReview(itemId: number, status: string, analysis: string, message?: string): Promise<ApiResponse>;
41
+ export declare function callQualityGate(category: string, message?: string): Promise<ApiResponse>;
42
+ /**
43
+ * Orchestrator finalization — mandatory last action before exit.
44
+ *
45
+ * Returns {ok: true} when every outstanding check has cleared, transitioning the
46
+ * dispatch to completed. Returns {ok: false, outstanding: [...]} when any category
47
+ * is pending, any item failed, any template has validation_errors, or any directive
48
+ * has meta.selector_mismatches / meta.extraction_coverage_errors. In the non-empty
49
+ * case, the dispatch stays running so the agent can fix the issues and re-call.
50
+ */
51
+ export declare function completeDispatch(message?: string): Promise<ApiResponse>;
41
52
  export declare function postProgress(progressMessage: string, phase?: string): Promise<ApiResponse>;
42
53
  export { SCHEMA_ID };
@@ -177,6 +177,73 @@ export async function submitQualityGateReview(itemId, status, analysis, message)
177
177
  message,
178
178
  });
179
179
  }
180
+ /**
181
+ * Check (or kick off) quality gate review for a single category.
182
+ *
183
+ * BLOCKING BY DESIGN. This call dispatches a reviewer (if one isn't already
184
+ * running) and then polls the backend until the gate for that category reaches
185
+ * a terminal state (`passed`, `failed`, or `error`). The agent sees exactly
186
+ * one call per category — no polling loop in agent context, no wasted context
187
+ * on intermediate `running` responses.
188
+ *
189
+ * Returns one of:
190
+ * - `passed` — all items green (cache hit or fresh review)
191
+ * - `failed` — items need fixes (with `analysis`)
192
+ * - `error` — a prior reviewer dispatch failed at this iteration; manual
193
+ * intervention needed (the agent must surface this to the user)
194
+ *
195
+ * Polling cadence: POLL_INTERVAL_MS between requests, capped at MAX_POLL_MS
196
+ * total wall time. If the cap is reached the function returns a synthetic
197
+ * `timeout` status so the agent doesn't hang forever.
198
+ */
199
+ const QUALITY_GATE_POLL_INTERVAL_MS = 3_000;
200
+ const QUALITY_GATE_MAX_POLL_MS = 15 * 60 * 1_000; // 15 minutes
201
+ export async function callQualityGate(category, message) {
202
+ const terminal = new Set(["passed", "failed", "error", "timeout"]);
203
+ const deadline = Date.now() + QUALITY_GATE_MAX_POLL_MS;
204
+ let attempt = 0;
205
+ while (true) {
206
+ attempt += 1;
207
+ // Only pass the agent-authored message on the FIRST call — subsequent
208
+ // polls are internal and shouldn't spam the backend logs with duplicates.
209
+ const body = { category };
210
+ if (attempt === 1 && message) {
211
+ body.message = message;
212
+ }
213
+ const result = await apiRequest("POST", mcpPath("quality-gate"), body);
214
+ const status = String(result.status ?? "");
215
+ if (terminal.has(status)) {
216
+ return result;
217
+ }
218
+ if (status !== "running") {
219
+ // Unknown status — surface it verbatim so bugs are loud rather
220
+ // than swallowed by the polling loop.
221
+ return result;
222
+ }
223
+ if (Date.now() >= deadline) {
224
+ return {
225
+ ...result,
226
+ status: "timeout",
227
+ message: `quality_gate(${category}) did not reach a terminal state within ${QUALITY_GATE_MAX_POLL_MS / 1000}s. The reviewer dispatch may be stuck — check dispatch status and retry.`,
228
+ };
229
+ }
230
+ await new Promise((resolve) => setTimeout(resolve, QUALITY_GATE_POLL_INTERVAL_MS));
231
+ }
232
+ }
233
+ /**
234
+ * Orchestrator finalization — mandatory last action before exit.
235
+ *
236
+ * Returns {ok: true} when every outstanding check has cleared, transitioning the
237
+ * dispatch to completed. Returns {ok: false, outstanding: [...]} when any category
238
+ * is pending, any item failed, any template has validation_errors, or any directive
239
+ * has meta.selector_mismatches / meta.extraction_coverage_errors. In the non-empty
240
+ * case, the dispatch stays running so the agent can fix the issues and re-call.
241
+ */
242
+ export async function completeDispatch(message) {
243
+ return apiRequest("POST", mcpPath("complete"), {
244
+ message,
245
+ });
246
+ }
180
247
  export async function postProgress(progressMessage, phase) {
181
248
  return apiRequest("POST", mcpPath("progress"), {
182
249
  message: progressMessage,
package/dist/index.js CHANGED
@@ -66,6 +66,9 @@ const ROLE_TOOLS = {
66
66
  "context_workflow_inputs",
67
67
  "context_workflow_input_pages",
68
68
  "context_chat_history",
69
+ // Agent-driven quality gate lifecycle (orchestrator-only)
70
+ "quality_gate",
71
+ "complete",
69
72
  ]),
70
73
  "schema-builder": new Set([
71
74
  ...COMMON_TOOLS,
@@ -530,12 +533,23 @@ if (shouldRegister("template_create"))
530
533
  return jsonResult(result);
531
534
  });
532
535
  if (shouldRegister("template_list"))
533
- server.tool("template_list", "List all template definitions for the current schema", {}, async () => {
536
+ server.tool("template_list", "List all template definitions for the current schema. " +
537
+ "Each template includes a `validation_errors` field — when non-null it indicates unresolved " +
538
+ "coherence-check failures (field_warnings, style_warnings, meta_warnings, region_warnings, " +
539
+ "formatter_warnings, required_warnings) from the most recent mutation. Orchestrators do not " +
540
+ "need to poll this — `complete` automatically surfaces template validation errors as " +
541
+ "`template_errors` in its outstanding list.", {}, async () => {
534
542
  const result = await api.listTemplates();
535
543
  return jsonResult(result);
536
544
  });
537
545
  if (shouldRegister("template_get"))
538
- server.tool("template_get", "Get full details of a template definition including template_json", {
546
+ server.tool("template_get", "Get full details of a template definition including template_json. " +
547
+ "The response includes a `validation_errors` field — when non-null it indicates unresolved " +
548
+ "coherence-check failures (field_warnings, style_warnings, meta_warnings, region_warnings, " +
549
+ "formatter_warnings, required_warnings). Template-builder sub-agents should inspect this field " +
550
+ "after their own mutations to self-correct before handing work back. Orchestrators do not need " +
551
+ "to poll this — `complete` automatically surfaces template validation errors in its " +
552
+ "outstanding list.", {
539
553
  template_id: z.number().describe("ID of the template to fetch"),
540
554
  }, async ({ template_id }) => {
541
555
  const result = await api.getTemplateDetails(template_id);
@@ -666,6 +680,42 @@ if (shouldRegister("quality_gate_submit_review"))
666
680
  const result = await api.submitQualityGateReview(quality_gate_item_id, status, analysis, message);
667
681
  return jsonResult(result);
668
682
  });
683
+ if (shouldRegister("quality_gate"))
684
+ server.tool("quality_gate", "Run the quality gate for a single category (schema, directive, or template). " +
685
+ "BLOCKING — this call dispatches a reviewer (if one isn't already running) and waits " +
686
+ "until the gate reaches a terminal state. You receive ONE response per call: never " +
687
+ "poll the same category repeatedly. Response statuses: " +
688
+ "'passed' means all items are green (safe to move on); " +
689
+ "'failed' means items need fixes — read each item's `analysis`, apply fixes via " +
690
+ "mutation tools (which auto-invalidate the category), then call this tool again; " +
691
+ "'error' means the reviewer dispatch itself failed — surface the error to the user " +
692
+ "and stop (do NOT retry the same category); " +
693
+ "'timeout' means the call hit its 15-minute wall-time cap — the reviewer may be " +
694
+ "stuck, report it and move on. " +
695
+ "Parallelism is still available via concurrent tool calls for DIFFERENT categories " +
696
+ "(e.g. schema + directive in parallel). Do NOT call this tool twice for the same " +
697
+ "category before the first call returns.", {
698
+ category: z
699
+ .enum(["schema", "directive", "template"])
700
+ .describe("The quality gate category to check. Must be one of: schema, directive, template."),
701
+ message: messageParam,
702
+ }, async ({ category, message }) => {
703
+ const result = await api.callQualityGate(category, message);
704
+ return jsonResult(result);
705
+ });
706
+ if (shouldRegister("complete"))
707
+ server.tool("complete", "MANDATORY final action — call this to finalize your dispatch. Aggregates every known " +
708
+ "failure surface (pending quality gate categories, failed items, template validation " +
709
+ "errors, directive meta errors) and either transitions your dispatch to completed " +
710
+ "when everything is clean (returns {ok: true}) or returns {ok: false, outstanding: [...]} " +
711
+ "listing exactly what still needs fixing. When outstanding is non-empty, work through " +
712
+ "each item and call this tool again. Do NOT exit your session without calling this tool " +
713
+ "successfully — your dispatch will be marked dead by the heartbeat watchdog instead.", {
714
+ message: messageParam,
715
+ }, async ({ message }) => {
716
+ const result = await api.completeDispatch(message);
717
+ return jsonResult(result);
718
+ });
669
719
  // ============================================================================
670
720
  // Style Library Tools (read-only)
671
721
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/schema-mcp-server",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "MCP server for Schema Builder - translates Claude Code tool calls into Laravel API requests",
5
5
  "license": "MIT",
6
6
  "repository": {