Skip to main content
Glama

get_conversation

Retrieve recent messages from a contact or group, supporting pagination and date filtering. Locally marks messages as read without sending read receipts.

Instructions

Get recent message history with a contact or group from local store. Automatically marks returned messages as read in the local store (does NOT send a Signal read receipt — call send_read_receipt for that).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
recipientYesPhone number or group ID
limitNoMax messages to return (default: 50)
offsetNoNumber of messages to skip for pagination (default: 0)
sinceNoOnly messages after this ISO datetime (e.g. 2024-01-01T00:00:00)

Implementation Reference

  • Core handler: retrieves message history from SQLite store for a contact (by phone number) or group (by group_id). Filters by recipient/sender/group_id, supports pagination (limit/offset) and optional 'since' timestamp. Returns messages in chronological order.
    def get_conversation(
        recipient: str, limit: int = 50, offset: int = 0, since: datetime | None = None
    ) -> list[Message]:
        """Get message history with a contact (by number) or group (by group_id)."""
        init_db()
        with _db() as conn:
            params: list = [recipient, recipient, recipient]
            since_clause = ""
            if since:
                since_clause = "AND timestamp >= ?"
                params.append(int(since.timestamp() * 1000))
            params.extend([limit, offset])
            rows = conn.execute(
                f"""SELECT * FROM messages
                   WHERE (group_id = ?
                      OR (group_id IS NULL AND (sender = ? OR recipient = ?)))
                   {since_clause}
                   ORDER BY timestamp DESC LIMIT ? OFFSET ?""",
                params,
            ).fetchall()
            return _rows_to_messages(conn, list(reversed(rows)))
  • Client wrapper: delegates to store.get_conversation via asyncio.to_thread. Auto-marks received messages as read after fetching them.
    async def get_conversation(
        self, recipient: str, limit: int = 50, offset: int = 0, since: datetime | None = None
    ) -> list[Message]:
        messages = await asyncio.to_thread(
            _store.get_conversation, recipient, limit=limit, offset=offset, since=since
        )
        # Auto-mark received messages as read (like every Signal client does)
        unread_ids = [m.id for m in messages if not m.is_read and m.sender != self.account]
        if unread_ids:
            await asyncio.to_thread(_store.mark_as_read, unread_ids)
            for m in messages:
                if m.id in unread_ids:
                    m.is_read = True
        return messages
  • Input schema: accepts recipient (str, phone number or group_id), limit (int, default 50), offset (int, default 0), and optional since (datetime). Returns list[Message].
    def get_conversation(
        recipient: str, limit: int = 50, offset: int = 0, since: datetime | None = None
    ) -> list[Message]:
  • CLI command 'history' calls client.get_conversation to display message history for a recipient.
    async def _run():
        async with SignalClient() as client:
            messages = await client.get_conversation(recipient, limit=limit, offset=offset, since=since_dt)
            if not messages:
                click.echo("No messages found.")
                return
            if as_json:
                click.echo(json.dumps([m.to_dict() for m in messages], indent=2))
            else:
                for msg in messages:
                    _print_message(msg)
    try:
        run(_run())
    except SignalError as e:
        click.echo(f"Error: {e}", err=True)
        sys.exit(1)
  • Helper that counts total messages matching get_conversation's filter logic, used for 'has_more' pagination support.
    def count_conversation(
        recipient: str, since: datetime | None = None
    ) -> int:
        """Return total message count matching get_conversation's filter — used for has_more."""
        init_db()
        with _db() as conn:
            params: list = [recipient, recipient, recipient]
            since_clause = ""
            if since:
                since_clause = "AND timestamp >= ?"
                params.append(int(since.timestamp() * 1000))
            row = conn.execute(
                f"""SELECT COUNT(*) FROM messages
                   WHERE (group_id = ?
                      OR (group_id IS NULL AND (sender = ? OR recipient = ?)))
                   {since_clause}""",
                params,
            ).fetchone()
            return row[0] if row else 0
Behavior4/5

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

Despite no annotations, the description reveals a key behavioral trait: it automatically marks messages as read in the local store but does not send a read receipt. This adds value beyond what the input schema provides.

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?

The description is concise with two sentences. The first sentence states the primary action, and the second provides a critical behavioral note. No extraneous information.

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

Completeness4/5

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

The description covers the main purpose and a key behavior. It does not mention the return format or ordering, but the parameters are well-documented in the schema. Slightly more detail could improve completeness, but it is adequate.

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?

The input schema has 100% description coverage for all 4 parameters. The tool description does not add additional meaning beyond the schema descriptions, so it meets the baseline expectation.

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

Purpose5/5

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

The description clearly states the tool retrieves recent message history from the local store, specifying the verb 'Get', resource 'message history', and scope 'with a contact or group'. It distinguishes itself from the sibling 'send_read_receipt' by explicitly noting that it does not send a Signal read receipt.

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

Usage Guidelines4/5

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

The description includes a clear alternative: 'call send_read_receipt for that' if a read receipt is needed. However, it does not provide guidance on when to use this tool versus other message retrieval tools like 'receive_messages', 'export_messages', or 'get_unread'.

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/googlarz/signal-mcp'

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