Add DeferChangedEvents() to McpServerPrimitiveCollection for batched change notifications#1689
Add DeferChangedEvents() to McpServerPrimitiveCollection for batched change notifications#1689jeffhandley with Copilot wants to merge 10 commits into
Conversation
… notifications - Add _deferralDepth and _pendingChange int fields for thread-safe deferral tracking - Update RaiseChanged() to defer notification when _deferralDepth > 0 - Add DeferChanges() public method returning a DeferralScope : IDisposable - Add EndDeferral() private method using Interlocked ops for atomicity - Add unit tests: no-mutation scope, single/multi-mutations, mixed add+remove, Clear, nested scopes, idempotent dispose, no-handler case, baseline behavior Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
- Rename DeferralScope to ChangeDeferralScope - Add preexisting Changed behavior tests (TryAdd, Remove, Clear -- with and without success) - Add DeferChanges tests for duplicate TryAdd (with and without prior entry) - Add DeferChanges test for TryAdd-then-Remove same tool (fires one Changed even though net content is unchanged) Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
tarekgh
left a comment
There was a problem hiding this comment.
Faithful implementation of the agreed design, and it picks up the gate-at-RaiseChanged and net-zero-fires points. One correctness issue on the concurrency handling, plus a small doc gap, noted inline.
|
A couple of tests are missing to back the review points, plus a few worth adding: Required:
Recommended:
|
|
@copilot Please address the following excellent feedback from @tarekgh:
|
Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
…erralDepth->_activeDeferralScopes Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
…in exception-safety sentence Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
jeffhandley
left a comment
There was a problem hiding this comment.
@copilot add unit tests that verify out-of-order disposals result in the Changed event being raised once all deferrals have been disposed. This can be achieved by capturing the IDisposable instances and calling Dispose() rather than with using blocks.
These tests should ensure re-disposing the same deferral instance multiple times also does not result in the count decrementing more than once (potentially leading to the Changed event being raised while there are still undisposed deferrals.
With this, we should change the "outermost" phrasing to say once all deferrals have been disposed, as the nesting isn't a requirement.
If you find bugs with these new tests, fix them.
…rmost phrasing Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
Done in the latest commit. Added four new tests:
Updated the |
Fixes #1688
Registering a batch of N tools/prompts/resources emits N
list_changedwire notifications -- one perTryAdd-- instead of one. This can trip clients that debounce or rate-limit notifications, and is wasteful for progressive-disclosure patterns.Changes
DeferChangedEvents()onMcpServerPrimitiveCollection<T>-- returns anIDisposablescope that suppressesChangedevents. A single notification fires when the outermost scope disposes, only if at least one mutation occurred. Nesting is supported via a depth counter._activeDeferralScopesand_hasDeferredChangeEventsare guarded by_deferralLock.RaiseChanged()defers when_activeDeferralScopes > 0, andChangeDeferralScope.Dispose()is idempotent viaInterlocked.Exchange.Changedis invoked outside the lock.Clear, nested scopes, idempotent dispose, no-handler, baseline non-deferred behavior, concurrent mutations, mutation racing with dispose, derived-type coalescing, exception-safety, and prompt/resource collection coverage.McpServerBuilderExtensionsToolsTests,McpServerBuilderExtensionsPromptsTests, andMcpServerBuilderExtensionsResourcesTestsasserting exactly one wire notification for a batch add.Usage