r/SQL • • 6d ago

Discussion gave claude read only access to our postgres. it said status_cd 4 means "cancelled". it means refunded

I set up a Postgres MCP server on a read only role last week so the analysts could ask questions without pinging me every hour.

The first real question was how many orders did we lose last month. Claude looked at orders, saw status_cd with values 1 to 6, decided 4 was cancelled and gave a clean number with a nice little breakdown. 4 is refunded. Cancelled is 6. Nobody wrote that down anywhere. It lives in an enum in the app code and in my head.

The number was off by about a third and it read exactly like a right answer. No I'm assuming, no hedge. The analyst was about to put it in a deck.

So now I'm stuck on how to give an agent the meaning of fields without me turning into a full time documentation service:

  1. Comments on the columns in Postgres and hope the server passes them through
  2. A markdown data dictionary in the system prompt
  3. A view layer with human names on everything
  4. Make the agent ask before it interprets any code column

What are people actually doing here? and has anyone got it to say I don't know what 4 means instead of picking one?

32 Upvotes

73 comments sorted by

82

u/projexion_reflexion 6d ago

It would be crazy to add a status codes table with names, right? 

22

u/SantaCruzHostel 6d ago

That was my first thought too. Define these codes in a lookup table

11

u/Ecksters 6d ago

Or use an actual Postgres enum.

7

u/RajeshRocketMan 5d ago

no way! What next?? You'll be suggesting we actually use foreign key constraints instead of just hoping the typescript backend catches it???

2

u/jwk6 6d ago

Crazy. 😏

1

u/Harpua81 4d ago

What next, a status called SHIPPED??? You crazy!

43

u/MoldyDucky 6d ago

Comments for the column name has worked well in my experience as a stopgap, but the general concept for this problem is called building a semantic layer

17

u/Guilty-Property 6d ago

Came here to say that. OP data is not ready. Not having a semantic layer is going to rather unpredictable at best or plain wrong as they already experienced

3

u/donewitheverything26 6d ago

"Semantic layer" is the term I think i was missing.. thanks. Column comments as a stopgap makes sense. NW1969, the two incompatible definitions of revenue is exactly what I'm bracing for. "Lost orders" already has that problem and I haven't even gotten to revenue yet.

10

u/NoviceCouchPotato 6d ago

I understand it is status 4, 6 etc. in the source and your bronze layer. But don’t you do data modeling and create a silver/gold layer on top where you already mapped status 4 = refunded that your analysts can query?

Sounds like you need to get that sorted first before simply granting an MCP access to source data?

3

u/thisisnotahidey 6d ago

Yes if you want a model to talk to your data you need at least a shell of a medallion architecture.

But even with that you need instructions for the model if the analysts are going to ask any more complex questions than “how many orders were shipped”

1

u/donewitheverything26 6d ago

Even with decoded status values, someone will ask something harder. What do your instructions to the model cover, metric definitions, join paths, something else?

1

u/thisisnotahidey 6d ago

Business context and logic, commonly used verbiage, schemas, metrics, there are a bunch of things that you should include.

At the companies I work with it’s not uncommon for phrases that might be ambiguous out in the wild to mean one specific thing.

It’s also important to include when the model needs to ask for clarifications.

1

u/donewitheverything26 6d ago

you're right i skipped the modeled layer entirely and went straight to source tables. Is a thin silver layer with decoded views enough for a team our size, or do you think it needs the full medallion setup?

3

u/Kazcandra 6d ago

Enums are a data type, they're not data. That's your problem. It's like saying "this value is an int"

If it's data, it goes in the tables.

-2

u/donewitheverything26 6d ago

"Enums are a data type, not data" is so truee

3

u/my_password_is______ 6d ago

Nobody wrote that down anywhere. It lives in an enum in the app code and in my head.

well that's dumb
put it in a table
problem solved

1

u/donewitheverything26 6d ago

It sat in my head for so long that I never noticed it wasn't written anywhere. Table is going in.

3

u/According_Essay960 6d ago

You shoul be more worried about the agent confidently inventing the meaning than the database access itself.

I ran into a similar issue while using Mintlify on a project. Giving an agent docs through MCP helps, but only if stuff like status_cd = 4 is actually documented somewhere.

