Skip to content

Reintroduce sink interceptor type - #28

Open
PederHP wants to merge 5 commits into
mainfrom
feat/reintroduce-sink-type
Open

PederHP wants to merge 5 commits into
mainfrom
feat/reintroduce-sink-type

Conversation

@PederHP

@PederHP PederHP commented Jun 18, 2026 •

Copy link
Copy Markdown
Member

Adds a third interceptor type — sink — to the working copy of the SEP (docs/sep.md).

What

A sink is a non-blocking, non-mutating interceptor that reacts to a context operation without affecting the interaction. It MUST NOT block execution and MUST NOT modify the payload — it exists to drive concurrent UX, workflow state, or downstream pipelines based on what the agent is doing.

Use cases: avatar/character animation from conversation mood, voice-mode / TTS switching, real-time UI affordances, background context indexing, secondary-agent triggers. The C# reference impl already demonstrates the avatar case end-to-end in AvatarMoodInterceptorSample.

Changes to docs/sep.md:

  • Type union extended to "validation" | "mutation" | "sink" (all sites)
  • New Sink section parallel to Validator / Mutator, with SinkResult
  • Execution model: sink as a third stage (parallel, fire-and-forget) on each phase; Type Behaviors table updated
  • Sinks exempt from aggregate timeouts, failOpen, and fail-closed requirements
  • New avatar-mood sink example in discovery (audit-logger stays an audit-mode validator, since compliance logging must also capture rejected operations)
  • Rationale restructured: separation of the three types, why sink vs mode: "audit", and why "sink" and not "observability" (avoids implying OpenTelemetry overlap)

Sinks are additive to the validator/mutator split and mode: "audit": audit mode disables enforcement while preserving a validator/mutator's interface; sinks are structurally non-enforcing and cannot be promoted to affect the interaction. Implementations MUST reject sinks declared with mode: "audit".

Why here

This re-homes the proposal from Degiorgio/modelcontextprotocol#1 — which targeted the SEP author's personal fork — onto this repo's working copy of the SEP. #21 established docs/sep.md as where the WG maintains and reviews the SEP, so the sink proposal belongs here. Once the WG agrees, it folds upstream into SEP-2624 along with the rest of docs/sep.md.

🤖 Generated with Claude Code

Comment thread docs/sep.md Resolved
@Degiorgio

Degiorgio commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

@PederHP This is looking great, the sink/audit distinction is really clearly argued. One small thing I wanted to check my understanding on:

The Sink section notes that a sink MUST return a SinkResult, while the execution model describes sinks as
parallel, fire-and-forget. I might be missing something, but I wasn't totally sure how those fit together in
practice, if the runtime doesn't wait for the sink, when does it collect the SinkResult for ChainExecutionResult.results?

@PederHP

PederHP commented Sep 12, 2026

Copy link
Copy Markdown
Member Author

@PederHP This is looking great, the sink/audit distinction is really clearly argued. One small thing I wanted to check my understanding on:

The Sink section notes that a sink MUST return a SinkResult, while the execution model describes sinks as parallel, fire-and-forget. I might be missing something, but I wasn't totally sure how those fit together in practice, if the runtime doesn't wait for the sink, when does it collect the SinkResult for ChainExecutionResult.results?

Yeah, the point of this one just to ensure that the request has been "sinked" - it is not a guarantee that the sink has actually been processed. One could argue that the sender can't really do anything with this - which is true - it is not supposed to be blocking, but it might still be useful in some cases to be able to send an error message back. But purely for observability - not something to build logic around. We could have a void-ish type instead - I wouldn't mind.

PederHP and others added 2 commits October 28, 2026 21:19
Adds a third interceptor type, `sink`, for non-blocking, non-mutating
interceptors that react to context to drive concurrent UX, workflow
state, or downstream pipelines (avatar animation, voice-mode triggers,
real-time UI affordances, background indexing) without affecting the
request/response interaction.

Additive to the validator/mutator split and `mode: "audit"`. Audit mode
disables enforcement while preserving the validator/mutator interface;
sinks are structurally non-enforcing and cannot be promoted to affect
the interaction. Renamed from the original draft's `observability` to
`sink` to avoid implying overlap with OpenTelemetry / general logging.

