Skip to main content
Glama
notes.tsβ€’10.4 kB
/** * Note operations for companies */ import { getLazyAttioClient } from '../../api/lazy-client.js'; import { getObjectNotes, createObjectNote, } from '../../api/operations/index.js'; import { ResourceType, AttioNote } from '../../types/attio.js'; interface HttpErrorLike { response?: { status?: number; }; message?: string; } function getStatus(error: unknown): number | undefined { if (typeof error !== 'object' || error === null) { return undefined; } const candidate = error as HttpErrorLike; const status = candidate.response?.status; return typeof status === 'number' ? status : undefined; } function getMessage(error: unknown): string | undefined { if (error instanceof Error) { return error.message; } if (typeof error === 'object' && error !== null) { return (error as HttpErrorLike).message; } if (typeof error === 'string') { return error; } return undefined; } /** * Gets notes for a specific company * * @param companyIdOrUri - The ID of the company or its URI (attio://companies/{id}) * @param limit - Maximum number of notes to fetch (default: 10) * @param offset - Number of notes to skip (default: 0) * @returns Array of notes */ export async function getCompanyNotes( companyIdOrUri: string, limit: number = 10, offset: number = 0 ): Promise<AttioNote[]> { let companyId: string; try { // Determine if the input is a URI or a direct ID const isUri = companyIdOrUri.startsWith('attio://'); if (isUri) { try { // Try to parse the URI formally const [resourceType, id] = companyIdOrUri.match(/^attio:\/\/([^/]+)\/(.+)$/)?.slice(1) || []; if (resourceType !== ResourceType.COMPANIES) { throw new Error( `Invalid resource type in URI: Expected 'companies', got '${resourceType}'` ); } companyId = id; } catch { // Fallback to simple string splitting if formal parsing fails const parts = companyIdOrUri.split('/'); companyId = parts[parts.length - 1]; } if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'getCompanyNotes').debug( 'Extracted company ID from URI', { companyId, companyIdOrUri } ); } } else { // Direct ID was provided companyId = companyIdOrUri; if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'getCompanyNotes').debug( 'Using direct company ID', { companyId } ); } } // Validate that we have a non-empty ID if (!companyId || companyId.trim() === '') { throw new Error(`Invalid company ID: ${companyIdOrUri}`); } // Use the unified operation if available, with fallback to direct implementation try { return await getObjectNotes( ResourceType.COMPANIES, companyId, limit, offset ); } catch (error: unknown) { if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'getCompanyNotes').error( 'Unified operation failed', error instanceof Error ? error : new Error(String(error)) ); } // Fallback implementation with better error handling try { const api = getLazyAttioClient(); const path = `/notes?limit=${limit}&offset=${offset}&parent_object=companies&parent_record_id=${companyId}`; if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'getCompanyNotes').warn( 'Trying direct API call', { path } ); } const response = await api.get(path); const notes = Array.isArray(response?.data?.data) ? (response.data.data as AttioNote[]) : []; return notes; } catch (directError: unknown) { if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'getCompanyNotes').error( 'All attempts failed', new Error('All attempts failed'), { companyId, originalUri: companyIdOrUri, errors: { unified: getMessage(error) || 'Unknown error', direct: getMessage(directError) || 'Unknown error', }, } ); } // Return empty array instead of throwing error when no notes are found if (getStatus(directError) === 404) { return []; } throw new Error( `Could not retrieve notes for company ${companyIdOrUri}: ${ getMessage(directError) || 'Unknown error' }` ); } } } catch (error: unknown) { // Catch any errors in the URI parsing logic if (error instanceof Error && error.message.includes('match')) { throw new Error( `Cannot parse company identifier: ${companyIdOrUri}. Use either a direct ID or URI format 'attio://companies/{id}'` ); } throw error; } } /** * Creates a note for a specific company * * @param companyIdOrUri - The ID of the company or its URI (attio://companies/{id}) * @param title - The title of the note (will be prefixed with "[AI]") * @param content - The text content of the note * @returns The created note object * @throws Error if company ID cannot be parsed or note creation fails * @example * ```typescript * const note = await createCompanyNote("comp_123", "Meeting Notes", * "Discussed Q4 strategy with the team..."); * ``` */ export async function createCompanyNote( companyIdOrUri: string, title: string, content: string ): Promise<AttioNote> { let companyId: string; try { // Determine if the input is a URI or a direct ID const isUri = companyIdOrUri.startsWith('attio://'); if (isUri) { try { // Try to parse the URI formally const [resourceType, id] = companyIdOrUri.match(/^attio:\/\/([^/]+)\/(.+)$/)?.slice(1) || []; if (resourceType !== ResourceType.COMPANIES) { throw new Error( `Invalid resource type in URI: Expected 'companies', got '${resourceType}'` ); } companyId = id; } catch { // Fallback to simple string splitting if formal parsing fails const parts = companyIdOrUri.split('/'); companyId = parts[parts.length - 1]; } if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'createCompanyNote').debug( 'Extracted company ID from URI', { companyId, companyIdOrUri } ); } } else { // Direct ID was provided companyId = companyIdOrUri; if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'createCompanyNote').debug( 'Using direct company ID', { companyId } ); } } // Validate that we have a non-empty ID if (!companyId || companyId.trim() === '') { throw new Error(`Invalid company ID: ${companyIdOrUri}`); } // Use the unified operation if available, with fallback to direct implementation try { return await createObjectNote( ResourceType.COMPANIES, companyId, title, content ); } catch (error: unknown) { if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'createCompanyNote').error( 'Unified operation failed', error instanceof Error ? error : new Error(String(error)) ); } // Fallback implementation with better error handling try { const api = getLazyAttioClient(); const path = 'notes'; if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'createCompanyNote').warn( 'Trying direct API call', { path } ); } const response = await api.post(path, { data: { format: 'plaintext', parent_object: 'companies', parent_record_id: companyId, title: `[AI] ${title}`, content, }, }); const createdNote = response?.data as AttioNote | undefined; if (!createdNote) { throw new Error('Note creation returned empty response'); } return createdNote; } catch (directError: unknown) { if (process.env.NODE_ENV === 'development') { const { createScopedLogger } = await import('../../utils/logger.js'); createScopedLogger('companies.notes', 'createCompanyNote').error( 'All attempts failed', new Error('All attempts failed'), { companyId, originalUri: companyIdOrUri, errors: { unified: getMessage(error) || 'Unknown error', direct: getMessage(directError) || 'Unknown error', }, } ); } throw new Error( `Could not create note for company ${companyIdOrUri}: ${ getMessage(directError) || 'Unknown error' }` ); } } } catch (error: unknown) { // Catch any errors in the URI parsing logic if (error instanceof Error && error.message.includes('match')) { throw new Error( `Cannot parse company identifier: ${companyIdOrUri}. Use either a direct ID or URI format 'attio://companies/{id}'` ); } throw error; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server