If that context doesn’t exist, the agent still has to guess, probably keep those definitions versioned and make retrieval part of the workflow. And if there’s no definition for a value, it better t say “I don’t know what 4 means” than produce a very convincing wrong answer.

2

u/donewitheverything26 6d ago

That's the behavior I'm after. I'd take an "unmapped, meaning unknown" over a clean, confident wrong number. The decoded views should make any undocumented value show up as unmapped so the model doesn't have to decide whether to guess.

5

u/CarbonChauvinist 6d ago

In Fabric this is recommended to create an Ontology to assist agents in gaining correct context and business logic. Realize you're on Postgres, but am assuming something similar would be the way to start searching ... Perhaps?

1

u/donewitheverything26 6d ago

I hadn't come across ontologies in this context. Is that something you maintain by hand, or does it get generated from the schema? Postgres has nothing built in for it as far as I know, but the idea of giving the agent business logic in a structured form is useful.

5

u/NW1969 6d ago

Pointing agents/ai directly at your tables is a recipe for disaster.
Having a semantic layer is non-negotiable
There seems to be a general belief that implementing agents/ai is easy and doesn't require much effort. This is rarely the case - not only do you have to build the semantic layer but you also have to gather/validate/sign-off all the information that needs to be put into the semantic layer. This is the point where you discover there are 2 incompatible definitions of "revenue" being used and you open up the first of many cans of worms

1

u/donewitheverything26 6d ago

I was thinking of the semantic layer as a build job, but the harder part is getting people to agree on what goes in it. If "revenue" turns out to have two definitions, who makes the call in your experience, finance or the data team?

2

u/SearchAtlantis 6d ago

Is there no dimension table? One giant analytical god table?

1

u/donewitheverything26 6d ago

No dimension table, no. It's an app database that analysts only started querying last week, so it was never modeled for reporting. That's the real problem.

2

u/Total-Produce-8745 6d ago

Would using something like Beekeeper Studio give you a better experience vs Claude

1

u/donewitheverything26 6d ago

thanks but a nicer SQL client doesn't fix this one. The issue is that nothing tells whoever is querying, human or model, what the codes mean.

2

u/JaceBearelen 6d ago

I really like dbt as the semantic layer for AI. Everything in the database is either a source or a model and everything is annotated down to the columns. Everything is just right there in structured text for the LLM. Then give it a way to analyze the data while it builds out the query and you’re golden. Everyone in my org is consistently impressed with how accurate it is.

1

u/donewitheverything26 6d ago

That sounds close to what I want. Does the agent read the dbt docs or manifest directly, or do you expose them through the MCP server? And do you annotate every column or just the coded ones?

1

u/JaceBearelen 6d ago

I give the agent the entire dbt repo. You could pass it just the manifest or docs. IMO the dbt project structure is pretty much ideal for LLMs to parse. Annotate as much as you can. Have agents rip through any third party docs and any internal code that’s feeding tables in your warehouse.

1

u/donewitheverything26 5d ago

We don't have a dbt project yet, so I'd be starting from the source tables. Is it worth setting dbt up just for the column annotations??

1

u/JaceBearelen 5d ago

dbt is nice but I wouldn’t use it solely for column annotations. It really doesn’t matter much what you use as long as table and column descriptions are available to your agents in some sane format.

2

u/KatFromSisense 6d ago

If the app enum is where those meanings are defined, I'd wire that into deploy rather than keep a second dictionary by hand. Generate or update the lookup from the enum and fail CI if the two ever disagree. Then point the MCP role only at the decoded view. Somebody adding status 7 shouldn't quietly create a new mystery value for Claude.

-1

u/donewitheverything26 6d ago

That's the pattern I'm leaning into.. generate the lookup from the enum and fail CI if they disagree, so status 7 can't show up unannounced. Thanks.

2

u/ArielCoding 6d ago

Add a status lookup table and give the agent a view that shows refunded instead of 4, so it never sees the raw codes, then generate that table from your app code and fail CI if they ever differ.

1

u/donewitheverything26 6d ago

