@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.
@@ -259,29 +259,57 @@ export const TOOL_REGISTRY = [
259
259
  name: "project_operation",
260
260
  handler: handleProjectOperation,
261
261
  config: {
262
- title: "πŸ—‚οΈ Project - Namespace & Object Graph",
263
- description: "Phase 2 project namespace service β€” manage and query the local object dependency graph (DAG) " +
264
- "built on top of local_mark tags. No on-chain writes; all data comes from local marks tagged " +
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
- "Actions:\n" +
267
- " β€’ list_projects β€” List all discovered projects (grouped by `project:<prefix>` tag) with object counts.\n" +
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 objects tagged `shareable:true` (cross-project reusable: common Permission, 3rd-party Arb).\n" +
270
- " β€’ dependencies β€” Trace forward dependencies of an object (BFS transitive closure + dangling refs).\n" +
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 for a service (dangling deps + cycles).\n" +
273
- " β€’ cross_project_refs β€” Find edges that cross project boundaries.\n" +
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 the in-memory graph to ~/.wowok/project-graph.json (persist across sessions).\n" +
276
- " β€’ load_graph β€” Load the graph from file (restore after restart).\n\n" +
277
- "NOTE: The graph is in-memory by default. Use save_graph/load_graph for cross-session persistence " +
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: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
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 TOOL_REGISTRY.map(entry => {
478
- const preGenerated = loadPreGeneratedSchema(entry.name);
479
- const inputSchema = preGenerated ?? zodToJsonSchemaSafe(entry.config.inputSchema, entry.name);
480
- return {
481
- name: entry.name,
482
- title: entry.config.title,
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
- for (const entry of TOOL_REGISTRY) {
490
- const timeoutMs = entry.timeoutMs ?? 60000;
491
- const handlerWithRules = async (args) => {
492
- return dispatchToolCall(entry.name, args);
493
- };
494
- const wrapped = wrapHandlerWithErrorShape(handlerWithRules, timeoutMs, entry.name, entry.errorShapeBuilder);
495
- server.registerTool(entry.name, entry.config, withTelemetry(entry.name, wrapped));
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: TOOL_REGISTRY.map(entry => {
502
- const rawSchema = loadPreGeneratedSchema(entry.name) ??
503
- zodToJsonSchemaSafe(entry.config.inputSchema, entry.name);
504
- const inputSchema = ensureObjectType(rawSchema);
505
- const toolDef = {
506
- name: entry.name,
507
- title: entry.config.title,
508
- description: entry.config.description,
509
- inputSchema,
510
- annotations: entry.config.annotations,
511
- _meta: entry.config._meta,
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) {
@@ -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 && result?.structuredContent) {
145
- const sc = result.structuredContent;
146
- const semantic = sc.semantic;
147
- if (semantic) {
148
- const existing = Array.isArray(semantic.harness_warnings) ? semantic.harness_warnings : [];
149
- semantic.harness_warnings = [...existing, ...harnessWarnings];
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
- sc.harness_warnings = harnessWarnings;
204
+ for (const w of harnessWarnings) {
205
+ console.warn(`[harness] ${w}`);
206
+ }
153
207
  }
154
208
  }
155
209
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wowok/agent-mcp",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "Making It Easy for AI Agents to Communicate, Collaborate, Trade, and Trust.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",