Production-ready MCP client with mTLS, OAuth 2.1, and semantic discovery
Project description
DataGrout Conduit — Python SDK
Production-ready MCP client with mTLS identity, OAuth 2.1, semantic discovery, and cost tracking.
Installation
pip install datagrout-conduit==0.5.0
Quick Start
from datagrout.conduit import Client
async with Client("https://gateway.datagrout.ai/servers/{uuid}/mcp") as client:
tools = await client.list_tools()
result = await client.call_tool("salesforce@1/get_lead@1", {"id": "123"})
Authentication
Bearer Token
client = Client(
"https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth={"bearer": "your-access-token"},
)
OAuth 2.1 (client_credentials)
client = Client(
"https://gateway.datagrout.ai/servers/{uuid}/mcp",
client_id="your-client-id",
client_secret="your-client-secret",
)
The SDK automatically fetches, caches, and refreshes JWTs before they expire.
mTLS (Mutual TLS)
After bootstrapping, the client certificate handles authentication at the TLS layer — no tokens needed.
from datagrout.conduit import Client, ConduitIdentity
# Auto-discover from env vars, CONDUIT_IDENTITY_DIR, or ~/.conduit/
client = Client("https://gateway.datagrout.ai/servers/{uuid}/mcp", identity_auto=True)
# Explicit identity from files
identity = ConduitIdentity.from_paths("certs/client.pem", "certs/client_key.pem")
client = Client("...", identity=identity)
# Multiple agents on one machine
client = Client("...", identity_dir="/opt/agents/agent-a/.conduit", identity_auto=True)
Identity Auto-Discovery Order
identity_diroption (if provided)CONDUIT_MTLS_CERT+CONDUIT_MTLS_KEYenvironment variables (inline PEM)CONDUIT_IDENTITY_DIRenvironment variable (directory path)~/.conduit/identity.pem+~/.conduit/identity_key.pem.conduit/relative to the current working directory
For DataGrout URLs (*.datagrout.ai), auto-discovery runs silently even without identity_auto=True.
Bootstrapping an mTLS Identity
First-run provisioning — generates a keypair, registers with the DataGrout CA, and saves certs locally. After this, the token is never needed again.
# First run: token needed for registration
client = await Client.bootstrap_identity(
url="https://gateway.datagrout.ai/servers/{uuid}/mcp",
auth_token="your-access-token",
name="my-laptop",
)
# Or bootstrap with OAuth 2.1 client_credentials
client = await Client.bootstrap_identity_oauth(
url="https://gateway.datagrout.ai/servers/{uuid}/mcp",
client_id="your-client-id",
client_secret="your-client-secret",
name="my-laptop",
)
# Subsequent runs: no token needed, mTLS auto-discovered
client = Client("https://gateway.datagrout.ai/servers/{uuid}/mcp")
Semantic Discovery
When use_intelligent_interface is enabled, list_tools() returns only DataGrout's meta-tools. Agents use semantic search instead of enumerating raw integrations:
client = Client("...", use_intelligent_interface=True)
# Semantic search across all connected integrations
results = await client.discover(query="find unpaid invoices", limit=5)
# Direct execution with cost tracking
result = await client.perform(
tool="salesforce@1/get_lead@1",
args={"id": "123"},
)
Cost Tracking
Every tool call returns a receipt with credit usage:
from datagrout.conduit import extract_meta
result = await client.call_tool("salesforce@1/get_lead@1", {"id": "123"})
meta = extract_meta(result)
if meta:
print(f"Credits: {meta.receipt.net_credits}")
print(f"Savings: {meta.receipt.savings}")
Transports
# MCP (default) — full MCP protocol over Streamable HTTP
client = Client(url)
# JSONRPC — lightweight, stateless, same tools and auth
client = Client(url, transport="jsonrpc")
# WebSocket — bidirectional push; requires pip install 'datagrout-conduit[ws]'
client = Client("wss://gateway.datagrout.ai/servers/{uuid}/ws", transport="websocket")
WebSocket transport
The WebSocket transport uses the datagrout-jsonrpc.v1 subprotocol over a single persistent wss:// connection. All concurrent requests are multiplexed on that connection; responses are correlated by JSON-RPC id via asyncio.Future with no head-of-line blocking.
from datagrout.conduit import Client
async with Client(
"wss://gateway.datagrout.ai/servers/{uuid}/ws",
auth={"bearer": "your-token"},
transport="websocket",
) as client:
# Subscribe to server-pushed events
sub = await client.subscribe("agents.my-agent-id.events")
async for event in sub:
print(f"{event.event}: {event.data}")
await client.unsubscribe(sub.id)
Supported topics:
| Topic | Fires when |
|---|---|
agents.<agent_id>.events |
Agent lifecycle events (plan started, IC completed, grounding failed, …) |
tools.<tool_name>.results |
A specific tool call completes |
tasks.<task_id>.* |
Long-running background task transitions |
flows.<flow_id>.* |
flow.into progress and completion |
governor.<server_uuid> |
Governor percept events (file change, schedule, webhook) |
You can also call await sub.recv() for manual, one-event-at-a-time consumption:
event = await sub.recv()
print(event.event, event.data)
Reconnection: after a disconnect, send_request raises RuntimeError("WS transport not connected"). Re-call connect() and re-subscribe — subscriptions do not survive reconnects in v0.4.
API Reference
Client Options
Client(
url: str,
auth: dict = None, # {"bearer": "..."} or {"client_credentials": {...}}
transport: str = "jsonrpc", # "jsonrpc", "mcp", or "websocket"
use_intelligent_interface: bool = False,
identity: ConduitIdentity = None, # explicit mTLS identity
identity_auto: bool = False, # auto-discover identity
identity_dir: str = None, # custom identity directory
disable_mtls: bool = False, # opt out of mTLS auto-discovery
client_id: str = None, # OAuth shorthand
client_secret: str = None, # OAuth shorthand
)
Standard MCP Methods
| Method | Description |
|---|---|
list_tools() |
List available tools |
call_tool(name, args) |
Execute a tool |
list_resources() |
List resources |
read_resource(uri) |
Read a resource |
list_prompts() |
List prompts |
get_prompt(name, args) |
Get a prompt |
DataGrout Extensions
| Method | Description |
|---|---|
discover(query, limit, integrations) |
Semantic tool search |
perform(tool, args, demux) |
Direct tool execution with tracking |
perform_batch(calls) |
Parallel tool execution |
guide(goal, policy, session_id) |
Guided multi-step workflow |
flow_into(plan, ...) |
Workflow orchestration |
prism_focus(data, lens) |
Data transformation via Prism lens |
estimate_cost(tool, args) |
Pre-execution credit estimate |
Bootstrap Methods
| Method | Description |
|---|---|
Client.bootstrap_identity(url, auth_token, name) |
Bootstrap mTLS with access token |
Client.bootstrap_identity_oauth(url, client_id, client_secret, name) |
Bootstrap mTLS with OAuth 2.1 |
Requirements
- Python 3.10+
httpx(for JSONRPC transport)mcppackage (optional, for MCP transport mode)
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file datagrout_conduit-0.5.0.tar.gz.
File metadata
- Download URL: datagrout_conduit-0.5.0.tar.gz
- Upload date:
- Size: 58.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a607b5498873a898a363c83653f996035e6b17d916399ae5c8b40391cd86630
|
|
| MD5 |
ef235ace19b3b86494078d9d70065020
|
|
| BLAKE2b-256 |
40b1f636a8d901a483405e47dc6d7519050a01cc929334fb4ece3f62bcd23427
|
File details
Details for the file datagrout_conduit-0.5.0-py3-none-any.whl.
File metadata
- Download URL: datagrout_conduit-0.5.0-py3-none-any.whl
- Upload date:
- Size: 47.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26a9cfadaf04897bde35491b647184ac806feab1b3ede9e9dad85a2fab949b63
|
|
| MD5 |
4cb73042fa68cf41348ce2136671af73
|
|
| BLAKE2b-256 |
fdf1290f30a68d1519cd9934ee04894afd7dfb53af7ed560685f4ac2900e669b
|