/**
* Tool: search_companies
* Search for companies with sophisticated filtering
*/
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 } from '../utils/errors.js';
interface AttioCompanyRecord {
id: {
workspace_id: string;
object_id: string;
record_id: string;
};
created_at?: string;
values: {
name?: Array<{ value: string }>;
domains?: Array<{ domain: string }>;
description?: Array<{ value: string }>;
linkedin?: Array<{ value: string }>;
};
}
interface AttioCompaniesResponse {
data: AttioCompanyRecord[];
}
/**
* Tool definition for MCP
*/
export const searchCompaniesTool: Tool = {
name: 'search_companies',
description:
'Search for companies in Attio CRM. Supports filtering by text (name or domain) and date ranges. Supports sorting by various fields. All filters are optional. Returns company details including name, domains, description, LinkedIn URL, web_url, and created_at.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description:
'Text search in company name or domains (e.g., "acme", "acme.com", "tech"). Case-insensitive partial matching.',
},
created_after: {
type: 'string',
description:
'Filter by creation date - only records created after this date (ISO 8601 format, e.g., "2024-01-01" or "2024-01-01T00:00:00Z").',
},
created_before: {
type: 'string',
description:
'Filter by creation date - only records created before this date (ISO 8601 format, e.g., "2024-12-31" or "2024-12-31T23:59:59Z").',
},
sort_by: {
type: 'string',
description: 'Field to sort results by. Options: "created_at", "name".',
enum: ['created_at', 'name'],
},
sort_direction: {
type: 'string',
description:
'Sort direction. Options: "asc" (ascending), "desc" (descending). Default: "desc".',
enum: ['asc', 'desc'],
default: 'desc',
},
limit: {
type: 'number',
description:
'Maximum number of results to return (default: 50, max: 500)',
default: 50,
minimum: 1,
maximum: 500,
},
},
required: [],
},
};
/**
* Handler function for search_companies tool
*/
export async function handleSearchCompanies(args: {
query?: string;
created_after?: string;
created_before?: string;
sort_by?: string;
sort_direction?: string;
limit?: number;
}): Promise<CallToolResult> {
try {
const apiKey = process.env['ATTIO_API_KEY'];
if (!apiKey) {
throw new ConfigurationError('ATTIO_API_KEY not configured');
}
const {
query,
created_after,
created_before,
sort_by,
sort_direction = 'desc',
limit = 50,
} = args;
// Validate and cap limit
const validatedLimit = Math.min(Math.max(1, limit), 500);
const client = createAttioClient(apiKey);
// Build filter conditions
const conditions: Array<Record<string, unknown>> = [];
// Text search in name or domain
if (query && query.trim().length > 0) {
conditions.push({
$or: [
{ name: { value: { $contains: query.trim() } } },
{ domains: { domain: { $contains: query.trim() } } },
],
});
}
// Date range filters
if (created_after && created_after.trim().length > 0) {
conditions.push({
created_at: { $gte: created_after.trim() },
});
}
if (created_before && created_before.trim().length > 0) {
conditions.push({
created_at: { $lte: created_before.trim() },
});
}
// Build final filter (omit filter key if no conditions to get all records)
const filter =
conditions.length === 0
? undefined
: conditions.length === 1
? conditions[0]
: { $and: conditions };
// Query Attio API
const requestBody: {
limit: number;
filter?: Record<string, unknown>;
sorts?: Array<{ attribute: string; direction: string }>;
} = {
limit: validatedLimit,
};
// Only include filter if we have conditions
if (filter) {
requestBody.filter = filter;
}
// Add sorting if specified
if (sort_by && sort_by.trim().length > 0) {
requestBody.sorts = [
{
attribute: sort_by.trim(),
direction: sort_direction === 'asc' ? 'asc' : 'desc',
},
];
}
const response = await client.post<AttioCompaniesResponse>(
'/objects/companies/records/query',
requestBody
);
const companies = response.data || [];
// Transform to clean format
const results = companies.map((company) => ({
record_id: company.id.record_id,
web_url: `https://app.attio.com/${process.env['ATTIO_WORKSPACE_SLUG'] || 'workspace'}/companies/${company.id.record_id}`,
name: company.values.name?.[0]?.value || null,
domains:
company.values.domains?.map((d) => d.domain).filter(Boolean) || [],
description: company.values.description?.[0]?.value || null,
linkedin: company.values.linkedin?.[0]?.value || null,
created_at: company.created_at || null,
}));
const result = {
filters: {
query: query || null,
created_after: created_after || null,
created_before: created_before || null,
},
sort: sort_by ? { by: sort_by, direction: sort_direction } : null,
limit: validatedLimit,
count: results.length,
has_more: results.length === validatedLimit,
companies: results,
};
return createSuccessResponse(result);
} catch (error) {
return handleToolError(error, 'search_companies');
}
}