Skip to main content

Secure AI Database Access: Exograph now supports MCP

· 10 min read
Ramnivas Laddad
Co-founder @ Exograph

Exograph now enables you to create secure, flexible, and performant MCP servers for your Postgres database. Define your domain model, add access control rules, deploy to the cloud, and you're off to the races!

MCP provides AI applications with custom data via a standard protocol. This is good because it gives LLMs the ability to get relevant context to provide better responses. However, it presents engineering challenges to ensure that even if LLMs inadvertently send wrong queries, the server never returns data that the user isn't authorized to see.

One of the common sources of information is databases, and this is where things get complex. LLMs perform best with rich, cross-table data that requires sophisticated SQL queries. But the server must ensure it never returns data the user isn't authorized to see. Writing SQL queries to consider both these aspects is challenging. These queries are hard to write, harder to optimize, and difficult to test across different authentication scenarios. You might be tempted to let the LLM form the query and use Row-Level Security (RLS) to deal with access control, but it is quite difficult to implement correctly.

Exograph's MCP support lets you focus on your domain model and access control, rather than testing ad-hoc queries and setting up custom plumbing. It acts as an intelligent proxy that offers LLMs access to comprehensive data in fewer roundtrips while ensuring that LLMs never see data that they aren't meant to.

Exograph MCP in a Nutshell

Consider a financial advisory company with the following Exograph model (for a more comprehensive version, please see example implementation):

context AuthContext {
@jwt("sub") id: Uuid
@jwt role: String
}

@postgres
module FinanceService {
@access(query=AuthContext.id == self.id || AuthContext.id == self.advisor.id || AuthContext.role == "admin")
type Customer {
@pk id: Uuid = uuidGenerateV4()
name: String
advisor: Advisor?
accounts: Set<Account>
}

@access(query=AuthContext.id == self.id || AuthContext.role == "admin")
type Advisor {
@pk id: Uuid = uuidGenerateV4()
name: String
customers: Set<Customer>
}

@access(query=AuthContext.id == self.customer.id || AuthContext.id == self.customer.advisor.id || AuthContext.role == "admin")
type Account {
@pk id: Uuid = uuidGenerateV4()
@unique accountNumber: String
balance: Decimal = "0.00"
customer: Customer
}
}

Here we have three entities: Advisor, Customer, Account whose fields describe the data they contain and the relationships with others.

The @access directive specifies the access control rules for each entity. The rules are specified as a boolean expression that must evaluate to true for the user to have access to the entity and express the following:

  • If the accessing user is a customer, they can view their own information and their own accounts.
  • If the accessing user is an advisor, they can view their own information, customers they advise, and accounts belonging to their customers.
  • If the accessing user is an admin, they can view all data.

We specified rules only for queries. Following the "secure by default" principle, Exograph will deny any mutations. You will have to explicitly add access rules for mutations if you want to allow them.

Now LLMs can form tools with queries to get, for example, "all customers" without considering access control rules. Exograph will return customers that the user has access to based on the defined access control rules. For example, a financial advisor will only see customers they advise. This removes any fear that LLMs will access data they shouldn't.

Here is a quick video that shows the process of starting with an existing database, creating an Exograph model, and then using Exograph MCP to enable Claude Desktop to query the database.

From Database to MCP server

Exograph makes it easy to create an MCP server for either a new application or an existing database. Let's do this with an existing database, using the exo schema import command, which introspects the database and creates an Exograph project to match.

DATABASE_URL=postgres://... exo new finance-service \
--from-database \
--query-access=true
cd finance-service
exo dev

Starting server in development mode...
Watching the current directory for changes...

Verifying new model...
Starting server...
Started server on localhost:9876 in 126.07 ms
- GraphQL endpoint hosted at:
http://localhost:9876/graphql
- MCP endpoint hosted at:
http://localhost:9876/mcp
- Playground hosted at:
http://localhost:9876/playground

Then configure Claude Desktop (or any other MCP client) to use this server.

Next you will replace query=true with appropriate access control rules. For example, for Customer, you could add query=self.id == AuthContext.id.

How does it work?

Exograph MCP uses GraphQL as the underlying protocol. Therefore, LLMs have the ability to query all related data in one go. For example, an advisor can get all customers with their account balances in a single request. With a typical home-grown MCP server, this would likely require multiple turns and requires relying on LLMs to connect results from these multiple tool invocations, which becomes another source of hallucination.

