Class: Pikuri::Mcp::Extension
- Inherits:
-
Object
- Object
- Pikuri::Mcp::Extension
- Includes:
- Agent::Extension
- Defined in:
- lib/pikuri/mcp/extension.rb
Overview
An Agent::Extension that wires MCP (Model Context Protocol) support onto an agent: builds an Servers runtime from a Registry, appends the <available_mcps> block to the system prompt, registers an on_close handler that tears down the live MCP clients, and (in bind) installs a per-agent mcp_connect tool so the LLM can pull MCP-exposed tools into its toolset on demand.
Configure / bind split
configure runs once on the parent’s Configurator and creates the shared Servers runtime — that’s the resource extensions own. bind fires per agent (parent + each sub-agent in Step 4’s world) and creates a fresh Connect tool keyed to whichever agent is being bound, registered via Agent#internal_add_tool so it lands only on that agent’s RubyLLM::Chat (not in pikuri’s @tools list — sub-agents therefore don’t inherit the parent’s connect tool through the snapshot, and each agent’s activation Set stays per-agent).
Usage
registry = Pikuri::Mcp::Registry.new(entries: [
Pikuri::Mcp::Registry::StdioEntry.new(id: 'gmail', command: %w[gmail-mcp])
])
Pikuri::Agent.new(transport: ..., system_prompt: ...) do |c|
c.add_extension Pikuri::Mcp::Extension.new(registry: registry)
end
Empty registry
When the registry is Registry#empty?, the extension is a no-op — no Servers, no snippet, no tool, no on_close. Same semantics as the legacy mcp_registry: kwarg on Agent#initialize, which routes through this extension as a transition layer.
Instance Attribute Summary collapse
-
#servers ⇒ Mcp::Servers?
readonly
The runtime built in
configure, ornilwhen the registry was empty (extension is a no-op).
Instance Method Summary collapse
-
#bind(agent) ⇒ void
Register a per-agent
mcp_connecttool on the agent’s chat. -
#configure(c) ⇒ void
Build the shared Servers runtime, append the <available_mcps> block to the system prompt, and register the close handler.
-
#initialize(registry: Registry::EMPTY, synthesize_descriptions: true, verify_mcp_servers: true) ⇒ Extension
constructor
A new instance of Extension.
Constructor Details
#initialize(registry: Registry::EMPTY, synthesize_descriptions: true, verify_mcp_servers: true) ⇒ Extension
Returns a new instance of Extension.
57 58 59 60 61 62 63 64 |
# File 'lib/pikuri/mcp/extension.rb', line 57 def initialize(registry: Registry::EMPTY, synthesize_descriptions: true, verify_mcp_servers: true) @registry = registry @synthesize_descriptions = synthesize_descriptions @verify_mcp_servers = verify_mcp_servers @servers = nil end |
Instance Attribute Details
#servers ⇒ Mcp::Servers? (readonly)
Returns the runtime built in configure, or nil when the registry was empty (extension is a no-op).
69 70 71 |
# File 'lib/pikuri/mcp/extension.rb', line 69 def servers @servers end |
Instance Method Details
#bind(agent) ⇒ void
This method returns an undefined value.
Register a per-agent mcp_connect tool on the agent’s chat. The tool’s execute closure captures the agent reference so activations register their tools on the correct chat — see IDEAS.md §“Two invariants worth recording” for the static-vs-dynamic tool boundary.
103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/pikuri/mcp/extension.rb', line 103 def bind(agent) return if @servers.nil? || @servers.empty? if agent.tools.any?(Mcp::Servers::Connect) raise 'Mcp::Servers::Connect cannot be passed in tools: when an MCP runtime is wired; ' \ 'Agent auto-registers it.' end connect = @servers.build_mcp_connect_tool(agent) agent.internal_add_tool(connect.to_ruby_llm_tool) nil end |
#configure(c) ⇒ void
This method returns an undefined value.
Build the shared Servers runtime, append the <available_mcps> block to the system prompt, and register the close handler. The Synthesizer / Verifier thinker closure captures the Configurator’s transport + cancellable so the LLM-driven boot passes run against the same model the agent itself uses and honor the same cancel flag.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/pikuri/mcp/extension.rb', line 80 def configure(c) return if @registry.empty? if @synthesize_descriptions || @verify_mcp_servers thinker = build_thinker(c.transport, c.cancellable) end synthesizer = build_synthesizer(thinker, c.transport.model) if @synthesize_descriptions verifier = build_verifier(thinker, c.transport.model) if @verify_mcp_servers @servers = Mcp::Servers.new(@registry, synthesizer: synthesizer, verifier: verifier) c.append_system_prompt(@servers.system_prompt_snippet.lstrip) unless @servers.empty? c.on_close { @servers.close } nil end |