Class: Woods::Console::DispatchPipeline

Inherits:
Object
  • Object
show all
Defined in:
lib/woods/console/dispatch_pipeline.rb

Overview

Per-tool dispatch flow for the Console MCP server.

Encapsulates everything that happens between MCP’s ‘define_tool` block firing and the `MCP::Tool::Response` returning to the client:

1. Coerce integer-typed args from strings. MCP clients sometimes send
   `"10"` for an `integer` property — we normalize before dispatch.
2. Run the Layer 1 table gate ({ResponseContext#enforce!}).
3. Translate the tool args into a bridge/executor request via the
   handler proc supplied in the {ToolSpec}.
4. Send the request through the connection manager / embedded executor.
5. Apply Layer 3 column + EAV redaction to the result
   ({ResponseContext#redact}).
6. Apply Layer 2 credential scanning to the result and/or error text
   ({ResponseContext#scan}), logging hit counts when any fire.
7. Render the result via the optional renderer or JSON, wrap in a
   response object.

Previously this flow lived inline in ‘Server.register` with four `method(:…)` captures closing over module-level methods. Pulling it into a first-class object collapses that wiring, removes the need for the captures, and makes the pipeline directly testable without going through the full server build path.

Instance Method Summary collapse

Constructor Details

#initialize(tool_name:, handler:, integer_keys:, conn_mgr:, ctx: NullResponseContext.instance, renderer: nil, logger: nil) ⇒ DispatchPipeline

Returns a new instance of DispatchPipeline.

Parameters:

  • tool_name (String)
  • handler (#call)

    Maps a Hash of tool args to a bridge request Hash.

  • integer_keys (Array<Symbol>)

    Property keys declared as ‘integer` in the tool schema.

  • conn_mgr (#send_request)

    ConnectionManager or EmbeddedExecutor.

  • ctx (ResponseContext) (defaults to: NullResponseContext.instance)

    Bundles the three response-safety layers. Defaults to the Null context so tests can stub minimally.

  • renderer (#render_default, nil) (defaults to: nil)

    Optional response renderer.

  • logger (#warn, nil) (defaults to: nil)

    Structured logger used for credential-scan and table-gate telemetry. Failures are swallowed so observability issues never break a tool response.



43
44
45
46
47
48
49
50
51
52
# File 'lib/woods/console/dispatch_pipeline.rb', line 43

def initialize(tool_name:, handler:, integer_keys:, conn_mgr:, # rubocop:disable Metrics/ParameterLists
               ctx: NullResponseContext.instance, renderer: nil, logger: nil)
  @tool_name = tool_name
  @handler = handler
  @integer_keys = integer_keys
  @conn_mgr = conn_mgr
  @ctx = ctx
  @renderer = renderer
  @logger = logger
end

Instance Method Details

#call(args) ⇒ MCP::Tool::Response

Run the full dispatch pipeline for a single tool call.

Parameters:

  • args (Hash)

    Tool arguments (symbol keys from MCP).

Returns:

  • (MCP::Tool::Response)


58
59
60
61
62
63
64
65
66
67
68
# File 'lib/woods/console/dispatch_pipeline.rb', line 58

def call(args)
  coerce_integer_args!(args)
  @ctx.enforce!(args)
  request = @handler.call(args).transform_keys(&:to_s)
  send_to_bridge(request)
rescue TableGateError => e
  log_table_gate_rejection(args, e)
  error_response(e.message)
rescue SqlValidationError, ForbiddenExpressionError => e
  error_response(e.message)
end