Skip to main content
Glama
ajaystream

HubSpot MCP Server

by ajaystream

hubspot-get-link

Read-onlyIdempotent

Generate HubSpot UI links for object lists or specific records using object types and IDs. Validate object types and create direct links to HubSpot pages for reference.

Instructions

🎯 Purpose:
  1. Generates HubSpot UI links for different pages based on object types and IDs.
  2. Supports both index pages (lists of objects) and record pages (specific object details).

📋 Prerequisites:
  1. Use the hubspot-get-user-details tool to get the PortalId and UiDomain.

🧭 Usage Guidance:
  1. Use to generate links to HubSpot UI pages when users need to reference specific HubSpot records.
  2. Validates that object type IDs exist in the HubSpot system.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
portalIdYesThe HubSpot portal/account ID
uiDomainYesThe HubSpot UI domain(e.g., 'app.hubspot.com')
pageRequestsYesArray of page link requests to generate

Implementation Reference

  • The GetHubspotLinkTool class implements the core tool logic. The `process` method validates inputs, generates HubSpot UI links for records or indexes based on object types and IDs, and returns the URLs as JSON.
    export class GetHubspotLinkTool extends BaseTool {
        constructor() {
            super(GetHubspotLinkSchema, ToolDefinition);
        }
        async process(args) {
            const { portalId, uiDomain, pageRequests } = args;
            const validationResult = this.validateRequests(pageRequests);
            if (validationResult.errors.length > 0) {
                return this.formatErrorResponse(validationResult);
            }
            const urlResults = this.generateUrls(portalId, uiDomain, pageRequests);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(urlResults, null, 2),
                    },
                ],
            };
        }
        isValidObjectTypeId(objectTypeId) {
            if (Object.keys(HUBSPOT_ID_TO_OBJECT_TYPE).includes(objectTypeId)) {
                return true;
            }
            if (objectTypeId.startsWith('2-')) {
                return true;
            }
            return false;
        }
        validateRequests(pageRequests) {
            const errors = [];
            const invalidObjectTypeIds = [];
            for (const request of pageRequests) {
                const { pagetype, objectTypeId, objectId } = request;
                // Validate objectTypeId exists
                if (!this.isValidObjectTypeId(objectTypeId)) {
                    invalidObjectTypeIds.push(objectTypeId);
                    errors.push(`Invalid objectTypeId: ${objectTypeId}`);
                    continue;
                }
                // For record pages, objectId is required
                if (pagetype === 'record' && !objectId) {
                    errors.push(`objectId is required for record page with objectTypeId: ${objectTypeId}`);
                }
            }
            return { errors, invalidObjectTypeIds };
        }
        formatErrorResponse(validationResult) {
            const errorResponse = {
                errors: validationResult.errors,
            };
            // Add valid object type IDs only once if there were invalid IDs
            if (validationResult.invalidObjectTypeIds.length > 0) {
                errorResponse.validObjectTypeIds = Object.keys(HUBSPOT_ID_TO_OBJECT_TYPE);
                errorResponse.validObjectTypeIds.push('2-x (where x is your custom object ID)');
            }
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(errorResponse, null, 2),
                    },
                ],
                isError: true,
            };
        }
        generateUrls(portalId, uiDomain, pageRequests) {
            return pageRequests.map(request => {
                const { pagetype, objectTypeId, objectId } = request;
                let url = '';
                if (pagetype === 'index') {
                    url = `https://${uiDomain}/contacts/${portalId}/objects/${objectTypeId}`;
                }
                else {
                    url = `https://${uiDomain}/contacts/${portalId}/record/${objectTypeId}/${objectId}`;
                }
                return {
                    pagetype,
                    objectTypeId,
                    objectId,
                    url,
                };
            });
        }
    }
  • Zod schemas defining the input parameters for the tool, including page type enum, object type ID, object ID, portal ID, UI domain, and array of page requests.
    const PageTypeEnum = z
        .enum(['record', 'index'])
        .describe("The type of page to link to: 'record' for a specific object's page, 'index' for a list page");
    const PageRequestSchema = z.object({
        pagetype: PageTypeEnum,
        objectTypeId: z
            .string()
            .describe("The HubSpot object type ID to link to (e.g., '0-1', '0-2' for contacts, companies, or '2-x' for custom objects)"),
        objectId: z
            .string()
            .optional()
            .describe("The specific object ID to link to (required for 'record' page types)"),
    });
    const GetHubspotLinkSchema = z.object({
        portalId: z.string().describe('The HubSpot portal/account ID'),
        uiDomain: z.string().describe("The HubSpot UI domain(e.g., 'app.hubspot.com')"),
        pageRequests: z.array(PageRequestSchema).describe('Array of page link requests to generate'),
    });
  • ToolDefinition object with the tool name 'hubspot-get-link', detailed description, converted JSON schema, and annotations. Passed to BaseTool constructor.
    const ToolDefinition = {
        name: 'hubspot-get-link',
        description: `
        🎯 Purpose:
          1. Generates HubSpot UI links for different pages based on object types and IDs.
          2. Supports both index pages (lists of objects) and record pages (specific object details).
    
        📋 Prerequisites:
          1. Use the hubspot-get-user-details tool to get the PortalId and UiDomain.
    
        🧭 Usage Guidance:
          1. Use to generate links to HubSpot UI pages when users need to reference specific HubSpot records.
          2. Validates that object type IDs exist in the HubSpot system.
      `,
        inputSchema: zodToJsonSchema(GetHubspotLinkSchema),
        annotations: {
            title: 'Get HubSpot Link',
            readOnlyHint: true,
            destructiveHint: false,
            idempotentHint: true,
            openWorldHint: false,
        },
    };
  • The tool is instantiated and registered in the central tools registry using registerTool.
    registerTool(new GetHubspotLinkTool());
  • Helper constant mapping HubSpot object type IDs to object type names, used in isValidObjectTypeId for validation.
    export const HUBSPOT_ID_TO_OBJECT_TYPE = Object.entries(HUBSPOT_OBJECT_TYPE_TO_ID).reduce((acc, [objectType, id]) => ({
        ...acc,
        [id]: objectType,
    }), {});
