hs_crm_search
Run structured CRM searches with custom filters against any object type. Supports operators, sorting, and property selection. Use when preset tools don't cover your query.
Instructions
Escape hatch: run a structured CRM search with custom filter conditions against any object type. Use when preset tools don't cover your query. Docs: developers.hubspot.com/docs/api/crm/search
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| objectType | Yes | CRM object type to search | |
| filters | Yes | Filter conditions to apply | |
| properties | No | Properties to return | |
| sorts | No | ||
| limit | No |
Implementation Reference
- src/tools/power.ts:23-30 (handler)The crmSearch handler function that executes the HubSpot CRM search. It calls the POST /crm/v3/objects/{objectType}/search endpoint with filter groups, properties, sorts, and limit.
export async function crmSearch(args: z.infer<typeof CrmSearchSchema>) { return hubspot(`/crm/v3/objects/${args.objectType}/search`, "POST", { filterGroups: [{ filters: args.filters }], properties: args.properties, sorts: args.sorts ?? [{ propertyName: "createdate", direction: "DESCENDING" }], limit: args.limit ?? 50, }); } - src/tools/power.ts:6-21 (schema)The CrmSearchSchema Zod schema defining the input shape: objectType (enum of CRM types), filters (array of filter conditions), properties, sorts, and limit.
export const CrmSearchSchema = z.object({ objectType: z.enum(OBJECT_TYPES).describe("CRM object type to search"), filters: z.array(z.object({ propertyName: z.string(), operator: z.enum(["EQ", "NEQ", "LT", "LTE", "GT", "GTE", "BETWEEN", "IN", "NOT_IN", "HAS_PROPERTY", "NOT_HAS_PROPERTY", "CONTAINS_TOKEN", "NOT_CONTAINS_TOKEN"]), value: z.string().optional(), values: z.array(z.string()).optional(), highValue: z.string().optional(), })).describe("Filter conditions to apply"), properties: z.array(z.string()).optional().describe("Properties to return"), sorts: z.array(z.object({ propertyName: z.string(), direction: z.enum(["ASCENDING", "DESCENDING"]).default("DESCENDING"), })).optional(), limit: z.number().int().min(1).max(200).default(50).optional(), }); - src/index.ts:305-310 (registration)Registration of the 'hs_crm_search' tool on the MCP server with its schema and handler.
server.tool( "hs_crm_search", "Escape hatch: run a structured CRM search with custom filter conditions against any object type. Use when preset tools don't cover your query. Docs: developers.hubspot.com/docs/api/crm/search", CrmSearchSchema.shape, async (args) => { try { return ok(await crmSearch(args)); } catch (e) { return err(e); } }, ); - src/index.ts:74-74 (helper)Import of CrmSearchSchema and crmSearch from the power module.
import { CrmSearchSchema, crmSearch } from "./tools/power.js"; - src/client.ts:16-48 (helper)The hubspot() helper function used by crmSearch to make authenticated API calls to HubSpot.
export async function hubspot<T = unknown>( path: string, method: "GET" | "POST" | "PATCH" | "DELETE" = "GET", body?: unknown, params?: Record<string, string | number | boolean>, ): Promise<T> { const token = getToken(); let url = `${BASE_URL}${path}`; if (params && method === "GET") { const qs = new URLSearchParams( Object.entries(params).map(([k, v]) => [k, String(v)]), ).toString(); if (qs) url += `?${qs}`; } const res = await fetch(url, { method, headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, ...(body && method !== "GET" ? { body: JSON.stringify(body) } : {}), }); if (!res.ok) { const text = await res.text().catch(() => res.statusText); throw new HubSpotError(`HubSpot API error (${res.status}): ${text}`, res.status); } if (res.status === 204) return undefined as T; return (await res.json()) as T; }