Make the CLI wait for the MCP tool shim; bump to 0.99.87

A claude_think request with tools registered none: the CLI reported tools:[] at
init with the shim still "pending", so the model had nothing to call and instead
described the calls in prose ("[Tool: list_dir]") with stop_reason=end_turn. The
turn looked successful, so this surfaced as a wrong answer rather than an error.

The CLI connects --mcp-config servers "fully async (nonblocking)" and starts the
turn without waiting, so whether any tools exist is a race between the shim's
handshake and the first API request. Marking the server alwaysLoad puts it on
the CLI's blocking path instead (its own two code paths: alwaysLoad servers are
awaited, the rest are fire-and-forget).

The race is host-dependent, which is why local testing never caught it — my box
won it every time and registered the tools. Confirmed on the box that loses it,
with identical CLI versions:

  without alwaysLoad -> tools=[],                        mcp=pending
  with    alwaysLoad -> tools=['mcp__aisbf__get_weather'], mcp=connected

Ruled out first: the shim is deployed there, both interpreters run it in ~20ms,
and it answers initialize/tools-list correctly under production's python.
Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 294a7273
......@@ -55,7 +55,7 @@ from .auth.qwen import QwenOAuth2
from .handlers import RequestHandler, RotationHandler, AutoselectHandler
from .utils import count_messages_tokens, split_messages_into_chunks, get_max_request_tokens_for_model, get_max_completion_tokens_for_model
__version__ = "0.99.86"
__version__ = "0.99.87"
__all__ = [
# Config
"config",
......
......@@ -469,6 +469,14 @@ class ClaudeProviderHandler(BaseProviderHandler):
'command': sys.executable,
'args': [shim_path],
'env': {'AISBF_MCP_TOOLS_FILE': tools_path},
# Without this the CLI connects --mcp-config servers "fully
# async (nonblocking)" and starts the turn without waiting.
# Losing that race registers zero tools, and the model then
# describes tool calls in prose ("[Tool: list_dir]") instead
# of emitting tool_use — a silent wrong answer, not an error.
# alwaysLoad puts the shim on the CLI's blocking path, so
# the tools are always registered before the first request.
'alwaysLoad': True,
},
},
}
......
......@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "aisbf"
version = "0.99.86"
version = "0.99.87"
description = "AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme = "README.md"
license = "GPL-3.0-or-later"
......
......@@ -106,7 +106,7 @@ class InstallCommand(_install):
setup(
name="aisbf",
version="0.99.86",
version="0.99.87",
author="AISBF Contributors",
author_email="stefy@nexlab.net",
description="AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment