Dibbla Docs Get started Guides Workflows Changelog
Open Console
Guides

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:

  1. Configure search

    Say which table to search, which column holds the vectors, which columns come back, and which columns may be filtered on.

  2. 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.

  3. Ask it

    In Claude Code through the Dibbla connector, or with dibbla datasource query.

  4. 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 dibbla CLI, 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.

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
On a self-hosted installation

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"
  }
}
FieldMeaning
tableThe table to search, name or schema.name.
columnsThe vector column. Default ["embedding"].
model, dimensionsThe model that filled the column, and its vector size. Questions are embedded with the same model.
content_columnsThe columns that come back per hit. The first is the hit’s id, the second its title. Only these columns are ever returned.
labelThe name an agent can use as target to search only this entry.
distance_metriccosine (default), l2 or inner_product.
filter_schemaThe 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.

connection_config replaces the whole map

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:

  1. Switch Exposed on for the source.
  2. Pick the Minimum role — the lowest organization role that may ask it: viewer, developer, admin or owner.

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.

A minimum role widens access; it does not protect the database

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 isA member at or above the minimum roleA member below it
Exposedlisted, can ask itnot listed; asking is refused (403)
Not exposed, or switched offnot listed; asking answers 404not 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 list shows 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 in filter_schema can 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 truncated with a note saying what was cut. Ask for fewer hits or a narrower filter to see more.
Results are data, not instructions

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 seeWhat it means
The source is missing from dibbla datasource listIt is not exposed, your role is below its minimum role, or it is switched off under Databases.
Not available in the Data sources sectionThe source is not a managed database, or it is switched off under Databases.
this data source has no semantic search configuredStep 1 has not been done for this source.
400 Invalid search configuration when savingThe config does not match the table; the problems list says exactly what.
429Too many questions — the organization’s limit per minute or per day. Wait as long as Retry-After says.
Hits that look unrelatedThe question is embedded with a different model than the one that filled the column. model must name the model you used.