Behavior4/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, indicating safe, non-destructive operations. The description adds valuable behavioral context beyond annotations: 'Validates that object type IDs exist in the HubSpot system' and clarifies it generates UI links rather than performing data mutations. No contradiction with annotations.

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 well-structured with clear sections (Purpose, Prerequisites, Usage Guidance), front-loaded with the core purpose, and every sentence adds value without redundancy. It's appropriately sized for the tool's complexity.

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

Completeness4/5

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

Given the tool's moderate complexity, 100% schema coverage, and comprehensive annotations, the description is mostly complete. It covers purpose, prerequisites, usage, and validation behavior. The main gap is no output schema, but the description doesn't need to explain return values extensively for a link generation tool.

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?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds minimal parameter semantics beyond the schema, mainly reinforcing that parameters come from hubspot-get-user-details and object types/IDs must be valid. Baseline 3 is appropriate when schema does the heavy lifting.

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

Purpose5/5

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

The description explicitly states the tool 'Generates HubSpot UI links for different pages based on object types and IDs' and distinguishes it from siblings by specifying it's for link generation rather than data operations like create, read, update, or search. It clearly identifies the verb (generates) and resource (HubSpot UI links).

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

Usage Guidelines5/5

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

The description provides explicit usage guidance: 'Use to generate links to HubSpot UI pages when users need to reference specific HubSpot records' and includes prerequisites ('Use the hubspot-get-user-details tool to get the PortalId and UiDomain'). It clearly states when to use this tool and references an alternative tool for prerequisites.

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/ajaystream/hubspot-mcp-custom'

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