/**
* Company Tool
*
* Application layer tool for managing Company entities in Twenty CRM.
*/
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { ToolDefinition, ToolResult } from '../../domain/types.js';
import { Company } from '../../domain/twenty-types.js';
import { ITwentyGraphQLClient } from '../../infrastructure/clients/twenty-graphql-client.js';
import { ITwentyRESTClient } from '../../infrastructure/clients/twenty-rest-client.js';
import { ILogger } from '../../infrastructure/logging/logger.js';
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)'),
});
export type CreateCompanyInput = z.infer<typeof CreateCompanyInputSchema>;
export type GetCompanyInput = z.infer<typeof GetCompanyInputSchema>;
export type ListCompaniesInput = z.infer<typeof ListCompaniesInputSchema>;
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: string, description: string, schema: z.ZodObject<any>): ToolDefinition {
const jsonSchema = zodToJsonSchema(schema, { name: `${name}Input`, $refStrategy: 'none' });
const actualSchema = (jsonSchema as any).definitions?.[`${name}Input`] || jsonSchema;
return { name, description, inputSchema: actualSchema as ToolDefinition['inputSchema'] };
}
export class CompanyTool {
constructor(
private readonly graphqlClient: ITwentyGraphQLClient,
private readonly restClient: ITwentyRESTClient,
private readonly logger: ILogger
) {}
async createCompany(args: unknown): Promise<ToolResult> {
const parseResult = CreateCompanyInputSchema.safeParse(args);
if (!parseResult.success) {
return this.error('Invalid input: ' + parseResult.error.message);
}
const result = await this.graphqlClient.mutate<{ createCompany: Company }>(
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: unknown): Promise<ToolResult> {
const parseResult = GetCompanyInputSchema.safeParse(args);
if (!parseResult.success) {
return this.error('Invalid input: ' + parseResult.error.message);
}
const result = await this.graphqlClient.query<{ company: { edges: Array<{ node: Company }> } }>(
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: unknown): Promise<ToolResult> {
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<{
companies: {
edges: Array<{ node: any }>;
pageInfo: { hasNextPage: boolean; endCursor: string };
};
}>(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) }],
};
}
private error(message: string): ToolResult {
return { content: [{ type: 'text', text: message }] };
}
}
export function getCreateCompanyToolDefinition(): ToolDefinition {
return createToolDefinition(CREATE_COMPANY_TOOL_NAME, 'Create a new company in Twenty CRM', CreateCompanyInputSchema);
}
export function getGetCompanyToolDefinition(): ToolDefinition {
return createToolDefinition(GET_COMPANY_TOOL_NAME, 'Get a company by ID from Twenty CRM', GetCompanyInputSchema);
}
export function getListCompaniesToolDefinition(): ToolDefinition {
return createToolDefinition(LIST_COMPANIES_TOOL_NAME, 'List companies from Twenty CRM', ListCompaniesInputSchema);
}
export function createCompanyTool(
graphqlClient: ITwentyGraphQLClient,
restClient: ITwentyRESTClient,
logger: ILogger
): CompanyTool {
return new CompanyTool(graphqlClient, restClient, logger);
}