Class: GroqRuby::MCP::Bridge

Inherits:
Object
  • Object
show all
Defined in:
lib/groq_ruby/mcp/bridge.rb

Overview

Glues one or more MCP servers into a Groq chat completion. Connects to each server, builds the OpenAI-style ‘tools:` array the model expects, and routes each `tool_calls` from the model back to the owning MCP server.

Bridge surfaces three MCP capabilities:

  • Tools — every server-advertised tool becomes a Groq function tool, namespaced as ‘<server>__<tool>`. The LLM calls these exactly like any other function tool; #call routes the call to the owning server.

  • Resources — for every server that advertises resources, Bridge injects a synthetic ‘<server>__read_resource(uri)` tool into #tools so the LLM can fetch resource content on demand. #resources returns the full inventory if you want to surface it (e.g. in a system-prompt catalogue).

  • Prompts#prompts returns the list of prompt templates advertised by each server. Prompts are typically surfaced to a user (a picker in your UI), not the LLM directly. #get_prompt renders one with arguments.

Optional capabilities are probed gracefully — a server that responds with ‘-32601 method not found` to `resources/list` simply contributes no resources.

Examples:

Wire a filesystem MCP server into chat.completions

fs = GroqRuby::MCP::ServerConfig.new(name: "fs", command: "...")
bridge = GroqRuby::MCP::Bridge.new([fs])
begin
  response = groq.chat.completions.create(
    model: "llama-3.3-70b-versatile",
    messages: messages,
    tools: bridge.tools
  )
  tool_call = response.choices.first.message.tool_calls&.first
  result = bridge.call(tool_call["function"]["name"], tool_call["function"]["arguments"]) if tool_call
ensure
  bridge.stop
end

Constant Summary collapse

NAME_SEPARATOR =
"__".freeze
READ_RESOURCE_SUFFIX =
"read_resource".freeze

Instance Method Summary collapse

Constructor Details

#initialize(configs, request_timeout: Client::DEFAULT_REQUEST_TIMEOUT) ⇒ Bridge

Returns a new instance of Bridge.

Parameters:

  • configs (Array<ServerConfig>)
  • request_timeout (Numeric) (defaults to: Client::DEFAULT_REQUEST_TIMEOUT)

    forwarded to each Client



50
51
52
53
54
55
# File 'lib/groq_ruby/mcp/bridge.rb', line 50

def initialize(configs, request_timeout: Client::DEFAULT_REQUEST_TIMEOUT)
  @clients = configs.map { |c| Client.connect(c, request_timeout: request_timeout) }
  @tool_index = build_tool_index
  @resource_index = build_resource_index
  @prompt_index = build_prompt_index
end

Instance Method Details

#call(namespaced_name, arguments) ⇒ Hash

Dispatch a tool call from a Groq response to the owning MCP server. Recognises the synthetic ‘<server>__read_resource` tool and routes to #read_resource instead.

Parameters:

  • namespaced_name (String)
  • arguments (Hash, String)

    parsed Hash or the JSON string Groq returns

Returns:

  • (Hash)

    the MCP server’s tool-call result

Raises:



96
97
98
99
100
101
102
103
104
# File 'lib/groq_ruby/mcp/bridge.rb', line 96

def call(namespaced_name, arguments)
  parsed_args = parse_args(arguments)
  if (server_name = match_synthetic_resource_tool(namespaced_name))
    uri = parsed_args["uri"] || parsed_args[:uri]
    read_resource(uri, server: server_name)
  else
    dispatch_tool_call(namespaced_name, parsed_args)
  end
end

#get_prompt(namespaced_name, arguments = {}) ⇒ Hash

Render a prompt template. The name is the namespaced ‘<server>__<prompt>` form returned by #prompts.

Parameters:

  • namespaced_name (String)
  • arguments (Hash) (defaults to: {})

Returns:

  • (Hash)

    the server’s ‘prompts/get` result (`messages` array)

Raises:



127
128
129
130
# File 'lib/groq_ruby/mcp/bridge.rb', line 127

def get_prompt(namespaced_name, arguments = {})
  entry = @prompt_index[namespaced_name] or raise UnknownToolError, "no MCP prompt named #{namespaced_name.inspect}"
  entry[:client].prompts_get(entry[:prompt].name, arguments)
end

#promptsArray<Hash>

Flat inventory of every prompt template discovered across all servers.

Returns:

  • (Array<Hash>)

    each entry has keys ‘:server`, `:namespaced_name`, `:prompt`



82
83
84
85
86
# File 'lib/groq_ruby/mcp/bridge.rb', line 82

def prompts
  @prompt_index.map do |namespaced, entry|
    {server: entry[:server_name], namespaced_name: namespaced, prompt: entry[:prompt]}
  end
end

#read_resource(uri, server: nil) ⇒ Hash

Read a resource by URI. When more than one server advertises the same URI, pass ‘server:` to disambiguate.

Parameters:

  • uri (String)
  • server (String, nil) (defaults to: nil)

    explicit server name

Returns:

  • (Hash)

    the server’s ‘resources/read` result

Raises:



113
114
115
116
117
118
# File 'lib/groq_ruby/mcp/bridge.rb', line 113

def read_resource(uri, server: nil)
  candidates = @resource_index.values.select { |e| e[:resource].uri == uri }
  candidates = candidates.select { |e| e[:server_name] == server } if server
  entry = candidates.first or raise UnknownToolError, "no MCP resource for uri #{uri.inspect}"
  entry[:client].resources_read(uri)
end

#resourcesArray<Hash>

Flat inventory of every resource discovered across all servers.

Returns:

  • (Array<Hash>)

    each entry has keys ‘:server`, `:resource` (a Resource)



75
76
77
# File 'lib/groq_ruby/mcp/bridge.rb', line 75

def resources
  @resource_index.values.map { |entry| {server: entry[:server_name], resource: entry[:resource]} }
end

#stopObject

Stop every underlying MCP server. Idempotent.



133
134
135
# File 'lib/groq_ruby/mcp/bridge.rb', line 133

def stop
  @clients.each(&:stop)
end

#tool_namesArray<String>

Returns every namespaced tool name #tools surfaces.

Returns:

  • (Array<String>)

    every namespaced tool name #tools surfaces



68
69
70
# File 'lib/groq_ruby/mcp/bridge.rb', line 68

def tool_names
  tools.map { |t| t[:function][:name] }
end

#toolsArray<Hash>

Tool definitions for ‘chat.completions.create(tools:)`. Includes every advertised MCP tool plus, for each server with resources, a synthetic `<server>__read_resource(uri)` tool so the LLM can fetch resource content on demand.

Returns:

  • (Array<Hash>)


63
64
65
# File 'lib/groq_ruby/mcp/bridge.rb', line 63

def tools
  @tool_index.values.map { |entry| as_groq_tool(entry) } + synthetic_resource_tools
end