Module: Nexo::MCP
- Defined in:
- lib/nexo/mcp.rb,
lib/nexo/mcp/gated_tool.rb
Overview
Composes the ruby_llm-mcp gem so an agent can attach one or more MCP servers
with a single mcp class macro — no MCP client wiring by hand. A loaded server
contributes tools that are gated by Nexo::Permissions#authorize_mcp! before
the model can invoke them (see MCP::GatedTool).
ruby_llm-mcp is a SOFT (optional) dependency: it is required lazily by load!
the first time an MCP server is built. With the gem absent, require "nexo"
still loads cleanly; only building a client raises MissingDependencyError
with install guidance.
Provider-neutral by construction: MCP servers are reached through a server (not
a vendor SDK), so behavior is identical on Anthropic, a local model, or anything
else ruby_llm supports.
Defined Under Namespace
Classes: GatedTool
Class Method Summary collapse
-
.build(name:, transport:, token: nil, **config) ⇒ Object
Builds a
ruby_llm-mcpclient from Nexo's friendly, transport-shaped macro opts. -
.client_factory ⇒ Object
The object whose
#clientbuilds the MCP client —::RubyLLM::MCPin production. -
.load! ⇒ Object
Lazily loads the
ruby_llm-mcpgem. -
.stub_client_factory(factory) ⇒ Object
Swaps #client_factory to
factoryfor the duration of the block, then restores it.
Class Method Details
.build(name:, transport:, token: nil, **config) ⇒ Object
Builds a ruby_llm-mcp client from Nexo's friendly, transport-shaped macro
opts. name and transport map onto the client's +name:+/+transport_type:+;
every other kwarg is collected into the client's config: hash verbatim
(no key renaming) — e.g. +command:+/+args:+ for :stdio, url: for :sse.
token: (Spec 18) is Nexo's own option — a static bearer String or a
callable (+#call+) that yields one — for an OAuth-authenticated HTTP-family
server (+:http+/+:sse+/+:streamable+). When present, Nexo resolves it and
injects an Authorization: Bearer header (see #inject_auth). token: is a
dedicated keyword, so it never enters config: (nothing to strip — the
handoff config carries no token key by construction). When absent,
config: passes through byte-for-byte (no headers key added) — :stdio is
untouched. Nexo runs no OAuth flow, refresh, or token store: it injects a
header, the host owns the credential (see docs/mcp.md).
VERIFY (Group 0, ruby_llm-mcp 1.0.0): the constructor is
RubyLLM::MCP.client(name:, transport_type:, config: {}) and the client
connects on construction (+start: true+ default), so it is reusable across
prompts until torn down with #stop. The HTTP-family transports read auth
headers from config[:headers] (a Hash) and SNAPSHOT them at construction
(+@headers = headers || {}+, dup'd per request) — there is no per-request
header callback for a plain headers Hash. So a callable token: is
resolved ONCE per client build; a rotated token needs the documented
Agent#close + fresh-prompt reconnect (Spec 6 memoizes the client).
54 55 56 57 58 |
# File 'lib/nexo/mcp.rb', line 54 def build(name:, transport:, token: nil, **config) load! config = inject_auth(config, token) unless token.nil? client_factory.client(name: name.to_s, transport_type: transport, config: config) end |
.client_factory ⇒ Object
The object whose #client builds the MCP client — ::RubyLLM::MCP in
production. Overridable via #stub_client_factory so an offline test can
inspect the exact config: Nexo hands off without a live server or stubbing
a constant.
80 81 82 |
# File 'lib/nexo/mcp.rb', line 80 def client_factory @client_factory || ::RubyLLM::MCP end |
.load! ⇒ Object
Lazily loads the ruby_llm-mcp gem. Idempotent (a second call is a cheap
no-op once the gem is loaded). Raises MissingDependencyError — naming the
gem and the exact remedy — when the gem is not installed. Mirrors
Nexo::Skills.load!.
23 24 25 26 27 28 |
# File 'lib/nexo/mcp.rb', line 23 def load! require "ruby_llm/mcp" rescue LoadError raise MissingDependencyError, 'MCP requires the `ruby_llm-mcp` gem. Add `gem "ruby_llm-mcp"` to your Gemfile.' end |
.stub_client_factory(factory) ⇒ Object
Swaps #client_factory to factory for the duration of the block, then
restores it. Test-only seam (Spec 18) — keeps the token: transform
unit-testable at the ruby_llm-mcp boundary.
87 88 89 90 91 92 93 |
# File 'lib/nexo/mcp.rb', line 87 def stub_client_factory(factory) previous = @client_factory @client_factory = factory yield ensure @client_factory = previous end |