Skip to main content
Glama

monica_manage_media

Manage documents and photos in Monica CRM by listing, viewing, uploading, or deleting media files for contacts.

Instructions

List, inspect, upload, or delete documents and photos stored in Monica. Use mediaType to pick the resource.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
mediaTypeYes
actionYes
mediaIdNo
contactIdNo
limitNo
pageNo
filePathNo
base64DataNo
fileNameNo
mimeTypeNo

Implementation Reference

  • Registers the 'monica_manage_media' tool with server.registerTool, providing title, description, input schema, and a dispatching handler that routes to document or photo handlers based on mediaType.
    export function registerMediaTools(context: ToolRegistrationContext): void { const { server, client, logger } = context; server.registerTool( 'monica_manage_media', { title: 'Manage Monica documents and photos', description: 'List, inspect, upload, or delete documents and photos stored in Monica. Use mediaType to pick the resource.', inputSchema: mediaInputShape }, async (rawInput: unknown) => { const input = mediaInputSchema.parse(rawInput); if (input.mediaType === 'document') { return handleDocument({ input: input as DocumentInput, client, logger }); } return handlePhoto({ input: input as PhotoInput, client, logger }); } ); }
  • Defines the input schema using Zod (mediaInputShape and mediaInputSchema) with validation rules for mediaType, action, and other parameters specific to Monica media management.
    const mediaInputShape = { mediaType: z.enum(['document', 'photo']), action: z.enum(['list', 'get', 'upload', 'delete']), mediaId: z.number().int().positive().optional(), contactId: z.number().int().positive().optional(), limit: z.number().int().min(1).max(100).optional(), page: z.number().int().min(1).optional(), filePath: z.string().min(1).optional(), base64Data: z.string().min(1).optional(), fileName: z.string().min(1).optional(), mimeType: z.string().min(1).optional() } as const; const mediaInputSchema = z .object(mediaInputShape) .superRefine((input, ctx) => { if (['get', 'delete'].includes(input.action) && input.mediaId == null) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'mediaId is required for get and delete actions.', path: ['mediaId'] }); } if (input.action === 'upload') { if (!input.filePath && !input.base64Data) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Provide either filePath or base64Data when uploading media.', path: ['filePath'] }); } if (input.filePath && input.base64Data) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Choose either filePath or base64Data, not both.', path: ['filePath'] }); } if (!input.filePath && !input.fileName) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'fileName is required when providing base64Data directly.', path: ['fileName'] }); } } }); type MediaInput = z.infer<typeof mediaInputSchema>; type DocumentInput = MediaInput & { mediaType: 'document' };
  • Handler for document operations (list, get, upload, delete) using Monica client API, with response building and logging.
    async function handleDocument({ input, client, logger }: HandlerArgs<DocumentInput>) { switch (input.action) { case 'list': { const response = await client.listDocuments({ contactId: input.contactId, limit: input.limit, page: input.page }); const documents = response.data.map(normalizeDocument); return buildListResponse({ mediaType: input.mediaType, action: input.action, contactId: input.contactId, records: documents, pagination: response.meta }); } case 'get': { if (input.mediaId == null) { return missingMediaIdError('document', input.action); } const response = await client.getDocument(input.mediaId); const document = normalizeDocument(response.data); return buildSingleRecordResponse({ mediaType: input.mediaType, action: input.action, mediaId: input.mediaId, record: document, text: `Document ${document.originalFilename} (ID ${document.id}).` }); } case 'upload': { const upload = await prepareUploadPayload(input); const response = await client.uploadDocument({ base64Data: upload.base64Data, fileName: upload.fileName, mimeType: upload.mimeType, contactId: input.contactId }); const document = normalizeDocument(response.data); logger.info({ documentId: document.id, contactId: input.contactId }, 'Uploaded Monica document'); return buildSingleRecordResponse({ mediaType: input.mediaType, action: input.action, record: document, text: `Uploaded document ${document.originalFilename} (ID ${document.id}).` }); } case 'delete': { if (input.mediaId == null) { return missingMediaIdError('document', input.action); } await client.deleteDocument(input.mediaId); logger.info({ documentId: input.mediaId }, 'Deleted Monica document'); return { content: [ { type: 'text' as const, text: `Deleted document ID ${input.mediaId}.` } ], structuredContent: { mediaType: input.mediaType, action: input.action, mediaId: input.mediaId, deleted: true } }; } default: return unknownActionError(input.action); } }
  • Handler for photo operations (list, get, upload, delete) using Monica client API, with response building and logging.
    async function handlePhoto({ input, client, logger }: HandlerArgs<PhotoInput>) { switch (input.action) { case 'list': { const response = await client.listPhotos({ contactId: input.contactId, limit: input.limit, page: input.page }); const photos = response.data.map(normalizePhoto); return buildListResponse({ mediaType: input.mediaType, action: input.action, contactId: input.contactId, records: photos, pagination: response.meta }); } case 'get': { if (input.mediaId == null) { return missingMediaIdError('photo', input.action); } const response = await client.getPhoto(input.mediaId); const photo = normalizePhoto(response.data); return buildSingleRecordResponse({ mediaType: input.mediaType, action: input.action, mediaId: input.mediaId, record: photo, text: `Photo ${photo.originalFilename} (ID ${photo.id}).` }); } case 'upload': { const upload = await prepareUploadPayload(input); const response = await client.uploadPhoto({ base64Data: upload.base64Data, fileName: upload.fileName, mimeType: upload.mimeType, contactId: input.contactId }); const photo = normalizePhoto(response.data); logger.info({ photoId: photo.id, contactId: input.contactId }, 'Uploaded Monica photo'); return buildSingleRecordResponse({ mediaType: input.mediaType, action: input.action, record: photo, text: `Uploaded photo ${photo.originalFilename} (ID ${photo.id}).` }); } case 'delete': { if (input.mediaId == null) { return missingMediaIdError('photo', input.action); } await client.deletePhoto(input.mediaId); logger.info({ photoId: input.mediaId }, 'Deleted Monica photo'); return { content: [ { type: 'text' as const, text: `Deleted photo ID ${input.mediaId}.` } ], structuredContent: { mediaType: input.mediaType, action: input.action, mediaId: input.mediaId, deleted: true } }; } default: return unknownActionError(input.action); } }
  • Helper function to prepare upload payload by reading file or using base64, resolving MIME types.
    async function prepareUploadPayload(input: MediaInput) { if (!input.filePath && !input.base64Data) { throw new Error('Provide either filePath or base64Data for uploads.'); } if (input.filePath) { const resolvedPath = resolveFilePath(input.filePath); try { const fileBuffer = await readFile(resolvedPath); const fileName = input.fileName ? basename(input.fileName) : basename(resolvedPath); const mimeType = resolveMimeType(fileName, input.mimeType); return { base64Data: fileBuffer.toString('base64'), fileName, mimeType }; } catch (error) { throw new Error(`Unable to read file at ${resolvedPath}: ${(error as Error).message}`); } } if (!input.base64Data || !input.fileName) { throw new Error('Provide base64Data and fileName when not using filePath.'); } const fileName = basename(input.fileName); return { base64Data: input.base64Data, fileName, mimeType: resolveMimeType(fileName, input.mimeType) }; }

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/Jacob-Stokes/monica-mcp'

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