Module: Riffer::Mcp::AuthenticatedTool
- Extended by:
- AuthenticatedTool
- Included in:
- AuthenticatedTool
- Defined in:
- lib/riffer/mcp/authenticated_tool.rb
Overview
Wraps MCP-generated tool classes so tools/call resolves Riffer.config.mcp.credentials per invocation, delegating metadata to the inner class.
Instance 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.
Instance Method Details
#wrap_all(tool_classes, manifest, matched_tags) ⇒ Object
14 15 16 |
# File 'lib/riffer/mcp/authenticated_tool.rb', line 14 def 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.
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 65 66 67 68 |
# File 'lib/riffer/mcp/authenticated_tool.rb', line 22 def wrap_one(inner_class, manifest, ) # steep:ignore MethodBodyTypeMismatch inner = inner_class man = manifest = Class.new(Riffer::Tool) do # steep cannot type the body of a dynamically created anonymous class: # its ivars and `self` inside define_method are unresolvable. # steep:ignore:start @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) } # Creates a fresh client per +tools/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 # steep:ignore:end end end |