Class: OmniAI::MCP::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/omniai/mcp/server.rb

Overview

Examples:

server = OmniAI::MCP::Server.new(tools: [...])
server.run

Constant Summary collapse

PROTOCOL_VERSION =
"2025-03-26"

Instance Method Summary collapse

Constructor Details

#initialize(tools:, logger: nil, name: "OmniAI", version: OmniAI::VERSION) ⇒ Server

Returns a new instance of Server.

Parameters:

  • tools (Array<OmniAI::Tool>)
  • logger (Logger, nil) (defaults to: nil)


13
14
15
16
17
18
# File 'lib/omniai/mcp/server.rb', line 13

def initialize(tools:, logger: nil, name: "OmniAI", version: OmniAI::VERSION)
  @tools = tools
  @logger = logger
  @name = name
  @version = version
end

Instance Method Details

#process(message) ⇒ String?

Parameters:

  • message (String)

Returns:

  • (String, nil)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/omniai/mcp/server.rb', line 41

def process(message)
  request = JRPC::Request.parse(message)

  response =
    case request.method
    when "initialize" then process_initialize(request:)
    when "ping" then process_ping(request:)
    when "tools/list" then process_tools_list(request:)
    when "tools/call" then process_tools_call(request:)
    end

  response&.generate if response&.result
rescue JRPC::Error => e
  JSON.generate({
    jsonrpc: JRPC::VERSION,
    id: request&.id,
    error: {
      code: e.code,
      message: e.message,
    },
  })
end

#process_initialize(request:) ⇒ JRPC::Response

Parameters:

Returns:



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/omniai/mcp/server.rb', line 66

def process_initialize(request:)
  JRPC::Response.new(id: request.id, result: {
    protocolVersion: PROTOCOL_VERSION,
    serverInfo: {
      name: @name,
      version: @version,
    },
    capabilities: {
      tools: { listChanged: true },
    },
  })
end

#process_ping(request:) ⇒ JRPC::Response

Parameters:

Returns:



81
82
83
# File 'lib/omniai/mcp/server.rb', line 81

def process_ping(request:)
  JRPC::Response.new(id: request.id, result: {})
end

#process_tools_call(request:) ⇒ JRPC::Response

Parameters:

Returns:

Raises:



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/omniai/mcp/server.rb', line 107

def process_tools_call(request:)
  name = request.params["name"]
  tool = @tools.find { |tool| tool.name.eql?(name) }

  text =
    begin
      tool.call(request.params["arguments"])
    rescue StandardError => e
      raise JRPC::Error.new(code: JRPC::Error::Code::INTERNAL_ERROR, message: e.message)
    end

  JRPC::Response.new(id: request.id, result: {
    content: [
      {
        type: "text",
        text:,
      },
    ],
  })
end

#process_tools_list(request:) ⇒ JRPC::Response

Parameters:

Returns:

Raises:



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/omniai/mcp/server.rb', line 90

def process_tools_list(request:)
  tools = @tools.map do |tool|
    {
      name: tool.name,
      description: tool.description,
      inputSchema: tool.parameters.serialize,
    }
  end

  JRPC::Response.new(id: request.id, result: { tools: })
end

#run(transport: OmniAI::MCP::Transport::Stdio.new) ⇒ Object

Parameters:



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/omniai/mcp/server.rb', line 21

def run(transport: OmniAI::MCP::Transport::Stdio.new)
  @logger&.info("#{self.class}#run: running")

  loop do
    message = transport.gets
    break if message.nil?

    @logger&.info("#{self.class}#run: message=#{message.inspect}")
    response = process(message)
    @logger&.info("#{self.class}#run: response=#{response.inspect}")

    transport.puts(response) if response
  end

  @logger&.info("#{self.class}#run: finished")
end