Typically, a network communication system, like gRPC for example, will accept contexts for network calls and turn context done-ness into an error. The typical signature of an RPC-backed function is
func(context.Context, req *Request) (*Response, error)
If the context times out or is cancelled, a good RPC library will return from this call immediately with a non-nil error. So context done-ness checking is just part of ordinary error checking.
I'm not sure how you're mapping the RPCs to channels. If you're doing
res, err := RPC(ctx, req)
if err != nil { return ... }
ch <- res
then you indeed have a problem: you'll need another check whenever you receive from the channel. But if you're doing
res, err := RPC(ctx, req)
ch <- rpcResult{res, err}
then receivers don't have to check ctx.Done, but of course they do have to check the error:
result <- ch
if result.err != nil { ...}
// use result.res