Skip to main content
Glama
activities.ts7.17 kB
import { z } from 'zod'; import type { CreateActivityPayload, UpdateActivityPayload } from '../../client/MonicaClient.js'; import type { ToolRegistrationContext } from '../context.js'; import { normalizeActivity } from '../../utils/formatters.js'; import { resolveActivityTypeId } from '../../utils/resolvers.js'; const activityPayloadSchema = z .object({ activityTypeId: z.number().int().positive().optional(), activityTypeName: z.string().min(1).max(255).optional(), summary: z.string().min(1).max(255), description: z.string().max(1_000_000).optional().nullable(), happenedAt: z .string() .regex(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/u, 'happenedAt must be in YYYY-MM-DD format.'), contactIds: z.array(z.number().int().positive()).min(1, 'Provide at least one contact ID.'), emotionIds: z.array(z.number().int().positive()).optional() }) .superRefine((data, ctx) => { if (typeof data.activityTypeId !== 'number' && !data.activityTypeName) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Provide activityTypeId or activityTypeName.' }); } }); type ActivityPayloadForm = z.infer<typeof activityPayloadSchema>; export function registerActivityTools(context: ToolRegistrationContext): void { const { server, client, logger } = context; server.registerTool( 'monica_manage_activity', { title: 'Manage Monica activities', description: 'List, inspect, create, update, or delete activities (meetings, events, shared interactions). Provide either activityTypeId or activityTypeName.', inputSchema: { action: z.enum(['list', 'get', 'create', 'update', 'delete']), activityId: 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(), payload: activityPayloadSchema.optional() } }, async ({ action, activityId, contactId, limit, page, payload }) => { if (action === 'list') { const response = await client.listActivities({ contactId, limit, page }); const activities = response.data.map(normalizeActivity); const scope = contactId ? `contact ${contactId}` : 'your account'; const summary = activities.length ? `Fetched ${activities.length} activit${activities.length === 1 ? 'y' : 'ies'} for ${scope}.` : `No activities found for ${scope}.`; return { content: [ { type: 'text' as const, text: summary } ], structuredContent: { action, contactId, activities, pagination: { currentPage: response.meta.current_page, lastPage: response.meta.last_page, perPage: response.meta.per_page, total: response.meta.total } } }; } if (action === 'get') { if (!activityId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide activityId when retrieving an activity.' } ] }; } const response = await client.getActivity(activityId); const activity = normalizeActivity(response.data); return { content: [ { type: 'text' as const, text: `Activity ${activity.summary || `#${activity.id}`} (ID ${activity.id}).` } ], structuredContent: { action, activity } }; } if (action === 'create') { if (!payload) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide an activity payload when creating an activity.' } ] }; } const activityTypeId = await resolveActivityTypeId(client, { activityTypeId: payload.activityTypeId, activityTypeName: payload.activityTypeName }); const result = await client.createActivity( toActivityPayloadInput({ ...payload, activityTypeId }) ); const activity = normalizeActivity(result.data); logger.info({ activityId: activity.id }, 'Created Monica activity'); return { content: [ { type: 'text' as const, text: `Created activity ${activity.summary || `#${activity.id}`} (ID ${activity.id}).` } ], structuredContent: { action, activity } }; } if (action === 'update') { if (!activityId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide activityId when updating an activity.' } ] }; } if (!payload) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide an activity payload when updating an activity.' } ] }; } const activityTypeId = await resolveActivityTypeId(client, { activityTypeId: payload.activityTypeId, activityTypeName: payload.activityTypeName }); const result = await client.updateActivity( activityId, toActivityPayloadInput({ ...payload, activityTypeId }) ); const activity = normalizeActivity(result.data); logger.info({ activityId }, 'Updated Monica activity'); return { content: [ { type: 'text' as const, text: `Updated activity ${activity.summary || `#${activity.id}`} (ID ${activity.id}).` } ], structuredContent: { action, activityId, activity } }; } if (!activityId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide activityId when deleting an activity.' } ] }; } const result = await client.deleteActivity(activityId); logger.info({ activityId }, 'Deleted Monica activity'); return { content: [ { type: 'text' as const, text: `Deleted activity ID ${activityId}.` } ], structuredContent: { action, activityId, result } }; } ); } function toActivityPayloadInput( payload: ActivityPayloadForm & { activityTypeId: number } ): CreateActivityPayload & UpdateActivityPayload { return { activityTypeId: payload.activityTypeId, summary: payload.summary, description: payload.description ?? null, happenedAt: payload.happenedAt, contactIds: payload.contactIds, emotionIds: payload.emotionIds && payload.emotionIds.length ? payload.emotionIds : undefined }; }

Implementation Reference

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