=== DONE === - MCP Apps UI system added (11 apps with _meta.ui.resourceUri) - 19 new tool modules added - Tool count: 269 → 461 across 38 categories - Upstream changes merged - All tools tagged with _meta labels - Package lock updated === TO-DO === - [ ] Fix 42 failing edge case tests (BLOCKER — Stage 11) - [ ] Live API testing with GHL credentials - [ ] App design approval for Stage 7→8
368 lines
11 KiB
TypeScript
368 lines
11 KiB
TypeScript
/**
|
|
* 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)'
|
|
}
|
|
}
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
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)'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
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'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
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)'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
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'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "write",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
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'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "write",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
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)'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "delete",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
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'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "funnels",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
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}`);
|
|
}
|
|
}
|
|
}
|