guesty-mcp-server 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +6 -0
- package/.env.example +2 -0
- package/CHANGELOG.md +32 -0
- package/CONTRIBUTING.md +66 -0
- package/Dockerfile +13 -0
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/SECURITY.md +25 -0
- package/docs/multi-account-design.md +32 -0
- package/examples/claude-code-config.json +12 -0
- package/examples/docker-compose.yml +13 -0
- package/package.json +36 -0
- package/src/cli.js +101 -0
- package/src/health.js +43 -0
- package/src/http-transport.js +50 -0
- package/src/server.js +1096 -0
- package/src/token-cache.js +20 -0
- package/src/webhooks.js +83 -0
package/.dockerignore
ADDED
package/.env.example
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Guesty MCP Server will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.2.0] - 2026-03-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `create_reservation` - Create direct bookings (website → Guesty)
|
|
9
|
+
- `get_reviews` - Fetch guest reviews from all channels
|
|
10
|
+
- `get_calendar` - Check availability and pricing by date range
|
|
11
|
+
- `update_calendar` - Block/unblock dates, set minimum nights
|
|
12
|
+
- `respond_to_review` - Post responses to guest reviews
|
|
13
|
+
- `get_owner_statements` - Owner revenue statements and reports
|
|
14
|
+
- `get_expenses` - Track operational expenses
|
|
15
|
+
- `get_channels` - List connected booking channels per property
|
|
16
|
+
- `get_tasks` - Fetch cleaning and maintenance tasks
|
|
17
|
+
- Rate limit retry with exponential backoff
|
|
18
|
+
- Token caching module
|
|
19
|
+
|
|
20
|
+
## [0.1.0] - 2026-03-26
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Initial release with 6 core tools
|
|
24
|
+
- `get_reservations` - Fetch reservations with date/listing/status filters
|
|
25
|
+
- `get_listing` - Get property details or list all properties
|
|
26
|
+
- `get_conversations` - Fetch guest message history
|
|
27
|
+
- `send_guest_message` - Send messages to guests in conversations
|
|
28
|
+
- `get_financials` - Revenue, payouts, and commission data
|
|
29
|
+
- `update_pricing` - Update base price or date-specific pricing
|
|
30
|
+
- OAuth2 authentication with automatic token refresh
|
|
31
|
+
- CONTRIBUTING.md for open source contributors
|
|
32
|
+
- MIT License
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Contributing to Guesty MCP Server
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! This is the first MCP server for property management, and community contributions help make it better for everyone.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. Fork the repository
|
|
8
|
+
2. Clone your fork: `git clone https://github.com/YOUR-USERNAME/guesty-mcp-server.git`
|
|
9
|
+
3. Install dependencies: `npm install`
|
|
10
|
+
4. Create a `.env` file with your Guesty API credentials (see `.env.example`)
|
|
11
|
+
5. Run the server: `npm start`
|
|
12
|
+
|
|
13
|
+
## Adding New Tools
|
|
14
|
+
|
|
15
|
+
Each tool wraps a Guesty API endpoint. To add a new tool:
|
|
16
|
+
|
|
17
|
+
1. Identify the Guesty API endpoint from [Guesty Open API docs](https://open-api.guesty.com/api-docs)
|
|
18
|
+
2. Add the tool in `src/server.js` using the `server.tool()` pattern
|
|
19
|
+
3. Follow the existing naming convention: `verb_noun` (e.g., `get_reservations`, `update_pricing`)
|
|
20
|
+
4. Include proper Zod schema validation for all parameters
|
|
21
|
+
5. Return structured JSON in the response
|
|
22
|
+
|
|
23
|
+
### Tool Template
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
server.tool(
|
|
27
|
+
"tool_name",
|
|
28
|
+
"Clear description of what this tool does",
|
|
29
|
+
{
|
|
30
|
+
param1: z.string().describe("What this parameter does"),
|
|
31
|
+
param2: z.number().optional().describe("Optional parameter"),
|
|
32
|
+
},
|
|
33
|
+
async (params) => {
|
|
34
|
+
const data = await guestyGet("/endpoint", { key: params.param1 });
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Desired Contributions
|
|
43
|
+
|
|
44
|
+
We'd love help with:
|
|
45
|
+
- **New tools**: Reviews management, task/cleaning scheduling, owner statements, channel management
|
|
46
|
+
- **Testing**: Integration tests, error handling edge cases
|
|
47
|
+
- **Documentation**: Usage examples, video tutorials, blog posts
|
|
48
|
+
- **PMS integrations**: Adapting this pattern for Hostaway, Lodgify, OwnerRez, etc.
|
|
49
|
+
|
|
50
|
+
## Code Style
|
|
51
|
+
|
|
52
|
+
- ES modules (import/export)
|
|
53
|
+
- Async/await for all API calls
|
|
54
|
+
- Descriptive error messages
|
|
55
|
+
- Keep tools focused -- one API action per tool
|
|
56
|
+
|
|
57
|
+
## Pull Requests
|
|
58
|
+
|
|
59
|
+
1. Create a feature branch: `git checkout -b feature/new-tool`
|
|
60
|
+
2. Make your changes
|
|
61
|
+
3. Test against the Guesty API
|
|
62
|
+
4. Submit a PR with a clear description of what the tool does and why
|
|
63
|
+
|
|
64
|
+
## Questions?
|
|
65
|
+
|
|
66
|
+
Open an issue on GitHub or reach out to [DLJ Properties](https://tinyhomeboutiques.com).
|
package/Dockerfile
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DLJ Properties
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Guesty MCP Server
|
|
2
|
+
|
|
3
|
+
The first MCP (Model Context Protocol) server for [Guesty](https://guesty.com) property management. Connect AI agents directly to your Guesty account to manage reservations, communicate with guests, track finances, and update pricing -- all autonomously.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
| Tool | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| `get_reservations` | Fetch reservations with filters (dates, listing, status, guest) |
|
|
10
|
+
| `get_listing` | Get property details or list all properties |
|
|
11
|
+
| `get_conversations` | Fetch guest message history |
|
|
12
|
+
| `send_guest_message` | Send messages to guests in conversations |
|
|
13
|
+
| `get_financials` | Revenue, payouts, and commission data |
|
|
14
|
+
| `update_pricing` | Update base price or date-specific pricing |
|
|
15
|
+
| `create_reservation` | Create direct bookings (website → Guesty) |
|
|
16
|
+
| `get_reviews` | Fetch guest reviews from all channels |
|
|
17
|
+
| `get_calendar` | Check availability and pricing by date |
|
|
18
|
+
| `update_calendar` | Block/unblock dates, set minimum nights |
|
|
19
|
+
| `respond_to_review` | Post responses to guest reviews |
|
|
20
|
+
| `get_owner_statements` | Owner revenue statements and reports |
|
|
21
|
+
| `get_expenses` | Track operational expenses |
|
|
22
|
+
| `get_channels` | List connected booking channels per property |
|
|
23
|
+
| `get_tasks` | Fetch cleaning and maintenance tasks |
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### 1. Get Guesty API Credentials
|
|
28
|
+
|
|
29
|
+
1. Log into [Guesty Dashboard](https://app.guesty.com)
|
|
30
|
+
2. Go to **Settings > API** (or Marketplace > API Credentials)
|
|
31
|
+
3. Create an API application with `open-api` scope
|
|
32
|
+
4. Copy your **Client ID** and **Client Secret**
|
|
33
|
+
|
|
34
|
+
### 2. Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/DLJRealty/guesty-mcp-server.git
|
|
38
|
+
cd guesty-mcp-server
|
|
39
|
+
npm install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Configure
|
|
43
|
+
|
|
44
|
+
Set your Guesty credentials as environment variables:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export GUESTY_CLIENT_ID="your-client-id"
|
|
48
|
+
export GUESTY_CLIENT_SECRET="your-client-secret"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 4. Add to Claude Code
|
|
52
|
+
|
|
53
|
+
Add to your Claude Code settings (`~/.claude/settings.json`):
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"guesty": {
|
|
59
|
+
"command": "node",
|
|
60
|
+
"args": ["/path/to/guesty-mcp-server/src/server.js"],
|
|
61
|
+
"env": {
|
|
62
|
+
"GUESTY_CLIENT_ID": "your-client-id",
|
|
63
|
+
"GUESTY_CLIENT_SECRET": "your-client-secret"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 5. Use
|
|
71
|
+
|
|
72
|
+
Once connected, your AI agent can:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
"Show me all reservations checking in this week"
|
|
76
|
+
"What's the total revenue for March?"
|
|
77
|
+
"Send a welcome message to the guest checking in tomorrow"
|
|
78
|
+
"Update the base price for Unit Y to $159"
|
|
79
|
+
"List all my properties with their current status"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Use Cases
|
|
83
|
+
|
|
84
|
+
- **Guest Communication**: AI agents auto-respond to guest inquiries using real reservation data
|
|
85
|
+
- **Revenue Management**: Pull financial reports, analyze occupancy, optimize pricing
|
|
86
|
+
- **Operations**: Track check-ins/outs, coordinate cleaning schedules, manage availability
|
|
87
|
+
- **Marketing**: Identify low-occupancy periods, create targeted promotions
|
|
88
|
+
- **Multi-Agent Teams**: Give your entire AI team (CEO, Marketing, CS, Ops) access to property data
|
|
89
|
+
|
|
90
|
+
## Requirements
|
|
91
|
+
|
|
92
|
+
- Node.js 18+
|
|
93
|
+
- Guesty account with API access (Professional plan or higher)
|
|
94
|
+
- MCP-compatible AI client (Claude Code, OpenClaw, etc.)
|
|
95
|
+
|
|
96
|
+
## API Reference
|
|
97
|
+
|
|
98
|
+
This server wraps the [Guesty Open API](https://open-api.guesty.com/api-docs). Authentication uses OAuth2 client credentials flow with automatic token caching and refresh.
|
|
99
|
+
|
|
100
|
+
## Built By
|
|
101
|
+
|
|
102
|
+
[DLJ Properties](https://tinyhomeboutiques.com) -- Running 7 properties with a fully autonomous AI agent team. Built for our own use, shared with the STR community.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## API Credentials
|
|
4
|
+
|
|
5
|
+
**Never commit API credentials to version control.**
|
|
6
|
+
|
|
7
|
+
- Store `GUESTY_CLIENT_ID` and `GUESTY_CLIENT_SECRET` as environment variables
|
|
8
|
+
- Use `.env` files locally (already in `.gitignore`)
|
|
9
|
+
- Use secrets management in production (Docker secrets, AWS SSM, etc.)
|
|
10
|
+
|
|
11
|
+
## Token Handling
|
|
12
|
+
|
|
13
|
+
- OAuth2 tokens are cached in memory with automatic refresh
|
|
14
|
+
- Tokens expire after 24 hours
|
|
15
|
+
- Token cache file (`.token-cache.json`) is gitignored
|
|
16
|
+
|
|
17
|
+
## API Access
|
|
18
|
+
|
|
19
|
+
- The server uses OAuth2 client credentials flow
|
|
20
|
+
- All API calls go through HTTPS
|
|
21
|
+
- Rate limiting is handled with automatic retry
|
|
22
|
+
|
|
23
|
+
## Reporting Vulnerabilities
|
|
24
|
+
|
|
25
|
+
If you discover a security vulnerability, please email dljrealty@yahoo.com rather than opening a public issue.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Multi-Account Support Design (v3)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Support managing multiple Guesty accounts from a single MCP server instance.
|
|
5
|
+
|
|
6
|
+
## Configuration
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"accounts": {
|
|
10
|
+
"default": { "clientId": "xxx", "clientSecret": "xxx", "label": "DLJ Properties" },
|
|
11
|
+
"client-abc": { "clientId": "yyy", "clientSecret": "yyy", "label": "ABC Vacation Rentals" }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
Every tool accepts optional `accountId`:
|
|
18
|
+
```
|
|
19
|
+
get_reservations({ accountId: "client-abc", limit: 10 })
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Key Features
|
|
23
|
+
- Separate token cache per account
|
|
24
|
+
- Independent rate limit tracking
|
|
25
|
+
- Per-account health checks
|
|
26
|
+
- No cross-account data leakage
|
|
27
|
+
- Audit log per account
|
|
28
|
+
|
|
29
|
+
## Enterprise Use Cases
|
|
30
|
+
- Property management companies with regional accounts
|
|
31
|
+
- White-label SaaS serving multiple clients
|
|
32
|
+
- Multi-brand hospitality groups
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
services:
|
|
3
|
+
guesty-mcp:
|
|
4
|
+
build: .
|
|
5
|
+
environment:
|
|
6
|
+
- GUESTY_CLIENT_ID=${GUESTY_CLIENT_ID}
|
|
7
|
+
- GUESTY_CLIENT_SECRET=${GUESTY_CLIENT_SECRET}
|
|
8
|
+
- WEBHOOK_PORT=3001
|
|
9
|
+
- MCP_HTTP_PORT=3002
|
|
10
|
+
ports:
|
|
11
|
+
- "3001:3001"
|
|
12
|
+
- "3002:3002"
|
|
13
|
+
restart: unless-stopped
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "guesty-mcp-server",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "MCP Server for Guesty Property Management - The first Guesty MCP server",
|
|
5
|
+
"main": "src/server.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"guesty-mcp-server": "src/server.js",
|
|
8
|
+
"guesty-cli": "src/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "node src/server.js",
|
|
13
|
+
"test": "node tests/test-tools.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"guesty",
|
|
18
|
+
"property-management",
|
|
19
|
+
"short-term-rental",
|
|
20
|
+
"airbnb",
|
|
21
|
+
"ai-agent"
|
|
22
|
+
],
|
|
23
|
+
"author": "DLJ Properties",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "latest"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/DLJRealty/guesty-mcp-server.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/DLJRealty/guesty-mcp-server",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Guesty CLI - Command line tool for quick Guesty queries
|
|
4
|
+
* Usage: guesty-cli <command> [options]
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const GUESTY_CLIENT_ID = process.env.GUESTY_CLIENT_ID;
|
|
8
|
+
const GUESTY_CLIENT_SECRET = process.env.GUESTY_CLIENT_SECRET;
|
|
9
|
+
|
|
10
|
+
if (!GUESTY_CLIENT_ID || !GUESTY_CLIENT_SECRET) {
|
|
11
|
+
console.error("Set GUESTY_CLIENT_ID and GUESTY_CLIENT_SECRET environment variables.");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let token = null;
|
|
16
|
+
|
|
17
|
+
async function auth() {
|
|
18
|
+
const res = await fetch("https://open-api.guesty.com/oauth2/token", {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
21
|
+
body: new URLSearchParams({
|
|
22
|
+
grant_type: "client_credentials", scope: "open-api",
|
|
23
|
+
client_id: GUESTY_CLIENT_ID, client_secret: GUESTY_CLIENT_SECRET,
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
const d = await res.json();
|
|
27
|
+
if (!d.access_token) throw new Error("Auth failed");
|
|
28
|
+
token = d.access_token;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function api(path) {
|
|
32
|
+
const res = await fetch(`https://open-api.guesty.com/v1${path}`, {
|
|
33
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) throw new Error(`API ${res.status}`);
|
|
36
|
+
return res.json();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const commands = {
|
|
40
|
+
reservations: async () => {
|
|
41
|
+
const d = await api("/reservations?limit=10&sort=checkIn&order=desc");
|
|
42
|
+
console.log(`\nUpcoming Reservations (${d.count} total):\n`);
|
|
43
|
+
for (const r of d.results || []) {
|
|
44
|
+
const guest = r.guest?.fullName || "Unknown";
|
|
45
|
+
const dates = `${r.checkIn?.slice(0,10)} → ${r.checkOut?.slice(0,10)}`;
|
|
46
|
+
const listing = r.listing?.title?.slice(0,35) || "?";
|
|
47
|
+
console.log(` ${guest.padEnd(20)} | ${dates} | ${listing}`);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
listings: async () => {
|
|
51
|
+
const d = await api("/listings?limit=25");
|
|
52
|
+
console.log(`\nProperties (${d.count} total):\n`);
|
|
53
|
+
for (const l of d.results || []) {
|
|
54
|
+
const status = l.active ? "ACTIVE" : "OFF";
|
|
55
|
+
console.log(` ${(l.nickname||"?").padEnd(25)} | ${status.padEnd(6)} | ${l.title?.slice(0,40)}`);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
revenue: async () => {
|
|
59
|
+
const d = await api("/reservations?limit=100&fields=money%20guest%20checkIn%20listing%20status");
|
|
60
|
+
let total = 0;
|
|
61
|
+
for (const r of d.results || []) total += r.money?.totalPaid || 0;
|
|
62
|
+
console.log(`\nRevenue Summary:\n Reservations: ${d.count}\n Total Revenue: $${total.toLocaleString()}`);
|
|
63
|
+
},
|
|
64
|
+
reviews: async () => {
|
|
65
|
+
const d = await api("/reviews?limit=5");
|
|
66
|
+
console.log(`\nRecent Reviews (${d.count} total):\n`);
|
|
67
|
+
for (const r of d.results || []) {
|
|
68
|
+
console.log(` ⭐ ${r.rating}/5 | ${r.guest?.fullName || "?"} | ${r.comment?.slice(0,60) || ""}`);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
help: () => {
|
|
72
|
+
console.log(`
|
|
73
|
+
Guesty CLI - Quick property management queries
|
|
74
|
+
|
|
75
|
+
Commands:
|
|
76
|
+
reservations Show upcoming reservations
|
|
77
|
+
listings List all properties
|
|
78
|
+
revenue Revenue summary
|
|
79
|
+
reviews Recent guest reviews
|
|
80
|
+
help Show this help
|
|
81
|
+
|
|
82
|
+
Usage: GUESTY_CLIENT_ID=xxx GUESTY_CLIENT_SECRET=xxx node src/cli.js <command>
|
|
83
|
+
`);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const cmd = process.argv[2] || "help";
|
|
88
|
+
if (!commands[cmd]) {
|
|
89
|
+
console.error(`Unknown command: ${cmd}. Run with 'help' for options.`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
(async () => {
|
|
94
|
+
try {
|
|
95
|
+
if (cmd !== "help") await auth();
|
|
96
|
+
await commands[cmd]();
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.error(`Error: ${e.message}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
})();
|
package/src/health.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createServer } from "http";
|
|
2
|
+
|
|
3
|
+
const startTime = Date.now();
|
|
4
|
+
const HEALTH_PORT = parseInt(process.env.HEALTH_PORT || "3003", 10);
|
|
5
|
+
|
|
6
|
+
export function startHealthCheck(guestyAuthFn) {
|
|
7
|
+
const server = createServer(async (req, res) => {
|
|
8
|
+
if (req.url !== "/health" && req.url !== "/") {
|
|
9
|
+
res.writeHead(404);
|
|
10
|
+
res.end("Not Found");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const uptime = Math.floor((Date.now() - startTime) / 1000);
|
|
15
|
+
let guestyStatus = "unknown";
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
await guestyAuthFn();
|
|
19
|
+
guestyStatus = "connected";
|
|
20
|
+
} catch (e) {
|
|
21
|
+
guestyStatus = `error: ${e.message}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const health = {
|
|
25
|
+
status: guestyStatus === "connected" ? "healthy" : "degraded",
|
|
26
|
+
uptime: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m ${uptime % 60}s`,
|
|
27
|
+
guestyApi: guestyStatus,
|
|
28
|
+
toolCount: 15,
|
|
29
|
+
version: "0.2.0",
|
|
30
|
+
transport: "stdio",
|
|
31
|
+
timestamp: new Date().toISOString(),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
35
|
+
res.end(JSON.stringify(health, null, 2));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
server.listen(HEALTH_PORT, () => {
|
|
39
|
+
console.log(`[health] Health check endpoint at http://localhost:${HEALTH_PORT}/health`);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return server;
|
|
43
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createServer } from "http";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Streamable HTTP Transport for Guesty MCP Server
|
|
5
|
+
* Allows the MCP server to be accessed via HTTP instead of stdio.
|
|
6
|
+
* Useful for remote access, web integrations, and multi-client setups.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const HTTP_PORT = parseInt(process.env.MCP_HTTP_PORT || "3002", 10);
|
|
10
|
+
|
|
11
|
+
export function startHttpTransport(server) {
|
|
12
|
+
const httpServer = createServer(async (req, res) => {
|
|
13
|
+
// CORS headers
|
|
14
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
15
|
+
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
16
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
17
|
+
|
|
18
|
+
if (req.method === "OPTIONS") {
|
|
19
|
+
res.writeHead(204);
|
|
20
|
+
res.end();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (req.method !== "POST" || req.url !== "/mcp") {
|
|
25
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
26
|
+
res.end(JSON.stringify({ error: "Use POST /mcp" }));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let body = "";
|
|
31
|
+
for await (const chunk of req) body += chunk;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const request = JSON.parse(body);
|
|
35
|
+
// Forward to MCP server and get response
|
|
36
|
+
// This is a simplified passthrough -- full SSE streaming would need more work
|
|
37
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
38
|
+
res.end(JSON.stringify({ received: true, request }));
|
|
39
|
+
} catch (e) {
|
|
40
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
41
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
httpServer.listen(HTTP_PORT, () => {
|
|
46
|
+
console.log(`[http] MCP HTTP transport listening on port ${HTTP_PORT}`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return httpServer;
|
|
50
|
+
}
|