Atrio MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Atrio MCP Server What are my unit's overdue fees and latest payments?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
A read-only MCP server over Atrio
A sample from Atrio, a multi-tenant SaaS I built for managing residential buildings in Panama (accounting, payroll, resident portal). It shows how I connect an AI agent to a business system: through the Model Context Protocol, with the database, not the prompt, deciding what the agent can see.
Any MCP client (Claude Desktop, Claude Code, Cursor and others) can plug into it and answer questions like "Who's behind on fees this month?" or "What's booked in the party room next week?" for the person who's signed in, and only for them.
The three tools
Tool | Answers | Who can use it |
| Balance of one unit: charged, paid, overdue, latest charges and payments | Staff: any unit in their building. A resident: their own unit |
| Units with an overdue balance at the end of a month, largest first, with the total | Staff only |
| Bookings of common areas for the next N days | Everyone in the building. Residents don't see which unit booked |
All three are marked readOnlyHint, so clients can call them without asking for confirmation.
Related MCP server: AgenticMCP
How the agent is kept in its lane
The model can be talked into anything, so the rules don't live in the model or in the tool code. Each tool call runs inside the database like this:
Read-only transaction.
begin read only. Anupdatefails even if a bug or an injected prompt produces one.A role with no write grants.
set local role atrio_agent. The role can onlyselect, and it is subject to Row Level Security.The caller's identity.
set_config('app.user_id', …, true), scoped to the transaction. In production this is the verified JWT (auth.uid()in Supabase).Row Level Security on every table. The policies use that identity. Staff see their whole building, a resident sees their own unit, and nobody sees another building. With no identity, every table looks empty.
When a resident asks for a neighbour's unit, the answer is the same as for a unit that doesn't exist: No unit "A-102" is visible to you. That way the answer doesn't reveal whether the unit exists.
The tool layer adds one check of its own (arrears is staff-only) and input validation with Zod. Even if a tool's SQL were wrong, it still couldn't return rows the policies hide.
Run the tests
You need Node.js 22.18+ and nothing else. The tests use PGlite, real PostgreSQL compiled to WebAssembly, so RLS and roles behave as in production.
npm install
npm test
# ℹ tests 15
# ℹ pass 15The tests talk to the server over the MCP protocol as three different users (a Torre Coral admin, a Torre Coral resident and an admin of another building). They check the numbers, the isolation between users and buildings, and that the agent role can't write even with hand-written SQL.
Try it in Claude
With no DATABASE_URL the server starts with the fictional demo data in memory.
Claude Code
claude mcp add atrio -- node /path/to/atrio-mcp-sample/src/main.tsClaude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"atrio": {
"command": "node",
"args": ["/path/to/atrio-mcp-sample/src/main.ts"],
"env": { "ATRIO_USER_ID": "aaaaaaaa-0000-0000-0000-000000000001" }
}
}
}Switch ATRIO_USER_ID to see the same questions from different people:
User | Who |
| Admin, Torre Coral |
| Resident of A-101, Torre Coral |
| Admin, PH Mirador del Mar |
Against a real PostgreSQL
psql "$DATABASE_URL" -f sql/schema.sql -f sql/seed.sql
psql "$DATABASE_URL" -c "create role atrio_mcp login password '…'; grant atrio_agent to atrio_mcp;"
DATABASE_URL=postgres://atrio_mcp:…@host/db ATRIO_USER_ID=… npm startThe server's login role only needs to be able to set role atrio_agent. It never reads a table with its own privileges.
What changes in production
Transport. It runs over Streamable HTTP instead of stdio, and the user id comes from the client's OAuth token rather than an environment variable.
Schema. It's the full Atrio schema. The in-app assistant uses twelve read-only tools under the same model: finances, maintenance, packages, visitor passes, votes and more.
Writes. Anything that writes, such as a payment reminder or a booking, would be a separate tool that drafts the action for a person to approve.
Files
File | Content |
| Tables, the |
| Two fictional buildings, three users, dates relative to today |
| Runs each call as the user: read-only, |
| The three MCP tools |
| stdio entry point |
| 15 tests over the MCP protocol |
All names and figures are fictional.
Available Tools
3 toolsarrearsArrears for a monthARead-only
Units with an overdue balance at the end of a month (charges due by then minus payments made by then), largest first, with the building total. Staff only. For the current month the cut-off is today.
Inputs: month.
unit_balanceUnit balanceARead-only
Account balance of one unit in the building, by unit code (e.g. 'A-102'): total charged, total paid, balance, the part already overdue, and the latest charges and payments.
Inputs: unit_code (required).
upcoming_bookingsUpcoming bookingsARead-only
Confirmed and pending bookings of common areas (party room, BBQ, pool) from today through the next N days.
Inputs: days.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 3 tools
Each tool targets a clearly distinct question: one unit's balance, the building's arrears list, and upcoming common-area bookings. There is no meaningful overlap in purpose or output.
All names use lowercase snake_case and are noun-oriented, which is readable and consistent overall. 'arrears' is a single-word noun while the others are compound noun phrases, a minor deviation but not confusing.
Three tools are within a reasonable range for a focused read-only reporting server, and each earns its place. It is somewhat thin for a building-management domain, but not mismatched.
The surface covers key read-only views: unit balances, arrears, and upcoming bookings. However, it lacks write/lifecycle operations such as creating or updating bookings, recording payments, listing units, or producing broader building summaries, which may leave agents at dead ends for management tasks.
Maintenance
Related MCP Connectors
Ask your Rent Manager portfolio anything: live, read-only financials, rent roll, leasing.
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
Read-only AI access to HAVN properties, leads, tasks, files, media, and analytics.
Tenant-separated encrypted context and model routing for your AI work; OAuth 2.1 sign-in.
Related MCP Servers
- AlicenseAqualityDmaintenanceA read-only MCP server that connects AI assistants to the OfficeRnD coworking and flex-space management platform. It enables natural language queries for community members, space bookings, billing records, and office resources.51MIT
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol (MCP) server that provides secure, role-based access to PostgreSQL databases for AI agents.MIT
- AlicenseNot gradedqualityDmaintenanceA secure, multi-tenant Model Context Protocol server that gives AI clients controlled access to enterprise knowledge bases, SQL databases, REST APIs, files, and GitHub.Apache 2.0
- AlicenseAqualityBmaintenanceA drop-in Model Context Protocol (MCP) server that lets an AI agent query PostgreSQL — read-only, enforced at the database level, with real SQL validation, timeouts, cost limits, OAuth 2.1, and an audit trail.103MIT