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 the extension owns. bind fires once on the parent agent and installs a Connect tool keyed to it, registered via Agent#internal_add_tool so the tool’s execute closure can call back into the right chat when the LLM activates a server. Sub-agents do not inherit extensions, so they receive neither the Connect tool nor any MCP-backed tools — personas own their toolset by construction.
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 |