@elitedcs/ghl-mcp 3.3.1 → 3.4.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.4.1 — fixes from real-world testing of v3.4.0 tools
4
+
5
+ **175 tools across 38 modules. Bundle: 275.2 KB.**
6
+
7
+ Two bugs caught when exercising v3.4.0's `health_check` and `validate_workflow` end-to-end against a real sandbox workflow:
8
+
9
+ 1. **`health_check` API-key probe used the wrong endpoint.** `/locations/search` is agency-level — sub-account Private Integration keys (the kind buyers have) get 403, not 200, even when the key is fine. Fixed to use `/locations/{locationId}` which sub-account keys can call. Also added explicit handling for 403 with a clearer message ("doesn't have access to this location" vs. just "limited").
10
+
11
+ 2. **`validate_workflow` errored when checking workflow references.** Called `builderClient.listWorkflowsFull()`, which doesn't exist — the actual method is `listWorkflows()`. Any workflow containing a `remove_from_workflow` action or a trigger with an `add_to_workflow` reference would crash the validator. Fixed.
12
+
13
+ Also: `src/index.ts` startup `validateApiKey()` had the same wrong-endpoint bug as `health_check`; fixed for consistency. Buyers will now see "API key validated" or a clear 403 message on startup instead of silent ambiguity.
14
+
15
+ ### Verified end-to-end
16
+ Re-ran the spawn-handshake test against QA Test Clinic v3:
17
+ - `health_check` returns 5/5 PASS ("All systems go.")
18
+ - `validate_workflow` on the CLL Onboarding Form Submitted workflow correctly scans 2 references (form.id + self-referencing workflow.id), reports 0 issues, surfaces the self-reference as an informational note.
19
+
20
+ ### Files changed
21
+ - `src/tools/diagnostics.ts` — endpoint fix + 403 handling
22
+ - `src/tools/validators.ts` — method name fix
23
+ - `src/index.ts` — same endpoint fix in startup probe
24
+
25
+ ## 3.4.0 — health_check tool + validate_workflow expansion + smoke-test CI
26
+
27
+ **175 tools across 38 modules. Bundle: 274.6 KB.**
28
+
29
+ Three improvements that buyers don't have to ask for.
30
+
31
+ ### `health_check` tool (NEW)
32
+ A single diagnostic that runs five probes in parallel and reports a structured pass/fail/warn list:
33
+
34
+ 1. **npm registry + version** — is the registry reachable? Are you on the latest version?
35
+ 2. **GHL API key** — does it validate against `/locations/search`? (catches revoked/expired keys)
36
+ 3. **Default location** — can you actually read it? Reports the location name on success.
37
+ 4. **Firebase auth** — refreshes the workflow-builder token; reports `skip` if not configured, `pass`/`fail` if it is.
38
+ 5. **Token registry** — how many sub-accounts are registered for `switch_location`?
39
+
40
+ Use case: "is my install OK?" first-line troubleshooting, post-restart sanity check after credentials rotate, support back-and-forth with buyers.
41
+
42
+ ### `validate_workflow` expansion
43
+ The v3.3.1 validator now also checks **form IDs**, **calendar IDs**, and **survey IDs** in trigger conditions. Coverage went from 5 → 8 reference categories:
44
+ - Pipeline IDs
45
+ - Pipeline Stage IDs
46
+ - Custom Field IDs
47
+ - User IDs
48
+ - Workflow IDs
49
+ - **Form IDs** (NEW — checks `form.id` in form_submission triggers)
50
+ - **Calendar IDs** (NEW — checks `calendar.id` in appointment / customer_appointment triggers)
51
+ - **Survey IDs** (NEW — checks `survey.id` in survey_submission triggers)
52
+
53
+ ### Post-publish smoke-test CI
54
+ A new job in `.github/workflows/publish.yml` runs after npm publish: waits for registry propagation (~30s typical), then `npx`-installs the just-published version in a clean machine, spawns it with the full MCP handshake (initialize → notifications/initialized → tools/list), and asserts that `get_mcp_version` and `setup_ghl_mcp` show up in the response.
55
+
56
+ Catches the kind of publish regression where the package builds locally and uploads to npm fine but fails to boot on a buyer's machine because of a missing file, bad permission, or runtime error during tool registration. Adds ~2 minutes to the release cycle, makes future buyers safer.
57
+
58
+ ### Tool count tidy-up
59
+ Fixed stale "171 tools" / "165 tools" strings in `setup-tool.ts` that buyers saw in the bootstrap-mode setup message. Now correctly reports 175 (full) / 167 (without workflow builder).
60
+
61
+ ### Files changed
62
+ - `src/tools/diagnostics.ts` — NEW: `registerDiagnosticTools` + `health_check`.
63
+ - `src/workflow-builder-client.ts` — public `checkAuth()` method for Firebase probe.
64
+ - `src/tools/validators.ts` — form/calendar/survey reference extraction + lookups.
65
+ - `src/tools/index.ts` — registered diagnostics module.
66
+ - `src/setup-tool.ts` — corrected tool counts (171 → 175, 165 → 167).
67
+ - `.github/workflows/publish.yml` — added smoke-test job.
68
+
3
69
  ## 3.3.1 — validate_workflow + catalogue in npm package
