Skip to main content
Glama

Manage Monica gifts and debts

monica_manage_financial_record

Manage financial records in Monica CRM by creating, updating, deleting, listing, or viewing gifts and debts associated with contacts.

Instructions

List, inspect, create, update, or delete gifts and debts. Use recordType="gift" for presents, recordType="debt" for money owed.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
recordTypeYes
actionYes
recordIdNo
contactIdNo
limitNo
pageNo
giftPayloadNo
debtPayloadNo

Implementation Reference

  • Main handler function for 'gift' recordType operations: list, get, create, update, delete gifts using MonicaClient API calls, normalization, and logging.
    async function handleGift({
      input,
      client,
      logger
    }: {
      input: GiftInput;
      client: ToolRegistrationContext['client'];
      logger: ToolRegistrationContext['logger'];
    }) {
      const { action } = input;
    
      switch (action) {
        case 'list': {
          const response = await client.listGifts({
            contactId: input.contactId,
            limit: input.limit,
            page: input.page
          });
          const gifts = response.data.map(normalizeGift);
          const scope = input.contactId ? `contact ${input.contactId}` : 'your account';
          const summary = gifts.length
            ? `Found ${gifts.length} gift${gifts.length === 1 ? '' : 's'} for ${scope}.`
            : `No gifts found for ${scope}.`;
    
          return {
            content: [
              {
                type: 'text' as const,
                text: summary
              }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              contactId: input.contactId,
              gifts,
              pagination: {
                currentPage: response.meta.current_page,
                lastPage: response.meta.last_page,
                perPage: response.meta.per_page,
                total: response.meta.total
              }
            }
          };
        }
    
        case 'get': {
          if (input.recordId == null) {
            return missingIdError('gift');
          }
    
          const response = await client.getGift(input.recordId);
          const gift = normalizeGift(response.data);
          return {
            content: [
              {
                type: 'text' as const,
                text: `Gift ${gift.title} (ID ${gift.id}).`
              }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              recordId: input.recordId,
              gift
            }
          };
        }
    
        case 'create': {
          if (!input.giftPayload) {
            return missingPayloadError('gift');
          }
    
          const response = await client.createGift(toGiftCreatePayload(input.giftPayload));
          const gift = normalizeGift(response.data);
          logger.info({ giftId: gift.id, contactId: gift.contact.id }, 'Created Monica gift');
    
          return {
            content: [
              { type: 'text' as const, text: `Created gift ${gift.title} (ID ${gift.id}).` }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              gift
            }
          };
        }
    
        case 'update': {
          if (input.recordId == null) {
            return missingIdError('gift');
          }
          if (!input.giftPayload) {
            return missingPayloadError('gift');
          }
    
          const response = await client.updateGift(input.recordId, toGiftUpdatePayload(input.giftPayload));
          const gift = normalizeGift(response.data);
          logger.info({ giftId: gift.id }, 'Updated Monica gift');
    
          return {
            content: [
              { type: 'text' as const, text: `Updated gift ${gift.title} (ID ${gift.id}).` }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              recordId: input.recordId,
              gift
            }
          };
        }
    
        case 'delete': {
          if (input.recordId == null) {
            return missingIdError('gift');
          }
    
          const result = await client.deleteGift(input.recordId);
          logger.info({ giftId: input.recordId }, 'Deleted Monica gift');
    
          return {
            content: [
              { type: 'text' as const, text: `Deleted gift ID ${input.recordId}.` }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              recordId: input.recordId,
              result
            }
          };
        }
    
        default:
          return unsupportedAction(action);
      }
    }
  • Main handler function for 'debt' recordType operations: list, get, create, update, delete debts using MonicaClient API calls, normalization, and logging.
    async function handleDebt({
      input,
      client,
      logger
    }: {
      input: DebtInput;
      client: ToolRegistrationContext['client'];
      logger: ToolRegistrationContext['logger'];
    }) {
      const { action } = input;
    
      switch (action) {
        case 'list': {
          const response = await client.listDebts({
            contactId: input.contactId,
            limit: input.limit,
            page: input.page
          });
          const debts = response.data.map(normalizeDebt);
          const scope = input.contactId ? `contact ${input.contactId}` : 'your account';
          const summary = debts.length
            ? `Found ${debts.length} debt${debts.length === 1 ? '' : 's'} for ${scope}.`
            : `No debts found for ${scope}.`;
    
          return {
            content: [
              {
                type: 'text' as const,
                text: summary
              }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              contactId: input.contactId,
              debts,
              pagination: {
                currentPage: response.meta.current_page,
                lastPage: response.meta.last_page,
                perPage: response.meta.per_page,
                total: response.meta.total
              }
            }
          };
        }
    
        case 'get': {
          if (input.recordId == null) {
            return missingIdError('debt');
          }
    
          const response = await client.getDebt(input.recordId);
          const debt = normalizeDebt(response.data);
          return {
            content: [
              {
                type: 'text' as const,
                text: `Debt ID ${debt.id} (${debt.amount} ${debt.currency?.iso ?? ''}).`
              }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              recordId: input.recordId,
              debt
            }
          };
        }
    
        case 'create': {
          if (!input.debtPayload) {
            return missingPayloadError('debt');
          }
    
          const response = await client.createDebt(toDebtCreatePayload(input.debtPayload));
          const debt = normalizeDebt(response.data);
          logger.info({ debtId: debt.id, contactId: debt.contact.id }, 'Created Monica debt');
    
          return {
            content: [
              {
                type: 'text' as const,
                text: `Created debt ID ${debt.id} (${debt.amount} ${debt.currency?.iso ?? ''}).`
              }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              debt
            }
          };
        }
    
        case 'update': {
          if (input.recordId == null) {
            return missingIdError('debt');
          }
          if (!input.debtPayload) {
            return missingPayloadError('debt');
          }
    
          const response = await client.updateDebt(input.recordId, toDebtUpdatePayload(input.debtPayload));
          const debt = normalizeDebt(response.data);
          logger.info({ debtId: debt.id }, 'Updated Monica debt');
    
          return {
            content: [
              {
                type: 'text' as const,
                text: `Updated debt ID ${debt.id} (${debt.amount} ${debt.currency?.iso ?? ''}).`
              }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              recordId: input.recordId,
              debt
            }
          };
        }
    
        case 'delete': {
          if (input.recordId == null) {
            return missingIdError('debt');
          }
    
          const result = await client.deleteDebt(input.recordId);
          logger.info({ debtId: input.recordId }, 'Deleted Monica debt');
    
          return {
            content: [
              { type: 'text' as const, text: `Deleted debt ID ${input.recordId}.` }
            ],
            structuredContent: {
              recordType: input.recordType,
              action,
              recordId: input.recordId,
              result
            }
          };
        }
    
        default:
          return unsupportedAction(action);
      }
    }
  • Zod schemas for validating tool inputs: giftPayloadSchema, debtPayloadSchema, financialInputSchema (combines recordType, action, payloads), and derived types.
    const giftPayloadSchema = z.object({
      contactId: z.number().int().positive(),
      receivedOn: z
        .string()
        .regex(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/u, 'receivedOn must use YYYY-MM-DD format.'),
      title: z.string().min(1).max(255),
      description: z.string().max(1_000_000).optional().nullable(),
      wasGivenByMe: z.boolean().optional(),
      amount: z.number().optional(),
      currencyId: z.number().int().positive().optional()
    });
    
    type GiftPayloadForm = z.infer<typeof giftPayloadSchema>;
    
    const debtPayloadSchema = z.object({
      contactId: z.number().int().positive(),
      description: z.string().max(1_000_000).optional().nullable(),
      amount: z.number().positive(),
      currencyId: z.number().int().positive().optional(),
      happenedAt: z
        .string()
        .regex(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/u, 'happenedAt must use YYYY-MM-DD format.'),
      isSettled: z.boolean().optional(),
      settledAt: z
        .string()
        .regex(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/u, 'settledAt must use YYYY-MM-DD format.')
        .optional()
        .nullable()
    });
    
    type DebtPayloadForm = z.infer<typeof debtPayloadSchema>;
    
    const financialInputShape = {
      recordType: z.enum(['gift', 'debt']),
      action: z.enum(['list', 'get', 'create', 'update', 'delete']),
      recordId: 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(),
      giftPayload: giftPayloadSchema.optional(),
      debtPayload: debtPayloadSchema.optional()
    } as const;
    
    const financialInputSchema = z.object(financialInputShape);
    
    type FinancialInput = z.infer<typeof financialInputSchema>;
    
    type GiftInput = FinancialInput & { recordType: 'gift' };
    type DebtInput = FinancialInput & { recordType: 'debt' };
  • Registers the 'monica_manage_financial_record' tool with server.registerTool, providing title, description, inputSchema, and dispatcher handler that routes to handleGift or handleDebt.
    server.registerTool(
      'monica_manage_financial_record',
      {
        title: 'Manage Monica gifts and debts',
        description:
          'List, inspect, create, update, or delete gifts and debts. Use recordType="gift" for presents, recordType="debt" for money owed.',
        inputSchema: financialInputShape
      },
      async (rawInput) => {
        const input = financialInputSchema.parse(rawInput);
    
        if (input.recordType === 'gift') {
          return handleGift({ input: input as GiftInput, client, logger });
        }
    
        return handleDebt({ input: input as DebtInput, client, logger });
      }
    );
  • Calls registerFinancialTools as part of the overall registerTools function that registers all MCP tools.
    registerFinancialTools(context);
  • Module-level registration function for financial tools, invoked from registerTools.ts.
    export function registerFinancialTools(context: ToolRegistrationContext): void {
      const { server, client, logger } = context;
    
      server.registerTool(
        'monica_manage_financial_record',
        {
          title: 'Manage Monica gifts and debts',
          description:
            'List, inspect, create, update, or delete gifts and debts. Use recordType="gift" for presents, recordType="debt" for money owed.',
          inputSchema: financialInputShape
        },
        async (rawInput) => {
          const input = financialInputSchema.parse(rawInput);
    
          if (input.recordType === 'gift') {
            return handleGift({ input: input as GiftInput, client, logger });
          }
    
          return handleDebt({ input: input as DebtInput, client, logger });
        }
      );
    }
