Misleading "server does not advertise tools capability" log leaks onto the terminal after CLI exit

Status Open
Reported on v2.1.237
Maintainer reply None cached
Activity 1 comment · opened Aug 20, 2026

Version: @anthropic-ai/claude-code 2.1.237 (npm global install), macOS 26.5.2 (Darwin 25.5.0, arm64), zsh

What happens

After exiting an interactive CLI session, this line is left behind on the terminal:

Client.listTools() called but server does not advertise tools capability - returning empty list

Why the wording is wrong

The message blames the server, but no server is at fault. In my case all configured MCP
servers do advertise tools — confirmed both from /mcp (e.g. github: connected,
authenticated, tools · resources · prompts, 44 tools) and from direct initialize
probes (convex{"tools":{}}, Mintlify{"tools":{"listChanged":true},...}).

The empty capabilities are client-side state after close(). From the bundled SDK in
bin/claude.exe:

listTools(e,t){
  if(!this._serverCapabilities?.tools && !this._enforceStrictCapabilities)
    return console.debug("Client.listTools() called but server does not advertise tools capability - returning empty list"), {tools:[]}
  ...
}

_resetConnectionState(){ ...this._serverCapabilities = void 0, ... }

async close(){ try{ await super.close() } finally { this._resetConnectionState() } }

_enforceStrictCapabilities = t?.enforceStrictCapabilities ?? !1 — always false in practice,
so the branch logs and returns an empty list rather than throwing.

So the sequence is: connection closes → _serverCapabilities cleared → an in-flight or
late listTools() lands → this branch fires.

Why it is only visible after exit

The TUI runs in the alternate screen buffer. Anything printed during the session is
discarded with it; only what is printed after \e[?1049l survives on the normal
screen. So it is not that the call only happens at exit — it is that only the ones at
exit leave a trace.

Impact

Cosmetic. Empty list, no throw, no tool loss. But it is confusing: the wording sends
people to audit their MCP server configuration, which is the wrong place. Two separate
debugging sessions on my machine were misled by it before reading the shipped code.

The same wording exists for listPrompts, listResources and listResourceTemplates.

Suggested fixes (any one would help)

  1. Skip the log when the client has been closed / capabilities were reset — a post-close

listTools() is expected, not noteworthy.

  1. Include the server name in the message. Without it, the message is un-attributable;

there is no way for a user to tell which of N servers it refers to.

  1. Reword to describe client state, e.g.

Client.listTools() called with no negotiated tools capability (client closed or not yet initialized) - returning empty list.

  1. Route it to the --debug-file sink rather than stdout, so it cannot land on the

user's terminal after exit.

Reproduction

Intermittent — roughly 1 in 19 claude -p "…" runs on my machine; it fires much more
reliably on interactive exit. It is not tied to any particular server: with
--strict-mcp-config and each server isolated (including a deliberately broken one that
fails with HTTP 400), the line does not appear.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