4
70
 
5
71
  **174 tools across 37 modules. Bundle: 266.2 KB.**
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GHL Command — GoHighLevel MCP Server
2
2
 
3
- **Full GoHighLevel API access for Claude.** 174 tools across 36 modules — manage contacts, conversations, pipelines, calendars, funnels, workflows, invoices, custom objects, webhooks, and more. **Includes full workflow builder, funnel/page editor, form builder, pipeline builder, bulk operations, account export, and workflow cloning** — capabilities no other GHL tool offers.
3
+ **Full GoHighLevel API access for Claude.** 175 tools across 36 modules — manage contacts, conversations, pipelines, calendars, funnels, workflows, invoices, custom objects, webhooks, and more. **Includes full workflow builder, funnel/page editor, form builder, pipeline builder, bulk operations, account export, and workflow cloning** — capabilities no other GHL tool offers.
4
4
 
5
5
  **Distributed via npm as [`@elitedcs/ghl-mcp`](https://www.npmjs.com/package/@elitedcs/ghl-mcp).** Buyers install with one config block — no git, no Node.js setup, no terminal commands. Updates flow automatically (`npx @latest` re-resolves on every Claude restart).
6
6
 
@@ -98,7 +98,7 @@ Run setup_ghl_mcp to activate GHL Command:
98
98
  ghl_location_id: YOUR_LOCATION_ID
99
99
  ```
100
100
 
101
- Approve the tool call. Server validates your license, verifies your GHL credentials, writes them to a per-user config file. **Quit Claude one more time and reopen** — all 166 tools are now unlocked.
101
+ Approve the tool call. Server validates your license, verifies your GHL credentials, writes them to a per-user config file. **Quit Claude one more time and reopen** — all 167 tools are now unlocked.
102
102
 
103
103
  ### 4. Try it
104
104
 
@@ -138,7 +138,7 @@ https://app.gohighlevel.com/v2/location/YOUR_LOCATION_ID/dashboard
138
138
 
139
139
  ## Enable Workflow Builder (Optional)
140
140
 
141
- The 8 workflow builder tools use GHL'''s internal API and require Firebase credentials. Without them, the other 166 tools work fine — you just won'''t have workflow editing.
141
+ The 8 workflow builder tools use GHL'''s internal API and require Firebase credentials. Without them, the other 167 tools work fine — you just won'''t have workflow editing.
142
142
 
143
143
  Grab the three values from your GHL browser session, then re-run `setup_ghl_mcp` with them:
144
144
 
@@ -167,7 +167,9 @@ Re-run setup_ghl_mcp with workflow builder:
167
167
 
168
168
  ---
169
169
 
170
- ## Tools (174)
170
+ ## Tools (175)
171
+
172
+ > **v3.4.0 adds `health_check`** — one diagnostic that reports npm registry + version, GHL API key validity, default location reachability, Firebase auth status, and token registry in a single structured report. Plus `validate_workflow` (v3.3.1) now covers form, calendar, and survey IDs in addition to pipelines/stages/custom-fields/users/workflows.
171
173
 
172
174
  ### CRM & Contacts (15 tools)
173
175
 
@@ -411,11 +413,12 @@ Re-run setup_ghl_mcp with workflow builder:
411
413
  | `get_template_questionnaire` | Get the questionnaire for a template — present to user conversationally |
412
414
  | `deploy_template` | Deploy a complete sub-account setup from a template (tags, fields, pipelines, workflows, forms). Supports dry-run mode. |
413
415
 
414
- ### Meta (1 tool)
416
+ ### Meta & Diagnostics (2 tools)
415
417
 
416
418
  | Tool | What It Does |
417
419
  |---|---|
418
420
  | `get_mcp_version` | Check installed version against the latest published to npm. Confirms an upgrade landed after restarting Claude. Available even before GHL credentials are configured. |
421
+ | `health_check` | Run a full health check: npm registry + version status, GHL API key validity, default location reachability, Firebase auth status, token registry presence. Use when something feels broken. |
419
422
 
420
423
  ### Other Modules
421
424
 
package/dist/index.js CHANGED
@@ -31,8 +31,8 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "@elitedcs/ghl-mcp",
34
- version: "3.3.1",
35
- description: "GoHighLevel MCP Server for Claude. 174 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
34
+ version: "3.4.1",
35
+ description: "GoHighLevel MCP Server for Claude. 175 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
36
36
  main: "dist/index.js",
37
37
  bin: {
38
38
  "ghl-mcp": "dist/index.js"
@@ -869,6 +869,19 @@ var WorkflowBuilderClient = class _WorkflowBuilderClient {
869
869
  getApiKey() {
870
870
  return this.apiKey;
871
871
  }
872
+ /**
873
+ * Probe Firebase auth by refreshing the ID token. Used by the health_check
874
+ * diagnostic tool. Does not touch GHL backend — just exchanges the refresh
875
+ * token at securetoken.googleapis.com.
876
+ */
877
+ async checkAuth() {
878
+ try {
879
+ await this.getIdToken();
880
+ return { ok: true };
881
+ } catch (error) {
882
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
883
+ }
884
+ }
872
885
  /**
873
886
  * Create from environment variables, with optional token registry fallback
874
887
  */
@@ -5960,6 +5973,15 @@ function extractFromTrigger(trigger, refs) {
5960
5973
  case "workflow.id":
5961
5974
  refs.push({ kind: "workflow", id: v, where: childWhere });
5962
5975
  break;
5976
+ case "form.id":
5977
+ refs.push({ kind: "form", id: v, where: childWhere });
5978
+ break;
5979
+ case "calendar.id":
5980
+ refs.push({ kind: "calendar", id: v, where: childWhere });
5981
+ break;
5982
+ case "survey.id":
5983
+ refs.push({ kind: "survey", id: v, where: childWhere });
5984
+ break;
5963
5985
  }
5964
5986
  }
5965
5987
  }
@@ -6056,12 +6078,28 @@ function registerValidatorTools(server2, client, builderClient) {
6056
6078
  if (!builderClient) return;
6057
6079
  server2.tool(
6058
6080
  "validate_workflow",
6059
- "Pre-flight ID validation for a deployed GHL workflow. Scans every trigger and action for references to pipelines, pipeline stages, custom fields, users, and other workflows; verifies each ID exists in the current location. Use this BEFORE publish_workflow when a workflow has been edited, or whenever a published workflow stops behaving as expected. Catches the silent-failure bug where invalid IDs make GHL skip all subsequent actions without warning. Returns a structured report of valid + missing references.",
6081
+ "Pre-flight ID validation for a deployed GHL workflow. Scans every trigger and action for references to pipelines, pipeline stages, custom fields, users, workflows, forms, calendars, and surveys; verifies each ID exists in the current location. Use this BEFORE publish_workflow when a workflow has been edited, or whenever a published workflow stops behaving as expected. Catches the silent-failure bug where invalid IDs make GHL skip all subsequent actions without warning. Returns a structured report of valid + missing references.",
6060
6082
  {
6061
6083
  workflowId: import_zod40.z.string().describe("The workflow ID to validate.")
6062
6084
  },
6063
6085
  async ({ workflowId }) => {
6064
6086
  try {
6087
+ let collectIds2 = function(envelope, listKey) {
6088
+ const ids = /* @__PURE__ */ new Set();
6089
+ if (envelope && typeof envelope === "object") {
6090
+ const e = envelope;
6091
+ const arr = Array.isArray(e[listKey]) ? e[listKey] : Array.isArray(envelope) ? envelope : [];
6092
+ for (const item of arr) {
6093
+ if (typeof item === "object" && item !== null) {
6094
+ const o = item;
6095
+ const id = typeof o.id === "string" ? o.id : typeof o._id === "string" ? o._id : null;
6096
+ if (id) ids.add(id);
6097
+ }
6098
+ }
6099
+ }
6100
+ return ids;
6101
+ };
6102
+ var collectIds = collectIds2;
6065
6103
  const workflow = await builderClient.getWorkflow(workflowId);
6066
6104
  if (!workflow) return errorResponse(new Error(`Workflow ${workflowId} not found`));
6067
6105
  const refs = [];
@@ -6097,7 +6135,16 @@ function registerValidatorTools(server2, client, builderClient) {
6097
6135
  fetches.users = client.get("/users/", { params: { locationId: locationId2 } });
6098
6136
  }
6099
6137
  if (refCategories.has("workflow")) {
6100
- fetches.workflows = builderClient.listWorkflowsFull();
6138
+ fetches.workflows = builderClient.listWorkflows(200);
6139
+ }
6140
+ if (refCategories.has("form")) {
6141
+ fetches.forms = client.get("/forms/", { params: { locationId: locationId2 } });
6142
+ }
6143
+ if (refCategories.has("calendar")) {
6144
+ fetches.calendars = client.get("/calendars/", { params: { locationId: locationId2 } });
6145
+ }
6146
+ if (refCategories.has("survey")) {
6147
+ fetches.surveys = client.get("/surveys/", { params: { locationId: locationId2 } });
6101
6148
  }
6102
6149
  const results = await Promise.allSettled(Object.values(fetches));
6103
6150
  const keys = Object.keys(fetches);
@@ -6154,6 +6201,9 @@ function registerValidatorTools(server2, client, builderClient) {
6154
6201
  }
6155
6202
  }
6156
6203
  }
6204
+ const validFormIds = collectIds2(data.forms, "forms");
6205
+ const validCalendarIds = collectIds2(data.calendars, "calendars");
6206
+ const validSurveyIds = collectIds2(data.surveys, "surveys");
6157
6207
  const findings = [];
6158
6208
  for (const ref of refs) {
6159
6209
  let valid = false;
@@ -6177,6 +6227,15 @@ function registerValidatorTools(server2, client, builderClient) {
6177
6227
  extraMsg = " (self-reference)";
6178
6228
  }
6179
6229
  break;
6230
+ case "form":
6231
+ valid = validFormIds.has(ref.id);
6232
+ break;
6233
+ case "calendar":
6234
+ valid = validCalendarIds.has(ref.id);
6235
+ break;
6236
+ case "survey":
6237
+ valid = validSurveyIds.has(ref.id);
6238
+ break;
6180
6239
  }
6181
6240
  if (!valid) {
6182
6241
  findings.push({
@@ -6212,6 +6271,140 @@ function registerValidatorTools(server2, client, builderClient) {
6212
6271
  );
6213
6272
  }
6214
6273
 
6274
+ // src/version-check.ts
6275
+ var REGISTRY_URL = "https://registry.npmjs.org/@elitedcs/ghl-mcp/latest";
6276
+ async function fetchLatestVersion(timeoutMs = 5e3) {
6277
+ try {
6278
+ const response = await fetch(REGISTRY_URL, {
6279
+ headers: { Accept: "application/json" },
6280
+ signal: AbortSignal.timeout(timeoutMs)
6281
+ });
6282
+ if (!response.ok) return null;
6283
+ const body = await response.json();
6284
+ return typeof body.version === "string" ? body.version : null;
6285
+ } catch {
6286
+ return null;
6287
+ }
6288
+ }
6289
+ async function getVersionStatus(installed) {
6290
+ const latest = await fetchLatestVersion();
6291
+ if (latest === null) {
6292
+ return { installed, latest: null, upToDate: null, fetchError: "Could not reach npm registry" };
6293
+ }
6294
+ return { installed, latest, upToDate: installed === latest };
6295
+ }
6296
+
6297
+ // src/tools/diagnostics.ts
6298
+ function registerDiagnosticTools(server2, installedVersion, client, builderClient, registry2) {
6299
+ server2.tool(
6300
+ "health_check",
6301
+ "Run a full health check of the GHL Command MCP install. Reports on: npm registry + version status, GHL API key validity, default location reachability, Firebase auth status (workflow builder), and token registry. Returns a structured pass/fail/warn list with detail per check. Use this when something feels broken or after credentials change.",
6302
+ {},
6303
+ async () => {
6304
+ const checks = [];
6305
+ const versionPromise = getVersionStatus(installedVersion);
6306
+ const apiKeyPromise = (async () => {
6307
+ const locId = client.defaultLocationId;
6308
+ if (!locId) {
6309
+ return { name: "GHL API key", status: "warn", detail: `Can't validate without a default location ID. Set GHL_LOCATION_ID or run setup_ghl_mcp.` };
6310
+ }
6311
+ try {
6312
+ const response = await fetch(`https://services.leadconnectorhq.com/locations/${locId}`, {
6313
+ method: "GET",
6314
+ headers: {
6315
+ Authorization: `Bearer ${client.getApiKey()}`,
6316
+ "Content-Type": "application/json",
6317
+ Accept: "application/json",
6318
+ Version: "2021-07-28"
6319
+ },
6320
+ signal: AbortSignal.timeout(8e3)
6321
+ });
6322
+ if (response.status === 401) {
6323
+ return { name: "GHL API key", status: "fail", detail: `401 Unauthorized \u2014 key ${client.getApiKeyPrefix()} is invalid or revoked. Run setup_ghl_mcp with a fresh key.` };
6324
+ }
6325
+ if (response.status === 403) {
6326
+ return { name: "GHL API key", status: "fail", detail: `403 Forbidden \u2014 key ${client.getApiKeyPrefix()} doesn't have access to location ${locId}. The key may belong to a different sub-account.` };
6327
+ }
6328
+ if (!response.ok) {
6329
+ return { name: "GHL API key", status: "warn", detail: `HTTP ${response.status} reading /locations/${locId} \u2014 key may be limited or GHL is having issues.` };
6330
+ }
6331
+ return { name: "GHL API key", status: "pass", detail: `Valid (key ${client.getApiKeyPrefix()}).` };
6332
+ } catch (error) {
6333
+ return { name: "GHL API key", status: "warn", detail: `Network error: ${error instanceof Error ? error.message : String(error)}. Server starts anyway; tool calls will report errors when invoked.` };
6334
+ }
6335
+ })();
6336
+ const locationPromise = (async () => {
6337
+ const locId = client.defaultLocationId;
6338
+ if (!locId) {
6339
+ return { name: "Default location", status: "warn", detail: "No default location set. Tools that don't accept locationId arg will fail." };
6340
+ }
6341
+ try {
6342
+ const result = await client.get(`/locations/${locId}`);
6343
+ const loc = result.location ?? result;
6344
+ return { name: "Default location", status: "pass", detail: `${loc?.name ?? "Unknown"} (${locId}) reachable.` };
6345
+ } catch (error) {
6346
+ return { name: "Default location", status: "fail", detail: `Could not read location ${locId}: ${error instanceof Error ? error.message : String(error)}` };
6347
+ }
6348
+ })();
6349
+ const firebasePromise = (async () => {
6350
+ if (!builderClient) {
6351
+ return { name: "Firebase auth (workflow builder)", status: "skip", detail: "Not configured. Workflow builder tools (8 of 174) require ghl_user_id, ghl_firebase_api_key, ghl_firebase_refresh_token. Other 166 tools work fine without." };
6352
+ }
6353
+ const result = await builderClient.checkAuth();
6354
+ if (result.ok) {
6355
+ return { name: "Firebase auth (workflow builder)", status: "pass", detail: "ID token refresh succeeded. Workflow builder tools are usable." };
6356
+ }
6357
+ return { name: "Firebase auth (workflow builder)", status: "fail", detail: `Token refresh failed: ${result.error}. Re-capture Firebase values from GHL DevTools and re-run setup_ghl_mcp.` };
6358
+ })();
6359
+ const registryCheck = (() => {
6360
+ if (!registry2) {
6361
+ return { name: "Token registry", status: "skip", detail: "Not initialized \u2014 using env-var credentials only. switch_location won't auto-swap keys between sub-accounts." };
6362
+ }
6363
+ const locs = registry2.listLocations();
6364
+ if (locs.length === 0) {
6365
+ return { name: "Token registry", status: "warn", detail: "Initialized but empty \u2014 register sub-accounts via register_location for cross-account switching." };
6366
+ }
6367
+ return { name: "Token registry", status: "pass", detail: `${locs.length} sub-account(s) registered.` };
6368
+ })();
6369
+ const [versionStatus, apiKeyCheck, locationCheck, firebaseCheck] = await Promise.all([
6370
+ versionPromise,
6371
+ apiKeyPromise,
6372
+ locationPromise,
6373
+ firebasePromise
6374
+ ]);
6375
+ let versionCheck;
6376
+ if (versionStatus.latest === null) {
6377
+ versionCheck = { name: "npm registry + version", status: "warn", detail: `Installed v${versionStatus.installed}. Could not reach npm registry to check for updates: ${versionStatus.fetchError ?? "unknown"}.` };
6378
+ } else if (versionStatus.upToDate) {
6379
+ versionCheck = { name: "npm registry + version", status: "pass", detail: `Up to date at v${versionStatus.installed}.` };
6380
+ } else {
6381
+ versionCheck = { name: "npm registry + version", status: "warn", detail: `Installed v${versionStatus.installed} but latest is v${versionStatus.latest}. Quit Claude (Cmd+Q) and reopen to upgrade.` };
6382
+ }
6383
+ checks.push(versionCheck, apiKeyCheck, locationCheck, firebaseCheck, registryCheck);
6384
+ const symbols = { pass: "\u2713", fail: "\u2717", warn: "!", skip: "\u2014" };
6385
+ const lines = [];
6386
+ lines.push("GHL Command \u2014 Health Check");
6387
+ lines.push("\u2501".repeat(50));
6388
+ for (const c of checks) {
6389
+ lines.push(`${symbols[c.status]} ${c.name.padEnd(35)} ${c.status.toUpperCase()}`);
6390
+ lines.push(` ${c.detail}`);
6391
+ }
6392
+ lines.push("\u2501".repeat(50));
6393
+ const fails = checks.filter((c) => c.status === "fail").length;
6394
+ const warns = checks.filter((c) => c.status === "warn").length;
6395
+ if (fails === 0 && warns === 0) {
6396
+ lines.push("All systems go.");
6397
+ } else {
6398
+ const parts = [];
6399
+ if (fails > 0) parts.push(`${fails} failure(s)`);
6400
+ if (warns > 0) parts.push(`${warns} warning(s)`);
6401
+ lines.push(parts.join(", ") + " \u2014 see details above.");
6402
+ }
6403
+ return { content: [{ type: "text", text: lines.join("\n") }] };
6404
+ }
6405
+ );
6406
+ }
6407
+
6215
6408
  // src/tools/index.ts
