Production-ready MCP client with mTLS, OAuth 2.1, and semantic discovery.
Connect to remote MCP and JSONRPC servers, invoke tools, discover capabilities with natural language, and track costs — all with enterprise-grade security.
Add to your Gemfile:
gem "datagrout-conduit", "~> 0.1"Or install directly:
gem install datagrout-conduitrequire "datagrout_conduit"
# Create a client
client = DatagroutConduit::Client.new(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth: { bearer: "your-token" }
)
# Connect and initialize the MCP session
client.connect
# List available tools
tools = client.list_tools
puts "Found #{tools.size} tools"
# Call a tool
result = client.call_tool("salesforce@1/get_lead@1", { id: "123" })
puts result
# Disconnect
client.disconnectclient = DatagroutConduit::Client.new(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth: { bearer: "your-token" }
)client = DatagroutConduit::Client.new(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth: { api_key: "your-api-key" }
)provider = DatagroutConduit::OAuth::TokenProvider.new(
client_id: "my_client_id",
client_secret: "my_client_secret",
token_endpoint: "https://app.datagrout.ai/servers/{uuid}/oauth/token"
)
client = DatagroutConduit::Client.new(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth: { oauth: provider }
)The token endpoint is auto-derived from MCP URLs — /mcp becomes /oauth/token. Tokens are cached and refreshed 60 seconds before expiry.
# Auto-discover identity from standard locations
identity = DatagroutConduit::Identity.try_discover
# Or load explicitly
identity = DatagroutConduit::Identity.from_paths("cert.pem", "key.pem", ca_path: "ca.pem")
# Or from PEM strings
identity = DatagroutConduit::Identity.from_pem(cert_pem, key_pem, ca_pem: ca_pem)
# Or from environment variables
identity = DatagroutConduit::Identity.from_env
client = DatagroutConduit::Client.new(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
identity: identity
)Identity auto-discovery order:
override_dir(if provided)CONDUIT_MTLS_CERT+CONDUIT_MTLS_KEYenv varsCONDUIT_IDENTITY_DIRenv var~/.conduit/identity.pem+identity_key.pem.conduit/relative to cwd
For DataGrout URLs, identity discovery happens automatically.
Bootstrap a new mTLS identity with a one-time access token:
client = DatagroutConduit::Client.bootstrap_identity(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth_token: "your-one-time-token",
name: "my-agent"
)Or with OAuth client credentials:
client = DatagroutConduit::Client.bootstrap_identity_oauth(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
client_id: "id",
client_secret: "secret",
name: "my-agent"
)After bootstrap, subsequent runs auto-discover the saved identity — no tokens needed.
You can also use the registration class directly:
private_pem, public_pem = DatagroutConduit::Registration.generate_keypair
response = DatagroutConduit::Registration.register_identity(public_pem, auth_token: "token")
paths = DatagroutConduit::Registration.save_identity(response.cert_pem, private_pem, "~/.conduit", ca_pem: response.ca_cert_pem)
ca_pem = DatagroutConduit::Registration.fetch_ca_certclient = DatagroutConduit::Client.new(
url: "https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth: { bearer: "token" },
transport: :mcp
)client = DatagroutConduit::Client.new(
url: "https://gateway.datagrout.ai/servers/{uuid}/jsonrpc",
auth: { bearer: "token" },
transport: :jsonrpc
)Both transports send JSON-RPC 2.0 requests via HTTP POST. MCP uses the MCP Streamable HTTP framing. Both configure Faraday SSL with mTLS client certificates when an identity is present.
client.connect
# Tools
tools = client.list_tools
result = client.call_tool("tool-name", { arg: "value" })
# Resources
resources = client.list_resources
content = client.read_resource("resource://uri")
# Prompts
prompts = client.list_prompts
messages = client.get_prompt("prompt-name", { key: "value" })
client.disconnectFind tools by natural language — 10-100x more token-efficient than listing all tools.
results = client.discover(goal: "find unpaid invoices", limit: 10)
results.tools.each do |tool|
puts "#{tool.name} (score: #{tool.score})"
endresult = client.perform("salesforce@1/get_lead@1", { email: "john@example.com" }, demux: false)session = client.guide(goal: "create invoice from lead")
puts session.status # => "in_progress"
puts session.options # => available choices
session = session.choose("option_a")
result = session.complete # => final result when status == "completed"plan = [
{ "tool" => "get_lead", "args" => { "email" => "john@example.com" } },
{ "tool" => "create_invoice", "args" => { "lead_id" => "$prev.id" } }
]
result = client.flow_into(plan)result = client.prism_focus(data: raw_data, lens: "summary")estimate = client.estimate_cost("salesforce@1/get_lead@1", { id: "123" })Every tool-call result from DataGrout includes a cost receipt:
result = client.call_tool("salesforce@1/get_lead@1", { id: "123" })
meta = DatagroutConduit.extract_meta(result)
if meta
puts "Credits charged: #{meta.receipt.net_credits}"
puts "Receipt ID: #{meta.receipt.receipt_id}"
puts "Balance: #{meta.receipt.balance_after}"
if meta.receipt.byok.enabled
puts "BYOK discount: #{meta.receipt.byok.discount_applied}"
end
end- DataGrout URL detection:
DatagroutConduit.dg_url?(url)returnstruefordatagrout.aiordatagrout.devdomains - Intelligent interface: Automatically enabled for DG URLs —
list_toolsfilters to non-@tools (DG semantic tools only). Disable withuse_intelligent_interface: false - Auto mTLS: DG URLs automatically attempt identity discovery
- DG extension warnings: Non-DG URLs log a one-time warning when DG-specific methods are called
- Default transport:
:mcp
begin
client.list_tools
rescue DatagroutConduit::NotInitializedError
client.connect
retry
rescue DatagroutConduit::McpError => e
puts "MCP error #{e.code}: #{e.message}"
rescue DatagroutConduit::RateLimitedError => e
puts "Rate limited: #{e.used}/#{e.limit}"
rescue DatagroutConduit::AuthError => e
puts "Authentication failed: #{e.message}"
rescue DatagroutConduit::ConnectionError => e
puts "Connection error: #{e.message}"
rescue DatagroutConduit::ConfigError => e
puts "Configuration error: #{e.message}"
end- DataGrout Library — Browse and discover integrations
- Security — Security documentation
- MCP Inspector — Interactive MCP testing
- JSONRPC Inspector — Interactive JSONRPC testing
- Labs Papers — Research and whitepapers
MIT License. Copyright (c) DataGrout.