Skip to main content
Glama
lkm1developer

HubSpot MCP Server

hubspot_get_recent_engagements

Retrieve recent engagement activities from HubSpot CRM for contacts and companies to track interactions and monitor relationship history.

Instructions

Get recent engagement activities across all contacts and companies

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
daysNoNumber of days to look back (default: 7)
limitNoMaximum number of engagements to return (default: 50)

Implementation Reference

  • Core handler function that queries HubSpot's engagements API for recent engagements (notes, emails, tasks, meetings, calls) within the given number of days, formats the response consistently, converts datetime fields, and handles errors.
    async getRecentEngagements(days: number = 7, limit: number = 50): Promise<any> {
      try {
        // Calculate the date range (past N days)
        const endTime = new Date();
        const startTime = new Date(endTime);
        startTime.setDate(startTime.getDate() - days);
        
        // Format timestamps for API call
        const startTimestamp = Math.floor(startTime.getTime());
        const endTimestamp = Math.floor(endTime.getTime());
        
        // Get all recent engagements
        const engagementsResponse = await this.client.apiRequest({
          method: 'GET',
          path: '/engagements/v1/engagements/recent/modified',
          qs: {
            count: limit,
            since: startTimestamp,
            offset: 0
          }
        });
        
        // Format the engagements similar to company_activity
        const formattedEngagements = [];
        
        // Ensure we have a proper response body
        const responseBody = engagementsResponse.body as any;
        for (const engagement of responseBody.results || []) {
          const engagementData = engagement.engagement || {};
          const metadata = engagement.metadata || {};
          
          const formattedEngagement: Record<string, any> = {
            id: engagementData.id,
            type: engagementData.type,
            created_at: engagementData.createdAt,
            last_updated: engagementData.lastUpdated,
            created_by: engagementData.createdBy,
            modified_by: engagementData.modifiedBy,
            timestamp: engagementData.timestamp,
            associations: engagement.associations || {}
          };
          
          // Add type-specific metadata formatting identical to company_activity
          if (engagementData.type === 'NOTE') {
            formattedEngagement.content = metadata.body || '';
          } else if (engagementData.type === 'EMAIL') {
            formattedEngagement.content = {
              subject: metadata.subject || '',
              from: {
                raw: metadata.from?.raw || '',
                email: metadata.from?.email || '',
                firstName: metadata.from?.firstName || '',
                lastName: metadata.from?.lastName || ''
              },
              to: (metadata.to || []).map((recipient: any) => ({
                raw: recipient.raw || '',
                email: recipient.email || '',
                firstName: recipient.firstName || '',
                lastName: recipient.lastName || ''
              })),
              cc: (metadata.cc || []).map((recipient: any) => ({
                raw: recipient.raw || '',
                email: recipient.email || '',
                firstName: recipient.firstName || '',
                lastName: recipient.lastName || ''
              })),
              bcc: (metadata.bcc || []).map((recipient: any) => ({
                raw: recipient.raw || '',
                email: recipient.email || '',
                firstName: recipient.firstName || '',
                lastName: recipient.lastName || ''
              })),
              sender: {
                email: metadata.sender?.email || ''
              },
              body: metadata.text || metadata.html || ''
            };
          } else if (engagementData.type === 'TASK') {
            formattedEngagement.content = {
              subject: metadata.subject || '',
              body: metadata.body || '',
              status: metadata.status || '',
              for_object_type: metadata.forObjectType || ''
            };
          } else if (engagementData.type === 'MEETING') {
            formattedEngagement.content = {
              title: metadata.title || '',
              body: metadata.body || '',
              start_time: metadata.startTime,
              end_time: metadata.endTime,
              internal_notes: metadata.internalMeetingNotes || ''
            };
          } else if (engagementData.type === 'CALL') {
            formattedEngagement.content = {
              body: metadata.body || '',
              from_number: metadata.fromNumber || '',
              to_number: metadata.toNumber || '',
              duration_ms: metadata.durationMilliseconds,
              status: metadata.status || '',
              disposition: metadata.disposition || ''
            };
          }
          
          formattedEngagements.push(formattedEngagement);
        }
        
        // Convert any datetime fields and return
        return convertDatetimeFields(formattedEngagements);
      } catch (error: any) {
        console.error('Error getting recent engagements:', error);
        return { error: error.message };
      }
    }
  • MCP server dispatch handler for the tool; parses arguments, calls the HubspotClient's getRecentEngagements method, and returns the result as JSON text response.
    case 'hubspot_get_recent_engagements': {
      const result = await this.hubspot.getRecentEngagements(
        args.days as number | undefined,
        args.limit as number | undefined
      );
      return {
        content: [{
          type: 'text',
          text: JSON.stringify(result, null, 2)
        }]
      };
  • src/index.ts:139-157 (registration)
    Tool registration in the listTools response, including name, description, and input schema definition.
    {
      name: 'hubspot_get_recent_engagements',
      description: 'Get recent engagement activities across all contacts and companies',
      inputSchema: {
        type: 'object',
        properties: {
          days: { 
            type: 'number', 
            description: 'Number of days to look back (default: 7)',
            default: 7
          },
          limit: { 
            type: 'number', 
            description: 'Maximum number of engagements to return (default: 50)',
            default: 50
          }
        }
      }
    },
  • Input schema definition for the tool parameters (days and limit with defaults).
    properties: {
      days: { 
        type: 'number', 
        description: 'Number of days to look back (default: 7)',
        default: 7
      },
      limit: { 
        type: 'number', 
        description: 'Maximum number of engagements to return (default: 50)',
        default: 50
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool 'Get[s] recent engagement activities', which implies a read-only operation, but it doesn't disclose any behavioral traits such as authentication requirements, rate limits, pagination, or what constitutes an 'engagement activity'. This leaves significant gaps for a tool with no annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose without any wasted words. It's appropriately sized for a simple tool and earns its place by clearly stating what the tool does.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of annotations and no output schema, the description is incomplete. It doesn't explain what 'engagement activities' entail, the format of the return data, or any error conditions. For a tool that retrieves data across multiple entities, more context is needed to guide the agent effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, with clear documentation for both parameters (days and limit), including defaults. The description doesn't add any parameter semantics beyond what the schema provides, such as explaining what 'engagements' include or how the lookback period works. This meets the baseline of 3 since the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Get' and the resource 'recent engagement activities across all contacts and companies', making the purpose specific and understandable. It distinguishes this tool from siblings like hubspot_get_company_activity by specifying 'across all contacts and companies' rather than focusing on a single entity, though it doesn't explicitly name alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for retrieving recent engagement activities, but it doesn't provide explicit guidance on when to use this tool versus alternatives like hubspot_get_company_activity or hubspot_get_active_contacts. No exclusions or prerequisites are mentioned, leaving the agent to infer context based on the tool's name and description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/lkm1developer/hubspot-mcp-server'

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