Add MCP apps UI system, 19 new tool modules, merge upstream changes

This commit is contained in:
Jake Shore 2026-01-31 01:09:14 -05:00
commit c1fbbdd95b
24 changed files with 5796 additions and 33 deletions

Binary file not shown.

128
README.md
View File

@ -1,9 +1,87 @@
<<<<<<< HEAD
**Instead of trying to tackle this ---- use our hosted version --- GHL Agent Framework, One Click to Sign in!**
https://www.strategixagents.com/
# 🚀 GoHighLevel MCP Server
=======
> **🚀 Don't want to self-host?** [Join the waitlist for our fully managed solution →](https://mcp.localbosses.org)
>
> Zero setup. Zero maintenance. Just connect and automate.
---
### 🙏 Credits
**Original Creator:** [@mastanley13](https://github.com/mastanley13) — Built the foundation for this MCP server.
**Extended by:** [@BusyBee3333](https://github.com/BusyBee3333) — Expanded to 461+ tools covering the entire GHL API.
---
# 🚀 GoHighLevel MCP Server
## 💡 What This Unlocks
**This MCP server gives AI direct access to your entire GoHighLevel CRM.** Instead of clicking through menus, you just *tell* it what you want.
### 🎯 GHL-Native Power Moves
| Just say... | What happens |
|-------------|--------------|
| *"Find everyone who filled out a form this week but hasn't been contacted"* | Searches contacts, filters by source and last activity, returns a ready-to-call list |
| *"Create an opportunity for John Smith, $15k deal, add to Enterprise pipeline"* | Creates the opp, assigns pipeline stage, links to contact — done |
| *"Schedule a discovery call with Sarah for Tuesday 2pm and send her a confirmation"* | Checks calendar availability, books the slot, fires off an SMS |
| *"Draft a blog post about our new service and schedule it for Friday"* | Creates the post in your GHL blog, SEO-ready, scheduled to publish |
| *"Send a payment link for Invoice #1042 to the client via text"* | Generates text2pay link, sends SMS with payment URL |
### 🔗 The Real Power: Combining Tools
When you pair this MCP with other tools (web search, email, spreadsheets, Slack, etc.), things get *wild*:
| Combo | What you can build |
|-------|-------------------|
| **GHL + Calendar + SMS** | "Every morning, text me a summary of today's appointments and any leads that went cold" |
| **GHL + Web Search + Email** | "Research this prospect's company, then draft a personalized outreach email and add them as a contact" |
| **GHL + Slack + Opportunities** | "When a deal closes, post a celebration to #wins with the deal value and rep name" |
| **GHL + Spreadsheet + Invoices** | "Import this CSV of clients, create contacts, and generate invoices for each one" |
| **GHL + AI + Conversations** | "Analyze the last 50 customer conversations and tell me what objections keep coming up" |
> **This isn't just API access — it's your CRM on autopilot, controlled by natural language.**
---
## 🎁 Don't Want to Self-Host? We've Got You.
**Not everyone wants to manage servers, deal with API keys, or troubleshoot deployments.** We get it.
👉 **[Join the waitlist for our fully managed solution](https://mcp.localbosses.org)**
**What you get:**
- ✅ **Zero setup** — We handle everything
- ✅ **Always up-to-date** — Latest features and security patches automatically
- ✅ **Priority support** — Real humans who know GHL and AI
- ✅ **Enterprise-grade reliability** — 99.9% uptime, monitored 24/7
**Perfect for:**
- Agencies who want to focus on clients, not infrastructure
- Teams without dedicated DevOps resources
- Anyone who values their time over tinkering with configs
<p align="center">
<a href="https://mcp.localbosses.org">
<img src="https://img.shields.io/badge/Join_Waitlist-Get_Early_Access-0ea5e9?style=for-the-badge&logo=rocket&logoColor=white" alt="Join Waitlist">
</a>
</p>
---
*Prefer to self-host? Keep reading below for the full open-source setup guide.*
---
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
## 🚨 **IMPORTANT: FOUNDATIONAL PROJECT NOTICE**
> **⚠️ This is a BASE-LEVEL foundational project designed to connect the GoHighLevel community with AI automation through MCP (Model Context Protocol).**
@ -83,7 +161,53 @@ This project was a 'time-taker' but I felt it was important. Feel free to donate
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https://github.com/mastanley13/GoHighLevel-MCP)
[![Donate to the Project](https://img.shields.io/badge/Donate_to_the_Project-💝_Support_Development-ff69b4?style=for-the-badge&logo=stripe&logoColor=white)](https://buy.stripe.com/28E14o1hT7JAfstfvqdZ60y)
<<<<<<< HEAD
> **🔥 Transform Claude Desktop into a complete GoHighLevel CRM powerhouse with 461+ powerful tools across 38+ categories**
=======
---
### 🤖 Recommended Setup Options
#### Option 1: Clawdbot (Easiest — Full AI Assistant)
**[Clawdbot](https://clawd.bot)** is the easiest way to run this MCP server. It's an AI assistant platform that handles all the MCP configuration, environment setup, and integration automatically.
**Why Clawdbot?**
- ✅ **Zero-config MCP setup** — Just add your GHL API key and go
- ✅ **Multi-channel AI** — Use your GHL tools via Discord, Slack, iMessage, WhatsApp, and more
- ✅ **Built-in automation** — Schedule tasks, create workflows, and chain tools together
- ✅ **Always-on assistant** — Runs 24/7 so your GHL automation never sleeps
**Quick start:**
```bash
npm install -g clawdbot
clawdbot init
clawdbot config set skills.entries.ghl-mcp.apiKey "your_private_integrations_key"
```
Learn more at [docs.clawd.bot](https://docs.clawd.bot) or join the [community Discord](https://discord.com/invite/clawd).
#### Option 2: mcporter (Lightweight CLI)
**[mcporter](https://github.com/cyanheads/mcporter)** is a lightweight CLI tool for managing and calling MCP servers directly from the command line. Perfect if you want to test tools, debug integrations, or build your own automation scripts.
**Why mcporter?**
- ✅ **Direct MCP access** — Call any MCP tool from the terminal
- ✅ **Config management** — Easy server setup and auth handling
- ✅ **Great for scripting** — Pipe MCP tools into shell scripts and automations
- ✅ **Debugging friendly** — Inspect requests/responses in real-time
**Quick start:**
```bash
npm install -g mcporter
mcporter config add ghl-mcp --transport stdio --command "node /path/to/ghl-mcp-server/dist/server.js"
mcporter call ghl-mcp search_contacts --params '{"query": "test"}'
```
---
> **🔥 Transform Claude Desktop into a complete GoHighLevel CRM powerhouse with 461+ powerful tools across 19+ categories**
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
## 🎯 What This Does
@ -743,7 +867,11 @@ This project is licensed under the **ISC License** - see the [LICENSE](LICENSE)
This comprehensive MCP server delivers:
<<<<<<< HEAD
### ✅ **461 Operational Tools** across 38 categories
=======
### ✅ **461 Operational Tools** across 19 categories
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
### ✅ **Real-time GoHighLevel Integration** with full API coverage
### ✅ **Production-Ready Deployment** on multiple platforms
### ✅ **Enterprise-Grade Architecture** with comprehensive error handling

52
package-lock.json generated
View File

@ -1,11 +1,19 @@
{
<<<<<<< HEAD
"name": "ghl-mcp",
=======
"name": "@mastanley13/ghl-mcp-server",
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
<<<<<<< HEAD
"name": "ghl-mcp",
=======
"name": "@mastanley13/ghl-mcp-server",
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"version": "1.0.0",
"license": "ISC",
"dependencies": {
@ -17,6 +25,12 @@
"dotenv": "^16.5.0",
"express": "^5.1.0"
},
<<<<<<< HEAD
=======
"bin": {
"ghl-mcp-server": "dist/server.js"
},
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^22.15.29",
@ -25,6 +39,12 @@
"ts-jest": "^29.3.4",
"ts-node": "^10.9.2",
"typescript": "^5.8.3"
<<<<<<< HEAD
=======
},
"engines": {
"node": ">=18.0.0"
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
}
},
"node_modules/@ampproject/remapping": {
@ -68,6 +88,10 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz",
"integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==",
"dev": true,
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
@ -1078,6 +1102,10 @@
"version": "22.15.29",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
"integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==",
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"dependencies": {
"undici-types": "~6.21.0"
}
@ -1459,6 +1487,10 @@
"url": "https://github.com/sponsors/ai"
}
],
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"dependencies": {
"caniuse-lite": "^1.0.30001718",
"electron-to-chromium": "^1.5.160",
@ -2123,6 +2155,10 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
@ -2842,6 +2878,10 @@
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
"dev": true,
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"dependencies": {
"@jest/core": "^29.7.0",
"@jest/types": "^29.6.3",
@ -4651,6 +4691,10 @@
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@ -4728,6 +4772,10 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -4946,6 +4994,10 @@
"version": "3.25.51",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.51.tgz",
"integrity": "sha512-TQSnBldh+XSGL+opiSIq0575wvDPqu09AqWe1F7JhUMKY+M91/aGlK4MhpVNO7MgYfHcVCB1ffwAUTJzllKJqg==",
<<<<<<< HEAD
=======
"peer": true,
>>>>>>> 422de92c1c7a69e2ca2b7045d9142636bc3e321d
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@ -11,15 +11,7 @@ import { fileURLToPath } from 'url';
export interface AppToolResult {
content: Array<{ type: 'text'; text: string }>;
structuredContent?: {
type: 'resource';
resource: {
uri: string;
mimeType: string;
text?: string;
blob?: string;
};
};
structuredContent?: Record<string, unknown>;
[key: string]: unknown;
}
@ -33,21 +25,22 @@ export interface AppResourceHandler {
* MCP Apps Manager class
* Registers app tools and handles structuredContent responses
*/
// Note: We use process.cwd() based path resolution to find UI dist
// This works when running from the project root directory
// Resolve UI build path - works regardless of working directory
function getUIBuildPath(): string {
// First try dist/app-ui (where MCP Apps are built)
// When compiled, this file is at dist/apps/index.js
// UI files are at dist/app-ui/
// Use __dirname which is available in CommonJS
const fromDist = path.resolve(__dirname, '..', 'app-ui');
if (fs.existsSync(fromDist)) {
return fromDist;
}
// Fallback: try process.cwd() based paths
const appUiPath = path.join(process.cwd(), 'dist', 'app-ui');
if (fs.existsSync(appUiPath)) {
return appUiPath;
}
// Fallback to src/ui/dist for legacy UI components
const fromCwd = path.join(process.cwd(), 'src', 'ui', 'dist');
if (fs.existsSync(fromCwd)) {
return fromCwd;
}
// Default fallback
return appUiPath;
return fromDist;
}
export class MCPAppsManager {
@ -306,6 +299,22 @@ export class MCPAppsManager {
_meta: {
ui: { resourceUri: 'ui://ghl/mcp-app' }
}
},
// 12. Update Opportunity - action tool for UI to update opportunities
{
name: 'update_opportunity',
description: 'Update an opportunity (move to stage, change value, status, etc.)',
inputSchema: {
type: 'object',
properties: {
opportunityId: { type: 'string', description: 'Opportunity ID to update' },
pipelineStageId: { type: 'string', description: 'New stage ID (for moving)' },
name: { type: 'string', description: 'Opportunity name' },
monetaryValue: { type: 'number', description: 'Monetary value' },
status: { type: 'string', enum: ['open', 'won', 'lost', 'abandoned'], description: 'Opportunity status' }
},
required: ['opportunityId']
}
}
];
}
@ -325,7 +334,8 @@ export class MCPAppsManager {
'view_agent_stats',
'view_contact_timeline',
'view_workflow_status',
'view_dashboard'
'view_dashboard',
'update_opportunity'
];
}
@ -365,6 +375,14 @@ export class MCPAppsManager {
return await this.viewWorkflowStatus(args.workflowId);
case 'view_dashboard':
return await this.viewDashboard();
case 'update_opportunity':
return await this.updateOpportunity(args as {
opportunityId: string;
pipelineStageId?: string;
name?: string;
monetaryValue?: number;
status?: 'open' | 'won' | 'lost' | 'abandoned';
});
default:
throw new Error(`Unknown app tool: ${toolName}`);
}
@ -415,9 +433,26 @@ export class MCPAppsManager {
const pipeline = pipelinesResponse.data?.pipelines?.find((p: any) => p.id === pipelineId);
const opportunities = opportunitiesResponse.data?.opportunities || [];
// Simplify opportunity data to only include fields the UI needs (reduces payload size)
const simplifiedOpportunities = opportunities.map((opp: any) => ({
id: opp.id,
name: opp.name || 'Untitled',
pipelineStageId: opp.pipelineStageId,
status: opp.status || 'open',
monetaryValue: opp.monetaryValue || 0,
contact: opp.contact ? {
name: opp.contact.name || 'Unknown',
email: opp.contact.email,
phone: opp.contact.phone
} : { name: 'Unknown' },
updatedAt: opp.updatedAt || opp.createdAt,
createdAt: opp.createdAt,
source: opp.source
}));
const data = {
pipeline,
opportunities,
opportunities: simplifiedOpportunities,
stages: pipeline?.stages || []
};
@ -692,6 +727,50 @@ export class MCPAppsManager {
);
}
/**
* Update opportunity (action tool for UI)
*/
private async updateOpportunity(args: {
opportunityId: string;
pipelineStageId?: string;
name?: string;
monetaryValue?: number;
status?: 'open' | 'won' | 'lost' | 'abandoned';
}): Promise<AppToolResult> {
const { opportunityId, ...updates } = args;
// Build the update payload
const updatePayload: any = {};
if (updates.pipelineStageId) updatePayload.pipelineStageId = updates.pipelineStageId;
if (updates.name) updatePayload.name = updates.name;
if (updates.monetaryValue !== undefined) updatePayload.monetaryValue = updates.monetaryValue;
if (updates.status) updatePayload.status = updates.status;
process.stderr.write(`[MCP Apps] Updating opportunity ${opportunityId}: ${JSON.stringify(updatePayload)}\n`);
const response = await this.ghlClient.updateOpportunity(opportunityId, updatePayload);
if (!response.success) {
throw new Error(response.error?.message || 'Failed to update opportunity');
}
const opportunity = response.data;
return {
content: [{ type: 'text', text: `Updated opportunity: ${opportunity?.name || opportunityId}` }],
structuredContent: {
success: true,
opportunity: {
id: opportunity?.id,
name: opportunity?.name,
pipelineStageId: opportunity?.pipelineStageId,
monetaryValue: opportunity?.monetaryValue,
status: opportunity?.status
}
}
};
}
/**
* Create app tool result with structuredContent
*/
@ -702,19 +781,11 @@ export class MCPAppsManager {
htmlContent: string,
data: any
): AppToolResult {
// Inject the data into the HTML
const htmlWithData = this.injectDataIntoHTML(htmlContent, data);
// structuredContent is the data object that gets passed to ontoolresult
// The UI accesses it via result.structuredContent
return {
content: [{ type: 'text', text: textSummary }],
structuredContent: {
type: 'resource',
resource: {
uri: resourceUri,
mimeType: mimeType,
text: htmlWithData
}
}
structuredContent: data
};
}

View File

@ -575,12 +575,12 @@ export class GHLApiClient {
const filters: any = {};
let hasFilters = false;
if (searchParams.filters.email && searchParams.filters.email.trim()) {
if (searchParams.filters.email && typeof searchParams.filters.email === 'string' && searchParams.filters.email.trim()) {
filters.email = searchParams.filters.email.trim();
hasFilters = true;
}
if (searchParams.filters.phone && searchParams.filters.phone.trim()) {
if (searchParams.filters.phone && typeof searchParams.filters.phone === 'string' && searchParams.filters.phone.trim()) {
filters.phone = searchParams.filters.phone.trim();
hasFilters = true;
}
@ -1558,6 +1558,36 @@ export class GHLApiClient {
return { ...this.config };
}
/**
* Generic request method for new endpoints
* Used by new tool modules that don't have specific client methods yet
*/
async makeRequest<T = any>(method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', path: string, body?: Record<string, unknown>): Promise<GHLApiResponse<T>> {
try {
let response;
switch (method) {
case 'GET':
response = await this.axiosInstance.get(path);
break;
case 'POST':
response = await this.axiosInstance.post(path, body);
break;
case 'PUT':
response = await this.axiosInstance.put(path, body);
break;
case 'PATCH':
response = await this.axiosInstance.patch(path, body);
break;
case 'DELETE':
response = await this.axiosInstance.delete(path);
break;
}
return this.wrapResponse(response.data);
} catch (error) {
throw error;
}
}
/**
* OPPORTUNITIES API METHODS
*/

View File

@ -0,0 +1,395 @@
/**
* GoHighLevel Affiliates Tools
* Tools for managing affiliate marketing program
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class AffiliatesTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// Affiliate Campaigns
{
name: 'get_affiliate_campaigns',
description: 'Get all affiliate campaigns',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
status: { type: 'string', enum: ['active', 'inactive', 'all'], description: 'Campaign status filter' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_affiliate_campaign',
description: 'Get a specific affiliate campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Affiliate Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
{
name: 'create_affiliate_campaign',
description: 'Create a new affiliate campaign',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Campaign name' },
description: { type: 'string', description: 'Campaign description' },
commissionType: { type: 'string', enum: ['percentage', 'fixed'], description: 'Commission type' },
commissionValue: { type: 'number', description: 'Commission value (percentage or fixed amount)' },
cookieDays: { type: 'number', description: 'Cookie tracking duration in days' },
productIds: { type: 'array', items: { type: 'string' }, description: 'Product IDs for this campaign' }
},
required: ['name', 'commissionType', 'commissionValue']
}
},
{
name: 'update_affiliate_campaign',
description: 'Update an affiliate campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Campaign name' },
description: { type: 'string', description: 'Campaign description' },
commissionType: { type: 'string', enum: ['percentage', 'fixed'], description: 'Commission type' },
commissionValue: { type: 'number', description: 'Commission value' },
status: { type: 'string', enum: ['active', 'inactive'], description: 'Campaign status' }
},
required: ['campaignId']
}
},
{
name: 'delete_affiliate_campaign',
description: 'Delete an affiliate campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
// Affiliates
{
name: 'get_affiliates',
description: 'Get all affiliates',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
campaignId: { type: 'string', description: 'Filter by campaign' },
status: { type: 'string', enum: ['pending', 'approved', 'rejected', 'all'], description: 'Status filter' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_affiliate',
description: 'Get a specific affiliate',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['affiliateId']
}
},
{
name: 'create_affiliate',
description: 'Create/add a new affiliate',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
contactId: { type: 'string', description: 'Contact ID to make affiliate' },
campaignId: { type: 'string', description: 'Campaign to assign to' },
customCode: { type: 'string', description: 'Custom affiliate code' },
status: { type: 'string', enum: ['pending', 'approved'], description: 'Initial status' }
},
required: ['contactId', 'campaignId']
}
},
{
name: 'update_affiliate',
description: 'Update an affiliate',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' },
status: { type: 'string', enum: ['pending', 'approved', 'rejected'], description: 'Status' },
customCode: { type: 'string', description: 'Custom affiliate code' }
},
required: ['affiliateId']
}
},
{
name: 'approve_affiliate',
description: 'Approve a pending affiliate',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['affiliateId']
}
},
{
name: 'reject_affiliate',
description: 'Reject/deny a pending affiliate',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' },
reason: { type: 'string', description: 'Rejection reason' }
},
required: ['affiliateId']
}
},
{
name: 'delete_affiliate',
description: 'Remove an affiliate',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['affiliateId']
}
},
// Commissions & Payouts
{
name: 'get_affiliate_commissions',
description: 'Get commissions for an affiliate',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' },
status: { type: 'string', enum: ['pending', 'approved', 'paid', 'all'], description: 'Status filter' },
startDate: { type: 'string', description: 'Start date' },
endDate: { type: 'string', description: 'End date' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
},
required: ['affiliateId']
}
},
{
name: 'get_affiliate_stats',
description: 'Get affiliate performance statistics',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date' },
endDate: { type: 'string', description: 'End date' }
},
required: ['affiliateId']
}
},
{
name: 'create_payout',
description: 'Create a payout for affiliate',
inputSchema: {
type: 'object',
properties: {
affiliateId: { type: 'string', description: 'Affiliate ID' },
locationId: { type: 'string', description: 'Location ID' },
amount: { type: 'number', description: 'Payout amount' },
commissionIds: { type: 'array', items: { type: 'string' }, description: 'Commission IDs to include' },
note: { type: 'string', description: 'Payout note' }
},
required: ['affiliateId', 'amount']
}
},
{
name: 'get_payouts',
description: 'Get affiliate payouts',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
affiliateId: { type: 'string', description: 'Filter by affiliate' },
status: { type: 'string', enum: ['pending', 'completed', 'failed', 'all'], description: 'Status filter' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
// Referrals
{
name: 'get_referrals',
description: 'Get referrals (leads/sales) from affiliates',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
affiliateId: { type: 'string', description: 'Filter by affiliate' },
campaignId: { type: 'string', description: 'Filter by campaign' },
type: { type: 'string', enum: ['lead', 'sale', 'all'], description: 'Referral type' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
// Campaigns
case 'get_affiliate_campaigns': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/affiliates/campaigns?${params.toString()}`);
}
case 'get_affiliate_campaign': {
return this.ghlClient.makeRequest('GET', `/affiliates/campaigns/${args.campaignId}?locationId=${locationId}`);
}
case 'create_affiliate_campaign': {
return this.ghlClient.makeRequest('POST', `/affiliates/campaigns`, {
locationId,
name: args.name,
description: args.description,
commissionType: args.commissionType,
commissionValue: args.commissionValue,
cookieDays: args.cookieDays,
productIds: args.productIds
});
}
case 'update_affiliate_campaign': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.description) body.description = args.description;
if (args.commissionType) body.commissionType = args.commissionType;
if (args.commissionValue) body.commissionValue = args.commissionValue;
if (args.status) body.status = args.status;
return this.ghlClient.makeRequest('PUT', `/affiliates/campaigns/${args.campaignId}`, body);
}
case 'delete_affiliate_campaign': {
return this.ghlClient.makeRequest('DELETE', `/affiliates/campaigns/${args.campaignId}?locationId=${locationId}`);
}
// Affiliates
case 'get_affiliates': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.campaignId) params.append('campaignId', String(args.campaignId));
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/affiliates/?${params.toString()}`);
}
case 'get_affiliate': {
return this.ghlClient.makeRequest('GET', `/affiliates/${args.affiliateId}?locationId=${locationId}`);
}
case 'create_affiliate': {
return this.ghlClient.makeRequest('POST', `/affiliates/`, {
locationId,
contactId: args.contactId,
campaignId: args.campaignId,
customCode: args.customCode,
status: args.status
});
}
case 'update_affiliate': {
const body: Record<string, unknown> = { locationId };
if (args.status) body.status = args.status;
if (args.customCode) body.customCode = args.customCode;
return this.ghlClient.makeRequest('PUT', `/affiliates/${args.affiliateId}`, body);
}
case 'approve_affiliate': {
return this.ghlClient.makeRequest('POST', `/affiliates/${args.affiliateId}/approve`, { locationId });
}
case 'reject_affiliate': {
return this.ghlClient.makeRequest('POST', `/affiliates/${args.affiliateId}/reject`, {
locationId,
reason: args.reason
});
}
case 'delete_affiliate': {
return this.ghlClient.makeRequest('DELETE', `/affiliates/${args.affiliateId}?locationId=${locationId}`);
}
// Commissions
case 'get_affiliate_commissions': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.status) params.append('status', String(args.status));
if (args.startDate) params.append('startDate', String(args.startDate));
if (args.endDate) params.append('endDate', String(args.endDate));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/affiliates/${args.affiliateId}/commissions?${params.toString()}`);
}
case 'get_affiliate_stats': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.startDate) params.append('startDate', String(args.startDate));
if (args.endDate) params.append('endDate', String(args.endDate));
return this.ghlClient.makeRequest('GET', `/affiliates/${args.affiliateId}/stats?${params.toString()}`);
}
case 'create_payout': {
return this.ghlClient.makeRequest('POST', `/affiliates/${args.affiliateId}/payouts`, {
locationId,
amount: args.amount,
commissionIds: args.commissionIds,
note: args.note
});
}
case 'get_payouts': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.affiliateId) params.append('affiliateId', String(args.affiliateId));
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/affiliates/payouts?${params.toString()}`);
}
// Referrals
case 'get_referrals': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.affiliateId) params.append('affiliateId', String(args.affiliateId));
if (args.campaignId) params.append('campaignId', String(args.campaignId));
if (args.type) params.append('type', String(args.type));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/affiliates/referrals?${params.toString()}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,232 @@
/**
* GoHighLevel Businesses Tools
* Tools for managing businesses (multi-business support)
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class BusinessesTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_businesses',
description: 'Get all businesses for a location. Businesses represent different entities within a sub-account.',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
}
}
},
{
name: 'get_business',
description: 'Get a specific business by ID',
inputSchema: {
type: 'object',
properties: {
businessId: {
type: 'string',
description: 'The business ID to retrieve'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['businessId']
}
},
{
name: 'create_business',
description: 'Create a new business for a location',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
name: {
type: 'string',
description: 'Business name'
},
phone: {
type: 'string',
description: 'Business phone number'
},
email: {
type: 'string',
description: 'Business email address'
},
website: {
type: 'string',
description: 'Business website URL'
},
address: {
type: 'string',
description: 'Business street address'
},
city: {
type: 'string',
description: 'Business city'
},
state: {
type: 'string',
description: 'Business state'
},
postalCode: {
type: 'string',
description: 'Business postal/zip code'
},
country: {
type: 'string',
description: 'Business country'
},
description: {
type: 'string',
description: 'Business description'
},
logoUrl: {
type: 'string',
description: 'URL to business logo image'
}
},
required: ['name']
}
},
{
name: 'update_business',
description: 'Update an existing business',
inputSchema: {
type: 'object',
properties: {
businessId: {
type: 'string',
description: 'The business ID to update'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
name: {
type: 'string',
description: 'Business name'
},
phone: {
type: 'string',
description: 'Business phone number'
},
email: {
type: 'string',
description: 'Business email address'
},
website: {
type: 'string',
description: 'Business website URL'
},
address: {
type: 'string',
description: 'Business street address'
},
city: {
type: 'string',
description: 'Business city'
},
state: {
type: 'string',
description: 'Business state'
},
postalCode: {
type: 'string',
description: 'Business postal/zip code'
},
country: {
type: 'string',
description: 'Business country'
},
description: {
type: 'string',
description: 'Business description'
},
logoUrl: {
type: 'string',
description: 'URL to business logo image'
}
},
required: ['businessId']
}
},
{
name: 'delete_business',
description: 'Delete a business from a location',
inputSchema: {
type: 'object',
properties: {
businessId: {
type: 'string',
description: 'The business ID to delete'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['businessId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_businesses': {
return this.ghlClient.makeRequest('GET', `/businesses/?locationId=${locationId}`);
}
case 'get_business': {
const businessId = args.businessId as string;
return this.ghlClient.makeRequest('GET', `/businesses/${businessId}?locationId=${locationId}`);
}
case 'create_business': {
const body: Record<string, unknown> = {
locationId,
name: args.name
};
const optionalFields = ['phone', 'email', 'website', 'address', 'city', 'state', 'postalCode', 'country', 'description', 'logoUrl'];
optionalFields.forEach(field => {
if (args[field]) body[field] = args[field];
});
return this.ghlClient.makeRequest('POST', `/businesses/`, body);
}
case 'update_business': {
const businessId = args.businessId as string;
const body: Record<string, unknown> = { locationId };
const optionalFields = ['name', 'phone', 'email', 'website', 'address', 'city', 'state', 'postalCode', 'country', 'description', 'logoUrl'];
optionalFields.forEach(field => {
if (args[field]) body[field] = args[field];
});
return this.ghlClient.makeRequest('PUT', `/businesses/${businessId}`, body);
}
case 'delete_business': {
const businessId = args.businessId as string;
return this.ghlClient.makeRequest('DELETE', `/businesses/${businessId}?locationId=${locationId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,243 @@
/**
* GoHighLevel Campaigns Tools
* Tools for managing email and SMS campaigns
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class CampaignsTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// Campaign Management
{
name: 'get_campaigns',
description: 'Get all campaigns (email/SMS) for a location',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
status: { type: 'string', enum: ['draft', 'scheduled', 'running', 'completed', 'paused'], description: 'Filter by campaign status' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_campaign',
description: 'Get a specific campaign by ID',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
{
name: 'create_campaign',
description: 'Create a new campaign',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Campaign name' },
type: { type: 'string', enum: ['email', 'sms', 'voicemail'], description: 'Campaign type' },
status: { type: 'string', enum: ['draft', 'scheduled'], description: 'Initial status' }
},
required: ['name', 'type']
}
},
{
name: 'update_campaign',
description: 'Update a campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Campaign name' },
status: { type: 'string', enum: ['draft', 'scheduled', 'paused'], description: 'Campaign status' }
},
required: ['campaignId']
}
},
{
name: 'delete_campaign',
description: 'Delete a campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
// Campaign Actions
{
name: 'start_campaign',
description: 'Start/launch a campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
{
name: 'pause_campaign',
description: 'Pause a running campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
{
name: 'resume_campaign',
description: 'Resume a paused campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
// Campaign Stats
{
name: 'get_campaign_stats',
description: 'Get statistics for a campaign (opens, clicks, bounces, etc.)',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['campaignId']
}
},
{
name: 'get_campaign_recipients',
description: 'Get all recipients of a campaign',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID' },
locationId: { type: 'string', description: 'Location ID' },
status: { type: 'string', enum: ['sent', 'delivered', 'opened', 'clicked', 'bounced', 'unsubscribed'], description: 'Filter by recipient status' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
},
required: ['campaignId']
}
},
// Scheduled Messages
{
name: 'get_scheduled_messages',
description: 'Get all scheduled messages in campaigns',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
contactId: { type: 'string', description: 'Filter by contact ID' },
campaignId: { type: 'string', description: 'Filter by campaign ID' }
}
}
},
{
name: 'cancel_scheduled_campaign_message',
description: 'Cancel a scheduled campaign message for a contact',
inputSchema: {
type: 'object',
properties: {
messageId: { type: 'string', description: 'Scheduled message ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['messageId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_campaigns': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/campaigns/?${params.toString()}`);
}
case 'get_campaign': {
return this.ghlClient.makeRequest('GET', `/campaigns/${args.campaignId}?locationId=${locationId}`);
}
case 'create_campaign': {
return this.ghlClient.makeRequest('POST', `/campaigns/`, {
locationId,
name: args.name,
type: args.type,
status: args.status || 'draft'
});
}
case 'update_campaign': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.status) body.status = args.status;
return this.ghlClient.makeRequest('PUT', `/campaigns/${args.campaignId}`, body);
}
case 'delete_campaign': {
return this.ghlClient.makeRequest('DELETE', `/campaigns/${args.campaignId}?locationId=${locationId}`);
}
case 'start_campaign': {
return this.ghlClient.makeRequest('POST', `/campaigns/${args.campaignId}/start`, { locationId });
}
case 'pause_campaign': {
return this.ghlClient.makeRequest('POST', `/campaigns/${args.campaignId}/pause`, { locationId });
}
case 'resume_campaign': {
return this.ghlClient.makeRequest('POST', `/campaigns/${args.campaignId}/resume`, { locationId });
}
case 'get_campaign_stats': {
return this.ghlClient.makeRequest('GET', `/campaigns/${args.campaignId}/stats?locationId=${locationId}`);
}
case 'get_campaign_recipients': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/campaigns/${args.campaignId}/recipients?${params.toString()}`);
}
case 'get_scheduled_messages': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.contactId) params.append('contactId', String(args.contactId));
if (args.campaignId) params.append('campaignId', String(args.campaignId));
return this.ghlClient.makeRequest('GET', `/campaigns/scheduled-messages?${params.toString()}`);
}
case 'cancel_scheduled_campaign_message': {
return this.ghlClient.makeRequest('DELETE', `/campaigns/scheduled-messages/${args.messageId}?locationId=${locationId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,304 @@
/**
* GoHighLevel Companies Tools
* Tools for managing company records (B2B CRM functionality)
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class CompaniesTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_companies',
description: 'Get all companies for a location. Companies represent business entities in B2B scenarios.',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
skip: {
type: 'number',
description: 'Number of records to skip for pagination'
},
limit: {
type: 'number',
description: 'Maximum number of companies to return'
},
query: {
type: 'string',
description: 'Search query to filter companies'
}
}
}
},
{
name: 'get_company',
description: 'Get a specific company by ID',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'The company ID to retrieve'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['companyId']
}
},
{
name: 'create_company',
description: 'Create a new company record',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
name: {
type: 'string',
description: 'Company name'
},
phone: {
type: 'string',
description: 'Company phone number'
},
email: {
type: 'string',
description: 'Company email address'
},
website: {
type: 'string',
description: 'Company website URL'
},
address1: {
type: 'string',
description: 'Street address line 1'
},
address2: {
type: 'string',
description: 'Street address line 2'
},
city: {
type: 'string',
description: 'City'
},
state: {
type: 'string',
description: 'State/Province'
},
postalCode: {
type: 'string',
description: 'Postal/ZIP code'
},
country: {
type: 'string',
description: 'Country'
},
industry: {
type: 'string',
description: 'Industry/vertical'
},
employeeCount: {
type: 'number',
description: 'Number of employees'
},
annualRevenue: {
type: 'number',
description: 'Annual revenue'
},
description: {
type: 'string',
description: 'Company description'
},
customFields: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
key: { type: 'string' },
value: { type: 'string' }
}
},
description: 'Custom field values'
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Tags to apply to the company'
}
},
required: ['name']
}
},
{
name: 'update_company',
description: 'Update an existing company record',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'The company ID to update'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
name: {
type: 'string',
description: 'Company name'
},
phone: {
type: 'string',
description: 'Company phone number'
},
email: {
type: 'string',
description: 'Company email address'
},
website: {
type: 'string',
description: 'Company website URL'
},
address1: {
type: 'string',
description: 'Street address line 1'
},
city: {
type: 'string',
description: 'City'
},
state: {
type: 'string',
description: 'State/Province'
},
postalCode: {
type: 'string',
description: 'Postal/ZIP code'
},
country: {
type: 'string',
description: 'Country'
},
industry: {
type: 'string',
description: 'Industry/vertical'
},
employeeCount: {
type: 'number',
description: 'Number of employees'
},
annualRevenue: {
type: 'number',
description: 'Annual revenue'
},
description: {
type: 'string',
description: 'Company description'
},
customFields: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
key: { type: 'string' },
value: { type: 'string' }
}
},
description: 'Custom field values'
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Tags to apply to the company'
}
},
required: ['companyId']
}
},
{
name: 'delete_company',
description: 'Delete a company record',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'The company ID to delete'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['companyId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_companies': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
if (args.query) params.append('query', String(args.query));
return this.ghlClient.makeRequest('GET', `/companies/?${params.toString()}`);
}
case 'get_company': {
const companyId = args.companyId as string;
return this.ghlClient.makeRequest('GET', `/companies/${companyId}`);
}
case 'create_company': {
const body: Record<string, unknown> = {
locationId,
name: args.name
};
const optionalFields = ['phone', 'email', 'website', 'address1', 'address2', 'city', 'state', 'postalCode', 'country', 'industry', 'employeeCount', 'annualRevenue', 'description', 'customFields', 'tags'];
optionalFields.forEach(field => {
if (args[field] !== undefined) body[field] = args[field];
});
return this.ghlClient.makeRequest('POST', `/companies/`, body);
}
case 'update_company': {
const companyId = args.companyId as string;
const body: Record<string, unknown> = {};
const optionalFields = ['name', 'phone', 'email', 'website', 'address1', 'address2', 'city', 'state', 'postalCode', 'country', 'industry', 'employeeCount', 'annualRevenue', 'description', 'customFields', 'tags'];
optionalFields.forEach(field => {
if (args[field] !== undefined) body[field] = args[field];
});
return this.ghlClient.makeRequest('PUT', `/companies/${companyId}`, body);
}
case 'delete_company': {
const companyId = args.companyId as string;
return this.ghlClient.makeRequest('DELETE', `/companies/${companyId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

674
src/tools/courses-tools.ts Normal file
View File

@ -0,0 +1,674 @@
/**
* GoHighLevel Courses/Memberships Tools
* Tools for managing courses, products, and memberships
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class CoursesTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// Course Importers
{
name: 'get_course_importers',
description: 'Get list of all course import jobs/processes',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID (uses default if not provided)' },
limit: { type: 'number', description: 'Max results to return' },
offset: { type: 'number', description: 'Offset for pagination' }
}
}
},
{
name: 'create_course_importer',
description: 'Create a new course import job to import courses from external sources',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Import job name' },
sourceUrl: { type: 'string', description: 'Source URL to import from' },
type: { type: 'string', description: 'Import type' }
},
required: ['name']
}
},
// Course Products
{
name: 'get_course_products',
description: 'Get all course products (purchasable course bundles)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_course_product',
description: 'Get a specific course product by ID',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'Course product ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['productId']
}
},
{
name: 'create_course_product',
description: 'Create a new course product',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Product title' },
description: { type: 'string', description: 'Product description' },
imageUrl: { type: 'string', description: 'Product image URL' },
statementDescriptor: { type: 'string', description: 'Payment statement descriptor' }
},
required: ['title']
}
},
{
name: 'update_course_product',
description: 'Update a course product',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'Course product ID' },
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Product title' },
description: { type: 'string', description: 'Product description' },
imageUrl: { type: 'string', description: 'Product image URL' }
},
required: ['productId']
}
},
{
name: 'delete_course_product',
description: 'Delete a course product',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'Course product ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['productId']
}
},
// Categories
{
name: 'get_course_categories',
description: 'Get all course categories',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'create_course_category',
description: 'Create a new course category',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Category title' }
},
required: ['title']
}
},
{
name: 'update_course_category',
description: 'Update a course category',
inputSchema: {
type: 'object',
properties: {
categoryId: { type: 'string', description: 'Category ID' },
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Category title' }
},
required: ['categoryId', 'title']
}
},
{
name: 'delete_course_category',
description: 'Delete a course category',
inputSchema: {
type: 'object',
properties: {
categoryId: { type: 'string', description: 'Category ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['categoryId']
}
},
// Courses
{
name: 'get_courses',
description: 'Get all courses',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' },
categoryId: { type: 'string', description: 'Filter by category' }
}
}
},
{
name: 'get_course',
description: 'Get a specific course by ID',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId']
}
},
{
name: 'create_course',
description: 'Create a new course',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Course title' },
description: { type: 'string', description: 'Course description' },
thumbnailUrl: { type: 'string', description: 'Course thumbnail URL' },
visibility: { type: 'string', enum: ['published', 'draft'], description: 'Course visibility' },
categoryId: { type: 'string', description: 'Category ID to place course in' }
},
required: ['title']
}
},
{
name: 'update_course',
description: 'Update a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Course title' },
description: { type: 'string', description: 'Course description' },
thumbnailUrl: { type: 'string', description: 'Course thumbnail URL' },
visibility: { type: 'string', enum: ['published', 'draft'], description: 'Course visibility' }
},
required: ['courseId']
}
},
{
name: 'delete_course',
description: 'Delete a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId']
}
},
// Instructors
{
name: 'get_course_instructors',
description: 'Get all instructors for a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId']
}
},
{
name: 'add_course_instructor',
description: 'Add an instructor to a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' },
userId: { type: 'string', description: 'User ID of instructor' },
name: { type: 'string', description: 'Instructor display name' },
bio: { type: 'string', description: 'Instructor bio' }
},
required: ['courseId']
}
},
// Posts/Lessons
{
name: 'get_course_posts',
description: 'Get all posts/lessons in a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
},
required: ['courseId']
}
},
{
name: 'get_course_post',
description: 'Get a specific course post/lesson',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
postId: { type: 'string', description: 'Post/Lesson ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId', 'postId']
}
},
{
name: 'create_course_post',
description: 'Create a new course post/lesson',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Post/lesson title' },
contentType: { type: 'string', enum: ['video', 'text', 'quiz', 'assignment'], description: 'Content type' },
content: { type: 'string', description: 'Post content (text/HTML)' },
videoUrl: { type: 'string', description: 'Video URL (if video type)' },
visibility: { type: 'string', enum: ['published', 'draft'], description: 'Visibility' }
},
required: ['courseId', 'title']
}
},
{
name: 'update_course_post',
description: 'Update a course post/lesson',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
postId: { type: 'string', description: 'Post/Lesson ID' },
locationId: { type: 'string', description: 'Location ID' },
title: { type: 'string', description: 'Post/lesson title' },
content: { type: 'string', description: 'Post content' },
videoUrl: { type: 'string', description: 'Video URL' },
visibility: { type: 'string', enum: ['published', 'draft'], description: 'Visibility' }
},
required: ['courseId', 'postId']
}
},
{
name: 'delete_course_post',
description: 'Delete a course post/lesson',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
postId: { type: 'string', description: 'Post/Lesson ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId', 'postId']
}
},
// Offers
{
name: 'get_course_offers',
description: 'Get all offers (pricing tiers) for a course product',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'Course product ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['productId']
}
},
{
name: 'create_course_offer',
description: 'Create a new offer for a course product',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'Course product ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Offer name' },
price: { type: 'number', description: 'Price in cents' },
currency: { type: 'string', description: 'Currency code (e.g., USD)' },
type: { type: 'string', enum: ['one-time', 'subscription'], description: 'Payment type' },
interval: { type: 'string', enum: ['month', 'year'], description: 'Subscription interval (if subscription)' }
},
required: ['productId', 'name', 'price']
}
},
{
name: 'update_course_offer',
description: 'Update a course offer',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'Course product ID' },
offerId: { type: 'string', description: 'Offer ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Offer name' },
price: { type: 'number', description: 'Price in cents' }
},
required: ['productId', 'offerId']
}
},
{
name: 'delete_course_offer',
description: 'Delete a course offer',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'Course product ID' },
offerId: { type: 'string', description: 'Offer ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['productId', 'offerId']
}
},
// Student/Enrollment Management
{
name: 'get_course_enrollments',
description: 'Get all enrollments for a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
},
required: ['courseId']
}
},
{
name: 'enroll_contact_in_course',
description: 'Enroll a contact in a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
contactId: { type: 'string', description: 'Contact ID to enroll' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId', 'contactId']
}
},
{
name: 'remove_course_enrollment',
description: 'Remove a contact from a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
contactId: { type: 'string', description: 'Contact ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId', 'contactId']
}
},
// Progress tracking
{
name: 'get_student_progress',
description: 'Get a student\'s progress in a course',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
contactId: { type: 'string', description: 'Contact/Student ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['courseId', 'contactId']
}
},
{
name: 'update_lesson_completion',
description: 'Mark a lesson as complete/incomplete for a student',
inputSchema: {
type: 'object',
properties: {
courseId: { type: 'string', description: 'Course ID' },
postId: { type: 'string', description: 'Post/Lesson ID' },
contactId: { type: 'string', description: 'Contact/Student ID' },
locationId: { type: 'string', description: 'Location ID' },
completed: { type: 'boolean', description: 'Whether lesson is completed' }
},
required: ['courseId', 'postId', 'contactId', 'completed']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
// Course Importers
case 'get_course_importers': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/courses/courses-exporter?${params.toString()}`);
}
case 'create_course_importer': {
return this.ghlClient.makeRequest('POST', `/courses/courses-exporter`, {
locationId,
name: args.name,
sourceUrl: args.sourceUrl,
type: args.type
});
}
// Course Products
case 'get_course_products': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/courses/courses-exporter/products?${params.toString()}`);
}
case 'get_course_product': {
return this.ghlClient.makeRequest('GET', `/courses/courses-exporter/products/${args.productId}?locationId=${locationId}`);
}
case 'create_course_product': {
return this.ghlClient.makeRequest('POST', `/courses/courses-exporter/products`, {
locationId,
title: args.title,
description: args.description,
imageUrl: args.imageUrl,
statementDescriptor: args.statementDescriptor
});
}
case 'update_course_product': {
const body: Record<string, unknown> = { locationId };
if (args.title) body.title = args.title;
if (args.description) body.description = args.description;
if (args.imageUrl) body.imageUrl = args.imageUrl;
return this.ghlClient.makeRequest('PUT', `/courses/courses-exporter/products/${args.productId}`, body);
}
case 'delete_course_product': {
return this.ghlClient.makeRequest('DELETE', `/courses/courses-exporter/products/${args.productId}?locationId=${locationId}`);
}
// Categories
case 'get_course_categories': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/courses/categories?${params.toString()}`);
}
case 'create_course_category': {
return this.ghlClient.makeRequest('POST', `/courses/categories`, { locationId, title: args.title });
}
case 'update_course_category': {
return this.ghlClient.makeRequest('PUT', `/courses/categories/${args.categoryId}`, { locationId, title: args.title });
}
case 'delete_course_category': {
return this.ghlClient.makeRequest('DELETE', `/courses/categories/${args.categoryId}?locationId=${locationId}`);
}
// Courses
case 'get_courses': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
if (args.categoryId) params.append('categoryId', String(args.categoryId));
return this.ghlClient.makeRequest('GET', `/courses?${params.toString()}`);
}
case 'get_course': {
return this.ghlClient.makeRequest('GET', `/courses/${args.courseId}?locationId=${locationId}`);
}
case 'create_course': {
const body: Record<string, unknown> = { locationId, title: args.title };
if (args.description) body.description = args.description;
if (args.thumbnailUrl) body.thumbnailUrl = args.thumbnailUrl;
if (args.visibility) body.visibility = args.visibility;
if (args.categoryId) body.categoryId = args.categoryId;
return this.ghlClient.makeRequest('POST', `/courses`, body);
}
case 'update_course': {
const body: Record<string, unknown> = { locationId };
if (args.title) body.title = args.title;
if (args.description) body.description = args.description;
if (args.thumbnailUrl) body.thumbnailUrl = args.thumbnailUrl;
if (args.visibility) body.visibility = args.visibility;
return this.ghlClient.makeRequest('PUT', `/courses/${args.courseId}`, body);
}
case 'delete_course': {
return this.ghlClient.makeRequest('DELETE', `/courses/${args.courseId}?locationId=${locationId}`);
}
// Instructors
case 'get_course_instructors': {
return this.ghlClient.makeRequest('GET', `/courses/${args.courseId}/instructors?locationId=${locationId}`);
}
case 'add_course_instructor': {
const body: Record<string, unknown> = { locationId };
if (args.userId) body.userId = args.userId;
if (args.name) body.name = args.name;
if (args.bio) body.bio = args.bio;
return this.ghlClient.makeRequest('POST', `/courses/${args.courseId}/instructors`, body);
}
// Posts/Lessons
case 'get_course_posts': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/courses/${args.courseId}/posts?${params.toString()}`);
}
case 'get_course_post': {
return this.ghlClient.makeRequest('GET', `/courses/${args.courseId}/posts/${args.postId}?locationId=${locationId}`);
}
case 'create_course_post': {
const body: Record<string, unknown> = { locationId, title: args.title };
if (args.contentType) body.contentType = args.contentType;
if (args.content) body.content = args.content;
if (args.videoUrl) body.videoUrl = args.videoUrl;
if (args.visibility) body.visibility = args.visibility;
return this.ghlClient.makeRequest('POST', `/courses/${args.courseId}/posts`, body);
}
case 'update_course_post': {
const body: Record<string, unknown> = { locationId };
if (args.title) body.title = args.title;
if (args.content) body.content = args.content;
if (args.videoUrl) body.videoUrl = args.videoUrl;
if (args.visibility) body.visibility = args.visibility;
return this.ghlClient.makeRequest('PUT', `/courses/${args.courseId}/posts/${args.postId}`, body);
}
case 'delete_course_post': {
return this.ghlClient.makeRequest('DELETE', `/courses/${args.courseId}/posts/${args.postId}?locationId=${locationId}`);
}
// Offers
case 'get_course_offers': {
return this.ghlClient.makeRequest('GET', `/courses/courses-exporter/products/${args.productId}/offers?locationId=${locationId}`);
}
case 'create_course_offer': {
const body: Record<string, unknown> = {
locationId,
name: args.name,
price: args.price
};
if (args.currency) body.currency = args.currency;
if (args.type) body.type = args.type;
if (args.interval) body.interval = args.interval;
return this.ghlClient.makeRequest('POST', `/courses/courses-exporter/products/${args.productId}/offers`, body);
}
case 'update_course_offer': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.price) body.price = args.price;
return this.ghlClient.makeRequest('PUT', `/courses/courses-exporter/products/${args.productId}/offers/${args.offerId}`, body);
}
case 'delete_course_offer': {
return this.ghlClient.makeRequest('DELETE', `/courses/courses-exporter/products/${args.productId}/offers/${args.offerId}?locationId=${locationId}`);
}
// Enrollments
case 'get_course_enrollments': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/courses/${args.courseId}/enrollments?${params.toString()}`);
}
case 'enroll_contact_in_course': {
return this.ghlClient.makeRequest('POST', `/courses/${args.courseId}/enrollments`, {
locationId,
contactId: args.contactId
});
}
case 'remove_course_enrollment': {
return this.ghlClient.makeRequest('DELETE', `/courses/${args.courseId}/enrollments/${args.contactId}?locationId=${locationId}`);
}
// Progress
case 'get_student_progress': {
return this.ghlClient.makeRequest('GET', `/courses/${args.courseId}/progress/${args.contactId}?locationId=${locationId}`);
}
case 'update_lesson_completion': {
return this.ghlClient.makeRequest('POST', `/courses/${args.courseId}/posts/${args.postId}/completion`, {
locationId,
contactId: args.contactId,
completed: args.completed
});
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

134
src/tools/forms-tools.ts Normal file
View File

@ -0,0 +1,134 @@
/**
* GoHighLevel Forms Tools
* Tools for managing forms and form submissions
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class FormsTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_forms',
description: 'Get all forms for a location. Forms are used to collect leads and information from contacts.',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
skip: {
type: 'number',
description: 'Number of records to skip for pagination'
},
limit: {
type: 'number',
description: 'Maximum number of forms to return (default: 20, max: 100)'
},
type: {
type: 'string',
description: 'Filter by form type (e.g., "form", "survey")'
}
}
}
},
{
name: 'get_form_submissions',
description: 'Get all submissions for a specific form. Returns lead data collected through the form.',
inputSchema: {
type: 'object',
properties: {
formId: {
type: 'string',
description: 'Form ID to get submissions for'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
startAt: {
type: 'string',
description: 'Start date for filtering submissions (ISO 8601 format)'
},
endAt: {
type: 'string',
description: 'End date for filtering submissions (ISO 8601 format)'
},
skip: {
type: 'number',
description: 'Number of records to skip for pagination'
},
limit: {
type: 'number',
description: 'Maximum number of submissions to return (default: 20, max: 100)'
},
page: {
type: 'number',
description: 'Page number for pagination'
}
},
required: ['formId']
}
},
{
name: 'get_form_by_id',
description: 'Get a specific form by its ID',
inputSchema: {
type: 'object',
properties: {
formId: {
type: 'string',
description: 'The form ID to retrieve'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['formId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_forms': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
if (args.type) params.append('type', String(args.type));
return this.ghlClient.makeRequest('GET', `/forms/?${params.toString()}`);
}
case 'get_form_submissions': {
const formId = args.formId as string;
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.startAt) params.append('startAt', String(args.startAt));
if (args.endAt) params.append('endAt', String(args.endAt));
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
if (args.page) params.append('page', String(args.page));
return this.ghlClient.makeRequest('GET', `/forms/submissions?formId=${formId}&${params.toString()}`);
}
case 'get_form_by_id': {
const formId = args.formId as string;
return this.ghlClient.makeRequest('GET', `/forms/${formId}?locationId=${locationId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

311
src/tools/funnels-tools.ts Normal file
View File

@ -0,0 +1,311 @@
/**
* GoHighLevel Funnels Tools
* Tools for managing funnels and funnel pages
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class FunnelsTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_funnels',
description: 'Get all funnels for a location. Funnels are multi-page sales/marketing flows.',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
skip: {
type: 'number',
description: 'Number of records to skip for pagination'
},
limit: {
type: 'number',
description: 'Maximum number of funnels to return (default: 10)'
},
name: {
type: 'string',
description: 'Filter by funnel name (partial match)'
},
category: {
type: 'string',
description: 'Filter by category'
},
parentId: {
type: 'string',
description: 'Filter by parent folder ID'
},
type: {
type: 'string',
enum: ['funnel', 'website'],
description: 'Filter by type (funnel or website)'
}
}
}
},
{
name: 'get_funnel',
description: 'Get a specific funnel by ID with all its pages',
inputSchema: {
type: 'object',
properties: {
funnelId: {
type: 'string',
description: 'The funnel ID to retrieve'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['funnelId']
}
},
{
name: 'get_funnel_pages',
description: 'Get all pages for a specific funnel',
inputSchema: {
type: 'object',
properties: {
funnelId: {
type: 'string',
description: 'The funnel ID to get pages for'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
skip: {
type: 'number',
description: 'Number of records to skip'
},
limit: {
type: 'number',
description: 'Maximum number of pages to return'
}
},
required: ['funnelId']
}
},
{
name: 'count_funnel_pages',
description: 'Get the total count of pages for a funnel',
inputSchema: {
type: 'object',
properties: {
funnelId: {
type: 'string',
description: 'The funnel ID'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['funnelId']
}
},
{
name: 'create_funnel_redirect',
description: 'Create a URL redirect for a funnel',
inputSchema: {
type: 'object',
properties: {
funnelId: {
type: 'string',
description: 'The funnel ID'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
target: {
type: 'string',
description: 'Target URL to redirect to'
},
action: {
type: 'string',
enum: ['funnel', 'website', 'url', 'all'],
description: 'Redirect action type'
},
pathName: {
type: 'string',
description: 'Source path for the redirect'
}
},
required: ['funnelId', 'target', 'action']
}
},
{
name: 'update_funnel_redirect',
description: 'Update an existing funnel redirect',
inputSchema: {
type: 'object',
properties: {
funnelId: {
type: 'string',
description: 'The funnel ID'
},
redirectId: {
type: 'string',
description: 'The redirect ID to update'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
target: {
type: 'string',
description: 'Target URL to redirect to'
},
action: {
type: 'string',
enum: ['funnel', 'website', 'url', 'all'],
description: 'Redirect action type'
},
pathName: {
type: 'string',
description: 'Source path for the redirect'
}
},
required: ['funnelId', 'redirectId']
}
},
{
name: 'delete_funnel_redirect',
description: 'Delete a funnel redirect',
inputSchema: {
type: 'object',
properties: {
funnelId: {
type: 'string',
description: 'The funnel ID'
},
redirectId: {
type: 'string',
description: 'The redirect ID to delete'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['funnelId', 'redirectId']
}
},
{
name: 'get_funnel_redirects',
description: 'List all redirects for a funnel',
inputSchema: {
type: 'object',
properties: {
funnelId: {
type: 'string',
description: 'The funnel ID'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
skip: {
type: 'number',
description: 'Number of records to skip'
},
limit: {
type: 'number',
description: 'Maximum number of redirects to return'
}
},
required: ['funnelId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_funnels': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
if (args.name) params.append('name', String(args.name));
if (args.category) params.append('category', String(args.category));
if (args.parentId) params.append('parentId', String(args.parentId));
if (args.type) params.append('type', String(args.type));
return this.ghlClient.makeRequest('GET', `/funnels/?${params.toString()}`);
}
case 'get_funnel': {
const funnelId = args.funnelId as string;
return this.ghlClient.makeRequest('GET', `/funnels/${funnelId}?locationId=${locationId}`);
}
case 'get_funnel_pages': {
const funnelId = args.funnelId as string;
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
return this.ghlClient.makeRequest('GET', `/funnels/${funnelId}/pages?${params.toString()}`);
}
case 'count_funnel_pages': {
const funnelId = args.funnelId as string;
return this.ghlClient.makeRequest('GET', `/funnels/${funnelId}/pages/count?locationId=${locationId}`);
}
case 'create_funnel_redirect': {
const funnelId = args.funnelId as string;
const body: Record<string, unknown> = {
locationId,
target: args.target,
action: args.action
};
if (args.pathName) body.pathName = args.pathName;
return this.ghlClient.makeRequest('POST', `/funnels/${funnelId}/redirect`, body);
}
case 'update_funnel_redirect': {
const funnelId = args.funnelId as string;
const redirectId = args.redirectId as string;
const body: Record<string, unknown> = { locationId };
if (args.target) body.target = args.target;
if (args.action) body.action = args.action;
if (args.pathName) body.pathName = args.pathName;
return this.ghlClient.makeRequest('PATCH', `/funnels/${funnelId}/redirect/${redirectId}`, body);
}
case 'delete_funnel_redirect': {
const funnelId = args.funnelId as string;
const redirectId = args.redirectId as string;
return this.ghlClient.makeRequest('DELETE', `/funnels/${funnelId}/redirect/${redirectId}?locationId=${locationId}`);
}
case 'get_funnel_redirects': {
const funnelId = args.funnelId as string;
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
return this.ghlClient.makeRequest('GET', `/funnels/${funnelId}/redirect?${params.toString()}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

188
src/tools/links-tools.ts Normal file
View File

@ -0,0 +1,188 @@
/**
* GoHighLevel Links (Trigger Links) Tools
* Tools for managing trigger links and link tracking
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class LinksTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_links',
description: 'Get all trigger links for a location. Trigger links are trackable URLs that can trigger automations.',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
skip: {
type: 'number',
description: 'Number of records to skip for pagination'
},
limit: {
type: 'number',
description: 'Maximum number of links to return'
}
}
}
},
{
name: 'get_link',
description: 'Get a specific trigger link by ID',
inputSchema: {
type: 'object',
properties: {
linkId: {
type: 'string',
description: 'The link ID to retrieve'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['linkId']
}
},
{
name: 'create_link',
description: 'Create a new trigger link',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
name: {
type: 'string',
description: 'Link name for identification'
},
redirectTo: {
type: 'string',
description: 'Target URL to redirect to when clicked'
},
fieldKey: {
type: 'string',
description: 'Custom field key to update on click'
},
fieldValue: {
type: 'string',
description: 'Value to set for the custom field'
}
},
required: ['name', 'redirectTo']
}
},
{
name: 'update_link',
description: 'Update an existing trigger link',
inputSchema: {
type: 'object',
properties: {
linkId: {
type: 'string',
description: 'The link ID to update'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
name: {
type: 'string',
description: 'Link name for identification'
},
redirectTo: {
type: 'string',
description: 'Target URL to redirect to when clicked'
},
fieldKey: {
type: 'string',
description: 'Custom field key to update on click'
},
fieldValue: {
type: 'string',
description: 'Value to set for the custom field'
}
},
required: ['linkId']
}
},
{
name: 'delete_link',
description: 'Delete a trigger link',
inputSchema: {
type: 'object',
properties: {
linkId: {
type: 'string',
description: 'The link ID to delete'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['linkId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_links': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
return this.ghlClient.makeRequest('GET', `/links/?${params.toString()}`);
}
case 'get_link': {
const linkId = args.linkId as string;
return this.ghlClient.makeRequest('GET', `/links/${linkId}?locationId=${locationId}`);
}
case 'create_link': {
const body: Record<string, unknown> = {
locationId,
name: args.name,
redirectTo: args.redirectTo
};
if (args.fieldKey) body.fieldKey = args.fieldKey;
if (args.fieldValue) body.fieldValue = args.fieldValue;
return this.ghlClient.makeRequest('POST', `/links/`, body);
}
case 'update_link': {
const linkId = args.linkId as string;
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.redirectTo) body.redirectTo = args.redirectTo;
if (args.fieldKey) body.fieldKey = args.fieldKey;
if (args.fieldValue) body.fieldValue = args.fieldValue;
return this.ghlClient.makeRequest('PUT', `/links/${linkId}`, body);
}
case 'delete_link': {
const linkId = args.linkId as string;
return this.ghlClient.makeRequest('DELETE', `/links/${linkId}?locationId=${locationId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

200
src/tools/oauth-tools.ts Normal file
View File

@ -0,0 +1,200 @@
/**
* GoHighLevel OAuth/Auth Tools
* Tools for managing OAuth apps, tokens, and integrations
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class OAuthTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// OAuth Apps
{
name: 'get_oauth_apps',
description: 'Get all OAuth applications/integrations for a location',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
companyId: { type: 'string', description: 'Company ID for agency-level apps' }
}
}
},
{
name: 'get_oauth_app',
description: 'Get a specific OAuth application by ID',
inputSchema: {
type: 'object',
properties: {
appId: { type: 'string', description: 'OAuth App ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['appId']
}
},
{
name: 'get_installed_locations',
description: 'Get all locations where an OAuth app is installed',
inputSchema: {
type: 'object',
properties: {
appId: { type: 'string', description: 'OAuth App ID' },
companyId: { type: 'string', description: 'Company ID' },
skip: { type: 'number', description: 'Records to skip' },
limit: { type: 'number', description: 'Max results' },
query: { type: 'string', description: 'Search query' },
isInstalled: { type: 'boolean', description: 'Filter by installation status' }
},
required: ['appId', 'companyId']
}
},
// Access Tokens
{
name: 'get_access_token_info',
description: 'Get information about the current access token',
inputSchema: {
type: 'object',
properties: {}
}
},
{
name: 'get_location_access_token',
description: 'Get an access token for a specific location (agency use)',
inputSchema: {
type: 'object',
properties: {
companyId: { type: 'string', description: 'Company/Agency ID' },
locationId: { type: 'string', description: 'Target Location ID' }
},
required: ['companyId', 'locationId']
}
},
// Connected Integrations
{
name: 'get_connected_integrations',
description: 'Get all connected third-party integrations for a location',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'disconnect_integration',
description: 'Disconnect a third-party integration',
inputSchema: {
type: 'object',
properties: {
integrationId: { type: 'string', description: 'Integration ID to disconnect' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['integrationId']
}
},
// API Keys
{
name: 'get_api_keys',
description: 'List all API keys for a location',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'create_api_key',
description: 'Create a new API key',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'API key name/label' },
scopes: {
type: 'array',
items: { type: 'string' },
description: 'Permission scopes for the key'
}
},
required: ['name']
}
},
{
name: 'delete_api_key',
description: 'Delete/revoke an API key',
inputSchema: {
type: 'object',
properties: {
keyId: { type: 'string', description: 'API Key ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['keyId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_oauth_apps': {
const params = new URLSearchParams();
if (locationId) params.append('locationId', locationId);
if (args.companyId) params.append('companyId', String(args.companyId));
return this.ghlClient.makeRequest('GET', `/oauth/apps?${params.toString()}`);
}
case 'get_oauth_app': {
return this.ghlClient.makeRequest('GET', `/oauth/apps/${args.appId}?locationId=${locationId}`);
}
case 'get_installed_locations': {
const params = new URLSearchParams();
params.append('appId', String(args.appId));
params.append('companyId', String(args.companyId));
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
if (args.query) params.append('query', String(args.query));
if (args.isInstalled !== undefined) params.append('isInstalled', String(args.isInstalled));
return this.ghlClient.makeRequest('GET', `/oauth/installedLocations?${params.toString()}`);
}
case 'get_access_token_info': {
return this.ghlClient.makeRequest('GET', `/oauth/locationToken`);
}
case 'get_location_access_token': {
return this.ghlClient.makeRequest('POST', `/oauth/locationToken`, {
companyId: args.companyId,
locationId: args.locationId
});
}
case 'get_connected_integrations': {
return this.ghlClient.makeRequest('GET', `/integrations/connected?locationId=${locationId}`);
}
case 'disconnect_integration': {
return this.ghlClient.makeRequest('DELETE', `/integrations/${args.integrationId}?locationId=${locationId}`);
}
case 'get_api_keys': {
return this.ghlClient.makeRequest('GET', `/oauth/api-keys?locationId=${locationId}`);
}
case 'create_api_key': {
return this.ghlClient.makeRequest('POST', `/oauth/api-keys`, {
locationId,
name: args.name,
scopes: args.scopes
});
}
case 'delete_api_key': {
return this.ghlClient.makeRequest('DELETE', `/oauth/api-keys/${args.keyId}?locationId=${locationId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

417
src/tools/phone-tools.ts Normal file
View File

@ -0,0 +1,417 @@
/**
* GoHighLevel Phone System Tools
* Tools for managing phone numbers, call settings, and IVR
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class PhoneTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// Phone Numbers
{
name: 'get_phone_numbers',
description: 'Get all phone numbers for a location',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'get_phone_number',
description: 'Get a specific phone number by ID',
inputSchema: {
type: 'object',
properties: {
phoneNumberId: { type: 'string', description: 'Phone Number ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['phoneNumberId']
}
},
{
name: 'search_available_numbers',
description: 'Search for available phone numbers to purchase',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
country: { type: 'string', description: 'Country code (e.g., US, CA)' },
areaCode: { type: 'string', description: 'Area code to search' },
contains: { type: 'string', description: 'Number pattern to search for' },
type: { type: 'string', enum: ['local', 'tollfree', 'mobile'], description: 'Number type' }
},
required: ['country']
}
},
{
name: 'purchase_phone_number',
description: 'Purchase a phone number',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
phoneNumber: { type: 'string', description: 'Phone number to purchase' },
name: { type: 'string', description: 'Friendly name for the number' }
},
required: ['phoneNumber']
}
},
{
name: 'update_phone_number',
description: 'Update phone number settings',
inputSchema: {
type: 'object',
properties: {
phoneNumberId: { type: 'string', description: 'Phone Number ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Friendly name' },
forwardingNumber: { type: 'string', description: 'Number to forward calls to' },
callRecording: { type: 'boolean', description: 'Enable call recording' },
whisperMessage: { type: 'string', description: 'Whisper message played to agent' }
},
required: ['phoneNumberId']
}
},
{
name: 'release_phone_number',
description: 'Release/delete a phone number',
inputSchema: {
type: 'object',
properties: {
phoneNumberId: { type: 'string', description: 'Phone Number ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['phoneNumberId']
}
},
// Call Forwarding
{
name: 'get_call_forwarding_settings',
description: 'Get call forwarding configuration',
inputSchema: {
type: 'object',
properties: {
phoneNumberId: { type: 'string', description: 'Phone Number ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['phoneNumberId']
}
},
{
name: 'update_call_forwarding',
description: 'Update call forwarding settings',
inputSchema: {
type: 'object',
properties: {
phoneNumberId: { type: 'string', description: 'Phone Number ID' },
locationId: { type: 'string', description: 'Location ID' },
enabled: { type: 'boolean', description: 'Enable forwarding' },
forwardTo: { type: 'string', description: 'Number to forward to' },
ringTimeout: { type: 'number', description: 'Ring timeout in seconds' },
voicemailEnabled: { type: 'boolean', description: 'Enable voicemail on no answer' }
},
required: ['phoneNumberId']
}
},
// IVR/Call Menu
{
name: 'get_ivr_menus',
description: 'Get all IVR/call menus',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'create_ivr_menu',
description: 'Create an IVR/call menu',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Menu name' },
greeting: { type: 'string', description: 'Greeting message (text or URL)' },
options: {
type: 'array',
items: {
type: 'object',
properties: {
digit: { type: 'string', description: 'Digit to press (0-9, *, #)' },
action: { type: 'string', description: 'Action type' },
destination: { type: 'string', description: 'Action destination' }
}
},
description: 'Menu options'
}
},
required: ['name', 'greeting']
}
},
{
name: 'update_ivr_menu',
description: 'Update an IVR menu',
inputSchema: {
type: 'object',
properties: {
menuId: { type: 'string', description: 'IVR Menu ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Menu name' },
greeting: { type: 'string', description: 'Greeting message' },
options: { type: 'array', description: 'Menu options' }
},
required: ['menuId']
}
},
{
name: 'delete_ivr_menu',
description: 'Delete an IVR menu',
inputSchema: {
type: 'object',
properties: {
menuId: { type: 'string', description: 'IVR Menu ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['menuId']
}
},
// Voicemail
{
name: 'get_voicemail_settings',
description: 'Get voicemail settings',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'update_voicemail_settings',
description: 'Update voicemail settings',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
enabled: { type: 'boolean', description: 'Enable voicemail' },
greeting: { type: 'string', description: 'Voicemail greeting (text or URL)' },
transcriptionEnabled: { type: 'boolean', description: 'Enable transcription' },
notificationEmail: { type: 'string', description: 'Email for voicemail notifications' }
}
}
},
{
name: 'get_voicemails',
description: 'Get voicemail messages',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
phoneNumberId: { type: 'string', description: 'Filter by phone number' },
status: { type: 'string', enum: ['new', 'read', 'archived'], description: 'Filter by status' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'delete_voicemail',
description: 'Delete a voicemail message',
inputSchema: {
type: 'object',
properties: {
voicemailId: { type: 'string', description: 'Voicemail ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['voicemailId']
}
},
// Caller ID
{
name: 'get_caller_ids',
description: 'Get verified caller IDs',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'add_caller_id',
description: 'Add a caller ID for verification',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
phoneNumber: { type: 'string', description: 'Phone number to verify' },
name: { type: 'string', description: 'Friendly name' }
},
required: ['phoneNumber']
}
},
{
name: 'verify_caller_id',
description: 'Submit verification code for caller ID',
inputSchema: {
type: 'object',
properties: {
callerIdId: { type: 'string', description: 'Caller ID record ID' },
locationId: { type: 'string', description: 'Location ID' },
code: { type: 'string', description: 'Verification code' }
},
required: ['callerIdId', 'code']
}
},
{
name: 'delete_caller_id',
description: 'Delete a caller ID',
inputSchema: {
type: 'object',
properties: {
callerIdId: { type: 'string', description: 'Caller ID record ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['callerIdId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
// Phone Numbers
case 'get_phone_numbers': {
return this.ghlClient.makeRequest('GET', `/phone-numbers/?locationId=${locationId}`);
}
case 'get_phone_number': {
return this.ghlClient.makeRequest('GET', `/phone-numbers/${args.phoneNumberId}?locationId=${locationId}`);
}
case 'search_available_numbers': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('country', String(args.country));
if (args.areaCode) params.append('areaCode', String(args.areaCode));
if (args.contains) params.append('contains', String(args.contains));
if (args.type) params.append('type', String(args.type));
return this.ghlClient.makeRequest('GET', `/phone-numbers/available?${params.toString()}`);
}
case 'purchase_phone_number': {
return this.ghlClient.makeRequest('POST', `/phone-numbers/`, {
locationId,
phoneNumber: args.phoneNumber,
name: args.name
});
}
case 'update_phone_number': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.forwardingNumber) body.forwardingNumber = args.forwardingNumber;
if (args.callRecording !== undefined) body.callRecording = args.callRecording;
if (args.whisperMessage) body.whisperMessage = args.whisperMessage;
return this.ghlClient.makeRequest('PUT', `/phone-numbers/${args.phoneNumberId}`, body);
}
case 'release_phone_number': {
return this.ghlClient.makeRequest('DELETE', `/phone-numbers/${args.phoneNumberId}?locationId=${locationId}`);
}
// Call Forwarding
case 'get_call_forwarding_settings': {
return this.ghlClient.makeRequest('GET', `/phone-numbers/${args.phoneNumberId}/forwarding?locationId=${locationId}`);
}
case 'update_call_forwarding': {
const body: Record<string, unknown> = { locationId };
if (args.enabled !== undefined) body.enabled = args.enabled;
if (args.forwardTo) body.forwardTo = args.forwardTo;
if (args.ringTimeout) body.ringTimeout = args.ringTimeout;
if (args.voicemailEnabled !== undefined) body.voicemailEnabled = args.voicemailEnabled;
return this.ghlClient.makeRequest('PUT', `/phone-numbers/${args.phoneNumberId}/forwarding`, body);
}
// IVR
case 'get_ivr_menus': {
return this.ghlClient.makeRequest('GET', `/phone-numbers/ivr?locationId=${locationId}`);
}
case 'create_ivr_menu': {
return this.ghlClient.makeRequest('POST', `/phone-numbers/ivr`, {
locationId,
name: args.name,
greeting: args.greeting,
options: args.options
});
}
case 'update_ivr_menu': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.greeting) body.greeting = args.greeting;
if (args.options) body.options = args.options;
return this.ghlClient.makeRequest('PUT', `/phone-numbers/ivr/${args.menuId}`, body);
}
case 'delete_ivr_menu': {
return this.ghlClient.makeRequest('DELETE', `/phone-numbers/ivr/${args.menuId}?locationId=${locationId}`);
}
// Voicemail
case 'get_voicemail_settings': {
return this.ghlClient.makeRequest('GET', `/phone-numbers/voicemail/settings?locationId=${locationId}`);
}
case 'update_voicemail_settings': {
const body: Record<string, unknown> = { locationId };
if (args.enabled !== undefined) body.enabled = args.enabled;
if (args.greeting) body.greeting = args.greeting;
if (args.transcriptionEnabled !== undefined) body.transcriptionEnabled = args.transcriptionEnabled;
if (args.notificationEmail) body.notificationEmail = args.notificationEmail;
return this.ghlClient.makeRequest('PUT', `/phone-numbers/voicemail/settings`, body);
}
case 'get_voicemails': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.phoneNumberId) params.append('phoneNumberId', String(args.phoneNumberId));
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/phone-numbers/voicemail?${params.toString()}`);
}
case 'delete_voicemail': {
return this.ghlClient.makeRequest('DELETE', `/phone-numbers/voicemail/${args.voicemailId}?locationId=${locationId}`);
}
// Caller ID
case 'get_caller_ids': {
return this.ghlClient.makeRequest('GET', `/phone-numbers/caller-id?locationId=${locationId}`);
}
case 'add_caller_id': {
return this.ghlClient.makeRequest('POST', `/phone-numbers/caller-id`, {
locationId,
phoneNumber: args.phoneNumber,
name: args.name
});
}
case 'verify_caller_id': {
return this.ghlClient.makeRequest('POST', `/phone-numbers/caller-id/${args.callerIdId}/verify`, {
locationId,
code: args.code
});
}
case 'delete_caller_id': {
return this.ghlClient.makeRequest('DELETE', `/phone-numbers/caller-id/${args.callerIdId}?locationId=${locationId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,310 @@
/**
* GoHighLevel Reporting/Analytics Tools
* Tools for accessing reports and analytics
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class ReportingTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// Attribution Reports
{
name: 'get_attribution_report',
description: 'Get attribution/source tracking report showing where leads came from',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }
},
required: ['startDate', 'endDate']
}
},
// Call Reports
{
name: 'get_call_reports',
description: 'Get call activity reports including call duration, outcomes, etc.',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
userId: { type: 'string', description: 'Filter by user ID' },
type: { type: 'string', enum: ['inbound', 'outbound', 'all'], description: 'Call type filter' }
},
required: ['startDate', 'endDate']
}
},
// Appointment Reports
{
name: 'get_appointment_reports',
description: 'Get appointment activity reports',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
calendarId: { type: 'string', description: 'Filter by calendar ID' },
status: { type: 'string', enum: ['booked', 'confirmed', 'showed', 'noshow', 'cancelled'], description: 'Appointment status filter' }
},
required: ['startDate', 'endDate']
}
},
// Pipeline/Opportunity Reports
{
name: 'get_pipeline_reports',
description: 'Get pipeline/opportunity performance reports',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
pipelineId: { type: 'string', description: 'Filter by pipeline ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
userId: { type: 'string', description: 'Filter by assigned user' }
},
required: ['startDate', 'endDate']
}
},
// Email/SMS Reports
{
name: 'get_email_reports',
description: 'Get email performance reports (deliverability, opens, clicks)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }
},
required: ['startDate', 'endDate']
}
},
{
name: 'get_sms_reports',
description: 'Get SMS performance reports',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }
},
required: ['startDate', 'endDate']
}
},
// Funnel Reports
{
name: 'get_funnel_reports',
description: 'Get funnel performance reports (page views, conversions)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
funnelId: { type: 'string', description: 'Filter by funnel ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }
},
required: ['startDate', 'endDate']
}
},
// Google/Facebook Ad Reports
{
name: 'get_ad_reports',
description: 'Get advertising performance reports (Google/Facebook ads)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
platform: { type: 'string', enum: ['google', 'facebook', 'all'], description: 'Ad platform' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }
},
required: ['startDate', 'endDate']
}
},
// Agent Performance
{
name: 'get_agent_reports',
description: 'Get agent/user performance reports',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
userId: { type: 'string', description: 'Filter by user ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }
},
required: ['startDate', 'endDate']
}
},
// Dashboard Stats
{
name: 'get_dashboard_stats',
description: 'Get main dashboard statistics overview',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
dateRange: { type: 'string', enum: ['today', 'yesterday', 'last7days', 'last30days', 'thisMonth', 'lastMonth', 'custom'], description: 'Date range preset' },
startDate: { type: 'string', description: 'Start date for custom range' },
endDate: { type: 'string', description: 'End date for custom range' }
}
}
},
// Conversion Reports
{
name: 'get_conversion_reports',
description: 'Get conversion tracking reports',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
source: { type: 'string', description: 'Filter by source' }
},
required: ['startDate', 'endDate']
}
},
// Revenue Reports
{
name: 'get_revenue_reports',
description: 'Get revenue/payment reports',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
groupBy: { type: 'string', enum: ['day', 'week', 'month'], description: 'Group results by' }
},
required: ['startDate', 'endDate']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_attribution_report': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
return this.ghlClient.makeRequest('GET', `/reporting/attribution?${params.toString()}`);
}
case 'get_call_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.userId) params.append('userId', String(args.userId));
if (args.type) params.append('type', String(args.type));
return this.ghlClient.makeRequest('GET', `/reporting/calls?${params.toString()}`);
}
case 'get_appointment_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.calendarId) params.append('calendarId', String(args.calendarId));
if (args.status) params.append('status', String(args.status));
return this.ghlClient.makeRequest('GET', `/reporting/appointments?${params.toString()}`);
}
case 'get_pipeline_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.pipelineId) params.append('pipelineId', String(args.pipelineId));
if (args.userId) params.append('userId', String(args.userId));
return this.ghlClient.makeRequest('GET', `/reporting/pipelines?${params.toString()}`);
}
case 'get_email_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
return this.ghlClient.makeRequest('GET', `/reporting/emails?${params.toString()}`);
}
case 'get_sms_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
return this.ghlClient.makeRequest('GET', `/reporting/sms?${params.toString()}`);
}
case 'get_funnel_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.funnelId) params.append('funnelId', String(args.funnelId));
return this.ghlClient.makeRequest('GET', `/reporting/funnels?${params.toString()}`);
}
case 'get_ad_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.platform) params.append('platform', String(args.platform));
return this.ghlClient.makeRequest('GET', `/reporting/ads?${params.toString()}`);
}
case 'get_agent_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.userId) params.append('userId', String(args.userId));
return this.ghlClient.makeRequest('GET', `/reporting/agents?${params.toString()}`);
}
case 'get_dashboard_stats': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.dateRange) params.append('dateRange', String(args.dateRange));
if (args.startDate) params.append('startDate', String(args.startDate));
if (args.endDate) params.append('endDate', String(args.endDate));
return this.ghlClient.makeRequest('GET', `/reporting/dashboard?${params.toString()}`);
}
case 'get_conversion_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.source) params.append('source', String(args.source));
return this.ghlClient.makeRequest('GET', `/reporting/conversions?${params.toString()}`);
}
case 'get_revenue_reports': {
const params = new URLSearchParams();
params.append('locationId', locationId);
params.append('startDate', String(args.startDate));
params.append('endDate', String(args.endDate));
if (args.groupBy) params.append('groupBy', String(args.groupBy));
return this.ghlClient.makeRequest('GET', `/reporting/revenue?${params.toString()}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,322 @@
/**
* GoHighLevel Reputation/Reviews Tools
* Tools for managing reviews, reputation, and business listings
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class ReputationTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// Reviews
{
name: 'get_reviews',
description: 'Get all reviews for a location from various platforms',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
platform: { type: 'string', enum: ['google', 'facebook', 'yelp', 'all'], description: 'Filter by platform' },
rating: { type: 'number', description: 'Filter by minimum rating (1-5)' },
status: { type: 'string', enum: ['replied', 'unreplied', 'all'], description: 'Filter by reply status' },
startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_review',
description: 'Get a specific review by ID',
inputSchema: {
type: 'object',
properties: {
reviewId: { type: 'string', description: 'Review ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['reviewId']
}
},
{
name: 'reply_to_review',
description: 'Reply to a review',
inputSchema: {
type: 'object',
properties: {
reviewId: { type: 'string', description: 'Review ID' },
locationId: { type: 'string', description: 'Location ID' },
reply: { type: 'string', description: 'Reply text' }
},
required: ['reviewId', 'reply']
}
},
{
name: 'update_review_reply',
description: 'Update a review reply',
inputSchema: {
type: 'object',
properties: {
reviewId: { type: 'string', description: 'Review ID' },
locationId: { type: 'string', description: 'Location ID' },
reply: { type: 'string', description: 'Updated reply text' }
},
required: ['reviewId', 'reply']
}
},
{
name: 'delete_review_reply',
description: 'Delete a review reply',
inputSchema: {
type: 'object',
properties: {
reviewId: { type: 'string', description: 'Review ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['reviewId']
}
},
// Review Stats
{
name: 'get_review_stats',
description: 'Get review statistics/summary',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
platform: { type: 'string', enum: ['google', 'facebook', 'yelp', 'all'], description: 'Platform filter' },
startDate: { type: 'string', description: 'Start date' },
endDate: { type: 'string', description: 'End date' }
}
}
},
// Review Requests
{
name: 'send_review_request',
description: 'Send a review request to a contact',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
contactId: { type: 'string', description: 'Contact ID to request review from' },
platform: { type: 'string', enum: ['google', 'facebook', 'yelp'], description: 'Platform to request review on' },
method: { type: 'string', enum: ['sms', 'email', 'both'], description: 'Delivery method' },
message: { type: 'string', description: 'Custom message (optional)' }
},
required: ['contactId', 'platform', 'method']
}
},
{
name: 'get_review_requests',
description: 'Get sent review requests',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
contactId: { type: 'string', description: 'Filter by contact' },
status: { type: 'string', enum: ['sent', 'clicked', 'reviewed', 'all'], description: 'Status filter' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
// Connected Platforms
{
name: 'get_connected_review_platforms',
description: 'Get connected review platforms (Google, Facebook, etc.)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'connect_google_business',
description: 'Initiate Google Business Profile connection',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'disconnect_review_platform',
description: 'Disconnect a review platform',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
platform: { type: 'string', enum: ['google', 'facebook', 'yelp'], description: 'Platform to disconnect' }
},
required: ['platform']
}
},
// Review Links
{
name: 'get_review_links',
description: 'Get direct review links for platforms',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'update_review_links',
description: 'Update custom review links',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
googleLink: { type: 'string', description: 'Custom Google review link' },
facebookLink: { type: 'string', description: 'Custom Facebook review link' },
yelpLink: { type: 'string', description: 'Custom Yelp review link' }
}
}
},
// Review Widgets
{
name: 'get_review_widget_settings',
description: 'Get review widget embed settings',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'update_review_widget_settings',
description: 'Update review widget settings',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
enabled: { type: 'boolean', description: 'Enable widget' },
minRating: { type: 'number', description: 'Minimum rating to display' },
platforms: { type: 'array', items: { type: 'string' }, description: 'Platforms to show' },
layout: { type: 'string', enum: ['grid', 'carousel', 'list'], description: 'Widget layout' }
}
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
// Reviews
case 'get_reviews': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.platform) params.append('platform', String(args.platform));
if (args.rating) params.append('rating', String(args.rating));
if (args.status) params.append('status', String(args.status));
if (args.startDate) params.append('startDate', String(args.startDate));
if (args.endDate) params.append('endDate', String(args.endDate));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/reputation/reviews?${params.toString()}`);
}
case 'get_review': {
return this.ghlClient.makeRequest('GET', `/reputation/reviews/${args.reviewId}?locationId=${locationId}`);
}
case 'reply_to_review': {
return this.ghlClient.makeRequest('POST', `/reputation/reviews/${args.reviewId}/reply`, {
locationId,
reply: args.reply
});
}
case 'update_review_reply': {
return this.ghlClient.makeRequest('PUT', `/reputation/reviews/${args.reviewId}/reply`, {
locationId,
reply: args.reply
});
}
case 'delete_review_reply': {
return this.ghlClient.makeRequest('DELETE', `/reputation/reviews/${args.reviewId}/reply?locationId=${locationId}`);
}
// Stats
case 'get_review_stats': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.platform) params.append('platform', String(args.platform));
if (args.startDate) params.append('startDate', String(args.startDate));
if (args.endDate) params.append('endDate', String(args.endDate));
return this.ghlClient.makeRequest('GET', `/reputation/stats?${params.toString()}`);
}
// Review Requests
case 'send_review_request': {
return this.ghlClient.makeRequest('POST', `/reputation/review-requests`, {
locationId,
contactId: args.contactId,
platform: args.platform,
method: args.method,
message: args.message
});
}
case 'get_review_requests': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.contactId) params.append('contactId', String(args.contactId));
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/reputation/review-requests?${params.toString()}`);
}
// Platforms
case 'get_connected_review_platforms': {
return this.ghlClient.makeRequest('GET', `/reputation/platforms?locationId=${locationId}`);
}
case 'connect_google_business': {
return this.ghlClient.makeRequest('POST', `/reputation/platforms/google/connect`, { locationId });
}
case 'disconnect_review_platform': {
return this.ghlClient.makeRequest('DELETE', `/reputation/platforms/${args.platform}?locationId=${locationId}`);
}
// Links
case 'get_review_links': {
return this.ghlClient.makeRequest('GET', `/reputation/links?locationId=${locationId}`);
}
case 'update_review_links': {
const body: Record<string, unknown> = { locationId };
if (args.googleLink) body.googleLink = args.googleLink;
if (args.facebookLink) body.facebookLink = args.facebookLink;
if (args.yelpLink) body.yelpLink = args.yelpLink;
return this.ghlClient.makeRequest('PUT', `/reputation/links`, body);
}
// Widgets
case 'get_review_widget_settings': {
return this.ghlClient.makeRequest('GET', `/reputation/widget?locationId=${locationId}`);
}
case 'update_review_widget_settings': {
const body: Record<string, unknown> = { locationId };
if (args.enabled !== undefined) body.enabled = args.enabled;
if (args.minRating) body.minRating = args.minRating;
if (args.platforms) body.platforms = args.platforms;
if (args.layout) body.layout = args.layout;
return this.ghlClient.makeRequest('PUT', `/reputation/widget`, body);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

220
src/tools/saas-tools.ts Normal file
View File

@ -0,0 +1,220 @@
/**
* GoHighLevel SaaS/Agency Tools
* Tools for agency-level operations (company/agency management)
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class SaasTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_saas_locations',
description: 'Get all sub-accounts/locations for a SaaS agency. Requires agency-level access.',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
skip: {
type: 'number',
description: 'Number of records to skip'
},
limit: {
type: 'number',
description: 'Maximum number of locations to return (default: 10, max: 100)'
},
order: {
type: 'string',
enum: ['asc', 'desc'],
description: 'Sort order'
},
isActive: {
type: 'boolean',
description: 'Filter by active status'
}
},
required: ['companyId']
}
},
{
name: 'get_saas_location',
description: 'Get a specific sub-account/location by ID at the agency level',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
locationId: {
type: 'string',
description: 'Location ID to retrieve'
}
},
required: ['companyId', 'locationId']
}
},
{
name: 'update_saas_subscription',
description: 'Update SaaS subscription settings for a location',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
locationId: {
type: 'string',
description: 'Location ID'
},
subscriptionId: {
type: 'string',
description: 'Subscription ID'
},
status: {
type: 'string',
enum: ['active', 'paused', 'cancelled'],
description: 'Subscription status'
}
},
required: ['companyId', 'locationId']
}
},
{
name: 'pause_saas_location',
description: 'Pause a SaaS sub-account/location',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
locationId: {
type: 'string',
description: 'Location ID to pause'
},
paused: {
type: 'boolean',
description: 'Whether to pause (true) or unpause (false)'
}
},
required: ['companyId', 'locationId', 'paused']
}
},
{
name: 'enable_saas_location',
description: 'Enable or disable SaaS features for a location',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
locationId: {
type: 'string',
description: 'Location ID'
},
enabled: {
type: 'boolean',
description: 'Whether to enable (true) or disable (false) SaaS'
}
},
required: ['companyId', 'locationId', 'enabled']
}
},
{
name: 'rebilling_update',
description: 'Update rebilling configuration for agency',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
product: {
type: 'string',
description: 'Product to configure rebilling for'
},
markup: {
type: 'number',
description: 'Markup percentage'
},
enabled: {
type: 'boolean',
description: 'Whether rebilling is enabled'
}
},
required: ['companyId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const companyId = args.companyId as string;
switch (toolName) {
case 'get_saas_locations': {
const params = new URLSearchParams();
params.append('companyId', companyId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
if (args.order) params.append('order', String(args.order));
if (args.isActive !== undefined) params.append('isActive', String(args.isActive));
return this.ghlClient.makeRequest('GET', `/saas-api/public-api/locations?${params.toString()}`);
}
case 'get_saas_location': {
const locationId = args.locationId as string;
return this.ghlClient.makeRequest('GET', `/saas-api/public-api/locations/${locationId}?companyId=${companyId}`);
}
case 'update_saas_subscription': {
const locationId = args.locationId as string;
const body: Record<string, unknown> = { companyId };
if (args.subscriptionId) body.subscriptionId = args.subscriptionId;
if (args.status) body.status = args.status;
return this.ghlClient.makeRequest('PUT', `/saas-api/public-api/locations/${locationId}/subscription`, body);
}
case 'pause_saas_location': {
const locationId = args.locationId as string;
return this.ghlClient.makeRequest('POST', `/saas-api/public-api/locations/${locationId}/pause`, {
companyId,
paused: args.paused
});
}
case 'enable_saas_location': {
const locationId = args.locationId as string;
return this.ghlClient.makeRequest('POST', `/saas-api/public-api/locations/${locationId}/enable`, {
companyId,
enabled: args.enabled
});
}
case 'rebilling_update': {
const body: Record<string, unknown> = { companyId };
if (args.product) body.product = args.product;
if (args.markup !== undefined) body.markup = args.markup;
if (args.enabled !== undefined) body.enabled = args.enabled;
return this.ghlClient.makeRequest('PUT', `/saas-api/public-api/rebilling`, body);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,185 @@
/**
* GoHighLevel Smart Lists Tools
* Tools for managing smart lists (saved contact segments)
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class SmartListsTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_smart_lists',
description: 'Get all smart lists (saved contact filters/segments)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_smart_list',
description: 'Get a specific smart list by ID',
inputSchema: {
type: 'object',
properties: {
smartListId: { type: 'string', description: 'Smart List ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['smartListId']
}
},
{
name: 'create_smart_list',
description: 'Create a new smart list with filter criteria',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Smart list name' },
filters: {
type: 'array',
items: {
type: 'object',
properties: {
field: { type: 'string', description: 'Field to filter on' },
operator: { type: 'string', description: 'Comparison operator (equals, contains, etc.)' },
value: { type: 'string', description: 'Filter value' }
}
},
description: 'Filter conditions'
},
filterOperator: { type: 'string', enum: ['AND', 'OR'], description: 'How to combine filters' }
},
required: ['name', 'filters']
}
},
{
name: 'update_smart_list',
description: 'Update a smart list',
inputSchema: {
type: 'object',
properties: {
smartListId: { type: 'string', description: 'Smart List ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Smart list name' },
filters: { type: 'array', description: 'Filter conditions' },
filterOperator: { type: 'string', enum: ['AND', 'OR'], description: 'How to combine filters' }
},
required: ['smartListId']
}
},
{
name: 'delete_smart_list',
description: 'Delete a smart list',
inputSchema: {
type: 'object',
properties: {
smartListId: { type: 'string', description: 'Smart List ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['smartListId']
}
},
{
name: 'get_smart_list_contacts',
description: 'Get contacts that match a smart list\'s criteria',
inputSchema: {
type: 'object',
properties: {
smartListId: { type: 'string', description: 'Smart List ID' },
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
},
required: ['smartListId']
}
},
{
name: 'get_smart_list_count',
description: 'Get the count of contacts matching a smart list',
inputSchema: {
type: 'object',
properties: {
smartListId: { type: 'string', description: 'Smart List ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['smartListId']
}
},
{
name: 'duplicate_smart_list',
description: 'Duplicate/clone a smart list',
inputSchema: {
type: 'object',
properties: {
smartListId: { type: 'string', description: 'Smart List ID to duplicate' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Name for the duplicate' }
},
required: ['smartListId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_smart_lists': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/contacts/smart-lists?${params.toString()}`);
}
case 'get_smart_list': {
return this.ghlClient.makeRequest('GET', `/contacts/smart-lists/${args.smartListId}?locationId=${locationId}`);
}
case 'create_smart_list': {
return this.ghlClient.makeRequest('POST', `/contacts/smart-lists`, {
locationId,
name: args.name,
filters: args.filters,
filterOperator: args.filterOperator || 'AND'
});
}
case 'update_smart_list': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.filters) body.filters = args.filters;
if (args.filterOperator) body.filterOperator = args.filterOperator;
return this.ghlClient.makeRequest('PUT', `/contacts/smart-lists/${args.smartListId}`, body);
}
case 'delete_smart_list': {
return this.ghlClient.makeRequest('DELETE', `/contacts/smart-lists/${args.smartListId}?locationId=${locationId}`);
}
case 'get_smart_list_contacts': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/contacts/smart-lists/${args.smartListId}/contacts?${params.toString()}`);
}
case 'get_smart_list_count': {
return this.ghlClient.makeRequest('GET', `/contacts/smart-lists/${args.smartListId}/count?locationId=${locationId}`);
}
case 'duplicate_smart_list': {
return this.ghlClient.makeRequest('POST', `/contacts/smart-lists/${args.smartListId}/duplicate`, {
locationId,
name: args.name
});
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,223 @@
/**
* GoHighLevel Snapshots Tools
* Tools for managing snapshots (location templates/backups)
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class SnapshotsTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_snapshots',
description: 'Get all snapshots for a company/agency. Snapshots are templates that can be used to set up new locations.',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
skip: {
type: 'number',
description: 'Number of records to skip'
},
limit: {
type: 'number',
description: 'Maximum number of snapshots to return'
}
},
required: ['companyId']
}
},
{
name: 'get_snapshot',
description: 'Get a specific snapshot by ID',
inputSchema: {
type: 'object',
properties: {
snapshotId: {
type: 'string',
description: 'The snapshot ID to retrieve'
},
companyId: {
type: 'string',
description: 'Company/Agency ID'
}
},
required: ['snapshotId', 'companyId']
}
},
{
name: 'create_snapshot',
description: 'Create a new snapshot from a location (backs up location settings)',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
locationId: {
type: 'string',
description: 'Source location ID to create snapshot from'
},
name: {
type: 'string',
description: 'Name for the snapshot'
},
description: {
type: 'string',
description: 'Description of the snapshot'
}
},
required: ['companyId', 'locationId', 'name']
}
},
{
name: 'get_snapshot_push_status',
description: 'Check the status of a snapshot push operation',
inputSchema: {
type: 'object',
properties: {
snapshotId: {
type: 'string',
description: 'The snapshot ID'
},
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
pushId: {
type: 'string',
description: 'The push operation ID'
}
},
required: ['snapshotId', 'companyId']
}
},
{
name: 'get_latest_snapshot_push',
description: 'Get the latest snapshot push for a location',
inputSchema: {
type: 'object',
properties: {
snapshotId: {
type: 'string',
description: 'The snapshot ID'
},
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
locationId: {
type: 'string',
description: 'Target location ID'
}
},
required: ['snapshotId', 'companyId', 'locationId']
}
},
{
name: 'push_snapshot_to_subaccounts',
description: 'Push/deploy a snapshot to one or more sub-accounts',
inputSchema: {
type: 'object',
properties: {
snapshotId: {
type: 'string',
description: 'The snapshot ID to push'
},
companyId: {
type: 'string',
description: 'Company/Agency ID'
},
locationIds: {
type: 'array',
items: { type: 'string' },
description: 'Array of location IDs to push the snapshot to'
},
override: {
type: 'object',
properties: {
workflows: { type: 'boolean', description: 'Override existing workflows' },
campaigns: { type: 'boolean', description: 'Override existing campaigns' },
funnels: { type: 'boolean', description: 'Override existing funnels' },
websites: { type: 'boolean', description: 'Override existing websites' },
forms: { type: 'boolean', description: 'Override existing forms' },
surveys: { type: 'boolean', description: 'Override existing surveys' },
calendars: { type: 'boolean', description: 'Override existing calendars' },
automations: { type: 'boolean', description: 'Override existing automations' },
triggers: { type: 'boolean', description: 'Override existing triggers' }
},
description: 'What to override vs skip'
}
},
required: ['snapshotId', 'companyId', 'locationIds']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const companyId = args.companyId as string;
switch (toolName) {
case 'get_snapshots': {
const params = new URLSearchParams();
params.append('companyId', companyId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
return this.ghlClient.makeRequest('GET', `/snapshots/?${params.toString()}`);
}
case 'get_snapshot': {
const snapshotId = args.snapshotId as string;
return this.ghlClient.makeRequest('GET', `/snapshots/${snapshotId}?companyId=${companyId}`);
}
case 'create_snapshot': {
const body: Record<string, unknown> = {
companyId,
locationId: args.locationId,
name: args.name
};
if (args.description) body.description = args.description;
return this.ghlClient.makeRequest('POST', `/snapshots/`, body);
}
case 'get_snapshot_push_status': {
const snapshotId = args.snapshotId as string;
const params = new URLSearchParams();
params.append('companyId', companyId);
if (args.pushId) params.append('pushId', String(args.pushId));
return this.ghlClient.makeRequest('GET', `/snapshots/${snapshotId}/push?${params.toString()}`);
}
case 'get_latest_snapshot_push': {
const snapshotId = args.snapshotId as string;
const locationId = args.locationId as string;
return this.ghlClient.makeRequest('GET', `/snapshots/${snapshotId}/push/${locationId}?companyId=${companyId}`);
}
case 'push_snapshot_to_subaccounts': {
const snapshotId = args.snapshotId as string;
const body: Record<string, unknown> = {
companyId,
locationIds: args.locationIds
};
if (args.override) body.override = args.override;
return this.ghlClient.makeRequest('POST', `/snapshots/${snapshotId}/push`, body);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

View File

@ -0,0 +1,373 @@
/**
* GoHighLevel Templates Tools
* Tools for managing SMS, Email, and other message templates
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class TemplatesTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
// SMS Templates
{
name: 'get_sms_templates',
description: 'Get all SMS templates',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_sms_template',
description: 'Get a specific SMS template',
inputSchema: {
type: 'object',
properties: {
templateId: { type: 'string', description: 'SMS Template ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['templateId']
}
},
{
name: 'create_sms_template',
description: 'Create a new SMS template',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Template name' },
body: { type: 'string', description: 'SMS message body (can include merge fields like {{contact.first_name}})' }
},
required: ['name', 'body']
}
},
{
name: 'update_sms_template',
description: 'Update an SMS template',
inputSchema: {
type: 'object',
properties: {
templateId: { type: 'string', description: 'SMS Template ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Template name' },
body: { type: 'string', description: 'SMS message body' }
},
required: ['templateId']
}
},
{
name: 'delete_sms_template',
description: 'Delete an SMS template',
inputSchema: {
type: 'object',
properties: {
templateId: { type: 'string', description: 'SMS Template ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['templateId']
}
},
// Voicemail Drop Templates
{
name: 'get_voicemail_templates',
description: 'Get all voicemail drop templates',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'create_voicemail_template',
description: 'Create a voicemail drop template',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Template name' },
audioUrl: { type: 'string', description: 'URL to audio file' }
},
required: ['name', 'audioUrl']
}
},
{
name: 'delete_voicemail_template',
description: 'Delete a voicemail template',
inputSchema: {
type: 'object',
properties: {
templateId: { type: 'string', description: 'Template ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['templateId']
}
},
// Social Templates
{
name: 'get_social_templates',
description: 'Get social media post templates',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'create_social_template',
description: 'Create a social media post template',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Template name' },
content: { type: 'string', description: 'Post content' },
mediaUrls: { type: 'array', items: { type: 'string' }, description: 'Media URLs' },
platforms: { type: 'array', items: { type: 'string' }, description: 'Target platforms' }
},
required: ['name', 'content']
}
},
{
name: 'delete_social_template',
description: 'Delete a social template',
inputSchema: {
type: 'object',
properties: {
templateId: { type: 'string', description: 'Template ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['templateId']
}
},
// WhatsApp Templates
{
name: 'get_whatsapp_templates',
description: 'Get WhatsApp message templates (must be pre-approved)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
status: { type: 'string', enum: ['approved', 'pending', 'rejected', 'all'], description: 'Template status' }
}
}
},
{
name: 'create_whatsapp_template',
description: 'Create a WhatsApp template (submits for approval)',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Template name' },
category: { type: 'string', enum: ['marketing', 'utility', 'authentication'], description: 'Template category' },
language: { type: 'string', description: 'Language code (e.g., en_US)' },
components: { type: 'array', description: 'Template components (header, body, footer, buttons)' }
},
required: ['name', 'category', 'language', 'components']
}
},
{
name: 'delete_whatsapp_template',
description: 'Delete a WhatsApp template',
inputSchema: {
type: 'object',
properties: {
templateId: { type: 'string', description: 'Template ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['templateId']
}
},
// Snippet/Canned Response Templates
{
name: 'get_snippets',
description: 'Get canned response snippets',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
type: { type: 'string', enum: ['sms', 'email', 'all'], description: 'Snippet type' }
}
}
},
{
name: 'create_snippet',
description: 'Create a canned response snippet',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Snippet name' },
shortcut: { type: 'string', description: 'Keyboard shortcut (e.g., /thanks)' },
content: { type: 'string', description: 'Snippet content' },
type: { type: 'string', enum: ['sms', 'email', 'both'], description: 'Snippet type' }
},
required: ['name', 'content']
}
},
{
name: 'update_snippet',
description: 'Update a snippet',
inputSchema: {
type: 'object',
properties: {
snippetId: { type: 'string', description: 'Snippet ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Snippet name' },
shortcut: { type: 'string', description: 'Keyboard shortcut' },
content: { type: 'string', description: 'Snippet content' }
},
required: ['snippetId']
}
},
{
name: 'delete_snippet',
description: 'Delete a snippet',
inputSchema: {
type: 'object',
properties: {
snippetId: { type: 'string', description: 'Snippet ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['snippetId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
// SMS Templates
case 'get_sms_templates': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/templates/sms?${params.toString()}`);
}
case 'get_sms_template': {
return this.ghlClient.makeRequest('GET', `/templates/sms/${args.templateId}?locationId=${locationId}`);
}
case 'create_sms_template': {
return this.ghlClient.makeRequest('POST', `/templates/sms`, {
locationId,
name: args.name,
body: args.body
});
}
case 'update_sms_template': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.body) body.body = args.body;
return this.ghlClient.makeRequest('PUT', `/templates/sms/${args.templateId}`, body);
}
case 'delete_sms_template': {
return this.ghlClient.makeRequest('DELETE', `/templates/sms/${args.templateId}?locationId=${locationId}`);
}
// Voicemail Templates
case 'get_voicemail_templates': {
return this.ghlClient.makeRequest('GET', `/templates/voicemail?locationId=${locationId}`);
}
case 'create_voicemail_template': {
return this.ghlClient.makeRequest('POST', `/templates/voicemail`, {
locationId,
name: args.name,
audioUrl: args.audioUrl
});
}
case 'delete_voicemail_template': {
return this.ghlClient.makeRequest('DELETE', `/templates/voicemail/${args.templateId}?locationId=${locationId}`);
}
// Social Templates
case 'get_social_templates': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/templates/social?${params.toString()}`);
}
case 'create_social_template': {
return this.ghlClient.makeRequest('POST', `/templates/social`, {
locationId,
name: args.name,
content: args.content,
mediaUrls: args.mediaUrls,
platforms: args.platforms
});
}
case 'delete_social_template': {
return this.ghlClient.makeRequest('DELETE', `/templates/social/${args.templateId}?locationId=${locationId}`);
}
// WhatsApp Templates
case 'get_whatsapp_templates': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.status) params.append('status', String(args.status));
return this.ghlClient.makeRequest('GET', `/templates/whatsapp?${params.toString()}`);
}
case 'create_whatsapp_template': {
return this.ghlClient.makeRequest('POST', `/templates/whatsapp`, {
locationId,
name: args.name,
category: args.category,
language: args.language,
components: args.components
});
}
case 'delete_whatsapp_template': {
return this.ghlClient.makeRequest('DELETE', `/templates/whatsapp/${args.templateId}?locationId=${locationId}`);
}
// Snippets
case 'get_snippets': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.type) params.append('type', String(args.type));
return this.ghlClient.makeRequest('GET', `/templates/snippets?${params.toString()}`);
}
case 'create_snippet': {
return this.ghlClient.makeRequest('POST', `/templates/snippets`, {
locationId,
name: args.name,
shortcut: args.shortcut,
content: args.content,
type: args.type
});
}
case 'update_snippet': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.shortcut) body.shortcut = args.shortcut;
if (args.content) body.content = args.content;
return this.ghlClient.makeRequest('PUT', `/templates/snippets/${args.snippetId}`, body);
}
case 'delete_snippet': {
return this.ghlClient.makeRequest('DELETE', `/templates/snippets/${args.snippetId}?locationId=${locationId}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

266
src/tools/triggers-tools.ts Normal file
View File

@ -0,0 +1,266 @@
/**
* GoHighLevel Triggers Tools
* Tools for managing automation triggers
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class TriggersTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_triggers',
description: 'Get all automation triggers for a location',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
type: { type: 'string', description: 'Filter by trigger type' },
status: { type: 'string', enum: ['active', 'inactive', 'all'], description: 'Status filter' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
}
}
},
{
name: 'get_trigger',
description: 'Get a specific trigger by ID',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['triggerId']
}
},
{
name: 'create_trigger',
description: 'Create a new automation trigger',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Trigger name' },
type: {
type: 'string',
enum: [
'contact_created', 'contact_tag_added', 'contact_tag_removed',
'form_submitted', 'appointment_booked', 'appointment_cancelled',
'opportunity_created', 'opportunity_status_changed', 'opportunity_stage_changed',
'invoice_paid', 'order_placed', 'call_completed', 'email_opened',
'email_clicked', 'sms_received', 'webhook'
],
description: 'Trigger type/event'
},
filters: {
type: 'array',
items: {
type: 'object',
properties: {
field: { type: 'string', description: 'Field to filter' },
operator: { type: 'string', description: 'Comparison operator' },
value: { type: 'string', description: 'Filter value' }
}
},
description: 'Conditions that must be met'
},
actions: {
type: 'array',
items: {
type: 'object',
properties: {
type: { type: 'string', description: 'Action type' },
config: { type: 'object', description: 'Action configuration' }
}
},
description: 'Actions to perform when triggered'
}
},
required: ['name', 'type']
}
},
{
name: 'update_trigger',
description: 'Update an existing trigger',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Trigger name' },
filters: { type: 'array', description: 'Filter conditions' },
actions: { type: 'array', description: 'Actions to perform' },
status: { type: 'string', enum: ['active', 'inactive'], description: 'Trigger status' }
},
required: ['triggerId']
}
},
{
name: 'delete_trigger',
description: 'Delete a trigger',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['triggerId']
}
},
{
name: 'enable_trigger',
description: 'Enable/activate a trigger',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['triggerId']
}
},
{
name: 'disable_trigger',
description: 'Disable/deactivate a trigger',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['triggerId']
}
},
{
name: 'get_trigger_types',
description: 'Get all available trigger types and their configurations',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'get_trigger_logs',
description: 'Get execution logs for a trigger',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID' },
locationId: { type: 'string', description: 'Location ID' },
status: { type: 'string', enum: ['success', 'failed', 'all'], description: 'Execution status filter' },
startDate: { type: 'string', description: 'Start date' },
endDate: { type: 'string', description: 'End date' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' }
},
required: ['triggerId']
}
},
{
name: 'test_trigger',
description: 'Test a trigger with sample data',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID' },
locationId: { type: 'string', description: 'Location ID' },
testData: { type: 'object', description: 'Sample data to test with' }
},
required: ['triggerId']
}
},
{
name: 'duplicate_trigger',
description: 'Duplicate/clone a trigger',
inputSchema: {
type: 'object',
properties: {
triggerId: { type: 'string', description: 'Trigger ID to duplicate' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Name for the duplicate' }
},
required: ['triggerId']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_triggers': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.type) params.append('type', String(args.type));
if (args.status) params.append('status', String(args.status));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/triggers/?${params.toString()}`);
}
case 'get_trigger': {
return this.ghlClient.makeRequest('GET', `/triggers/${args.triggerId}?locationId=${locationId}`);
}
case 'create_trigger': {
return this.ghlClient.makeRequest('POST', `/triggers/`, {
locationId,
name: args.name,
type: args.type,
filters: args.filters,
actions: args.actions
});
}
case 'update_trigger': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.filters) body.filters = args.filters;
if (args.actions) body.actions = args.actions;
if (args.status) body.status = args.status;
return this.ghlClient.makeRequest('PUT', `/triggers/${args.triggerId}`, body);
}
case 'delete_trigger': {
return this.ghlClient.makeRequest('DELETE', `/triggers/${args.triggerId}?locationId=${locationId}`);
}
case 'enable_trigger': {
return this.ghlClient.makeRequest('POST', `/triggers/${args.triggerId}/enable`, { locationId });
}
case 'disable_trigger': {
return this.ghlClient.makeRequest('POST', `/triggers/${args.triggerId}/disable`, { locationId });
}
case 'get_trigger_types': {
return this.ghlClient.makeRequest('GET', `/triggers/types?locationId=${locationId}`);
}
case 'get_trigger_logs': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.status) params.append('status', String(args.status));
if (args.startDate) params.append('startDate', String(args.startDate));
if (args.endDate) params.append('endDate', String(args.endDate));
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
return this.ghlClient.makeRequest('GET', `/triggers/${args.triggerId}/logs?${params.toString()}`);
}
case 'test_trigger': {
return this.ghlClient.makeRequest('POST', `/triggers/${args.triggerId}/test`, {
locationId,
testData: args.testData
});
}
case 'duplicate_trigger': {
return this.ghlClient.makeRequest('POST', `/triggers/${args.triggerId}/duplicate`, {
locationId,
name: args.name
});
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

291
src/tools/users-tools.ts Normal file
View File

@ -0,0 +1,291 @@
/**
* GoHighLevel Users Tools
* Tools for managing users and team members
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class UsersTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_users',
description: 'Get all users/team members for a location. Returns team members with their roles and permissions.',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
skip: {
type: 'number',
description: 'Number of records to skip for pagination'
},
limit: {
type: 'number',
description: 'Maximum number of users to return (default: 25, max: 100)'
},
type: {
type: 'string',
description: 'Filter by user type'
},
role: {
type: 'string',
description: 'Filter by role (e.g., "admin", "user")'
},
ids: {
type: 'string',
description: 'Comma-separated list of user IDs to filter'
},
sort: {
type: 'string',
description: 'Sort field'
},
sortDirection: {
type: 'string',
enum: ['asc', 'desc'],
description: 'Sort direction'
}
}
}
},
{
name: 'get_user',
description: 'Get a specific user by their ID',
inputSchema: {
type: 'object',
properties: {
userId: {
type: 'string',
description: 'The user ID to retrieve'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['userId']
}
},
{
name: 'create_user',
description: 'Create a new user/team member for a location',
inputSchema: {
type: 'object',
properties: {
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
firstName: {
type: 'string',
description: 'User first name'
},
lastName: {
type: 'string',
description: 'User last name'
},
email: {
type: 'string',
description: 'User email address'
},
phone: {
type: 'string',
description: 'User phone number'
},
type: {
type: 'string',
description: 'User type (e.g., "account")'
},
role: {
type: 'string',
description: 'User role (e.g., "admin", "user")'
},
permissions: {
type: 'object',
description: 'User permissions object'
},
scopes: {
type: 'array',
items: { type: 'string' },
description: 'OAuth scopes for the user'
},
scopesAssignedToOnly: {
type: 'array',
items: { type: 'string' },
description: 'Scopes only assigned to this user'
}
},
required: ['firstName', 'lastName', 'email']
}
},
{
name: 'update_user',
description: 'Update an existing user/team member',
inputSchema: {
type: 'object',
properties: {
userId: {
type: 'string',
description: 'The user ID to update'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
},
firstName: {
type: 'string',
description: 'User first name'
},
lastName: {
type: 'string',
description: 'User last name'
},
email: {
type: 'string',
description: 'User email address'
},
phone: {
type: 'string',
description: 'User phone number'
},
type: {
type: 'string',
description: 'User type'
},
role: {
type: 'string',
description: 'User role'
},
permissions: {
type: 'object',
description: 'User permissions object'
}
},
required: ['userId']
}
},
{
name: 'delete_user',
description: 'Delete a user/team member from a location',
inputSchema: {
type: 'object',
properties: {
userId: {
type: 'string',
description: 'The user ID to delete'
},
locationId: {
type: 'string',
description: 'Location ID (uses default if not provided)'
}
},
required: ['userId']
}
},
{
name: 'search_users',
description: 'Search for users across a company/agency by email, name, or other criteria',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'Company ID to search within'
},
query: {
type: 'string',
description: 'Search query string'
},
skip: {
type: 'number',
description: 'Records to skip'
},
limit: {
type: 'number',
description: 'Max records to return'
}
}
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_users': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
if (args.type) params.append('type', String(args.type));
if (args.role) params.append('role', String(args.role));
if (args.ids) params.append('ids', String(args.ids));
if (args.sort) params.append('sort', String(args.sort));
if (args.sortDirection) params.append('sortDirection', String(args.sortDirection));
return this.ghlClient.makeRequest('GET', `/users/?${params.toString()}`);
}
case 'get_user': {
const userId = args.userId as string;
return this.ghlClient.makeRequest('GET', `/users/${userId}`);
}
case 'create_user': {
const body: Record<string, unknown> = {
locationId,
firstName: args.firstName,
lastName: args.lastName,
email: args.email
};
if (args.phone) body.phone = args.phone;
if (args.type) body.type = args.type;
if (args.role) body.role = args.role;
if (args.permissions) body.permissions = args.permissions;
if (args.scopes) body.scopes = args.scopes;
if (args.scopesAssignedToOnly) body.scopesAssignedToOnly = args.scopesAssignedToOnly;
return this.ghlClient.makeRequest('POST', `/users/`, body);
}
case 'update_user': {
const userId = args.userId as string;
const body: Record<string, unknown> = {};
if (args.firstName) body.firstName = args.firstName;
if (args.lastName) body.lastName = args.lastName;
if (args.email) body.email = args.email;
if (args.phone) body.phone = args.phone;
if (args.type) body.type = args.type;
if (args.role) body.role = args.role;
if (args.permissions) body.permissions = args.permissions;
return this.ghlClient.makeRequest('PUT', `/users/${userId}`, body);
}
case 'delete_user': {
const userId = args.userId as string;
return this.ghlClient.makeRequest('DELETE', `/users/${userId}`);
}
case 'search_users': {
const params = new URLSearchParams();
if (args.companyId) params.append('companyId', String(args.companyId));
if (args.query) params.append('query', String(args.query));
if (args.skip) params.append('skip', String(args.skip));
if (args.limit) params.append('limit', String(args.limit));
return this.ghlClient.makeRequest('GET', `/users/search?${params.toString()}`);
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}

194
src/tools/webhooks-tools.ts Normal file
View File

@ -0,0 +1,194 @@
/**
* GoHighLevel Webhooks Tools
* Tools for managing webhooks and event subscriptions
*/
import { GHLApiClient } from '../clients/ghl-api-client.js';
export class WebhooksTools {
constructor(private ghlClient: GHLApiClient) {}
getToolDefinitions() {
return [
{
name: 'get_webhooks',
description: 'Get all webhooks for a location',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' }
}
}
},
{
name: 'get_webhook',
description: 'Get a specific webhook by ID',
inputSchema: {
type: 'object',
properties: {
webhookId: { type: 'string', description: 'Webhook ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['webhookId']
}
},
{
name: 'create_webhook',
description: 'Create a new webhook subscription',
inputSchema: {
type: 'object',
properties: {
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Webhook name' },
url: { type: 'string', description: 'Webhook URL to receive events' },
events: {
type: 'array',
items: { type: 'string' },
description: 'Events to subscribe to (e.g., contact.created, opportunity.updated)'
},
secret: { type: 'string', description: 'Secret key for webhook signature verification' }
},
required: ['name', 'url', 'events']
}
},
{
name: 'update_webhook',
description: 'Update a webhook',
inputSchema: {
type: 'object',
properties: {
webhookId: { type: 'string', description: 'Webhook ID' },
locationId: { type: 'string', description: 'Location ID' },
name: { type: 'string', description: 'Webhook name' },
url: { type: 'string', description: 'Webhook URL' },
events: {
type: 'array',
items: { type: 'string' },
description: 'Events to subscribe to'
},
active: { type: 'boolean', description: 'Whether webhook is active' }
},
required: ['webhookId']
}
},
{
name: 'delete_webhook',
description: 'Delete a webhook',
inputSchema: {
type: 'object',
properties: {
webhookId: { type: 'string', description: 'Webhook ID' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['webhookId']
}
},
{
name: 'get_webhook_events',
description: 'Get list of all available webhook event types',
inputSchema: {
type: 'object',
properties: {}
}
},
{
name: 'get_webhook_logs',
description: 'Get webhook delivery logs/history',
inputSchema: {
type: 'object',
properties: {
webhookId: { type: 'string', description: 'Webhook ID' },
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Max results' },
offset: { type: 'number', description: 'Pagination offset' },
status: { type: 'string', enum: ['success', 'failed', 'pending'], description: 'Filter by delivery status' }
},
required: ['webhookId']
}
},
{
name: 'retry_webhook',
description: 'Retry a failed webhook delivery',
inputSchema: {
type: 'object',
properties: {
webhookId: { type: 'string', description: 'Webhook ID' },
logId: { type: 'string', description: 'Webhook log entry ID to retry' },
locationId: { type: 'string', description: 'Location ID' }
},
required: ['webhookId', 'logId']
}
},
{
name: 'test_webhook',
description: 'Send a test event to a webhook',
inputSchema: {
type: 'object',
properties: {
webhookId: { type: 'string', description: 'Webhook ID' },
locationId: { type: 'string', description: 'Location ID' },
eventType: { type: 'string', description: 'Event type to test' }
},
required: ['webhookId', 'eventType']
}
}
];
}
async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<unknown> {
const config = this.ghlClient.getConfig();
const locationId = (args.locationId as string) || config.locationId;
switch (toolName) {
case 'get_webhooks': {
return this.ghlClient.makeRequest('GET', `/webhooks/?locationId=${locationId}`);
}
case 'get_webhook': {
return this.ghlClient.makeRequest('GET', `/webhooks/${args.webhookId}?locationId=${locationId}`);
}
case 'create_webhook': {
return this.ghlClient.makeRequest('POST', `/webhooks/`, {
locationId,
name: args.name,
url: args.url,
events: args.events,
secret: args.secret
});
}
case 'update_webhook': {
const body: Record<string, unknown> = { locationId };
if (args.name) body.name = args.name;
if (args.url) body.url = args.url;
if (args.events) body.events = args.events;
if (args.active !== undefined) body.active = args.active;
return this.ghlClient.makeRequest('PUT', `/webhooks/${args.webhookId}`, body);
}
case 'delete_webhook': {
return this.ghlClient.makeRequest('DELETE', `/webhooks/${args.webhookId}?locationId=${locationId}`);
}
case 'get_webhook_events': {
return this.ghlClient.makeRequest('GET', `/webhooks/events`);
}
case 'get_webhook_logs': {
const params = new URLSearchParams();
params.append('locationId', locationId);
if (args.limit) params.append('limit', String(args.limit));
if (args.offset) params.append('offset', String(args.offset));
if (args.status) params.append('status', String(args.status));
return this.ghlClient.makeRequest('GET', `/webhooks/${args.webhookId}/logs?${params.toString()}`);
}
case 'retry_webhook': {
return this.ghlClient.makeRequest('POST', `/webhooks/${args.webhookId}/logs/${args.logId}/retry`, { locationId });
}
case 'test_webhook': {
return this.ghlClient.makeRequest('POST', `/webhooks/${args.webhookId}/test`, {
locationId,
eventType: args.eventType
});
}
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
}