@wowok/agent-mcp 2.5.0 β 2.5.2
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.
- package/dist/knowledge/mcp-schema-audit.js +3 -2
- package/dist/project/business-puzzle.d.ts +1 -0
- package/dist/project/business-puzzle.js +11 -0
- package/dist/project/deployment-doc.d.ts +11 -0
- package/dist/project/deployment-doc.js +37 -0
- package/dist/project/index.d.ts +25 -0
- package/dist/project/index.js +42 -0
- package/dist/project/risk-aggregator.d.ts +7 -0
- package/dist/project/risk-aggregator.js +15 -0
- package/dist/schema/call/base.js +2 -2
- package/dist/schema/project/index.d.ts +223 -3
- package/dist/schema/project/index.js +60 -1
- package/dist/schemas/index.json +1 -1
- package/dist/schemas/onchain_operations.schema.json +0 -16
- package/dist/schemas/onchain_operations_guard.schema.json +0 -1092
- package/dist/schemas/onchain_operations_payment.schema.json +0 -515
- package/dist/schemas/onchain_operations_permission.schema.json +0 -515
- package/dist/schemas/onchain_operations_personal.schema.json +0 -515
- package/dist/schemas/project_operation.output.json +196 -0
- package/dist/schemas/project_operation.schema.json +3 -2
- package/dist/tools/handlers/project.js +51 -2
- package/dist/tools/index.js +387 -54
- package/dist/tools/wrap.js +61 -7
- package/package.json +1 -1
- package/dist/schemas/onchain_operations.output.json +0 -2819
package/dist/tools/index.js
CHANGED
|
@@ -259,29 +259,57 @@ export const TOOL_REGISTRY = [
|
|
|
259
259
|
name: "project_operation",
|
|
260
260
|
handler: handleProjectOperation,
|
|
261
261
|
config: {
|
|
262
|
-
title: "ποΈ Project -
|
|
263
|
-
description: "
|
|
264
|
-
"
|
|
262
|
+
title: "ποΈ Project - 5-Stage Deployment & Object Graph",
|
|
263
|
+
description: "Project-based deployment service β manage the local object dependency graph (DAG) AND guide users " +
|
|
264
|
+
"through the 5-stage project deployment workflow. All data comes from local marks tagged " +
|
|
265
265
|
"`project:<prefix>` (auto-injected by the ProjectService when enabled, default ON).\n\n" +
|
|
266
|
-
"
|
|
267
|
-
"
|
|
266
|
+
"βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n" +
|
|
267
|
+
" PROJECT-BASED DEPLOYMENT β 5-STAGE WORKFLOW (C1-C4)\n" +
|
|
268
|
+
"βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n" +
|
|
269
|
+
"When a user wants to build/deploy a service, follow these 5 stages IN ORDER. " +
|
|
270
|
+
"Each stage's output tells you the next action to call:\n\n" +
|
|
271
|
+
" Stage 1: Project Naming\n" +
|
|
272
|
+
" β Use `project` + `version` params to name the project (e.g. myshop_v1).\n" +
|
|
273
|
+
" Stage 2: Business Puzzle (action='analyze_intent')\n" +
|
|
274
|
+
" β Input: user_intent (natural language), industry hint.\n" +
|
|
275
|
+
" β Output: ODG (object dependency graph), puzzles, missing fields, next_step.\n" +
|
|
276
|
+
" β Guide user to fill missing fields, then proceed to Stage 3.\n" +
|
|
277
|
+
" Stage 3: Risk Calibration (action='aggregate_risks')\n" +
|
|
278
|
+
" β Input: puzzles from Stage 2.\n" +
|
|
279
|
+
" β Output: risk findings, can_proceed flag.\n" +
|
|
280
|
+
" β CRITICAL risks block; HIGH/MEDIUM/LOW only warn. Fix CRITICAL before proceeding.\n" +
|
|
281
|
+
" Stage 4: Deployment Doc (action='generate_deployment_doc')\n" +
|
|
282
|
+
" β Input: objects, edges, steps (topological plan from Stage 2-3).\n" +
|
|
283
|
+
" β Output: markdown deployment doc, D-01..D-18 scanner checks, can_proceed.\n" +
|
|
284
|
+
" β 0 D-errors required before Stage 5.\n" +
|
|
285
|
+
" Stage 5: Substep Trace (action='trace_substeps')\n" +
|
|
286
|
+
" β Input: substeps (from deployment doc), network (testnet/mainnet).\n" +
|
|
287
|
+
" β Output: coherence check, D-10 substep linkage, can_proceed.\n" +
|
|
288
|
+
" β Execute each substep via onchain_operations, record outcomes.\n\n" +
|
|
289
|
+
" Status Query (action='get_project_status')\n" +
|
|
290
|
+
" β Input: project, version.\n" +
|
|
291
|
+
" β Output: current stage (1-5), stage_name, next_action, can_proceed.\n" +
|
|
292
|
+
" β Call anytime to check which stage the project is in and what to do next.\n\n" +
|
|
293
|
+
"Correct object creation order (topological): Permission β Allocation β Guard β Machine β Service.\n" +
|
|
294
|
+
"All objects created as draft (publish=false) first; publish Machine before Service (last).\n\n" +
|
|
295
|
+
"βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n" +
|
|
296
|
+
" GRAPH QUERY ACTIONS (10)\n" +
|
|
297
|
+
"βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n" +
|
|
298
|
+
" β’ list_projects β List all discovered projects with object counts.\n" +
|
|
268
299
|
" β’ list_objects β List objects in a project (requires `project` param).\n" +
|
|
269
|
-
" β’ shareable β List
|
|
270
|
-
" β’ dependencies β Trace forward dependencies of an object (BFS
|
|
300
|
+
" β’ shareable β List cross-project reusable objects (common Permission, 3rd-party Arb).\n" +
|
|
301
|
+
" β’ dependencies β Trace forward dependencies of an object (BFS + dangling refs).\n" +
|
|
271
302
|
" β’ referenced_by β Reverse query: who references this object?\n" +
|
|
272
|
-
" β’ pre_publish_check β Pre-publish sanity check
|
|
273
|
-
" β’ cross_project_refs β Find edges
|
|
303
|
+
" β’ pre_publish_check β Pre-publish sanity check (dangling deps + cycles).\n" +
|
|
304
|
+
" β’ cross_project_refs β Find edges crossing project boundaries.\n" +
|
|
274
305
|
" β’ graph_stats β Graph statistics (node/edge counts, cycles, dangling deps).\n" +
|
|
275
|
-
" β’ save_graph β Save
|
|
276
|
-
" β’ load_graph β Load
|
|
277
|
-
"
|
|
278
|
-
"(or toggle graph_persist via config_operation for auto-save). " +
|
|
279
|
-
"list_projects/shareable always query local_mark (persistent).\n\n" +
|
|
280
|
-
"π‘ Schema hint: Use schema_query with action='get' and name='project_operation' to retrieve the exact parameter schema with all actions and field definitions.",
|
|
306
|
+
" β’ save_graph β Save graph to ~/.wowok/project-graph.json.\n" +
|
|
307
|
+
" β’ load_graph β Load graph from file (restore after restart).\n\n" +
|
|
308
|
+
"π‘ Schema hint: Use schema_query with action='get' and name='project_operation' for exact field definitions.",
|
|
281
309
|
inputSchema: ProjectOperationInputSchema,
|
|
282
310
|
outputSchema: ProjectOperationOutputSchema,
|
|
283
|
-
annotations: { readOnlyHint:
|
|
284
|
-
_meta: createToolMeta("project", ["project", "namespace", "graph", "dag", "dependency", "reference", "organization"]),
|
|
311
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
312
|
+
_meta: createToolMeta("project", ["project", "namespace", "graph", "dag", "dependency", "reference", "organization", "deployment", "5-stage", "analyze_intent", "aggregate_risks", "deployment_doc", "trace_substeps", "puzzle", "risk", "odg"]),
|
|
285
313
|
},
|
|
286
314
|
},
|
|
287
315
|
{
|
|
@@ -348,7 +376,123 @@ export const TOOL_REGISTRY = [
|
|
|
348
376
|
},
|
|
349
377
|
];
|
|
350
378
|
export const TOOL_REGISTRY_MAP = new Map(TOOL_REGISTRY.map(t => [t.name, t]));
|
|
379
|
+
const SUB_TOOL_NAMES = TOOL_REGISTRY.map(t => t.name);
|
|
380
|
+
const WowokToolRuntimeSchema = z.unknown().describe("WoWok unified tool input. Expected: { tool: \"<sub-tool>\", data: {<params>} }. " +
|
|
381
|
+
"Aliases accepted: tool_nameβtool, argsβdata, nameβtool, paramsβdata. " +
|
|
382
|
+
"If parameters don't match the sub-tool's schema, the response includes the correct schema for self-correction.");
|
|
383
|
+
const WowokToolDisplaySchema = z.object({
|
|
384
|
+
tool: z.enum(SUB_TOOL_NAMES).optional().describe("The sub-tool to execute. Each sub-tool has its own parameter schema."),
|
|
385
|
+
data: z.record(z.unknown()).optional().describe("Sub-tool specific parameters. If validation fails, the response includes the correct schema for this sub-tool β read it, fix your params, and retry."),
|
|
386
|
+
tool_name: z.enum(SUB_TOOL_NAMES).optional().describe("DEPRECATED alias for 'tool'. Use 'tool' instead."),
|
|
387
|
+
args: z.record(z.unknown()).optional().describe("DEPRECATED alias for 'data'. Use 'data' instead."),
|
|
388
|
+
name: z.enum(SUB_TOOL_NAMES).optional().describe("DEPRECATED alias for 'tool'. Use 'tool' instead."),
|
|
389
|
+
params: z.record(z.unknown()).optional().describe("DEPRECATED alias for 'data'. Use 'data' instead."),
|
|
390
|
+
}).describe("WoWok unified tool input. Call with { tool: \"<sub-tool>\", data: {<params>} }. " +
|
|
391
|
+
"Aliases: tool_name/nameβtool, args/paramsβdata.");
|
|
392
|
+
const WOWOK_TOOL_DESCRIPTION = [
|
|
393
|
+
"WoWok MCP unified interface. Call with { tool: \"<sub-tool>\", data: {<params>} }.",
|
|
394
|
+
"",
|
|
395
|
+
" tool: name of the sub-tool (string)",
|
|
396
|
+
" data: sub-tool parameters (object)",
|
|
397
|
+
"",
|
|
398
|
+
" Aliases accepted (deprecated): tool_name/name for tool, args/params for data.",
|
|
399
|
+
" Inline params also accepted: { tool: \"<sub-tool>\", <param1>: ..., <param2>: ... }",
|
|
400
|
+
"",
|
|
401
|
+
" EXAMPLE: wowok({ tool: \"schema_query\", data: { action: \"get\", name: \"onchain_operations\" } })",
|
|
402
|
+
" EXAMPLE: wowok({ tool: \"wowok_buildin_info\", data: { info: \"mainnet bridge tokens\" } })",
|
|
403
|
+
" EXAMPLE (inline params): wowok({ tool: \"schema_query\", action: \"get\", name: \"onchain_operations\" })",
|
|
404
|
+
"",
|
|
405
|
+
"Available sub-tools:",
|
|
406
|
+
" onchain_operations β On-chain ops: service, machine, progress, repository, arbitration, etc.",
|
|
407
|
+
" account_operation β LOCAL wallet management (generate, suspend, faucet, sign).",
|
|
408
|
+
" local_mark_operation β LOCAL address book (name/tag for addresses and object IDs).",
|
|
409
|
+
" local_info_operation β LOCAL private data (delivery addresses, phone numbers).",
|
|
410
|
+
" wip_file β Work-in-progress file management.",
|
|
411
|
+
" messenger_operation β End-to-end encrypted messaging.",
|
|
412
|
+
" guard2file β Export guard rules to file.",
|
|
413
|
+
" machineNode2file β Export machine nodes to file.",
|
|
414
|
+
" query_toolkit β Query on-chain objects by ID/type/owner.",
|
|
415
|
+
" onchain_table_data β Query on-chain dynamic field tables with pagination.",
|
|
416
|
+
" onchain_events β Watch on-chain events (arbitration, order, progress, demand).",
|
|
417
|
+
" wowok_buildin_info β Protocol constants, built-in permissions, bridge tokens.",
|
|
418
|
+
" schema_query β Query JSON schemas for any sub-tool (action: list/get/get_output/search).",
|
|
419
|
+
" bridge_operation β Cross-chain bridge between WOW and EVM (mainnet only).",
|
|
420
|
+
" project_operation β 5-stage project deployment workflow (C1-C4 + graph queries).",
|
|
421
|
+
" config_operation β Runtime service toggles (confirm_gate, harness, etc.).",
|
|
422
|
+
" trust_score β Service trust/risk assessment (0-100 score, 5 dimensions).",
|
|
423
|
+
"",
|
|
424
|
+
"SCHEMA-GATED EXECUTION:",
|
|
425
|
+
" If your parameters don't match the sub-tool's schema, the response will include:",
|
|
426
|
+
" result.status = \"schema_mismatch\"",
|
|
427
|
+
" result.errors = specific validation errors (field path + expected type)",
|
|
428
|
+
" result.hint = instructions to fix and retry",
|
|
429
|
+
" schema.input = the correct JSON schema for the sub-tool",
|
|
430
|
+
" schema.tool = which sub-tool this schema belongs to",
|
|
431
|
+
" Read the schema, fix your params, and retry. This is normal β no need to call schema_query separately.",
|
|
432
|
+
"",
|
|
433
|
+
"SCHEMA CACHING:",
|
|
434
|
+
" Each sub-tool has a DIFFERENT schema. When you receive a schema in a mismatch response:",
|
|
435
|
+
" 1. Read the schema carefully",
|
|
436
|
+
" 2. Fix your parameters",
|
|
437
|
+
" 3. Retry the call",
|
|
438
|
+
" 4. CACHE the schema in your context for future calls to the SAME sub-tool",
|
|
439
|
+
" You should only need to see each sub-tool's schema once per conversation.",
|
|
440
|
+
"",
|
|
441
|
+
"PROJECT-BASED DEPLOYMENT (5 stages):",
|
|
442
|
+
" Stage 1: Project naming (provide project + version params)",
|
|
443
|
+
" Stage 2: project_operation action='analyze_intent' β ODG + puzzles",
|
|
444
|
+
" Stage 3: project_operation action='aggregate_risks' β risk findings",
|
|
445
|
+
" Stage 4: project_operation action='generate_deployment_doc' β deployment doc",
|
|
446
|
+
" Stage 5: project_operation action='trace_substeps' β substep verification",
|
|
447
|
+
" Status: project_operation action='get_project_status' β current stage + next action",
|
|
448
|
+
"",
|
|
449
|
+
"Correct object creation order: Permission β Allocation β Guard β Machine β Service.",
|
|
450
|
+
"All objects created as draft (publish=false) first; publish Machine before Service (last).",
|
|
451
|
+
].join("\n");
|
|
452
|
+
function formatZodErrors(error) {
|
|
453
|
+
return error.issues.map(issue => {
|
|
454
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
455
|
+
return `${path}: ${issue.message}`;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function checkUnknownFields(schema, input) {
|
|
459
|
+
let innerSchema = schema;
|
|
460
|
+
while (true) {
|
|
461
|
+
const def = innerSchema._def;
|
|
462
|
+
if (!def || def.typeName !== "ZodEffects")
|
|
463
|
+
break;
|
|
464
|
+
innerSchema = def.schema ?? innerSchema;
|
|
465
|
+
}
|
|
466
|
+
const def = innerSchema._def;
|
|
467
|
+
const shape = def?.shape?.();
|
|
468
|
+
if (!shape || typeof input !== "object" || input === null)
|
|
469
|
+
return [];
|
|
470
|
+
const allowedFields = Object.keys(shape);
|
|
471
|
+
const inputFields = Object.keys(input);
|
|
472
|
+
return inputFields.filter(f => !allowedFields.includes(f));
|
|
473
|
+
}
|
|
474
|
+
function extractRelevantSchema(fullSchema, toolName, data) {
|
|
475
|
+
if (toolName === "onchain_operations" && typeof data === "object" && data !== null) {
|
|
476
|
+
const opType = data.operation_type;
|
|
477
|
+
if (typeof opType === "string") {
|
|
478
|
+
const subSchemaName = `onchain_operations_${opType}`;
|
|
479
|
+
const subSchema = loadPreGeneratedSchema(subSchemaName);
|
|
480
|
+
if (subSchema)
|
|
481
|
+
return subSchema;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return fullSchema;
|
|
485
|
+
}
|
|
486
|
+
function isSchemaValidationError(message) {
|
|
487
|
+
return (message.startsWith("Unknown ") && message.includes("field") ||
|
|
488
|
+
message.includes("expected") && message.includes("received") ||
|
|
489
|
+
message.includes("Invalid input") ||
|
|
490
|
+
message.includes("Required"));
|
|
491
|
+
}
|
|
351
492
|
export async function dispatchToolCall(name, args, options) {
|
|
493
|
+
if (name === "wowok") {
|
|
494
|
+
return handleWowok(args);
|
|
495
|
+
}
|
|
352
496
|
const entry = TOOL_REGISTRY_MAP.get(name);
|
|
353
497
|
if (!entry) {
|
|
354
498
|
throw new Error(`Unknown tool: ${name}. Available: ${TOOL_REGISTRY.map(t => t.name).join(", ")}`);
|
|
@@ -363,6 +507,206 @@ export async function dispatchToolCall(name, args, options) {
|
|
|
363
507
|
const wrapped = wrapHandlerWithErrorShape(harnessWrapped, timeoutMs, name, entry.errorShapeBuilder);
|
|
364
508
|
return wrapped(args);
|
|
365
509
|
}
|
|
510
|
+
const TOOL_FIELD_ALIASES = ["tool", "tool_name", "name"];
|
|
511
|
+
const DATA_FIELD_ALIASES = ["data", "args", "params", "parameters"];
|
|
512
|
+
const RESERVED_FIELD_NAMES = new Set([
|
|
513
|
+
...TOOL_FIELD_ALIASES,
|
|
514
|
+
...DATA_FIELD_ALIASES,
|
|
515
|
+
]);
|
|
516
|
+
function normalizeInput(args) {
|
|
517
|
+
const notes = [];
|
|
518
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
519
|
+
return { tool: undefined, data: {}, normalizationNotes: ["input is not an object"] };
|
|
520
|
+
}
|
|
521
|
+
const obj = args;
|
|
522
|
+
let tool;
|
|
523
|
+
for (const alias of TOOL_FIELD_ALIASES) {
|
|
524
|
+
if (typeof obj[alias] === "string" && obj[alias]) {
|
|
525
|
+
tool = obj[alias];
|
|
526
|
+
if (alias !== "tool") {
|
|
527
|
+
notes.push(`normalized '${alias}' β 'tool'`);
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
let data;
|
|
533
|
+
for (const alias of DATA_FIELD_ALIASES) {
|
|
534
|
+
const v = obj[alias];
|
|
535
|
+
if (v !== undefined && v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
536
|
+
data = v;
|
|
537
|
+
if (alias !== "data") {
|
|
538
|
+
notes.push(`normalized '${alias}' β 'data'`);
|
|
539
|
+
}
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
if (!data) {
|
|
544
|
+
const inlineParams = {};
|
|
545
|
+
let hasInline = false;
|
|
546
|
+
for (const key of Object.keys(obj)) {
|
|
547
|
+
if (!RESERVED_FIELD_NAMES.has(key)) {
|
|
548
|
+
inlineParams[key] = obj[key];
|
|
549
|
+
hasInline = true;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (hasInline) {
|
|
553
|
+
data = inlineParams;
|
|
554
|
+
notes.push("extracted inline params β 'data' (no data wrapper was provided)");
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
data = {};
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return { tool, data, normalizationNotes: notes };
|
|
561
|
+
}
|
|
562
|
+
async function handleWowok(args) {
|
|
563
|
+
const { tool: normalizedToolName, data: normalizedData, normalizationNotes } = normalizeInput(args);
|
|
564
|
+
if (!normalizedToolName) {
|
|
565
|
+
const receivedKeys = (args && typeof args === "object" && !Array.isArray(args))
|
|
566
|
+
? Object.keys(args).join(", ") || "(none)"
|
|
567
|
+
: `(non-object input: ${typeof args})`;
|
|
568
|
+
const errors = [
|
|
569
|
+
`Missing 'tool' field. Received keys: ${receivedKeys}`,
|
|
570
|
+
`Expected one of: ${SUB_TOOL_NAMES.join(", ")}`,
|
|
571
|
+
];
|
|
572
|
+
return {
|
|
573
|
+
content: [{
|
|
574
|
+
type: "text",
|
|
575
|
+
text: `Missing 'tool' field. Call format: wowok({ tool: "<sub-tool>", data: {<params>} }). Received keys: ${receivedKeys}. Available sub-tools: ${SUB_TOOL_NAMES.join(", ")}`,
|
|
576
|
+
}],
|
|
577
|
+
structuredContent: {
|
|
578
|
+
result: {
|
|
579
|
+
status: "schema_mismatch",
|
|
580
|
+
errors,
|
|
581
|
+
hint: `Provide 'tool' (one of: ${SUB_TOOL_NAMES.join(", ")}) and 'data' (sub-tool parameters). Aliases accepted: tool_name/nameβtool, args/paramsβdata.`,
|
|
582
|
+
received: args,
|
|
583
|
+
},
|
|
584
|
+
schema: {
|
|
585
|
+
input: zodToJsonSchemaSafe(WowokToolDisplaySchema, "wowok"),
|
|
586
|
+
tool: "wowok",
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
const entry = TOOL_REGISTRY_MAP.get(normalizedToolName);
|
|
592
|
+
if (!entry) {
|
|
593
|
+
const suggestions = TOOL_REGISTRY
|
|
594
|
+
.map(t => t.name)
|
|
595
|
+
.filter(name => name.includes(normalizedToolName) || normalizedToolName.includes(name))
|
|
596
|
+
.slice(0, 5);
|
|
597
|
+
return {
|
|
598
|
+
content: [{
|
|
599
|
+
type: "text",
|
|
600
|
+
text: `Unknown sub-tool: '${normalizedToolName}'. Available: ${TOOL_REGISTRY.map(t => t.name).join(", ")}${suggestions.length > 0 ? `. Did you mean: ${suggestions.join(", ")}?` : ""}`,
|
|
601
|
+
}],
|
|
602
|
+
structuredContent: {
|
|
603
|
+
result: {
|
|
604
|
+
status: "error",
|
|
605
|
+
errors: [`Unknown sub-tool: ${normalizedToolName}`],
|
|
606
|
+
hint: `Available sub-tools: ${TOOL_REGISTRY.map(t => t.name).join(", ")}${suggestions.length > 0 ? `. Did you mean: ${suggestions.join(", ")}?` : ""}`,
|
|
607
|
+
suggestions,
|
|
608
|
+
},
|
|
609
|
+
schema: null,
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
if (normalizationNotes.length > 0) {
|
|
614
|
+
console.warn(`[wowok] input normalization for '${normalizedToolName}': ${normalizationNotes.join("; ")}`);
|
|
615
|
+
}
|
|
616
|
+
const validationResult = entry.config.inputSchema.safeParse(normalizedData);
|
|
617
|
+
const unknownFields = checkUnknownFields(entry.config.inputSchema, normalizedData);
|
|
618
|
+
if (!validationResult.success || unknownFields.length > 0) {
|
|
619
|
+
const allErrors = [];
|
|
620
|
+
if (!validationResult.success) {
|
|
621
|
+
allErrors.push(...formatZodErrors(validationResult.error));
|
|
622
|
+
}
|
|
623
|
+
if (unknownFields.length > 0) {
|
|
624
|
+
allErrors.push(`Unknown field(s): ${unknownFields.join(", ")}`);
|
|
625
|
+
}
|
|
626
|
+
const fullSchema = loadPreGeneratedSchema(normalizedToolName) ?? zodToJsonSchemaSafe(entry.config.inputSchema, normalizedToolName);
|
|
627
|
+
const relevantSchema = extractRelevantSchema(fullSchema, normalizedToolName, normalizedData);
|
|
628
|
+
return {
|
|
629
|
+
content: [{
|
|
630
|
+
type: "text",
|
|
631
|
+
text: `Schema mismatch for ${normalizedToolName}. ${allErrors.length} error(s). Read the schema below, fix your params, and retry.`,
|
|
632
|
+
}],
|
|
633
|
+
structuredContent: {
|
|
634
|
+
result: {
|
|
635
|
+
status: "schema_mismatch",
|
|
636
|
+
errors: allErrors,
|
|
637
|
+
hint: `Fix your parameters based on the schema below and retry. CACHE THIS SCHEMA for future ${normalizedToolName} calls β you don't need to query it again.`,
|
|
638
|
+
},
|
|
639
|
+
schema: {
|
|
640
|
+
input: relevantSchema,
|
|
641
|
+
tool: normalizedToolName,
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
try {
|
|
647
|
+
const subResult = await dispatchToolCall(normalizedToolName, normalizedData);
|
|
648
|
+
const sc = subResult.structuredContent;
|
|
649
|
+
const resultField = sc?.result;
|
|
650
|
+
const isErrorResponse = resultField?.status === "error" || resultField?.operation === "error";
|
|
651
|
+
if (isErrorResponse) {
|
|
652
|
+
return {
|
|
653
|
+
content: subResult.content,
|
|
654
|
+
structuredContent: {
|
|
655
|
+
result: {
|
|
656
|
+
status: "error",
|
|
657
|
+
data: subResult.structuredContent,
|
|
658
|
+
errors: [subResult.content[0]?.text ?? "Sub-tool returned an error"],
|
|
659
|
+
},
|
|
660
|
+
schema: null,
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
return {
|
|
665
|
+
content: subResult.content,
|
|
666
|
+
structuredContent: {
|
|
667
|
+
result: {
|
|
668
|
+
status: "success",
|
|
669
|
+
data: subResult.structuredContent,
|
|
670
|
+
},
|
|
671
|
+
schema: null,
|
|
672
|
+
},
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
catch (error) {
|
|
676
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
677
|
+
if (isSchemaValidationError(errorMessage)) {
|
|
678
|
+
const fullSchema = loadPreGeneratedSchema(normalizedToolName) ?? zodToJsonSchemaSafe(entry.config.inputSchema, normalizedToolName);
|
|
679
|
+
const relevantSchema = extractRelevantSchema(fullSchema, normalizedToolName, normalizedData);
|
|
680
|
+
return {
|
|
681
|
+
content: [{
|
|
682
|
+
type: "text",
|
|
683
|
+
text: `Schema mismatch for ${normalizedToolName}: ${errorMessage}`,
|
|
684
|
+
}],
|
|
685
|
+
structuredContent: {
|
|
686
|
+
result: {
|
|
687
|
+
status: "schema_mismatch",
|
|
688
|
+
errors: [errorMessage],
|
|
689
|
+
hint: `Fix your parameters based on the schema below and retry. CACHE THIS SCHEMA for future ${normalizedToolName} calls.`,
|
|
690
|
+
},
|
|
691
|
+
schema: {
|
|
692
|
+
input: relevantSchema,
|
|
693
|
+
tool: normalizedToolName,
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
return {
|
|
699
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
700
|
+
structuredContent: {
|
|
701
|
+
result: {
|
|
702
|
+
status: "error",
|
|
703
|
+
errors: [errorMessage],
|
|
704
|
+
},
|
|
705
|
+
schema: null,
|
|
706
|
+
},
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
}
|
|
366
710
|
function zodToJsonSchemaSafe(schema, name) {
|
|
367
711
|
try {
|
|
368
712
|
const jsonSchema = zodToJsonSchema(schema, {
|
|
@@ -474,49 +818,38 @@ function loadPreGeneratedOutputSchema(toolName) {
|
|
|
474
818
|
return null;
|
|
475
819
|
}
|
|
476
820
|
export function listTools() {
|
|
477
|
-
return
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
description: entry.config.description,
|
|
484
|
-
inputSchema,
|
|
485
|
-
};
|
|
486
|
-
});
|
|
821
|
+
return [{
|
|
822
|
+
name: "wowok",
|
|
823
|
+
title: "WoWok MCP",
|
|
824
|
+
description: WOWOK_TOOL_DESCRIPTION,
|
|
825
|
+
inputSchema: zodToJsonSchemaSafe(WowokToolDisplaySchema, "wowok"),
|
|
826
|
+
}];
|
|
487
827
|
}
|
|
488
828
|
export function registerAllTools(server) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
829
|
+
const timeoutMs = 120000;
|
|
830
|
+
const wrapped = wrapHandlerWithErrorShape(handleWowok, timeoutMs, "wowok", undefined);
|
|
831
|
+
server.registerTool("wowok", {
|
|
832
|
+
title: "WoWok MCP",
|
|
833
|
+
description: WOWOK_TOOL_DESCRIPTION,
|
|
834
|
+
inputSchema: WowokToolRuntimeSchema,
|
|
835
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
836
|
+
}, withTelemetry("wowok", wrapped));
|
|
497
837
|
overrideToolsListHandler(server);
|
|
498
838
|
}
|
|
499
839
|
function overrideToolsListHandler(server) {
|
|
500
840
|
server.server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
501
|
-
tools:
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
if (entry.config.outputSchema) {
|
|
514
|
-
const outputSchema = loadPreGeneratedOutputSchema(entry.name) ??
|
|
515
|
-
zodToJsonSchemaSafe(entry.config.outputSchema, `${entry.name}_output`);
|
|
516
|
-
toolDef.outputSchema = outputSchema;
|
|
517
|
-
}
|
|
518
|
-
return toolDef;
|
|
519
|
-
}),
|
|
841
|
+
tools: [{
|
|
842
|
+
name: "wowok",
|
|
843
|
+
title: "WoWok MCP",
|
|
844
|
+
description: WOWOK_TOOL_DESCRIPTION,
|
|
845
|
+
inputSchema: ensureObjectType(zodToJsonSchemaSafe(WowokToolDisplaySchema, "wowok")),
|
|
846
|
+
annotations: {
|
|
847
|
+
readOnlyHint: false,
|
|
848
|
+
destructiveHint: true,
|
|
849
|
+
idempotentHint: false,
|
|
850
|
+
openWorldHint: true,
|
|
851
|
+
},
|
|
852
|
+
}],
|
|
520
853
|
}));
|
|
521
854
|
}
|
|
522
855
|
function ensureObjectType(schema) {
|
package/dist/tools/wrap.js
CHANGED
|
@@ -4,6 +4,7 @@ import { shouldRunLightHooks, shouldRunHeavyHooks, recordOperationFailure, } fro
|
|
|
4
4
|
import { getHarness } from "../harness/index.js";
|
|
5
5
|
import { recordOperationOutcome } from "../experience/index.js";
|
|
6
6
|
import { recordIndustrySignal } from "../knowledge/flywheel-loop.js";
|
|
7
|
+
import { getStageCache } from "../project/index.js";
|
|
7
8
|
export function withTimeout(promise, timeoutMs = 30000, operationName = "operation") {
|
|
8
9
|
let timer;
|
|
9
10
|
return Promise.race([
|
|
@@ -57,6 +58,34 @@ export const operationErrorShape = (error, classified, ctx) => ({
|
|
|
57
58
|
export const strictStatusErrorShape = (error, _classified, _ctx) => ({
|
|
58
59
|
result: { status: "error", error },
|
|
59
60
|
});
|
|
61
|
+
function checkProjectStage(action, project, version) {
|
|
62
|
+
const entry = getStageCache(project, version);
|
|
63
|
+
if (!entry) {
|
|
64
|
+
if (action === "analyze_intent" || action === "get_project_status") {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return `project_stage: calling '${action}' before 'analyze_intent' β recommend calling analyze_intent first for project '${project}:${version}'`;
|
|
68
|
+
}
|
|
69
|
+
const stageOrder = {
|
|
70
|
+
analyze_intent: 2,
|
|
71
|
+
aggregate_risks: 3,
|
|
72
|
+
generate_deployment_doc: 4,
|
|
73
|
+
trace_substeps: 5,
|
|
74
|
+
};
|
|
75
|
+
const requiredStage = stageOrder[action];
|
|
76
|
+
if (!requiredStage)
|
|
77
|
+
return null;
|
|
78
|
+
if (entry.stage < requiredStage - 1) {
|
|
79
|
+
return `project_stage: calling '${action}' (stage ${requiredStage}) but project is at stage ${entry.stage} β recommend completing prior stages first for '${project}:${version}'`;
|
|
80
|
+
}
|
|
81
|
+
if (action === "generate_deployment_doc" && entry.risk_can_proceed === false) {
|
|
82
|
+
return `project_stage: calling 'generate_deployment_doc' but CRITICAL risks are unresolved β recommend fixing risks first for '${project}:${version}'`;
|
|
83
|
+
}
|
|
84
|
+
if (action === "trace_substeps" && entry.doc_error_count && entry.doc_error_count > 0) {
|
|
85
|
+
return `project_stage: calling 'trace_substeps' but deployment doc has ${entry.doc_error_count} D-error(s) β recommend fixing doc errors first for '${project}:${version}'`;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
60
89
|
export function dispatchWithHarness(handler, toolName) {
|
|
61
90
|
return async (args) => {
|
|
62
91
|
const harnessWarnings = [];
|
|
@@ -78,6 +107,22 @@ export function dispatchWithHarness(handler, toolName) {
|
|
|
78
107
|
harnessWarnings.push(`pre_call hook failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
79
108
|
}
|
|
80
109
|
}
|
|
110
|
+
if (toolName === "project_operation" && runLight) {
|
|
111
|
+
try {
|
|
112
|
+
const projArgs = args;
|
|
113
|
+
const action = projArgs?.action;
|
|
114
|
+
const project = projArgs?.project;
|
|
115
|
+
const version = projArgs?.version;
|
|
116
|
+
if (action && project && version) {
|
|
117
|
+
const stageCheck = checkProjectStage(action, project, version);
|
|
118
|
+
if (stageCheck) {
|
|
119
|
+
harnessWarnings.push(stageCheck);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
81
126
|
if (isPublishOp) {
|
|
82
127
|
try {
|
|
83
128
|
harnessWarnings.push("pre_publish: full-tier check engaged");
|
|
@@ -141,15 +186,24 @@ export function dispatchWithHarness(handler, toolName) {
|
|
|
141
186
|
harnessWarnings.push(`signal collection failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
142
187
|
}
|
|
143
188
|
}
|
|
144
|
-
if (harnessWarnings.length > 0
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
189
|
+
if (harnessWarnings.length > 0) {
|
|
190
|
+
if (result?.structuredContent) {
|
|
191
|
+
const sc = result.structuredContent;
|
|
192
|
+
const semantic = sc.semantic;
|
|
193
|
+
if (semantic) {
|
|
194
|
+
const existing = Array.isArray(semantic.warnings) ? semantic.warnings : [];
|
|
195
|
+
semantic.warnings = [...existing, ...harnessWarnings];
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
for (const w of harnessWarnings) {
|
|
199
|
+
console.warn(`[harness] ${w}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
150
202
|
}
|
|
151
203
|
else {
|
|
152
|
-
|
|
204
|
+
for (const w of harnessWarnings) {
|
|
205
|
+
console.warn(`[harness] ${w}`);
|
|
206
|
+
}
|
|
153
207
|
}
|
|
154
208
|
}
|
|
155
209
|
return result;
|