feat(mcp): Option B pass-through - callback wiring, bug fixes, events, config loading#1
Open
agent-of-mkmeral wants to merge 2 commits into
Open
feat(mcp): Option B pass-through - callback wiring, bug fixes, events, config loading#1agent-of-mkmeral wants to merge 2 commits into
agent-of-mkmeral wants to merge 2 commits into
Conversation
…, config loading ## Bug Fixes - **_meta extraction**: Extract `_meta` from tool arguments dict and pass as `meta=` keyword argument to `session.call_tool()`. This enables protocol-level metadata (e.g., progress tokens) to flow through correctly. The original arguments dict is not mutated. - **isError field**: Add `isError` field to `MCPToolResult` TypedDict and populate it from `call_tool_result.isError`. Downstream consumers can now distinguish error results without inspecting the status string. ## Callback Wiring (Option B: Pass-Through) Wire ALL remaining `ClientSession` callbacks through `MCPClient`: - `sampling_callback`: Handle `sampling/createMessage` requests from server - `list_roots_callback`: Handle `roots/list` requests from server - `logging_callback`: Handle log notifications from server (defaults to routing MCP log levels to Python `logging`) - `progress_callback`: Per-call progress notifications passed to `call_tool()` - `elicitation_callback`: Already existed, now documented alongside others Default logging callback maps MCP levels (emergency→CRITICAL, error→ERROR, warning→WARNING, notice/info→INFO, debug→DEBUG) to Python logging. ## MCPEvent Hook Hierarchy New `src/strands/hooks/mcp_events.py` with typed events: - `MCPServerConnectedEvent` / `MCPServerDisconnectedEvent` - `MCPToolCallStartEvent` / `MCPToolCallEndEvent` - `MCPLoggingEvent`, `MCPProgressEvent`, `MCPSamplingRequestEvent` All events extend `BaseHookEvent` and work with `HookRegistry`. ## Config Loading New `load_mcp_servers()` utility function in `mcp_config.py`: - Load from JSON file, JSON string, or dict - Standard `mcpServers` format (Claude Desktop compatible) - Supports `disabled`, `disabledTools`, `prefix`, stdio and HTTP transports - Shared callbacks applied to all created MCPClient instances ## Option B Design - NO Agent changes - users wire callbacks manually on MCPClient - No MCPRegistry, no MCPPlugin, no Agent modifications - Minimal, composable, pass-through approach ## Tests 44 new tests covering all changes: - _meta extraction (4 tests) - isError field (3 tests) - Callback wiring (6 tests) - Default logging callback (5 tests) - Config loading (15 tests) - MCP events (9 tests) - MCPToolResult type (2 tests) Updated 10 existing tests to account for new call_tool() kwargs.
Bug fixes: - fix(mcp_config): load_mcp_servers(logging_callback=None) now correctly disables logging instead of falling back to MCPClient default. Uses sentinel _UNSET to distinguish 'not provided' from 'explicitly None'. Adversarial tests (57 new tests): - _meta extraction edge cases (None, string, empty dict) - Default logging callback with non-string data - Config loading boundary conditions - isError backward compatibility - MCP event immutability and defaults - stop() without start() - __init__.py export completeness - HookRegistry integration with reverse callback ordering - Simultaneous progress callbacks - Structured content and metadata pass-through Contract violations documented (not fixed, as behavior may be intentional): - Docstring claims FileNotFoundError for missing paths, but ValueError raised - Docstring claims ValueError for missing mcpServers key, but returns []
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Option B (MCP as Pass-Through) for deeper MCP integration in the Strands Agents SDK. This approach exposes all MCP ClientSession callbacks directly through
MCPClientwithout modifying the Agent class.Changes
Bug Fixes
_metaextraction (mcp_client.py): The_metakey is now extracted from tool arguments and passed asmeta=tosession.call_tool(). This enables protocol-level metadata (e.g., progress tokens) to flow correctly. The original arguments dict is not mutated.isErrorfield (mcp_types.py,mcp_client.py): AddedisError: NotRequired[bool]toMCPToolResultTypedDict, populated fromcall_tool_result.isError. Downstream consumers can now distinguish error results without parsing the status string.Callback Wiring (Pass-Through)
All remaining
ClientSessioncallbacks are exposed onMCPClient.__init__()and wired to the session:sampling_callbacksampling/createMessagefrom serverNonelist_roots_callbackroots/listfrom serverNonelogging_callbackloggingprogress_callbackcall_tool()Noneelicitation_callbackNoneThe default logging callback maps MCP levels to Python levels:
emergency,alert,critical→CRITICALerror→ERRORwarning→WARNINGnotice,info→INFOdebug→DEBUGMCP Event Hook Hierarchy (
src/strands/hooks/mcp_events.py)New typed events for MCP operations, compatible with
HookRegistry:MCPServerConnectedEvent,MCPServerDisconnectedEventMCPToolCallStartEvent,MCPToolCallEndEventMCPLoggingEvent,MCPProgressEvent,MCPSamplingRequestEventConfig Loading (
src/strands/tools/mcp/mcp_config.py)load_mcp_servers()utility function:mcpServersformat (Claude Desktop / Cursor compatible)disabled,disabledTools,prefix, stdio and HTTP transportsOption B Design Decisions
Tests
44 new tests (
test_mcp_option_b.py):_metaextraction (4 tests)isErrorfield (3 tests)10 existing tests updated to account for new
call_tool()kwargs.All 172 MCP tests pass. All 31 hooks tests pass.
Usage Example