Behavior2/5

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

With no annotations provided, the description carries full burden for behavioral disclosure. While it mentions the five possible actions, it doesn't describe permissions needed, whether deletions are permanent, rate limits, error conditions, or what happens when creating/updating records. For a multi-action tool with destructive operations, this leaves significant gaps.

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

Conciseness5/5

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

The description is perfectly concise with two sentences that each earn their place. The first sentence states the core functionality, and the second provides essential parameter guidance. There's zero wasted text and it's front-loaded with the most important information.

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 (8 parameters, multiple actions including destructive operations, nested objects), no annotations, and no output schema, the description is insufficient. It doesn't explain how different actions map to different parameter requirements, what the tool returns, or behavioral constraints. For a multi-purpose CRUD tool, this leaves too many questions unanswered.

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 meaningful context for the recordType parameter by explaining what 'gift' and 'debt' represent. However, with 8 total parameters and 0% schema description coverage, it doesn't explain the purpose of action, recordId, contactId, limit, page, giftPayload, or debtPayload. The description provides some value but doesn't compensate for the extensive coverage gap.

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 resources ('gifts and debts'), and distinguishes it from siblings by focusing on financial records rather than contacts, activities, or other entities. The explanation of recordType values further clarifies the scope.

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

Usage Guidelines4/5

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

The description provides clear context for when to use specific recordType values ('gift' for presents, 'debt' for money owed), which helps guide parameter selection. However, it doesn't explicitly state when to use this tool versus alternatives like monica_manage_contact or other sibling tools for related data.

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