Starting from an existing database
We'll redo the same tutorial, but this time starting from an existing database to show how you can do the same with your own database.
Setting up a new database
Let's use ddl.sql
and seed.sql
from the financial-advisor-mcp
directory to create a new database (which is the same as the one we used in the existing Exograph model tutorial).
createdb financial-advisor-mcp
psql -U postgres -d financial-advisor-mcp -f ddl.sql
psql -U postgres -d financial-advisor-mcp -f seed.sql
Creating a new Exograph model from the database
Use the exo new
command to create a new Exograph model from the database:
DATABASE_URL=postgresql://localhost:5432/financial-advisor-mcp exo new financial-advisor-mcp-scratch --query-access true
This creates a new Exograph model in the financial-advisor-mcp-scratch
directory. The --query-access true
flag sets up access control rules so that anyone can query any data (but no one can mutate it).
Open the financial-advisor-mcp-scratch/index.exo
file. You should see the Exograph model for the financial advisor domain (and all access control annotations set to @access(query=true, mutate=false)
).
Starting the MCP server
Before starting the MCP server, create a .env.dev.local
file in the financial-advisor-mcp-scratch
directory:
echo "DATABASE_URL=postgresql://localhost:5432/financial-advisor-mcp" > financial-advisor-mcp-scratch/.env.dev.local
Use the exo dev
command to start the MCP server:
exo dev
Update the Claude Desktop configuration file to use the MCP server:
{
"mcpServers": {
"finance-advisory-local": {
"command": "<your home directory>/.exograph/bin/exo-mcp-bridge",
"args": [
"--endpoint",
"http://localhost:9876/mcp"
]
}
}
}
Since the model in the current version doesn't require authentication, we don't need to pass the Authorization
header.
Now start Claude Desktop, open a new chat window, and try the same prompts as before.
Applying access control rules
First, add a context
element to the index.exo
file to extract the current user's ID and role:
context AuthContext {
@jwt("sub") id: Uuid
@jwt role: String
}
Next, use the AuthContext
in the index.exo
file to tighten the access control rules. We want to restrict the Account
type so it can be viewed only by:
- The associated customer (
self.customer.id == AuthContext.id
) - The customer's financial advisor (
self.customer.financialAdvisor.id == AuthContext.id
) - An admin (
AuthContext.role == "admin"
)
So the access control rule would be:
AuthContext.id == self.customer.id ||
AuthContext.id == self.customer.financialAdvisor.id ||
AuthContext.role == "admin"
Set this access control rule on the Account
type as follows:
@access(query=AuthContext.id == self.customer.id ||
AuthContext.id == self.customer.financialAdvisor.id ||
AuthContext.role == "admin")
type Account {
...
}
Since Exograph's default access control is false
, we don't need to specify @access(mutate=false)
on the Account
type.
Similarly, for the Customer
type, we want to ensure it can be viewed only by:
- The customer themselves (
self.id == AuthContext.id
) - The associated financial advisor (
self.financialAdvisor.id == AuthContext.id
) - An admin (
AuthContext.role == "admin"
)
So the access control rule would be:
AuthContext.id == self.id || AuthContext.id == self.financialAdvisor.id || AuthContext.role == "admin"
Set this access control rule on the Customer
type as follows:
@access(query=AuthContext.id == self.id ||
AuthContext.id == self.financialAdvisor.id ||
AuthContext.role == "admin")
type Customer {
...
}
You can now update the rest of the types in a similar manner. Study access control rules for more details or take a peek at the code from the existing Exograph model tutorial.
Using the MCP server
Using the MCP server is the same as in the existing Exograph model tutorial. Make sure to pass the Authorization
header, since with access control in place, you'll get errors without it (except for types that have access control set to @access(query=true, mutate=false)
).