Module: McpAuthorization::Diagnostics
- Defined in:
- lib/mcp_authorization/diagnostics.rb
Overview
Shared diagnostic helpers used by both the field-level
RbsSchemaCompiler#predicate_excluded? and the tool-level
Tool#gates_pass? paths.
Lives in its own module so the two predicate sites — field-level (compile time) and tool-level (request time) — share a single "Did you mean?" warning implementation. Avoids two copies of Levenshtein drifting.
Class Method Summary collapse
-
.levenshtein(a, b) ⇒ Integer
Minimal Levenshtein distance for typo suggestions.
-
.warn_unknown_predicate(name, server_context, site:) ⇒ void
Emit a development-mode warning when a predicate method is not found on the server context.
Class Method Details
.levenshtein(a, b) ⇒ Integer
Minimal Levenshtein distance for typo suggestions.
Iterative two-row implementation — O(m*n) time, O(m) extra space. Used only in development for "Did you mean?" suggestions, so the naive version is fine.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/mcp_authorization/diagnostics.rb', line 68 def levenshtein(a, b) m, n = a.length, b.length d = Array.new(m + 1) { |i| i } (1..n).each do |j| prev = d[0] d[0] = j (1..m).each do |i| cost = a[i - 1] == b[j - 1] ? 0 : 1 temp = d[i] d[i] = [d[i] + 1, d[i - 1] + 1, prev + cost].min prev = temp end end d[m] end |
.warn_unknown_predicate(name, server_context, site:) ⇒ void
This method returns an undefined value.
Emit a development-mode warning when a predicate method is not
found on the server context. Helps catch typos like
@feture(:x) or gate :feture, :sms.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/mcp_authorization/diagnostics.rb', line 22 def warn_unknown_predicate(name, server_context, site:) return unless defined?(Rails) && Rails.respond_to?(:env) && Rails.env.local? str = name.to_s available = server_context.class.public_instance_methods(true) .select { |m| m.to_s.end_with?("?") } .map { |m| m.to_s.chomp("?") } best = available.min_by { |a| levenshtein(a, str) } suggestion = best && levenshtein(best, str) <= 3 ? best : nil hint = case site when :tool then suggestion ? " Did you mean gate :#{suggestion}?" : "" when :field then suggestion ? " Did you mean @#{suggestion}?" : "" else suggestion ? " Did you mean #{suggestion}?" : "" end surface = case site when :tool then "Gate predicate" when :field then "Predicate" else "Predicate" end fallthrough = case site when :tool then "Tool will be shown to all users." when :field then "Field will be shown to all users." else "" end Rails.logger&.warn( "[McpAuthorization] #{surface} '#{str}?' not found on #{server_context.class}.#{hint} #{fallthrough}".strip ) end |