Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions packages/bub-acp-server/tests/test_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ async def test_model_receives_only_current_session_mcp_tools(

seen = []

def run(self, *, tools, **kwargs):
seen.append({tool.name for tool in tools})
def run(self, *, system_prompt, **kwargs):
seen.append(system_prompt or "")

async def events():
yield StreamEvent("text", {"delta": "ok"})
Expand All @@ -227,7 +227,8 @@ async def events():
await agent.prompt(
session_id=session.session_id, prompt=[TextContentBlock(text="hello")]
)
# Bub aliases dots for the model, but execution still uses each session's tool object.
# MCP tools are deferred, so the model sees their aliased names in the prompt;
# execution still uses each session's tool object.
assert "mcp_first_echo" in seen[0] and "mcp_second_echo" not in seen[0]
assert "mcp_second_echo" in seen[1] and "mcp_first_echo" not in seen[1]
finally:
Expand Down
4 changes: 4 additions & 0 deletions packages/bub-mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ The file must contain a top-level `mcpServers` mapping:
}
```

Discovered MCP tools are registered as Bub deferred tools by default: the model only sees their
names until it loads their definitions with `tool.describe`, which keeps large MCP tool sets out of
the prompt. Set `BUB_MCP_DEFERRED_TOOLS=false` to expose them directly instead.

## CLI Usage

Use the CLI to inspect and manage `mcp.json`:
Expand Down
1 change: 1 addition & 0 deletions packages/bub-mcp/src/bub_mcp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class MCPSettings(bub.Settings):

config_path: Path = Field(default_factory=default_config_path)
init_timeout_seconds: float | None = 20.0
deferred_tools: bool = True

def read_mcp_servers(self) -> dict[str, Any]:
return read_config(self.config_path)
Expand Down
1 change: 1 addition & 0 deletions packages/bub-mcp/src/bub_mcp/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ def _build_tool(self, server_name: str, remote_tool: mcp.types.Tool) -> Tool | N
handler=self._make_handler(server_name, remote_name, result_mode),
renderer=_render_tool_value,
output_schema=output_schema,
deferred=self.settings.deferred_tools,
)

def _record_failed_server(
Expand Down
16 changes: 16 additions & 0 deletions packages/bub-mcp/tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,22 @@ def test_build_tool_forwards_remote_output_schema() -> None:
assert tool.render({"temperature": 21.5}) == '{\n "temperature": 21.5\n}'


def test_build_tool_defers_remote_tools_by_default(monkeypatch) -> None:
remote_tool = FakeRemoteTool(
"get_forecast", "Get forecast.", {"type": "object", "properties": {}}
)

channel = plugin.MCPChannel()
tool = channel._build_tool("weather", remote_tool)
assert tool is not None
assert tool.deferred is True

monkeypatch.setattr(channel.settings, "deferred_tools", False)
tool = channel._build_tool("weather", remote_tool)
assert tool is not None
assert tool.deferred is False


def test_fastmcp_wrapped_results_are_unwrapped() -> None:
remote_tool = FakeRemoteTool(
"describe", "Describe.", {"type": "object", "properties": {}}
Expand Down
4 changes: 2 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading