feat(server): thread ToolContext through TestControllerStore (closes #227)#237
Merged
feat(server): thread ToolContext through TestControllerStore (closes #227)#237
Conversation
…227) TestControllerStore is storyboard-shaped — scenarios dispatch via the comply_test_controller skill. Downstream sellers whose test runtime reads request headers (AdCPTestContext.from_headers) had no way to compose header-driven mock state with the SDK's storyboard testing. register_test_controller now accepts the same context_factory as create_mcp_server. The dispatcher builds a ToolContext per call and threads it into store methods that declare a `*, context` keyword. Backward-compat: the dispatcher uses inspect.signature to detect the opt-in. Stores that don't declare `context` keep working unchanged. Also: - serve() auto-wires context_factory into register_test_controller - a2a_server executor now passes its ToolContext into the store dispatch - docs/handler-authoring.md gets a "Testing hooks" section documenting the header-driven + storyboard-driven composition pattern Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code reviewer: - _accepts_context_kwarg now rejects positional-only `context`. A method like `def fn(self, context, /, ...)` would previously register as opted-in but raise TypeError when the dispatcher passes `context=ctx` by keyword. - Documented the functools.wraps caveat — inspect.signature follows __wrapped__, which is the intended contract but worth noting. - Added tests: positional-only context, @functools.wraps-preserving decorator (both legacy and modern signatures), inherited override from an intermediate base class via mixin. Security reviewer (L1): - TestHeaderMiddleware docs example now resets the ContextVar token in finally. Without reset(token) the set value leaks into the next request reusing the asyncio task — same shape as the PR #232 cross- tenant idempotency scoping issue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Closes #227 — composes header-driven test runtime (e.g. salesagent's
AdCPTestContext.from_headers(request.headers)) with the SDK's storyboard-drivenTestControllerStore.register_test_controller(mcp, store, *, context_factory=None)accepts the samecontext_factoryascreate_mcp_server. Per-call ToolContext threads into store methods that opt in.*, context: ToolContext | None = None. Dispatcher detects viainspect.signature— legacy stores without the kwarg keep working unchanged.serve()auto-wirescontext_factoryintoregister_test_controller._handle_test_controller.docs/handler-authoring.mdshowing the header-driven + storyboard-driven composition side-by-side.Rationale
The issue proposed a parallel
test_context_factory. Rejected in favor of reusing the existingcontext_factorybecause:ToolContext.metadata[\"test_context\"], readable by both regular handler methods AND store methods — not just the storyboard skill.*, context=None) keeps backward compat watertight.Test plan
_accepts_context_kwargdetects keyword-only +**kwargs, rejects legacy signaturescontextkwarg keeps workingcontext=Nonepath doesn't pass None to legacy-shape methodsregister_test_controllerwithcontext_factorypattern using a ContextVar (the documented salesagent shape)🤖 Generated with Claude Code