Re-homed from Degiorgio/modelcontextprotocol#1 (a PR against the SEP
author's personal fork) onto this repo's working copy of the SEP, which
PR #21 established as where the WG maintains and reviews the SEP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per review feedback: metrics duplicates what OTel already covers and no
other interceptor result type has an equivalent field. recorded stays,
with explicit async semantics: true is an acknowledgment of acceptance,
false definitively means the event was not recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@PederHP
PederHP force-pushed the feat/reintroduce-sink-type branch from f74f856 to de7906c Compare October 28, 2026 20:19
Copilot AI balanced review requested due to automatic review settings October 28, 2026 20:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The documentation still subjects sinks to blocking result aggregation, aggregate timeouts, and fail-closed requirements, contradicting their required non-blocking behavior.

2 open issues
What changed in this PR

This PR reintroduces sinks in the working-copy interceptor SEP, adding a non-blocking, non-mutating type for context-reactive workflows alongside validators and mutators.

Changes:

  • Defines the sink contract and SinkResult, with audit-logger and avatar-mood examples.
  • Adds fire-and-forget sink execution to the trust-boundary-aware execution model.
  • Explains how sinks differ from audit mode and observability tooling.
File Description
docs/​sep.md Adds sink definitions, execution semantics, examples, and design rationale.

Review effort: Balanced

Comment thread docs/sep.md Resolved
Comment thread docs/sep.md Resolved
Per review feedback: the sink sections say sinks never block, but the
general rules elsewhere in the SEP still applied to all interceptors.

- Orchestration: sinks are dispatched without awaiting completion and
  are excluded from the aggregate timeout; sink results, if collected,
  are consumed asynchronously and never delay the operation
- failOpen (interceptor default and invoker override): ignored for sinks
- InterceptorOverrides.timeoutMs: bounds only the sink's own invocation
- Timeout and Cancellation: a timed-out sink is swallowed like any other
  sink failure
- Security Best Practices #4 (Fail Secure): scoped to validators and
  mutators, with sinks explicitly exempt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MqPQYRWhLmScrZ19A8eKJp
Copilot AI balanced review requested due to automatic review settings October 29, 2026 17:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The documentation claims complete compliance logging even though validation failures skip the sink-based logger, leaving rejected operations unlogged.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Sink logger omits rejected operations from compliance auditing

docs/​sep.md:590

Changing this logger to a sink means it will not run when a receiving validator rejects a request: the new execution model skips sinks on validation failure. As a result, it no longer satisfies the adjacent description, “Logs all MCP operations for compliance,” and omits rejected attempts even when the logger is healthy. Please label this as best-effort post-validation logging rather than a complete compliance audit, and make clear that rejected-operation auditing must be handled separately.

2 resolved since last review

Review effort: Balanced

@PederHP

PederHP commented Oct 29, 2026

Copy link
Copy Markdown
Member Author

@PederHP This is looking great, the sink/audit distinction is really clearly argued. One small thing I wanted to check my understanding on:

The Sink section notes that a sink MUST return a SinkResult, while the execution model describes sinks as parallel, fire-and-forget. I might be missing something, but I wasn't totally sure how those fit together in practice, if the runtime doesn't wait for the sink, when does it collect the SinkResult for ChainExecutionResult.results?

The orchestration change now makes this clear - so sink results are read in the background and never delay the operation.

Per review feedback: as a sink, audit-logger would be skipped when a
receiving validator rejects a request, contradicting its "logs all MCP
operations for compliance" description. Every other example in the SEP
(execution ordering, invoker overrides with promotion to active, context
passing) already treats it as an audit-mode validator.

Compliance auditing that must capture rejected operations belongs in an
audit-mode validator; avatar-mood remains the sink example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MqPQYRWhLmScrZ19A8eKJp
Copilot AI balanced review requested due to automatic review settings October 29, 2026 19:14
@PederHP

PederHP commented Oct 29, 2026

Copy link
Copy Markdown
Member Author

Re the Copilot finding on audit-logger (docs/sep.md:590): fixed in 9a85746 by restoring it as an audit-mode validator, matching main and the other examples in the SEP (execution ordering, invoker overrides, context passing). Compliance logging that must capture rejected operations belongs in the validate stage; avatar-mood remains the sink example.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The documentation at docs/sep.md:1571 still uses “an” before mode, which should be corrected to “a.”

1 open issue

Review effort: Balanced

Comment thread docs/sep.md Resolved
Clarified explanation of audit mode for validators and mutators.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Peder Holdgaard Pedersen <127606677+PederHP@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 29, 2026 20:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues remain for the documentation changes introducing sink interceptors.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants