guesty-mcp-server 0.9.1 → 0.9.2

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/README.md CHANGED
@@ -11,6 +11,8 @@ The first MCP (Model Context Protocol) server for [Guesty](https://guesty.com) p
11
11
 
12
12
  > **Stay updated:** [Sign up for release notes and new tool announcements](https://guestycopilot.com#signup)
13
13
 
14
+ > **v0.9.2 (2026-05-21) — Paid tiers launching v1.0 next week.** Free tier (23 read-only tools) is fully functional now: reservations, listings, guests, calendars, financial reports, operations, reviews, and webhook reads. Write operations (create/update/delete) and the full 43-tool surface ship in v1.0 with Stripe-backed paid-tier activation. Until then, paid-tier license keys return a `NOT YET WIRED — PAID TIERS LAUNCH v1.0` refusal — set or omit `GUESTY_MCP_LICENSE_KEY` to use the free tier.
15
+
14
16
  ## Quick Start
15
17
 
16
18
  ```bash
package/hero.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guesty-mcp-server",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "MCP server for Guesty property management — 43 production tools + 7 addressable resource templates covering reservations, guests, messaging, pricing, revenue, tasks, webhooks, and IoT/property-health Enterprise tier.",
5
5
  "main": "src/server.js",
6
6
  "bin": {
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "guesty-mcp-server",
3
+ "version": "0.9.1",
4
+ "description": "MCP server for Guesty property management — 43 production tools + 7 addressable resource templates covering reservations, guests, messaging, pricing, revenue, tasks, webhooks, and IoT/property-health Enterprise tier.",
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-enterprise.js && node tests/test-iot.js"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "mcp-server",
18
+ "model-context-protocol",
19
+ "guesty",
20
+ "property-management",
21
+ "short-term-rental",
22
+ "airbnb",
23
+ "vrbo",
24
+ "vacation-rental",
25
+ "ai-agent",
26
+ "pms",
27
+ "iot",
28
+ "enterprise",
29
+ "hospitality",
30
+ "real-estate",
31
+ "automation"
32
+ ],
33
+ "author": "DLJ Properties",
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "latest",
37
+ "@vercel/analytics": "^2.0.1",
38
+ "express": "^5.2.1"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/DLJRealty/guesty-mcp-server.git"
43
+ },
44
+ "homepage": "https://github.com/DLJRealty/guesty-mcp-server",
45
+ "mcpName": "io.github.DLJRealty/guesty",
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ }
49
+ }
package/src/license.js CHANGED
@@ -1,20 +1,46 @@
1
1
  /**
2
2
  * Guesty MCP Server — License Key System (v2)
3
- *
3
+ *
4
+ * v0.9.2 (2026-05-21): PAID_TIERS_LIVE=false. Paid-tier prefix keys
5
+ * (gmcp_pro_*, gmcp_biz_*, gmcp_ent_*, test_pro/biz/ent) are recognized
6
+ * but tool access is REFUSED with "NOT YET WIRED — PAID TIERS LAUNCH v1.0"
7
+ * message until v1.0 ships Stripe-backed activation. Free tier
8
+ * (23 read-only tools) is fully functional and unchanged. Closes the
9
+ * prefix-match self-mint exploit window for pre-launch OSS readers.
10
+ *
4
11
  * 3-Layer Monetization Model (Danny-approved 2026-04-06):
5
12
  * - Layer 1: MCP Server = operations/data tool (FREE, lead gen)
6
- * - Layer 2: Guesty Copilot = SaaS platform (PAID)
13
+ * - Layer 2: Guesty Copilot = SaaS platform (PAID — v1.0)
7
14
  * - Layer 3: DLJ Managed AI = premium service (our real IP)
8
- *
15
+ *
9
16
  * FREE tier: Read-only operations data — reservations, listings, calendar,
10
17
  * financials, tasks, guest lookup, pricing, occupancy, channels, photos.
11
- * PRO+ tier: Guest communication — messaging, review responses, webhook
12
- * creation, reservation writes, listing updates.
13
- *
18
+ * PRO+ tier (v1.0): Guest communication — messaging, review responses,
19
+ * webhook creation, reservation writes, listing updates. Stripe-backed.
20
+ *
14
21
  * Reads GUESTY_MCP_LICENSE_KEY from env.
15
22
  * No key or invalid key = free tier (operations data only).
23
+ * Paid-prefix key with PAID_TIERS_LIVE=false = free tools + refusal message
24
+ * on any paid-tier tool call.
16
25
  */
17
26
 
27
+ // v0.9.2 paid-tier kill-switch.
28
+ // Flip to true ONLY when Stripe webhook + signed-key validation ships in v1.0.
29
+ // While false: paid-prefix keys are accepted as "paid_not_yet_wired" tier,
30
+ // which has the same tool access as free + emits a refusal message on any
31
+ // paid-tier tool call. Prevents prefix-match self-mint exploit.
32
+ const PAID_TIERS_LIVE = false;
33
+
34
+ // Verbatim refusal message — matches HN launch body and README callout.
35
+ // Keep wording stable; HN/Marketing reference this exact string.
36
+ const PAID_TIERS_NOT_WIRED_MSG =
37
+ "NOT YET WIRED — PAID TIERS LAUNCH v1.0\n\n" +
38
+ "The license key you provided is recognized as a paid-tier prefix, but " +
39
+ "paid-tier validation is not yet active in v0.9.2. Free tier (23 read-only " +
40
+ "tools) remains fully functional and unchanged. Paid tiers " +
41
+ "(Pro/Business/Enterprise) ship in v1.0 next week with full Stripe-backed " +
42
+ "activation. Track: https://github.com/DLJRealty/guesty-mcp-server";
43
+
18
44
  // Free tier: read-only operations and data tools
19
45
  const FREE_TOOLS = [
20
46
  // Reservations (read-only)
@@ -50,6 +76,23 @@ const FREE_TOOLS = [
50
76
  "get_webhooks",
51
77
  ];
52
78
 
79
+ // Enterprise tier: IoT + property aggregator tools (4 total)
80
+ // These ship in active validation per data-integrity gate; require Enterprise license.
81
+ const ENT_TOOLS = [
82
+ "get_readiness_score",
83
+ "get_property_health",
84
+ "submit_checkout_photos",
85
+ "get_maintenance_alerts",
86
+ ];
87
+
88
+ // Business tier: operational/SLA features the binary advertises (consumed by getTierInfo).
89
+ // No per-call tool gating — Business unlocks the same 39 base tools as Pro plus the
90
+ // multi-account license terms and priority support promised by guestycopilot.com pricing.
91
+ const BIZ_FEATURES = {
92
+ multiAccountLicense: true,
93
+ prioritySupport: true,
94
+ };
95
+
53
96
  // PRO+ gated tools: guest communication, writes, and real-time events
54
97
  // send_guest_message, respond_to_review, create_webhook, delete_webhook,
55
98
  // create_reservation, update_reservation, create_reservation_note,
@@ -58,11 +101,21 @@ const FREE_TOOLS = [
58
101
  // get_conversations (contains message content)
59
102
 
60
103
  // Simple key-to-tier mapping
61
- // Production: Stripe webhook or DB lookup
62
- // For now: keys prefixed gmcp_pro_*, gmcp_biz_*, gmcp_ent_*
104
+ // v0.9.2: when PAID_TIERS_LIVE=false, any paid-prefix key resolves to
105
+ // "paid_not_yet_wired" free tool access + refusal on paid tool calls.
106
+ // v1.0 (planned): Stripe webhook + signed-key DB lookup replaces prefix match.
63
107
  function resolveTier(licenseKey) {
64
108
  if (!licenseKey) return "free";
65
109
  const key = licenseKey.trim();
110
+ const isPaidPrefix =
111
+ key.startsWith("gmcp_ent_") ||
112
+ key.startsWith("gmcp_biz_") ||
113
+ key.startsWith("gmcp_pro_") ||
114
+ key === "test_pro" ||
115
+ key === "test_biz" ||
116
+ key === "test_ent";
117
+ // v0.9.2 kill-switch: paid keys recognized but refused.
118
+ if (isPaidPrefix && !PAID_TIERS_LIVE) return "paid_not_yet_wired";
66
119
  if (key.startsWith("gmcp_ent_")) return "enterprise";
67
120
  if (key.startsWith("gmcp_biz_")) return "business";
68
121
  if (key.startsWith("gmcp_pro_")) return "pro";
@@ -78,8 +131,16 @@ function getTier() {
78
131
 
79
132
  function isToolAllowed(toolName) {
80
133
  const tier = getTier();
81
- if (tier !== "free") return true;
82
- return FREE_TOOLS.includes(toolName);
134
+ // v0.9.2: "paid_not_yet_wired" has same tool surface as free.
135
+ if (tier === "free" || tier === "paid_not_yet_wired") {
136
+ return FREE_TOOLS.includes(toolName);
137
+ }
138
+ // Enterprise-tier tools require enterprise license
139
+ if (ENT_TOOLS.includes(toolName)) {
140
+ return tier === "enterprise";
141
+ }
142
+ // All other paid tools (Pro+) allowed for pro / business / enterprise
143
+ return true;
83
144
  }
84
145
 
85
146
  function getTierInfo() {
@@ -88,23 +149,51 @@ function getTierInfo() {
88
149
  // (get_property_health, submit_checkout_photos, get_maintenance_alerts) = 43.
89
150
  // Updated 2026-04-17 for Enterprise Tier MVP merge (Owner msg 6406).
90
151
  const totalTools = 43;
152
+ const entToolCount = ENT_TOOLS.length;
153
+ const baseToolCount = totalTools - entToolCount; // 39
154
+ // v0.9.2: paid_not_yet_wired gets same accessible surface as free.
155
+ const accessibleCount =
156
+ tier === "free" || tier === "paid_not_yet_wired" ? FREE_TOOLS.length :
157
+ tier === "enterprise" ? totalTools :
158
+ baseToolCount; // pro / business
91
159
  return {
92
160
  tier,
93
161
  hasKey: !!process.env.GUESTY_MCP_LICENSE_KEY,
94
162
  freeToolCount: FREE_TOOLS.length,
95
- gatedToolCount: totalTools - FREE_TOOLS.length,
96
- unlocked: tier !== "free",
163
+ baseToolCount,
164
+ entToolCount,
165
+ accessibleToolCount: accessibleCount,
166
+ gatedToolCount: totalTools - accessibleCount,
167
+ unlocked: tier !== "free" && tier !== "paid_not_yet_wired",
168
+ paidTiersLive: PAID_TIERS_LIVE,
169
+ paidKeyDetected: tier === "paid_not_yet_wired",
170
+ bizFeatures: tier === "business" || tier === "enterprise" ? BIZ_FEATURES : null,
97
171
  };
98
172
  }
99
173
 
100
174
  function gatedHandler(toolName, handler) {
101
175
  return async (params) => {
102
176
  if (!isToolAllowed(toolName)) {
103
- const msg = "This tool (" + toolName + ") requires a Pro or higher license. " +
104
- "Free tier includes " + FREE_TOOLS.length + " operations and data tools. " +
105
- "Guest messaging, review responses, and write operations require Pro+. " +
106
- "Upgrade at https://guestycopilot.com/pricing -- " +
107
- "Set GUESTY_MCP_LICENSE_KEY env var to unlock all 43 tools.";
177
+ const tier = getTier();
178
+ let msg;
179
+ // v0.9.2: paid-key supplied but tiers not wired yet refusal-with-context.
180
+ if (tier === "paid_not_yet_wired") {
181
+ msg = PAID_TIERS_NOT_WIRED_MSG;
182
+ } else if (tier === "free") {
183
+ msg = "This tool (" + toolName + ") requires a Pro or higher license. " +
184
+ "Free tier includes " + FREE_TOOLS.length + " operations and data tools. " +
185
+ "Guest messaging, review responses, and write operations require Pro+. " +
186
+ "Upgrade at https://guestycopilot.com/pricing -- " +
187
+ "Set GUESTY_MCP_LICENSE_KEY env var to unlock all 43 tools.";
188
+ } else if (ENT_TOOLS.includes(toolName)) {
189
+ msg = "This tool (" + toolName + ") requires an Enterprise license. " +
190
+ "Your current tier (" + tier + ") includes the 39 base Guesty tools. " +
191
+ "Enterprise adds IoT readiness, property health, checkout photo intake, " +
192
+ "and maintenance alerts (4 additional tools). " +
193
+ "Talk to us at https://guestycopilot.com/pricing";
194
+ } else {
195
+ msg = "This tool (" + toolName + ") is not available in your current tier (" + tier + ").";
196
+ }
108
197
  return {
109
198
  content: [{ type: "text", text: msg }],
110
199
  isError: true,
@@ -114,4 +203,14 @@ function gatedHandler(toolName, handler) {
114
203
  };
115
204
  }
116
205
 
117
- export { getTier, isToolAllowed, getTierInfo, gatedHandler, FREE_TOOLS };
206
+ export {
207
+ getTier,
208
+ isToolAllowed,
209
+ getTierInfo,
210
+ gatedHandler,
211
+ FREE_TOOLS,
212
+ ENT_TOOLS,
213
+ BIZ_FEATURES,
214
+ PAID_TIERS_LIVE,
215
+ PAID_TIERS_NOT_WIRED_MSG,
216
+ };
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Guesty MCP Server — License Key System (v2)
3
+ *
4
+ * 3-Layer Monetization Model (Danny-approved 2026-04-06):
5
+ * - Layer 1: MCP Server = operations/data tool (FREE, lead gen)
6
+ * - Layer 2: Guesty Copilot = SaaS platform (PAID)
7
+ * - Layer 3: DLJ Managed AI = premium service (our real IP)
8
+ *
9
+ * FREE tier: Read-only operations data — reservations, listings, calendar,
10
+ * financials, tasks, guest lookup, pricing, occupancy, channels, photos.
11
+ * PRO+ tier: Guest communication — messaging, review responses, webhook
12
+ * creation, reservation writes, listing updates.
13
+ *
14
+ * Reads GUESTY_MCP_LICENSE_KEY from env.
15
+ * No key or invalid key = free tier (operations data only).
16
+ */
17
+
18
+ // Free tier: read-only operations and data tools
19
+ const FREE_TOOLS = [
20
+ // Reservations (read-only)
21
+ "get_reservations",
22
+ "search_reservations",
23
+ "get_reservation_financials",
24
+ // Listings (read-only)
25
+ "get_listing",
26
+ "get_listing_occupancy",
27
+ "get_listing_pricing",
28
+ "get_photos",
29
+ // Guests (read-only)
30
+ "get_guests",
31
+ "get_guest_by_id",
32
+ // Calendar (read-only)
33
+ "get_calendar",
34
+ "get_calendar_blocks",
35
+ // Financials (read-only)
36
+ "get_financials",
37
+ "get_owner_statements",
38
+ "get_expenses",
39
+ "get_revenue_summary",
40
+ // Operations (read-only)
41
+ "get_tasks",
42
+ "get_channels",
43
+ "get_automation_rules",
44
+ "get_custom_fields",
45
+ "get_account_info",
46
+ "get_supported_languages",
47
+ // Reviews (read-only)
48
+ "get_reviews",
49
+ // Webhooks (read-only)
50
+ "get_webhooks",
51
+ ];
52
+
53
+ // PRO+ gated tools: guest communication, writes, and real-time events
54
+ // send_guest_message, respond_to_review, create_webhook, delete_webhook,
55
+ // create_reservation, update_reservation, create_reservation_note,
56
+ // update_pricing, update_calendar, update_listing, update_photos,
57
+ // update_listing_pricing, create_expense, create_task,
58
+ // get_conversations (contains message content)
59
+
60
+ // Simple key-to-tier mapping
61
+ // Production: Stripe webhook or DB lookup
62
+ // For now: keys prefixed gmcp_pro_*, gmcp_biz_*, gmcp_ent_*
63
+ function resolveTier(licenseKey) {
64
+ if (!licenseKey) return "free";
65
+ const key = licenseKey.trim();
66
+ if (key.startsWith("gmcp_ent_")) return "enterprise";
67
+ if (key.startsWith("gmcp_biz_")) return "business";
68
+ if (key.startsWith("gmcp_pro_")) return "pro";
69
+ if (key === "test_pro") return "pro";
70
+ if (key === "test_biz") return "business";
71
+ if (key === "test_ent") return "enterprise";
72
+ return "free";
73
+ }
74
+
75
+ function getTier() {
76
+ return resolveTier(process.env.GUESTY_MCP_LICENSE_KEY);
77
+ }
78
+
79
+ function isToolAllowed(toolName) {
80
+ const tier = getTier();
81
+ if (tier !== "free") return true;
82
+ return FREE_TOOLS.includes(toolName);
83
+ }
84
+
85
+ function getTierInfo() {
86
+ const tier = getTier();
87
+ // 39 Guesty core tools + 1 IoT (get_readiness_score) + 3 Enterprise aggregators
88
+ // (get_property_health, submit_checkout_photos, get_maintenance_alerts) = 43.
89
+ // Updated 2026-04-17 for Enterprise Tier MVP merge (Owner msg 6406).
90
+ const totalTools = 43;
91
+ return {
92
+ tier,
93
+ hasKey: !!process.env.GUESTY_MCP_LICENSE_KEY,
94
+ freeToolCount: FREE_TOOLS.length,
95
+ gatedToolCount: totalTools - FREE_TOOLS.length,
96
+ unlocked: tier !== "free",
97
+ };
98
+ }
99
+
100
+ function gatedHandler(toolName, handler) {
101
+ return async (params) => {
102
+ if (!isToolAllowed(toolName)) {
103
+ const msg = "This tool (" + toolName + ") requires a Pro or higher license. " +
104
+ "Free tier includes " + FREE_TOOLS.length + " operations and data tools. " +
105
+ "Guest messaging, review responses, and write operations require Pro+. " +
106
+ "Upgrade at https://guestycopilot.com/pricing -- " +
107
+ "Set GUESTY_MCP_LICENSE_KEY env var to unlock all 43 tools.";
108
+ return {
109
+ content: [{ type: "text", text: msg }],
110
+ isError: true,
111
+ };
112
+ }
113
+ return handler(params);
114
+ };
115
+ }
116
+
117
+ export { getTier, isToolAllowed, getTierInfo, gatedHandler, FREE_TOOLS };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Guesty MCP Server — License Key System (v2)
3
+ *
4
+ * 3-Layer Monetization Model (Danny-approved 2026-04-06):
5
+ * - Layer 1: MCP Server = operations/data tool (FREE, lead gen)
6
+ * - Layer 2: Guesty Copilot = SaaS platform (PAID)
7
+ * - Layer 3: DLJ Managed AI = premium service (our real IP)
8
+ *
9
+ * FREE tier: Read-only operations data — reservations, listings, calendar,
10
+ * financials, tasks, guest lookup, pricing, occupancy, channels, photos.
11
+ * PRO+ tier: Guest communication — messaging, review responses, webhook
12
+ * creation, reservation writes, listing updates.
13
+ *
14
+ * Reads GUESTY_MCP_LICENSE_KEY from env.
15
+ * No key or invalid key = free tier (operations data only).
16
+ */
17
+
18
+ // Free tier: read-only operations and data tools
19
+ const FREE_TOOLS = [
20
+ // Reservations (read-only)
21
+ "get_reservations",
22
+ "search_reservations",
23
+ "get_reservation_financials",
24
+ // Listings (read-only)
25
+ "get_listing",
26
+ "get_listing_occupancy",
27
+ "get_listing_pricing",
28
+ "get_photos",
29
+ // Guests (read-only)
30
+ "get_guests",
31
+ "get_guest_by_id",
32
+ // Calendar (read-only)
33
+ "get_calendar",
34
+ "get_calendar_blocks",
35
+ // Financials (read-only)
36
+ "get_financials",
37
+ "get_owner_statements",
38
+ "get_expenses",
39
+ "get_revenue_summary",
40
+ // Operations (read-only)
41
+ "get_tasks",
42
+ "get_channels",
43
+ "get_automation_rules",
44
+ "get_custom_fields",
45
+ "get_account_info",
46
+ "get_supported_languages",
47
+ // Reviews (read-only)
48
+ "get_reviews",
49
+ // Webhooks (read-only)
50
+ "get_webhooks",
51
+ ];
52
+
53
+ // Enterprise tier: IoT + property aggregator tools (4 total)
54
+ // These ship in active validation per data-integrity gate; require Enterprise license.
55
+ const ENT_TOOLS = [
56
+ "get_readiness_score",
57
+ "get_property_health",
58
+ "submit_checkout_photos",
59
+ "get_maintenance_alerts",
60
+ ];
61
+
62
+ // Business tier: operational/SLA features the binary advertises (consumed by getTierInfo).
63
+ // No per-call tool gating — Business unlocks the same 39 base tools as Pro plus the
64
+ // multi-account license terms and priority support promised by guestycopilot.com pricing.
65
+ const BIZ_FEATURES = {
66
+ multiAccountLicense: true,
67
+ prioritySupport: true,
68
+ };
69
+
70
+ // PRO+ gated tools: guest communication, writes, and real-time events
71
+ // send_guest_message, respond_to_review, create_webhook, delete_webhook,
72
+ // create_reservation, update_reservation, create_reservation_note,
73
+ // update_pricing, update_calendar, update_listing, update_photos,
74
+ // update_listing_pricing, create_expense, create_task,
75
+ // get_conversations (contains message content)
76
+
77
+ // Simple key-to-tier mapping
78
+ // Production: Stripe webhook or DB lookup
79
+ // For now: keys prefixed gmcp_pro_*, gmcp_biz_*, gmcp_ent_*
80
+ function resolveTier(licenseKey) {
81
+ if (!licenseKey) return "free";
82
+ const key = licenseKey.trim();
83
+ if (key.startsWith("gmcp_ent_")) return "enterprise";
84
+ if (key.startsWith("gmcp_biz_")) return "business";
85
+ if (key.startsWith("gmcp_pro_")) return "pro";
86
+ if (key === "test_pro") return "pro";
87
+ if (key === "test_biz") return "business";
88
+ if (key === "test_ent") return "enterprise";
89
+ return "free";
90
+ }
91
+
92
+ function getTier() {
93
+ return resolveTier(process.env.GUESTY_MCP_LICENSE_KEY);
94
+ }
95
+
96
+ function isToolAllowed(toolName) {
97
+ const tier = getTier();
98
+ if (tier === "free") {
99
+ return FREE_TOOLS.includes(toolName);
100
+ }
101
+ // Enterprise-tier tools require enterprise license
102
+ if (ENT_TOOLS.includes(toolName)) {
103
+ return tier === "enterprise";
104
+ }
105
+ // All other paid tools (Pro+) allowed for pro / business / enterprise
106
+ return true;
107
+ }
108
+
109
+ function getTierInfo() {
110
+ const tier = getTier();
111
+ // 39 Guesty core tools + 1 IoT (get_readiness_score) + 3 Enterprise aggregators
112
+ // (get_property_health, submit_checkout_photos, get_maintenance_alerts) = 43.
113
+ // Updated 2026-04-17 for Enterprise Tier MVP merge (Owner msg 6406).
114
+ const totalTools = 43;
115
+ const entToolCount = ENT_TOOLS.length;
116
+ const baseToolCount = totalTools - entToolCount; // 39
117
+ const accessibleCount =
118
+ tier === "free" ? FREE_TOOLS.length :
119
+ tier === "enterprise" ? totalTools :
120
+ baseToolCount; // pro / business
121
+ return {
122
+ tier,
123
+ hasKey: !!process.env.GUESTY_MCP_LICENSE_KEY,
124
+ freeToolCount: FREE_TOOLS.length,
125
+ baseToolCount,
126
+ entToolCount,
127
+ accessibleToolCount: accessibleCount,
128
+ gatedToolCount: totalTools - accessibleCount,
129
+ unlocked: tier !== "free",
130
+ bizFeatures: tier === "business" || tier === "enterprise" ? BIZ_FEATURES : null,
131
+ };
132
+ }
133
+
134
+ function gatedHandler(toolName, handler) {
135
+ return async (params) => {
136
+ if (!isToolAllowed(toolName)) {
137
+ const tier = getTier();
138
+ let msg;
139
+ if (tier === "free") {
140
+ msg = "This tool (" + toolName + ") requires a Pro or higher license. " +
141
+ "Free tier includes " + FREE_TOOLS.length + " operations and data tools. " +
142
+ "Guest messaging, review responses, and write operations require Pro+. " +
143
+ "Upgrade at https://guestycopilot.com/pricing -- " +
144
+ "Set GUESTY_MCP_LICENSE_KEY env var to unlock all 43 tools.";
145
+ } else if (ENT_TOOLS.includes(toolName)) {
146
+ msg = "This tool (" + toolName + ") requires an Enterprise license. " +
147
+ "Your current tier (" + tier + ") includes the 39 base Guesty tools. " +
148
+ "Enterprise adds IoT readiness, property health, checkout photo intake, " +
149
+ "and maintenance alerts (4 additional tools). " +
150
+ "Talk to us at https://guestycopilot.com/pricing";
151
+ } else {
152
+ msg = "This tool (" + toolName + ") is not available in your current tier (" + tier + ").";
153
+ }
154
+ return {
155
+ content: [{ type: "text", text: msg }],
156
+ isError: true,
157
+ };
158
+ }
159
+ return handler(params);
160
+ };
161
+ }
162
+
163
+ export { getTier, isToolAllowed, getTierInfo, gatedHandler, FREE_TOOLS, ENT_TOOLS, BIZ_FEATURES };