Skip to main content
Glama
createEngagementTool.js5.42 kB
import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; import { BaseTool } from '../baseTool.js'; import HubSpotClient from '../../utils/client.js'; // Define the engagement types we support const ENGAGEMENT_TYPES = ['NOTE', 'TASK']; const NoteMetadataSchema = z.object({ body: z.string().describe('The content of the note'), }); const TaskMetadataSchema = z.object({ body: z.string().describe('The body/description of the task'), subject: z.string().describe('The title/subject of the task'), status: z.enum(['NOT_STARTED', 'IN_PROGRESS', 'COMPLETED', 'WAITING']).default('NOT_STARTED'), forObjectType: z.enum(['CONTACT', 'COMPANY', 'DEAL', 'TICKET']).default('CONTACT'), }); export const AssociationsSchema = z.object({ contactIds: z.array(z.number().int()).optional().default([]), companyIds: z.array(z.number().int()).optional().default([]), dealIds: z.array(z.number().int()).optional().default([]), ownerIds: z.array(z.number().int()).optional().default([]), ticketIds: z.array(z.number().int()).optional().default([]), }); // Map engagement types to their metadata schemas const metadataSchemas = { NOTE: NoteMetadataSchema, TASK: TaskMetadataSchema, }; const CreateEngagementSchema = z .object({ type: z.enum(ENGAGEMENT_TYPES).describe('The type of engagement to create (NOTE or TASK)'), ownerId: z.number().int().positive().describe('The ID of the owner of this engagement'), timestamp: z .number() .int() .optional() .describe('Timestamp for the engagement (milliseconds since epoch). Defaults to current time if not provided.'), associations: AssociationsSchema.describe('Associated records for this engagement'), metadata: z.object({}).passthrough().describe('Metadata specific to the engagement type'), }) .superRefine((data, ctx) => { const schema = metadataSchemas[data.type]; if (!schema) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Unsupported engagement type: ${data.type}`, path: ['type'], }); return; } const result = schema.safeParse(data.metadata); if (!result.success) { result.error.issues.forEach(issue => { ctx.addIssue({ ...issue, path: ['metadata', ...(issue.path || [])], }); }); } }); const ToolDefinition = { name: 'hubspot-create-engagement', description: ` 🛡️ Guardrails: 1. Data Modification Warning: This tool modifies HubSpot data. Only use when the user has explicitly requested to update their CRM. 🎯 Purpose: 1. Creates a HubSpot engagement (Note or Task) associated with contacts, companies, deals, or tickets. 2. This endpoint is useful for keeping your CRM records up-to-date on any interactions that take place outside of HubSpot. 3. Activity reporting in the CRM also feeds off of this data. 📋 Prerequisites: 1. Use the hubspot-get-user-details tool to get the OwnerId and UserId. 🧭 Usage Guidance: 1. Use NOTE type for adding notes to records 2. Use TASK type for creating tasks with subject, status, and assignment 3. Both require relevant associations to connect them to CRM records 4. Other types of engagements (EMAIL, CALL, MEETING) are NOT supported yet. 5. HubSpot notes and task descriptions support HTML formatting. However headings (<h1>, <h2>, etc.) look ugly in the CRM. So use them sparingly. `, inputSchema: zodToJsonSchema(CreateEngagementSchema), annotations: { title: 'Create Engagement', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true, }, }; export class CreateEngagementTool extends BaseTool { client; constructor() { super(CreateEngagementSchema, ToolDefinition); this.client = new HubSpotClient(); } async process(args) { try { const { type, ownerId, timestamp, associations, metadata } = args; const engagementTimestamp = timestamp || Date.now(); const requestBody = { engagement: { active: true, ownerId, type, timestamp: engagementTimestamp, }, associations, metadata, }; const response = await this.client.post('/engagements/v1/engagements', { body: requestBody, }); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', engagement: response, message: `Successfully created ${type.toLowerCase()} engagement`, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error creating HubSpot engagement: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ajaystream/hubspot-mcp-custom'

If you have feedback or need assistance with the MCP directory API, please join our Discord server