6216
6409
  var publicApiTools = [
6217
6410
  [registerContactTools, "contacts"],
@@ -6255,6 +6448,7 @@ function registerAllTools(server2, client, registry2, mcpVersion) {
6255
6448
  registerPipelineBuilderTools(server2, builderClient);
6256
6449
  registerWorkflowClonerTools(server2, builderClient);
6257
6450
  registerValidatorTools(server2, client, builderClient);
6451
+ registerDiagnosticTools(server2, mcpVersion ?? "unknown", client, builderClient, registry2 ?? null);
6258
6452
  registerLocationSwitcherTools(server2, client, builderClient, registry2, mcpVersion);
6259
6453
  }
6260
6454
 
@@ -6392,7 +6586,7 @@ async function validateFirebase(firebaseKey, refreshToken) {
6392
6586
  function registerSetupTool(server2) {
6393
6587
  server2.tool(
6394
6588
  "setup_ghl_mcp",
6395
- "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 171 tools.",
6589
+ "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 175 tools.",
6396
6590
  {
6397
6591
  email: import_zod42.z.string().email().describe("Email used at purchase."),
6398
6592
  license_key: import_zod42.z.string().min(20).describe("License key from your purchase email."),
@@ -6447,7 +6641,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
6447
6641
  ghl_firebase_api_key: workflowBuilderEnabled ? args.ghl_firebase_api_key?.trim() : void 0,
6448
6642
  ghl_firebase_refresh_token: workflowBuilderEnabled ? args.ghl_firebase_refresh_token?.trim() : void 0
6449
6643
  });
6450
- const toolCount = workflowBuilderEnabled ? "171" : "165";
6644
+ const toolCount = workflowBuilderEnabled ? "175" : "167";
6451
6645
  const wfLine = workflowBuilderEnabled ? "Workflow Builder: enabled." : "Workflow Builder: not configured (optional).";
6452
6646
  return {
6453
6647
  content: [{
@@ -6472,29 +6666,6 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
6472
6666
  );
6473
6667
  }
6474
6668
 
6475
- // src/version-check.ts
6476
- var REGISTRY_URL = "https://registry.npmjs.org/@elitedcs/ghl-mcp/latest";
6477
- async function fetchLatestVersion(timeoutMs = 5e3) {
6478
- try {
6479
- const response = await fetch(REGISTRY_URL, {
6480
- headers: { Accept: "application/json" },
6481
- signal: AbortSignal.timeout(timeoutMs)
6482
- });
6483
- if (!response.ok) return null;
6484
- const body = await response.json();
6485
- return typeof body.version === "string" ? body.version : null;
6486
- } catch {
6487
- return null;
6488
- }
6489
- }
6490
- async function getVersionStatus(installed) {
6491
- const latest = await fetchLatestVersion();
6492
- if (latest === null) {
6493
- return { installed, latest: null, upToDate: null, fetchError: "Could not reach npm registry" };
6494
- }
6495
- return { installed, latest, upToDate: installed === latest };
6496
- }
6497
-
6498
6669
  // src/tools/meta.ts
6499
6670
  function registerMetaTools(server2, installedVersion) {
6500
6671
  server2.tool(
@@ -6603,9 +6774,9 @@ if (inBootstrapMode) {
6603
6774
  }
6604
6775
  }
6605
6776
  async function validateApiKey() {
6606
- if (!apiKey) return;
6777
+ if (!apiKey || !locationId) return;
6607
6778
  try {
6608
- const response = await fetch("https://services.leadconnectorhq.com/locations/search", {
6779
+ const response = await fetch(`https://services.leadconnectorhq.com/locations/${locationId}`, {
6609
6780
  method: "GET",
6610
6781
  headers: {
6611
6782
  Authorization: `Bearer ${apiKey}`,
@@ -6617,6 +6788,9 @@ async function validateApiKey() {
6617
6788
  });
6618
6789
  if (response.status === 401) {
6619
6790
  process.stderr.write("[ghl-mcp] WARNING: API key is invalid (401 Unauthorized). Tools will fail.\n");
6791
+ } else if (response.status === 403) {
6792
+ process.stderr.write(`[ghl-mcp] WARNING: API key doesn't have access to location ${locationId} (403 Forbidden).
6793
+ `);
6620
6794
  } else if (response.ok) {
6621
6795
  process.stderr.write("[ghl-mcp] API key validated.\n");
6622
6796
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.3.1",
4
- "description": "GoHighLevel MCP Server for Claude. 174 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
3
+ "version": "3.4.1",
4
+ "description": "GoHighLevel MCP Server for Claude. 175 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "ghl-mcp": "dist/index.js"