ConduitMcp.Client.Adapters.Gemini (ConduitMCP v0.11.0)

Copy Markdown View Source

Converts between a ConduitMcp.Client.ToolSet and Gemini API (generateContent) function calling. Pure functions over string-keyed maps; no HTTP.

Experimental

This adapter is experimental: its shapes come from the Gemini API reference, not from end-to-end runs against the API, and may change in a minor release.

{toolset, _} = ConduitMcp.Client.Naming.build([{"files", tools}])
request = %{"contents" => contents,
            "tools" => [%{"functionDeclarations" => Gemini.function_declarations(toolset)}]}

{calls, rejected} = Gemini.function_calls(response, toolset)
# run each %ToolCall{} through the client pipeline (or let
# ConduitMcp.Client.Adapters.execute_tool_calls/4 do all of this), then:
parts = Enum.map(outcomes, fn {call, outcome} ->
  {:ok, name} = ConduitMcp.Client.ToolSet.exposed_name(toolset, call.server_id, call.name)
  Gemini.function_response(name, outcome, id: call.call_id)
end)
# next turn: %{"role" => "user", "parts" => rejected ++ parts}

Verified rules (2026-10-17)

From the FunctionDeclaration, FunctionCall and FunctionResponse reference at https://ai.google.dev/api/generate-content and the guide at https://ai.google.dev/gemini-api/docs/function-calling:

  • FunctionDeclaration.name may contain a-z A-Z 0-9 _ : . -, at most 128 characters. The toolset's exposed names (^[A-Za-z0-9_-]{1,64}$) are a subset.
  • parametersJsonSchema takes the parameters as JSON Schema (an object schema); parameters takes the OpenAPI 3.0 Schema subset instead. The two are mutually exclusive. This adapter sends the MCP inputSchema unchanged as parametersJsonSchema.
  • description is required, so a tool without one gets "".
  • FunctionCall is {id?, name, args?}; FunctionResponse is {id?, name, response} where response must be a JSON object. By the reference's convention the output goes under "output" and a failure under "error".

Results

function_response/3 puts the result under response.output: the structuredContent object when present (default structured: :prefer), otherwise the content as text, with media as placeholders such as "[image: image/png, 1024 bytes]". A result with is_error: true, or a ConduitMcp.Client.Error, goes under response.error as text. Descriptions have invisible characters removed and are capped at 4 KiB (PI-1); only tools in the toolset are emitted (PO-7).

Summary

Functions

Extracts the function calls from a response.

The functionDeclarations for toolset, in toolset order. Wrap them as %{"functionDeclarations" => declarations} in the request's tools.

Builds the functionResponse part answering a call to name (the exposed name the model used).

Functions

function_calls(response, toolset)

@spec function_calls(map() | [map()], ConduitMcp.Client.ToolSet.t()) ::
  {[ConduitMcp.Client.ToolCall.t()], [map()]}

Extracts the function calls from a response.

Accepts a generateContent response (the first candidate's content is used), a Content ("parts"), or a list of parts. Each functionCall part whose name resolves in toolset becomes a ConduitMcp.Client.ToolCall with call_id set to the call's id (which Gemini may omit, giving nil).

A call naming a tool not in the toolset, or with args that are not an object, produces no call (HB-2). Instead it yields a functionResponse part with an "error", returned in the second list, to send back to the model. Other parts are ignored.

function_declarations(toolset)

@spec function_declarations(ConduitMcp.Client.ToolSet.t()) :: [map()]

The functionDeclarations for toolset, in toolset order. Wrap them as %{"functionDeclarations" => declarations} in the request's tools.

function_response(name, result, opts \\ [])

@spec function_response(
  String.t(),
  ConduitMcp.Client.ToolResult.t() | ConduitMcp.Client.Error.t(),
  keyword()
) :: map()

Builds the functionResponse part answering a call to name (the exposed name the model used).

Options

  • :id - the functionCall.id being answered, when Gemini sent one.
  • :structured - :prefer (default) sends structuredContent itself as output when present; :auto, :append and :ignore render the content as text, handling structuredContent as described in ConduitMcp.Client.Adapters.OpenAI.tool_output/3.