Skip to main content
Glama
carterlasalle

mac-messages-mcp

tool_send_message

Send text messages through macOS Messages app to individuals or groups using phone numbers, email addresses, or contact names.

Instructions

Send a message using the Messages app.

Args:
    recipient: Phone number, email, contact name, or "contact:N" to select from matches
              For example, "contact:1" selects the first contact from a previous search
    message: Message text to send
    group_chat: Whether to send to a group chat (uses chat ID instead of buddy)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
recipientYes
messageYes
group_chatNo

Implementation Reference

  • The tool handler function decorated with @mcp.tool(). It logs the action, ensures recipient is string, calls the send_message helper, and handles exceptions.
    @mcp.tool()
    def tool_send_message(ctx: Context, recipient: str, message: str, group_chat: bool = False) -> str:
        """
        Send a message using the Messages app.
        
        Args:
            recipient: Phone number, email, contact name, or "contact:N" to select from matches
                      For example, "contact:1" selects the first contact from a previous search
            message: Message text to send
            group_chat: Whether to send to a group chat (uses chat ID instead of buddy)
        """
        logger.info(f"Sending message to: {recipient}, group_chat: {group_chat}")
        try:
            # Ensure recipient is a string (handles numbers properly)
            recipient = str(recipient)
            result = send_message(recipient=recipient, message=message, group_chat=group_chat)
            return result
        except Exception as e:
            logger.error(f"Error in send_message: {str(e)}")
            return f"Error sending message: {str(e)}"
  • The core helper function implementing message sending logic: resolves contacts via fuzzy matching, handles 'contact:N' selection, and delegates to low-level AppleScript sender.
    def send_message(recipient: str, message: str, group_chat: bool = False) -> str:
        """
        Send a message using the Messages app with improved contact resolution.
        
        Args:
            recipient: Phone number, email, contact name, or special format for contact selection
                      Use "contact:N" to select the Nth contact from a previous ambiguous match
            message: Message text to send
            group_chat: Whether this is a group chat (uses chat ID instead of buddy)
        
        Returns:
            Success or error message
        """
        # Convert to string to ensure phone numbers work properly
        recipient = str(recipient).strip()
        
        # Handle contact selection format (contact:N)
        if recipient.lower().startswith("contact:"):
            try:
                # Get the selected index (1-based)
                index = int(recipient.split(":", 1)[1].strip()) - 1
                
                # Get the most recent contact matches from global cache
                if not hasattr(send_message, "recent_matches") or not send_message.recent_matches:
                    return "No recent contact matches available. Please search for a contact first."
                
                if index < 0 or index >= len(send_message.recent_matches):
                    return f"Invalid selection. Please choose a number between 1 and {len(send_message.recent_matches)}."
                
                # Get the selected contact
                contact = send_message.recent_matches[index]
                return _send_message_to_recipient(contact['phone'], message, contact['name'], group_chat)
            except (ValueError, IndexError) as e:
                return f"Error selecting contact: {str(e)}"
        
        # Check if recipient is directly a phone number
        if all(c.isdigit() or c in '+- ()' for c in recipient):
            # Clean the phone number
            clean_number = ''.join(c for c in recipient if c.isdigit())
            return _send_message_to_recipient(clean_number, message, group_chat=group_chat)
        
        # Try to find the contact by name
        contacts = find_contact_by_name(recipient)
        
        if not contacts:
            return f"Error: Could not find any contact matching '{recipient}'"
        
        if len(contacts) == 1:
            # Single match, use it
            contact = contacts[0]
            return _send_message_to_recipient(contact['phone'], message, contact['name'], group_chat)
        else:
            # Store the matches for later selection
            send_message.recent_matches = contacts
            
            # Multiple matches, return them all
            contact_list = "\n".join([f"{i+1}. {c['name']} ({c['phone']})" for i, c in enumerate(contacts[:10])])
            return f"Multiple contacts found matching '{recipient}'. Please specify which one using 'contact:N' where N is the number:\n{contact_list}"
  • Low-level helper that executes AppleScript to send the message via Messages app, with fallback to direct method.
    def _send_message_to_recipient(recipient: str, message: str, contact_name: str = None, group_chat: bool = False) -> str:
        """
        Internal function to send a message to a specific recipient using file-based approach.
        
        Args:
            recipient: Phone number or email
            message: Message text to send
            contact_name: Optional contact name for the success message
            group_chat: Whether this is a group chat
        
        Returns:
            Success or error message
        """
        try:
            # Create a temporary file with the message content
            file_path = os.path.abspath('imessage_tmp.txt')
            
            with open(file_path, 'w') as f:
                f.write(message)
            
            # Adjust the AppleScript command based on whether this is a group chat
            if not group_chat:
                command = f'tell application "Messages" to send (read (POSIX file "{file_path}") as «class utf8») to participant "{recipient}" of (1st service whose service type = iMessage)'
            else:
                command = f'tell application "Messages" to send (read (POSIX file "{file_path}") as «class utf8») to chat "{recipient}"'
            
            # Run the AppleScript
            result = run_applescript(command)
            
            # Clean up the temporary file
            try:
                os.remove(file_path)
            except:
                pass
            
            # Check result
            if result.startswith("Error:"):
                # Try fallback to direct method
                return _send_message_direct(recipient, message, contact_name, group_chat)
            
            # Message sent successfully
            display_name = contact_name if contact_name else recipient
            return f"Message sent successfully to {display_name}"
        except Exception as e:
            # Try fallback method
            return _send_message_direct(recipient, message, contact_name, group_chat)
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the tool sends messages but doesn't cover critical aspects like whether it requires authentication, potential rate limits, error conditions (e.g., invalid recipient), or what happens on success/failure. The description adds some context about recipient formats but misses key behavioral traits needed for safe invocation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded with the core purpose in the first sentence. The parameter explanations are organized in a clear list format, making it easy to scan. While efficient, it could be slightly more concise by integrating the parameter details more seamlessly, but overall it avoids unnecessary verbosity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a message-sending tool with 3 parameters, 0% schema coverage, no annotations, and no output schema, the description is moderately complete. It covers parameter semantics well but lacks behavioral context (e.g., side effects, errors) and output information. For a tool that performs a write operation, more completeness around success criteria and potential impacts would be beneficial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 0%, so the description must compensate. It provides meaningful semantics for all three parameters: 'recipient' explains formats (phone, email, contact name, 'contact:N'), 'message' clarifies it's text to send, and 'group_chat' indicates it uses chat ID instead of buddy. This adds substantial value beyond the bare schema, though it could include examples for the message parameter or more details on group_chat behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Send a message') and resource ('using the Messages app'), making the purpose immediately understandable. It distinguishes itself from sibling tools like tool_fuzzy_search_messages or tool_get_recent_messages by focusing on sending rather than retrieving messages. However, it doesn't explicitly differentiate from potential message-related tools that might exist elsewhere.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage through the parameter explanations (e.g., 'contact:N' for selecting from matches), suggesting when to use certain recipient formats. However, it lacks explicit guidance on when to use this tool versus alternatives like tool_get_chats for viewing chats or tool_find_contact for contact lookup, and doesn't mention prerequisites such as needing the Messages app available.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/carterlasalle/mac_messages_mcp'

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