Back to Blog

MCP or RAG: Which One Does Your AI Feature Actually Need?

MCP connects a model to live data. RAG compresses a corpus that will not fit in a prompt. They solve different problems, and picking the wrong one produces confident wrong answers.

Posted by

Hand-drawn illustrated header reading MCP or RAG, two answers to two very different questions

Both get described the same way in pitch decks: “we give the AI access to your data”. That sentence hides the only distinction that matters. MCP is a connection. RAG is a compression trick. One is about reaching your data at all, the other is about what to do when there is too much of it to fit in a prompt.

Confusing them is expensive in a specific way. It does not produce an error, a crash, or an empty screen. It produces a confident, well-written, wrong answer, arrived at by reading ten rows out of four hundred and never mentioning that it did.

Lighthouse ships an MCP server with both kinds of tool on it: some that return every row, some that retrieve a handful. Getting there involved building the retrieval half, pulling it back out, and then shipping a narrower version in a different order. This is what I learned about which question each one is for.

Table of contents

What MCP is, in one paragraph

MCP (Model Context Protocol) is an open standard for letting an AI client talk to an outside system. The service publishes a set of named tools, the client discovers them automatically, and the model decides which to call. It is a USB port for AI apps: the app has the socket, the service provides the plug, and neither side writes custom glue.

What it solves is access. Before MCP your options were pasting an export into the chat, which is stale the moment anyone new signs up, or building a custom integration, which is a real project. MCP is the third option, and the data it returns is live. A longer version of this is in what MCP is in plain English.

What RAG is, in one paragraph

RAG (retrieval-augmented generation) turns your text into vectors, which are lists of numbers positioned so that passages meaning similar things sit near each other. At question time it converts the question into a vector too, grabs the handful of chunks closest to it, and pastes only those into the prompt. The model answers from that handful.

What it solves is size. RAG exists because context windows are finite and corpora are not. Every design decision in it, the chunking, the top-k cutoff, the similarity threshold, is a strategy for throwing away most of your data before the model sees any of it. That is the feature. It is also the whole risk.

The useful side effect is that similarity is semantic rather than lexical. “Switching from a spreadsheet” finds someone who wrote “we track it all in Excel and it is falling apart”, with no shared keyword. That is genuinely something no amount of SQL will do for you.

The line between them: reading versus finding

Here is the test that settles it, and it is about the question rather than the technology. Does the answer depend on every row, or on a handful of them?

Two-panel comparison of questions that need every row versus questions that need a few matching rows

“What are the three most common complaints” is a left-hand question. It is a claim about the distribution of four hundred answers, and you cannot make it from ten. “Has anyone reported this bug before” is a right-hand question. It wants two or three rows and every other row is noise.

Note that MCP appears on the left and retrieval on the right, but they are not really opposites. MCP is the pipe; retrieval is one thing you might put through it. A perfectly reasonable architecture is an MCP server whose tools happen to be search tools, and that is exactly what Lighthouse ended up with. The mistake is not using both. The mistake is answering a left-hand question with right-hand machinery.

The counting trap

This is the part worth internalising, because it is silent.

Ask a retrieval-backed system “how many people complained about price?” and it does not refuse. It retrieves the eight chunks most similar to the question, counts the ones that mention price, and answers “six people”. The number is real. It is also drawn from eight rows out of four hundred, and nothing in the output tells you that, because the retrieval step is invisible by the time the sentence gets written.

A wrong answer that looks wrong is a bug you fix on Tuesday. A wrong answer that looks right is a decision you make in March and regret in July. Retrieval systems specialise in the second kind, which is why “can this question be answered from a sample?” is worth asking before you wire anything up.

The mitigation is not clever prompting. It is keeping the aggregate questions off the retrieval path entirely. Counting belongs to SQL or to a tool that returns every row; search gets the find-me-one questions and nothing else. If you build this, say so in the tool description too, because the model picks the tool and it will reach for search unless told when not to.

Why most indie products need neither

RAG became standard practice when a good context window was 4,000 tokens. A 400-signup waitlist with three survey answers each is somewhere around 30,000 tokens of text. In 2023 that was three books' worth of problem. Today you can hand the entire thing to a model in one call, for a few cents, and get an answer that read every word.

So the honest default for an indie SaaS is: read everything. No chunking strategy, no embedding pipeline, no vector index to keep in sync, no backfill job, and no sampling risk. When your whole dataset fits in the prompt, RAG is a way of deliberately seeing less of it.

