MCPcopy
hub / github.com/BuilderIO/agent-native / wrapWithValidation

Function wrapWithValidation

packages/core/src/action.ts:392–466  ·  view source on GitHub ↗

* Wrap an action's run function with schema validation. * Invalid inputs get a clear error message (including what was actually passed) * so the agent can see its own mistake and correct it on the next turn.

(
  schema: StandardSchemaV1,
  run: Function,
  toolParameters?: ActionTool["parameters"],
)

Source from the content-addressed store, hash-verified

390 * so the agent can see its own mistake and correct it on the next turn.
391 */
392function wrapWithValidation(
393 schema: StandardSchemaV1,
394 run: Function,
395 toolParameters?: ActionTool["parameters"],
396): (args: any) => any {
397 return async (args: any) => {
398 const result = await schema["~standard"].validate(args);
399 if (result.issues) {
400 // Split issues into "missing required field" vs other validation errors
401 // so the error message reads naturally rather than as "fieldName: Required".
402 const missing: string[] = [];
403 const other: string[] = [];
404 for (const issue of result.issues) {
405 const pathStr = issue.path
406 ? issue.path.map((p) => (typeof p === "object" ? p.key : p)).join(".")
407 : "";
408 const msg = String(issue.message ?? "");
409 // Zod emits "Required" for missing fields; other libraries may use
410 // similar wording. Treat any variant as "missing".
411 if (
412 pathStr &&
413 (msg === "Required" ||
414 /invalid.*undefined/i.test(msg) ||
415 /expected.*received undefined/i.test(msg))
416 ) {
417 missing.push(pathStr);
418 } else {
419 other.push(pathStr ? `${pathStr}: ${msg}` : msg);
420 }
421 }
422
423 const parts: string[] = [];
424 if (missing.length > 0) {
425 parts.push(
426 `Missing required parameter${missing.length === 1 ? "" : "s"}: ${missing.join(", ")}`,
427 );
428 }
429 if (other.length > 0) {
430 parts.push(other.join("; "));
431 }
432
433 // Echo the args that were actually passed so the caller (usually an
434 // agent) can see exactly what it sent and fix its next call.
435 let received: string;
436 try {
437 received = JSON.stringify(args);
438 if (received.length > 500) received = received.slice(0, 500) + "…";
439 } catch {
440 received = String(args);
441 }
442
443 // Also show the EXPECTED signature so the agent doesn't have to guess.
444 // Format: `{ deckId*: string, content*: string, slideId?: string, ... }`
445 // where `*` = required, `?` = optional.
446 let expected = "";
447 if (toolParameters?.properties) {
448 const required = new Set(toolParameters.required ?? []);
449 const sig = Object.entries(toolParameters.properties)

Callers 1

defineActionFunction · 0.85

Calls 2

validateMethod · 0.80
runFunction · 0.50

Tested by

no test coverage detected