Skip to main content
Glama

HubSpot MCP

import { StdioClientTransport, StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js' import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { CallToolRequest, JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js' import path from 'path' const RESPONSE_TIMEOUT = 1_000 // 1s const START_DELAY = 1_000 // 1s const TEST_TIMEOUT = 10_000 // 10s const SEARCH_DELAY = 8 // 8s const TOTAL_TOOLS = 112 const streamableClientUrl = new URL(`http://localhost:${process.env.PORT || 3000}/mcp`) jest.setTimeout(TEST_TIMEOUT) type ReadMessageType = { jsonrpc: string id: number result: { content?: { type: string text: string }[], tools?: any[] } } type JSONRPCMessageWithParams = JSONRPCMessage & { params?: CallToolRequest["params"] } // In some tests the argument IDs in this object are modified, ensure the ID is always set per-test where relevant const jsonRpcMessage: Record<string, JSONRPCMessageWithParams> = { ping: { jsonrpc: "2.0", id: 1, method: "ping" }, pong: { jsonrpc: '2.0', id: 1, result: {} }, toolsList: { jsonrpc: "2.0", id: 1, method: "tools/list" }, crmCreateCompany: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_create_company", arguments: { properties: { name: "Test Company", domain: "test.com", website: "https://test.com", description: "Test Description", industry: "CONSUMER_SERVICES", numberofemployees: 10, annualrevenue: 100000, city: "Test City", state: "Test State", country: "Test Country", phone: "1234567890", address: "Test Address", address2: "Test Address 2", zip: "123456", type: "PARTNER", lifecyclestage: "lead" } } } }, crmGetCompany: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_get_company", arguments: { companyId: "test-id" } } }, crmUpdateCompany: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_update_company", arguments: { companyId: "test-id", properties: { name: "Test Company Updated" } } } }, crmSearchCompanies: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_search_companies", arguments: { filterGroups: [{ filters: [{ propertyName: "name", operator: "CONTAINS_TOKEN", value: "Test Company" }] }] } } }, crmArchiveObject: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_archive_object", arguments: { objectType: "companies", objectId: "test-id" } } }, productsCreate: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_create", arguments: { properties: { name: "Test Product" } } } }, productsRead: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_read", arguments: { productId: "test-id" } } }, productsUpdate: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_update", arguments: { productId: "test-id", properties: { name: "Test Product Updated" } } } }, productsList: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_list", arguments: { limit: 100, properties: ["name", "description"] } } }, productsSearch: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_search", arguments: { limit: 100, properties: ["name", "description"], filterGroups: [{ filters: [{ propertyName: "name", operator: "CONTAINS_TOKEN", value: "Test Product" }] }] } } }, productsArchive: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_archive", arguments: { productId: "test-id" } } }, productsBatchCreate: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_batch_create", arguments: { inputs: [ { properties: { name: "Test Product" } }, { properties: { name: "Test Product 2" } } ] } } }, productsBatchRead: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_batch_read", arguments: { propertiesWithHistory: [], properties: ["name"] } } }, productsBatchUpdate: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_batch_update", arguments: {} } }, productsBatchUpsert: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_batch_upsert", arguments: {} } }, productsBatchArchive: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "products_batch_archive", arguments: {} } }, crmBatchReadObjects: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_batch_read_objects", arguments: { objectType: "companies", propertiesWithHistory: ["name", "domain"], inputs: [{ id: "test-id" }], properties: ["name", "domain"] } } }, crmListObjects: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_list_objects", arguments: { objectType: "companies", limit: 100, properties: ["name", "domain"] } } }, crmCreateObject: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_create_object", arguments: { objectType: "companies", properties: { name: "Test Company", domain: "test.com", website: "https://test.com", description: "Test Description", industry: "CONSUMER_SERVICES", numberofemployees: 10, annualrevenue: 100000, city: "Test City", state: "Test State", country: "Test Country", phone: "1234567890", address: "Test Address", address2: "Test Address 2", zip: "123456", type: "PARTNER", lifecyclestage: "lead" } } } }, crmGetObject: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_get_object", arguments: { objectType: "companies", objectId: "test-id", properties: ["name", "domain"] } } }, crmUpdateObject: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_update_object", arguments: { objectType: "companies", objectId: "test-id", properties: { name: "Test Company Updated", domain: "updated-test.com" } } } }, crmSearchObjects: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_search_objects", arguments: { objectType: "companies", filterGroups: [{ filters: [{ propertyName: "name", operator: "CONTAINS_TOKEN", value: "Test Company" }] }], properties: ["name", "domain"] } } }, crmBatchCreateObjects: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_batch_create_objects", arguments: { objectType: "companies", inputs: [ { properties: { name: "Test Company 1", domain: "test1.com", type: "PARTNER", lifecyclestage: "lead" } }, { properties: { name: "Test Company 2", domain: "test2.com", type: "PARTNER", lifecyclestage: "lead" } } ] } } }, crmBatchUpdateObjects: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_batch_update_objects", arguments: { objectType: "companies", inputs: [ { id: "test-id", properties: { name: "Updated Company 1", type: "PARTNER" } }, { id: "test-id", properties: { name: "Updated Company 2", type: "PARTNER" } } ] } } }, crmBatchArchiveObjects: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "crm_batch_archive_objects", arguments: { objectType: "companies", inputs: [{ id: "test-id" }] } } } } const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) describe('Hubspot MCP', () => { let stdioClient: StdioClientTransport beforeAll(async () => { const serverParameters: StdioServerParameters = { command: "node", args: [path.resolve(__dirname, '../dist/index.js')], env: process.env as Record<string, string> } stdioClient = new StdioClientTransport(serverParameters) await stdioClient.start() }) afterAll(async () => { await stdioClient.close() }) describe('Stdio Transport', () => { let readMessages: ReadMessageType[] let errors: Error[] let companyId: string let productId: string let batchProductIds: string[] let crmObjectId: string let crmBatchObjectIds: string[] beforeAll(async () => { await delay(START_DELAY) stdioClient.onmessage = (message) => readMessages.push(message as ReadMessageType) stdioClient.onerror = (error) => errors.push(error) }) beforeEach(async () => { readMessages = [] errors = [] }) it('responds to ping', async () => { stdioClient.send(jsonRpcMessage.ping) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0]).toEqual(jsonRpcMessage.pong) expect(errors).toHaveLength(0) }) it('returns a list of tools', async () => { stdioClient.send(jsonRpcMessage.toolsList) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.tools?.length).toEqual(TOTAL_TOOLS) }) it('can call the crm_create_company tool', async () => { stdioClient.send(jsonRpcMessage.crmCreateCompany) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const company = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(company.properties.name).toEqual('Test Company') companyId = company.id }) it('can call the crm_get_company tool', async () => { if (!jsonRpcMessage.crmGetCompany.params?.arguments) throw new Error('Missing params or arguments') jsonRpcMessage.crmGetCompany.params.arguments.companyId = companyId stdioClient.send(jsonRpcMessage.crmGetCompany) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const company = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(company.properties.name).toEqual('Test Company') }) it('can call the crm_update_company tool', async () => { const params = jsonRpcMessage.crmUpdateCompany.params as CallToolRequest["params"] if (params?.arguments) params.arguments.companyId = companyId stdioClient.send(jsonRpcMessage.crmUpdateCompany) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const updated = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(updated.properties.name).toEqual('Test Company Updated') }) it('can call the crm_search_companies tool', async () => { await delay(RESPONSE_TIMEOUT * SEARCH_DELAY) // Wait additional time to ensure the company is indexed stdioClient.send(jsonRpcMessage.crmSearchCompanies) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const results = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(results.results)).toBe(true) expect(results.results.length).toBeGreaterThan(0) expect(results.results[0].id).toEqual(companyId) }) it('can call the crm_archive_object tool', async () => { const params = jsonRpcMessage.crmArchiveObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = companyId stdioClient.send(jsonRpcMessage.crmArchiveObject) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) expect(readMessages[0].result.content?.[0].text).toEqual(`No data returned: Status 204`) }) it('can call the products_create tool', async () => { stdioClient.send(jsonRpcMessage.productsCreate) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const product = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(product.properties.name).toEqual('Test Product') productId = product.id }) it('can call the products_read tool', async () => { const params = jsonRpcMessage.productsRead.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productId = productId stdioClient.send(jsonRpcMessage.productsRead) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const product = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(product.properties.name).toEqual('Test Product') }) it('can call the products_update tool', async () => { const params = jsonRpcMessage.productsUpdate.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productId = productId stdioClient.send(jsonRpcMessage.productsUpdate) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const updated = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(updated.properties.name).toEqual('Test Product Updated') }) it('can call the products_list tool', async () => { stdioClient.send(jsonRpcMessage.productsList) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const results = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(results.results)).toBe(true) }) it('can call the products_search tool', async () => { await delay(RESPONSE_TIMEOUT * SEARCH_DELAY) // Wait additional time to ensure the product is indexed stdioClient.send(jsonRpcMessage.productsSearch) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const results = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(results.results)).toBe(true) expect(results.results.length).toBeGreaterThan(0) expect(results.results[0].id).toEqual(productId) }) it('can call the products_archive tool', async () => { const params = jsonRpcMessage.productsArchive.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productId = productId stdioClient.send(jsonRpcMessage.productsArchive) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) expect(readMessages[0].result.content?.[0].text).toEqual(`No data returned: Status 204`) }) it('can call the products_batch_create tool', async () => { stdioClient.send(jsonRpcMessage.productsBatchCreate) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) batchProductIds = result.results.map((p: any) => p.id) }) it('can call the products_batch_read tool', async () => { const params = jsonRpcMessage.productsBatchRead.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productIds = batchProductIds stdioClient.send(jsonRpcMessage.productsBatchRead) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) }) it('can call the products_batch_update tool', async () => { const params = jsonRpcMessage.productsBatchUpdate.params as CallToolRequest["params"] if (params?.arguments) params.arguments.inputs = batchProductIds.map(id => ({ id, properties: { name: `Batch Updated Product ${id}` } })) stdioClient.send(jsonRpcMessage.productsBatchUpdate) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) }) it('can call the products_batch_archive tool', async () => { const params = jsonRpcMessage.productsBatchArchive.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productIds = batchProductIds stdioClient.send(jsonRpcMessage.productsBatchArchive) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) }) describe('CRM Object Operations', () => { it('can call the crm_create_object tool', async () => { stdioClient.send(jsonRpcMessage.crmCreateObject) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(result.properties.name).toEqual('Test Company') expect(result.properties.domain).toEqual('test.com') crmObjectId = result.id }) it('can call the crm_get_object tool', async () => { const params = jsonRpcMessage.crmGetObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = crmObjectId stdioClient.send(jsonRpcMessage.crmGetObject) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(result.properties.name).toEqual('Test Company') expect(result.properties.domain).toEqual('test.com') }) it('can call the crm_update_object tool', async () => { const params = jsonRpcMessage.crmUpdateObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = crmObjectId stdioClient.send(jsonRpcMessage.crmUpdateObject) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(result.properties.name).toEqual('Test Company Updated') expect(result.properties.domain).toEqual('updated-test.com') }) it('can call the crm_search_objects tool', async () => { await delay(RESPONSE_TIMEOUT * SEARCH_DELAY) // Wait for indexing stdioClient.send(jsonRpcMessage.crmSearchObjects) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBeGreaterThan(0) expect(result.results[0].id).toEqual(crmObjectId) }) it('can call the crm_list_objects tool', async () => { stdioClient.send(jsonRpcMessage.crmListObjects) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBeGreaterThan(0) const foundObject = result.results.find((obj: any) => obj.id === crmObjectId) expect(foundObject).toBeDefined() }) it('can call the crm_batch_create_objects tool', async () => { stdioClient.send(jsonRpcMessage.crmBatchCreateObjects) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBe(2) crmBatchObjectIds = result.results.map((obj: any) => obj.id) }) it('can call the crm_batch_read_objects tool', async () => { const params = jsonRpcMessage.crmBatchReadObjects.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectIds = crmBatchObjectIds stdioClient.send(jsonRpcMessage.crmBatchReadObjects) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBe(2) expect(result.results[0].properties.name).toBeDefined() expect(result.results[0].properties.domain).toBeDefined() expect(result.results[1].properties.name).toBeDefined() expect(result.results[1].properties.domain).toBeDefined() }) it('can call the crm_batch_update_objects tool', async () => { const params = jsonRpcMessage.crmBatchUpdateObjects.params as CallToolRequest["params"] if (params?.arguments) params.arguments.inputs = crmBatchObjectIds.map((id, index) => ({ id, properties: { name: `Updated Company ${index + 1}`, type: "PARTNER" } })) stdioClient.send(jsonRpcMessage.crmBatchUpdateObjects) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) const result = JSON.parse(readMessages[0].result.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBe(2) }) it('can call the crm_batch_archive_objects tool', async () => { const params = jsonRpcMessage.crmBatchArchiveObjects.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectIds = crmBatchObjectIds stdioClient.send(jsonRpcMessage.crmBatchArchiveObjects) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) }) it('can call the crm_archive_object tool', async () => { const params = jsonRpcMessage.crmArchiveObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = crmObjectId stdioClient.send(jsonRpcMessage.crmArchiveObject) await delay(RESPONSE_TIMEOUT) expect(readMessages).toHaveLength(1) expect(readMessages[0].result.content?.length).toEqual(1) expect(readMessages[0].result.content?.[0].text).toEqual(`No data returned: Status 204`) }) }) }) describe('Streamable HTTP Transport', () => { let streamableClient: Client = new Client({ name: 'streamable-test-client', version: '1.0.0' }) let companyId: string let productId: string let batchProductIds: string[] let crmObjectId: string let crmBatchObjectIds: string[] beforeAll(async () => { let transport = new StreamableHTTPClientTransport(streamableClientUrl) await streamableClient.connect(transport) }) it('responds to ping', async () => { const response = await streamableClient.ping() expect(response).toEqual({}) }) it('returns a list of tools', async () => { const response = await streamableClient.listTools() expect(response.tools.length).toEqual(TOTAL_TOOLS) }) it('can call the crm_create_company tool', async () => { const params = jsonRpcMessage.crmCreateCompany.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const company = JSON.parse(response.content?.[0].text ?? '{}') expect(response.content?.[0].type).toBe('text') companyId = company.id }) it('can call the crm_get_company tool', async () => { const params = jsonRpcMessage.crmGetCompany.params as CallToolRequest["params"] if (params?.arguments) params.arguments.companyId = companyId const response = await streamableClient.callTool(params) const company = JSON.parse(response.content?.[0].text ?? '{}') expect(company.properties.name).toEqual('Test Company') }) it('can call the crm_update_company tool', async () => { const params = jsonRpcMessage.crmUpdateCompany.params as CallToolRequest["params"] if (params?.arguments) params.arguments.companyId = companyId const response = await streamableClient.callTool(params) const updated = JSON.parse(response.content?.[0].text ?? '{}') expect(updated.properties.name).toEqual('Test Company Updated') }) it('can call the crm_search_companies tool', async () => { await delay(RESPONSE_TIMEOUT * SEARCH_DELAY) // Wait additional time to ensure the company is indexed const params = jsonRpcMessage.crmSearchCompanies.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const results = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(results.results)).toBe(true) expect(results.results.length).toBeGreaterThan(0) expect(results.results[0].id).toEqual(companyId) }) it('can call the crm_archive_object tool', async () => { if (!companyId) throw new Error('No company ID available') const params = jsonRpcMessage.crmArchiveObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = companyId const response = await streamableClient.callTool(params) expect(response.content?.[0].text).toEqual(`No data returned: Status 204`) }) it('can call the products_create tool', async () => { const params = jsonRpcMessage.productsCreate.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const product = JSON.parse(response.content?.[0].text ?? '{}') expect(product.properties.name).toEqual('Test Product') productId = product.id }) it('can call the products_read tool', async () => { const params = jsonRpcMessage.productsRead.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productId = productId const response = await streamableClient.callTool(params) const product = JSON.parse(response.content?.[0].text ?? '{}') expect(product.properties.name).toEqual('Test Product') }) it('can call the products_update tool', async () => { const params = jsonRpcMessage.productsUpdate.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productId = productId const response = await streamableClient.callTool(params) const updated = JSON.parse(response.content?.[0].text ?? '{}') expect(updated.properties.name).toEqual('Test Product Updated') }) it('can call the products_list tool', async () => { const params = jsonRpcMessage.productsList.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const results = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(results.results)).toBe(true) expect(results.results.length).toBeGreaterThan(0) expect(results.results[0].id).toEqual(productId) }) it('can call the products_search tool', async () => { await delay(RESPONSE_TIMEOUT * SEARCH_DELAY) // Wait additional time to ensure the product is indexed const params = jsonRpcMessage.productsSearch.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const results = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(results.results)).toBe(true) expect(results.results.length).toBeGreaterThan(0) expect(results.results[0].id).toEqual(productId) }) it('can call the products_archive tool', async () => { const params = jsonRpcMessage.productsArchive.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productId = productId const response = await streamableClient.callTool(params) expect(response.content?.[0].text).toEqual(`No data returned: Status 204`) }) it('can call the products_batch_create tool', async () => { const params = jsonRpcMessage.productsBatchCreate.params as CallToolRequest["params"] if (params?.arguments) params.arguments.inputs = [ { properties: { name: "Test Product" } }, { properties: { name: "Test Product 2" } } ] const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) batchProductIds = result.results.map((p: any) => p.id) }) it('can call the products_batch_read tool', async () => { const params = jsonRpcMessage.productsBatchRead.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productIds = batchProductIds const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) }) it('can call the products_batch_update tool', async () => { const params = jsonRpcMessage.productsBatchUpdate.params as CallToolRequest["params"] if (params?.arguments) params.arguments.inputs = batchProductIds.map(id => ({ id, properties: { name: `Batch Updated Product ${id}` } })) const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) }) it('can call the products_batch_archive tool', async () => { const params = jsonRpcMessage.productsBatchArchive.params as CallToolRequest["params"] if (params?.arguments) params.arguments.productIds = batchProductIds const response = await streamableClient.callTool(params) expect(response.content?.[0].text).toEqual(`No data returned: Status 204`) }) describe('CRM Object Operations', () => { it('can call the crm_create_object tool', async () => { const params = jsonRpcMessage.crmCreateObject.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(result.properties.name).toEqual('Test Company') expect(result.properties.domain).toEqual('test.com') crmObjectId = result.id }) it('can call the crm_get_object tool', async () => { const params = jsonRpcMessage.crmGetObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = crmObjectId const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(result.properties.name).toEqual('Test Company') expect(result.properties.domain).toEqual('test.com') }) it('can call the crm_update_object tool', async () => { const params = jsonRpcMessage.crmUpdateObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = crmObjectId const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(result.properties.name).toEqual('Test Company Updated') expect(result.properties.domain).toEqual('updated-test.com') }) it('can call the crm_search_objects tool', async () => { await delay(RESPONSE_TIMEOUT * SEARCH_DELAY) // Wait for indexing const params = jsonRpcMessage.crmSearchObjects.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBeGreaterThan(0) expect(result.results[0].id).toEqual(crmObjectId) }) it('can call the crm_list_objects tool', async () => { const params = jsonRpcMessage.crmListObjects.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBeGreaterThan(0) const foundObject = result.results.find((obj: any) => obj.id === crmObjectId) expect(foundObject).toBeDefined() }) it('can call the crm_batch_create_objects tool', async () => { const params = jsonRpcMessage.crmBatchCreateObjects.params as CallToolRequest["params"] const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBe(2) crmBatchObjectIds = result.results.map((obj: any) => obj.id) }) it('can call the crm_batch_read_objects tool', async () => { const params = jsonRpcMessage.crmBatchReadObjects.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectIds = crmBatchObjectIds const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBe(2) expect(result.results[0].properties.name).toBeDefined() expect(result.results[0].properties.domain).toBeDefined() expect(result.results[1].properties.name).toBeDefined() expect(result.results[1].properties.domain).toBeDefined() }) it('can call the crm_batch_update_objects tool', async () => { const params = jsonRpcMessage.crmBatchUpdateObjects.params as CallToolRequest["params"] if (params?.arguments) params.arguments.inputs = crmBatchObjectIds.map((id, index) => ({ id, properties: { name: `Updated Company ${index + 1}`, type: "PARTNER" } })) const response = await streamableClient.callTool(params) const result = JSON.parse(response.content?.[0].text ?? '{}') expect(Array.isArray(result.results)).toBe(true) expect(result.results.length).toBe(2) }) it('can call the crm_batch_archive_objects tool', async () => { const params = jsonRpcMessage.crmBatchArchiveObjects.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectIds = crmBatchObjectIds const response = await streamableClient.callTool(params) expect(response.content?.[0].text).toEqual(`No data returned: Status 204`) }) it('can call the crm_archive_object tool', async () => { const params = jsonRpcMessage.crmArchiveObject.params as CallToolRequest["params"] if (params?.arguments) params.arguments.objectId = crmObjectId const response = await streamableClient.callTool(params) expect(response.content?.[0].text).toEqual(`No data returned: Status 204`) }) }) }) })

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/shinzo-labs/hubspot-mcp'

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