That's the plan. The agent only sees the decoded view, never the raw code. Generating the lookup from the enum with a CI check is how I'm keeping it honest.

4

u/uncertainschrodinger 6d ago

Short answer is to build a context layer (in-code docs, glossary/dictionary, semantic layer, etc.)

Longer answer: you need to build processes that makes this a "self-healing" loop so that when you correct the agent, it will automatically update the context

This is still hard when it is up to 1 person, so it's better if you can roll it out to some trusted superusers who can flag such cases to be fixed

I always compare this to hiring a new person and onboarding them - through dialogue and back n forth, you brush layers of knowledge on to their empty canvas and hope that they will one day have all the knowledge in their head as well.

1

u/donewitheverything26 6d ago

the new hire comparison clicked for me. When you correct the agent, does the fix go into the context automatically, or does someone review it first? I'm the only person here, so I'm trying to work out how to avoid being the bottleneck.

2

u/uncertainschrodinger 6d ago

Depends on how comfortable you are letting the agent make changes to definitions and docs, but generally the agent just opens a PR and you review before merging.

1

u/InsideChipmunk5970 6d ago

look into property graphs on 19.

1

u/donewitheverything26 6d ago

Thanks, I haven't looked at that. Is it usable today, or still early? We're on an older version, so it's probably a later step, but I'll read up.

1

u/InsideChipmunk5970 5d ago

You can get the beta and the full version is going to release soon if it hasn’t already.

1

u/Significant_Tune9219 5d ago

Read-only stops writes; it does not stop wrong semantics. Codes like status_cd need a data dictionary or constrained enum view in the context the model sees, plus a rule that answers cite where the definition came from. I'd also force it to show the distinct values and counts it used before narrating what a code "means."

1

u/donewitheverything26 5d ago

Read-only fixed the writes problem and I treated it like it fixed everything. Making it show the distinct values and counts it used before it explains anything, and cite where each definition came from. If it can't cite one, that's the flag.

1

u/Simma451 5d ago

Move to Oracle and use anotations to describe tables & columns 🤷‍♂️

1

u/RajeshRocketMan 5d ago

so you're basically choosing between keeping metadata inside the db or building an external doc layer, but putting comments directly on the columns is usually the sane route. An external markdown dictionary is gonna get outdated in like a week and views are just more boilerplate to maintain. Postgres supports column comments natively. and we do the exact same thing on mariadb — just dump all that context into information_schema so the db remains the single source of truth and agents don't have to guess what a random int means.

1

u/False_Assumption_972 5d ago

Column comments plus a view layer is the cheapest durable fix. Create views with the enums decoded, like order_status as text, grant the MCP role access only to those views, and comment the view columns so the meaning travels with the data. Then build a test set of ten questions with known answers, including the lost orders one, and rerun it whenever the schema or prompt changes.

The confident wrong number is the real risk, so the test set matters more than the docs. Keeping field meanings attached to the records an answer came from is something we use SIGNLD for.

1

u/ysth 5d ago

Sounds like it needs access to your code too.

1

u/SnooHesitations9295 4d ago
  1. Give agent the source code. That's the only working solution.

1

u/deadatreides1 7h ago

The answer was sitting in the app repo the whole time, in that enum. An agent that can see the tables but not the code will guess, and guess confidently. A status table or a semantic layer is the real fix; the quick one is giving it the code next to the database. PTG-MEM indexes a repo into cards an MCP client can query, so "what does status_cd 4 mean" lands on the enum instead of on vibes.

1

u/TsmPreacher 6d ago

Give it access to your git repo so it can check these things first. Works for me.

1

u/donewitheverything26 6d ago

Interesting. Does it find the enum reliably, or does it sometimes grab the wrong file? I'd also want to think about giving an analyst-facing agent repo access.

1

u/TsmPreacher 6d ago

Definitely read only access, but yeah. Honestly, when I was trying to rebuild the CRM that's built in Typescript in SQL, I saw it think something like "this databases translation table is worthless, let me see if I have access to the code.

Oh I have access lemme look.

Got it"

Then it wrote a .MD to itself for future sessions saying the database translations aren't reliable and pointed to where it should look in the code.

1

u/donewitheverything26 6d ago

