@@ -47,6 +47,7 @@ async def list_contacts(account: Optional[str] = None) -> str:
4747async def search_contacts (query : str , account : Optional [str ] = None ) -> str :
4848 """
4949 Search for contacts by name, username, or phone number using Telethon's SearchRequest.
50+ Saved favorite aliases matching the query are checked first and returned at the top.
5051 Args:
5152 query: The search term to look for in contact names, usernames, or phone numbers.
5253
@@ -55,11 +56,17 @@ async def search_contacts(query: str, account: Optional[str] = None) -> str:
5556 try :
5657 cl = get_client (account )
5758 await ensure_connected (cl )
59+ q = query .strip ().lstrip ("@" ).lower ()
60+ alias_records = [
61+ {"alias" : alias , "id" : chat_id , "favorite" : True }
62+ for alias , chat_id in load_aliases ().items ()
63+ if q and (q in alias or alias in q )
64+ ]
5865 result = await cl (functions .contacts .SearchRequest (q = query , limit = 50 ))
5966 users = result .users
60- if not users :
67+ if not users and not alias_records :
6168 return f"No contacts found matching '{ query } '."
62- records = []
69+ records = alias_records
6370 for user in users :
6471 name = f"{ getattr (user , 'first_name' , '' )} { getattr (user , 'last_name' , '' )} " .strip ()
6572 record = {
@@ -596,6 +603,57 @@ async def send_contact(
596603 return log_and_format_error ("send_contact" , e , chat_id = chat_id , phone_number = phone_number )
597604
598605
606+ @mcp .tool (annotations = ToolAnnotations (title = "Set Contact Alias" , openWorldHint = True ))
607+ @with_account (readonly = True )
608+ async def set_contact_alias (alias : str , chat_id : str , account : Optional [str ] = None ) -> str :
609+ """
610+ Save a favorite alias for a contact or chat. After this, the alias can be used
611+ anywhere a chat_id is accepted (e.g. alias "андрей" -> send_message("андрей", ...)).
612+ Args:
613+ alias: Short name to remember (case-insensitive, e.g. "андрей").
614+ chat_id: Chat ID, username (@user), or phone of the target.
615+ """
616+ try :
617+ cl = get_client (account )
618+ entity = await resolve_entity (chat_id , cl )
619+ marked_id = get_marked_id (entity )
620+ aliases = load_aliases ()
621+ aliases [alias .strip ().lstrip ("@" ).lower ()] = marked_id
622+ save_aliases (aliases )
623+ return format_tool_result (
624+ {"alias" : alias .strip ().lower (), "resolved" : format_entity (entity )}
625+ )
626+ except Exception as e :
627+ return log_and_format_error ("set_contact_alias" , e , alias = alias , chat_id = chat_id )
628+
629+
630+ @mcp .tool (annotations = ToolAnnotations (title = "List Contact Aliases" , readOnlyHint = True ))
631+ @with_account (readonly = True )
632+ async def list_contact_aliases (account : Optional [str ] = None ) -> str :
633+ """List all saved favorite aliases and their chat IDs."""
634+ try :
635+ aliases = load_aliases ()
636+ return format_tool_result (aliases ) if aliases else "No aliases saved."
637+ except Exception as e :
638+ return log_and_format_error ("list_contact_aliases" , e )
639+
640+
641+ @mcp .tool (annotations = ToolAnnotations (title = "Delete Contact Alias" , openWorldHint = True ))
642+ @with_account (readonly = True )
643+ async def delete_contact_alias (alias : str , account : Optional [str ] = None ) -> str :
644+ """Delete a saved favorite alias."""
645+ try :
646+ aliases = load_aliases ()
647+ key = alias .strip ().lstrip ("@" ).lower ()
648+ if key not in aliases :
649+ return f"Alias '{ alias } ' not found."
650+ del aliases [key ]
651+ save_aliases (aliases )
652+ return f"Alias '{ alias } ' deleted."
653+ except Exception as e :
654+ return log_and_format_error ("delete_contact_alias" , e , alias = alias )
655+
656+
599657__all__ = [
600658 "list_contacts" ,
601659 "search_contacts" ,
@@ -611,4 +669,7 @@ async def send_contact(
611669 "export_contacts" ,
612670 "get_blocked_users" ,
613671 "send_contact" ,
672+ "set_contact_alias" ,
673+ "list_contact_aliases" ,
674+ "delete_contact_alias" ,
614675]
0 commit comments