Skip to main content
Glama

Manage Monica relationships

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
NameRequiredDescriptionDefault
actionYes
relationshipIdNo
contactIdNo
limitNo
pageNo
payloadNo

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}.`
                }
              ]
            };
        }
      }
    );
  • 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
      };
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions actions like create, update, and delete, implying mutations, but fails to describe permissions, side effects, error handling, or response formats. This is a significant gap for a multi-action tool with potential destructive operations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose in the first sentence and adds a brief usage note in the second. It avoids redundancy and is appropriately sized, though it could be slightly more structured to separate actions from parameters.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (6 parameters, nested objects, multiple actions including mutations) and lack of annotations and output schema, the description is incomplete. It does not cover behavioral aspects, parameter details, or return values, making it inadequate for safe and effective use by an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds minimal semantics by mentioning relationshipTypeId or relationshipTypeName, which maps to one parameter in the nested payload. However, with 0% schema description coverage and 6 total parameters, it does not compensate for the lack of schema documentation. The baseline is 3 as it provides some context but leaves most parameters unexplained.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('List, inspect, create, update, or delete') and resource ('relationships between contacts'), distinguishing it from siblings like monica_list_contacts or monica_manage_contact. It explicitly mentions the scope of operations, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by mentioning relationshipTypeId or relationshipTypeName for identification, but it does not provide explicit guidance on when to use this tool versus alternatives (e.g., monica_manage_contact for contact-level operations). No exclusions or prerequisites are stated, leaving usage context partially inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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