Skip to main content
Glama
server.cpython-312.pyc35.1 kB
� ��gLc��N�ddlZddlZddlmZmZmZddlmZddlmZm Z m Z m Z m Z m Z ddlmZddlmZddlmZddlmZddlmZdd lmZdd lmZdd lmZdd lmZdd l m!Z!ddl"m#Z#ddl$m%Z%ddl&m'Z'ddl(m)Z)ddl*m+Z+ddl,m-Z-ddl.m/Z/ddl0m1Z1ddl2m3Z3ed��ejhjkd�Z6ee�dz Z7e7jqdd��e7dz Z9e7dz Z:de!fd�Z;Gd �d!�Z<de<fd"�Z=d#e e>de>fd$�Z?d#e e>d%e e e>d&e@de e@fd'�ZAd(e e>d)e e@d*e e>de>fd+�ZB d^d%e e e>d#e e>d*e e e>de>fd,�ZCd-e e e>e fde e>e ffd.�ZDd/e>ezde e>e ffd0�ZEd/e>ezd1e e>e fddfd2�ZFed3�ZGGd4�d5�ZHeH�ZIeGj�d6�de e>fd7��ZKeGj�d8�d9e>deLej�fd:��ZNd;e!d9e>d<e>de e>fd=�ZOeGj�� d_d>e>d?eeQed@�A�fdBe>d<e e>dCe e>de e>e ff dD��ZReGj��d>e>deQfdE��ZSeGj��de>fdF��ZTeGj��d9e>de>fdG��ZUeGj��d9e>d>e>de>fdH��ZVdIe3dJe e>e>fdKe>de e>fdL�ZWdMe e3dKe>de e3fdN�ZXeGj�� d`d9e>dKee>edO�A�fdPee e@edQ�A�fde>fdR��ZYeGj��dMe e3dSe>dTe>de e3fdU��ZZ dad9e>dSe>dVe>dTe>de>f dW�Z[dXe'e e>e fzde\e>e>eQeQffdY�Z]d?eQde>fdZ�Z^eGj��d9e>de>fd[��Z_eGj��d9e>de>fd\��Z`eGj��d9e>de>fd]��Zay)b�N)�date�datetime� timedelta)�Path)� Annotated�Any�Dict�List�Optional�cast)� load_dotenv)�FastMCP)�Field)�XDG_CONFIG_HOME)� AccountsApi)� BudgetsApi)� CategoriesApi)�TransactionsApi)� ApiClient)� Configuration)�Account)�Category)�CategoryGroupWithCategories)�ExistingTransaction)�NewTransaction)�PostTransactionsWrapper)�PutTransactionWrapper)�TransactionDetailT)�verbose� YNAB_API_KEYzmcp-ynab)�parents�exist_okzpreferred_budget_id.jsonzbudget_category_cache.json�returnc��bK�ts td��tt��}t|�S�w)zKGet a configured YNAB API client. Reads API key from environment variables.z/YNAB_API_KEY not found in environment variables)� access_token)� ynab_api_key� ValueErrorrr)� configurations �F/Users/home/1-Dev-Projects/mcp-servers/ynab-mcp/src/mcp_ynab/server.py� _get_clientr*+s*���� ��J�K�K�!�|�<�M� �]� #�#�s�-/c�(�eZdZdZd�Zdefd�Zd�Zy)�AsyncYNABClientz*Async context manager for YNAB API client.c��d|_y�N��client��selfs r)�__init__zAsyncYNABClient.__init__6s ��+/�� �r#c��TK�t��d{���|_|jS7��wr.)r*r0r1s r)� __aenter__zAsyncYNABClient.__aenter__9s!����'�M�)�� ��{�{��*�s �(�&�(c��&K�|jryy�wr.r/)r2�exc_type�exc_val�exc_tbs r)� __aexit__zAsyncYNABClient.__aexit__=s���� �;�;� � ���N)�__name__� __module__� __qualname__�__doc__r3rr6r;�r4r)r,r,3s��4�0��)��r4r,c��K�t�S�w)z)Get an async YNAB client context manager.)r,rAr4r)�get_ynab_clientrCCs���� � ��s� �headersc �"����D�cgc]}t|�dz��c}�ddj��fd�tt���D��zdz}ddj�fd�tt���D��zdz}||zd zScc}w) z1Create an empty markdown table with just headers.��| � | c3�8�K�|]}�|d�|������y�w)�<NrA)�.0�irD�widthss ��r)� <genexpr>z#_get_empty_table.<locals>.<genexpr>Ks*�����#^�J]�Q�w�q�z�!�F�1�I�;��&?�$@�J]���z | �|c3�4�K�|]}d�|dzz���y�w)�-rFNrA)rKrLrMs �r)rNz#_get_empty_table.<locals>.<genexpr>Ls!�����Q�=P��c�V�A�Y��]�3�=P�s��| � )�len�join�range)rD�h� header_line�sep_linerMs` @r)�_get_empty_tabler[Hs����")� *�'�Q�c�!�f�q�j�'� *�F�����#^�%�PS�T[�P\�J]�#^�^�^�ag�g�K��S�X�X�Q�U�3�w�<�=P�Q�Q�Q�TY�Y�H� �� !�D� (�(��+s�B �rows� col_countc ���|D�cgc] }t|���}}|D]0}t|�D] }t||t||��||<�"�2|D�cgc]}|dz�� c}Scc}wcc}w)z)Calculate column widths based on content.rF)rUrW�max)rDr\r]rXrM�rowrL�ws r)�_get_column_widthsrbPst��%� &�g��c�!�f�g�F� &����y�!�A��F�1�I�s�3�q�6�{�3�F�1�I�"��"� "�6�a�A��E�6� "�"�� '�� #s �A!� A&�itemsrM� alignmentsc��d}t|�D],\}}||dk(r||d||���d�z }�||d||���d�z }�.|j�dzS)z+Format a single line of the markdown table.rG�right�>rHrJrT)� enumerate�rstrip)rcrMrd�linerL�items r)�_format_table_linerlYsu�� �D��U�#���4� �a�=�G� #� �t�A�f�Q�i�[�=�)��-� -�D� �t�A�f�Q�i�[�=�)��-� -�D� $� �;�;�=�4� �r4c����|s t|�S���ndgt|�z�t|�}t|||��t|���}ddj d��D��zdz}dj ��fd�|D��}||z|zS)z-Build a markdown table from rows and headers.�leftrPc3�,K�|] }d|dzz���y�w)rR�NrA)rKras r)rNz(_build_markdown_table.<locals>.<genexpr>ps����<�V��c�Q��U�m�V�s�rS�c3�8�K�|]}t|������y�wr.)rl)rKr`rdrMs ��r)rNz(_build_markdown_table.<locals>.<genexpr>rs�����T�t��*�3�� �C�t�rO)r[rUrbrlrV)r\rDrdr]rYrZ� row_linesrMs ` @r)�_build_markdown_tablertds���� ���(�(�)�5��F�8�c�'�l�;R�J��G� �I� ���y� 9�F�$�W�f�j�A�K��S�X�X�<�V�<�<�<�u�D�H����T�t�T�T�I� �� !�I� -�-r4�accountsc ��i}gd�}dddddddd d�}|D]i}|jd d �s|jd d �r�(|d }||vrg||<t|d�dz }||j|dd|d��||dd���k|j�D]}|j d�d���gdddd�d�}|D]�}||vs�||s�|j||�||d�} t d�||D��} d| d��| d<|dvr|dd xx| z cc<n|d!vr|dd"xxt | �z cc<|d#j| ���|dd |dd"z |dd$<d|dd d��|dd <d|dd"d��|dd"<d|dd$d��|dd%<|S)&z3Format account data into a user-friendly structure.)�checking�savings� creditCard�mortgage�autoLoan� studentLoan� otherAsset�otherLiabilityzChecking AccountszSavings Accountsz Credit Cards� Mortgagesz Auto Loansz Student Loansz Other AssetszOther Liabilities�closedF�deleted�type�balance���name�$�,.2f�id)r�r�� balance_rawr�c��t|d�S)Nr���abs)�xs r)�<lambda>z)_format_accounts_output.<locals>.<lambda>�s���Q�}�%5�!6r4T)�key�reverseg)� total_assets�total_liabilities� net_worth)ru�summary)r�ruc3�&K�|] }|d��� y�w)r�NrA)rK�accts r)rNz*_format_accounts_output.<locals>.<genexpr>�s����X�>W�d�d�=�1�>W�r<�total)rwrxr}r�r�)ryrzr{r|r~r�ru� net_worth_rawr�)�get�float�append�values�sort�sumr�) ru�account_groups� type_order�type_display_names�account� acct_typer��group�output� group_data� group_totals r)�_format_accounts_outputr�vs_��68�N� �J�(�%�$�� �&�$�-� ���� �;�;�x�� '�7�;�;�y�%�+H� ��F�O� � �N� *�(*�N�9� %��� �*�+�d�2���y�!�(�(�����w�t�n�-�&��d�m�  � ��$ �&�&�(�� � � �6�� �E�)���!$�� ��F� � � �� &�>�)�+D�*�.�.�y�)�D�*�9�5��J��X�n�Y�>W�X�X�K�$%�k�$�%7�"8�J�w� ��A�A��y�!�.�1�[�@�1�����y�!�"5�6�#�k�:J�J�6� �:� � %� %�j� 1�) �. �y��.�)�F�9�,=�>Q�,R�R� �9��o�&�+,�F�9�,=�n�,M�d�+S�(T�F�9��n�%�/0�� �1B�CV�1W�X\�0]�-^�F�9��)�*�'(�� �):�?�)K�D�(Q�%R�F�9��k�"� �Mr4�filenamec�� t|d�5}tj|�cddd�S#1swYyxYw#t$ricYSwxYw)zLoad JSON data from a file.�rN)�open�json�load�FileNotFoundError)r��fs r)�_load_json_filer��s=��� �(�C� �A��9�9�Q�<�!� � �� ��� ��s#� 9�-� 9�6�9�9� A�A�datac�v�t|d�5}tj||d��ddd�y#1swYyxYw)zSave JSON data to a file.rarF)�indentN)r�r��dump)r�r�r�s r)�_save_json_filer��s*�� �h�� �� � � �$��!�$� � � �s�/�8�YNABc��eZdZd�Zd d�Zdeefd�Zdeddfd�Zdede e jfd�Z ded e eeefddfd �Zy) � YNABResourcesc�@�d|_i|_|j�yr.)�_preferred_budget_id�_category_cache� _load_datar1s r)r3zYNABResources.__init__�s��37��!�@B��� ���r4r#Nc� � ttd�5}|j�j�xsd|_ddd� t t�|_y#1swY�xYw#t $r d|_Y�4wxYw#t $r i|_YywxYw)zLoad data from files.r�N) r��PREFERRED_BUDGET_ID_FILE�read�stripr�r�r��BUDGET_CATEGORY_CACHE_FILEr�)r2r�s r)r�zYNABResources._load_data�s}�� -��.��4��,-�F�F�H�N�N�,<�,D���)�5�  &�#2�3M�#N�D� � 5�4�� � -�(,�D� %� -�� !� &�#%�D� � &�s9�A$�(A�A$�A:�A!�A$�$A7�6A7�:B � B c��|jS)zGet the preferred budget ID.)r�r1s r)�get_preferred_budget_idz%YNABResources.get_preferred_budget_id�s���(�(�(r4� budget_idc�~�||_ttd�5}|j|�ddd�y#1swYyxYw)zSet the preferred budget ID.raN)r�r�r��write)r2r�r�s r)�set_preferred_budget_idz%YNABResources.set_preferred_budget_id�s/��$-��!� �*�C� 0�A� �G�G�I� �1� 0� 0�s�3�<c ���|jj|g�}|D�cgc]?}tjd|jdd��d|jdd��d�����Ac}Scc}w) z:Get categories from the cache formatted for MCP resources.�textr��Unnamedz (ID: r��N/A�))r�r�)r�r��types� TextContent)r2r��cached_categories�cats r)�get_cached_categoriesz#YNABResources.get_cached_categories�s}�� �0�0�4�4�Y��C�� )�  �)�� � � ��S�W�W�V�Y�%?�$@��s�w�w�t�UZ�G[�F\�\]�"^� �)�  � �� s�AA(� categoriesc���|D�cgc]5}|jd�|jd�|jd�d���7c}|j|<tt|j�ycc}w)z!Cache categories for a budget ID.r�r��category_group_name)r�r�r�N)r�r�r�r�)r2r�r�r�s r)�cache_categorieszYNABResources.cache_categoriessm��"� + � "�� �g�g�d�m���������!6�7� � "� + ����Y�'� �2�D�4H�4H�I��+ s�:A*)r#N)r=r>r?r3r�r �strr�r��listr�r�r�r r rr�rAr4r)r�r��sy��� &�)��#��)������  �s� �t�E�<M�<M�7N� � J�#� J�4��S�#�X��;O� J�TX� Jr4r�zynab://preferences/budget_idc�*�tj�S)z!Get the preferred YNAB budget ID.)�ynab_resourcesr�rAr4r)r�r�s�� � 1� 1� 3�3r4zynab://categories/{budget_id}r�c�,�tj|�S)z&Get cached categories for a budget ID.)r�r��r�s r)r�r� s�� � /� /� � :�:r4r0� category_namec��K�t|�}|j|�}|jj}|D]N}|jD]=}|j j �|j �k(s�/|jccS�Py�w)zFind a category ID by name.N)r�get_categoriesr��category_groupsr�r��lowerr�)r0r�r��categories_api�categories_responser�r�r�s r)�_find_category_idr�)sv����"�6�*�N�(�7�7� �B��$�)�)�9�9�J����#�#�C��x�x�~�~��=�#6�#6�#8�8��v�v� �$�� �s �A2B �5B � account_id�amountzAmount in dollars)� description� payee_name�memoc ���K�t��d{���4�d{���}t|�}t|�}t|dz�}tj �} | s3|j �} | jjdj} d} |rt|| |��d{���} t|tj�|||| ��} t| ��} |j| | �}|jrK|jj r5|jj j#�cddd��d{���Sicddd��d{���S7��?7��97��7� 7�#1�d{���7swYyxYw�w)z!Create a new transaction in YNAB.Nr�r)r�rr�r�r�� category_id�� transaction)rCrr�intr�r�� get_budgetsr��budgetsr�r�rr�todayr�create_transactionr��to_dict)r�r�r�r�r�r0�transactions_api� budgets_api�amount_milliunitsr��budgets_responser�r��wrapper�responses r)r�r�5s9����%�&�&�&�&�&�*�6�2�� ��(� ���� �.��#�:�:�<� ��*�6�6�8� �(�-�-�5�5�a�8�;�;�I�� � � 1�&�)�]� S�S�K�%�!�����$�!��#�  � �*�k�B��#�6�6�y�'�J�� �=�=�X�]�]�6�6��=�=�,�,�4�4�6�;'�&�&�<�='�&�&��&��T��'��&��&�&�&�s��E0�E�E0�E�E0�BE�E� B E�* E0�6E�7E0�<E�= E0� E� E0�E0�E�E0�E0�E-�!E$ �"E-�)E0c��K�t��d{���4�d{���}t|�}t|�}|j�}|jj dj }|j||�}t|jjj�dz cddd��d{���S7��7��7� #1�d{���7swYyxYw�w)z7Get the current balance of a YNAB account (in dollars).Nrr�) rCrrr�r�r�r��get_account_by_idr�r�r�)r�r0� accounts_apir�r�r�r�s r)�get_account_balancer�_s�����%�&�&�&�&�&�"�6�*� � ��(� �&�2�2�4��$�)�)�1�1�!�4�7�7� ��1�1�)�Z�H���X�]�]�*�*�2�2�3�d�:�'�&�&��&��&��&�&�&�sU�C�B6�C�B8�C�BB<�$ C�0B:�1C�8C�:C�<C�C �C� Cc ��K�t��d{���4�d{���}t|�}|j�}|jj}d}|s|dz }nB|D]=}|j �}|d|j dd��d|j d��d �z }�?|cddd��d{���S7��7��7� #1�d{���7swYyxYw�w) z)List all YNAB budgets in Markdown format.Nz# YNAB Budgets z_No budgets found._z- **r�zUnnamed Budgetz** (ID: r�z) )rCrr�r�r�r�r�)r0r�r�� budgets_list�markdown�budget�bs r)r�r�ls�����%�&�&�&�&�&� ��(� �&�2�2�4��'�,�,�4�4� �'��� �-� -�H�&���N�N�$���d�1�5�5��1A�#B�"C�8�A�E�E�RV�K�=�X[�\�\��'��'�&�&��&��&��&�&�&�sU�C �B.�C �B0�C �A?B4� C �(B2�)C �0C �2C �4C�:B= �;C�C c ��K�t��d{���4�d{���}t|�}g}|j|�}|jjD]2}t |t �s�|j|j���4t|�}d}|dz }|d|dd�d�z }|d|dd �d�z }|d |dd �d �z }|d D]\}|d|d�d�z }|d|d�d �z }g} |d D]} | j| d| d| dg��!|t| gd�gd��z }|dz }�^|cddd��d{���S7��-7��'7� #1�d{���7swYyxYw�w)z?List all YNAB accounts in a specific budget in Markdown format.Nz# YNAB Account Summary z ## Summary z- **Total Assets:** r�r�rTz- **Total Liabilities:** r�z- **Net Worth:** r�� ru�## r�z**Group Total:** r�r�r�r�)z Account Name�Balance�ID)rnrfrn) rCr� get_accountsr�ru� isinstancerr�r�r�rt) r�r0r�� all_accountsr�r�� formattedrr�r\r�s r)rr~s�����%�&�&�&�&�&�"�6�*� �-/� ��,�,�Y�7���}�}�-�-�G��'�7�+��#�#�G�O�O�$5�6�.�,�L�9� �/���N�"���*�9�Y�+?��+O�*P�PR�S�S���/� �)�0D�EX�0Y�/Z�Z\�]�]���'� �)�(<�[�(I�'J�$�O�O���z�*�E� �#�e�F�m�_�B�/� /�H� �+�E�'�N�+;�4�@� @�H��D��j�)��� � �T�&�\�4� �?�D��J�G�H�*� �-��7�9R�� �H� �� �H�+��;'�&�&��&��&��&�&�&�s\�E�D=�E�E�E�AE�&CE�+ E�7E�8E�E�E�E� E � E�Ec ���K�t��d{���4�d{���}t|�}g}tj�j d��j �}|j |||��}|j|jj�d}|s|dzcddd��d{���Sgd�}gd�} g} |D]y} d | jd z d ��} | j| j| jjd �| | jxsd | j xsd | j"xsdg��{|t%| || �z }|cddd��d{���S7��K7��E7��7� #1�d{���7swYyxYw�w)zDGet recent transactions for a specific account in a specific budget.Nrp)�day�� since_datez# Recent Transactions z _No recent transactions found._ )r�Date�Amountz Payee Name� Category Name�Memo)rnrnrfrnrnrnr�r�r��%Y-%m-%dr�rq)rCrr�now�replacer�get_transactions_by_account�extendr�� transactionsr�r�r��var_date�strftimer�r�r�rt) r�r�r0r��all_transactionsrr�rrD�alignr\�txn� amount_strs r)�get_transactionsr �s_����%�&�&�&�&�&�*�6�2��46���\�\�^�+�+��+�2�7�7�9� �#�?�?� �z�j�@� �� ���� � � :� :�;�.����A�A�'�&�&�R��A����#�C��S�Z�Z�$�.�t�4�5�J� �K�K��F�F��L�L�)�)�*�5���N�N�+�e��%�%�.���H�H�N�� � �$� �)�$���?�?���?'�&�&��&��&��&��&�&�&�st�E:�E�E:�E�E:�BE%� E:�*E!�+E:�0BE%� E:�E#�E:�E:�!E:�#E:�%E7�+E. �,E7�3E:r� account_map� filter_typec��t|j�dz }dt|�d��}|dkrd|��}g}|js|j d�|j s|j d�|j |jjd�|j|jd �||jxsd d j|�|jxsd gS) z7Format a transaction into a row for the markdown table.r�r�r�rrR� Uncategorized� Unapprovedr�Unknownr�z, rq)r�r�r�r�r��approvedr�rrr�r�r�rVr�)rr!r"�amount_dollarsr�statuss r)�_get_transaction_rowr*�s����3�:�:�&��-�N��S��(��.�/�J������ �%� � �F� �?�?�� � �o�&� �<�<�� � �l�#� ��� � � ���j�)������� �2�� ����%� � � �&�� ����B� �r4rc��g}|D]O}t|t�s�|dvxr |j }|dvxr |j }|s|s�?|j |��Q|S)z-Filter transactions based on the filter type.)� uncategorized�both)� unapprovedr-)r rr�r'r�)rr"�needs_attentionr�needs_category�needs_approvals r)�_filter_transactionsr2�sg���O��� �c�,� -�(�,E�E�]�c�o�o�J]�N�(�,B�B�W�3�<�<�GW�N����&�&�s�+� � �r4zKType of transactions to show. One of: 'uncategorized', 'unapproved', 'both'� days_backz6Number of days to look back (default 30, None for all)c ��K�|j�}|dvryt��d{���4�d{���}t|�}t|�}|j |�}|j j D�cic]1}|js#|js|j|j��3}}|r/tj�t|��z j�nd} |j|| ��} t!| j j"|�} d|j%��d�} | s| dzcddd��d{���S| d z } | d |�d �z } |r | d |�d �z } | d z } gd�} gd�}| D�cgc]}t'|||���}}| t)|| |�z } | cddd��d{���S7��z7��tcc}w7�scc}w7�#1�d{���7swYyxYw�w)zVList transactions that need attention based on specified filter type in a YNAB budget.)r,r.r-zLError: Invalid filter_type. Must be 'uncategorized', 'unapproved', or 'both'N)�daysrz"# Transactions Needing Attention (z) z!_No transactions need attention._z**Filters Applied:** z- Filter type: rTz- Looking back z days )rrrr�Payee�Statusr)rnrnrnrfrnrnrn)r�rCrrrr�rur�r�r�r�rrrrr r2r�titler*rt)r�r"r3r0r�r��accounts_responser�r!rr�r/rrDrrr\s r)�"get_transactions_needing_attentionr:�s������#�#�%�K��A�A�]�$�&�&�&�&�&�*�6�2��"�6�*� �(�5�5�i�@��-�1�1�:�:� �:���>�>�'�/�/� �J�J�� � � $�:� � � MV�h�l�l�n�y�i�'@�@�F�F�H�[_� �#�4�4�Y�:�4�V��.�x�}�}�/I�/I�;�W��7� �8I�8I�8K�7L�E�R����A�A�#'�&�&�& �,�,���o�k�]�"�5�5�� � �/�)��G�<� <�H��D���P��I��O^�_���$�S�+�{�C���_��)�$���?�?���='�&�&��&��  � '��6`�7'��&�&�&�s��#G�F�G�F"�G�AF3�26F%�(BF3�* G�6F*�7G�<*F3�&F,�:F3� G�F1�G�"G�%F3�*G�,F3�1G�3G�9F< �:G�G�transaction_id�id_typec��|D]W}|dk(r|j|k(s>|dk(r|j|k(s*|dk(r|j|k(s|dk(s�E|j|k(s�U|cSy)z)Find a transaction by its ID and ID type.r�� import_id�transfer_transaction_id�matched_transaction_idN)r�r>r?r@)rr;r<rs r)�_find_transaction_by_idrA!so�� �� ��_����>�!9��;�&�3�=�=�N�+J��4�4��/�/�>�A��3�3��8R�8R�Vd�8d��J�� r4r�c��K�t��d{���4�d{���}t|�}d}|dk(r;d|vr7 tj|j d�dd�j �}|j||��}t|jj||�}|rgtt|j|j|����} |j!||j"| � �d |�d |�d |�d �cddd��d{���Sd |�d |�d�cddd��d{���S7��7�� #t tf$rY��wxYw7�:7�"#1�d{���7swYyxYw�w)a\Categorize a transaction for a given YNAB budget with the provided category ID. Args: budget_id: The YNAB budget ID transaction_id: The transaction identifier category_id: The category ID to assign id_type: The type of transaction ID being provided. One of: - "id": Direct transaction ID (default) - "import_id": YNAB import ID format (YNAB:[milliunit_amount]:[iso_date]:[occurrence]) - "transfer_transaction_id": ID of a transfer transaction - "matched_transaction_id": ID of a matched transaction Nr>�:rFrr)r�r�r�r�)r�r;r�z Transaction z (type: z) categorized as �.z ) not found.)rCrr�strptime�splitrr'� IndexErrorr rAr�rrrr�r��update_transactionr�) r�r;r�r<r0r�rr��target_transactionr�s r)�categorize_transactionrJ6sg����$%�&�&�&�&�&�*�6�2��� � �k� !�c�^�&;� �%�.�.�~�/C�/C�C�/H��/K�Z�X�]�]�_� �$�4�4�Y�:�4�V��4� �M�M� &� &��� �� �+�/�1�<�<�-�4�4� +���G� � /� /�#�1�4�4�� 0� � "�.�!1��'��BS�T_�S`�`a�b�;'�&�&�>�n�-�X�g�Y�l�K�?'�&�&��&��� �+� �� ��'��&��&�&�&�s��E�D"�E�D%�E�E�6D(�+B E�6 E�D=�E�E� E�D?�E�%E�(D:�7E�9D:�:E�=E�?E�E�E �E�E�categoryc���t|t�r.|j|j|j|j fSt tttf|�}|d|d|d|dfS)zIProcess category data and return tuple of (id, name, budgeted, activity).r�r��budgeted�activity) r rr�r�rMrNr r r�r)rK�cat_dicts r)�_process_category_datarPjse���(�H�%��{�{�H�M�M�8�+<�+<�h�>O�>O�O�O��D��c��N�H�-�H� �D�>�8�F�+�X�j�-A�8�J�CW� W�Wr4c�8�dt|�d��}|dkrd|��S|S)z7Format a dollar amount with proper sign and formatting.r�r�rrRr�)r�rs r)�_format_dollar_amountrRrs-���S��[��&�'�J�%��z�Q�z�l� �9�z�9r4c ���K�t��d{���4�d{���}t|�}|j|�}|jj}d}gd�}gd�}|D]�}t |t �r|j} |j} n5ttttf|j��} | d} | d} | s�d|d| �d�z }g} | D]]} t| �\}}}}|rt|�d z nd }|rt|�d z nd }| j!||t#|�t#|�g��_t%| ||�}||d zz }��|cddd��d{���S7��G7��A7� #1�d{���7swYyxYw�w) zKList all transaction categories for a given YNAB budget in Markdown format.Nz# YNAB Categories )z Category IDr�Budgeted�Activity)rnrnrfrfr�r�rrr�rrT)rCrr�r�r�r rr�r�r r r�rr�rPr�r�rRrt)r�r0r�r��groupsrrDrr��categories_list� group_name� group_dictr\rK�cat_idr�rMrN�budgeted_dollars�activity_dollars�table_mds r)r�r�xss����%�&�&�&�&�&�&�v�.��!�0�0��;�����.�.��*��J��2���E��%�!<�=�"'�"2�"2��"�Z�Z� �!�$�s�C�x�.�%�-�-�/�B� �",�\�":��'��/� �"�� �#�j�\��.� .�H��D�+��3I�(�3S�0���h��=E�5��?�T�#9�1� �=E�5��?�T�#9�1� �� � ���-�.>�?�-�.>�?� �� ,�-�T�7�E�B�H� ��4�� '�H�=�>�Q'�&�&��&��&��&�&�&�sU�E4�E�E4�E�E4�D(E� E4�E�E4�E4�E4�E1�%E( �&E1�-E4c��>K�tj|�d|��S�w)z!Set the preferred YNAB budget ID.zPreferred budget ID set to )r�r�r�s r)r�r��s#�����*�*�9�5� (�� � 4�4�s�c ���K�t��d{���4�d{���}t|�}|j|�}|jj}g}|D].}t |t �s�|j|j��0tj||D�cgc]}|j���c}�d|��cddd��d{���S7��7��cc}w7�#1�d{���7swYyxYw�w)z0Cache all categories for a given YNAB budget ID.Nz Categories cached for budget ID ) rCrr�r�r�r rrr�r�r�r�)r�r0r�r�rVr�r�r�s r)r�r��s�����%�&�&�&�&�&�&�v�.��!�0�0��;�����.�.��� ��E��%�!<�=��!�!�%�"2�"2�3�� �'�'� �Z�3X�Z�c�C�K�K�M�Z�3X�Y�1�)��=�'�&�&��&��4Y�'��&�&�&�sm�C-�C �C-�C�C-�A C�(1C�C �0 C�; C-�C�C-�C-�C�C-�C*�C! �C*�&C-r.)NN)r-�)r�)br��osrrr�pathlibr�typingrrr r r r � mcp.typesr��dotenvr �mcp.server.fastmcpr�pydanticr�xdgr�ynab.api.accounts_apir�ynab.api.budgets_apir�ynab.api.categories_apir�ynab.api.transactions_apir�ynab.api_clientr�ynab.configurationr�ynab.models.accountr�ynab.models.categoryr�*ynab.models.category_group_with_categoriesr� ynab.models.existing_transactionr�ynab.models.new_transactionr�%ynab.models.post_transactions_wrapperr�#ynab.models.put_transaction_wrapperr�ynab.models.transaction_detailr�environr�r&� CONFIG_DIR�mkdirr�r�r*r,rCr�r[r�rbrlrtr�r�r��mcpr�r��resourcer�r�r�r�r��toolr�r�r�r�rr r*r2r:rArJ�tuplerPrRr�r�r�rAr4r)�<module>r~s��� � �.�.��=�=���&���-�+�1�5�%�,�'�)�R�@�6�I�E�<� �D���z�z�~�~�n�-� ��/� "�Z� /� � ������-�%�(B�B��'�*F�F�� $�9�$� � � ��� )�d�3�i�)�C�)�#��S� �#��d�3�i��#�S�#�UY�Z]�U^�#� �d�3�i� ��c�� ��S� � �VY� �RV�.� �t�C�y�/�.�$(��I�.�;C�D��I�;N�.��.�$T�d�4��S��>�&:�T�t�C��H�~�T�n�c�D�j��T�#�s�(�^��%�c�D�j�%��S�#�X��%�4�%��f�o��1J�1J�j������,�-�4��#��4�.�4� ���-�.�;�S�;�T�%�2C�2C�-D�;�/�;� �I� �#� �c� �V^�_b�Vc� ����� $(�� &��&� �e�U�/B�C�C� D�&��&��C�=� &� �3�-� &�  �#�s�(�^� &� �&�R���� ;�#� ;�%� ;� � ;������3�� ��"�����#��#�� ��D����!�c�!�s�!�s�!� �!�H� ��)-�c�3�h���FI�� �#�Y��4 ��(�)� �8;� � � �� ����� � �/��/�� � �e� � ��/���� �u�)a�b�b��/� �/� �/�d������(�)��;>��IL�� �� �� ��0� 1L��1L��1L��1L�� 1L�  � 1L�hX�X��S�#�X��%>�X�5��c�SX�Z_�I_�C`�X�:�%�:�C�:� ����*�C�*�C�*� �*�Z����5�S�5�S�5� �5� ���� >�c� >�c� >� � >r4

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/Meh-S-Eze/ynab-mcp-client2'

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