Module: Riffer::Mcp::AuthenticatedTool
- Defined in:
- lib/riffer/mcp/authenticated_tool.rb
Overview
Wraps MCP-generated tool classes so tools/call uses Riffer.config.mcp.credentials per invocation while delegating metadata to the inner class.
Class Method Summary collapse
-
.wrap_all(tool_classes, manifest, matched_tags) ⇒ Object
Returns one wrapper class per inner tool, sharing
manifestandmatched_tags. -
.wrap_one(inner_class, manifest, matched_tags) ⇒ Object
– : (singleton(Riffer::Tool), Riffer::Mcp::Manifest, Array) -> singleton(Riffer::Tool) Class.new(Riffer::Tool) is typed as ::Class by steep — it cannot verify the subtype relationship for dynamically created anonymous classes, so the ignore is required.
Class Method Details
.wrap_all(tool_classes, manifest, matched_tags) ⇒ Object
12 13 14 |
# File 'lib/riffer/mcp/authenticated_tool.rb', line 12 def self.wrap_all(tool_classes, manifest, ) tool_classes.map { |tc| wrap_one(tc, manifest, ) } end |
.wrap_one(inner_class, manifest, matched_tags) ⇒ Object
– : (singleton(Riffer::Tool), Riffer::Mcp::Manifest, Array) -> singleton(Riffer::Tool) Class.new(Riffer::Tool) is typed as ::Class by steep — it cannot verify the subtype relationship for dynamically created anonymous classes, so the ignore is required.
20 21 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 57 58 59 60 61 62 63 64 |
# File 'lib/riffer/mcp/authenticated_tool.rb', line 20 def self.wrap_one(inner_class, manifest, ) # steep:ignore MethodBodyTypeMismatch inner = inner_class man = manifest = Class.new(Riffer::Tool) do @identifier = inner.identifier define_singleton_method(:name) { inner.name } define_singleton_method(:mcp_server_tool_name) { inner.mcp_server_tool_name } define_singleton_method(:description) { inner.description } define_singleton_method(:parameters_schema) { |strict: false| inner.parameters_schema(strict: strict) } # Builds a client for a single +tools/call+ invocation. # # Creates a fresh client per call so headers from the credentials proc stay # current. # TODO: A per-headers cache would reduce connection churn under load, and # requires a follow-up investigation to determine how to invalidate failing # clients. define_method(:build_call_client) do |endpoint, headers| Riffer::Mcp::Client.new(endpoint: endpoint, headers: headers) end private :build_call_client define_method(:call) do |context:, **kwargs| cred = Riffer.config.mcp.credentials unless cred # `next` rather than `return`: inside define_method the block IS the method # body, so both exit :call identically at runtime. `next` avoids a false # steep ReturnTypeMismatch that would otherwise need a steep:ignore. next inner.new.call(context: context, **kwargs) end headers = cred.call(manifest: man, matched_tags: , context: context) if headers.nil? raise Riffer::Mcp::CredentialsDeniedError, "MCP credentials returned nil for server '#{man.name}' during tools/call" end client = build_call_client(man.endpoint, headers) text(client.tools_call(inner.mcp_server_tool_name, kwargs)) end end end |