Connect OwnerRez to Claude
Ask Claude about your OwnerRez bookings, properties, and guests in plain English. No dashboards, no CSV exports, no copy-pasting into spreadsheets. Just ask.
Last week Cole Rubin at Conduit (YC W24) published Claude MCP connector guides for 8 property management systems. OwnerRez wasn't one of them. This is ours. Shout out to Cole for lighting the match. This guide uses the same 4-step format and the same @modelcontextprotocol/sdk pattern so if you've followed one of his, this will feel familiar.
What you'll be able to ask Claude
Once connected, Claude can query your OwnerRez account directly. A few examples from real operator workflows:
Prerequisites
- OwnerRez account with admin access (you'll need to generate a Personal Access Token).
- Claude Desktop installed on macOS or Windows, or the Claude Code CLI. Download Claude Desktop.
- Node.js 18 or later. Check with
node --version. If you don't have it, install from nodejs.org. - A few minutes of comfort with the terminal. No deep engineering experience required.
Generate an OwnerRez Personal Access Token
OwnerRez uses HTTP Basic Auth for its v2 API. Your credentials are your account email plus a Personal Access Token (PAT) that starts with pt_.
- Log in to OwnerRez.
- Go to Settings → Advanced Tools → Developer / API Settings.
- Click Create Personal Access Token. Give it a name (e.g. "Claude MCP") and create it.
- Copy the token immediately. It looks like
pt_abc123def456...and you won't be able to see it again after you close the dialog.
Note: Some endpoints are gated. The /v2/messages endpoints require a separate signed messaging agreement with OwnerRez (contact help@ownerrez.com), and /v2/listings and /v2/reviews require the "Integrated Websites" premium feature. The tools we expose in this guide (bookings, properties, guests) don't need either.
Set up the Node.js project
Create a new directory for the MCP server, initialize it, and install the two dependencies: the Model Context Protocol SDK and zod for schema validation.
mkdir ownerrez-mcp
cd ownerrez-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
Then open package.json and add "type": "module" so Node uses ES modules:
{
"name": "ownerrez-mcp",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.22.0"
}
}
Create the MCP server file
Create a file called index.js in your ownerrez-mcp directory and paste the following. This exposes four tools: list-bookings, list-properties, get-booking, and search-guests.
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const EMAIL = process.env.OWNERREZ_EMAIL;
const TOKEN = process.env.OWNERREZ_TOKEN;
const BASE_URL = "https://api.ownerrez.com/v2";
if (!EMAIL || !TOKEN) {
console.error("Missing OWNERREZ_EMAIL or OWNERREZ_TOKEN environment variables");
process.exit(1);
}
const authHeader =
"Basic " + Buffer.from(`${EMAIL}:${TOKEN}`).toString("base64");
async function ownerrezGet(path, params = {}) {
const url = new URL(`${BASE_URL}${path}`);
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null && value !== "") {
url.searchParams.append(key, String(value));
}
}
const response = await fetch(url.toString(), {
method: "GET",
headers: {
Authorization: authHeader,
Accept: "application/json",
"User-Agent": "rapideye-ownerrez-mcp/1.0",
},
});
if (!response.ok) {
const body = await response.text();
throw new Error(`OwnerRez API ${response.status}: ${body}`);
}
return response.json();
}
const server = new McpServer({
name: "ownerrez",
version: "1.0.0",
});
// Tool 1: List bookings
server.tool(
"list-bookings",
"List OwnerRez bookings modified since a given date. Defaults to bookings modified in the last 30 days. Use for questions about upcoming arrivals, recent bookings, or activity on specific properties.",
{
since_utc: z
.string()
.optional()
.describe(
"ISO 8601 UTC timestamp (e.g. 2026-04-01T00:00:00Z). Returns bookings modified since this date. Defaults to 30 days ago."
),
property_ids: z
.string()
.optional()
.describe(
"Comma-separated OwnerRez property IDs to filter by. Omit to query across all properties."
),
include_charges: z
.boolean()
.optional()
.describe("Include charge line items in the response."),
include_guest: z
.boolean()
.optional()
.describe("Include guest contact details."),
},
async ({ since_utc, property_ids, include_charges, include_guest }) => {
const defaultSince = new Date(
Date.now() - 30 * 24 * 60 * 60 * 1000
).toISOString();
const data = await ownerrezGet("/bookings", {
since_utc: since_utc || defaultSince,
property_ids,
include_charges,
include_guest,
});
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
// Tool 2: List properties
server.tool(
"list-properties",
"List all OwnerRez properties (rental listings) on the account. Returns property ID, name, address, bedroom/bathroom count, and active status.",
{},
async () => {
const data = await ownerrezGet("/properties");
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
// Tool 3: Get a single booking
server.tool(
"get-booking",
"Fetch a single OwnerRez booking by ID. Returns full booking details including guest, charges, dates, and notes.",
{
id: z.number().describe("Booking ID"),
},
async ({ id }) => {
const data = await ownerrezGet(`/bookings/${id}`);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
// Tool 4: Search guests
server.tool(
"search-guests",
"Search OwnerRez guests by name or email. Returns matching guest records with contact details.",
{
q: z.string().describe("Search query (name or email substring)"),
},
async ({ q }) => {
const data = await ownerrezGet("/guests", { q });
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
That's the entire server. About 120 lines. You can extend it with more tools by copying the server.tool(...) pattern and pointing at any of the 60+ v2 endpoints OwnerRez publishes.
Register the server with Claude Desktop
Open (or create) your Claude Desktop config file. On macOS, it lives at:
~/Library/Application Support/Claude/claude_desktop_config.json
On Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add the following (if the file already has an mcpServers block, just merge the ownerrez entry into it):
{
"mcpServers": {
"ownerrez": {
"command": "node",
"args": ["/absolute/path/to/ownerrez-mcp/index.js"],
"env": {
"OWNERREZ_EMAIL": "your-email@example.com",
"OWNERREZ_TOKEN": "pt_your_personal_access_token_here"
}
}
}
}
Replace /absolute/path/to/ownerrez-mcp/index.js with the actual absolute path on your machine (run pwd inside the ownerrez-mcp directory to get it). Replace the email and token values with your real credentials.
Restart Claude Desktop fully. Quit the app (don't just close the window) and reopen it. The OwnerRez tools should appear in the tools menu.
Try your first query
Open Claude Desktop and ask it something that requires your OwnerRez data. A good first test:
Claude will call list-properties, read the response, and answer in plain English. If it works, you're connected. Try a harder query next: "What bookings do I have arriving in the next 7 days, grouped by property?"
Troubleshooting
401 Unauthorized when Claude calls a tool
Your email or Personal Access Token is wrong or the token was revoked. Double-check the values in claude_desktop_config.json. The token must start with pt_. Regenerate it in OwnerRez Settings → Advanced Tools → Developer / API Settings if in doubt.
Also verify the email matches the OwnerRez account that owns the token — the Basic Auth uses both.
Claude says it doesn't see any OwnerRez tools
Claude Desktop only reads the config file at startup. Fully quit Claude (Cmd+Q on macOS, right-click tray icon → Quit on Windows) and reopen it. A window close is not enough.
If it still doesn't show up, check the Claude Desktop logs (Help → Open Logs Folder) for a startup error. Common causes: invalid JSON in the config file, wrong path to index.js, or node not being in your PATH.
"Cannot find module '@modelcontextprotocol/sdk/server/mcp.js'"
The dependency wasn't installed or you ran node index.js from outside the project directory. Make sure you ran npm install inside ownerrez-mcp and that "type": "module" is set in package.json.
OwnerRez returns an error about required parameters on /v2/bookings
The /v2/bookings endpoint requires either property_ids or since_utc. The MCP server passes a default since_utc of 30 days ago if you don't specify one, so this shouldn't happen with the code above — but if it does, pass an explicit since_utc in your prompt (e.g. "list bookings since 2026-03-01").
I want to expose more endpoints
The OwnerRez v2 API has 60+ endpoints covering payments, refunds, quotes, fees, field definitions, owners, inquiries, tag definitions, and webhooks. Add more tools by copying the existing server.tool(...) pattern and pointing at any path in the v2 reference. The auth header and fetch helper stay the same.
Remember: /v2/listings, /v2/reviews require the WordPress Plugin + Integrated Websites premium feature, and /v2/messages requires a signed messaging agreement.
What's next
This is guide 1 in a series for STR operators. Cole covered 8 PMSes. We're extending the format to categories of the stack he skipped: smart locks, guest screening, dynamic pricing, and more.