monica_manage_relationship
Manage relationships between contacts in Monica CRM by listing, viewing, creating, updating, or deleting connections using relationship types.
Instructions
List, inspect, create, update, or delete relationships between contacts. Provide relationshipTypeId or relationshipTypeName to identify the connection.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | ||
| relationshipId | No | ||
| contactId | No | ||
| limit | No | ||
| page | No | ||
| payload | No |
Implementation Reference
- Main handler function for the 'monica_manage_relationship' tool. Implements switch statement handling actions: list, get, create, update, delete relationships using MonicaClient methods.async ({ action, relationshipId, contactId, limit, page, payload }) => { switch (action) { case 'list': { if (!contactId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide contactId when listing relationships.' } ] }; } const response = await client.listRelationships({ contactId, limit, page }); const relationships = response.data.map(normalizeRelationship); const pagination = response.meta ? { currentPage: response.meta.current_page, lastPage: response.meta.last_page, perPage: response.meta.per_page, total: response.meta.total } : undefined; const text = relationships.length ? `Found ${relationships.length} relationship${relationships.length === 1 ? '' : 's'} for contact ${contactId}.` : `No relationships found for contact ${contactId}.`; const structuredContent: { action: typeof action; contactId: number; relationships: ReturnType<typeof normalizeRelationship>[]; pagination?: { currentPage: number; lastPage: number; perPage: number; total: number; }; } = { action, contactId, relationships }; if (pagination) { structuredContent.pagination = pagination; } return { content: [ { type: 'text' as const, text } ], structuredContent }; } case 'get': { if (!relationshipId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipId when retrieving a relationship.' } ] }; } const response = await client.getRelationship(relationshipId); const relationship = normalizeRelationship(response.data); return { content: [ { type: 'text' as const, text: `Relationship ${relationship.relationshipType.name} between ${relationship.contact.name} and ${relationship.relatedContact.name}.` } ], structuredContent: { action, relationshipId, relationship } }; } case 'create': { if (!payload) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide contactIsId, ofContactId, and relationshipTypeId or relationshipTypeName when creating a relationship.' } ] }; } const relationshipTypeId = await resolveRelationshipTypeId(client, { relationshipTypeId: payload.relationshipTypeId, relationshipTypeName: payload.relationshipTypeName }); const input = toRelationshipCreatePayload({ ...payload, relationshipTypeId }); const response = await client.createRelationship(input); const relationship = normalizeRelationship(response.data); logger.info({ relationshipId: relationship.id }, 'Created Monica relationship'); return { content: [ { type: 'text' as const, text: `Linked ${relationship.contact.name} and ${relationship.relatedContact.name} as ${relationship.relationshipType.name}.` } ], structuredContent: { action, relationship } }; } case 'update': { if (!relationshipId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipId when updating a relationship.' } ] }; } if (!payload) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipTypeId or relationshipTypeName when updating a relationship.' } ] }; } const relationshipTypeId = await resolveRelationshipTypeId(client, { relationshipTypeId: payload.relationshipTypeId, relationshipTypeName: payload.relationshipTypeName }); const input = toRelationshipUpdatePayload({ ...payload, relationshipTypeId }); const response = await client.updateRelationship(relationshipId, input); const relationship = normalizeRelationship(response.data); logger.info({ relationshipId }, 'Updated Monica relationship'); return { content: [ { type: 'text' as const, text: `Updated relationship ${relationshipId} to ${relationship.relationshipType.name}.` } ], structuredContent: { action, relationshipId, relationship } }; } case 'delete': { if (!relationshipId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipId when deleting a relationship.' } ] }; } const result = await client.deleteRelationship(relationshipId); logger.info({ relationshipId }, 'Deleted Monica relationship'); return { content: [ { type: 'text' as const, text: `Deleted relationship ID ${relationshipId}.` } ], structuredContent: { action, relationshipId, result } }; } default: return { isError: true as const, content: [ { type: 'text' as const, text: `Unsupported action: ${action}.` } ] }; } } );
- src/tools/modules/relationships.ts:19-261 (registration)Registration of the 'monica_manage_relationship' tool via server.registerTool call within registerRelationshipTools function.export function registerRelationshipTools(context: ToolRegistrationContext): void { const { server, client, logger } = context; server.registerTool( 'monica_manage_relationship', { title: 'Manage Monica relationships', description: 'List, inspect, create, update, or delete relationships between contacts. Provide relationshipTypeId or relationshipTypeName to identify the connection.', inputSchema: { action: z.enum(['list', 'get', 'create', 'update', 'delete']), relationshipId: 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: relationshipPayloadSchema.optional() } }, async ({ action, relationshipId, contactId, limit, page, payload }) => { switch (action) { case 'list': { if (!contactId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide contactId when listing relationships.' } ] }; } const response = await client.listRelationships({ contactId, limit, page }); const relationships = response.data.map(normalizeRelationship); const pagination = response.meta ? { currentPage: response.meta.current_page, lastPage: response.meta.last_page, perPage: response.meta.per_page, total: response.meta.total } : undefined; const text = relationships.length ? `Found ${relationships.length} relationship${relationships.length === 1 ? '' : 's'} for contact ${contactId}.` : `No relationships found for contact ${contactId}.`; const structuredContent: { action: typeof action; contactId: number; relationships: ReturnType<typeof normalizeRelationship>[]; pagination?: { currentPage: number; lastPage: number; perPage: number; total: number; }; } = { action, contactId, relationships }; if (pagination) { structuredContent.pagination = pagination; } return { content: [ { type: 'text' as const, text } ], structuredContent }; } case 'get': { if (!relationshipId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipId when retrieving a relationship.' } ] }; } const response = await client.getRelationship(relationshipId); const relationship = normalizeRelationship(response.data); return { content: [ { type: 'text' as const, text: `Relationship ${relationship.relationshipType.name} between ${relationship.contact.name} and ${relationship.relatedContact.name}.` } ], structuredContent: { action, relationshipId, relationship } }; } case 'create': { if (!payload) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide contactIsId, ofContactId, and relationshipTypeId or relationshipTypeName when creating a relationship.' } ] }; } const relationshipTypeId = await resolveRelationshipTypeId(client, { relationshipTypeId: payload.relationshipTypeId, relationshipTypeName: payload.relationshipTypeName }); const input = toRelationshipCreatePayload({ ...payload, relationshipTypeId }); const response = await client.createRelationship(input); const relationship = normalizeRelationship(response.data); logger.info({ relationshipId: relationship.id }, 'Created Monica relationship'); return { content: [ { type: 'text' as const, text: `Linked ${relationship.contact.name} and ${relationship.relatedContact.name} as ${relationship.relationshipType.name}.` } ], structuredContent: { action, relationship } }; } case 'update': { if (!relationshipId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipId when updating a relationship.' } ] }; } if (!payload) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipTypeId or relationshipTypeName when updating a relationship.' } ] }; } const relationshipTypeId = await resolveRelationshipTypeId(client, { relationshipTypeId: payload.relationshipTypeId, relationshipTypeName: payload.relationshipTypeName }); const input = toRelationshipUpdatePayload({ ...payload, relationshipTypeId }); const response = await client.updateRelationship(relationshipId, input); const relationship = normalizeRelationship(response.data); logger.info({ relationshipId }, 'Updated Monica relationship'); return { content: [ { type: 'text' as const, text: `Updated relationship ${relationshipId} to ${relationship.relationshipType.name}.` } ], structuredContent: { action, relationshipId, relationship } }; } case 'delete': { if (!relationshipId) { return { isError: true as const, content: [ { type: 'text' as const, text: 'Provide relationshipId when deleting a relationship.' } ] }; } const result = await client.deleteRelationship(relationshipId); logger.info({ relationshipId }, 'Deleted Monica relationship'); return { content: [ { type: 'text' as const, text: `Deleted relationship ID ${relationshipId}.` } ], structuredContent: { action, relationshipId, result } }; } default: return { isError: true as const, content: [ { type: 'text' as const, text: `Unsupported action: ${action}.` } ] }; } } ); }
- inputSchema defining the tool's input parameters: action, relationshipId, contactId, limit, page, payload.inputSchema: { action: z.enum(['list', 'get', 'create', 'update', 'delete']), relationshipId: 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: relationshipPayloadSchema.optional() }
- relationshipPayloadSchema for the payload parameter, defining contactIsId, ofContactId, relationshipTypeId, relationshipTypeName.const relationshipPayloadSchema = z.object({ contactIsId: z.number().int().positive().optional(), ofContactId: z.number().int().positive().optional(), relationshipTypeId: z.number().int().positive().optional(), relationshipTypeName: z.string().min(1).max(255).optional() }); type RelationshipPayloadForm = z.infer<typeof relationshipPayloadSchema>;
- toRelationshipCreatePayload helper function converting payload to CreateRelationshipPayload.function toRelationshipCreatePayload( payload: RelationshipPayloadForm & { relationshipTypeId: number } ): CreateRelationshipPayload { if ( typeof payload.contactIsId !== 'number' || typeof payload.ofContactId !== 'number' ) { throw new Error('contactIsId and ofContactId are required to create a relationship.'); } return { contactIsId: payload.contactIsId, ofContactId: payload.ofContactId, relationshipTypeId: payload.relationshipTypeId }; }