/**
* Tool: get_company
* Get full details of a specific company by record ID
*/
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: {
name?: Array<{ value: string }>;
domains?: Array<{ domain: string }>;
description?: Array<{ value: string }>;
linkedin?: Array<{ value: string }>;
[key: string]: unknown; // Allow for additional fields
};
}
interface AttioCompanyResponse {
data: AttioCompanyRecord;
}
/**
* Tool definition for MCP
*/
export const getCompanyTool: Tool = {
name: 'get_company',
description:
'Get complete details of a company by record_id. Returns all attributes. Set include_all=true to also fetch notes, tasks, comment threads, meetings, and list entries, or use individual flags (include_notes, include_tasks, etc.).',
inputSchema: {
type: 'object',
properties: {
record_id: {
type: 'string',
description:
'The unique record ID of the company (e.g., from search results)',
},
include_notes: {
type: 'boolean',
description: 'Include notes attached to this company',
default: false,
},
include_tasks: {
type: 'boolean',
description: 'Include tasks linked to this company',
default: false,
},
include_threads: {
type: 'boolean',
description: 'Include comment threads on this company',
default: false,
},
include_meetings: {
type: 'boolean',
description: 'Include meetings linked to this company',
default: false,
},
include_list_entries: {
type: 'boolean',
description: 'Include list entries where this company is a parent',
default: false,
},
include_all: {
type: 'boolean',
description:
'Include all related data (notes, tasks, threads, meetings, list entries)',
default: false,
},
},
required: ['record_id'],
},
};
/**
* Handler function for get_company tool
*/
export async function handleGetCompany(args: {
record_id: string;
include_notes?: boolean;
include_tasks?: boolean;
include_threads?: boolean;
include_meetings?: boolean;
include_list_entries?: boolean;
include_all?: boolean;
}): Promise<CallToolResult> {
try {
const apiKey = process.env['ATTIO_API_KEY'];
if (!apiKey) {
throw new ConfigurationError('ATTIO_API_KEY not configured');
}
const {
record_id,
include_notes = false,
include_tasks = false,
include_threads = false,
include_meetings = false,
include_list_entries = false,
include_all = false,
} = args;
if (!record_id || record_id.trim().length === 0) {
throw new ValidationError(
'record_id parameter is required and cannot be empty',
'record_id'
);
}
const client = createAttioClient(apiKey);
// Determine what to fetch
const shouldFetchNotes = include_all || include_notes;
const shouldFetchTasks = include_all || include_tasks;
const shouldFetchThreads = include_all || include_threads;
const shouldFetchMeetings = include_all || include_meetings;
const shouldFetchListEntries = include_all || include_list_entries;
// Get company by record ID
const response = await client.get<AttioCompanyResponse>(
`/objects/companies/records/${record_id}`
);
const company = response.data;
// Transform to clean format, preserving all fields
const result: Record<string, unknown> = {
record_id: company.id.record_id,
workspace_id: company.id.workspace_id,
object_id: company.id.object_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,
all_attributes: company.values, // Include all raw attributes
};
// Fetch notes if requested
if (shouldFetchNotes) {
try {
const notesResponse = await client.get<{
data: Array<{
id: { note_id: string };
title: string;
content_plaintext: string;
content_markdown: string;
created_at: string;
created_by_actor: unknown;
}>;
}>(`/notes?parent_object=companies&parent_record_id=${record_id}`);
result['notes'] = (notesResponse.data || []).map((note) => ({
id: note.id.note_id,
title: note.title,
content_plaintext: note.content_plaintext,
content_markdown: note.content_markdown,
created_at: note.created_at,
created_by_actor: note.created_by_actor,
}));
} catch {
result['notes'] = [];
}
}
// Fetch tasks if requested
if (shouldFetchTasks) {
try {
const tasksResponse = await client.get<{
data: Array<{
id: { task_id: string };
content_plaintext: string;
deadline_at: string | null;
is_completed: boolean;
assignees: unknown[];
linked_records: unknown[];
created_at: string;
}>;
}>(`/tasks?linked_object=companies&linked_record_id=${record_id}`);
result['tasks'] = (tasksResponse.data || []).map((task) => ({
id: task.id.task_id,
content_plaintext: task.content_plaintext,
deadline_at: task.deadline_at,
is_completed: task.is_completed,
assignees: task.assignees,
linked_records: task.linked_records,
created_at: task.created_at,
}));
} catch {
result['tasks'] = [];
}
}
// Fetch comment threads if requested
if (shouldFetchThreads) {
try {
const threadsResponse = await client.get<{
data: Array<{
id: { thread_id: string };
comments: Array<{
id: { comment_id: string };
content_plaintext: string;
author: unknown;
created_at: string;
}>;
created_at: string;
}>;
}>(`/threads?record_id=${record_id}&object=companies`);
result['comment_threads'] = (threadsResponse.data || []).map(
(thread) => ({
id: thread.id.thread_id,
comments: thread.comments.map((comment) => ({
id: comment.id.comment_id,
content_plaintext: comment.content_plaintext,
author: comment.author,
created_at: comment.created_at,
})),
created_at: thread.created_at,
})
);
} catch {
result['comment_threads'] = [];
}
}
// Fetch meetings if requested
if (shouldFetchMeetings) {
try {
const meetingsResponse = await client.get<{
data: Array<{
id: { meeting_id: string };
title: string;
description: string;
start: unknown;
end: unknown;
participants: unknown[];
linked_records: unknown[];
created_at: string;
}>;
}>(`/meetings?linked_object=companies&linked_record_id=${record_id}`);
result['meetings'] = (meetingsResponse.data || []).map((meeting) => ({
id: meeting.id.meeting_id,
title: meeting.title,
description: meeting.description,
start: meeting.start,
end: meeting.end,
participants: meeting.participants,
linked_records: meeting.linked_records,
created_at: meeting.created_at,
}));
} catch {
result['meetings'] = [];
}
}
// Fetch list entries if requested
if (shouldFetchListEntries) {
try {
const entriesResponse = await client.get<{
data: Array<{
id: { list_id: string; entry_id: string };
created_at: string;
entry_values: unknown;
}>;
}>(`/objects/companies/records/${record_id}/entries`);
result['list_entries'] = (entriesResponse.data || []).map((entry) => ({
id: entry.id.entry_id,
list_id: entry.id.list_id,
entry_id: entry.id.entry_id,
created_at: entry.created_at,
entry_values: entry.entry_values,
}));
} catch {
result['list_entries'] = [];
}
}
return createSuccessResponse(result);
} catch (error) {
return handleToolError(error, 'get_company');
}
}