Skip to main content
marklaz81
Conversationalist
May 27, 2026

Lack of personal conversation views through MCP

  • May 27, 2026
  • 17 replies
  • 475 views

Good news :) Hopefully v1.1 will be ready soon. When you query open conversations via the API or MCP, you get the global conversation status not the per-inbox view your sidebar shows. This means archived conversations still show as open if they're active in any other inbox, making it impossible to programmatically replicate what you see in Front. For any team building AI agents or automations on top of Front, this is a fundamental blocker. 

    17 replies

    JimKeough
    Conversationalist
    July 27, 2026


    Hey ​@marklaz81 , ​@JimKeough , and ​@Santevia, this is Martín from the Product team, stepping in for Javier.

    Thanks again for the thorough testing — it sent us digging properly, and I wanted to come back with what we found. The short version: after reproducing your steps, we believe search_conversations is behaving as intended. It's designed to mirror Front's own search, not the sidebar's "Open" counter — and that distinction explains all three of your points.

    1. On the over-count / scope. Our MCP search replicates Front's search functionality directly. Could you try this in Front itself: go to your Open inbox and type is:open in the search bar (exactly like the screenshot below)? That's effectively the query the MCP is running, and it should line up with what the MCP returns. We weren't able to reproduce a mismatch on our side. The reason you're seeing more than your sidebar shows is most likely open Discussions and Drafts — the sidebar counter of “Open” doesn't include those, but search does (As front works as of today)
    2. On "open" returning archived/resolved conversations. This one is actually expected behavior rather than a bug. In Front, each subscriber to a conversation has their own individual status. When another teammate who was the assignee resolved it, that updated the conversation's status globally — but it stayed open for the other subscribers in their own inboxes. So a search for "open" returns conversations that are open to you, even if they've been resolved globally, and the MCP mirrors that exact behavior. Drafts surface under the same logic.

    3. On the extra records inflating the count. Same root cause: Drafts come back as "open" because they haven't been archived. So what you're getting is a mix of everything that's open to you as a person — conversations, discussions, and drafts — which won't line up one-to-one with the "Open" counter in your sidebar.

    A couple of things that would help us close this out:

    • Would you mind running the conversation status "open" search in Front's own search bar (as in the image) and comparing it against what the MCP returns? That's the true apples-to-apples check.
    • If it still doesn't match after that, could you share the actual response from the LLM? That would let us see exactly what it's working with.

    Hope this is helpful. Since search_conversations is built to mimic the search tool rather than the sidebar view, if what you're really after is validating specifically against the sidebar UI, I'd genuinely love to understand the "why" behind it — it may be a distinct need worth looking at separately. But as far as we can tell, the search itself is working as intended.

    Thanks for keeping at this with us.

     

    Thanks — that's a helpful breakdown, and points 1 and 3 hold up. I ran the comparison and the count reconciles: Front's search bar and the MCP both give me 34. The per-subscriber status model plus drafts and discussions accounts for the delta against the sidebar counter. Happy to consider the over-count question closed.

    On your question about *why* I was validating against the sidebar — I'm building an agent that needs a deterministic answer to "what is actually on my plate right now." The sidebar counter is the human-facing version of that, so it was my reference point. What I'm really after is a stable personal-queue primitive; I've been using a tag as a proxy, which works but isn't ideal.

    That said, the testing surfaced three things I don't think the explanation above covers.

    **1. search_conversations silently truncates and doesn't return a cursor.**

    A plain call with `filters: {status: "open"}` returns:
    - `totalCount: 34`
    - 19 conversations
    - no `nextCursor` field

    There's no signal that 15 records are missing. An agent following the response contract stops at 19 and reports that as the complete set — which is worse than an error, because it's confidently wrong.

    I only found the cursor by accident. A narrower query (`after: 2026-07-18`, `before: 2026-07-23`) *did* return `nextCursor: "20"`, which revealed the cursor is a numeric offset. I passed `cursor: "20"` back into the unfiltered call and got the remaining records. So the data is reachable — the server just isn't advertising the path. Two sub-notes: the cursor appears inconsistently, and it looks off by one (page 1 returns 19 items, `cursor: "20"` starts at item 19, so the last record repeats).

    **2. The before / after filters don't appear to key off updatedAt.**

    `filters: {status: "open", before: "2026-07-22"}` returned conversations with `updatedAt` of `2026-07-27`. The docs describe these as filtering on last-update date, and `updatedAt` is the only timestamp in the response, so I can't tell what field they're actually reading. This matters because date-windowing was my workaround for #1.

    **3. The response reports global status, not requester status.**

    This follows directly from your point 2. If the search is correctly returning conversations that are open *to me* even though they're resolved globally, the payload should say so. Right now those records come back as `"status": "archived"` with `"ticketStatus": {"category": "resolved"}`. The field contradicts the filter that selected the record.

    For a human reading results in the UI, that's invisible. For an agent it's a real problem: I can either trust the filter and ignore the status field, or trust the status field and discard records the filter deliberately included. There's no way to reconcile them from the response alone. Exposing the requester-scoped status — even just a boolean alongside the global one — would close it.

    Happy to share raw request/response pairs for any of these. #1 is the one I'd prioritize: the other two I can work around, but silent truncation quietly corrupts anything built on top of it.

    Javier - Developer Relations
    Community Manager
    July 27, 2026

    @JimKeough For the pagination issue, have you tried asking your AI assistant to process every matching conversation, and if so, how has it failed?

    @eric.neto ​@martin_silva  This seems like the same possible pagination issue reported in the other thread

    Javier - Front Developer Relations
    JimKeough
    Conversationalist
    July 28, 2026

    @JimKeough For the pagination issue, have you tried asking your AI assistant to process every matching conversation, and if so, how has it failed?

    @eric.neto ​@martin_silva  This seems like the same possible pagination issue reported in the other thread

    Yes, and here's exactly how it failed.

     

    I asked it to enumerate every conversation in my open queue. It returned 19 and stopped, reporting that as complete. The problem is that from the response alone, stopping was the correct behavior:

     

    Request: `search_conversations` with `filters: {status: "open"}`

    Response: `totalCount: 34`, 19 conversations, no `nextCursor` field.

     

    No error, no cursor. An assistant following the response contract has no way to know 15 more records exist. It fails silently and confidently, which is worse than erroring — an error I'd have caught immediately.

     

    The records are reachable, though. A narrower query — `filters: {status: "open", after: "2026-07-18", before: "2026-07-23"}` — *did* return `nextCursor: "20"`. That revealed the cursor is a plain numeric offset, so I passed `cursor: "20"` back into the original unfiltered call and got the remaining 15. Reconciled at 34.

     

    So the pagination mechanism itself works. The bug is that `nextCursor` is omitted when more records exist — on some queries but not others. If it came back consistently, this would be a non-issue.

     

    Two smaller details from the same test:

     

    - The cursor looks off by one. Page 1 returns 19 records, and `cursor: "20"` starts at record 19, so the last record of the previous page repeats and has to be deduplicated by `id`.

    - Confirming your point 1: the count does match. Front's own search bar and the MCP both give me 34. Per-subscriber status plus drafts and discussions accounts for the sidebar delta, exactly as you described. I'd consider that part settled.

     

    Glad this lines up with what @eric.neto and @martin_silva are seeing — happy to share raw request/response pairs if that helps narrow it down.

     

    Separately, and much smaller: neither search_conversations nor read_conversation returns a web URL for a conversation, only the raw cnv_ ID. I had to construct app.frontapp.com/open/cnv_xxx by hand to get a clickable link. It works, but including it in the response would be an easy win for any assistant that wants to hand a human something to click.

    Javier - Developer Relations
    Community Manager
    July 28, 2026

    Thanks ​@JimKeough. We have created tickets for these issues!

    Javier - Front Developer Relations
    alexis
    Rookie
    August 6, 2026

    Some other items i noticed:

     

    1. status: "open" returns archived conversations under scope: "my_conversations"

    Call: search_conversations with scope: "my_conversations", filters: { inboxId: <my inbox>, status: "open" }.

    One of the returned results carries "status": "archived" in the search payload itself — the filter contradicts its own result. read_conversation confirms that conversation was genuinely archived the day before (a teammate replied and resolved it).

    The same conversation behaves correctly under scope: "all_inboxes": returned by status: "archived", excluded by status: "open". So the leak appears specific to the my_conversations scope, not to the filter value.

    Impact: totalCount overcounts, and a client can't trust the filter without re-checking every result's own status.

    2. scope: "my_conversations" omits conversations its own definition covers

    The scope is documented as "conversations assigned to you, in your private inboxes, or in shared inboxes you participate in".

    A conversation satisfying all three clauses — status: "open", in my own inbox, assigned to me — is returned by scope: "all_inboxes" + status: "open" filtered to that inbox, but not by scope: "my_conversations" with the identical filter.

    Impact: combined with defect 1, the narrow scope is lossy in both directions — it includes archived threads and drops live ones — so neither scope corresponds to what I actually see in my Front inbox.

    3. Feature request: no way to see snoozed conversations

    conversation.status collapses snoozed into archived, and neither search_conversations nor read_conversation returns the reminders object. An MCP client therefore can't distinguish "dealt with" from "snoozed until Thursday", and can't see the Later queue at all. A snoozed status value, or reminders on the conversation header, would fix it.

    Javier - Developer Relations
    Community Manager
    August 17, 2026

    Update regarding snooze...this status should now be exposed to the MCP server, both to read and to set! Please let us know if you see any issues with it!

    Javier - Front Developer Relations
    Javier - Developer Relations
    Community Manager
    August 25, 2026

    @jay I posted in a couple other threads but might have missed it here, you should now be able to view a conversation’s personal snooze status.

    Javier - Front Developer Relations