Connect Guesty to Claude
Run a small local server so Claude can read your Guesty listings, reservations, calendar, and guests, and answer questions about them in plain English. Free, working code, about twenty minutes.
Can you connect Guesty to Claude? Yes. You run a small MCP server: a lightweight program on your own machine that wraps the Guesty Open API and exposes it to Claude as a set of tools. Once it's connected, Claude can read your listings, reservations, calendar, and guests and answer questions about them, with no dashboards or exports.
MCP (the Model Context Protocol) is an open standard from Anthropic that lets AI assistants talk to outside systems. Guesty has no first-party MCP server as of June 2026, so this guide builds a focused, read-only one. Your Open API credentials are self-serve: you create them yourself under Integrations.
What you can ask once it's connected
- "Which reservations check in this week, and at which listings?"
- "How many active listings do we manage right now?"
- "What's the availability and nightly price for this listing over the next 14 days?"
- "Pull this reservation by id and tell me the guest, dates, and status."
- "Summarize how many nights are booked across the portfolio this month."
Before you start
Three things: a Guesty account with Open API access (you create OAuth credentials under Integrations), Node.js 18 or newer (20+ recommended; check with node --version), and either Claude Desktop or Claude Code.
Create Open API credentials
In Guesty, open Integrations → OAuth applications and click New application. Give it a name and description and save. Guesty shows you a client ID and client secret. You can create up to five applications.
Scaffold the project
Make a folder and install the two dependencies, the official MCP SDK and zod (used to describe each tool's inputs). No build step, no TypeScript compiler.
mkdir guesty-mcp && cd guesty-mcp
npm init -y
npm pkg set type="module"
npm install @modelcontextprotocol/sdk zod
Write the server
Save this as index.js. Guesty's token lives for 24 hours and you are allowed only five tokens per day, so the server caches the token to a small file and reuses it across restarts instead of minting a new one each time. It exposes six read-only tools.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { readFileSync, writeFileSync } from "fs";
import { join } from "path";
import { tmpdir } from "os";
const API_BASE = "https://open-api.guesty.com/v1";
const TOKEN_URL = "https://open-api.guesty.com/oauth2/token";
const CLIENT_ID = process.env.GUESTY_CLIENT_ID;
const CLIENT_SECRET = process.env.GUESTY_CLIENT_SECRET;
const CACHE = join(tmpdir(), "guesty-mcp-token.json");
if (!CLIENT_ID || !CLIENT_SECRET) {
console.error("Set GUESTY_CLIENT_ID and GUESTY_CLIENT_SECRET first.");
process.exit(1);
}
// Guesty allows only 5 tokens per client per 24h, and each lasts 24h.
// Cache the token to disk so restarts reuse it instead of minting a new one.
let token = null;
let tokenExp = 0;
async function getToken() {
const now = Date.now();
if (token && now < tokenExp) return token;
try {
const c = JSON.parse(readFileSync(CACHE, "utf8"));
if (c.access_token && now < c.expires_at) {
token = c.access_token; tokenExp = c.expires_at; return token;
}
} catch {}
const res = await fetch(TOKEN_URL, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({ clientId: CLIENT_ID, clientSecret: CLIENT_SECRET }),
});
if (!res.ok) throw new Error(`Token request failed (${res.status})`);
const data = await res.json();
token = data.access_token;
// Refresh ~1 hour before the 24h expiry, per Guesty's guidance.
tokenExp = now + (data.expires_in - 3600) * 1000;
try { writeFileSync(CACHE, JSON.stringify({ access_token: token, expires_at: tokenExp })); } catch {}
return token;
}
async function get(path) {
const res = await fetch(`${API_BASE}${path}`, {
headers: { Authorization: `Bearer ${await getToken()}`, Accept: "application/json" },
});
if (!res.ok) throw new Error(`Guesty ${res.status}: ${await res.text()}`);
return res.json();
}
function json(data) {
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
const server = new McpServer({ name: "guesty", version: "1.0.0" });
server.registerTool(
"list_listings",
{
description: "List the listings (properties) in the Guesty account.",
inputSchema: {
limit: z.number().optional().describe("Max rows, 1-100, default 100"),
skip: z.number().optional().describe("Rows to skip, for paging"),
},
},
async ({ limit, skip }) => {
const q = new URLSearchParams({ limit: String(limit ?? 100) });
if (skip) q.set("skip", String(skip));
return json(await get(`/listings?${q}`));
}
);
server.registerTool(
"get_listing",
{
description: "Get full details for one listing by its id.",
inputSchema: { id: z.string().describe("Guesty listing id") },
},
async ({ id }) => json(await get(`/listings/${id}`))
);
server.registerTool(
"list_reservations",
{
description: "List reservations. Use skip to page through results.",
inputSchema: {
limit: z.number().optional().describe("Max rows, 1-100, default 100"),
skip: z.number().optional().describe("Rows to skip, for paging"),
},
},
async ({ limit, skip }) => {
const q = new URLSearchParams({ limit: String(limit ?? 100) });
if (skip) q.set("skip", String(skip));
return json(await get(`/reservations?${q}`));
}
);
server.registerTool(
"get_reservation",
{
description: "Get one reservation by its id, including guest, dates, and status.",
inputSchema: { id: z.string().describe("Guesty reservation id") },
},
async ({ id }) => json(await get(`/reservations/${id}`))
);
server.registerTool(
"get_calendar",
{
description: "Get availability and nightly pricing for one or more listings over a date range.",
inputSchema: {
listingIds: z.string().describe("One listing id, or several comma-separated"),
startDate: z.string().describe("First day, YYYY-MM-DD"),
endDate: z.string().describe("Last day, YYYY-MM-DD"),
},
},
async ({ listingIds, startDate, endDate }) => {
const q = new URLSearchParams({ listingIds, startDate, endDate });
return json(await get(`/availability-pricing/api/calendar/listings?${q}`));
}
);
server.registerTool(
"get_guest",
{
description: "Get one guest by id (find the guestId on a reservation).",
inputSchema: { guestId: z.string().describe("Guesty guest id") },
},
async ({ guestId }) => json(await get(`/guests-crud/${guestId}`))
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Guesty MCP server running on stdio");
Connect it to Claude
Point Claude at the server and pass your two credentials.
Add a guesty entry to your config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %AppData%\Claude\claude_desktop_config.json on Windows), then fully quit and reopen the app.
{
"mcpServers": {
"guesty": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/guesty-mcp/index.js"],
"env": {
"GUESTY_CLIENT_ID": "your-client-id",
"GUESTY_CLIENT_SECRET": "your-client-secret"
}
}
}
}
One command from your terminal. Everything after -- is what launches the server.
claude mcp add \
--env GUESTY_CLIENT_ID=your-client-id \
--env GUESTY_CLIENT_SECRET=your-client-secret \
--transport stdio guesty \
-- node /ABSOLUTE/PATH/TO/guesty-mcp/index.js
Confirm with claude mcp list. Add --scope user to make it available in every project.
Ask away
Start a new conversation. Claude asks permission the first time it calls a tool, approve it, and you're set.
Which reservations check in over the next 7 days? Group them by listing.
How many active listings do we have, and which are inactive?
Get the calendar for this listing id for the next 14 days and flag open nights.
Prefer to skip the typing?
Paste this into Claude Code (or any coding agent) and it will build, install, and wire up the server for you. Then come back to step 1 for your credentials.
Build a local MCP server (Node.js, ESM, @modelcontextprotocol/sdk and zod)
that wraps the Guesty Open API (base https://open-api.guesty.com/v1). Read
GUESTY_CLIENT_ID and GUESTY_CLIENT_SECRET from the environment. Get an access
token by POSTing JSON {clientId, clientSecret} to
https://open-api.guesty.com/oauth2/token; the token lasts 24h and Guesty only
allows 5 tokens per day, so CACHE the token to a file in tmpdir and reuse it
until ~1h before expiry. Add an authenticated GET helper that returns the
parsed JSON body. Register these read-only tools, default limit 100, built
with URLSearchParams: list_listings(limit?, skip?), get_listing(id),
list_reservations(limit?, skip?), get_reservation(id),
get_calendar(listingIds, startDate, endDate) ->
/availability-pricing/api/calendar/listings, get_guest(guestId) ->
/guests-crud/{guestId}. Use StdioServerTransport; log startup to stderr. Then
make a package.json (type: module), install the deps, and show me the
claude mcp add command, the claude_desktop_config.json block, and three
prompts to try.
Good to know
- The token is cached on purpose. Guesty allows only five tokens per client every 24 hours. Because an MCP server restarts each time Claude launches, the server writes its 24-hour token to a small file in your temp directory and reuses it. Delete that file if you ever rotate credentials.
- It's read-only. The tools only ever GET. Claude can't change pricing, cancel a booking, or message a guest. To add writes later, add a POST helper and register write tools deliberately, confirm each endpoint's fields in the Guesty Open API reference, and test on non-live data first.
- Credentials stay with you. They live in environment variables (the Claude config) and the server runs locally over stdio. If you ever put them in a .env file, add it to .gitignore and never commit the secret.
- It uses the official Open API as intended. No scraping, no undocumented endpoints. Responses carry ratelimit-remaining headers, so cache results and avoid tight loops.
One Guesty connection worth its own server: your turnover photos. This MCP server gives you conversational access to reservations and listings. RapidEye covers the other half of the operation, the turnover itself, reading the inspection photos your cleaners capture after each clean and flagging damage and missed cleaning before the next guest checks in.
FAQ
Can you connect Guesty to Claude?
Yes. Guesty has no first-party MCP server as of June 2026, but its Open API is self-serve, so you can build a small read-only server yourself (this guide) in about 20 minutes. A community server on npm and Zapier's no-code bridge are alternatives.
Does Guesty have an official MCP server?
Not a first-party one. There is a community MCP server on npm and Zapier offers a no-code bridge. Building your own, as here, keeps it read-only and under your control.
Why does the server cache its token?
Guesty allows only five access tokens per client ID per 24 hours, and each lasts 24 hours. Caching the token to a file lets the server reuse it across restarts instead of burning through the daily limit.
Sources
- Guesty Open API: Quick start guidehttps://open-api-docs.guesty.com/docs/quick-start-guide
- Guesty Open API: Authenticationhttps://open-api-docs.guesty.com/docs/authentication
- Guesty Open API: Search reservationshttps://open-api-docs.guesty.com/reference/get_reservations
- Model Context Protocol: Build an MCP serverhttps://modelcontextprotocol.io/quickstart/server
- Claude Code: Connect to tools via MCPhttps://code.claude.com/docs/en/mcp
Independent integration guide. RapidEye is not affiliated with or endorsed by Guesty or Anthropic. API behavior and dashboard menus can change; confirm specifics against the official docs above. Last reviewed June 14, 2026.