Skip to main content
Glama

get_chat_messages

Retrieve messages from an iMessage chat by providing chat ID or phone/email handle, with optional time filter and limit.

Instructions

Return messages for a chat. Provide chat_id or handle (phone/email).

since is an ISO8601 datetime. Attachments are metadata only.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
chat_idNo
handleNo
limitNo
sinceNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Registration of 'get_chat_messages' as an MCP tool via @mcp.tool() decorator. The handler function delegates to db.get_chat_messages().
    @mcp.tool()
    def get_chat_messages(
        chat_id: int | None = None,
        handle: str | None = None,
        limit: int = 50,
        since: str | None = None,
    ) -> list[dict[str, Any]]:
        """Return messages for a chat. Provide chat_id or handle (phone/email).
    
        `since` is an ISO8601 datetime. Attachments are metadata only.
        """
        return db.get_chat_messages(chat_id=chat_id, handle=handle, limit=limit, since=since)
  • Core implementation of get_chat_messages(). Builds SQL query to fetch messages from chat.db, filtered by chat_id or handle, with optional since filter. Returns messages oldest-first with sender, body, attachments.
    def get_chat_messages(
        chat_id: int | None = None,
        handle: str | None = None,
        limit: int = 50,
        since: str | None = None,
    ) -> list[dict[str, Any]]:
        if chat_id is None and not handle:
            raise ValueError("Provide chat_id or handle.")
        limit = max(1, min(int(limit), 500))
        with _open() as conn:
            params: list[Any] = []
            where: list[str] = []
            if chat_id is not None:
                join = "JOIN chat_message_join cmj ON cmj.message_id = m.ROWID"
                where.append("cmj.chat_id = ?")
                params.append(int(chat_id))
            else:
                join = "JOIN handle h ON h.ROWID = m.handle_id"
                where.append("LOWER(h.id) = LOWER(?)")
                params.append(normalize_handle(handle or ""))
            if since:
                where.append("m.date >= ?")
                params.append(iso_to_apple_ns(since))
            sql = f"""
                SELECT m.ROWID AS message_id, m.date, m.is_from_me, m.text, m.attributedBody,
                       m.handle_id,
                       (SELECT h2.id FROM handle h2 WHERE h2.ROWID = m.handle_id) AS sender_handle,
                       (SELECT m.cache_has_attachments) AS has_attach
                FROM message m
                {join}
                WHERE {' AND '.join(where)}
                ORDER BY m.date DESC
                LIMIT ?
            """
            params.append(limit)
            rows = conn.execute(sql, params).fetchall()
            out: list[dict[str, Any]] = []
            for r in rows:
                attachments = _attachments_for(conn, r["message_id"]) if r["has_attach"] else []
                out.append(
                    {
                        "message_id": r["message_id"],
                        "date": apple_ts_to_iso(r["date"]),
                        "is_from_me": bool(r["is_from_me"]),
                        "sender": None if r["is_from_me"] else r["sender_handle"],
                        "body": _extract_text(r),
                        "attachments": attachments,
                    }
                )
            out.reverse()  # return oldest-first for readability
            return out
  • Input schema definition: chat_id (int|None), handle (str|None), limit (int, default 50), since (str|None ISO8601). Return type is list[dict].
    def get_chat_messages(
        chat_id: int | None = None,
        handle: str | None = None,
        limit: int = 50,
        since: str | None = None,
    ) -> list[dict[str, Any]]:
  • Helper 'iso_to_apple_ns' converts ISO8601 string to Apple nanosecond timestamp for the 'since' filter.
    def iso_to_apple_ns(iso_str: str) -> int:
        """Convert an ISO8601 string to Apple nanoseconds-since-2001-01-01."""
        dt = datetime.fromisoformat(iso_str.replace("Z", "+00:00"))
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        unix_ts = dt.timestamp()
        return int((unix_ts - APPLE_EPOCH_OFFSET) * 1_000_000_000)
  • Helper 'normalize_handle' trims whitespace and lowercases email handles for SQL matching.
    def normalize_handle(value: str) -> str:
        """Trim whitespace. Keep + for phones, lowercase emails."""
        v = value.strip()
        if "@" in v:
            return v.lower()
        return v
Behavior4/5

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

Reveals that attachments are metadata only and that 'since' expects ISO8601 datetime. No annotations provided, so description carries behavioral disclosure. Does not mention read-only nature explicitly, but it's implied.

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

Conciseness5/5

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

Extremely concise: two sentences cover purpose, parameter options, and a key behavioral note. No unnecessary words.

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?

While output schema exists, description lacks details on return format, pagination, or behavior when both identifiers provided. Attachments mention is good, but missing parameter defaults (limit=50).

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

Parameters3/5

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

Adds meaning for chat_id/handle and clarifies 'since' format, but does not describe 'limit' parameter. Schema coverage is 0%, so description partially compensates but not fully.

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?

Description clearly states it returns messages for a chat, and specifies two ways to identify the chat (chat_id or handle). This distinguishes it from sibling tools like search_imessages that search across chats.

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?

Provides context on how to specify the chat (chat_id or handle), but does not give guidance on when to use this tool versus alternatives like search_imessages. No exclusions or alternative suggestions.

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/camfortin/imessage-mcp'

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