Class: McpAuthorization::FacadeBuilder
- Inherits:
-
Object
- Object
- McpAuthorization::FacadeBuilder
- Defined in:
- lib/mcp_authorization/facade_builder.rb
Overview
Builds grouped facade tools for a faceted domain.
A facade is a per-request synthetic MCP::Tool subclass — one per
non-empty category — whose description carries routing signal only
(group summary + RBAC-filtered tool one-liners) and whose inputSchema
defers per-tool argument schemas out of the selection prompt. A
tools/call on a facade names the inner tool (+tool_name+) and its
arguments; dispatch resolves the real tool through
ToolRegistry.tool_class_for — re-running permitted? — and delegates
to its materialized call, so authorization, gating, and input/output
filtering apply exactly as if the tool had been called directly.
Facades are never registered in ToolRegistry; they are produced on
demand by facades_for. See docs/designs/tool-grouping-facades.md.
Defined Under Namespace
Classes: FacadeNameCollisionError, UncategorizedToolError
Constant Summary collapse
- FALLBACK_CATEGORY =
Fallback group for uncategorized tools (default mode).
:uncategorized
Class Method Summary collapse
-
.facade_for(domain:, name:, server_context:) ⇒ Object
A single facade by its tool name (e.g. "orders_tools"), or nil when the name matches no non-empty group for this caller.
-
.facade_name(category, config) ⇒ Object
The MCP tool name a category's facade is exposed under.
-
.facades_for(domain:, server_context:) ⇒ Object
All facades for a domain, one per non-empty group the caller has at least one permitted tool in.
Class Method Details
.facade_for(domain:, name:, server_context:) ⇒ Object
A single facade by its tool name (e.g. "orders_tools"), or nil when
the name matches no non-empty group for this caller. Used by
tools/call routing so one call doesn't build every facade.
Built with for_dispatch: true — a tools/call only needs the
advertised name set to route through, never the _meta per-tool
schemas (those are a tools/list concern). Skipping them avoids
recompiling every tool's input schema in the group on each call,
preserving the "compile only the invoked tool" cost that facades'
large target domains most depend on.
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/mcp_authorization/facade_builder.rb', line 56 def facade_for(domain:, name:, server_context:) config = McpAuthorization.config.facet_config(domain) return nil unless config group_tools(domain, server_context, config).each do |category, tools| if facade_name(category, config) == name return build_facade(domain, category, tools, server_context, config, for_dispatch: true) end end nil end |
.facade_name(category, config) ⇒ Object
The MCP tool name a category's facade is exposed under. The suffix is
per-domain (facet config facade_suffix, default "tools"): category
:orders → orders_tools (or orders_hire, etc.).
72 73 74 75 |
# File 'lib/mcp_authorization/facade_builder.rb', line 72 def facade_name(category, config) suffix = config[:facade_suffix] || Configuration::DEFAULT_FACADE_SUFFIX "#{category}_#{suffix}" end |
.facades_for(domain:, server_context:) ⇒ Object
All facades for a domain, one per non-empty group the caller has at
least one permitted tool in. Empty groups produce no facade — which
also guarantees no facade ever advertises an empty enum.
36 37 38 39 40 41 42 43 |
# File 'lib/mcp_authorization/facade_builder.rb', line 36 def facades_for(domain:, server_context:) config = McpAuthorization.config.facet_config(domain) return [] unless config group_tools(domain, server_context, config).map do |category, tools| build_facade(domain, category, tools, server_context, config) end end |