Skip to main content
Glama
dgalarza

YNAB MCP Server

by dgalarza

move_category_funds

Transfer budgeted money between categories within a specific month to adjust your spending plan as financial needs change.

Instructions

Move funds from one category to another in a specific month.

Args:
    budget_id: The ID of the budget (use 'last-used' for default budget)
    month: Month in YYYY-MM-DD format (e.g., 2025-01-01 for January 2025)
    from_category_id: Source category ID to move funds from
    to_category_id: Destination category ID to move funds to
    amount: Amount to move (positive value)

Returns:
    JSON string with updated from and to categories

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
amountYes
budget_idYes
from_category_idYes
monthYes
to_category_idYes

Implementation Reference

  • MCP tool registration and handler wrapper for move_category_funds. Delegates execution to YNABClient instance.
    @mcp.tool()
    async def move_category_funds(
        budget_id: str,
        month: str,
        from_category_id: str,
        to_category_id: str,
        amount: float,
    ) -> str:
        """Move funds from one category to another in a specific month.
    
        Args:
            budget_id: The ID of the budget (use 'last-used' for default budget)
            month: Month in YYYY-MM-DD format (e.g., 2025-01-01 for January 2025)
            from_category_id: Source category ID to move funds from
            to_category_id: Destination category ID to move funds to
            amount: Amount to move (positive value)
    
        Returns:
            JSON string with updated from and to categories
        """
        client = get_ynab_client()
        result = await client.move_category_funds(
            budget_id, month, from_category_id, to_category_id, amount
        )
        return json.dumps(result, indent=2)
  • Core implementation executing the tool logic: retrieves current budgets, adjusts budgeted amounts for source and destination categories via YNAB API PATCH requests, and returns updated category details.
    async def move_category_funds(
        self,
        budget_id: str,
        month: str,
        from_category_id: str,
        to_category_id: str,
        amount: float,
    ) -> dict[str, Any]:
        """Move funds from one category to another in a specific month.
    
        Uses direct API calls since ynab-sdk is read-only.
    
        Args:
            budget_id: The budget ID or 'last-used'
            month: Month in YYYY-MM-DD format (e.g., 2025-01-01)
            from_category_id: Source category ID
            to_category_id: Destination category ID
            amount: Amount to move (positive value)
    
        Returns:
            Dictionary with updated from and to categories
        """
        try:
            # Get current budgeted amounts
            categories_response = self.client.categories.get_categories(budget_id)
            categories = {}
            for group in categories_response.data.category_groups:
                for cat in group.categories:
                    if cat.id in [from_category_id, to_category_id]:
                        categories[cat.id] = {"budgeted": cat.budgeted, "name": cat.name}
    
            if from_category_id not in categories or to_category_id not in categories:
                raise ValueError("One or both category IDs not found")
    
            # Calculate new budgeted amounts
            from_budgeted = (categories[from_category_id]["budgeted"] / 1000) - amount
            to_budgeted = (categories[to_category_id]["budgeted"] / 1000) + amount
    
            # Update both categories using direct API calls
            base_url = f"{self.api_base_url}/budgets/{budget_id}/months/{month}/categories"
    
            # Update from_category
            from_url = f"{base_url}/{from_category_id}"
            from_data = {"category": {"budgeted": int(from_budgeted * MILLIUNITS_FACTOR)}}
            from_result = await self._make_request_with_retry("patch", from_url, json=from_data)
    
            # Update to_category
            to_url = f"{base_url}/{to_category_id}"
            to_data = {"category": {"budgeted": int(to_budgeted * MILLIUNITS_FACTOR)}}
            to_result = await self._make_request_with_retry("patch", to_url, json=to_data)
    
            from_cat = from_result["data"]["category"]
            to_cat = to_result["data"]["category"]
    
            return {
                "from_category": {
                    "id": from_cat["id"],
                    "name": from_cat["name"],
                    "budgeted": from_cat["budgeted"] / 1000 if from_cat["budgeted"] else 0,
                    "balance": from_cat["balance"] / 1000 if from_cat["balance"] else 0,
                },
                "to_category": {
                    "id": to_cat["id"],
                    "name": to_cat["name"],
                    "budgeted": to_cat["budgeted"] / 1000 if to_cat["budgeted"] else 0,
                    "balance": to_cat["balance"] / 1000 if to_cat["balance"] else 0,
                },
                "amount_moved": amount,
            }
        except Exception as e:
            raise Exception(f"Failed to move category funds: {e}") from e

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/dgalarza/ynab-mcp'

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