{"record":{"id":"97390d4cc920e098","repo":"ruvnet/ruflo","slug":"allowedmcptools-must-have-1-entry","errorCode":null,"errorMessage":"allowedMcpTools must have ≥1 entry","messagePattern":"allowedMcpTools must have ≥1 entry","errorType":"validation","errorClass":"PodTemplateValidationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/business-pods/pod-schema.ts","lineNumber":217,"sourceCode":"      'roomId may only contain [A-Za-z0-9_.\\\\-:/@#]',\n      '/',\n    );\n  }\n  const agents = requireArray(json, 'agents', '/', validatePodAgent);\n  if (agents.length === 0) {\n    throw new PodTemplateValidationError('agents must have ≥1 entry', '/');\n  }\n  const allowedMcpTools = requireArray(json, 'allowedMcpTools', '/', (t, tp) => {\n    if (typeof t !== 'string' || t.length === 0) {\n      throw new PodTemplateValidationError(\n        'allowedMcpTools entries must be non-empty strings',\n        tp,\n      );\n    }\n    return t;\n  });\n  if (allowedMcpTools.length === 0) {\n    throw new PodTemplateValidationError('allowedMcpTools must have ≥1 entry', '/');\n  }\n  const bench = validatePodBench(json.bench, '/bench');\n  const piiPolicy = requireString(json, 'piiPolicy', '/');\n  if (!PII_POLICIES.includes(piiPolicy as PiiPolicy)) {\n    throw new PodTemplateValidationError(\n      `piiPolicy must be one of: ${PII_POLICIES.join(', ')}`,\n      '/',\n    );\n  }\n  const budgetUsdMonthly = requireNumber(json, 'budgetUsdMonthly', '/');\n  if (budgetUsdMonthly < 0) {\n    throw new PodTemplateValidationError('budgetUsdMonthly must be ≥0', '/');\n  }\n  const budgetUsdPerRun = requireNumber(json, 'budgetUsdPerRun', '/');\n  if (budgetUsdPerRun < 0) {\n    throw new PodTemplateValidationError('budgetUsdPerRun must be ≥0', '/');\n  }\n  if (budgetUsdMonthly > 0 && budgetUsdPerRun > budgetUsdMonthly) {","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/business-pods/pod-schema.ts#L199-L235","documentation":"After all allowedMcpTools entries pass the per-entry string check, validatePodTemplate() rejects an empty array. The tool allowlist is deny-by-default, so an empty list would silently block every MCP call; the schema instead forces you to explicitly name at least one permitted tool.","triggerScenarios":"A template with \"allowedMcpTools\": []. (An empty-string or non-string entry would have thrown error 188 first; this error means every entry was a valid non-empty string but there are none.)","commonSituations":"Templates intended to be fully sandboxed ('no tools') — not expressible, you must enumerate at least one tool; allowlist generation that filters aggressively and produces an empty set; removing tools during lockdown hardening.","solutions":["List the minimal set of tools the pod genuinely needs, e.g. [\"memory_store\"]","If the pod truly needs zero MCP tools, that configuration is not supported — reconsider whether a pod template is the right vehicle","Regenerate the allowlist from the agent's actual tool usage logs to get a non-empty, accurate set"],"exampleFix":"// before\n\"allowedMcpTools\": []\n// after\n\"allowedMcpTools\": [\"memory_store\"]","handlingStrategy":"try-catch","validationCode":"if (!Array.isArray(json.allowedMcpTools) || json.allowedMcpTools.length === 0) {\n  throw new Error('enumerate at least one MCP tool in allowedMcpTools');\n}","typeGuard":"function hasNonEmptyToolAllowlist(v: unknown): boolean {\n  const a = (v as { allowedMcpTools?: unknown[] })?.allowedMcpTools;\n  return Array.isArray(a) && a.length > 0;\n}","tryCatchPattern":"try { validatePodTemplate(json); } catch (err) {\n  if (err instanceof PodTemplateValidationError && /allowedMcpTools must have/.test(err.message)) {\n    // deny-by-default: 'no tools' is not expressible — add the minimal tool set\n  }\n}","preventionTips":["Derive the allowlist from observed tool-usage logs so it is never empty","Review lockdown edits that strip all tools from a template"],"tags":["schema-validation","business-pods","mcp-tools","allowlist","configuration"],"backgroundTag":"schema-validation-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}