@elitedcs/ghl-mcp 3.30.0 → 3.31.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/CHANGELOG.md +25 -0
- package/dist/index.js +117 -5
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.31.0 — Snapshots: list + share-link (agency tooling)
|
|
4
|
+
|
|
5
|
+
Two new agency-level tools, the first build toward removing manual steps from the
|
|
6
|
+
client-provisioning runbook (snapshot selection + handoff for new sub-accounts).
|
|
7
|
+
|
|
8
|
+
- **`list_snapshots`** — list the agency's snapshots (`id`, `name`, `type`) so you
|
|
9
|
+
can pick the right one by name. Read-only.
|
|
10
|
+
- **`create_snapshot_share_link`** — mint a load/share link for a snapshot
|
|
11
|
+
(`gohighlevel.com/?share=…`) to import it into a sub-account. Requires an explicit
|
|
12
|
+
`share_type` (`link`, `permanent_link`, `agency_link`, `location_link`,
|
|
13
|
+
`marketplace_link`).
|
|
14
|
+
|
|
15
|
+
Both use the agency/company-scoped key (`getAgencyKey()`), not a sub-account PIT —
|
|
16
|
+
snapshots are an agency resource. The tools resolve `companyId` with strict rules
|
|
17
|
+
(explicit param > active location's company; mismatch is rejected, not guessed) so
|
|
18
|
+
a multi-tenant install can't list the wrong agency's snapshots, and they fail with
|
|
19
|
+
an actionable message when no agency key is registered.
|
|
20
|
+
|
|
21
|
+
**Notes / limits (verified against the live API):**
|
|
22
|
+
- Creating a sub-account from a snapshot is NOT exposed: `POST /locations/` returns
|
|
23
|
+
401 for PIT auth. Snapshot *apply* stays a guided GHL-UI step.
|
|
24
|
+
- `create_snapshot_share_link` is not idempotent (each call mints a new link, no
|
|
25
|
+
revoke API — remove in the UI). The HTTP client gained an internal `noRetry`
|
|
26
|
+
option so a lost-response retry can't silently create a duplicate.
|
|
27
|
+
|
|
3
28
|
## 3.30.0 — Workflow trigger stays active after edit/publish (Bug 7)
|
|
4
29
|
|
|
5
30
|
Editing a trigger on a published workflow via `update_workflow_actions` silently
|
package/dist/index.js
CHANGED
|
@@ -31,9 +31,9 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "@elitedcs/ghl-mcp",
|
|
34
|
-
version: "3.
|
|
34
|
+
version: "3.31.0",
|
|
35
35
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
36
|
-
description: "GoHighLevel MCP Server for Claude.
|
|
36
|
+
description: "GoHighLevel MCP Server for Claude. 214 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
37
37
|
main: "dist/index.js",
|
|
38
38
|
bin: {
|
|
39
39
|
"ghl-mcp": "dist/index.js"
|
|
@@ -203,7 +203,7 @@ var GHLClient = class {
|
|
|
203
203
|
if (error instanceof Error && error.name === "AbortError") {
|
|
204
204
|
throw new Error(`Request timeout (30s): ${method} ${path6}`);
|
|
205
205
|
}
|
|
206
|
-
if (attempt < MAX_RETRIES) {
|
|
206
|
+
if (!options.noRetry && attempt < MAX_RETRIES) {
|
|
207
207
|
const delay4 = computeRetryDelay(null, attempt, BASE_DELAY_MS);
|
|
208
208
|
process.stderr.write(`[ghl-mcp] Network error on ${method} ${path6}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
|
|
209
209
|
`);
|
|
@@ -214,7 +214,7 @@ var GHLClient = class {
|
|
|
214
214
|
} finally {
|
|
215
215
|
clearTimeout(timeout);
|
|
216
216
|
}
|
|
217
|
-
if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
|
|
217
|
+
if (!options.noRetry && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
|
|
218
218
|
const delay4 = computeRetryDelay(response.headers.get("Retry-After"), attempt, BASE_DELAY_MS);
|
|
219
219
|
process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${path6}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
|
|
220
220
|
`);
|
|
@@ -8817,6 +8817,115 @@ function registerDiagnosticTools(server2, installedVersion, client, builderClien
|
|
|
8817
8817
|
);
|
|
8818
8818
|
}
|
|
8819
8819
|
|
|
8820
|
+
// src/tools/snapshots.ts
|
|
8821
|
+
var import_zod48 = require("zod");
|
|
8822
|
+
var SnapshotSchema = import_zod48.z.object({ id: import_zod48.z.string(), name: import_zod48.z.string(), type: import_zod48.z.string() }).passthrough();
|
|
8823
|
+
var SnapshotsResponseSchema = import_zod48.z.object({ snapshots: import_zod48.z.array(SnapshotSchema) }).passthrough();
|
|
8824
|
+
var ShareLinkResponseSchema = import_zod48.z.object({ id: import_zod48.z.string(), shareLink: import_zod48.z.string() }).passthrough();
|
|
8825
|
+
var SHARE_TYPES = [
|
|
8826
|
+
"link",
|
|
8827
|
+
"permanent_link",
|
|
8828
|
+
"agency_link",
|
|
8829
|
+
"location_link",
|
|
8830
|
+
"marketplace_link"
|
|
8831
|
+
];
|
|
8832
|
+
function resolveSnapshotAuth(client, registry2, companyIdParam) {
|
|
8833
|
+
const agencyKey = registry2?.getAgencyKey();
|
|
8834
|
+
if (!agencyKey) {
|
|
8835
|
+
throw new Error(
|
|
8836
|
+
"Snapshots require an agency/company-scoped API key, and none is registered. This install only has sub-account Private Integration keys. Add the agency key (created at the AGENCY level in GHL Settings > Integrations) to the token registry, then retry."
|
|
8837
|
+
);
|
|
8838
|
+
}
|
|
8839
|
+
const storedCompanyId = client.defaultLocationId ? registry2?.getToken(client.defaultLocationId)?.companyId : void 0;
|
|
8840
|
+
const param = companyIdParam?.trim() || void 0;
|
|
8841
|
+
if (param && storedCompanyId && param !== storedCompanyId) {
|
|
8842
|
+
throw new Error(
|
|
8843
|
+
`companyId mismatch: you passed ${param}, but the active location belongs to company ${storedCompanyId}. Refusing to guess. Switch to a location in the intended company, or pass the matching companyId.`
|
|
8844
|
+
);
|
|
8845
|
+
}
|
|
8846
|
+
const companyId = param ?? storedCompanyId;
|
|
8847
|
+
if (!companyId) {
|
|
8848
|
+
throw new Error(
|
|
8849
|
+
"Could not determine the companyId for this snapshot call. The active location has no stored company. Pass companyId explicitly, or run register_location / switch_location for the active location so its company is recorded."
|
|
8850
|
+
);
|
|
8851
|
+
}
|
|
8852
|
+
return { agencyClient: new GHLClient({ apiKey: agencyKey }), companyId };
|
|
8853
|
+
}
|
|
8854
|
+
function explainSnapshotError(error, companyId) {
|
|
8855
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8856
|
+
if (message.includes("401") || message.includes("Unauthorized") || message.includes("403")) {
|
|
8857
|
+
throw new Error(
|
|
8858
|
+
`${message}
|
|
8859
|
+
|
|
8860
|
+
The agency key is scoped to a single company and may not cover company ${companyId}. Snapshots only list for the agency that owns the key. Use a companyId belonging to the agency key, or register that agency's own key.`
|
|
8861
|
+
);
|
|
8862
|
+
}
|
|
8863
|
+
throw error instanceof Error ? error : new Error(message);
|
|
8864
|
+
}
|
|
8865
|
+
function registerSnapshotTools(server2, client, registry2) {
|
|
8866
|
+
safeTool(
|
|
8867
|
+
server2,
|
|
8868
|
+
"list_snapshots",
|
|
8869
|
+
"List the agency's GHL snapshots (id, name, type) so you can pick the right one by name before applying it to a sub-account. Reads the agency/company-scoped key (not a sub-account PIT). companyId defaults to the active location's company; pass it explicitly to target a different company you have the agency key for. Read-only.",
|
|
8870
|
+
{
|
|
8871
|
+
companyId: import_zod48.z.string().optional().describe(
|
|
8872
|
+
"Agency/company ID whose snapshots to list. Defaults to the active location's company. Must match the company your agency key is scoped to."
|
|
8873
|
+
)
|
|
8874
|
+
},
|
|
8875
|
+
async ({ companyId }) => {
|
|
8876
|
+
const { agencyClient, companyId: resolved } = resolveSnapshotAuth(client, registry2, companyId);
|
|
8877
|
+
let raw;
|
|
8878
|
+
try {
|
|
8879
|
+
raw = await agencyClient.get("/snapshots/", { params: { companyId: resolved } });
|
|
8880
|
+
} catch (error) {
|
|
8881
|
+
explainSnapshotError(error, resolved);
|
|
8882
|
+
}
|
|
8883
|
+
const parsed = SnapshotsResponseSchema.parse(raw);
|
|
8884
|
+
return {
|
|
8885
|
+
companyId: resolved,
|
|
8886
|
+
count: parsed.snapshots.length,
|
|
8887
|
+
snapshots: parsed.snapshots
|
|
8888
|
+
};
|
|
8889
|
+
}
|
|
8890
|
+
);
|
|
8891
|
+
safeTool(
|
|
8892
|
+
server2,
|
|
8893
|
+
"create_snapshot_share_link",
|
|
8894
|
+
"Create a shareable load link for one of the agency's snapshots (returns a gohighlevel.com/?share=... URL to import the snapshot into a sub-account). Uses the agency/company-scoped key. WARNING: this is NOT idempotent \u2014 each call mints a NEW link, and there is no API to list or revoke links (revoke in the GHL UI under the snapshot's share settings). Pick share_type deliberately. Use list_snapshots first to get the snapshot id.",
|
|
8895
|
+
{
|
|
8896
|
+
snapshot_id: import_zod48.z.string().describe("The snapshot id to share (from list_snapshots)."),
|
|
8897
|
+
share_type: import_zod48.z.enum(SHARE_TYPES).describe(
|
|
8898
|
+
"Share link type. 'link' = standard share link; 'permanent_link' = non-expiring; 'agency_link' = share to agencies; 'location_link' = load into a sub-account/location; 'marketplace_link' = marketplace listing. No default \u2014 choose intentionally."
|
|
8899
|
+
),
|
|
8900
|
+
companyId: import_zod48.z.string().optional().describe(
|
|
8901
|
+
"Agency/company ID that owns the snapshot. Defaults to the active location's company. Must match the company your agency key is scoped to."
|
|
8902
|
+
)
|
|
8903
|
+
},
|
|
8904
|
+
async ({ snapshot_id, share_type, companyId }) => {
|
|
8905
|
+
const { agencyClient, companyId: resolved } = resolveSnapshotAuth(client, registry2, companyId);
|
|
8906
|
+
let raw;
|
|
8907
|
+
try {
|
|
8908
|
+
raw = await agencyClient.post("/snapshots/share/link", {
|
|
8909
|
+
params: { companyId: resolved },
|
|
8910
|
+
body: { snapshot_id, share_type },
|
|
8911
|
+
noRetry: true
|
|
8912
|
+
});
|
|
8913
|
+
} catch (error) {
|
|
8914
|
+
explainSnapshotError(error, resolved);
|
|
8915
|
+
}
|
|
8916
|
+
const parsed = ShareLinkResponseSchema.parse(raw);
|
|
8917
|
+
return {
|
|
8918
|
+
companyId: resolved,
|
|
8919
|
+
snapshot_id,
|
|
8920
|
+
share_type,
|
|
8921
|
+
id: parsed.id,
|
|
8922
|
+
shareLink: parsed.shareLink,
|
|
8923
|
+
_note: "A new share link was created. There is no API to revoke it \u2014 remove it in the GHL UI (snapshot share settings) if needed."
|
|
8924
|
+
};
|
|
8925
|
+
}
|
|
8926
|
+
);
|
|
8927
|
+
}
|
|
8928
|
+
|
|
8820
8929
|
// src/tools/index.ts
|
|
8821
8930
|
var publicApiTools = [
|
|
8822
8931
|
[registerContactTools, "contacts"],
|
|
@@ -8865,12 +8974,14 @@ var internalApiTools = [
|
|
|
8865
8974
|
var VALIDATORS_MODULE = "validators";
|
|
8866
8975
|
var DIAGNOSTICS_MODULE = "diagnostics";
|
|
8867
8976
|
var LOCATION_SWITCHER_MODULE = "location-switcher";
|
|
8977
|
+
var SNAPSHOTS_MODULE = "snapshots";
|
|
8868
8978
|
var KNOWN_MODULES = /* @__PURE__ */ new Set([
|
|
8869
8979
|
...publicApiTools.map(([, label]) => label),
|
|
8870
8980
|
...internalApiTools.map(([, label]) => label),
|
|
8871
8981
|
VALIDATORS_MODULE,
|
|
8872
8982
|
DIAGNOSTICS_MODULE,
|
|
8873
|
-
LOCATION_SWITCHER_MODULE
|
|
8983
|
+
LOCATION_SWITCHER_MODULE,
|
|
8984
|
+
SNAPSHOTS_MODULE
|
|
8874
8985
|
]);
|
|
8875
8986
|
function registerAllTools(server2, client, registry2, mcpVersion, env = process.env) {
|
|
8876
8987
|
const config3 = parseAllowlist(env);
|
|
@@ -8892,6 +9003,7 @@ function registerAllTools(server2, client, registry2, mcpVersion, env = process.
|
|
|
8892
9003
|
builderClient,
|
|
8893
9004
|
registry2 ?? null
|
|
8894
9005
|
);
|
|
9006
|
+
registerSnapshotTools(wrap(SNAPSHOTS_MODULE), client, registry2);
|
|
8895
9007
|
registerLocationSwitcherTools(
|
|
8896
9008
|
wrap(LOCATION_SWITCHER_MODULE),
|
|
8897
9009
|
client,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.31.0",
|
|
4
4
|
"mcpName": "io.github.drjerryrelth/ghl-command",
|
|
5
|
-
"description": "GoHighLevel MCP Server for Claude.
|
|
5
|
+
"description": "GoHighLevel MCP Server for Claude. 214 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"ghl-mcp": "dist/index.js"
|