Class: LocalVault::MCP::Server
- Inherits:
-
Object
- Object
- LocalVault::MCP::Server
- Defined in:
- lib/localvault/mcp/server.rb
Instance Method Summary collapse
-
#handle_message(json_string) ⇒ Hash?
Parse and dispatch a single JSON-RPC message.
-
#initialize(input: $stdin, output: $stdout) ⇒ Server
constructor
Create an MCP server reading JSON-RPC from input, writing responses to output.
-
#start ⇒ void
Start the MCP server loop, reading JSON-RPC messages line-by-line.
Constructor Details
#initialize(input: $stdin, output: $stdout) ⇒ Server
Create an MCP server reading JSON-RPC from input, writing responses to output.
19 20 21 22 |
# File 'lib/localvault/mcp/server.rb', line 19 def initialize(input: $stdin, output: $stdout) @input = input @output = output end |
Instance Method Details
#handle_message(json_string) ⇒ Hash?
Parse and dispatch a single JSON-RPC message.
Handles initialize, tools/list, and tools/call methods.
Notifications (no "id" field) return nil.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/localvault/mcp/server.rb', line 60 def (json_string) = JSON.parse(json_string) # Notifications have no id — no response return nil unless .key?("id") id = ["id"] method = ["method"] params = ["params"] || {} case method when "initialize" success_response(id, { "protocolVersion" => "2025-11-25", "capabilities" => { "tools" => {} }, "serverInfo" => { "name" => "localvault", "version" => LocalVault::VERSION }, "instructions" => "Do not retrieve plaintext secrets for commands, API calls, evaluation, or configuration. " \ "Call list_secrets to discover names, then localvault_build_exec to construct process-scoped injection. " \ "Use get_secret with allow_plaintext: true only when the user task explicitly requires the value itself." }) when "tools/list" success_response(id, { "tools" => Tools::DEFINITIONS }) when "tools/call" tool_name = params["name"] arguments = params["arguments"] || {} unless Tools::DEFINITIONS.any? { |t| t["name"] == tool_name } return error_response(id, -32602, "Unknown tool: #{tool_name}") end result = Tools.call(tool_name, arguments, method(:vault_for), method(:vault_status)) success_response(id, result) else error_response(id, -32601, "Method not found: #{method}") end rescue JSON::ParserError error_response(nil, -32700, "Parse error") end |
#start ⇒ void
This method returns an undefined value.
Start the MCP server loop, reading JSON-RPC messages line-by-line.
Logs available unlocked vaults to stderr on startup. Blocks until input is exhausted or interrupted (Ctrl-C).
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/localvault/mcp/server.rb', line 30 def start unlocked = VaultResolver.unlocked_vault_names label = unlocked.empty? ? "no unlocked vaults (run: localvault show)" : "vaults=#{unlocked.join(', ')}" $stderr.puts "[localvault-mcp] started v#{LocalVault::VERSION} #{label}" $stderr.flush @input.each_line do |line| line = line.strip next if line.empty? response = (line) if response @output.puts(JSON.generate(response)) @output.flush end end rescue Interrupt # Clean shutdown on Ctrl-C ensure $stderr.puts "[localvault-mcp] stopped" $stderr.flush end |