Class: RosettAi::Thor::Tasks::Mcp

Inherits:
Thor
  • Object
show all
Defined in:
lib/rosett_ai/thor/tasks/mcp.rb

Overview

Thor CLI for MCP server and administration.

Provides serve, list, validate, status, audit, key management, and trust source subcommands for MCP server management.

Author:

  • hugo

  • claude

Instance Method Summary collapse

Instance Method Details

#add(name, uri) ⇒ void

This method returns an undefined value.

Add a new entry.

Parameters:

  • name (Object)

    the name

  • uri (Object)

    the uri



211
212
213
214
215
216
217
218
219
220
221
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 211

def add(name, uri)
  installer = build_installer
  result = installer.install(name: name, uri: uri, transport: options[:transport])

  if result[:success]
    say(Rainbow(result[:message]).green)
  else
    warn(Rainbow(result[:message]).red)
    exit(result[:trusted] == false ? 1 : 2)
  end
end

#auditvoid

This method returns an undefined value.

Run a security audit on MCP configuration.



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 180

def audit
  registry = build_registry
  checker = RosettAi::Mcp::Admin::HealthChecker.new
  validator = RosettAi::Mcp::Admin::SchemaValidator.new
  auditor = RosettAi::Mcp::Admin::Auditor.new(
    registry: registry, health_checker: checker, schema_validator: validator
  )

  report = auditor.audit
  render_audit_report(report)
end

#generate_keyvoid

This method returns an undefined value.

Generate a new MCP API key.



371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 371

def generate_key
  path = RosettAi::Mcp::Keyfile.create_if_missing(options[:keyfile])
  plaintext = RosettAi::Mcp::KeyHasher.generate_key
  key_hash = RosettAi::Mcp::KeyHasher.hash_key(plaintext)

  keyfile = RosettAi::Mcp::Keyfile.new(path)
  keyfile.load!
  keyfile.add_key(name: options[:name], key_hash: key_hash)

  say(Rainbow("API key generated for '#{options[:name]}':").green)
  say(plaintext)
  say(Rainbow('Store this key securely — it cannot be recovered.').yellow)
end

#listvoid

This method returns an undefined value.

List available items.



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 93

def list
  registry = build_registry
  servers = registry.list

  if servers.empty?
    say(t('no_servers'))
    return
  end

  render_server_list(servers)
end

#list_keysvoid

This method returns an undefined value.

List all MCP API keys.



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 401

def list_keys
  path = File.expand_path(options[:keyfile])
  unless File.exist?(path)
    say(Rainbow('No keyfile found.').yellow)
    return
  end

  keyfile = RosettAi::Mcp::Keyfile.new(path)
  keyfile.load!
  keys = keyfile.enabled_keys

  if keys.empty?
    say('No keys configured.')
    return
  end

  table = ::Terminal::Table.new(
    headings: ['Name', 'Created', 'Enabled', 'Expires'],
    rows: keys.map { |k| [k[:name], k[:created_at], k[:enabled], k[:expires_at] || 'never'] }
  )
  say(table)
end

#remove(name) ⇒ void

This method returns an undefined value.

Remove the component.

Parameters:

  • name (Object)

    the name



242
243
244
245
246
247
248
249
250
251
252
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 242

def remove(name)
  installer = build_installer
  result = installer.remove(name: name, force: options[:force])

  if result[:success]
    say(Rainbow(result[:message]).green)
  else
    warn(Rainbow(result[:message]).red)
    exit(1)
  end
end

#revoke_keyvoid

This method returns an undefined value.

Revoke an MCP API key.



440
441
442
443
444
445
446
447
448
449
450
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 440

def revoke_key
  keyfile = RosettAi::Mcp::Keyfile.new(File.expand_path(options[:keyfile]))
  keyfile.load!

  if keyfile.revoke_key(options[:name])
    say(Rainbow("Key '#{options[:name]}' revoked.").green)
  else
    warn(Rainbow("Key '#{options[:name]}' not found.").red)
    exit(1)
  end
