DatagroutConduit.Client (DataGrout Conduit v0.5.0)

Copy Markdown View Source

MCP/JSONRPC client GenServer.

Manages connection state (URL, auth tokens, request IDs, mTLS identity) and provides high-level methods for MCP protocol operations and DataGrout extensions.

Usage

{:ok, client} = DatagroutConduit.Client.start_link(
  url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
  auth: {:bearer, "token"}
)

{:ok, tools} = DatagroutConduit.Client.list_tools(client)
{:ok, result} = DatagroutConduit.Client.call_tool(client, "tool-name", %{arg: "val"})

Options

  • :url - Remote server URL (required)
  • :auth - Authentication: {:bearer, token}, {:api_key, key}, {:basic, user, pass}, or {:oauth, pid}
  • :transport - :mcp (default) or :jsonrpc
  • :transport_mod - Override transport module directly (e.g. for testing)
  • :identity - %DatagroutConduit.Identity{} for mTLS (auto-discovered for DG URLs)
  • :use_intelligent_interface - Filter @-containing tools from list_tools (default: true for DG URLs)
  • :name - GenServer registration name

Summary

Functions

Bootstrap an mTLS identity and start a client with it.

Bootstrap an mTLS identity using OAuth client_credentials.

Bootstrap by performing the autonomous DG onramp flow.

Calls a tool on the remote server.

Returns a specification to start this module under a supervisor.

Call any DataGrout first-party tool by its short name.

Semantic discovery: find tools matching a goal.

Estimate cost of calling a tool without executing it.

Gets a prompt with the given arguments.

Start or continue a guided execution session.

Lists prompts available on the remote server.

Lists resources available on the remote server.

Lists tools available on the remote server.

Execute a tool with DG extensions (demux, refract, chart).

Execute multiple tool calls in a single gateway request.

Plan tool execution for a goal using semantic discovery.

Reads a resource from the remote server.

Starts the client GenServer. See module docs for options.

Subscribe to a server-push topic (WebSocket transport only).

Cancel a server-side push subscription.

Types

auth()

@type auth() ::
  {:bearer, String.t()}
  | {:api_key, String.t()}
  | {:basic, String.t(), String.t()}
  | {:oauth, GenServer.server()}
  | nil

Functions

bootstrap_identity(opts)

@spec bootstrap_identity(keyword()) :: {:ok, pid()} | {:error, term()}

Bootstrap an mTLS identity and start a client with it.

Checks for an existing identity first. If found and not near expiry, starts a client with it. Otherwise generates a keypair, registers with DataGrout, saves the identity to disk, and starts a client.

Options

  • :url - Remote server URL (required)
  • :auth_token - Bearer token for registration (required for first run)
  • :name - Human-readable label (default: "conduit-client")
  • :identity_dir - Directory to store identity files (default: ~/.conduit/)
  • :endpoint - Registration endpoint (default: DG substrate endpoint)
  • :threshold_days - Days before expiry to trigger rotation (default: 7)
  • All other options are forwarded to start_link/1

bootstrap_identity_oauth(opts)

@spec bootstrap_identity_oauth(keyword()) :: {:ok, pid()} | {:error, term()}

Bootstrap an mTLS identity using OAuth client_credentials.

Like bootstrap_identity/1 but performs the OAuth token exchange inline instead of requiring a pre-obtained bearer token.

Options

  • :url - Remote server URL (required)
  • :client_id - OAuth client ID (required)
  • :client_secret - OAuth client secret (required)
  • :token_endpoint - OAuth token endpoint (derived from :url if absent)
  • :scope - OAuth scope (optional)
  • All other options from bootstrap_identity/1

bootstrap_onramp(opts)

@spec bootstrap_onramp(keyword()) :: {:ok, pid()} | {:error, term()}

Bootstrap by performing the autonomous DG onramp flow.

The all-in-one flow: onramp (no prior credentials required) → OAuth token exchange → mTLS identity registration and persistence.

On subsequent runs the saved mTLS identity is auto-discovered and no credentials are needed.

Options

  • :opts - %DatagroutConduit.Onramp.OnrampOptions{} (required)
  • :url - MCP server URL; required when the onramp response omits mcp_url
  • :name - Human-readable identity label (default: "conduit-client")
  • :identity_dir - Custom directory for identity persistence
  • All other options from bootstrap_identity/1

call_tool(client, name, arguments \\ %{})

@spec call_tool(GenServer.server(), String.t(), map()) ::
  {:ok, DatagroutConduit.Types.ToolResult.t()} | {:error, term()}

Calls a tool on the remote server.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

dg(client, tool_short_name, params \\ %{})

