/**
* Company Tool
*
* Application layer tool for managing Company entities in Twenty CRM.
*/
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
export const CREATE_COMPANY_TOOL_NAME = 'create-company';
export const GET_COMPANY_TOOL_NAME = 'get-company';
export const LIST_COMPANIES_TOOL_NAME = 'list-companies';
export const CreateCompanyInputSchema = z.object({
name: z.string().describe('Company name'),
domainName: z.string().optional().describe('Domain name (e.g., example.com)'),
address: z.string().optional().describe('Company address'),
employees: z.number().int().optional().describe('Number of employees'),
});
export const GetCompanyInputSchema = z.object({
id: z.string().describe('ID of the company to retrieve'),
});
export const ListCompaniesInputSchema = z.object({
limit: z.number().int().positive().optional().describe('Maximum number of results (default: 20)'),
offset: z.number().int().nonnegative().optional().describe('Number of results to skip (default: 0)'),
});
const CREATE_COMPANY_MUTATION = `
mutation CreateCompany($name: String!, $domainUrl: String, $addressStreet1: String, $addressCity: String, $addressState: String, $addressCountry: String, $addressPostcode: String, $employees: Float) {
createCompany(data: {
name: $name
domainName: { primaryLinkUrl: $domainUrl, primaryLinkLabel: "" }
address: {
addressStreet1: $addressStreet1
addressCity: $addressCity
addressState: $addressState
addressCountry: $addressCountry
addressPostcode: $addressPostcode
}
employees: $employees
}) {
id
name
domainName {
primaryLinkUrl
primaryLinkLabel
}
address {
addressStreet1
addressCity
addressState
addressCountry
addressPostcode
}
employees
createdAt
updatedAt
}
}
`;
const GET_COMPANY_QUERY = `
query GetCompany($id: ID!) {
company(filter: { id: { eq: $id } }) {
edges {
node {
id
name
domainName {
primaryLinkUrl
primaryLinkLabel
}
address {
addressStreet1
addressStreet2
addressCity
addressState
addressCountry
addressPostcode
}
employees
createdAt
updatedAt
}
}
}
}
`;
const LIST_COMPANIES_QUERY = `
query ListCompanies($limit: Int) {
companies(first: $limit) {
edges {
node {
id
name
domainName {
primaryLinkUrl
primaryLinkLabel
}
address {
addressStreet1
addressStreet2
addressCity
addressState
addressCountry
addressPostcode
}
employees
createdAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`;
function createToolDefinition(name, description, schema) {
const jsonSchema = zodToJsonSchema(schema, { name: `${name}Input`, $refStrategy: 'none' });
const actualSchema = jsonSchema.definitions?.[`${name}Input`] || jsonSchema;
return { name, description, inputSchema: actualSchema };
}
export class CompanyTool {
graphqlClient;
restClient;
logger;
constructor(graphqlClient, restClient, logger) {
this.graphqlClient = graphqlClient;
this.restClient = restClient;
this.logger = logger;
}
async createCompany(args) {
const parseResult = CreateCompanyInputSchema.safeParse(args);
if (!parseResult.success) {
return this.error('Invalid input: ' + parseResult.error.message);
}
const result = await this.graphqlClient.mutate(CREATE_COMPANY_MUTATION, parseResult.data);
if (!result?.createCompany) {
return this.error('Failed to create company');
}
return {
content: [{ type: 'text', text: JSON.stringify({ success: true, company: result.createCompany }, null, 2) }],
};
}
async getCompany(args) {
const parseResult = GetCompanyInputSchema.safeParse(args);
if (!parseResult.success) {
return this.error('Invalid input: ' + parseResult.error.message);
}
const result = await this.graphqlClient.query(GET_COMPANY_QUERY, parseResult.data);
if (!result?.company?.edges || result.company.edges.length === 0) {
return this.error('Company not found');
}
const company = result.company.edges[0].node;
return {
content: [{ type: 'text', text: JSON.stringify({ success: true, company }, null, 2) }],
};
}
async listCompanies(args) {
const parseResult = ListCompaniesInputSchema.safeParse(args);
if (!parseResult.success) {
return this.error('Invalid input: ' + parseResult.error.message);
}
const { limit = 20 } = parseResult.data;
const result = await this.graphqlClient.query(LIST_COMPANIES_QUERY, { limit });
if (!result?.companies) {
return this.error('Failed to list companies');
}
const companies = result.companies.edges.map((e) => ({
id: e.node.id,
name: e.node.name,
domainUrl: e.node.domainName?.primaryLinkUrl || null,
address: e.node.address,
employees: e.node.employees,
createdAt: e.node.createdAt,
}));
return {
content: [{ type: 'text', text: JSON.stringify({
companies,
count: companies.length,
hasMore: result.companies.pageInfo.hasNextPage,
}, null, 2) }],
};
}
error(message) {
return { content: [{ type: 'text', text: message }] };
}
}
export function getCreateCompanyToolDefinition() {
return createToolDefinition(CREATE_COMPANY_TOOL_NAME, 'Create a new company in Twenty CRM', CreateCompanyInputSchema);
}
export function getGetCompanyToolDefinition() {
return createToolDefinition(GET_COMPANY_TOOL_NAME, 'Get a company by ID from Twenty CRM', GetCompanyInputSchema);
}
export function getListCompaniesToolDefinition() {
return createToolDefinition(LIST_COMPANIES_TOOL_NAME, 'List companies from Twenty CRM', ListCompaniesInputSchema);
}
export function createCompanyTool(graphqlClient, restClient, logger) {
return new CompanyTool(graphqlClient, restClient, logger);
}