This is also why the first question I would ask any product claiming a RAG feature is not “which vector database?” but “how big is the corpus?”. Under a few thousand records the interesting answer is usually that it did not need to be a retrieval problem.

The three cases where retrieval earns its place

  1. The corpus genuinely outgrew the window. Thousands of support tickets, a documentation site, years of transcripts. At that size you have no choice, and the sampling risk becomes a cost you manage rather than one you introduced for nothing.
  2. The question is find-one by meaning. “Has this been reported before” and “is this a duplicate” are retrieval questions at any size. Here search is not a workaround for context limits, it is the correct algorithm.
  3. You need per-record grounding. Answers that must cite the specific rows they came from benefit from retrieval, because the citation falls out of the mechanism instead of being something you ask the model to produce and then hope is true.

Case two is the one most indie products actually have, and it shows up on the feedback side long before the waitlist side. Feedback accumulates duplicates in a way signups do not: the same bug arrives six times in six different wordings.

What I shipped, and in what order

The first attempt was the full RAG build: embeddings over signup answers and feedback submissions, a vector column and index in Postgres, extra MCP tools so a model could search by meaning, a search box in the dashboard, and a background job keeping the vectors current. It worked. It was maybe two hundred lines of real logic.

I pulled it back out before any customer saw it. Not because it broke, but because on my own data every question I actually wanted to ask was a left-hand question, and the existing tools answered those by reading every row. The search box was a second, worse way to ask things I could already ask, plus an embedding pipeline to maintain forever.

What went back in went back in a different order, and the order is the real lesson. Full-text search over feedback first: a generated tsvector column and a GIN index, which is a few lines of SQL, stays correct on every insert and update without a trigger, has nothing to backfill, and costs nothing to run. That covers the queries people actually type into a feedback board, which are error strings, feature names, and competitor names.

Vectors came second, on top of the full-text half rather than instead of it. So the feedback search tool is hybrid now: the full-text side catches the exact error string, the semantic side catches the paraphrase that shares no words with it. Signup search is semantic, because signups are paraphrase territory. And the counting questions never touch either one, because the tools that return every row are still there and the tool descriptions point the model at them.

The generalisable version: build the cheap half first. Full-text pays for itself the day you ship it and never goes stale. Embeddings are the upgrade you add once you can point at real queries where the wording varies more than the meaning does. Doing it in the other order, which is what I did the first time, means maintaining a pipeline before you know what it is for.

The hybrid that beats both

There is a second kind of hybrid worth knowing about, for when you have a large corpus and you want theme analysis out of it. Almost nobody builds this and it is better than either pure option.

  1. Embed everything and cluster it. Not to retrieve, but to group. Clustering touches every record, so the group sizes are real counts rather than estimates.
  2. Let the model name the clusters. Feed it a sample from each group and ask what these have in common. Naming is what language models are excellent at.
  3. Report the size from the maths, the label from the model. You get “41 answers about onboarding friction” where the 41 is arithmetic and the phrase is language.

That split is the general principle behind everything above. Ask each part of the stack for the thing it cannot get wrong. Databases count. Models describe. Trouble starts when you ask a model to count or a database to understand.

Frequently asked questions

Are MCP and RAG alternatives?

No, and the framing is the most common mistake. MCP is a transport, RAG is a retrieval strategy. You can serve retrieval results through MCP tools, and often should. They only look like alternatives because both get pitched as “giving AI your data”.

How big does a corpus need to be before RAG helps?

Rough rule: if the text fits comfortably in a single prompt, reading all of it beats retrieving from it. That is somewhere in the low thousands of short records with today's windows. Below it, retrieval is added risk and added machinery for no gain.

What should I try before RAG?

Aggregate in SQL, which makes counts exact and free. Add filters so the model can narrow before it reads. Then full-text search, which is close to free on Postgres and handles more find-one queries than people expect. If those still leave a real question unanswered, that question is your RAG spec.

Does Lighthouse use RAG?

Yes, in the narrow place it earns. Feedback search is hybrid, full-text and semantic together, and signup search is semantic. But the tools that return every row are what answer counting questions, and AI insights still reads every survey answer in one pass rather than a retrieved sample. Retrieval is the finding half, not the summarising half. The feedback side is walked through in ask Claude what your users are complaining about.

What is the one-line version?

Retrieval finds, it does not count. Wire it up for the questions where finding is the point, and keep it away from every question whose answer is a number. If you want the whole loop this sits inside, that is in AI orchestration before and after launch.

Join Discord