Conversation
POST /mcp only called list_tools once, so paginated MCP servers dropped later tools from the connect payload and composer list. Walk nextCursor, cap at 100 pages, and stop on a repeated cursor.
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Pagination awaits were unbounded after initialize, and a later tools/list page could fail after the new session was already stored. Bound each page and the whole walk with the connect timeout, and only swap after listing succeeds so a failed reconnect does not evict a working session.
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/chainlit/server.py">
<violation number="1" location="backend/chainlit/server.py:1825">
P2: When `/mcp` disconnects the same name while pagination is in progress, the later `swap_mcp_session` recreates the session after DELETE has completed. Coordinate pending connects with disconnects, or invalidate the in-flight connect before allowing it to store the new session.</violation>
<violation number="2" location="backend/chainlit/server.py:1830">
P1: When the request is cancelled during `list_all_mcp_tools`, `asyncio.CancelledError` bypasses this `except Exception`, so `stop_mcp_task` never runs. Catch cancellation, stop the task, then re-raise; the pre-swap listing otherwise leaves the transport unreachable from `mcp_sessions`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| page_timeout=connect_timeout, | ||
| total_timeout=connect_timeout, | ||
| ) | ||
| except Exception as e: |
There was a problem hiding this comment.
P1: When the request is cancelled during list_all_mcp_tools, asyncio.CancelledError bypasses this except Exception, so stop_mcp_task never runs. Catch cancellation, stop the task, then re-raise; the pre-swap listing otherwise leaves the transport unreachable from mcp_sessions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/chainlit/server.py, line 1830:
<comment>When the request is cancelled during `list_all_mcp_tools`, `asyncio.CancelledError` bypasses this `except Exception`, so `stop_mcp_task` never runs. Catch cancellation, stop the task, then re-raise; the pre-swap listing otherwise leaves the transport unreachable from `mcp_sessions`.</comment>
<file context>
@@ -1821,11 +1821,41 @@ async def _mcp_session_runner() -> None:
+ page_timeout=connect_timeout,
+ total_timeout=connect_timeout,
+ )
+ except Exception as e:
+ # Same teardown as a rejecting on_mcp_connect: tools/list is part of
+ # making the connection usable, and it runs *before* swap so a
</file context>
| except Exception as e: | |
| except asyncio.CancelledError: | |
| await stop_mcp_task(task, stop_event, payload.name) | |
| raise | |
| except Exception as e: |
| ) | ||
|
|
||
| try: | ||
| tools = await list_all_mcp_tools( |
There was a problem hiding this comment.
P2: When /mcp disconnects the same name while pagination is in progress, the later swap_mcp_session recreates the session after DELETE has completed. Coordinate pending connects with disconnects, or invalidate the in-flight connect before allowing it to store the new session.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/chainlit/server.py, line 1825:
<comment>When `/mcp` disconnects the same name while pagination is in progress, the later `swap_mcp_session` recreates the session after DELETE has completed. Coordinate pending connects with disconnects, or invalidate the in-flight connect before allowing it to store the new session.</comment>
<file context>
@@ -1821,11 +1821,41 @@ async def _mcp_session_runner() -> None:
)
+ try:
+ tools = await list_all_mcp_tools(
+ mcp_client_session,
+ page_timeout=connect_timeout,
</file context>
Description
POST /mcpcalledClientSession.list_tools()once. The MCP Python SDK returns a single page and leavesnextCursorto the caller, so servers that paginatetools/listsilently dropped later tools from the connect payload and the composer MCP list.This walks
nextCursor(and thenext_cursoralias), caps the walk at 100 pages, and stops if a cursor repeats.Why
MCP pagination is part of the spec;
list_tools()does not auto-paginate. The UI only sees the tools returned here.Testing
uv run pytest backend/tests/test_mcp.py::TestListAllMcpToolsuv run pytest backend/tests/test_mcp.py::TestConnectMcpEndpoint::test_connect_follows_list_tools_pagination(needs frontend build)uv run scripts/lint.py backend/chainlit/mcp.py backend/chainlit/server.py backend/tests/test_mcp.pyuv run scripts/format.py --checkon the same filesSummary by cubic
Fixes
POST /mcpso paginated MCP servers no longer silently drop later tools from the connect payload and composer MCP list, and ensures a failed or hung tools/list cannot evict a working session.nextCursor(and thenext_cursoralias) until the server omits it, capped at 100 pages and stopping on a repeated cursor.Written for commit de819a6. Summary will update on new commits.