Skip to content

fix(a2a): trace Go server calls through official SDK hooks - #478

Merged
nik13 merged 9 commits into
devfrom
fix/a2a-go-server-tracing
Nov 2, 2026
Merged

nik13 merged 9 commits into
devfrom
fix/a2a-go-server-tracing

Conversation

@nik13

@nik13 nik13 commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add a maintained Go server interceptor so an A2A receiving agent continues the caller's trace without application-owned propagation code.

  • NewServerInterceptor implements the official a2asrv.CallInterceptor hook for the same five protocol methods as the existing client interceptor, across JSON-RPC, REST and gRPC. Existing client entry points remain unchanged.
  • Share request, response, content-redaction and error handling. Preserve local transport parents and exclude headers, credentials and baggage from recorded attributes.
  • Finalize nested in-process calls from context-local state rather than the SDK's shared mutable CallContext.Method. Untraced nested methods cannot end an outer span or replace its output.
  • Add ServerInterceptor.Wrap for response-rejecting middleware. The SDK skips remaining After hooks when middleware returns an error; the adapter records that error and completes the server span before returning it unchanged. It preserves callback ordering and response mutations, without creating another span. Register tracing first, then wrap each interceptor that can return an After error.
  • Add maintained WrapClient / WrapHandler completion wrappers for clean EOF (including persisted nonterminal snapshots) and early abandonment. Wrapped clients and server spans stay open across active auth-required events. Existing interceptor-only clients retain their historical successful completion at an auth-required task or status event, so persisted authorization snapshots do not require a client migration. Use WrapClient to capture events after authorization and observe iterator completion; bare SDK hooks have no EOF callback. Each call must own its completion slot: nested unwrapped calls cannot borrow an outer wrapper. The wrappers complete existing spans without adding spans or changing events/errors. The latest snapshot/status message is retained subject to output redaction; a later status clears stale authorization text.
  • Honor FI_HIDE_ERROR_DETAILS and TraceConfig.HideErrorDetails (optional *bool) on client and server spans. Without an explicit override, input/output, message and text hiding derive error-detail hiding. Explicit false overrides derivation; true removes error text and status descriptions while retaining error class and ERROR status. Constructor options are snapshotted.
  • Require a2a-go v2.7.0 or later: earlier versions skip unary completion hooks for missing required extensions. Use the fixed upstream implementation instead of duplicating SDK validation.
  • Replace the example's handwritten server tracing with the maintained interceptor. Document cancellation/background-task boundaries and install the exporter dependencies needed by the setup snippets.

Verification

Real SDK tests cover all three transports and direct calls, parent chains, streams, task operations, protocol/middleware rejections, disconnects, redaction and nested calls. Response-policy regressions assert that rejected payloads are not captured, earlier policy callbacks remain skipped, and the original error is retained. Removing rejection observation makes the regression tests fail.

The two independent review findings were reproduced before repair on both toolchains: error text reached actual OTLP protobuf despite the privacy flag, and successful persisted auth snapshots remained open or became canceled errors. Both have committed regression coverage. The follow-up interceptor-only compatibility finding was also reproduced on both toolchains before repair. Its unchanged independent public-API reproduction passes 20 race-enabled repetitions per toolchain after repair. Committed tests exercise interceptor-only and wrapped clients against an uninstrumented official server over JSON-RPC, REST and gRPC, plus task/status events on both streaming methods and owned, absent, already-claimed, mismatched and server-only completion slots.

All 416 tests (including subtests; 59 top-level) pass with the race detector on Go 1.26.0 and 1.27.3. The supported minimum and current SDK release are both v2.7.0, not two distinct SDK versions. The privacy/completion/response-policy regressions pass 20 repeated race-enabled runs per toolchain. Tests cover true/false/derived/invalid privacy settings, code precedence, error classes, actual wire payloads, active auth streams, snapshot EOF, early abandonment, empty iterators, errors, panic propagation, context cancellation/deadlines, iterator reuse and duplicate-wrapper safety. Package checks include tidy, verify, vet, the example build, committed-module archives and the release plan. The latest dev base was normally merged; frozen-lockfile TypeScript installation with scripts disabled also passes. Fresh consumers compile the documentation snippets from the install commands without inherited sums or a corrective tidy. Loopback OTLP tests cover protobuf plain/gzip and Collector-encoded JSON plain/gzip; the stock Go exporter itself emits protobuf.

No package-version or changelog edits. The docs-site update remains held until this API is released and should be folded into existing docs PR #969 rather than duplicated.

Generated with Claude Code

nik13 and others added 3 commits October 31, 2026 18:21
Co-Authored-By: Claude Code <noreply@anthropic.com>
Keep unary status-message capture independent of stream termination. Require a2a-go v2.7.0 so required-extension rejections run completion hooks, with regression and OTLP privacy coverage.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Finalize each server call from its context-local state instead of the SDK shared mutable method. Shadow inherited state for untraced nested calls so their responses cannot finish the outer span.

Co-Authored-By: Claude Code <noreply@anthropic.com>
nik13 and others added 2 commits October 31, 2026 20:40
Wrap response-rejecting middleware so tracing sees its actual error before the SDK short-circuits remaining callbacks. Preserve policy ordering, response mutation and error identity, with real SDK, nested-call and OTLP regression coverage. Resolve exporter dependencies in the setup instructions.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@nik13 nik13 changed the title feat(a2a): trace Go server calls through official SDK hooks fix(a2a): trace Go server calls through official SDK hooks Nov 2, 2026
nik13 and others added 4 commits November 2, 2026 07:00
Co-Authored-By: Claude Code <noreply@anthropic.com>
Derive error-detail hiding from content flags with an explicit override. Add maintained client and handler completion wrappers because SDK interceptors do not receive iterator EOF; keep active auth-required streams open and distinguish early abandonment. Cover privacy on actual OTLP exports and completion across all three transports.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Keep the legacy auth-required completion boundary only for client calls without their own stream-completion hook. Wrapped clients and server streams continue recording active authorization streams. Cover both client APIs across transports and nested completion-slot ownership.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@nik13
nik13 merged commit 045f5f0 into dev Nov 2, 2026
39 checks passed
@nik13
nik13 deleted the fix/a2a-go-server-tracing branch November 2, 2026 16:43
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.

1 participant