Skip to main content
Glama
resources.ts7.65 kB
/** * Handlers for resource-related requests */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourcesResult, ReadResourceResult, } from '@modelcontextprotocol/sdk/types.js'; import { ServerContext } from '../server/createServer.js'; import { setGlobalContext } from '../api/lazy-client.js'; import { createErrorResult } from '../utils/error-handler.js'; import { listCompanies, getCompanyDetails, } from '../objects/companies/index.js'; import { listPeople, getPersonDetails } from '../objects/people/index.js'; import { getLists, getListDetails } from '../objects/lists.js'; import { parseResourceUri, formatResourceUri } from '../utils/uri-parser.js'; import { ResourceType, AttioRecord, AttioList } from '../types/attio.js'; /** * Type for API errors with response data */ interface ApiError extends Error { response?: { data?: Record<string, unknown>; }; } /** * Format a single record for resource response * * @param record - The record to format * @param type - The type of resource * @returns Formatted resource object */ function formatRecordAsResource(record: AttioRecord, type: ResourceType) { return { uri: formatResourceUri(type, record.id?.record_id || ''), name: (() => { const nameField = (record.values as Record<string, unknown> | undefined) ?.name as unknown; if ( Array.isArray(nameField) && nameField.length > 0 && nameField[0] && typeof nameField[0] === 'object' && 'value' in (nameField[0] as Record<string, unknown>) ) { const v = (nameField[0] as Record<string, unknown>).value; return typeof v === 'string' ? v : String(v); } return `Unknown ${type.slice(0, -1)}`; })(), mimeType: 'application/json', }; } /** * Format a list for resource response * * @param list - The list to format * @returns Formatted resource object */ function formatListAsResource(list: AttioList) { return { uri: formatResourceUri(ResourceType.LISTS, list.id?.list_id || ''), name: list.name || 'Unknown list', mimeType: 'application/json', }; } /** * Registers resource-related request handlers with the server * * @param server - The MCP server instance */ export function registerResourceHandlers( server: Server, context?: ServerContext ): void { // Set the global context for lazy initialization if provided if (context) { setGlobalContext(context); } // Handler for listing resources (Companies, People, and Lists) server.setRequestHandler( ListResourcesRequestSchema, async (request): Promise<ListResourcesResult> => { try { // Determine resource type (default to companies if not specified) const resourceType = (request.params?.type as ResourceType) || ResourceType.COMPANIES; switch (resourceType) { case ResourceType.PEOPLE: try { const people = await listPeople(); return { resources: people.map((person) => formatRecordAsResource(person, ResourceType.PEOPLE) ), }; } catch { // For resource requests, always return resources array even on error // This allows capability scanning to work without API key return { resources: [], }; } case ResourceType.LISTS: try { const lists = await getLists(); // Ensure lists is always an array const safeList = Array.isArray(lists) ? lists : []; return { resources: safeList.map((list) => formatListAsResource(list)), }; } catch { // For resource requests, always return resources array even on error return { resources: [], }; } case ResourceType.COMPANIES: default: try { const companies = await listCompanies(); return { resources: companies.map((company) => formatRecordAsResource(company, ResourceType.COMPANIES) ), }; } catch { // For resource requests, always return resources array even on error // This allows capability scanning to work without API key return { resources: [], }; } } } catch (error: unknown) { return createErrorResult( error instanceof Error ? error : new Error('Unknown error'), 'unknown', 'unknown', {} ) as ListResourcesResult; } } ); // Handler for reading resource details (Companies, People, and Lists) server.setRequestHandler( ReadResourceRequestSchema, async (request): Promise<ReadResourceResult> => { try { const uri = request.params.uri; const [resourceType, id] = parseResourceUri(uri); switch (resourceType) { case ResourceType.PEOPLE: try { const person = await getPersonDetails(id); return { contents: [ { uri, text: JSON.stringify(person, null, 2), mimeType: 'application/json', }, ], }; } catch (error: unknown) { return createErrorResult( error instanceof Error ? error : new Error('Unknown error'), `/objects/people/${id}`, 'GET', (error as ApiError).response?.data || {} ) as ReadResourceResult; } case ResourceType.LISTS: try { const list = await getListDetails(id); return { contents: [ { uri, text: JSON.stringify(list, null, 2), mimeType: 'application/json', }, ], }; } catch (error: unknown) { return createErrorResult( error instanceof Error ? error : new Error('Unknown error'), `/lists/${id}`, 'GET', (error as ApiError).response?.data || {} ) as ReadResourceResult; } case ResourceType.COMPANIES: try { const company = await getCompanyDetails(id); return { contents: [ { uri, text: JSON.stringify(company, null, 2), mimeType: 'application/json', }, ], }; } catch (error: unknown) { return createErrorResult( error instanceof Error ? error : new Error('Unknown error'), `/objects/companies/${id}`, 'GET', (error as ApiError).response?.data || {} ) as ReadResourceResult; } default: throw new Error(`Unsupported resource type: ${resourceType}`); } } catch (error: unknown) { return createErrorResult( error instanceof Error ? error : new Error('Unknown error'), request.params.uri, 'GET', {} ) as ReadResourceResult; } } ); }

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