BonnardBonnard0.3.11
Sdk

AI Agent Tools

Give your AI agents direct access to your semantic layer — pre-built tools for Vercel AI SDK, LangChain, and any framework that accepts Zod schemas.

Instead of writing boilerplate tool definitions for each AI framework, import createTools from the SDK and get four production-ready tools that handle schema discovery, querying, SQL execution, and field metadata.

import { createClient } from '@bonnard/sdk';
import { createTools } from '@bonnard/sdk/ai/vercel';

const bon = createClient({ apiKey: 'bon_pk_...' });
const tools = createTools(bon);

const result = await generateText({
  model: anthropic('claude-sonnet-4-5-20250514'),
  tools,
  prompt: 'What were our top 5 products by revenue last quarter?',
});

Install

The AI tools are included in @bonnard/sdk — no extra packages. You only need your AI framework as a peer dependency:

# Vercel AI SDK
npm install @bonnard/sdk ai @ai-sdk/anthropic

# LangChain / LangGraph
npm install @bonnard/sdk @langchain/core @langchain/anthropic @langchain/langgraph

Framework adapters

Vercel AI SDK

import { createClient } from '@bonnard/sdk';
import { createTools } from '@bonnard/sdk/ai/vercel';
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';

const bon = createClient({ apiKey: 'bon_pk_...' });
const tools = createTools(bon);

// tools is Record<string, CoreTool> — spread with your own tools
const result = await generateText({
  model: anthropic('claude-sonnet-4-5-20250514'),
  tools: { ...tools, ...myOtherTools },
  maxSteps: 10,
  prompt: 'Show me revenue by region for the last 6 months',
});

LangChain / LangGraph

import { createClient } from '@bonnard/sdk';
import { createTools } from '@bonnard/sdk/ai/langchain';
import { ChatAnthropic } from '@langchain/anthropic';
import { createReactAgent } from '@langchain/langgraph/prebuilt';

const bon = createClient({ apiKey: 'bon_pk_...' });
const tools = createTools(bon);

// tools is DynamicStructuredTool[] — spread with your own tools
const agent = createReactAgent({
  llm: new ChatAnthropic({ model: 'claude-sonnet-4-5-20250514' }),
  tools: [...tools, ...myOtherTools],
});

const result = await agent.invoke({
  messages: [{ role: 'user', content: 'What data sources are available?' }],
});

Framework-agnostic

If your framework isn't listed above, import the base tools and wrap them yourself. Each tool has a name, description, schema (Zod), and execute function:

import { createClient } from '@bonnard/sdk';
import { createTools } from '@bonnard/sdk/ai';
import type { BonnardTool } from '@bonnard/sdk/ai';

const bon = createClient({ apiKey: 'bon_pk_...' });
const tools = createTools(bon); // BonnardTool[]

// Wrap for your framework
for (const tool of tools) {
  console.log(tool.name);        // "explore_schema"
  console.log(tool.description);  // "Discover available data sources..."
  console.log(tool.schema);       // Zod schema — convert to JSON Schema with zod-to-json-schema
  await tool.execute({ ... });    // Call directly
}

This also works for MCP servers — convert the Zod schemas to JSON Schema with zod-to-json-schema.

Tools included

Every createTools(client) call returns four tools:

explore_schema

Discover available data sources, their measures, dimensions, and segments.

ParameterTypeDescription
namestring?Source name to get full field listings (e.g. "orders")
searchstring?Keyword to search across all field names and descriptions
// No args — list all sources with summary counts
await tools.explore_schema.execute({});
// → [{ name: "orders", type: "view", measures: 5, dimensions: 8, segments: 1 }]

// By name — full field details for one source
await tools.explore_schema.execute({ name: "orders" });
// → { name: "orders", measures: [...], dimensions: [...], segments: [...] }

// Search — find fields by keyword across all sources
await tools.explore_schema.execute({ search: "revenue" });
// → [{ source: "orders", field: "orders.revenue", kind: "measure", type: "number" }]

query

Run structured queries with measures, dimensions, filters, and time dimensions. All field names must be fully qualified (e.g. "orders.revenue").

ParameterTypeDescription
measuresstring[]?Measures to aggregate (e.g. ["orders.revenue"])
dimensionsstring[]?Dimensions to group by
timeDimensionsarray?Time dimensions with dimension, granularity, dateRange
filtersarray?Filters with member, operator, values
segmentsstring[]?Pre-defined filter segments
orderobject?Sort order (e.g. { "orders.revenue": "desc" })
limitnumber?Max rows (default 250)
offsetnumber?Pagination offset

sql_query

Execute raw SQL when structured queries can't express what you need — CTEs, UNIONs, custom arithmetic.

ParameterTypeDescription
sqlstringSQL using Cube syntax with MEASURE() for aggregations

describe_field

Get metadata for a specific field — its type, description, and which source it belongs to.

ParameterTypeDescription
fieldstringFully qualified field name (e.g. "orders.revenue")

Authentication for agents

The tools use whatever auth you configure on the client. All three patterns work:

Simple: publishable key

Best for internal tools or prototypes — all queries run with your org's full access.

const bon = createClient({ apiKey: 'bon_pk_...' });
const tools = createTools(bon);

Multi-tenant: token exchange

For B2B apps where each customer's agent should only see their own data. Your backend exchanges a secret key for a scoped token:

// Server-side: create client with token exchange
const bon = createClient({
  fetchToken: async () => {
    const res = await fetch('https://app.bonnard.dev/api/sdk/token', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.BONNARD_SECRET_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        security_context: { tenant_id: currentUser.tenantId },
      }),
    });
    const { token } = await res.json();
    return token;
  },
});

const tools = createTools(bon);
// Agent queries are automatically scoped to the tenant's data

See Authentication for details on token refresh and security context.

Frontend: Clerk session

For apps where users authenticate through Clerk:

import { useAuth } from '@clerk/nextjs';

const { getToken } = useAuth();
const bon = createClient({
  fetchToken: () => getToken(),
});
const tools = createTools(bon);

Tips for better agent responses

Add a system prompt with context

The tools give the agent the ability to query, but a good system prompt tells it how to approach your data:

const result = await generateText({
  model: anthropic('claude-sonnet-4-5-20250514'),
  tools,
  maxSteps: 10,
  system: `You are a data analyst. Always start by calling explore_schema to understand
what data is available before querying. Use describe_field to understand metrics
before presenting results. Format numbers with appropriate units.`,
  prompt: userMessage,
});

Set maxSteps high enough

Agents typically need 3–5 steps: explore schema → understand fields → query → maybe refine. Set maxSteps: 10 to give the agent room to work.

Combine with your own tools

The adapters return spreadable formats so you can mix Bonnard tools with your own:

// Vercel AI SDK — Record<string, CoreTool>
const result = await generateText({
  tools: { ...bonnardTools, saveReport, sendEmail },
});

// LangChain — StructuredTool[]
const agent = createReactAgent({
  tools: [...bonnardTools, saveReport, sendEmail],
});

See also

  • SDK — SDK overview and installation
  • Authentication — Auth patterns (publishable keys, token exchange)
  • Query Reference — Full query API reference
  • MCP — MCP server setup (alternative to SDK tools for Claude, ChatGPT, etc.)
  • Security Context — Row-level security for multi-tenant apps

On this page