/**
* Tool: manage_company_domains
* Unified tool to manage domains on companies (set, add, remove, clear)
*/
import type { Tool, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { createAttioClient } from '../attio-client.js';
import {
handleToolError,
createSuccessResponse,
} from '../utils/error-handler.js';
import { ConfigurationError, ValidationError } from '../utils/errors.js';
interface AttioCompanyRecord {
id: {
workspace_id: string;
object_id: string;
record_id: string;
};
values: {
domains?: Array<{ domain: string; root_domain?: string }>;
[key: string]: unknown;
};
}
interface AttioCompanyResponse {
data: AttioCompanyRecord;
}
/**
* Tool definition for MCP
*/
export const manageCompanyDomainsTool: Tool = {
name: 'manage_company_domains',
description:
'Manage domains on a company. Supports four operations: "set" (replace all domains), "add" (append new domains), "remove" (delete specific domains), and "clear" (remove all domains).',
inputSchema: {
type: 'object',
properties: {
record_id: {
type: 'string',
description:
'The unique record ID of the company (e.g., from search_companies or get_company)',
},
operation: {
type: 'string',
description:
'Operation to perform: "set" (replace all domains), "add" (append new domains), "remove" (delete specific domains), "clear" (remove all domains)',
enum: ['set', 'add', 'remove', 'clear'],
},
domains: {
type: 'array',
items: {
type: 'string',
},
description:
'Array of domain names to set, add, or remove. Not required for "clear" operation.',
},
},
required: ['record_id', 'operation'],
},
};
/**
* Handler function for manage_company_domains tool
*/
export async function handleManageCompanyDomains(args: {
record_id: string;
operation: string;
domains?: string[];
}): Promise<CallToolResult> {
try {
const apiKey = process.env['ATTIO_API_KEY'];
if (!apiKey) {
throw new ConfigurationError('ATTIO_API_KEY not configured');
}
const { record_id, operation, domains } = args;
// Validate required parameters
if (!record_id || record_id.trim().length === 0) {
throw new ValidationError(
'record_id parameter is required and cannot be empty',
'record_id'
);
}
if (!operation || operation.trim().length === 0) {
throw new ValidationError(
'operation parameter is required and cannot be empty',
'operation'
);
}
// Validate operation
const validOperations = ['set', 'add', 'remove', 'clear'];
if (!validOperations.includes(operation)) {
throw new ValidationError(
`operation must be one of: ${validOperations.join(', ')}`,
'operation'
);
}
// Validate domains parameter based on operation
if (operation !== 'clear') {
if (!domains || domains.length === 0) {
throw new ValidationError(
`domains parameter is required for "${operation}" operation`,
'domains'
);
}
}
const client = createAttioClient(apiKey);
// Handle clear operation (set domains to empty array)
if (operation === 'clear') {
// First get current domains for the response
const getResponse = await client.get<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`
);
const previousDomains =
getResponse.data.values.domains?.map((d) => d.domain).filter(Boolean) ||
[];
await client.put<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`,
{
data: {
values: {
domains: [],
},
},
}
);
return createSuccessResponse({
record_id,
operation: 'clear',
previous_domains: previousDomains,
current_domains: [],
message: 'All domains cleared successfully',
});
}
// For all other operations, we need the domains parameter
const validDomains = (domains || [])
.filter((d) => d && d.trim().length > 0)
.map((d) => d.trim().toLowerCase());
if (validDomains.length === 0) {
throw new ValidationError(
'At least one valid domain is required',
'domains'
);
}
// Handle set operation (replace all domains)
if (operation === 'set') {
// First get current domains for the response
const getResponse = await client.get<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`
);
const previousDomains =
getResponse.data.values.domains?.map((d) => d.domain).filter(Boolean) ||
[];
const response = await client.put<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`,
{
data: {
values: {
domains: validDomains.map((domain) => ({ domain })),
},
},
}
);
const currentDomains =
response.data.values.domains?.map((d) => d.domain).filter(Boolean) ||
[];
return createSuccessResponse({
record_id,
operation: 'set',
domains_set: validDomains,
previous_domains: previousDomains,
current_domains: currentDomains,
message: `Domains set to: ${validDomains.join(', ')}`,
});
}
// For add and remove operations, we need to get current domains first
const getResponse = await client.get<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`
);
const currentDomains =
getResponse.data.values.domains?.map((d) => d.domain).filter(Boolean) ||
[];
// Handle add operation (append to existing domains)
if (operation === 'add') {
// Filter out domains that already exist (case-insensitive)
const currentDomainsLower = currentDomains.map((d) => d.toLowerCase());
const domainsToAdd = validDomains.filter(
(domain) => !currentDomainsLower.includes(domain.toLowerCase())
);
if (domainsToAdd.length === 0) {
return createSuccessResponse({
record_id,
operation: 'add',
domains_requested: validDomains,
domains_added: [],
current_domains: currentDomains,
message: 'All requested domains already exist',
});
}
// PATCH to append new domains
const response = await client.patch<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`,
{
data: {
values: {
domains: domainsToAdd.map((domain) => ({ domain })),
},
},
}
);
const updatedDomains =
response.data.values.domains?.map((d) => d.domain).filter(Boolean) ||
[];
return createSuccessResponse({
record_id,
operation: 'add',
domains_requested: validDomains,
domains_added: domainsToAdd,
previous_domains: currentDomains,
current_domains: updatedDomains,
message: `Added ${domainsToAdd.length} domain(s): ${domainsToAdd.join(', ')}`,
});
}
// Handle remove operation (delete specific domains)
if (operation === 'remove') {
// Filter to domains that actually exist (case-insensitive)
const currentDomainsLower = currentDomains.map((d) => d.toLowerCase());
const domainsToRemove = validDomains.filter((domain) =>
currentDomainsLower.includes(domain.toLowerCase())
);
if (domainsToRemove.length === 0) {
return createSuccessResponse({
record_id,
operation: 'remove',
domains_requested: validDomains,
domains_removed: [],
current_domains: currentDomains,
message: 'None of the requested domains exist',
});
}
// Calculate remaining domains (case-insensitive comparison)
const domainsToRemoveLower = domainsToRemove.map((d) => d.toLowerCase());
const remainingDomains = currentDomains.filter(
(domain) => !domainsToRemoveLower.includes(domain.toLowerCase())
);
// PUT to replace with remaining domains
const response = await client.put<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`,
{
data: {
values: {
domains: remainingDomains.map((domain) => ({ domain })),
},
},
}
);
const updatedDomains =
response.data.values.domains?.map((d) => d.domain).filter(Boolean) ||
[];
return createSuccessResponse({
record_id,
operation: 'remove',
domains_requested: validDomains,
domains_removed: domainsToRemove,
previous_domains: currentDomains,
current_domains: updatedDomains,
message: `Removed ${domainsToRemove.length} domain(s): ${domainsToRemove.join(', ')}`,
});
}
// Should never reach here
throw new ValidationError('Invalid operation', 'operation');
} catch (error) {
return handleToolError(error, 'manage_company_domains');
}
}