Skip to content

[Bug]: TaskEventStream methods don't receive ServerCallContext #1374

Open

Description

@Varun9002

What happened?

Version: a2a-sdk 1.2.1

Problem

TaskEventStream (a2a/server/cluster/event_stream.py L17–32) is keyed only by task_id:

async def publish(self, task_id: str, event: VersionedEvent) -> None: ...
def subscribe(self, task_id: str, *, after: TaskVersion) -> AsyncGenerator[VersionedEvent, None]: ...
async def destroy(self, task_id: str) -> None: ...

TaskStore, VersionedTaskStore and PushNotificationConfigStore.{set,get,delete}_info all take context: ServerCallContext. TaskEventStream and PushNotificationConfigStore.get_info_for_dispatch don't.

Context-derived identity is already central to the SDK — OwnerResolver = Callable[[ServerCallContext], str], and DatabaseTaskStore scopes every query by owner = self.owner_resolver(context). TaskEventStream can't participate.

Impact

DefaultRequestHandlerV2 holds one event_stream from construction (L143) and calls publish (L304) / subscribe (L571) with no caller information, so an implementation is permanently bound to its startup resources. That rules out selecting a backing resource per caller, refreshing credentials at a request boundary, propagating auth downstream, or request-scoped tracing — all of which TaskStore supports today.

Related: DatabaseTaskEventStream filters only on task_id/seq, never on the owner column it writes. Authorization is enforced upstream (_versioned_store.get(task_id, context) precedes subscribe), so the default composition is safe — but the stream can't check independently.

Proposed change

Add context: ServerCallContext to the three methods, optional for source compatibility:

async def publish(self, task_id: str, event: VersionedEvent, context: ServerCallContext | None = None) -> None: ...
def subscribe(self, task_id: str, *, after: TaskVersion, context: ServerCallContext | None = None) -> ...: ...
async def destroy(self, task_id: str, context: ServerCallContext | None = None) -> None: ...

The handler already holds the context at both call sites.

get_info_for_dispatch has the same gap — dispatch runs after the request completes, so it needs a stored context, not a live one.

Relevant log output

Code of Conduct

  • I agree to follow this project's Code of Conduct
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions