DEV Community

Claix
Claix

Posted on Edited on

Your AI Agent Isn't the Problem: Its Database Access Is

Most AI agent workflows don't fail because the model is incapable.
They fail because the agent gets the wrong data, or gets the right data in the wrong shape.
A typical business workflow starts with a question like these:
• Has this customer accepted marketing consent?
• What is the status of this order?
• Which reservation does this guest have next?
The answer lives in a relational database such as Postgres or Supabase.
The agent is then expected to:
• Find the right tables
• Understand what the columns and codes mean
• Join tables correctly
• Write a query that is valid and read-only
• Read only what it is allowed to read
• Return a predictable result
• Trigger the next step in an automation
Sometimes it works.
Sometimes the output looks convincing but is wrong: a join that duplicates rows, a status spelled differently so that no rows match, a column nobody should have read, or an answer in a different shape that breaks the next node in the workflow.
This is the part of AI automation that is often underestimated: before an agent can reason about your business data, access to that data needs to be controlled, described and predictable.
The database problem behind many agent failures
When people talk about AI agents, they usually focus on the reasoning layer.
They discuss:
• Which model to use
• How many tools the agent should have
• Whether to use LangGraph, CrewAI, AutoGen, or another framework
• How to create planning loops
• How to give the agent memory
• How to reduce hallucinations
• How to make the agent autonomous
All of those decisions matter.
But in many real-world workflows, the biggest problem appears earlier.
The agent is connected to a database that was never designed to be queried by a language model.
Tables have names like t_ord_2. A status column holds the codes C, P and X. A relationship between two tables exists only in someone's head. Some columns contain internal notes that should never leave the company.
The agent has to solve the data problem and the business problem at the same time.
That creates unnecessary uncertainty.
Imagine an order-status workflow. The actual task may be simple:

  1. Read the customer's question.
  2. Find the order.
  3. Read its status.
  4. Check whether the customer accepted marketing consent.
  5. Reply, or escalate to a person. The difficult part is often not the reply. The difficult part is making sure the agent uses the right table, the right status value, the right join and only the columns it is supposed to read. Why giving the agent a database connection is fragile A common first approach is to give a general-purpose model a database connection, or a database MCP server, and let it write SQL. For example: Check whether Javier Perez accepted marketing consent and tell me his next reservation. This can work for prototypes. However, production workflows usually need stronger guarantees. The output changes shape. One execution may return: { "consent": true, "next_reservation": "2026-11-07 21:00" } Another may return: Yes, Javier Perez has accepted marketing consent. His next reservation is on Saturday at 9 PM. Both responses look reasonable to a human. They are not equivalent to an automation. A downstream node expects a boolean. If it receives a sentence, an object with different key names, or a value that is missing, the workflow can fail silently. Wrong answers don't raise errors. A query that spells a status value differently runs fine, matches no rows and returns an empty result. Nothing fails, and someone acts on it. A prompt is not a permission. Instructions hidden in a user message, or in text stored in a database field, can change what an agent does. Supabase's own documentation names prompt injection as the main risk of connecting an agent to your data, and says its MCP server is designed for development and testing. A rule written in a prompt can be overridden. A permission in the database can't. Context grows. If the agent sees the whole schema, with hundreds of tables, it receives far more than it needs. That increases: • Token usage • Latency • Cost • The chance of confusing similar tables • The amount of data the model could reach There is no record. When an answer looks wrong, you can't see which table the agent used or what query it ran. The more of your database the model can see, the more important it becomes to control exactly what it can use and what it returns. A better separation of responsibilities A more reliable architecture separates data access from agent reasoning. Instead of asking one general-purpose agent to understand the schema, write the query, protect the data and decide what to do, the workflow can be divided into layers: Question ↓ Description of your data ↓ Scoped access: only chosen tables and columns ↓ Read-only query ↓ Typed answer ↓ Business rules and agent reasoning ↓ Action or human review Each layer has a different job.
  6. Describing your data This layer says what each table is for and how tables relate to each other. For example, that orders.customer_id points to customers.id, and that cancelled orders stay in the orders table.
  7. Scoping access This layer decides which tables and columns can be used for a given purpose. Whatever is not included doesn't exist for the agent.
  8. Retrieving The query itself is a single read-only SELECT, restricted to the scope above. It is the least interesting part, and the part most people try to put first.
  9. Typing the answer The result becomes a value the next step can use: a boolean, a number, a text, a date, a list or an object.
  10. Validating The workflow checks what comes back before it acts: • Is the value present? • Is it the expected type? • Is it within a reasonable range? • Does it match what another source says? • Is the answer empty?
  11. Reasoning Only after the data has been retrieved in a usable form should the agent evaluate business logic. For example: Does this order exceed the customer's approval limit by more than 5%? That is a much narrower and more reliable task than asking the same agent to find the tables, write the query and make the decision from scratch. Introducing Claix Claix is a managed service that your agent calls to get answers from your database. It is a controlled database retrieval layer for AI agents. The core idea is simple: Ask a question. Get a typed answer from the data you allowed. Claix works with Postgres and Supabase. You set it up in four steps:
  12. Connect your database with a read-only role.
  13. Review your Data Map. Claix drafts a description of your tables and the relationships between them, and you correct it.
  14. Create routes. A route lists the tables and columns that can be used for one purpose, with a short guide of rules.
  15. Create an agent. It has a name, which becomes its URL, a business prompt, the routes it can use and a response mode. The read-only role is the foundation. It is a few lines of SQL: CREATE ROLE claix_agent WITH LOGIN PASSWORD 'use-a-secret-manager' NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOREPLICATION NOBYPASSRLS;

GRANT CONNECT ON DATABASE mydb TO claix_agent;
GRANT USAGE ON SCHEMA public TO claix_agent;
GRANT SELECT (id, name, marketing_consent) ON public.customers TO claix_agent;
Columns that aren't granted can't be read, whatever the agent asks.
The business prompt is plain text. For example:
You are the reservations assistant for a restaurant group.
Use the Bookings route for questions about reservations.
Use the Customers route for questions about consent.
Status C means confirmed, P means pending and X means cancelled.
The agent's endpoint becomes /agent/ followed by its name. No code to host, no framework to learn.
A simple API workflow
Claix is designed for backend integrations, scripts, automation platforms and agent tools.
You send one or several questions, each with the type of answer you expect:
const response = await fetch("https://claix.dev/agent/hr-assistant", {
method: "POST",
headers: {
"x-api-key": process.env.CLAIX_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
questions: [
{ question: "What is the phone number of the employee named Carlos?", format: "string" },
{ question: "Has the customer Javier Perez accepted marketing consent?", format: "boolean" },
{ question: "What is the job title of Marta?", format: "string" }
]
})
});

const [result] = await response.json();

console.log(result.ia_response);
The response keeps each question next to its answer, in the same order:
[
{
"user_ask": [
{ "question": "What is the phone number of the employee named Carlos?", "format": "string" },
{ "question": "Has the customer Javier Perez accepted marketing consent?", "format": "boolean" },
{ "question": "What is the job title of Marta?", "format": "string" }
],
"ia_response": [null, true, "Waiter"],
"log_id": "9ff38974-1392-4c1a-83e5-3b2f09bb43dc"
}
]
The important part is not just that the agent understood the question.
The important part is that the result is ready to be passed into another system.
For example:
Support ticket arrives
↓
Claix agent: order status and consent
↓
Branch on the typed values
↓
Reply, update the CRM or escalate
The same pattern works with n8n
A common n8n workflow might look like this:
Webhook or Trigger
↓
HTTP Request to the Claix agent
↓
IF node on a boolean or a number
↓
Slack / Email / CRM / Database
The HTTP Request node sends the questions to the agent's URL with your API key. Once the response comes back, the rest of the workflow works with typed values instead of trying to interpret a paragraph or a table of rows.
For a simple approval workflow, the next node might receive:
{
"order_status": "shipped",
"order_total": 1284,
"approval_limit": 1000
}
The agent doesn't need to rediscover the order total.
It can focus on the actual decision:
The order is above the approval limit. Request human approval.
That is a much better use of an agent.
A query result is not the same as an answer
A query returns rows. A business workflow usually needs something more specific:
Has this customer accepted marketing consent?
What is the status of this order?
When is this guest's next reservation?
The rows may contain several columns, several records and values that need interpreting. The workflow needs one value with a meaning.
That is why each Claix agent has a response mode:
Mode
What you get
Good for
Data
The result of the query as it comes out of your database
Code and other agents that process the result themselves
Answer
A reasoned answer in the type you asked for
Results that go straight to a person or to a workflow that needs a single value
In Answer mode, the rows returned by the query are sent to a model so it can write the answer. If your data is sensitive, take that into account when you choose the mode.
Handling missing information safely
One of the most important properties of a data workflow is how it handles missing data.
If the answer is not in the data the agent is allowed to use, the system should not invent a plausible value just to fill the gap.
In the response above, the first answer is null. The phone number is not in the data that agent can use, so Claix doesn't make one up.
A missing value is different from an inferred value.
That distinction matters in finance, legal workflows, compliance, procurement and customer operations.
A downstream workflow can then explicitly decide what to do:
if (result.ia_response[0] === null) {
return "manual_review";
}
This is more reliable than allowing a model to produce a number that looks right.
Routes and agents: deciding what each agent can see
Not every agent should see the same data.
A support bot, an internal assistant and a workflow check might all work on the same database, and each one needs a different slice of it.
That is the job of routes and agents:

Route
Agent
Decides
What data can be used, and how to read it
Who asks, and which routes it can use
Contains
Name, description, tables, columns and a guide
Name, description, business prompt, routes and response mode
A route might include only orders.status and orders.total. Another might include only a customer's name and consent. An agent can only use the routes attached to it.
This helps keep the main workflow focused and gives the data layer the responsibility for locating what is relevant.
Access is scoped per agent, not per end user: an agent can read everything its routes include, for every person who calls it. If each person must see only their own rows, enforce that in your database or in your application.
Traces: what happened, and why
Every response includes a log_id.
Use it to look up the request in Traces and see how it was answered: which route was used, the query that ran and what came back.
This matters when an answer looks wrong, because the useful question is not "why did the agent say that?" but one of these:
• Was the question understood?
• Was the right route chosen?
• Was the data missing?
• Was the description of a table unclear?
• Did the business rule calculate incorrectly?
You can also try your agents in the Sandbox with real questions before connecting them to your workflow.
Where this fits in an agent architecture
Claix is not intended to replace the entire agent stack.
It is better understood as a specialist tool, or a data agent.
A general-purpose orchestrator can decide:
This task needs the status of an order. Ask the data agent.
Claix returns a typed value.
The orchestrator then decides what to do next:
The order is shipped. Tell the customer.
This produces a clean division:
Orchestrator:
Planning, routing, decisions, tool selection

Claix:
Description of your data, scoped access, retrieval, typed answers

Business system:
CRM, ERP, database, spreadsheet, email, notifications
The main agent does not need to know your schema or write SQL.
It only needs to know when to use the capability and how to consume the result.
Every Claix agent is reachable over REST, MCP and A2A, so the same agent can serve a script, an MCP client or another agent.
A practical example: approving an invoice against the purchase order
Consider a procurement workflow.
The company receives a supplier invoice as a PDF. The purchase order lives in its database.
The workflow needs to determine whether the invoice should be approved.
A fragile implementation might give one agent the PDF and a database connection:
Read this invoice and tell me whether it matches the purchase order.
A more controlled implementation would look like this:
Step 1: Extract the invoice
The invoice arrives as a document, so a document extraction step turns it into fields. Claix Ingest does this, or you can use your own.
{
"invoice_number": "INV-2026-00456",
"supplier_name": "Acme Supplies Ltd",
"total_amount": 1284.5
}
Step 2: Ask the data agent for the purchase order
{
"questions": [
{ "question": "What is the approved total, in euros, of the purchase order for Acme Supplies Ltd?", "format": "integer" },
{ "question": "Is the supplier Acme Supplies Ltd currently active?", "format": "boolean" }
]
}
The answers come back typed:
{
"ia_response": [1200, true]
}
Step 3: Apply the business rule in code
difference = 1284.5 - 1200 = 84.5
difference_percentage = 84.5 / 1200 = 7.04%
This is arithmetic, so normal code does it.
Step 4: Decide
The invoice is 7.04% above the purchase order. Route to manual review.
The agent is still useful.
It simply is not forced to find the tables, write the query, protect the data and make the decision all at once.
Claix and the A2A direction
As agent systems become more modular, specialized capabilities need to be discoverable and callable by other agents.
A data agent can be useful in an Agent2Agent architecture because it offers a focused capability:
I can answer typed questions about this database, only from the tables I was given.
An orchestrator might delegate a task such as:
Is this customer's marketing consent recorded?
The data agent handles the database-specific work and returns a machine-readable result.
This is preferable to giving every agent direct access to the database and expecting each one to work out the schema.
Specialization can improve:
• Security boundaries
• Permission management
• Cost control
• Observability
• Reusability
• Testing
• Error handling
It also means a data-access capability can be reused across different orchestrators and applications.
Using Claix from Python
A basic request can be made with requests:
import requests

response = requests.post(
"https://claix.dev/agent/hr-assistant",
headers={"x-api-key": "YOUR_API_KEY"},
json={
"questions": [
{"question": "Has the customer Javier Perez accepted marketing consent?", "format": "boolean"},
{"question": "What is the job title of Marta?", "format": "string"},
]
},
)
response.raise_for_status()

result = response.json()[0]

consent, job_title = result["ia_response"]
print(consent, job_title)
print(result["log_id"])
The same pattern can be used from Node.js, n8n, Make, Zapier, an MCP client or a custom backend.
When to use Claix instead of building everything yourself
Building your own data access can be a good choice.
For a small number of fixed questions, a few well-written functions that your agent calls may be all you need.
A managed service becomes more interesting when:
• You have several agents on the same database, each needing different data.
• The questions vary and the rules change.
• You want typed answers rather than text or raw rows.
• You want a record of every request.
• You would rather not host and maintain query code.
• You want to describe your data in plain language and let non-engineers help.
• You want to choose your own model with your own key, or use a managed one.
It is not the right fit when:
• You need per-end-user row filtering inside the tool.
• You use a database other than Postgres or Supabase.
• You need dashboards, charts or governed metrics for analysts.
• You must self-host everything.
The choice depends on your requirements around privacy, latency, cost, control and operational ownership.
Designing reliable database automations
A few practical rules make a significant difference.
Start with a read-only role
Grant SELECT on the columns you want to share, and nothing else. It is the real barrier, and everything else builds on it.
Keep routes focused
Do not include every table if the workflow only needs three.
A smaller route is easier to understand, easier to test and easier to debug.
Separate retrieval from decisions
First get the facts.
Then apply business logic.
This makes it easier to understand whether a failure came from the data or from the rule itself.
Treat missing values explicitly
A missing value should remain missing.
Use null checks or a manual-review branch instead of silently guessing.
Keep the trace
Store the log_id next to important decisions.
This is particularly important for compliance, finance, contracts and healthcare workflows.
Add deterministic checks
Use normal code for things that normal code handles well:
• Date comparisons
• Arithmetic
• Thresholds
• Required fields
• Enum validation
• Duplicate detection
The agent can retrieve the data. It does not need to perform every deterministic operation.
Test with hostile questions
Use the Sandbox with real questions, and with questions that try to read what they shouldn't or to change the rules.
Add a human review path
Not every request should be processed automatically.
Empty answers, conflicting values and ambiguous questions should be routed for review.
A reliable workflow is not one that never asks a human for help.
It is one that knows when it should.
The bigger idea
The future of AI automation is probably not one giant agent that has every tool, sees every table and makes every decision.
A more practical pattern is composable specialization:
Input agent
↓
Data retrieval agent
↓
Validation layer
↓
Reasoning agent
↓
Business system
↓
Human approval when necessary
Each component has a clear responsibility.
The data layer describes your database, limits what can be read and returns typed answers.
The reasoning layer interprets those answers.
The workflow layer applies rules and triggers actions.
The human remains in control of high-impact decisions.
That architecture is easier to debug than a single autonomous loop because you can inspect each boundary.
When a workflow fails, you can ask:
• Was the question understood?
• Was the right route chosen?
• Was a field missing?
• Did validation reject the result?
• Did the business rule calculate incorrectly?
• Did the agent misunderstand an already-typed value?
That is much more useful than simply knowing that "the agent produced the wrong answer."
Final thoughts
AI models are becoming increasingly capable at reasoning over complex information.
But capability is not the same as reliability.
If your workflow depends on orders, customers, reservations, records or any other data that lives in a database, the quality of your data access will often matter more than the complexity of your agent framework.
Claix is built around a straightforward idea:
Data should stay in your database, and agents should reach only what you allow, through typed answers you can trace.
Once that happens, agents have less irrelevant information to process, workflows can rely on stable values, and developers can use AI for the parts that genuinely require reasoning.
Instead of asking an agent to do everything, give it a controlled way to read your data and a well-defined job.
That is where agent automation starts becoming useful in production.
You can try it without connecting your own database in the Sandbox, or read the docs at claix.dev.
Before you publish (not part of the post)
Item
What to check
Request body
The examples send a list called questions, with a question and a format for each. Your response echoes user_ask, so confirm the real field names
Endpoint
The examples call https://claix.dev/agent/{name} with the x-api-key header. Confirm it
Response
The examples use the exact format you shared: a list with user_ask, ia_response and log_id
Types
The examples use string, boolean and integer, which your reader prompt supports. The post says answers can be a boolean, a number, a text, a date, a list or an object. Your reader prompt has no decimal type yet, which is why the purchase order total is an integer
Response modes
The names Data and Answer are mine. Check what your interface calls them
Traces
The post says Traces show the route used, the query that ran and the result. Confirm it
MCP and A2A
The post says every agent is reachable over REST, MCP and A2A. Confirm that is true for each agent
Validation
The post doesn't claim queries are validated before they run. Add it only if it is implemented
Supabase link
The sentence about prompt injection and development use comes from Supabase's MCP documentation: https://supabase.com/docs/guides/getting-started/mcp
Claix Ingest
Step 1 of the invoice example mentions it as one option for extracting the invoice
Canonical URL
If you also publish the post on your own blog, add canonical_url to the front matter to avoid duplicate content

Top comments (3)

Collapse
 
jo-do profile image
Jo Do •

"The model receives the wrong input" covers more ground than it looks like. Document prep is unglamorous because it's upstream of everything the demo shows - nobody screenshots a cleaned table. And the failure mode is quiet: the agent doesn't error on a mangled number, it reasons fluently from it. Preserving numbers and dates correctly is the least celebrated, most load-bearing step in the whole pipeline.

Collapse
 
justinwilsonbi profile image
Justin Wilson •

The missing production contract is retention governance. Define deletion defaults audit trails and tenant isolation before documents become queryable memory.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.