By default, Exograph MCP offers tools sufficient to get started with an MCP client such as Claude Desktop. These tools declare the GraphQL schema so that LLMs can form queries. LLMs use this schema to form queries with a simple, predictable pattern: customer(id: "...") or account(id: "..."). The server then translates these GraphQL queries into optimized SQL while enforcing access control rules.

For agentic workflows, you can configure Exograph through customizable profiles (an idea rooted in bounded context from domain-driven design). Instead of overwhelming agents with every possible domain model, you can create specific tools. For example, you can create a profile for customer management, advisors, and financial operations. This allows agents to work more effectively by matching tools to their current context and objectives.

Exograph MCP as a proxy

Authentication tokens determine what data the MCP server returns. The server uses these tokens to enforce access control rules, ensuring users only see data they're authorized to access.

Comparing with Alternatives

Before building Exograph MCP, we evaluated existing approaches. Each one looked promising on paper but broke down in practice. We hear these common refrains:

"But what about existing Postgres MCP servers?"

There are several ready-made MCP servers: MCP Toolbox for Databases lets you bind tools to a specific query, while the official (now archived) executes any SQL the LLM throws at it. So why not just use those?

None of these servers focus on access control. The MCP Toolbox for Databases puts the onus on you to write secure SQL queries for every tool. For anything beyond simple queries, this becomes a difficult and error-prone approach. Furthermore, testing those queries requires complex setup and often gets neglected.

The official Postgres MCP server does not provide any control over the SQL executed: it simply passes SQL queries to the database and returns the result. This has an additional problem: It relies on the LLMs to form complex queries to fetch core data along with relevant context resulting in complex JOINs that often fail at runtime or perform poorly. Furthermore, in an AI application using an MCP server, getting consent from the user is often meaningless since a user without SQL expertise has no ability to assess the complex SQL query for its relevance or correctness.

In Exograph, access control is specified alongside the domain model and it is Exograph's responsibility to form SQL queries that respect the access control rules. LLMs can throw any queries and Exograph filters data according to the access control rules. And the generated queries look much simpler: they just specify the data shape sought by the LLMs, so the user is in a much better position to give informed consent.

"But what about Row-Level Security (RLS)?"

RLS sounds perfect for the problem with Postgres MCP servers, right? Define policies in Postgres and let the database handle access control.

Unfortunately, RLS is notoriously difficult to implement correctly. As the Neon team discussed: "Writing RLS policies in raw SQL is just a difficult experience with lots of pitfalls...It's extremely easy to forget to have all the checks, and that can lead to data leaks...Testing RLS policies is hard". Not only will you have a hard time implementing it, you will also have difficulty knowing that you got it right.

Compare and Contrast

Check the Exograph implementation that implements the same domain model and access control. It is much shorter, easier to read, and includes integration tests to verify that it works as expected.

And then even if you do implement it heroically, you have to watch for performance gotchas. Since it is the LLM that creates queries, you are at its mercy.

Best of Both Worlds

In a way, Exograph's approach combines these two approaches. Instead of letting the LLM speak SQL, it uses GraphQL and instead of RLS, it makes access control a part of the model.

"But why not build a custom MCP server?"

You already build custom backends, so why not just create a custom MCP server with exactly the tools you need?

What starts as "just expose a few database operations" quickly becomes implementing security, query optimization, schema management, connection pooling, error handling, and testing infrastructure. You're basically building a database proxy from scratch, instead of focusing on unique challenges in building an AI application.

With Exograph, all this comes out of the box, so you can focus on building your application instead of plumbing.

"But what about using my existing API?"

If you already have a backend (typically to support a web or mobile application), why not just create an MCP layer on top of it? In fact, if you have an Open API specification, there are many MCP servers that do this for you. They certainly get you started quickly.

APIs designed for UIs don't work well for AI. UIs make predictable requests for specific data. AI applications need to explore relationships and gather context in unpredictable ways, often requiring multiple related pieces of data in a single request to avoid excessive back-and-forth.

Exograph MCP servers take the role of combining relevant pieces of data in a deterministic fashion instead of hoping for LLMs to do it right.

Try it out

Follow our guide to see how easy it is to create AI applications. You can also try it with your own databases by importing it. Join our Discord to share feedback and get support.

Share: