Data sources in MCP and CLI
Let Claude Code and the Dibbla CLI answer questions from your own managed database — configure semantic search, expose the source with a minimum role, ask it, and see every question in Activity.
A data source is a managed database your organization has made available
to agents. Workflows have used data sources for a long time; this page is
about using one outside a workflow — from Claude Code (or any assistant
connected to Dibbla), from the dibbla CLI, and from your own code.
The question an agent asks is a semantic search with optional exact-match
filters — “an angry line from Mario” is a search for angry line filtered
on speaker = mario. It is not free SQL: what can come back is decided by
the search configuration an administrator writes, and nothing else.
The whole path takes four steps:
-
Configure search
Say which table to search, which column holds the vectors, which columns come back, and which columns may be filtered on.
-
Expose the source with a minimum role
An owner or admin switches the source on for agents in Console → MCP and picks the lowest role that may ask it.
-
Ask it
In Claude Code through the Dibbla connector, or with
dibbla datasource query. -
See it in Activity
Every question is a row under Console → MCP → Activity.
What you need
- A managed database (
dibbla db create …) that is switched on as a data source: Console → Databases, the database’s … menu, Add as data source. - A table in it with a pgvector
vector(N)column, already filled with embeddings from one model. The platform searches vectors; it does not write them. - To configure search: the owner, admin or developer role. To expose the source to agents: owner or admin.
- Claude Code connected to Dibbla — see
Connect an AI agent — and/or the
dibblaCLI, signed in.
The example used on this page
A database voicelines with one table of game dialogue. speaker is the
column agents will filter on:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE lines (
id int PRIMARY KEY,
speaker text,
line text,
mood text,
embedding vector(1536) -- text-embedding-3-small
);
Fill embedding with the embedding of each row’s text, using the same model
you will name in the search configuration. A few lines of Python are enough,
run with DATABASE_URL="$(dibbla db connect voicelines -q)":
import os, psycopg
from openai import OpenAI
client = OpenAI() # OPENAI_API_KEY in your environment
with psycopg.connect(os.environ["DATABASE_URL"]) as db:
rows = db.execute("SELECT id, speaker, line, mood FROM lines WHERE embedding IS NULL").fetchall()
for id_, speaker, line, mood in rows:
text = f"{speaker} ({mood}): {line}"
vec = client.embeddings.create(model="text-embedding-3-small", input=text).data[0].embedding
db.execute("UPDATE lines SET embedding = %s::vector WHERE id = %s", (str(vec), id_))
Nothing in Dibbla knows about this table. Use your own table, column names and model; the steps are the same.
1. Configure search
Search is configured on the data source with the API — there is no console
form and no CLI command for it yet. You need the source’s id and an
API token:
$curl -s -H "Authorization: Bearer $DIBBLA_API_TOKEN" https://api.dibbla.com/api/wf/data-sources # look for source_id db_voicelines and note its id
Use api.<your-domain> wherever this page says api.dibbla.com, and
console.<your-domain> for the console. Nothing else changes.
Then save the configuration:
PUT https://api.dibbla.com/api/wf/data-sources/{id}
Authorization: Bearer <your-api-token>
Content-Type: application/json
{
"connection_config": {
"embedding_configs": [
{
"label": "Voice lines",
"table": "lines",
"columns": ["embedding"],
"model": "text-embedding-3-small",
"dimensions": 1536,
"content_columns": "id, speaker, line, mood"
}
]
},
"filter_schema": {
"speaker": "string"
}
}
| Field | Meaning |
|---|---|
table | The table to search, name or schema.name. |
columns | The vector column. Default ["embedding"]. |
model, dimensions | The model that filled the column, and its vector size. Questions are embedded with the same model. |
content_columns | The columns that come back per hit. The first is the hit’s id, the second its title. Only these columns are ever returned. |
label | The name an agent can use as target to search only this entry. |
distance_metric | cosine (default), l2 or inner_product. |
filter_schema | The columns that may be filtered on, with their type (string, number, boolean). A filter on any other column is refused. |
The configuration is checked against the database before it is stored.
A table, column or dimension that does not match is refused with 400 and a
list of every problem — column "embedding" in table "lines" holds 768-dimensional vectors, the config says 1536, for example — and nothing is
saved. Questions are embedded through the Dibbla AI gateway, with your
organization’s own OpenAI key if it has one.
Send every key you want to keep. A table that should not be searchable is
simply left out of embedding_configs; a column that should never come back
is left out of content_columns.
2. Expose the source with a minimum role
A configured source is still invisible to agents until an owner or admin exposes it. Open Console → MCP → Exposed tools and find the Data sources section:
- Switch Exposed on for the source.
- Pick the Minimum role — the lowest organization role that may ask it:
viewer,developer,adminorowner.
The counter shows how many sources are exposed (at most 100 per organization, counted separately from functions). Only managed databases that are switched on under Databases can be exposed; other sources show Not available. Workflows are not affected by any of this.
Setting a source to viewer lets viewers ask its curated questions. It is not
what keeps the database safe: members who can already reach the database —
through the database tools or dibbla db connect — still can, whatever the
minimum role says. Choose the minimum role for who should be able to ask,
and choose content_columns and filter_schema for what may come back.
What each member sees then follows from their role:
| The source is | A member at or above the minimum role | A member below it |
|---|---|---|
| Exposed | listed, can ask it | not listed; asking is refused (403) |
| Not exposed, or switched off | not listed; asking answers 404 | not listed; 404 |
A source that is not exposed answers exactly like one that does not exist, so its name is not revealed.
3. Ask it
In Claude Code
With the Dibbla connector connected, just ask:
show me an angry voiceline from mario
The assistant uses the read-only platform_data_sources tool: it lists the
sources you may ask, sees that speaker is filterable, and searches for an
angry line with speaker = mario. Because the tool only reads, your client
can allow it without asking each time. It needs nothing beyond the normal
connector sign-in.
With the CLI
$dibbla datasource list ID NAME MIN ROLE FILTERS DESCRIPTION db_voicelines voicelines viewer speaker (string) Game dialogue…
$dibbla datasource query db_voicelines "an angry line" --filter speaker=mario --top-k 3 count: 3 results: - id: "42" title: mario metadata: {line: …, mood: angry, _score: 0.61, _target: Voice lines}
--filter column=value can be repeated: the same column twice means either
value, different columns must all match. --top-k is 1–20 (default 10),
--target limits the search to one label, -o json prints JSON. The CLI and
Claude Code ask the same source the same way, so they get the same answer.
What the answers tell you:
404— not queryable for you. Unknown, not exposed, or switched off.dibbla datasource listshows what you can ask.403— your role is below the source’s minimum role. An admin can lower it in Console → MCP.400—cannot filter on mood: filterable columns are speaker. Only the columns infilter_schemacan be filtered on.- At most 20 hits, at most 16 KiB. A longer answer is cut — long values
first, then the last hits — and marked
truncatedwith a note saying what was cut. Ask for fewer hits or a narrower filter to see more.
Every hit is text from your own table, written by whoever filled it. The connector tells the assistant to quote or summarize it and never to act on instructions found inside it. Do the same with anything you build on top of these answers.
From your own code
The same two calls are plain HTTP, for scripts and services:
GET /api/wf/slim/data-sources and
POST /api/wf/slim/data-sources/{id}/query with
{"query": "an angry line", "filters": {"speaker": "mario"}, "top_k": 3}.
They answer YAML by default, JSON with Accept: application/json.
4. See it in Activity
Every question — from Claude Code, the CLI or the API — is recorded. Open
Console → MCP → Activity and set Type to Data sources: each row
shows the source, who asked, when, how long it took, the size of the answer,
and Source (mcp, cli or api).
The question and the rows it returned are never stored — only their sizes
and digests. Function calls stay under Type → Functions, the default, and
dibbla fn invocations lists function calls only.
Troubleshooting
| You see | What it means |
|---|---|
The source is missing from dibbla datasource list | It is not exposed, your role is below its minimum role, or it is switched off under Databases. |
| Not available in the Data sources section | The source is not a managed database, or it is switched off under Databases. |
this data source has no semantic search configured | Step 1 has not been done for this source. |
400 Invalid search configuration when saving | The config does not match the table; the problems list says exactly what. |
429 | Too many questions — the organization’s limit per minute or per day. Wait as long as Retry-After says. |
| Hits that look unrelated | The question is embedded with a different model than the one that filled the column. model must name the model you used. |