Skip to main content
Glama

literateMCP

sqlite-paper-fastmcp-server.cpython-311.pyc70.8 kB
� ��og%���j �ddlmZddlZddlZddlZddlZddlmZmZm Z m Z m Z m Z m Z ddlmZddlmZddlZed��Zdejvr ed���eejd��ZGd �d ��ZGd �d ��ZGd �d��ZGd�d��ZGd�d��Zdee eeeefdedee e eeeffd�Zde eeefdedeeee ffd�Ze��� d7dede ee dede deeee ff d���Z!e���deefd ���Z"e���d!edeeeeffd"���Z#e���d!edeee ffd#���Z$e���deee ffd$���Z%e���deee ffd%���Z&e���dee eeeee eeeffdeeee ffd&���Z'e���d'ee eeeeeefdeeee ffd(���Z(e���d)ee eeeeefdeeee ffd*���Z)e���d+ee eeeeeefdeeee ffd,���Z*e���d-ee eeeeeee efdeeee ffd.���Z+e���dee eeeefdeeee ffd/���Z,e���d0ee eeeeee ee efdeeee ffd1���Z-e���d2ee eeeeefdeeee ffd3���Z.e���d4ee ee ee efdeeee ffd5���Z/e0d6kre�1��dSdS)8�)�PathN)�List�Dict�Any�Optional�Tuple�Set�Union)�FastMCP)�datetimezSource Manager�SQLITE_DB_PATHz/SQLITE_DB_PATH environment variable must be setc��eZdZdZhd�ZdS)�SourceIdentifiersz*Defines valid identifier types for sources>�doi�url�isbn�arxiv�semantic_scholarN��__name__� __module__� __qualname__�__doc__� VALID_TYPES���j/Users/yuzongmin/Documents/Obsidian Vault/0_Desktop/.uv/sqlite-paper-server/sqlite-paper-fastmcp-server.pyrrs%������4�4����K�K�Krrc��eZdZdZhd�ZdS)� SourceTypeszDefines valid source types>�blog�book�paper�video�webpageNrrrrrr s������$�$�?�?�?�K�K�Krrc��eZdZdZhd�ZdS)� SourceStatusz"Defines valid source status values>�unread�reading�archived� completedN)rrrr� VALID_STATUSrrrr&r&$s������,�,�A�A�A�L�L�Lrr&c�*�eZdZdZdefd�Zd�Zd�ZdS)�SQLiteConnectionz/Context manager for SQLite database connections�db_pathc�"�||_d|_dS�N)r.�conn)�selfr.s r�__init__zSQLiteConnection.__init__*s���� ��� � � rc��tjt|j����|_tj|j_|jSr0)�sqlite3�connect�strr.r1�Row� row_factory)r2s r� __enter__zSQLiteConnection.__enter__.s1���O�C�� �$5�$5�6�6�� � '� �� ���y�rc�J�|jr|j���dSdSr0)r1�close)r2�exc_type�exc_val�exc_tbs r�__exit__zSQLiteConnection.__exit__3s.�� �9� � �I�O�O� � � � � � � rN)rrrrrr3r:r@rrrr-r-(sS������9�9���������� ����rr-c��eZdZdZhd�ZdS)�EntityRelationsz-Defines valid relation types for entity links>�applies�extends� critiques� discusses� evaluates� introducesNrrrrrBrB7s%������7�7����K�K�KrrB�sourcesr.�returnc ��g}t|��5}|���}|D�]\}}}}|tjvrt dtj�����|t jvrt dt j�����|�d|d|��|g��|���} | r|�| dgf����|�d|d|� ���d�g��d�|� ��D��} |�d | f���� d d d ��n #1swxYwY|S) aF Bulk search for multiple sources simultaneously while maintaining consistent return format. Args: sources: List of tuples, each containing: - title: Source title - type: Source type - identifier_type: Type of identifier - identifier_value: Value of the identifier db_path: Path to SQLite database Returns: List of tuples, each containing: - UUID of exact match if found by identifier (else None) - List of potential matches by title/type (empty if exact match found) z%Invalid source type. Must be one of: z)Invalid identifier type. Must be one of: z� SELECT id FROM sources WHERE type = ? AND json_extract(identifiers, ?) = ? �$.�idz� SELECT id, title, identifiers FROM sources WHERE type = ? AND LOWER(title) LIKE ? �%c�b�g|],}|d|dtj|d��d���-S)rM�title� identifiers)rMrPrQ)�json�loads��.0�rows r� <listcomp>z"search_sources.<locals>.<listcomp>�sQ��!�!�!� � �d�)� ��\�#'�:�c�-�.@�#A�#A���!�!�!rN) r-�cursorrr� ValueErrorr�execute�fetchone�append�lower�fetchall) rIr.�resultsr1rXrP�type_�identifier_type�identifier_value�result�potential_matchess r�search_sourcesreDs���(�G� �'� "� "�16�d�������@G�- 6�- 6� ;�E�5�/�+;��K�3�3�3� �!b��I`�!b�!b�c�c�c��&7�&C�C�C� �!l�M^�Mj�!l�!l�m�m�m� �N�N�� �&�_�&�&� ��  � � ��_�_�&�&�F�� �����t� �b�1�2�2�2�� �N�N�� �$�E�K�K�M�M�$�$�$��  � � �!�!� "�?�?�,�,� !�!�!� � �N�N�D�"3�4� 5� 5� 5� 5�[- 6� 16�16�16�16�16�16�16�16�16�16�16����16�16�16�16�f �Ns�D(E�E �E �uuidsc ���t|t��r|g}|sgSt|��5}|���}d�dt |��z��}|�d|�d�|��|���}t |��t |��kr?d�|D����fd�|D��}tdd�|�������g}|D]O}|d |d |d |d tj |d ��d�} |� | ���P|�d|�d�|��i} |���D]D} | d} | | vrg| | <| | � | d| d| dd����E|�d|�d�|��i} |���D]D} | d} | | vrg| | <| | � | d| d| dd����E|D]<} | d } | � | g��| d<| � | g��| d<�=|cddd��S#1swxYwYdS)a Get complete information about multiple sources by their UUIDs. Args: uuids: Single UUID string or list of source UUIDs db_path: Path to SQLite database Returns: List of dictionaries, each containing source information: - Basic info (id, title, type, status, identifiers) - Notes (list of {title, content, created_at}) - Entity links (list of {entity_name, relation_type, notes}) Raises: ValueError: If any source UUID is not found �,�?zk SELECT id, title, type, status, identifiers FROM sources WHERE id IN (z ) c��h|] }|d�� S�rMr)rU�sources r� <setcomp>z&get_sources_details.<locals>.<setcomp>�s��<�<�<�&����<�<�<rc���g|]}|�v�|�� Srr)rU�uuid� found_idss �rrWz'get_sources_details.<locals>.<listcomp>�s#���K�K�K�D�T��5J�5J�4�5J�5J�5JrzSources not found for UUIDs: �, rMrP�type�statusrQ)rMrPrrrsrQz} SELECT source_id, note_title, content, created_at FROM source_notes WHERE source_id IN (z/) ORDER BY created_at DESC � source_id� note_title�content� created_at)rPrvrwz� SELECT source_id, entity_name, relation_type, notes FROM source_entity_links WHERE source_id IN (� entity_name� relation_type�notes)rxryrz� entity_linksN) � isinstancer7r-rX�join�lenrZr^rYrRrSr\�get)rfr.r1rX� placeholdersrI� missing_idsr_rl� source_data�notes_by_sourcerVrt�links_by_sourcerps @r�get_sources_detailsr��s����$�%�������� ��� � �'� "� "�J�d��������x�x��c�%�j�j� 0�1�1� ���� �'� � � ��  � � � �/�/�#�#�� �w�<�<�3�u�:�:� %� %�<�<�G�<�<�<�I�K�K�K�K�E�K�K�K�K��U�T�Y�Y�{�=S�=S�U�U�V�V� V���� (� (�F��T�l�����v�� ��*�#�z�&��*?�@�@� ��K� �N�N�;� '� '� '� '� ��� �".� � � � �  � � ����?�?�$�$� � �C��K�(�I���/�/�-/�� �*� �I� &� -� -��\�*��y�>�!�,�/�/�/� � � � � ��� �".� � � ��  � � ����?�?�$�$� � �C��K�(�I���/�/�-/�� �*� �I� &� -� -�"�=�1�!$�_�!5��W��/�/� � � � �#� M� M�K�#�D�)�I�#2�#6�#6�y�"�#E�#E�K�� �*9�*=�*=�i��*L�*L�K�� '� '��UJ�J�J�J�J�J�J�J�J�J�J�J����J�J�J�J�J�Js�II?�?J�JT���query�params� fetch_all� row_limitc�n� �t���stdt�����|���}|�d��r|dd����}dt dt fd�}||��rtd���|���� t� fd �d D����std ���|pg}tt��5}|� ��} d � vr|�d |��}|� ||��|r|� ��}n|���g}d�|D��cddd��S#tj$r$}tdt |�������d}~wwxYw#1swxYwYdS)aExecute a query on the Literature database. Args: query: SELECT SQL query to execute params: Optional list of parameters for the query fetch_all: If True, fetches all results. If False, fetches one row. row_limit: Maximum number of rows to return (default 1000) Returns: List of dictionaries containing the query results �"Literature database not found at: �;N������sqlrJc�b�d}d}|D]'}|dkr|s| }�|dkr|s| }�|dkr|s|sdS�(dS)NF�'�"r�Tr)r��in_single_quote�in_double_quote�chars r�contains_multiple_statementsz0read_query.<locals>.contains_multiple_statementssg������� � �D��s�{�{�?�{�&5�"5�������_��&5�"5�������_��_���t�t���urz'Multiple SQL statements are not allowedc3�B�K�|]}��|��V��dSr0)� startswith)rU�prefix� query_lowers �r� <genexpr>zread_query.<locals>.<genexpr>"s1�����O�O�&�{�%�%�f�-�-�O�O�O�O�O�Or)�select�withzCOnly SELECT queries (including WITH clauses) are allowed for safety�limitz LIMIT c�0�g|]}|�t|����Sr0��dictrTs rrWzread_query.<locals>.<listcomp>5s��D�D�D�#�C�O�D��I�I�O�O�Or�SQLite error: )�DB_PATH�exists�FileNotFoundError�strip�endswithr7�boolrYr]�anyr-rXrZr^r[r5�Error) r�r�r�r�r�r1rXr_�er�s @r� read_queryr��s+���$ �>�>� � �P�� N�W� N� N�O�O�O� �K�K�M�M�E� �~�~�c���#��c�r�c� � � �"�"�� �#� �$� � � � �$�#�E�*�*�D��B�C�C�C��+�+�-�-�K� �O�O�O�O�<N�O�O�O� O� O�`��^�_�_�_� �\�r�F� �'� "� "�8�d������� 8��k�)�)� �4�4��4�4�� �N�N�5�&� )� )� )�� .� �/�/�+�+���!�?�?�,�,�-��D�D��D�D�D�8�8�8�8�8�8�8�8�� �}� 8� 8� 8��6�c�!�f�f�6�6�7�7� 7����� 8����!8�8�8�8����8�8�8�8�8�8s1�9F*�AE4�4F'�F"�"F'�'F*�*F.�1F.c��t���stdt�����tt��5}|���} |�d��d�|���D��cddd��S#tj$r$}tdt|�������d}~wwxYw#1swxYwYdS)znList all tables in the Literature database. Returns: List of table names in the database r�z SELECT name FROM sqlite_master WHERE type='table' ORDER BY name c��g|] }|d�� S)�namerrTs rrWzlist_tables.<locals>.<listcomp>Ns��=�=�=�C�C��K�=�=�=rNr�) r�r�r�r-rXrZr^r5r�rYr7)r1rXr�s r� list_tablesr�:s'�� �>�>� � �P�� N�W� N� N�O�O�O� �'� "� "� 8�d������� 8� �N�N�� � � � >�=�6�?�?�+<�+<�=�=�=� 8� 8� 8� 8� 8� 8� 8� 8���}� 8� 8� 8��6�c�!�f�f�6�6�7�7� 7����� 8���� 8� 8� 8� 8���� 8� 8� 8� 8� 8� 8s0�C�2B�C �)C�C � C�C�C� table_namec�B�t���stdt�����tt��5}|���} |�d|g��|���std|�d����|�d|�d���|���}d�|D��cddd��S#tj $r$}td t|�������d}~wwxYw#1swxYwYdS) a�Get detailed information about a table's schema. Args: table_name: Name of the table to describe Returns: List of dictionaries containing column information: - name: Column name - type: Column data type - notnull: Whether the column can contain NULL values - dflt_value: Default value for the column - pk: Whether the column is part of the primary key r��k SELECT name FROM sqlite_master WHERE type='table' AND name=? �Table '�' does not exist�PRAGMA table_info(�)c�,�g|]}t|����Srr�rTs rrWz"describe_table.<locals>.<listcomp>vs��1�1�1�#�D��I�I�1�1�1rNr�) r�r�r�r-rXrZr[rYr^r5r�r7)r�r1rX�columnsr�s r�describe_tabler�Ss��� �>�>� � �P�� N�W� N� N�O�O�O� �'� "� "�8�d������� 8� �N�N���� � � � �?�?�$�$� I� �!G�:�!G�!G�!G�H�H�H� �N�N�=� �=�=�=� >� >� >��o�o�'�'�G�1�1��1�1�1�#8�8�8�8�8�8�8�8��&�}� 8� 8� 8��6�c�!�f�f�6�6�7�7� 7����� 8����'8�8�8�8����8�8�8�8�8�8s1�D�A6C�D�-D � D�D�D�Dc��t���stdt�����tt��5}|���} |�d|g��|���std|�d����|�d|����|���d}|�d��|���d}|�d |�d ���t|� ����}||||d �cd d d ��S#tj $r$}td t|�������d }~wwxYw#1swxYwYd S)z�Get statistics about a table, including row count and storage info. Args: table_name: Name of the table to analyze Returns: Dictionary containing table statistics r�r�r�r��SELECT COUNT(*) as count FROM �countzPRAGMA page_sizerr�r�)r�� row_count� column_count� page_sizeNr�) r�r�r�r-rXrZr[rYr~r^r5r�r7)r�r1rXr�r�r�r�s r�get_table_statsr�{s��� �>�>� � �P�� N�W� N� N�O�O�O� �'� "� "�8�d������� 8� �N�N���� � � � �?�?�$�$� I� �!G�:�!G�!G�!G�H�H�H� �N�N�H�J�H�H� I� I� I����)�)�'�2�I� �N�N�-� .� .� .����)�)�!�,�I� �N�N�=� �=�=�=� >� >� >��&�/�/�+�+�,�,�G�)�&� '�&� ��/8�8�8�8�8�8�8�8��<�}� 8� 8� 8��6�c�!�f�f�6�6�7�7� 7����� 8����=8�8�8�8����8�8�8�8�8�8s1�E=�CE�E:�E5�5E:�:E=�=F�Fc�b�t���stdt�����tt��5}|���} t j�t��}|�d��|� ��d}|�d��|� ��d}i}|�d��|� ��D]?}|d}|�d|����|� ��d||<�@||||tt��d �cd d d ��S#tj $r$}td t|�������d }~wwxYw#1swxYwYd S) z�Get overall database information and statistics. Returns: Dictionary containing database statistics and information r�z� SELECT COUNT(*) as count FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' r�zSELECT sqlite_version()rz� SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' r�r�)�database_size_bytes� table_count�sqlite_version�table_row_counts�pathNr�)r�r�r�r-rX�osr��getsizerZr[r^r7r5r�rY) r1rX�db_sizer��version�tablesrVr�r�s r�get_database_infor��s�� �>�>� � �P�� N�W� N� N�O�O�O� �'� "� "�(8�d�������& 8��g�o�o�g�.�.�G� �N�N�� � � � !�/�/�+�+�G�4�K� �N�N�4� 5� 5� 5��o�o�'�'��*�G��F� �N�N�� � � � ���(�(� @� @�� ��[� ����L� �L�L�M�M�M�%+�_�_�%6�%6�w�%?��z�"�"�(/�*�")�$*��G� � � ��?(8�(8�(8�(8�(8�(8�(8�(8��N�}� 8� 8� 8��6�c�!�f�f�6�6�7�7� 7����� 8����O(8�(8�(8�(8����(8�(8�(8�(8�(8�(8s1�F$�DE.�.F!�=F�F!�!F$�$F(�+F(c�"�t���stdt�����tt��5}|���} t j�t��}|�d��t j�t��}d||||z d�cddd��S#tj $r$}tdt|�������d}~wwxYw#1swxYwYdS)z�Optimize the database by running VACUUM command. This rebuilds the database file to reclaim unused space. Returns: Dictionary containing the operation results r��VACUUM�success)rs�size_before_bytes�size_after_bytes�space_saved_bytesNr�) r�r�r�r-rXr�r�r�rZr5r�rYr7)r1rX� size_before� size_afterr�s r�vacuum_databaser��sH�� �>�>� � �P�� N�W� N� N�O�O�O� �'� "� "�8�d������� 8��'�/�/�'�2�2�K� �N�N�8� $� $� $������1�1�J�$�%0�$.�%0�:�%=� ��8�8�8�8�8�8�8�8��&�}� 8� 8� 8��6�c�!�f�f�6�6�7�7� 7����� 8����'8�8�8�8����8�8�8�8�8�8s1�D�A&C�D�C<�<D�D�D� Dc �R���t���stdt�����|sgSd�|D��}t|t��}g}g}g}t ||��D�]k\\}}}} �\} } | rv t | t��d} |�dd| d���n?#t$r2} |�ddt| ����d���Yd } ~ nd } ~ wwxYw��| r|�dd | d �����ttj ����}|| i}|�|||tj |��d ����r^t�fd �dD����s|�dd|�d�d�����.|�|�d�dd���|�d|d�����m|�rItt��5}|���} |�d|��|r|�d|��|���d�|D��}t |t��}t%|��D]I\}}|�d��dkr+|d�t)�fd�|D����}d|d�||<�JnJ#t*j$r8} |���t1dt| �������d } ~ wwxYw d d d ��n #1swxYwY|S)aAdd multiple new sources with duplicate checking in a single transaction. Args: sources: List of tuples, each containing: - title: Source title - type: Source type (paper, webpage, book, video, blog) - identifier_type: Type of identifier - identifier_value: Value of the identifier - initial_note: Optional dict with 'title' and 'content' keys Returns: List of dictionaries containing operation results for each source: - On success: {"status": "success", "source": source_details} - On duplicate: {"status": "error", "message": "...", "existing_source": details} - On potential duplicate: {"status": "error", "message": "...", "matches": [...]} �Database not found at: c�&�g|]\}}}}}||||f��Srr�rUrPr`�id_type�id_value�_s rrWzadd_sources.<locals>.<listcomp>�;����� .�E�5�'�8�Q� ��w��)���rr�errorzSource already exists�rs�message�existing_source�"Error retrieving existing source: �rsr�Nz]Potential duplicates found. Please verify or use add_identifier if these are the same source.�rsr��matches)rMrPrrrQc3� �K�|]}|�vV�� dSr0r)rU�k� initial_notes �rr�zadd_sources.<locals>.<genexpr>Rs(�����G�G�Q�q�L�(�G�G�G�G�G�Gr)rPrvz(Invalid initial note format for source 'r�rPrv�rtrurv�pending�rsrtz� INSERT INTO sources (id, title, type, identifiers) VALUES (:id, :title, :type, :identifiers) �� INSERT INTO source_notes (source_id, note_title, content) VALUES (:source_id, :note_title, :content) c��g|] }|d�� Srkr)rU�ss rrWzadd_sources.<locals>.<listcomp>zs��#D�#D�#D��A�d�G�#D�#D�#Drrsrtc3�4�K�|]}|d�k�|V��dS�rMNr�rUr�rts �rr�zadd_sources.<locals>.<genexpr>�s2�����-_�-_�A�!�D�'�U^�J^�J^�a�J^�J^�J^�J^�-_�-_rr��rsrl�Database error: )r�r�r�re�zipr�r\� Exceptionr7ro�uuid4rR�dumps�allr-rX� executemany�commit� enumerater�nextr5r��rollbackrY)rI� search_inputs�search_resultsr_�sources_to_add� notes_to_addrPr`r�r��uuid_strr�r�r��new_idrQr1rX�added_source_ids� added_sources�irc�source_detailsr�rts @@r� add_sourcesr s�����( �>�>� � �E�� C�'� C� C�D�D�D� ��� ���29����M� $�M�7�;�;�N��G��N��L�PS�T[�]k�Pl�Pl�7 �7 �L�7���w��,�9L�(�G� � � �"5�h��"H�"H��"K�����%�6�'6� � ������ � � � ����%�L�C��F�F�L�L� � ������������� ���� � � � �N�N�!�z�"��� � � � ��T�Z�\�\�"�"����)� ��������:�k�2�2�  � � � � � � ��G�G�G�G�2F�G�G�G�G�G� ����%�R�%�R�R�R� � ����� � � �#�*�7�3�'� �2�!�!� � � � ����� � � � � � � �#>� �g� &� &�" >�$��[�[�]�]�F� >��"�"�$�$�%�%�%�  �'��&�&�(�&�'�'�'� � � � � � �$E�#D�^�#D�#D�#D� � 3�4D�g� N� N� �"+�7�!3�!3���I�A�v��z�z�(�+�+�y�8�8�$*�;�$7� �)-�-_�-_�-_�-_��-_�-_�-_�)_�)_��&/�&4�&�&��� �� ���=� >� >� >�� � ���� �!<�C��F�F�!<�!<�=�=�=����� >�����/" >�" >�" >�" >�" >�" >�" >�" >�" >�" >�" >����" >�" >�" >�" >�H �NsO�4B5�5 C1�?(C,�,C1�2L�B<K�L�L �3L�L � L�L �#L � source_notesc���t���stdt�����|sgSd�|D��}t|t��}g}g}g}t ||��D]�\\}}}} } } \} } | s5| r|�dd| d���n|�ddd����F|�| | | d���|�| ��|�d | d �����|�r t t��5}|���} d �d t|��z��}|� d d �d�|D�����d�d�|D����d�|� ��D��}g}t|��D]5\}}|d|df|vr ddd�||<� |�|���6|r�|� d|��|���tt!t#|����t��}t|��D]I\}}|�d��d kr+|d�t'�fd�|D����}d|d�||<�JnJ#t(j$r8}|���t/dt1|�������d}~wwxYwddd��n #1swxYwY|S)aTAdd notes to multiple sources in a single transaction. Args: source_notes: List of tuples, each containing: - title: Source title - type: Source type - identifier_type: Type of identifier - identifier_value: Value of the identifier - note_title: Title for the new note - note_content: Content of the note Returns: List of dictionaries containing operation results for each note addition: - On success: {"status": "success", "source": source_details} - On source not found: {"status": "error", "message": "Source not found"} - On ambiguous source: {"status": "error", "message": "...", "matches": [...]} - On duplicate note: {"status": "error", "message": "Note with this title already exists for this source"} r�c�(�g|]\}}}}}}||||f��Srrr�s rrWzadd_notes.<locals>.<listcomp>�s=����� 1�E�5�'�8�Q�� ��w��)���rr��;Multiple potential matches found. Please verify the source.r��Source not foundr�r�r�r�rhriz� SELECT source_id, note_title FROM source_notes WHERE (source_id, note_title) IN (c3�K�|]}dV��dS�z(?,?)Nr�rUr�s rr�zadd_notes.<locals>.<genexpr>�s"����>�>�A�x�>�>�>�>�>�>r�) c�8�g|]}|d|dfD]}|���S�rtrur)rU�note�vals rrWzadd_notes.<locals>.<listcomp>�sL����� � $�[� 1�4� �3E�F��������rc�.�h|]}|d|df��SrrrTs rrmzadd_notes.<locals>.<setcomp>�s7��"�"�"����%�s�<�'8�9�"�"�"rrtruz3Note with this title already exists for this sourcer�rsc3�4�K�|]}|d�k�|V��dSr�rr�s �rr�zadd_notes.<locals>.<genexpr>�3�����0c�0c�q�a�PT�g�Yb�Nb�Nb��Nb�Nb�Nb�Nb�0c�0crr�r�r�N)r�r�r�rer�r\r-rXr}r~rZr^r�r�r�r��list�setrr�r5r�r�rYr7)r r�rr_r� source_idsrPr`r�r�ru� note_contentrr�r1rXr��existing_notes�filtered_notesrrrrc� source_detailr�rts @r� add_notesr!�sD���, �>�>� � �E�� C�'� C� C�D�D�D� ��� ���5A����M� $�M�7�;�;�N��G��L��J�\_�`l�n|�\}�\}� � �X�C���w��*�l�EX�h�PW�� �� ����%�\�&� � ����� ���%�1� � ���� ����!�$�#� � � � � � ���(�#�#�#�����!� � � � � � � �9>� �g� &� &�8 >�$��[�[�]�]�F�6 >�"�x�x��c�,�.?�.?�(?�@�@� ���� ��h�h�>�>��>�>�>�>�>�  � � � ��$0���� ���"�"�%���0�0�"�"�"�� "$��(��6�6�4�4�G�A�t��[�)�4� �+=�>�.�P�P�&-�'\�&�&��� � � '�-�-�d�3�3�3�3�"���&�&�(�(�)�)�)� �K�K�M�M�M�&9��c�*�o�o�9N�9N�PW�%X�%X�N�&/�w�%7�%7��� ��6�!�:�:�h�/�/�9�<�<�(.�{�(;�I�,0�0c�0c�0c�0c�N�0c�0c�0c�,c�,c�M�*3�*7�*�*�G�A�J���� �=� >� >� >�� � ���� �!<�C��F�F�!<�!<�=�=�=����� >����m8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >����8 >�8 >�8 >�8 >�t �Ns7�K=�'E?J'�&K=�'K.�63K)�)K.�.K=�=L�L� source_statusc����t���stdt�����|sgS|D]2\}}}}}|tjvrt dtj������3d�|D��}t |t��}g}g}g}t||��D]�\\}} } } } \} }| s5|r|�dd|d���n|�ddd����E|�| | d ���|�| ��|�d | d �����|�r?tt��5}|� ��} |� d |��|� ��ttt|����t��}t!|��D]I\}}|�d ��d kr+|d�t%�fd�|D����}d|d�||<�JnJ#t&j$r8}|���t dt-|�������d}~wwxYw ddd��n #1swxYwY|S)aUpdate status for multiple sources in a single transaction. Args: source_status: List of tuples, each containing: - title: Source title - type: Source type - identifier_type: Type of identifier - identifier_value: Value of the identifier - new_status: New status value Returns: List of dictionaries containing operation results for each status update: - On success: {"status": "success", "source": source_details} - On source not found: {"status": "error", "message": "Source not found"} - On ambiguous source: {"status": "error", "message": "...", "matches": [...]} - On invalid status: {"status": "error", "message": "Invalid status. Must be one of: ..."} r�z Invalid status. Must be one of: c�&�g|]\}}}}}||||f��Srrr�s rrWz!update_status.<locals>.<listcomp>/r�rr�r r�rr�)rMrsr�r�z� UPDATE sources SET status = :status WHERE id = :id rsrtc3�4�K�|]}|d�k�|V��dSr�rr�s �rr�z update_status.<locals>.<genexpr>i�2�����,_�,_�1�!�D�'�U^�J^�J^�Q�J^�J^�J^�J^�,_�,_rr�r�r�N)r�r�r�r&r+rYrer�r\r-rXr�r�r�rrr�rr�r5r�r�r7)r"r�rsr�rr_�updates_to_makerrPr`r�r�� new_statusrr�r1rXrrrcr r�rts @r� update_statusr)sl���* �>�>� � �E�� C�'� C� C�D�D�D� ��� �,�]�]���1�a��F� ��2� 2� 2��[� �@Y�[�[�\�\� \� 3���2?����M� $�M�7�;�;�N��G��O��J�NQ�R_�ao�Np�Np� � �J�5���w��*�7J��'�� �� ����%�\�&� � ����� ���%�1� � ���� ����� � � � � � � ���(�#�#�#�����!� � � � � � � �>� �g� &� &� >�$��[�[�]�]�F� >��"�"�$�%� &�&�&� � � � � � �"5�T�#�j�/�/�5J�5J�G�!T�!T��"+�7�!3�!3���I�A�v��z�z�(�+�+�y�8�8�$*�;�$7� �(,�,_�,_�,_�,_��,_�,_�,_�(_�(_� �&/�&3�&�&��� �� ���=� >� >� >�� � ���� �!<�C��F�F�!<�!<�=�=�=����� >�����! >� >� >� >� >� >� >� >� >� >� >���� >� >� >� >�: �Ns7�I$�B2H � I$� I�3I�I�I$�$I(�+I(�source_identifiersc ����t���stdt�����|sgS|D]3\}}}}}}|tjvrt dtj������4d�|D��}t |t��}g}g}g}d�|D��}t d�t|��D��t��} d�t|| ��D��} t||��D�]#\\} } } }}}\}}|s5|r|� dd|d ���n|� dd d ����G| � | ||f��}|r|||krv t|t��d }|� dd |d���n?#t$r2}|� ddt|����d ���Yd}~nd}~wwxYw��|� |||d���|� |��|� d|d�����%|�r]tt��5}|���} |D]1}|�d|dd|d��|dd����2|���tt%t'|����t��}t|��D]I\}}|� d��dkr+|d�t)�fd�|D����}d|d�||<�JnJ#t*j$r8}|���t dt|�������d}~wwxYw ddd��n #1swxYwY|S) aAdd new identifiers to multiple sources in a single transaction. Args: source_identifiers: List of tuples, each containing: - title: Source title - type: Source type - current_identifier_type: Current identifier type - current_identifier_value: Current identifier value - new_identifier_type: New identifier type to add - new_identifier_value: New identifier value to add Returns: List of dictionaries containing operation results for each identifier addition: - On success: {"status": "success", "source": source_details} - On source not found: {"status": "error", "message": "Source not found"} - On ambiguous source: {"status": "error", "message": "...", "matches": [...]} - On duplicate identifier: {"status": "error", "message": "...", "existing_source": details} - On invalid identifier type: {"status": "error", "message": "Invalid identifier type. Must be one of: ..."} r�z-Invalid new identifier type. Must be one of: c�(�g|]\}}}}}}||||f��Srr)rUrPr`� current_type� current_valuer�s rrWz#add_identifiers.<locals>.<listcomp>�s=����� ;�E�5�,� �q�!� ��|�]�3���rc�&�g|]\}}}}}}|||f��Srr)rUr�r`�new_type� new_values rrWz#add_identifiers.<locals>.<listcomp>�s;����� /�A�u�a��H�i� ��)�$���rc�.�g|]\}\}}}d|��|||f��S)zCheck r)rUrr`r�r�s rrWz#add_identifiers.<locals>.<listcomp>�sB��,�,�,� )�A�)��w�� �!���u�g�x�0�,�,�,rc �8�i|]\\}}}}}}\}}|�|||f|��Srr)rUr�r`r�r�rs r� <dictcomp>z#add_identifiers.<locals>.<dictcomp>�sL����� >� /�Q��q�!�W�h��(�A� � � ���"�H���rr�r r�rr�rz3New identifier already exists on a different sourcer�r�N)rMr0r1r�r�a! UPDATE sources SET identifiers = json_set( identifiers, :path, :value ) WHERE id = :id rMrLr0r1)rMr��valuersrtc3�4�K�|]}|d�k�|V��dSr�rr�s �rr�z"add_identifiers.<locals>.<genexpr>�r&rr�r�r�)r�r�r�rrrYrer�r�r\rr�r�r7r-rXrZr�rrr�r5r�r�)r*r�r0r�rr_r'r�new_identifier_checks�new_id_search_results�existing_new_idsrPr`r-r.r1rr�r��existing_detailsr�r1rX�updaterrrcr rts @r�add_identifiersr<us1���. �>�>� � �E�� C�'� C� C�D�D�D� ��� �$6�n�n���1�a��H�a� �,�8� 8� 8��l�M^�Mj�l�l�m�m� m� 9���?Q����M� $�M�7�;�;�N��G��O��J���3E�����+�,�,�-6�7L�-M�-M�,�,�,����� �� �!�#8� 9� 9�����be�ew�zH�bI�bI�) �) �]�H���|�]�H�i�J]�8�U\�� �� ����%�\�&� � ����� ���%�1� � ���� �+�.�.��x��/K�L�L�� � ��(�:�:� �#6���#P�#P�QR�#S� ����%�T�'7� � ������ � � � ����%�L�C��F�F�L�L� � ������������� ���� ����� �"� � � � � � ���(�#�#�#�����!� � � � � � � �%>� �g� &� &�$ >�$��[�[�]�]�F�" >�-� � �F��N�N�$�%�T�l� 9�V�J�%7� 9� 9�!'� �!4��� � � � �� � � � � �"5�T�#�j�/�/�5J�5J�G�!T�!T��"+�7�!3�!3���I�A�v��z�z�(�+�+�y�8�8�$*�;�$7� �(,�,_�,_�,_�,_��,_�,_�,_�(_�(_� �&/�&3�&�&��� �� ���=� >� >� >�� � ���� �!<�C��F�F�!<�!<�=�=�=����� >�����3$ >�$ >�$ >�$ >�$ >�$ >�$ >�$ >�$ >�$ >�$ >����$ >�$ >�$ >�$ >�L �NsO�4F� G�(G � G�1M/�CL�M/�M�'3M�M�M/�/M3�6M3�source_entity_linksc �@��t���stdt�����|sgS|D]4\}}}}}}}|tjvrt dtj������5d�|D��}t |t��}g}g}g}t||��D]�\\}} } } } }} \}}|s5|r|�dd|d���n|�ddd����G|�|| || d ���|�|��|�d |d �����|�r�tt��5}|� ��} d � d �|D����}|� d|�d�d�|D����d�|� ��D��}g}t|��D]5\}}|d|df|vr ddd�||<� |�|���6|r�|�d|��|���t#t%t'|����t��}t|��D]I\}}|�d��d kr+|d�t+�fd�|D����}d|d�||<�JnJ#t,j$r8}|���t dt3|�������d}~wwxYwddd��n #1swxYwY|S)a�Link multiple sources to entities in the knowledge graph. Args: source_entity_links: List of tuples, each containing: - title: Source title - type: Source type (paper, webpage, book, video, blog) - identifier_type: Type of identifier (semantic_scholar, arxiv, doi, isbn, url) - identifier_value: Value of the identifier - entity_name: Name of the entity to link to - relation_type: Type of relationship (discusses, introduces, extends, evaluates, applies, critiques) - notes: Optional notes explaining the relationship Returns: List of operation results, each containing: { "status": "success" | "error", "message": Error message if status is "error", "source": Source details if status is "success", "matches": List of potential matches if ambiguous source found } r��'Invalid relation type. Must be one of: c �*�g|]\}}}}}}}||||f��Srrr�s rrWz$link_to_entities.<locals>.<listcomp>5�?����� 4�E�5�'�8�Q��1� ��w��)���rr�r r�rr��rtrxryrzr�r�rhc3�K�|]}dV��dSrrrs rr�z#link_to_entities.<locals>.<genexpr>bs"����'F�'F�A��'F�'F�'F�'F�'F�'Frz� SELECT source_id, entity_name FROM source_entity_links WHERE (source_id, entity_name) IN (rc�8�g|]}|d|dfD]}|���S�rtrxr�rU�linkrs rrWz$link_to_entities.<locals>.<listcomp>g�L����� � $�[� 1�4� �3F�G��������rc�.�h|]}|d|df��SrErrTs rrmz#link_to_entities.<locals>.<setcomp>ms7��"�"�"����%�s�=�'9�:�"�"�"rrtrxz2Link already exists between this source and entityz� INSERT INTO source_entity_links (source_id, entity_name, relation_type, notes) VALUES (:source_id, :entity_name, :relation_type, :notes) rsc3�4�K�|]}|d�k�|V��dSr�rr�s �rr�z#link_to_entities.<locals>.<genexpr>�rrr�r�r�N)r�r�r�rBrrYrer�r\r-rXr}rZr^r�r�r�r�rrrr�r5r�r�r7)r=r�ryr�rr_� links_to_addrrPr`r�r�rxrzrr�r1rXr��existing_links�filtered_linksrrGrrcr r�rts @r�link_to_entitiesrNs����2 �>�>� � �E�� C�'� C� C�D�D�D� ��� �,?�f�f�'��1�a��A�}�a� �� ;� ;� ;��d��Gb�d�d�e�e� e� <���8K����M� $�M�7�;�;�N��G��L��J�eh�i|�M�fN�fN� � �a�L���w��+�}�e�Na�x�Y`�� �� ����%�\�&� � ����� ���%�1� � ���� ����!�&�*��  � � � � � ���(�#�#�#�����!� � � � � � � �9>� �g� &� &�8 >�$��[�[�]�]�F�6 >�"�x�x�'F�'F��'F�'F�'F�F�F� ���� �9E� � � ���$0���� ���"�"�%���0�0�"�"�"�� "$��(��6�6�4�4�G�A�t��[�)�4� �+>�?�>�Q�Q�&-�'[�&�&��� � � '�-�-�d�3�3�3�3�"���&�&�(�(� )�)�)� �K�K�M�M�M�&9��c�*�o�o�9N�9N�PW�%X�%X�N�&/�w�%7�%7��� ��6�!�:�:�h�/�/�9�<�<�(.�{�(;�I�,0�0c�0c�0c�0c�N�0c�0c�0c�,c�,c�M�*3�*7�*�*�G�A�J���� �=� >� >� >�� � ���� �!<�C��F�F�!<�!<�=�=�=����� >����m8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >�8 >����8 >�8 >�8 >�8 >�t �Ns7� L� EJ=�<L�=L� 3K?�?L�L�L�Lc�>��t���stdt�����|sgSt|t��}g}g}t ||��D]q\\}}}}\}} |s5| r|�dd| d���n|�ddd����D|�|��|�d|d����r|r� t |t��} t|��D]I\} } | �d ��dkr+| d �t�fd �| D����} d | d �|| <�JnZ#t$rM}t|��D]3\} } | �d ��dkrdt|��d�|| <�4Yd}~nd}~wwxYw|S)a�Get all entities linked to multiple sources. Args: sources: List of tuples, each containing: - title: Source title - type: Source type - identifier_type: Type of identifier - identifier_value: Value of the identifier Returns: List of operation results, each containing: { "status": "success" | "error", "message": Error message if status is "error", "source": Source details including linked entities if status is "success", "matches": List of potential matches if ambiguous source found } r�r�r r�rr�r�r�rsrtc3�4�K�|]}|d�k�|V��dSr�rr�s �rr�z&get_source_entities.<locals>.<genexpr>�s2�����([�([�q�a��g�QZ�FZ�FZ��FZ�FZ�FZ�FZ�([�([rr�r�N) r�r�r�rer�r\r�r�rr�rYr7)rIrr_rrPr`r�r�rr�rrrcr r�rts @r�get_source_entitiesrQ�sY���, �>�>� � �E�� C�'� C� C�D�D�D� ��� �$�G�W�5�5�N��G��J�BE�g�~�B^�B^� � �>�)���w��+>�H�g�� �� ����%�\�&� � ����� ���%�1� � ���� ����(�#�#�#�����!� � � � � � � �� �0��W�E�E�N�'�w�/�/� � � ��6��:�:�h�'�'�9�4�4� &�{� 3�I�$(�([�([�([�([�N�([�([�([�$[�$[�M�"+�"/�"�"�G�A�J��  ��� � � �&�w�/�/� � � ��6��:�:�h�'�'�9�4�4�")�#&�q�6�6�"�"�G�A�J�� � � � � ����� ���� �Ns�A.E� F� AF�F�source_entity_updatesc �����t���stdt�����|sgS|D]I\}}}}}}}|r*|tjvrt dtj�����|s|�t d����Jd�|D��}t |t��}g}g}g}t||��D]�\\} } } } } }}\}}|s5|r|�dd|d���n|�dd d ����G|�|| ||d ���|�|��|�d |d �����|�r1tt��5}|� ��} |D�]�g}g}�dr0|�d��|��d���d�0|�d��|��d��|� �d�dg��dd� |���d�}|� ||��|jdkr0t�fd�t!|��D����}ddd �||<��|���t%t't)|����t��}t!|��D]I\}}|�d��d kr+|d�t�fd�|D����}d|d�||<�JnJ#t,j$r8}|���t dt3|�������d}~wwxYw ddd��n #1swxYwY|S)arUpdate existing links between sources and entities. Args: source_entity_updates: List of tuples, each containing: - title: Source title - type: Source type - identifier_type: Type of identifier - identifier_value: Value of the identifier - entity_name: Name of the entity - relation_type: Optional new relationship type - notes: Optional new notes Note: At least one of relation_type or notes must be provided in each tuple Returns: List of operation results, each containing: { "status": "success" | "error", "message": Error message if status is "error", "source": Source details if status is "success", "matches": List of potential matches if ambiguous source found } r�r?Nz7At least one of relation_type or notes must be providedc �*�g|]\}}}}}}}||||f��Srrr�s rrWz'update_entity_links.<locals>.<listcomp>rArr�r r�rr�rBr�r�ryzrelation_type = ?rzz notes = ?rtrxzQ UPDATE source_entity_links SET rqzU WHERE source_id = ? AND entity_name = ? rc3��K�|]A\}}|�d��dkr#|�d���dk�=|V��BdS�rsr�rtN�r)rUr�rr;s �rr�z&update_entity_links.<locals>.<genexpr>Xsg�����#L�#L���A�$%�E�E�(�O�O�y�$@�$@�!"���{�!3�!3�v�k�7J�!J�!J�$%�!J�!J�!J�!J�#L�#Lr�,No link found between this source and entityrsc3�4�K�|]}|d�k�|V��dSr�rr�s �rr�z&update_entity_links.<locals>.<genexpr>ir&rr�r�r�)r�r�r�rBrrYrer�r\r-rX�extendr}rZ�rowcountr�r�r�r�rrrr5r�r�r7)rRr�ryrzr�rr_r'rrPr`r�r�rxrr�r1rX�updatesr�r��idxrrrcr r�rtr;s @@r�update_entity_linksr_�s�����6 �>�>� � �E�� C�'� C� C�D�D�D� ��� �0E�X�X�+��1�a��A�}�e� � f�]�/�2M�M�M��d��Gb�d�d�e�e� e�� X����V�W�W� W����8M����M� $�M�7�;�;�N��G��O��J�eh�i~�AO�fP�fP� � �a�L���w��+�}�e�Na�x�Y`�� �� ����%�\�&� � ����� ���%�1� � ���� ����!�&�*��  � � � � � ���(�#�#�#�����!� � � � � � � �4>� �g� &� &�3 >�$��[�[�]�]�F�1 >�-���F� �G��F��o�.�?����':�;�;�;�� � �f�_�&=�>�>�>��g��2����{�3�3�3�� � �f�W�o�6�6�6��M�M�6�+�#6��}�8M�"N�O�O�O��!�Y�Y�w�/�/����E� �N�N�5�&�1�1�1���!�+�+�"�#L�#L�#L�#L��7�1C�1C�#L�#L�#L�L�L��'.�'U�(�(��� �� � � � � � �"5�T�#�j�/�/�5J�5J�G�!T�!T��"+�7�!3�!3���I�A�v��z�z�(�+�+�y�8�8�$*�;�$7� �(,�,_�,_�,_�,_��,_�,_�,_�(_�(_� �&/�&3�&�&��� �� ���=� >� >� >�� � ���� �!<�C��F�F�!<�!<�=�=�=����� >�����Q3 >�3 >�3 >�3 >�3 >�3 >�3 >�3 >�3 >�3 >�3 >����3 >�3 >�3 >�3 >�j �Ns7� M2�6F$L�M2�M"�*3M�M"�"M2�2M6�9M6�source_entity_pairsc�:���t���stdt�����|sgSd�|D��}t|t��}g}g}g}t ||��D]�\\}}}} } \} } | s5| r|�dd| d���n|�ddd����E|�| | d���|�| ��|�d | d �����|�rt t��5} | ���} d �d �|D����}|� d |�d�d�|D����|j }|t|��kr�|� d|�d�d�|D����d�|� ��D��}t|��D]G\}��d�df|vr0t�fd�t|��D����}ddd�||<�H| ���t!t#t%|����t��}t|��D]I\}}|�d��d kr+|d�t�fd�|D����}d|d�||<�JnJ#t(j$r8}| ���t/dt1|�������d}~wwxYw ddd��n #1swxYwY|S)a�Remove links between sources and entities. Args: source_entity_pairs: List of tuples, each containing: - title: Source title - type: Source type - identifier_type: Type of identifier - identifier_value: Value of the identifier - entity_name: Name of the entity Returns: List of operation results, each containing: { "status": "success" | "error", "message": Error message if status is "error", "source": Source details if status is "success", "matches": List of potential matches if ambiguous source found } r�c�&�g|]\}}}}}||||f��Srrr�s rrWz'remove_entity_links.<locals>.<listcomp>�r�rr�r r�rr�rEr�r�rhc3�K�|]}dV��dSrrrs rr�z&remove_entity_links.<locals>.<genexpr>�s"����'I�'I�A��'I�'I�'I�'I�'I�'Irzl DELETE FROM source_entity_links WHERE (source_id, entity_name) IN (rc�8�g|]}|d|dfD]}|���SrErrFs rrWz'remove_entity_links.<locals>.<listcomp>�rHrz� SELECT source_id, entity_name FROM source_entity_links WHERE (source_id, entity_name) IN (z) c�8�g|]}|d|dfD]}|���SrErrFs rrWz'remove_entity_links.<locals>.<listcomp>�sL����� $�$(��$5�t�M�7J�#K��������rc�.�h|]}|d|df��SrErrTs rrmz&remove_entity_links.<locals>.<setcomp>�s7��&�&�&���[�)�3�}�+=�>�&�&�&rrtrxc3��K�|]A\}}|�d��dkr#|�d���dk�=|V��BdSrVrW)rU�jrXrGs �rr�z&remove_entity_links.<locals>.<genexpr>�sg�����'N�'N�T�Q��()���h���9�(D�(D�%&�U�U�;�%7�%7�4� �;L�%L�%L�()�%L�%L�%L�%L�'N�'NrrYrsc3�4�K�|]}|d�k�|V��dSr�rr�s �rr�z&remove_entity_links.<locals>.<genexpr>�r&rr�r�r�N)r�r�r�rer�r\r-rXr}rZr\r~r^r�r�r�r�rrrr5r�r�rYr7)r`r�rr_�links_to_removerrPr`r�r�rxrr�r1rXr�� removed_countrLrr^rrcr r�rGrts @@r�remove_entity_linksrlus�����. �>�>� � �E�� C�'� C� C�D�D�D� ��� ���2E����M� $�M�7�;�;�N��G��O��J�OR�Sf�hv�Ow�Ow� � �K�6���w��+�8K��7�� �� ����%�\�&� � ����� ���%�1� � ���� ����!�&� � � � � � ���(�#�#�#�����!� � � � � � � �<>� �g� &� &�; >�$��[�[�]�]�F�9 >�"�x�x�'I�'I��'I�'I�'I�I�I� ���� �8D� � � ���$3�������!'�� � �3��#7�#7�7�7��N�N�$�=I�$�$�$���(7���� ���&�&�#)�?�?�#4�#4�&�&�&�N� $-�_�#=�#=�����4� ��-�t�M�/B�C�>�Y�Y�"&�'N�'N�'N�'N�Y�w�5G�5G�'N�'N�'N�#N�#N�C�+2�+Y�,�,�G�C�L�� � � � � � �"5�T�#�j�/�/�5J�5J�G�!T�!T��"+�7�!3�!3���I�A�v��z�z�(�+�+�y�8�8�$*�;�$7� �(,�,_�,_�,_�,_��,_�,_�,_�(_�(_� �&/�&3�&�&��� �� ���=� >� >� >�� � ���� �!<�C��F�F�!<�!<�=�=�=����� >�����a; >�; >�; >�; >�; >�; >�; >�; >�; >�; >�; >����; >�; >�; >�; >�z �Ns7�L�&FJ9�8L�9L�3K;�;L�L�L�L�entity_filtersc ��t���stdt�����|sgS|D]^\}}}|r*|tjvrt dtj�����|r*|t jvrt dt j������_g}tt��5}|���} |D]�\}}}d}|g} |r|dz }| � |��|r|dz }| � |��|� || ��d�|� ��D��} | r3t| t��} |� d|||d �| d �����|� d|||d �gd �����n6#tj$r$} t d t| �������d } ~ wwxYw d d d ��n #1swxYwY|S) aVGet all sources linked to specific entities with optional filtering. Args: entity_filters: List of tuples, each containing: - entity_name: Name of the entity - type_filter: Optional filter by source type (paper, webpage, book, video, blog) - relation_filter: Optional filter by relation type (discusses, introduces, extends, evaluates, applies, critiques) Returns: List of operation results, each containing: { "status": "success" | "error", "message": Error message if status is "error", "entity": Entity name, "filters_applied": { "type": Applied type filter, "relation": Applied relation filter }, "sources": List of source details if status is "success" } r�z%Invalid type filter. Must be one of: z)Invalid relation filter. Must be one of: z� SELECT DISTINCT s.id FROM sources s JOIN source_entity_links l ON s.id = l.source_id WHERE l.entity_name = ? z AND s.type = ?z AND l.relation_type = ?c��g|] }|d�� SrkrrTs rrWz&get_entity_sources.<locals>.<listcomp>6s��E�E�E�C�c�$�i�E�E�Err�)rr�relation)rs�entity�filters_appliedrIr�N)r�r�r�rrrYrBr-rXr\rZr^r�r5r�r7) rmr�� type_filter�relation_filterr_r1rXrxr�r�rrr�s r�get_entity_sourcesru�s���2 �>�>� � �E�� C�'� C� C�D�D�D� ��� �,:�h�h�'��;�� � `�;�k�.E�E�E��^�[�E\�^�^�_�_� _� � h��o�6Q�Q�Q��f��Id�f�f�g�g� g���G� �'� "� "�.:�d�������, :�=K�( �( �9� �[�/��� &����/��.�.�E��M�M�+�.�.�.�"�3��7�7�E��M�M�/�2�2�2����u�f�-�-�-�E�E�6�?�?�3D�3D�E�E�E� ���%8��W�%M�%M�N��N�N�"+�"-�$/�(7�,�,�$2�$�$������N�N�"+�"-�$/�(7�,�,�$&�$�$�����A( ��T�}� :� :� :��8��A���8�8�9�9� 9����� :����U( �.:�.:�.:�.:�.:�.:�.:�.:�.:�.:�.:����.:�.:�.:�.:�` �Ns7�,G�C F�G�G�F=�=G�G�G�G�__main__)NTr�)2�pathlibrr5r�rRro�typingrrrrrr r �fastmcpr r �re�mcp�environrYr�rrr&r-rBr7rer��toolr��intr�r�r�r�r�r�r r!r)r<rNrQr_rlrur�runrrr�<module>r�s������������� � � � � � � � � � � � �?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?������������� � � � ��g������2�:�%�%� �*�F� G� G�G� �$�r�z�*�+� ,� ,�� ��������@�@�@�@�@�@�@�@�B�B�B�B�B�B�B�B� � � � � � � � � � � � � � � � �I� �%��S�#�s�*�+� ,�I� �I� �%��� �t�D�z�)� *�+�I�I�I�I�Vb�u�S�$�s�)�^�4�b�t�b��T�RU�WZ�RZ�^�H\�b�b�b�b�T�����#'��� >8�>8� �>8� �T�#�Y� �>8��>8�� >8�  �$�s�C�x�.�� >8�>8�>8� ��>8�@�����8�T�#�Y�8�8�8� ��8�0�����%8�s�%8�t�D��c��N�';�%8�%8�%8� ��%8�N�����+8��+8��S�#�X��+8�+8�+8� ��+8�Z�����18�4��S��>�18�18�18� ��18�f�����8��c�3�h��8�8�8� ��8�L�����F� �%��S�#�s�H�T�#�s�(�^�,D�D�E� F�F� �$�s�C�x�.��F�F�F� ��F�P�����~��u�S�#�s�C��c�9�:�;�~� �$�s�C�x�.��~�~�~� ��~�@�����d���c�3��S�#�5�6�7�d� �$�s�C�x�.��d�d�d� ��d�L�����S��U�3��S�#�s�C�#?�@�A�S� �$�s�C�x�.��S�S�S� ��S�t�����G��e�C��c�3��S�(�3�-�$O�P�Q�G� �$�s�C�x�.��G�G�G� ��G�R�����O� �%��S�#�s�*�+� ,�O� �$�s�C�x�.��O�O�O� ��O�b�����F���c�3��S�#�x��}�h�WZ�m�&[� \�]�F� �$�s�C�x�.��F�F�F� ��F�P�����A��e�C��c�3��$;�<�=�A� �$�s�C�x�.��A�A�A� ��A�F�����W���s�H�S�M�8�C�=�@�A�B�W� �$�s�C�x�.��W�W�W� ��W�z �z����G�G�I�I�I�I�I��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/zongmin-yu/sqlite-literature-management-fastmcp-mcp-server'

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