query
queryRun a read-only SQL SELECT over the business database and get rows back. It is the FALLBACK, not the first move: when a read(entity) answers the question (leads, clients, conversations, bookings and the rest of read's catalog), use read, which carries the business's own definitions (e.g. open leads = read lead_pursuits active:true) that raw tables do not. Use query only when read cannot express the question (a count, a GROUP BY, a JOIN across tables, filtering on a column read() does not expose, a discount a customer used, an arbitrary aggregate). SCHEMA: query the org-scoped view schema soul_read (NOT public). Every table you can read is a view there, already filtered to THIS business. You never write a WHERE organization_id clause and never pass an org id. START with client_timeline for any question about one person or booking: every SMS, call (with AI summary), email, booking and human action in one feed (columns: channel, direction, actor, at, text, ai_summary, sentiment, category, client_id, conversation_id, booking_id). Filter client_id, ORDER BY at DESC, LIMIT. Read SELECT * FROM schema_notes once for column semantics (event-time columns, value meanings, columns that lie: e.g. use sent_at not created_at; contact_type = client means a real client). Never guess a column name: before you name a column of any other table you have not listed in this conversation, list THAT table's columns first (one table, never the whole schema): SELECT column_name FROM information_schema.columns WHERE table_schema='soul_read' AND table_name='<table>'. Reference tables unqualified (e.g. FROM bookings). Search_path is already soul_read. RULES: exactly ONE SELECT statement (a second statement, SET, INSERT/UPDATE/DELETE or DDL is refused). Results cap at 100 rows; the response is {rows, returned, truncated}. When truncated is true, add your OWN aggregation (COUNT/GROUP BY) or a tighter WHERE rather than paging blindly. Dates are stored as the business's local calendar; compare bare dates directly (e.g. scheduled_date BETWEEN '2026-07-01' AND '2026-07-31'). Prefer one precise aggregate query over many row dumps. YOUR ROLE reads only these views here (every other view returns no rows): activity_log, booking_assignments, booking_events, booking_notes, booking_series, bookings, call_transcripts, calls, campaign_recipients, campaigns, checks, claims, client_checkins, client_identifiers, client_pinned_notes, client_relationships, client_staff_relationships, clients, company_documents, contact_list_members, contact_lists, contact_tag_members, contact_tags, conversation_flows, conversation_reads, conversations, emails, flows, import_jobs, leads, messages, org_email_config, org_holidays, organizations, outbound_gate_verdicts, payments, recurrence_series, scheduling_rules, sop_categories, sop_question_log, sops, soul_findings, staff_availability_overrides, staff_default_availability, staff_earnings, staff_events, staff_profiles, tasks, team_members, training_assignments, training_modules, training_questions.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"sql": "<sql>"
},
"name": "query"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | A single read-only SQL SELECT over the soul_read schema, e.g. "SELECT status, count(*) FROM bookings WHERE scheduled_date BETWEEN '2026-07-01' AND '2026-07-31' GROUP BY status". |