end

#rotate_keyvoid

This method returns an undefined value.

Rotate an MCP API key.



468
469
470
471
472
473
474
475
476
477
478
479
480
481
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 468

def rotate_key
  keyfile = RosettAi::Mcp::Keyfile.new(File.expand_path(options[:keyfile]))
  keyfile.load!

  result = keyfile.rotate_key(options[:name])
  if result
    say(Rainbow("Key '#{options[:name]}' rotated. New key:").green)
    say(result[:plaintext])
    say(Rainbow('Store this key securely — it cannot be recovered.').yellow)
  else
    warn(Rainbow("Key '#{options[:name]}' not found.").red)
    exit(1)
  end
end

#servevoid

This method returns an undefined value.

Start the MCP JSON-RPC server.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 50

def serve
  server = RosettAi::Mcp::Server.new(
    config_path: options[:config],
    plugins: options[:plugin]
  )

  if options[:http]
    unless ENV.fetch('RAI_MCP_API_KEY', nil)
      warn Rainbow('[rosett-ai-mcp] WARNING: Starting HTTP server with no API key configured. ' \
                   'Set RAI_MCP_API_KEY or use a keyfile for authentication.').yellow
    end
    server.serve_http(
      port: options[:port],
      host: options[:host],
      tls_cert: options[:tls_cert],
      tls_key: options[:tls_key],
      allow_remote: options[:allow_remote]
    )
  else
    server.serve
  end
rescue RosettAi::McpError => e
  warn Rainbow(e.message).red
  exit(5)
end

#statusString

Return the current status.

Returns:

  • (String)


149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 149

def status
  registry = build_registry
  checker = RosettAi::Mcp::Admin::HealthChecker.new
  results = checker.check_all(registry)

  if results.empty?
    say(t('no_servers'))
    return
  end

  render_health_results(results)
end

#toolsvoid

This method returns an undefined value.

List registered MCP tools.



343
344
345
346
347
348
349
350
351
352
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 343

def tools
  server = RosettAi::Mcp::Server.new
  tool_list = server.tools

  table = ::Terminal::Table.new(
    headings: ['Tool', 'Description', 'Read-Only'],
    rows: tool_list.map { |t| [t[:name], t[:description], t[:annotations]['readOnlyHint']] }
  )
  say(table)
end

#trust_add(domain) ⇒ void

This method returns an undefined value.

Add a trusted MCP source.

Parameters:

  • domain (Object)

    the domain



296
297
298
299
300
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 296

def trust_add(domain)
  manager = build_trust_manager
  manager.add_trust(domain, description: options[:description])
  say(Rainbow("Trust added for: #{domain}").green)
end

#trust_remove(domain) ⇒ void

This method returns an undefined value.

Remove a trusted MCP source.

Parameters:

  • domain (Object)

    the domain



320
321
322
323
324
325
326
327
328
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 320

def trust_remove(domain)
  manager = build_trust_manager
  if manager.remove_trust(domain)
    say(Rainbow("Trust removed for: #{domain}").green)
  else
    warn(Rainbow("Domain not found in user trust sources: #{domain}").red)
    exit(1)
  end
end

#trust_sourcesvoid

This method returns an undefined value.

List trusted MCP sources.



272
273
274
275
276
277
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 272

def trust_sources
  manager = build_trust_manager
  sources = manager.list

  render_trust_sources(sources)
end

#validateArray<Hash>

Validate the input and return any errors.

Returns:

  • (Array<Hash>)


122
123
124
125
126
127
128
129
130
# File 'lib/rosett_ai/thor/tasks/mcp.rb', line 122

def validate
  registry = build_registry
  validator = RosettAi::Mcp::Admin::SchemaValidator.new
  results = validator.validate_all(registry)

  render_validation_results(results)
  all_valid = results.all? { |r| r[:valid] }
  exit(all_valid ? 0 : 2)
end