Class: OKF::MCP::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/okf/mcp/cli.rb

Overview

The argv-facing shell: parses options, builds the one server definition, and hands it to a transport — stdio by default (each agent host spawns its own process), --http for one warm process serving every agent. Exit codes keep the kernel CLI's contract: 0 ok, 2 usage error. The boot line goes to stderr because stdout is the stdio protocol channel.

Constant Summary collapse

USAGE =
"usage: okf mcp [options] [<bundle-dir>|@slug ...]   " \
"(no args: serve the bundles registered with `okf registry`)"
DEFAULT_BIND =
"127.0.0.1"
DEFAULT_PORT =
9134

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(argv, out: $stderr, stdout: $stdout) ⇒ CLI

Returns a new instance of CLI.



31
32
33
34
35
36
37
38
39
40
# File 'lib/okf/mcp/cli.rb', line 31

def initialize(argv, out: $stderr, stdout: $stdout)
  @argv = argv
  @out = out
  @stdout = stdout
  @http = false
  @bind = DEFAULT_BIND
  @port = DEFAULT_PORT
  @allow_hosts = []
  @done = false
end

Class Method Details

.run(argv, out: $stderr, stdout: $stdout) ⇒ Object

Two streams, because they carry different things. out is the diagnostic channel — the boot line, the notes, the refusals — and defaults to stderr since stdout belongs to the stdio protocol. stdout is the human channel the two informational flags use, and is a parameter rather than a literal $stdout so the okf mcp verb can hand over the streams the kernel injected into it.



27
28
29
# File 'lib/okf/mcp/cli.rb', line 27

def self.run(argv, out: $stderr, stdout: $stdout)
  new(argv, out: out, stdout: stdout).run
end

Instance Method Details

#runObject

Boot, then serve — structurally, because the two phases carry different exit contracts and the rescue below must never see a serving error. SystemCallError belongs in the boot rescue for the same reason the tool wrapper rescues it: an errno is a fact about the operator's machine, not a bug, and it must read as one line rather than a backtrace. The bind is where it actually bites — --http exists so one warm process is shared, which makes "that port is already serving" the likeliest mistake, and it came back as eleven frames and exit 1 while every other boot failure exited 2 with a sentence. The HTTP bind happens here, in boot (see #prepare_http), precisely so that stays true.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/okf/mcp/cli.rb', line 53

def run
  server = nil
  begin
    args = parser.parse(@argv)
    return 0 if @done

    registry = args.empty? ? Registry.from_kernel : Registry.from_argv(args)
    registry.boot_notes.each { |note| say("okf-mcp: #{note}") }
    engine = Backend.detect
    server = Server.build(registry, engine: engine)
    announce(registry, engine)
    prepare_http(server) if @http
  rescue Error, OKF::Error, OptionParser::ParseError, SystemCallError => e
    say("okf-mcp: #{e.message}")
    say(USAGE)
    return 2
  end

  serve(server)
end