Skip to main content
Glama

Malaysia Prayer Time MCP Server

server.cpython-311.pyc28.9 kB
� ���gXR���dZddlZddlZddlZddlZddlZddlZddlZddlZddl m Z m Z ddl m Z ddlmZmZmZmZmZmZmZddlmZddlmZmZmZdd lmZmZdd lm Z ej!ej"d � ��ej#e$��Z%e Gd �d����Z&Gd�d��Z'dd�Z(e$dkr e(��dSdS)a� Malaysia Prayer Time MCP Server This server implements access to Malaysia Prayer Time data using the API from github.com/mptwaktusolat/api-waktusolat. It provides three main tools: - get_prayer_times: Fetch prayer times for a specific zone - list_zones: List all available prayer time zones - get_current_prayer: Get current prayer time status for a zone Features: - Input validation - Response caching - Rate limiting - Graceful shutdown - Health checks �N)� dataclass�field)�Path)�Dict�Any�List�Optional�Callable� Awaitable�Set�)�config)�client�APIError�ValidationError)� PrayerTimes�Zone)�cachedz4%(asctime)s - %(name)s - %(levelname)s - %(message)s)�level�formatc�z�eZdZUdZeed<dZeed<ee���Z e e e e fed<de defd �Zd S) � RateLimiterz#Simple rate limiter implementation.�requests_per_minute�<� window_size)�default_factory� _requests� client_id�returnc�$���tj���|�jvr g�j|<��fd��j|D���j|<t�j|���jkrdS�j|����dS)z-Check if request is allowed under rate limit.c�0��g|]}�|z �jk�|��S�)r)�.0�t�now�selfs ���Q/Users/aman/2) personal/mcp-server-malaysia-prayer-time/src/waktu_solat/server.py� <listcomp>z*RateLimiter.is_allowed.<locals>.<listcomp>;s1���% �% �% ��C�!�G�d�>N�4N�4N�A�4N�4N�4N�FT)�timer�lenr�append)r&rr%s` @r'� is_allowedzRateLimiter.is_allowed4s������i�k�k�� �D�N� *� *�(*�D�N�9� %�% �% �% �% �% ��~�i�0�% �% �% ���y�!� �t�~�i�(� )� )�T�-E� E� E��5� ��y�!�(�(��-�-�-��tr)N)�__name__� __module__� __qualname__�__doc__�int�__annotations__rr�dictrr�strr�float�boolr-r"r)r'rr,s��������-�-������K�����(-��d�(C�(C�(C�I�t�C��e��$�%�C�C�C��C��D������r)rc���eZdZdZd�Zed���dedeefd���Z ed���dee fd ���Z ed ���dede fd ���Z d edd fd�Zdeedd fd�Zde eefde eeffd�Zde eefde eeffd�Zde eefde eeffd�Zde eeffd�Zde eefde eeffd�Zdd�Zdd�Zdd�Zd S)�WaktuSolatServerz4MCP server implementation for Malaysia prayer times.c�h�ddd�|_|j|j|jd�|_t d���|_t��|_tj ��|_ tj ��|_ tjtjfD]}tj||j���dS)z3Initialize the server with configuration and tools.zMalaysia Prayer Time MCP Serverz0.2.0)�name�version)�get_prayer_times� list_zones�get_current_prayerr)rN)� server_info�handle_get_prayer_times�handle_list_zones�handle_get_current_prayer�toolsr� rate_limiter�set�active_requests�asyncio�Event�shutdown_eventr*�last_activity_time�signal�SIGTERM�SIGINT�_handle_shutdown_signal)r&�sigs r'�__init__zWaktuSolatServer.__init__Ks���6��, �, ��� !%� <��0�"&�"@�+ �+ �� � (�B�?�?�?���25�%�%���-4�]�_�_���"&�)�+�+����N�F�M�2� =� =�C� �M�#�t�;� <� <� <� <� =� =r)i)�ttl�zonerc��K�t4�d{V��}|�|���d{V��cddd���d{V��S#1�d{V��swxYwYdS)z4Fetch prayer times for a specific zone with caching.N)rr=�r&rS�https r'r=z!WaktuSolatServer.get_prayer_times_s������ 5� 5� 5� 5� 5� 5� 5�T��.�.�t�4�4�4�4�4�4�4�4� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5� 5���� 5� 5� 5� 5� 5� 5��>� A� Ai�Qc��K�t4�d{V��}|����d{V��cddd���d{V��S#1�d{V��swxYwYdS)z'Fetch all available zones with caching.N)r� get_zones)r&rVs r'rYzWaktuSolatServer.get_zoneses������ *� *� *� *� *� *� *�T����)�)�)�)�)�)�)�)� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *� *���� *� *� *� *� *� *s�=� A� Arc��K�t4�d{V��}|�|���d{V��cddd���d{V��S#1�d{V��swxYwYdS)z;Get the current prayer time status for a zone with caching.N)rr?rUs r'r?z#WaktuSolatServer.get_current_prayerks������ 7� 7� 7� 7� 7� 7� 7�T��0�0��6�6�6�6�6�6�6�6� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7� 7���� 7� 7� 7� 7� 7� 7rW�signumNc��tj|��j}t�d|�d���|j���dS)z#Handle shutdown signals gracefully.z Received z!, initiating graceful shutdown...N)rL�Signalsr;�logger�inforJrF)r&r[�_�sig_names r'rOz(WaktuSolatServer._handle_shutdown_signalqsM���>�&�)�)�.��� � �K��K�K�K�L�L�L� ����!�!�!�!�!r)c�p�|std���tjd|��std���dS)zValidate zone code format.zZone is requiredz^[A-Z]{3}\d{2}$z+Invalid zone format. Expected format: ABC12N)r�re�match)r&rSs r'�_validate_zonezWaktuSolatServer._validate_zonewsJ��� 6�!�"4�5�5� 5��x�*�D�1�1� Q�!�"O�P�P� P� Q� Qr)�argsc��pK� |�|�d����|�|d���d{V��}ddtjd�|D��d���d�giS#t $r8}t �d |����d t|��icYd}~Sd}~wt$r8}t � d |����d t|��icYd}~Sd}~wt$r8}t � d ��d d t|����icYd}~Sd}~wwxYw)a� Handle get_prayer_times tool request. Args: args: Dictionary containing tool arguments including 'zone' Returns: Dictionary containing prayer times data or error message Example: Input: {"zone": "SGR01"} Output: { "content": [{ "type": "text", "text": "[{"date": "2024-04-04", ...}]" }] } rSN�content�textc�6�g|]}|�����Sr")� model_dump)r#�pts r'r(z<WaktuSolatServer.handle_get_prayer_times.<locals>.<listcomp>�s ��D�D�D��R�]�]�_�_�D�D�Dr)���indent��typeriz&Validation error in get_prayer_times: �errorzAPI error in get_prayer_times: z$Unexpected error in get_prayer_times�Internal server error: ) re�getr=�json�dumpsrr^�warningr5rrr� Exception� exception)r&rf� prayer_times�es r'rAz(WaktuSolatServer.handle_get_prayer_times~s�����& A� � � ����� 0� 0� 1� 1� 1�!%�!6�!6�t�F�|�!D�!D�D�D�D�D�D�D�L�� &� $� �D�D�|�D�D�D�Q�!�!�!���� � ��� %� %� %� �N�N�G�A�G�G� H� H� H��S��V�V�$� $� $� $� $� $� $������ %� %� %� �L�L�>�1�>�>� ?� ?� ?��S��V�V�$� $� $� $� $� $� $������ A� A� A� � � �C� D� D� D��?�s�1�v�v�?�?�@� @� @� @� @� @� @����� A���sB�A.A3�3 D5�=-B0�*D5�0 D5�=-C0�*D5�0 D5�=-D0�*D5�0D5r`c��K� |����d{V��}d�d�t|d����D����}dd|d�giS#t$r8}t�d |����d t |��icYd}~Sd}~wt$r8}t�d ��d d t |����icYd}~Sd}~wwxYw) ag Handle list_zones tool request. Returns: Dictionary containing formatted list of zones or error message Example: Output: { "content": [{ "type": "text", "text": "SGR01: Gombak (Selangor) KUL01: Kuala Lumpur..." }] } N� c3�JK�|]}|j�d|j�d|j�d�V��dS)z: z (�)N)�coder;�negeri)r#rSs r'� <genexpr>z5WaktuSolatServer.handle_list_zones.<locals>.<genexpr>�sU����(�(���9�;�;�� �;�;�T�[�;�;�;�(�(�(�(�(�(r)c��|j|jfS)N)r�r�)�zs r'�<lambda>z4WaktuSolatServer.handle_list_zones.<locals>.<lambda>�s����1�6�8J�r))�keyrhrirpzAPI error in list_zones: rrzUnexpected error in list_zonesrs) rY�join�sortedrr^rrr5rxry)r&r`�zones�formatted_zonesr{s r'rBz"WaktuSolatServer.handle_list_zones�s7���� A��.�.�*�*�*�*�*�*�*�*�E�"�i�i�(�(�"�5�.J�.J�K�K�K�(�(�(���O���� I� I�J�K� K��� %� %� %� �L�L�8�Q�8�8� 9� 9� 9��S��V�V�$� $� $� $� $� $� $������ A� A� A� � � �=� >� >� >��?�s�1�v�v�?�?�@� @� @� @� @� @� @����� A���s0�AA� C�-B� C� C�-C� C�Cc��\K� |�|�d����|�|d���d{V��}ddtj|d���d�giS#t $r8}t �d|����d t|��icYd}~Sd}~wt$r8}t � d |����d t|��icYd}~Sd}~wt$r8}t � d ��d d t|����icYd}~Sd}~wwxYw) a� Handle get_current_prayer tool request. Args: args: Dictionary containing tool arguments including 'zone' Returns: Dictionary containing current prayer data or error message Example: Input: {"zone": "SGR01"} Output: { "content": [{ "type": "text", "text": "{"prayer": "Asr", "time": "16:27"...}" }] } rSNrhrirmrnrpz(Validation error in get_current_prayer: rrz!API error in get_current_prayer: z&Unexpected error in get_current_prayerrs) rertr?rurvrr^rwr5rrrrxry)r&rf�current_prayerr{s r'rCz*WaktuSolatServer.handle_get_current_prayer�s�����& A� � � ����� 0� 0� 1� 1� 1�#'�#:�#:�4��<�#H�#H�H�H�H�H�H�H�N��#�T�Z��q�-Q�-Q�-Q�R�R��� �� � %� %� %� �N�N�I�a�I�I� J� J� J��S��V�V�$� $� $� $� $� $� $������ %� %� %� �L�L�@�Q�@�@� A� A� A��S��V�V�$� $� $� $� $� $� $������ A� A� A� � � �E� F� F� F��?�s�1�v�v�?�?�@� @� @� @� @� @� @����� A���sB�A$A)�) D+�3-B&� D+�& D+�3-C&� D+�& D+�3-D&� D+�&D+c �V�dddddddd�idgd �d �d d did �d �ddddddd�idgd �d �giS)zGet list of available tools.rDr=z0Get prayer times for a specific zone in Malaysia�objectrS�stringz,The zone code (e.g., 'SGR01', 'KUL01', etc.))rq� description)rq� properties�required)r;r�� inputSchemar>z0List all available prayer time zones in Malaysia)rqr�r?z6Get the current prayer time status for a specific zoner")r&s r'�get_tools_listzWaktuSolatServer.get_tools_list�s��� �.�#U� (�"�(0�/]�%�%�'� &,�H� $� $� � �)�#U�,4�B�#G�#G��� 1�#[� (�"�(0�/]�%�%�'� &,�H� $� $� � �)"�$ �$ r)�requestc��K�|�d��}|�d��}|dkr d||jd|jdid�d�S|d krd||���d�S|d kr�|�d i���d��}|�d i���d i��}||jvrp |j||���d {V��}d||d�S#t$r@}t �d|����d|ddt|����d�d�cYd }~Sd }~wwxYwd|dd|��d�d�Sd|dd|��d�d�S)zHandle incoming MCP requests.�method�id� initializez2.0r;r<)r;r<� capabilities)�jsonrpcr��result� listTools�callTool�params� argumentsNzError handling tool i����zTool execution failed: )r��message)r�r�rri����zUnknown tool: zUnknown method: )rtr@r�rDrxr^ryr5)r&r�r�� request_id� tool_name� tool_argsr�r{s r'�handle_requestzWaktuSolatServer.handle_requests �������X�&�&���[�[��&�&� � �\� !� !� � � �,�V�4�#�/� �:�$&����� ��{� "� "�$�J�$�BU�BU�BW�BW�X�X� X� �z� !� !�� � �H�b�1�1�5�5�f�=�=�I�� � �H�b�1�1�5�5�k�2�F�F�I��D�J�&�&� �#8�4�:�i�#8��#C�#C�C�C�C�C�C�C�F�',�J�&�Q�Q�Q�� � � � ��$�$�%G�I�%G�%G�H�H�H�#(�(�$*�'I��Q���'I�'I�"�"������������� ����!� �"(�5Q�i�5Q�5Q�R�R��� � ��$�1L�F�1L�1L�M�M� � � s�!C6�6 E�5D;�5E�;Ec��K�t�d��|j����s t�d��t |d��rZt j��|jz }t�d|d�d���|dkrt�d��t j��|_n4#t$r'}t� d |����Yd }~nd }~wwxYwtj d ���d {V��|j�����d Sd S) z5Perform periodic health checks and keep server alive.z$Starting server health monitoring...zServer health check: runningrKzServer idle for z.1fz secondsrz$Server idle - performing maintenancezError in health check: N�) r^r_rJ�is_set�debug�hasattrr*rKrxrrrH�sleep)r&� idle_timer{s r'� health_checkzWaktuSolatServer.health_checkAs]����� � �:�;�;�;��%�,�,�.�.� $� <�� � �;�<�<�<��4�!5�6�6�L� $� � � �d�.E� E�I��L�L�!K�I�!K�!K�!K�!K�L�L�L�!�2�~�~�� � �$J�K�K�K� +/�)�+�+��'�'��� <� <� <�� � �:�q�:�:�;�;�;�;�;�;�;�;����� <�����-��#�#� #� #� #� #� #� #� #�1�%�,�,�.�.� $� $� $� $� $s�BC� D�D�Dc �� K�t�d|jd�d|jd�d���tj|�����}|j�|��|�|jj ��t�d��d}d}|j � ���s� tj tj ���d tjj��d � ���d {V��}|���}|sBt�d ��|d z }||krt�d��d}��d}t+j��|_ t/j|��}n_#t.j$rM}t�d|����t7t/jddi��d���Yd }~��Jd }~wwxYw|�di���dd��}|j�|��sDt�d|����t7t/jddi��d������tj|� |����}|j�|��|�|jj ��|�d {V��} t7t/j| ��d����nh#tB$r�t�d��tj"d���d {V�� tj�#��rY���t�d��|j �$��Yn�#tJ$r7t�d��|j �$��YYn�wxYwtj&$rt�d��YnetJ$rY}t�'d��t7t/jddtQ|����i��d���Yd }~nd }~wwxYw|j � �����t�d �� |�)���d {V��t�d!��d S#t�d!��wxYw)"z'Start the server using stdio transport.z Starting r;z vr<z...z(Starting main request processing loop...r�Ni,)�timeoutzReceived empty request liner z=Too many consecutive empty requests, but keeping server alivezInvalid JSON request: rrzInvalid JSON requestT)�flushr�r�defaultz Rate limit exceeded for client: zRate limit exceededz3Received EOF, waiting for potential reconnection...� z4No stdin available after EOF, initiating shutdown...z5Stdin not available after EOF, initiating shutdown...z0Input timeout occurred, but keeping server alivezUnexpected server error�Server error: z&Initiating server shutdown sequence...z0All resources released, server shutdown complete)*r^r_r@rH� create_taskr�rG�add�add_done_callback�discardrJr��wait_for�get_event_loop�run_in_executor�sys�stdin�readline�stripr�rwr*rKru�loads�JSONDecodeErrorrr�printrvrtrEr-r��EOFErrorr��isattyrFrx� TimeoutErrorryr5�_cleanup) r&�health_check_task�connection_idle_count�max_idle_count� request_liner�r{r� request_task�responses r'�runzWaktuSolatServer.run^sO����� � � T��(��0� T� T�D�4D�Y�4O� T� T� T� � � � $�/��0A�0A�0C�0C�D�D�� �� � �!2�3�3�3��+�+�D�,@�,H�I�I�I�� � �>�?�?�?� !�����%�,�,�.�.�I T�H T�&-�%5��*�,�,�<�<�T�3�9�CU�V�V��&�&�&� � � � � � � � ,�1�1�3�3� �#���L�L�!>�?�?�?�)�Q�.�)�,�~�=�=����[����12�-��)*�%�+/�)�+�+��'��"�j��6�6�G�G���+�����L�L�!=�!�!=�!=�>�>�>��$�*�g�/E�%F�G�G�t�T�T�T�T��H�H�H�H��������� $�K�K��"�5�5�9�9�+�y�Q�Q� ��(�3�3�I�>�>���N�N�#Q�i�#Q�#Q�R�R�R��$�*�g�/D�%E�F�F�d�S�S�S�S�� '�2�4�3F�3F�w�3O�3O�P�P� ��$�(�(��6�6�6��.�.�t�/C�/K�L�L�L�!-�-�-�-�-�-�-���d�j��*�*�$�7�7�7�7�7��� � � �� � �Q�R�R�R��m�B�'�'�'�'�'�'�'�'�'� ��y�'�'�)�)�� ��� � �R�����+�/�/�1�1�1���� �����K�K� W�X�X�X��'�+�+�-�-�-��E�E������'� Q� Q� Q�� � �O�P�P�P�P�P�� T� T� T�� � �!:�;�;�;��d�j�'�+D�C��F�F�+D�+D�!E�F�F�d�S�S�S�S�S�S�S�S�S����� T����O�%�,�,�.�.�I T�\ � � �<�=�=�=� L��-�-�/�/� !� !� !� !� !� !� !� �K�K�J� K� K� K� K� K��F�K�K�J� K� K� K� K���s��B'L�:L�F*�)L�*H�9AH�;L�H�B L�B L�>R�N3�>3N3�3=O4�0R�3O4�4+R�! R�*AQ>�>R�;S1�1T c��K� |jrLt�dt|j���d���t j|jddi��d{V��t td��r2tjr(tj� ���d{V��dSdSdS#t$r(}t� d|����Yd}~dSd}~wwxYw)z#Clean up resources during shutdown.z Waiting for z active requests...�return_exceptionsTN�_clientzError during cleanup: ) rGr^r_r+rH�gatherr�rr��acloserxrr)r&r{s r'r�zWaktuSolatServer._cleanup�s���� 7��#� T�� � �Q�3�t�';�#<�#<�Q�Q�Q�����n�d�&:�S�d�S�S�S�S�S�S�S�S�S��v�y�)�)� .�f�n� .��n�+�+�-�-�-�-�-�-�-�-�-�-�-� .� .� .� .��� 7� 7� 7� �L�L�5�!�5�5� 6� 6� 6� 6� 6� 6� 6� 6� 6����� 7���s�BB"�" C�,C�C�rN)r.r/r0r1rQrr5rrr=rrYrr?r2rOr rerrArBrCr�r�r�r�r�r"r)r'r9r9Hsp������>�>�=�=�=�( �V�����5�3�5�4� �3D�5�5�5���5�  �V�����*��d��*�*�*���*�  �V��^�^�^�7�S�7�T�7�7�7��^�7� "�c�"��"�"�"�"� Q�8�C�=�Q�T�Q�Q�Q�Q�)A�$�s�C�x�.�)A�T�#�s�(�^�)A�)A�)A�)A�VA��c�3�h��A�D��c��N�A�A�A�A�:$A�D��c��N�$A�t�C�QT�H�~�$A�$A�$A�$A�L& ��S�#�X��& �& �& �& �P+ �D��c��N�+ �t�C��H�~�+ �+ �+ �+ �Z$�$�$�$�:aL�aL�aL�aL�F 7� 7� 7� 7� 7� 7r)r9rc��t�d��d}|�r dtjvr$t jtjd��ddlm}t�d�� tt��j j dz dz }|� ��r:t�d ��tj�|��nd t�d ��tj���n*#t $rt�d ��YnwxYwnf#t"$rY}t�d |����t'd |��tj� ��tjd��Yd}~nd}~wwxYwt-��}t/j��}t/j|��|�|�����t�d��d}n�#t8$rt�d��d}Yn�t:$rMt�d��t=jd��t�d��d}Yn~t@$rr}t�!d��t'd|��tj� ��t=jd��t�d��d}Yd}~nd}~wwxYw|��t�d��tjd��dS)zd Main entry point. Sets up the server and handles the main event loop and error cases. z/Initializing Malaysia Prayer Time MCP Server...Tzwaktu_solat.configrNz!Config module loaded successfullyrzclaude_desktop.yamlz'Loading Claude Desktop configuration...z3Loading configuration from environment variables...z8No configuration file found, using default configurationzConfiguration error: )�filer zServer completed its runFzReceived keyboard interruptzReceived EOF from main inputr�z#Attempting to restart the server...zFatal server errorr�z/Attempting to restart the server after error...z"Server is shutting down completely)"r^r_r��modules� importlib�reload�waktu_solat.configrr�r�__file__�parent�exists�waktu_solat_config�load_from_file� load_from_env�FileNotFoundError�EnvironmentErrorrrr��stderr�exitr9rH�new_event_loop�set_event_loop�run_until_completer��KeyboardInterruptr�r*r�rxry)�should_restart�waktu_solat_config_module�desktop_config_pathr{�server�loops r'�mainr��s[��  �K�K�A�B�B�B��N� �<"�; "�#�s�{�2�2�� ���-A�!B�C�C�C� B� B� B� B� B� B� �L�L�<� =� =� =� ���N�N�)�0�8�;�>S�S�$�'�-�-�/�/� ��K�K� I�J�J�J�&�-�<�<�=P�Q�Q�Q�Q��� � �Q����+�1�?�?�A�A�A�A��,����� � �V������������$� � � �� � �8�Q�8�8�9�9�9��1�a�1�1�� �C�C�C�C���� � � � � � � � ����� ���� &�'�'�F��)�+�+�D� � "�4� (� (� (� � #� #�F�J�J�L�L� 1� 1� 1� �K�K�2� 3� 3� 3�"�N�N�� � #� #� #� �K�K�5� 6� 6� 6�"�N�N�N�� "� "� "� �K�K�6� 7� 7� 7� �J�q�M�M�M� �K�K�=� >� >� >�!�N�N�N�� "� "� "� � � �1� 2� 2� 2� �&�1�&�&�S�Z� 8� 8� 8� 8� �J�q�M�M�M� �K�K�I� J� J� J�!�N�N�N�N�N�N�����  "����m �<"�| �K�K�4�5�5�5��H�Q�K�K�K�K�Ksw�AH*�4A2E �'8D �E � $E�E �E�E � H*� F.�AF)�$H*�)F.�.A;H*�*&L"�AL"�' L"�0A(L�L"�__main__r�))r1rH�loggingr�rurcrLr�r*� dataclassesrr�pathlibr�typingrrrr r r r �rr�rrr�modelsrr�cacher� basicConfig�INFO� getLoggerr.r^rr9r�r"r)r'�<module>r�s�����"������������ � � � � � � � � � � � � � � � � � � � �(�(�(�(�(�(�(�(�������F�F�F�F�F�F�F�F�F�F�F�F�F�F�F�F�F�F�*�*�*�*�*�*�5�5�5�5�5�5�5�5�5�5�%�%�%�%�%�%�%�%���������� �,�U����� �� �8� $� $�� �������� ���6F7�F7�F7�F7�F7�F7�F7�F7�R J�J�J�J�Z �z����D�F�F�F�F�F��r)

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/amanasmuei/mcp-server-malaysia-prayer-time'

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