Module: Kernai::MCP
- Defined in:
- lib/kernai/mcp.rb
Overview
Optional MCP (Model Context Protocol) adapter.
Kernai’s core stays fully protocol-agnostic. This file is loaded only when the user explicitly ‘require ’kernai/mcp’‘ — at which point we pull in the `mcp_client` gem (soft dependency) and register a handler for the `:mcp` block type via Kernai::Protocol.
The agent then speaks MCP’s native verbs directly:
<block type="mcp">{"method":"tools/list","params":{"server":"fs"}}</block>
There is no Kernai-invented wrapping around MCP primitives: method names come from the MCP spec, plus one Kernai extension (‘servers/list`) that exposes the multiplexing layer the adapter adds on top.
Defined Under Namespace
Classes: Adapter, ConfigError, DependencyMissingError
Constant Summary collapse
- MCP_DOCUMENTATION =
<<~DOC MCP (Model Context Protocol) external access. ## Two vocabularies — do not confuse them There are exactly TWO kinds of names you will encounter, and they live in different layers: 1. PROTOCOL METHODS — a fixed, closed set defined by the MCP spec (plus one Kernai extension). These go in the "method" field of your request. You cannot invent new ones. 2. TOOL NAMES — dynamic, server-specific identifiers like "read_file", "list_directory", "search_files", etc. Each MCP server publishes its own catalog. Tool names are NOT protocol methods. You CANNOT use a tool name in the "method" field. To invoke a tool you always go through the protocol method `tools/call`, passing the tool name in `params.name`. There is no other entry point for invoking tools. Ever. ## Request shape Emit a <block type="mcp"> whose content is a JSON request: {"method":"<PROTOCOL_METHOD>","params":{...}} ## The complete, closed set of PROTOCOL METHODS servers/list [Kernai ext] list configured MCP servers tools/list {"server":"..."} list tools on a server (omit server to list all) tools/describe {"server":"...","name":"..."} full JSON schema for one tool tools/call {"server":"...","name":"...","arguments":{...}} resources/list {"server":"..."} list resources (server optional) resources/read {"server":"...","uri":"..."} read a resource by URI prompts/list {"server":"..."} list prompts (server optional) prompts/get {"server":"...","name":"...","arguments":{...}} If a "method" value is not in this list, the request is rejected — including every tool name you see in tools/list output. ## Worked example: calling a tool named "list_directory" WRONG (tool name treated as a protocol method — rejected): <block type="mcp">{"method":"list_directory","params":{"path":"/tmp"}}</block> RIGHT (protocol method tools/call, tool name in params.name): <block type="mcp">{"method":"tools/call","params":{"server":"filesystem","name":"list_directory","arguments":{"path":"/tmp"}}}</block> The same pattern applies to every tool you discover, regardless of the server. ## Responses and errors Responses come back in <block type="result" name="mcp">. Errors come back in <block type="error" name="mcp">. ## Typical exploratory flow 1. servers/list — discover what's available 2. tools/list — see the tools on a specific server 3. tools/describe — inspect a tool's input schema before calling it 4. tools/call — execute the tool (this is the ONLY way to invoke it) ## Protocols vs skills Protocols are distinct from skills: skills are local Ruby callables invoked via <block type="command" name="...">. Protocols are external systems invoked via their own block type (here: <block type="mcp">). DOC
Class Attribute Summary collapse
-
.adapter ⇒ Object
readonly
Returns the value of attribute adapter.
Class Method Summary collapse
-
.load(config_path) ⇒ Object
Load servers from a YAML config file.
-
.setup(config, client: nil) ⇒ Object
Register the MCP protocol handler from an already-parsed config hash.
- .shutdown ⇒ Object
Class Attribute Details
.adapter ⇒ Object (readonly)
Returns the value of attribute adapter.
509 510 511 |
# File 'lib/kernai/mcp.rb', line 509 def adapter @adapter end |
Class Method Details
.load(config_path) ⇒ Object
Load servers from a YAML config file. The file must contain a top-level ‘servers:` key; each child is a server definition.
479 480 481 482 483 484 485 486 487 488 |
# File 'lib/kernai/mcp.rb', line 479 def load(config_path) raw = File.read(config_path) = (raw) config = YAML.safe_load(, aliases: true, permitted_classes: []) unless config.is_a?(Hash) && config['servers'].is_a?(Hash) && config['servers'].any? raise ConfigError, "MCP config must declare at least one server under 'servers:'" end setup(config) end |
.setup(config, client: nil) ⇒ Object
Register the MCP protocol handler from an already-parsed config hash. Useful from tests or programmatic setups. Pass ‘client:` to inject a pre-built client instance (skips the upstream gem wiring).
493 494 495 496 497 498 499 500 501 |
# File 'lib/kernai/mcp.rb', line 493 def setup(config, client: nil) shutdown if @adapter @adapter = Adapter.new(config, client: client) Kernai::Protocol.register(:mcp, documentation: MCP_DOCUMENTATION) do |block, ctx| @adapter.handle(block, ctx) end register_shutdown @adapter end |
.shutdown ⇒ Object
503 504 505 506 507 |
# File 'lib/kernai/mcp.rb', line 503 def shutdown @adapter&.shutdown Kernai::Protocol.unregister(:mcp) @adapter = nil end |