@spec dg(GenServer.server(), String.t(), map()) :: {:ok, map()} | {:error, term()}

Call any DataGrout first-party tool by its short name.

The short name (e.g. "prism.render") is automatically prefixed with data-grout/.

DatagroutConduit.Client.dg(client, "prism.render", %{"payload" => data})

discover(client, opts)

@spec discover(
  GenServer.server(),
  keyword()
) :: {:ok, DatagroutConduit.Types.DiscoverResult.t()} | {:error, term()}

Semantic discovery: find tools matching a goal.

estimate_cost(client, tool_name, args \\ %{})

@spec estimate_cost(GenServer.server(), String.t(), map()) ::
  {:ok, DatagroutConduit.Types.CreditEstimate.t()} | {:error, term()}

Estimate cost of calling a tool without executing it.

get_prompt(client, name, arguments \\ %{})

@spec get_prompt(GenServer.server(), String.t(), map()) ::
  {:ok, [DatagroutConduit.Types.PromptMessage.t()]} | {:error, term()}

Gets a prompt with the given arguments.

guide(client, opts)

@spec guide(
  GenServer.server(),
  keyword()
) :: {:ok, DatagroutConduit.Types.GuideState.t()} | {:error, term()}

Start or continue a guided execution session.

Options

  • :goal - Natural language description (required for new sessions)
  • :session_id - Continue an existing session
  • :choice - Make a choice in the current session

list_prompts(client)

@spec list_prompts(GenServer.server()) ::
  {:ok, [DatagroutConduit.Types.Prompt.t()]} | {:error, term()}

Lists prompts available on the remote server.

list_resources(client)

@spec list_resources(GenServer.server()) ::
  {:ok, [DatagroutConduit.Types.Resource.t()]} | {:error, term()}

Lists resources available on the remote server.

list_tools(client)

@spec list_tools(GenServer.server()) ::
  {:ok, [DatagroutConduit.Types.Tool.t()]} | {:error, term()}

Lists tools available on the remote server.

perform(client, tool_name, args \\ %{}, opts \\ [])

@spec perform(GenServer.server(), String.t(), map(), keyword()) ::
  {:ok, DatagroutConduit.Types.ToolResult.t()} | {:error, term()}

Execute a tool with DG extensions (demux, refract, chart).

perform_batch(client, calls)

@spec perform_batch(GenServer.server(), [map()]) :: {:ok, list()} | {:error, term()}

Execute multiple tool calls in a single gateway request.

Each element should be a map with "tool" and "args" keys. Returns a list of results in the same order as the input calls.

Example

calls = [
  %{"tool" => "data-grout/data.count", "args" => %{"data" => [1, 2, 3]}},
  %{"tool" => "data-grout/data.keys",  "args" => %{"data" => %{"a" => 1}}}
]
{:ok, results} = Client.perform_batch(client, calls)

plan(client, opts)

@spec plan(
  GenServer.server(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Plan tool execution for a goal using semantic discovery.

At least one of :goal or :query must be provided.

Options

  • :goal - Natural language goal
  • :query - Semantic search query
  • :server - Restrict to a specific server
  • :k - Number of candidates to consider
  • :policy - Execution policy
  • :have - Tools or data already available
  • :return_call_handles - Include call handles in response
  • :expose_virtual_skills - Include virtual skills in candidates
  • :model_overrides - Override model selection

read_resource(client, uri)

@spec read_resource(GenServer.server(), String.t()) ::
  {:ok, [DatagroutConduit.Types.ResourceContent.t()]} | {:error, term()}

Reads a resource from the remote server.

start_link(opts)

Starts the client GenServer. See module docs for options.

subscribe(client, topic)

@spec subscribe(GenServer.server(), String.t()) ::
  {:ok, String.t()} | {:error, term()}

Subscribe to a server-push topic (WebSocket transport only).

Requires transport: :websocket when starting the client.

Events arrive as {:subscription_event, subscription_id, event} messages in the calling process's mailbox, where event is a map with :event and :data keys.

{:ok, sub_id} = DatagroutConduit.Client.subscribe(client, "agents.my-agent-id.events")
receive do
  {:subscription_event, ^sub_id, %{event: e, data: d}} -> IO.inspect({e, d})
end
:ok = DatagroutConduit.Client.unsubscribe(client, sub_id)

Returns {:ok, subscription_id} or {:error, :not_ws_transport}.

unsubscribe(client, subscription_id)

@spec unsubscribe(GenServer.server(), String.t()) :: :ok | {:error, term()}

Cancel a server-side push subscription.

Requires transport: :websocket. Returns :ok.