Module: FastMcp::NestedRuleHandler

Included in:
SchemaCompiler
Defined in:
lib/mcp/tool.rb

Overview

Module for handling nested rules

Instance Method Summary collapse

Instance Method Details

#add_to_nested_rules(nested_key, nested_key_op, nested_rules, is_optional) ⇒ Object

Add to nested rules



654
655
656
657
658
659
660
661
# File 'lib/mcp/tool.rb', line 654

def add_to_nested_rules(nested_key, nested_key_op, nested_rules, is_optional)
  nested_rules[nested_key] = if is_optional
                               # For optional fields, create an Implication wrapper
                               create_implication(nested_key_op.rule)
                             else
                               nested_key_op.rule
                             end
end

#create_implication(rule) ⇒ Object

Create implication



664
665
666
667
668
669
670
# File 'lib/mcp/tool.rb', line 664

def create_implication(rule)
  # We don't need to create a new Key operation, just use the existing rule
  Dry::Logic::Operations::Implication.new(
    Dry::Logic::Rule.new(proc { true }), # Always true condition
    rule
  )
end

#extract_from_implication_and(and_rule, nested_rules) ⇒ Object

Extract from implication and



596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'lib/mcp/tool.rb', line 596

def extract_from_implication_and(and_rule, nested_rules)
  # Look for Set operations directly in the right side
  set_op = and_rule.rules.find { |r| r.is_a?(Dry::Logic::Operations::Set) }

  if set_op
    process_set_operation(set_op, nested_rules)
    return
  end

  # If no direct Set operation, look for Key operations in the rule structure
  key_op = and_rule.rules.find { |r| r.is_a?(Dry::Logic::Operations::Key) }
  return unless key_op && key_op.rule.is_a?(Dry::Logic::Operations::And)

  # Look for Set operations in the Key operation's rule
  set_op = key_op.rule.rules.find { |r| r.is_a?(Dry::Logic::Operations::Set) }
  process_set_operation(set_op, nested_rules) if set_op
end

#extract_from_implication_key(key_rule, nested_rules) ⇒ Object

Extract from implication key



587
588
589
590
591
592
593
# File 'lib/mcp/tool.rb', line 587

def extract_from_implication_key(key_rule, nested_rules)
  return unless key_rule.rule.is_a?(Dry::Logic::Operations::And)

  # Look for Set operations directly in the rule
  set_op = key_rule.rule.rules.find { |r| r.is_a?(Dry::Logic::Operations::Set) }
  process_set_operation(set_op, nested_rules) if set_op
end

#extract_nested_rules(rule) ⇒ Object

Extract nested rules from a rule



531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/mcp/tool.rb', line 531

def extract_nested_rules(rule)
  nested_rules = {}

  case rule
  when Dry::Logic::Operations::And
    extract_nested_rules_from_and(rule, nested_rules)
  when Dry::Logic::Operations::Implication
    extract_nested_rules_from_implication(rule, nested_rules)
  when Dry::Logic::Operations::Key
    extract_nested_rules_from_and(rule.rule, nested_rules) if rule.rule.is_a?(Dry::Logic::Operations::And)
  end

  nested_rules
end

#extract_nested_rules_from_and(rule, nested_rules) ⇒ Object

Extract nested rules from an And operation



547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/mcp/tool.rb', line 547

def extract_nested_rules_from_and(rule, nested_rules)
  # Look for Set operations directly in the rule
  set_op = rule.rules.find { |r| r.is_a?(Dry::Logic::Operations::Set) }

  if set_op
    process_set_operation(set_op, nested_rules)
    return
  end

  # If no direct Set operation, look for Key operations in the rule structure
  key_ops = rule.rules.select { |r| r.is_a?(Dry::Logic::Operations::Key) }

  key_ops.each do |key_op|
    next unless key_op.rule.is_a?(Dry::Logic::Operations::And)

    # Look for Set operations in the Key operation's rule
    set_op = key_op.rule.rules.find { |r| r.is_a?(Dry::Logic::Operations::Set) }
    process_set_operation(set_op, nested_rules) if set_op

    # Also look for direct predicates
    key_op.rule.rules.each do |r|
      if r.respond_to?(:name) && r.name != :hash?
        nested_key = key_op.path
        nested_rules[nested_key] = key_op.rule
      end
    end
  end
end

#extract_nested_rules_from_implication(rule, nested_rules) ⇒ Object

Extract nested rules from an Implication operation



577
578
579
580
581
582
583
584
# File 'lib/mcp/tool.rb', line 577

def extract_nested_rules_from_implication(rule, nested_rules)
  # For optional fields (Implication), we need to check the right side
  if rule.right.is_a?(Dry::Logic::Operations::Key)
    extract_from_implication_key(rule.right, nested_rules)
  elsif rule.right.is_a?(Dry::Logic::Operations::And)
    extract_from_implication_and(rule.right, nested_rules)
  end
end

#find_nested_key_op(rule) ⇒ Object

Find nested key operation



645
646
647
648
649
650
651
# File 'lib/mcp/tool.rb', line 645

def find_nested_key_op(rule)
  if rule.is_a?(Dry::Logic::Operations::And)
    rule.rules.find { |r| r.is_a?(Dry::Logic::Operations::Key) }
  elsif rule.is_a?(Dry::Logic::Operations::Key)
    rule
  end
end

#process_nested_rule(rule, nested_rules, is_optional) ⇒ Object

Process a nested rule



631
632
633
634
635
636
637
638
639
640
641
642
# File 'lib/mcp/tool.rb', line 631

def process_nested_rule(rule, nested_rules, is_optional)
  # Find the key operation which contains the nested key name
  nested_key_op = find_nested_key_op(rule)
  return unless nested_key_op

  # Get the nested key name
  nested_key = nested_key_op.respond_to?(:path) ? nested_key_op.path : nil
  return unless nested_key

  # Add to nested rules
  add_to_nested_rules(nested_key, nested_key_op, nested_rules, is_optional)
end

#process_set_operation(set_op, nested_rules) ⇒ Object

Process a set operation



615
616
617
618
619
620
621
622
623
624
625
626
627
628
# File 'lib/mcp/tool.rb', line 615

def process_set_operation(set_op, nested_rules)
  # Process each rule in the Set operation
  set_op.rules.each do |set_rule|
    next unless set_rule.is_a?(Dry::Logic::Operations::And) ||
                set_rule.is_a?(Dry::Logic::Operations::Implication)

    # For Implication (optional fields), we need to check the right side
    if set_rule.is_a?(Dry::Logic::Operations::Implication)
      process_nested_rule(set_rule.right, nested_rules, true)
    else
      process_nested_rule(set_rule, nested_rules, false)
    end
  end
end