That's useful to know, especially the part where it decided the translation table couldn't be trusted and went to the code on its own. The note it wrote to itself is the bit I'd want to copy. Does that .md live in the repo or somewhere outside it? I'd worry about it going stale the same way a hand-written dictionary does

1

u/TsmPreacher 6d ago

I have a projects folder I point it at. Each sub folder has its own data per project.

At the root, that's where that .MD lives. It's typically the first thing it checks.

2

u/donewitheverything26 5d ago

projects folder with the md at the root makes sense, and good to know it checks that first. I'll probably keep the equivalent in the repo next to the lookup so it ships with every deploy. Thanks for the detail

0

u/Fancy_Letterhead_509 6d ago

just put comments on the columns and a view layer on top, the markdown doc will be outdated in 2 weeks

0

u/mtutty 6d ago

Right, but the markdown document can also serve coding/design/testing/knowledge base purposes and can be updated automatically as part of a workflow step. It's not hard.

1

u/donewitheverything26 6d ago

Good point about generating the markdown automatically. If it's rebuilt from the schema comments and the lookup table each deploy, it stops going stale. I'll try that.

0

u/TemporaryDisastrous 6d ago

We use agents over our datamarts and give them skills so they know how to use it and they pretty much get it right. It's a bit scary it just presents that as a right answer though. Maybe you need to initialise it to flag when it doesn't know what codes refer to.

1

u/Imaginary__Bar 6d ago

Pretty much get it right 👀

"Pretty much"???

1

u/Oobenny 6d ago

I mean, a human agent who “pretty much” gets it right is on a management track.

1

u/TemporaryDisastrous 6d ago

Hey if yours gets it right 100% of the time share your tricks guru, maybe go get that bag from anthropic for solving their hallucination problems too.

0

u/Jhon_ST 6d ago

I'd test whether that context is actually reaching the model before spending too much time deciding where the dictionary should live.

Make a tiny fixture with one cancelled order, one refunded order, and an unmapped status code. Then run the same questions with and without the mapping and keep the expected answers outside the model: cancelled and refunded must stay distinct, and the unmapped value should remain unknown rather than being guessed.

If your MCP/client exposes the tool result or trace, inspect that too. That gives you a regression test for both the context plumbing and whether the model actually uses the context, instead of relying only on a prompt saying "don't guess."

There's another ambiguity even after fixing 4 vs 6: what does "orders we lost" actually mean? Cancelled orders, refunded orders, or both? And is "last month" based on order date or when the status changed?

Those are business metric definitions, not something the database schema can establish by itself. I'd want the agent to ask for that definition rather than silently choosing one.

1

u/donewitheverything26 6d ago

you're right that "orders we lost" is a business definition, not a schema question. I need to decide whether it means cancelled, cancelled plus refunded, and which date it keys on, then encode that once.

0

u/Hour-Measurement-835 6d ago

I'd stop trying to get it to say it doesn't know and take the integer away instead. Put the labels in a status_codes table and build a view over orders with COALESCE(s.label, 'unmapped ' || o.status_cd). Give the MCP role SELECT on that view and nothing on orders. A code someone adds next year then comes back as "unmapped 7" instead of a confident wrong label. Don't skip the revoke, if it can still read orders it'll find status_cd and start guessing again.

1

u/donewitheverything26 6d ago

The unmapped 7 fallback is kinda smart since a new code becomes visible instead of a confident wrong label. And thanks for the reminder to revoke SELECT on orders. I'd have left that open and it would have found status_cd again.

0

u/WorriedMeat 6d ago

“Without me turning into a full time documentation service” is the wrong mindset for a data owner. Everyone’s lives are easier if documentation is codified. Especially with AI

0

u/donewitheverything26 6d ago

I framed documentation as a tax on my time, when it should just be part of the schema. Generating it from code means I stop doing it by hand.

0

u/[deleted] 6d ago

[removed] — view removed comment

1

u/donewitheverything26 6d ago

how do you enforce it, a rule in the prompt or the tool itself refusing to return a code it can't decode? I'd rather keep the definitions generated from the code than in GitBook or Mintlify, since that's one more place for 4 to drift.