=== 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
699 lines
21 KiB
TypeScript
699 lines
21 KiB
TypeScript
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
import { GHLApiClient } from '../clients/ghl-api-client.js';
|
|
import {
|
|
MCPSearchPostsParams,
|
|
MCPCreatePostParams,
|
|
MCPGetPostParams,
|
|
MCPUpdatePostParams,
|
|
MCPDeletePostParams,
|
|
MCPBulkDeletePostsParams,
|
|
MCPGetAccountsParams,
|
|
MCPDeleteAccountParams,
|
|
MCPUploadCSVParams,
|
|
MCPGetUploadStatusParams,
|
|
MCPSetAccountsParams,
|
|
MCPGetCSVPostParams,
|
|
MCPFinalizeCSVParams,
|
|
MCPDeleteCSVParams,
|
|
MCPDeleteCSVPostParams,
|
|
MCPGetCategoriesParams,
|
|
MCPGetCategoryParams,
|
|
MCPGetTagsParams,
|
|
MCPGetTagsByIdsParams,
|
|
MCPStartOAuthParams,
|
|
MCPGetOAuthAccountsParams,
|
|
MCPAttachOAuthAccountParams
|
|
} from '../types/ghl-types.js';
|
|
|
|
export class SocialMediaTools {
|
|
constructor(private ghlClient: GHLApiClient) {}
|
|
|
|
getTools(): Tool[] {
|
|
return [
|
|
// Post Management Tools
|
|
{
|
|
name: 'search_social_posts',
|
|
description: 'Search and filter social media posts across all platforms',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
type: {
|
|
type: 'string',
|
|
enum: ['recent', 'all', 'scheduled', 'draft', 'failed', 'in_review', 'published', 'in_progress', 'deleted'],
|
|
description: 'Filter posts by status',
|
|
default: 'all'
|
|
},
|
|
accounts: {
|
|
type: 'string',
|
|
description: 'Comma-separated account IDs to filter by'
|
|
},
|
|
skip: { type: 'number', description: 'Number of posts to skip', default: 0 },
|
|
limit: { type: 'number', description: 'Number of posts to return', default: 10 },
|
|
fromDate: { type: 'string', description: 'Start date (ISO format)' },
|
|
toDate: { type: 'string', description: 'End date (ISO format)' },
|
|
includeUsers: { type: 'boolean', description: 'Include user data in response', default: true },
|
|
postType: {
|
|
type: 'string',
|
|
enum: ['post', 'story', 'reel'],
|
|
description: 'Type of post to search for'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['fromDate', 'toDate']
|
|
}
|
|
},
|
|
{
|
|
name: 'create_social_post',
|
|
description: 'Create a new social media post for multiple platforms',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
accountIds: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Array of social media account IDs to post to'
|
|
},
|
|
summary: { type: 'string', description: 'Post content/text' },
|
|
media: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
url: { type: 'string', description: 'Media URL' },
|
|
caption: { type: 'string', description: 'Media caption' },
|
|
type: { type: 'string', description: 'Media MIME type' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "write",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['url']
|
|
},
|
|
description: 'Media attachments'
|
|
},
|
|
status: {
|
|
type: 'string',
|
|
enum: ['draft', 'scheduled', 'published'],
|
|
description: 'Post status',
|
|
default: 'draft'
|
|
},
|
|
scheduleDate: { type: 'string', description: 'Schedule date for post (ISO format)' },
|
|
followUpComment: { type: 'string', description: 'Follow-up comment' },
|
|
type: {
|
|
type: 'string',
|
|
enum: ['post', 'story', 'reel'],
|
|
description: 'Type of post'
|
|
},
|
|
tags: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Tag IDs to associate with post'
|
|
},
|
|
categoryId: { type: 'string', description: 'Category ID' },
|
|
userId: { type: 'string', description: 'User ID creating the post' }
|
|
},
|
|
required: ['accountIds', 'summary', 'type']
|
|
}
|
|
},
|
|
{
|
|
name: 'get_social_post',
|
|
description: 'Get details of a specific social media post',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
postId: { type: 'string', description: 'Social media post ID' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['postId']
|
|
}
|
|
},
|
|
{
|
|
name: 'update_social_post',
|
|
description: 'Update an existing social media post',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
postId: { type: 'string', description: 'Social media post ID' },
|
|
summary: { type: 'string', description: 'Updated post content' },
|
|
status: {
|
|
type: 'string',
|
|
enum: ['draft', 'scheduled', 'published'],
|
|
description: 'Updated post status'
|
|
},
|
|
scheduleDate: { type: 'string', description: 'Updated schedule date' },
|
|
tags: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Updated tag IDs'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "write",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['postId']
|
|
}
|
|
},
|
|
{
|
|
name: 'delete_social_post',
|
|
description: 'Delete a social media post',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
postId: { type: 'string', description: 'Social media post ID to delete' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "delete",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['postId']
|
|
}
|
|
},
|
|
{
|
|
name: 'bulk_delete_social_posts',
|
|
description: 'Delete multiple social media posts at once (max 50)',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
postIds: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Array of post IDs to delete',
|
|
maxItems: 50
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "delete",
|
|
complexity: "batch"
|
|
}
|
|
}
|
|
},
|
|
required: ['postIds']
|
|
}
|
|
},
|
|
|
|
// Account Management Tools
|
|
{
|
|
name: 'get_social_accounts',
|
|
description: 'Get all connected social media accounts and groups',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {},
|
|
additionalProperties: false
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'delete_social_account',
|
|
description: 'Delete a social media account connection',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
accountId: { type: 'string', description: 'Account ID to delete' },
|
|
companyId: { type: 'string', description: 'Company ID' },
|
|
userId: { type: 'string', description: 'User ID' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "delete",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['accountId']
|
|
}
|
|
},
|
|
|
|
// CSV Operations Tools
|
|
{
|
|
name: 'upload_social_csv',
|
|
description: 'Upload CSV file for bulk social media posts',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
file: { type: 'string', description: 'CSV file data (base64 or file path)' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "write",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['file']
|
|
}
|
|
},
|
|
{
|
|
name: 'get_csv_upload_status',
|
|
description: 'Get status of CSV uploads',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
skip: { type: 'number', description: 'Number to skip', default: 0 },
|
|
limit: { type: 'number', description: 'Number to return', default: 10 },
|
|
includeUsers: { type: 'boolean', description: 'Include user data' },
|
|
userId: { type: 'string', description: 'Filter by user ID' }
|
|
}
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "write",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'set_csv_accounts',
|
|
description: 'Set accounts for CSV import processing',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
accountIds: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Account IDs for CSV import'
|
|
},
|
|
filePath: { type: 'string', description: 'CSV file path' },
|
|
rowsCount: { type: 'number', description: 'Number of rows to process' },
|
|
fileName: { type: 'string', description: 'CSV file name' },
|
|
approver: { type: 'string', description: 'Approver user ID' },
|
|
userId: { type: 'string', description: 'User ID' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "write",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['accountIds', 'filePath', 'rowsCount', 'fileName']
|
|
}
|
|
},
|
|
|
|
// Categories & Tags Tools
|
|
{
|
|
name: 'get_social_categories',
|
|
description: 'Get social media post categories',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
searchText: { type: 'string', description: 'Search for categories' },
|
|
limit: { type: 'number', description: 'Number to return', default: 10 },
|
|
skip: { type: 'number', description: 'Number to skip', default: 0 }
|
|
}
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'get_social_category',
|
|
description: 'Get a specific social media category by ID',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
categoryId: { type: 'string', description: 'Category ID' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['categoryId']
|
|
}
|
|
},
|
|
{
|
|
name: 'get_social_tags',
|
|
description: 'Get social media post tags',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
searchText: { type: 'string', description: 'Search for tags' },
|
|
limit: { type: 'number', description: 'Number to return', default: 10 },
|
|
skip: { type: 'number', description: 'Number to skip', default: 0 }
|
|
}
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'get_social_tags_by_ids',
|
|
description: 'Get specific social media tags by their IDs',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
tagIds: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Array of tag IDs'
|
|
},
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['tagIds']
|
|
}
|
|
},
|
|
|
|
// OAuth Integration Tools
|
|
{
|
|
name: 'start_social_oauth',
|
|
description: 'Start OAuth process for social media platform',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
platform: {
|
|
type: 'string',
|
|
enum: ['google', 'facebook', 'instagram', 'linkedin', 'twitter', 'tiktok', 'tiktok-business'],
|
|
description: 'Social media platform'
|
|
},
|
|
userId: { type: 'string', description: 'User ID initiating OAuth' },
|
|
page: { type: 'string', description: 'Page context' },
|
|
reconnect: { type: 'boolean', description: 'Whether this is a reconnection' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['platform', 'userId']
|
|
}
|
|
},
|
|
{
|
|
name: 'get_platform_accounts',
|
|
description: 'Get available accounts for a specific platform after OAuth',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
platform: {
|
|
type: 'string',
|
|
enum: ['google', 'facebook', 'instagram', 'linkedin', 'twitter', 'tiktok', 'tiktok-business'],
|
|
description: 'Social media platform'
|
|
},
|
|
accountId: { type: 'string', description: 'OAuth account ID' },
|
|
_meta: {
|
|
labels: {
|
|
category: "social-media",
|
|
access: "read",
|
|
complexity: "simple"
|
|
}
|
|
}
|
|
},
|
|
required: ['platform', 'accountId']
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
async executeTool(name: string, args: any): Promise<any> {
|
|
try {
|
|
switch (name) {
|
|
case 'search_social_posts':
|
|
return await this.searchSocialPosts(args);
|
|
case 'create_social_post':
|
|
return await this.createSocialPost(args);
|
|
case 'get_social_post':
|
|
return await this.getSocialPost(args);
|
|
case 'update_social_post':
|
|
return await this.updateSocialPost(args);
|
|
case 'delete_social_post':
|
|
return await this.deleteSocialPost(args);
|
|
case 'bulk_delete_social_posts':
|
|
return await this.bulkDeleteSocialPosts(args);
|
|
case 'get_social_accounts':
|
|
return await this.getSocialAccounts(args);
|
|
case 'delete_social_account':
|
|
return await this.deleteSocialAccount(args);
|
|
case 'get_social_categories':
|
|
return await this.getSocialCategories(args);
|
|
case 'get_social_category':
|
|
return await this.getSocialCategory(args);
|
|
case 'get_social_tags':
|
|
return await this.getSocialTags(args);
|
|
case 'get_social_tags_by_ids':
|
|
return await this.getSocialTagsByIds(args);
|
|
case 'start_social_oauth':
|
|
return await this.startSocialOAuth(args);
|
|
case 'get_platform_accounts':
|
|
return await this.getPlatformAccounts(args);
|
|
default:
|
|
throw new Error(`Unknown tool: ${name}`);
|
|
}
|
|
} catch (error) {
|
|
throw new Error(`Error executing ${name}: ${error}`);
|
|
}
|
|
}
|
|
|
|
// Implementation methods
|
|
private async searchSocialPosts(params: MCPSearchPostsParams) {
|
|
const response = await this.ghlClient.searchSocialPosts({
|
|
type: params.type,
|
|
accounts: params.accounts,
|
|
skip: params.skip?.toString(),
|
|
limit: params.limit?.toString(),
|
|
fromDate: params.fromDate,
|
|
toDate: params.toDate,
|
|
includeUsers: params.includeUsers?.toString() || 'true',
|
|
postType: params.postType
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
posts: response.data?.posts || [],
|
|
count: response.data?.count || 0,
|
|
message: `Found ${response.data?.count || 0} social media posts`
|
|
};
|
|
}
|
|
|
|
private async createSocialPost(params: MCPCreatePostParams) {
|
|
const response = await this.ghlClient.createSocialPost({
|
|
accountIds: params.accountIds,
|
|
summary: params.summary,
|
|
media: params.media,
|
|
status: params.status,
|
|
scheduleDate: params.scheduleDate,
|
|
followUpComment: params.followUpComment,
|
|
type: params.type,
|
|
tags: params.tags,
|
|
categoryId: params.categoryId,
|
|
userId: params.userId
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
post: response.data?.post,
|
|
message: `Social media post created successfully`
|
|
};
|
|
}
|
|
|
|
private async getSocialPost(params: MCPGetPostParams) {
|
|
const response = await this.ghlClient.getSocialPost(params.postId);
|
|
|
|
return {
|
|
success: true,
|
|
post: response.data?.post,
|
|
message: `Retrieved social media post ${params.postId}`
|
|
};
|
|
}
|
|
|
|
private async updateSocialPost(params: MCPUpdatePostParams) {
|
|
const { postId, ...updateData } = params;
|
|
const response = await this.ghlClient.updateSocialPost(postId, updateData);
|
|
|
|
return {
|
|
success: true,
|
|
message: `Social media post ${postId} updated successfully`
|
|
};
|
|
}
|
|
|
|
private async deleteSocialPost(params: MCPDeletePostParams) {
|
|
const response = await this.ghlClient.deleteSocialPost(params.postId);
|
|
|
|
return {
|
|
success: true,
|
|
message: `Social media post ${params.postId} deleted successfully`
|
|
};
|
|
}
|
|
|
|
private async bulkDeleteSocialPosts(params: MCPBulkDeletePostsParams) {
|
|
const response = await this.ghlClient.bulkDeleteSocialPosts({ postIds: params.postIds });
|
|
|
|
return {
|
|
success: true,
|
|
deletedCount: response.data?.deletedCount || 0,
|
|
message: `${response.data?.deletedCount || 0} social media posts deleted successfully`
|
|
};
|
|
}
|
|
|
|
private async getSocialAccounts(params: MCPGetAccountsParams) {
|
|
const response = await this.ghlClient.getSocialAccounts();
|
|
|
|
return {
|
|
success: true,
|
|
accounts: response.data?.accounts || [],
|
|
groups: response.data?.groups || [],
|
|
message: `Retrieved ${response.data?.accounts?.length || 0} social media accounts and ${response.data?.groups?.length || 0} groups`
|
|
};
|
|
}
|
|
|
|
private async deleteSocialAccount(params: MCPDeleteAccountParams) {
|
|
const response = await this.ghlClient.deleteSocialAccount(
|
|
params.accountId,
|
|
params.companyId,
|
|
params.userId
|
|
);
|
|
|
|
return {
|
|
success: true,
|
|
message: `Social media account ${params.accountId} deleted successfully`
|
|
};
|
|
}
|
|
|
|
private async getSocialCategories(params: MCPGetCategoriesParams) {
|
|
const response = await this.ghlClient.getSocialCategories(
|
|
params.searchText,
|
|
params.limit,
|
|
params.skip
|
|
);
|
|
|
|
return {
|
|
success: true,
|
|
categories: response.data?.categories || [],
|
|
count: response.data?.count || 0,
|
|
message: `Retrieved ${response.data?.count || 0} social media categories`
|
|
};
|
|
}
|
|
|
|
private async getSocialCategory(params: MCPGetCategoryParams) {
|
|
const response = await this.ghlClient.getSocialCategory(params.categoryId);
|
|
|
|
return {
|
|
success: true,
|
|
category: response.data?.category,
|
|
message: `Retrieved social media category ${params.categoryId}`
|
|
};
|
|
}
|
|
|
|
private async getSocialTags(params: MCPGetTagsParams) {
|
|
const response = await this.ghlClient.getSocialTags(
|
|
params.searchText,
|
|
params.limit,
|
|
params.skip
|
|
);
|
|
|
|
return {
|
|
success: true,
|
|
tags: response.data?.tags || [],
|
|
count: response.data?.count || 0,
|
|
message: `Retrieved ${response.data?.count || 0} social media tags`
|
|
};
|
|
}
|
|
|
|
private async getSocialTagsByIds(params: MCPGetTagsByIdsParams) {
|
|
const response = await this.ghlClient.getSocialTagsByIds({ tagIds: params.tagIds });
|
|
|
|
return {
|
|
success: true,
|
|
tags: response.data?.tags || [],
|
|
count: response.data?.count || 0,
|
|
message: `Retrieved ${response.data?.count || 0} social media tags by IDs`
|
|
};
|
|
}
|
|
|
|
private async startSocialOAuth(params: MCPStartOAuthParams) {
|
|
const response = await this.ghlClient.startSocialOAuth(
|
|
params.platform,
|
|
params.userId,
|
|
params.page,
|
|
params.reconnect
|
|
);
|
|
|
|
return {
|
|
success: true,
|
|
oauthData: response.data,
|
|
message: `OAuth process started for ${params.platform}`
|
|
};
|
|
}
|
|
|
|
private async getPlatformAccounts(params: MCPGetOAuthAccountsParams) {
|
|
let response;
|
|
|
|
switch (params.platform) {
|
|
case 'google':
|
|
response = await this.ghlClient.getGoogleBusinessLocations(params.accountId);
|
|
break;
|
|
case 'facebook':
|
|
response = await this.ghlClient.getFacebookPages(params.accountId);
|
|
break;
|
|
case 'instagram':
|
|
response = await this.ghlClient.getInstagramAccounts(params.accountId);
|
|
break;
|
|
case 'linkedin':
|
|
response = await this.ghlClient.getLinkedInAccounts(params.accountId);
|
|
break;
|
|
case 'twitter':
|
|
response = await this.ghlClient.getTwitterProfile(params.accountId);
|
|
break;
|
|
case 'tiktok':
|
|
response = await this.ghlClient.getTikTokProfile(params.accountId);
|
|
break;
|
|
case 'tiktok-business':
|
|
response = await this.ghlClient.getTikTokBusinessProfile(params.accountId);
|
|
break;
|
|
default:
|
|
throw new Error(`Unsupported platform: ${params.platform}`);
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
platformAccounts: response.data,
|
|
message: `Retrieved ${params.platform} accounts for OAuth ID ${params.accountId}`
|
|
};
|
|
}
|
|
}
|