fix: recognize dynamic AI Tool nodes in validator (Issue #522) (#526)

When n8n connects any node to an AI Agent's tool slot, it creates a
dynamic Tool variant at runtime (e.g., googleDrive → googleDriveTool).
These don't exist in npm packages, causing false "unknown node type"
errors.

Added validation-time inference: when a *Tool node type is not found,
check if the base node exists. If yes, treat as valid with warning.

Changes:
- workflow-validator.ts: Add INFERRED_TOOL_VARIANT logic
- node-similarity-service.ts: Add 98% confidence for valid Tool variants
- Added 7 unit tests for inferred tool variant functionality

Fixes #522

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Romuald Członkowski <romualdczlonkowski@MacBook-Pro-Romuald.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Romuald Członkowski
2026-01-07 18:09:55 +01:00
committed by GitHub
parent 861005eeed
commit ce2c94c1a5
11 changed files with 425 additions and 7 deletions

View File

@@ -398,7 +398,39 @@ export class WorkflowValidator {
const normalizedType = NodeTypeNormalizer.normalizeToFullForm(node.type);
// Get node definition using normalized type (needed for typeVersion validation)
const nodeInfo = this.nodeRepository.getNode(normalizedType);
let nodeInfo = this.nodeRepository.getNode(normalizedType);
// Check if this is a dynamic Tool variant (e.g., googleDriveTool, googleSheetsTool)
// n8n creates these at runtime when ANY node is used in an AI Agent's tool slot,
// but they don't exist in npm packages. We infer validity if the base node exists.
// See: https://github.com/czlonkowski/n8n-mcp/issues/522
if (!nodeInfo && ToolVariantGenerator.isToolVariantNodeType(normalizedType)) {
const baseNodeType = ToolVariantGenerator.getBaseNodeType(normalizedType);
if (baseNodeType) {
const baseNodeInfo = this.nodeRepository.getNode(baseNodeType);
if (baseNodeInfo) {
// Valid inferred tool variant - base node exists
result.warnings.push({
type: 'warning',
nodeId: node.id,
nodeName: node.name,
message: `Node type "${node.type}" is inferred as a dynamic AI Tool variant of "${baseNodeType}". ` +
`This Tool variant is created by n8n at runtime when connecting "${baseNodeInfo.displayName}" to an AI Agent.`,
code: 'INFERRED_TOOL_VARIANT'
});
// Create synthetic nodeInfo for validation continuity
nodeInfo = {
...baseNodeInfo,
nodeType: normalizedType,
displayName: `${baseNodeInfo.displayName} Tool`,
isToolVariant: true,
toolVariantOf: baseNodeType,
isInferred: true
};
}
}
}
if (!nodeInfo) {
@@ -494,6 +526,13 @@ export class WorkflowValidator {
continue;
}
// Skip PARAMETER validation for inferred tool variants (Issue #522)
// They have a different property structure (toolDescription added at runtime)
// that doesn't match the base node's schema. TypeVersion validation above still runs.
if ((nodeInfo as any).isInferred) {
continue;
}
// Validate node configuration
// Add @version to parameters for displayOptions evaluation (supports _cnd operators)
const paramsWithVersion = {