/**
* Tool: create_note
* Create a new note attached to a record in Attio CRM
*/
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';
import { fetchParentRecordNames } from '../utils/fetch-parent-record-name.js';
interface AttioNoteCreateResponse {
data: {
id: {
workspace_id: string;
note_id: string;
};
parent_object: string;
parent_record_id: string;
title: string;
content_plaintext: string;
created_by_actor: {
type: string;
id: string;
};
created_at: string;
};
}
/**
* Tool definition for MCP
*/
export const createNoteTool: Tool = {
name: 'create_note',
description:
'Create a new note attached to a record in Attio CRM. Notes can be attached to people, companies, or any other object type. Supports plaintext and markdown formatting.',
inputSchema: {
type: 'object',
properties: {
parent_object: {
type: 'string',
description:
'The object type to attach the note to (e.g., "people", "companies")',
},
parent_record_id: {
type: 'string',
description: 'The record ID to attach the note to',
},
title: {
type: 'string',
description: 'The note title (plaintext only, no formatting)',
},
content: {
type: 'string',
description:
'The note content. Formatting depends on the format parameter.',
},
format: {
type: 'string',
description:
'Content format: "plaintext" (default) uses \\n for newlines, "markdown" supports headers (#, ##, ###), lists (-, *, 1.), bold (**), italic (*), strikethrough (~~), highlight (==), and links ([text](url))',
enum: ['plaintext', 'markdown'],
default: 'plaintext',
},
},
required: ['parent_object', 'parent_record_id', 'title', 'content'],
},
};
/**
* Handler function for create_note tool
*/
export async function handleCreateNote(args: {
parent_object: string;
parent_record_id: string;
title: string;
content: string;
format?: string;
}): Promise<CallToolResult> {
try {
const apiKey = process.env['ATTIO_API_KEY'];
if (!apiKey) {
throw new ConfigurationError('ATTIO_API_KEY not configured');
}
const {
parent_object,
parent_record_id,
title,
content,
format = 'plaintext',
} = args;
// Validate required fields
if (!parent_object || parent_object.trim().length === 0) {
throw new ValidationError(
'parent_object parameter is required and cannot be empty',
'parent_object'
);
}
if (!parent_record_id || parent_record_id.trim().length === 0) {
throw new ValidationError(
'parent_record_id parameter is required and cannot be empty',
'parent_record_id'
);
}
if (!title || title.trim().length === 0) {
throw new ValidationError(
'title parameter is required and cannot be empty',
'title'
);
}
if (!content || content.trim().length === 0) {
throw new ValidationError(
'content parameter is required and cannot be empty',
'content'
);
}
// Validate format
const validFormats = ['plaintext', 'markdown'];
const normalizedFormat = format.toLowerCase().trim();
if (!validFormats.includes(normalizedFormat)) {
throw new ValidationError(
`Invalid format. Must be one of: ${validFormats.join(', ')}`,
'format'
);
}
const client = createAttioClient(apiKey);
// Create note via Attio API
const response = await client.post<AttioNoteCreateResponse>('/notes', {
data: {
parent_object: parent_object.trim(),
parent_record_id: parent_record_id.trim(),
title: title.trim(),
content: content,
format: normalizedFormat as 'plaintext' | 'markdown',
},
});
const note = response.data;
// Fetch parent record name
const parentNameMap = await fetchParentRecordNames(client, [
{
parent_object: note.parent_object,
parent_record_id: note.parent_record_id,
},
]);
const nameKey = `${note.parent_object}:${note.parent_record_id}`;
// Transform to clean format
const result = {
note_id: note.id.note_id,
workspace_id: note.id.workspace_id,
parent_object: note.parent_object,
parent_record_id: note.parent_record_id,
parent_record_name: parentNameMap.get(nameKey) || null,
title: note.title,
content: note.content_plaintext,
format: normalizedFormat,
created_by: {
type: note.created_by_actor.type,
id: note.created_by_actor.id,
},
created_at: note.created_at,
};
return createSuccessResponse(result);
} catch (error) {
return handleToolError(error, 'create_note');
}
}