diff --git a/BUILD b/BUILD
index 81095490e6b35a83b3f22a6c4a8d795ee3bf4125..01c085f188758e9789187cbae41d33dbc53e892d 100644
--- a/BUILD
+++ b/BUILD
@@ -171,6 +171,7 @@ cc_library(
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -327,6 +328,7 @@ cc_library(
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -564,6 +566,7 @@ cc_library(
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -706,6 +709,7 @@ cc_library(
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -914,6 +918,7 @@ cc_library(
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -1047,6 +1052,7 @@ cc_library(
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -1259,6 +1265,7 @@ cc_library(
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -1372,6 +1379,7 @@ cc_library(
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -1665,6 +1673,7 @@ cc_library(
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -1773,6 +1782,7 @@ cc_library(
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -2160,6 +2170,7 @@ objc_library(
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -2376,6 +2387,7 @@ objc_library(
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 62703653b113bb4786603660848f70ef30b5359d..116824f9948df4bcfc32a9424d3b2b3f90c2b2fd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -298,6 +298,7 @@ add_library(grpc
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
@@ -553,6 +554,7 @@ add_library(grpc_cronet
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
@@ -781,6 +783,7 @@ add_library(grpc_unsecure
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
@@ -1035,6 +1038,7 @@ add_library(grpc++
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
@@ -1387,6 +1391,7 @@ add_library(grpc++_unsecure
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
diff --git a/Makefile b/Makefile
index 98266f617c18b71fc4d61e6fb0d3b7392cd5fb71..a0b4d380c7920c8865b43c029260621130c21796 100644
--- a/Makefile
+++ b/Makefile
@@ -2527,6 +2527,7 @@ LIBGRPC_SRC = \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -2800,6 +2801,7 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -3062,6 +3064,7 @@ LIBGRPC_TEST_UTIL_SRC = \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -3252,6 +3255,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -3589,6 +3593,7 @@ LIBGRPC++_SRC = \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -4216,6 +4221,7 @@ LIBGRPC++_UNSECURE_SRC = \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
diff --git a/binding.gyp b/binding.gyp
index b4f182c4b74ec177e016f1cac9cedd059ccdcf78..9cad740882592b3fb146f3f41069a8ffbf3db9b6 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -573,6 +573,7 @@
         'src/core/lib/channel/handshaker.c',
         'src/core/lib/channel/http_client_filter.c',
         'src/core/lib/channel/http_server_filter.c',
+        'src/core/lib/channel/message_size_filter.c',
         'src/core/lib/compression/compression.c',
         'src/core/lib/compression/message_compress.c',
         'src/core/lib/debug/trace.c',
diff --git a/build.yaml b/build.yaml
index 65cdea7c908ed8c94eafa320ba4b0f5b164d9802..7e0dcf889664dab20390fc49a34349a28f9ff7f1 100644
--- a/build.yaml
+++ b/build.yaml
@@ -173,6 +173,7 @@ filegroups:
   - src/core/lib/channel/handshaker.h
   - src/core/lib/channel/http_client_filter.h
   - src/core/lib/channel/http_server_filter.h
+  - src/core/lib/channel/message_size_filter.h
   - src/core/lib/compression/algorithm_metadata.h
   - src/core/lib/compression/message_compress.h
   - src/core/lib/debug/trace.h
@@ -254,6 +255,7 @@ filegroups:
   - src/core/lib/channel/handshaker.c
   - src/core/lib/channel/http_client_filter.c
   - src/core/lib/channel/http_server_filter.c
+  - src/core/lib/channel/message_size_filter.c
   - src/core/lib/compression/compression.c
   - src/core/lib/compression/message_compress.c
   - src/core/lib/debug/trace.c
diff --git a/config.m4 b/config.m4
index 5947306c397ba1ad60cd966184d0071321854675..ed6cba1497fdecb599c7050d0f1284f6524481df 100644
--- a/config.m4
+++ b/config.m4
@@ -92,6 +92,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
diff --git a/doc/core/pending_api_cleanups.md b/doc/core/pending_api_cleanups.md
new file mode 100644
index 0000000000000000000000000000000000000000..6d30e0b0e6394af9fddc56f06ee41c254f964a9b
--- /dev/null
+++ b/doc/core/pending_api_cleanups.md
@@ -0,0 +1,15 @@
+There are times when we make changes that include a temporary shim for
+backward-compatibility (e.g., a macro or some other function to preserve
+the original API) to avoid having to bump the major version number in
+the next release.  However, when we do eventually want to release a
+feature that does change the API in a non-backward-compatible way, we
+will wind up bumping the major version number anyway, at which point we
+can take the opportunity to clean up any pending backward-compatibility
+shims.
+
+This file lists all pending backward-compatibility changes that should
+be cleaned up the next time we are going to bump the major version
+number:
+
+- remove `GRPC_ARG_MAX_MESSAGE_LENGTH` channel arg from
+  `include/grpc/impl/codegen/grpc_types.h` (commit `af00d8b`)
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index c7407874163f9cc633213df292c8f4958e491e47..92e113656be64aaec5542909a5e5781a7cfd8f9d 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -258,6 +258,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/handshaker.h',
                       'src/core/lib/channel/http_client_filter.h',
                       'src/core/lib/channel/http_server_filter.h',
+                      'src/core/lib/channel/message_size_filter.h',
                       'src/core/lib/compression/algorithm_metadata.h',
                       'src/core/lib/compression/message_compress.h',
                       'src/core/lib/debug/trace.h',
@@ -418,6 +419,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/handshaker.c',
                       'src/core/lib/channel/http_client_filter.c',
                       'src/core/lib/channel/http_server_filter.c',
+                      'src/core/lib/channel/message_size_filter.c',
                       'src/core/lib/compression/compression.c',
                       'src/core/lib/compression/message_compress.c',
                       'src/core/lib/debug/trace.c',
@@ -623,6 +625,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/channel/handshaker.h',
                               'src/core/lib/channel/http_client_filter.h',
                               'src/core/lib/channel/http_server_filter.h',
+                              'src/core/lib/channel/message_size_filter.h',
                               'src/core/lib/compression/algorithm_metadata.h',
                               'src/core/lib/compression/message_compress.h',
                               'src/core/lib/debug/trace.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 5d851089349575eb75cc4a84c438f09ead08d0e2..c3f53ec73387f795f503074280e22119ebc8b785 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -178,6 +178,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/handshaker.h )
   s.files += %w( src/core/lib/channel/http_client_filter.h )
   s.files += %w( src/core/lib/channel/http_server_filter.h )
+  s.files += %w( src/core/lib/channel/message_size_filter.h )
   s.files += %w( src/core/lib/compression/algorithm_metadata.h )
   s.files += %w( src/core/lib/compression/message_compress.h )
   s.files += %w( src/core/lib/debug/trace.h )
@@ -338,6 +339,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/handshaker.c )
   s.files += %w( src/core/lib/channel/http_client_filter.c )
   s.files += %w( src/core/lib/channel/http_server_filter.c )
+  s.files += %w( src/core/lib/channel/message_size_filter.c )
   s.files += %w( src/core/lib/compression/compression.c )
   s.files += %w( src/core/lib/compression/message_compress.c )
   s.files += %w( src/core/lib/debug/trace.c )
diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h
index 20087289f8fbb0c4acf73cf4887264b8eb97dea7..e211373e7d43bf500d112f679f336c0918d95aa1 100644
--- a/include/grpc++/impl/codegen/call.h
+++ b/include/grpc++/impl/codegen/call.h
@@ -175,7 +175,7 @@ template <int I>
 class CallNoOp {
  protected:
   void AddOp(grpc_op* ops, size_t* nops) {}
-  void FinishOp(bool* status, int max_message_size) {}
+  void FinishOp(bool* status, int max_receive_message_size) {}
 };
 
 class CallOpSendInitialMetadata {
@@ -213,7 +213,7 @@ class CallOpSendInitialMetadata {
     op->data.send_initial_metadata.maybe_compression_level.level =
         maybe_compression_level_.level;
   }
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (!send_) return;
     g_core_codegen_interface->gpr_free(initial_metadata_);
     send_ = false;
@@ -253,7 +253,7 @@ class CallOpSendMessage {
     // Flags are per-message: clear them after use.
     write_options_.Clear();
   }
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (own_buf_) g_core_codegen_interface->grpc_byte_buffer_destroy(send_buf_);
     send_buf_ = nullptr;
   }
@@ -301,13 +301,14 @@ class CallOpRecvMessage {
     op->data.recv_message = &recv_buf_;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (message_ == nullptr) return;
     if (recv_buf_) {
       if (*status) {
-        got_message = *status = SerializationTraits<R>::Deserialize(
-                                    recv_buf_, message_, max_message_size)
-                                    .ok();
+        got_message = *status =
+            SerializationTraits<R>::Deserialize(recv_buf_, message_,
+                                                max_receive_message_size)
+                .ok();
       } else {
         got_message = false;
         g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@@ -330,7 +331,8 @@ class CallOpRecvMessage {
 namespace CallOpGenericRecvMessageHelper {
 class DeserializeFunc {
  public:
-  virtual Status Deserialize(grpc_byte_buffer* buf, int max_message_size) = 0;
+  virtual Status Deserialize(grpc_byte_buffer* buf,
+                             int max_receive_message_size) = 0;
   virtual ~DeserializeFunc() {}
 };
 
@@ -339,8 +341,9 @@ class DeserializeFuncType GRPC_FINAL : public DeserializeFunc {
  public:
   DeserializeFuncType(R* message) : message_(message) {}
   Status Deserialize(grpc_byte_buffer* buf,
-                     int max_message_size) GRPC_OVERRIDE {
-    return SerializationTraits<R>::Deserialize(buf, message_, max_message_size);
+                     int max_receive_message_size) GRPC_OVERRIDE {
+    return SerializationTraits<R>::Deserialize(buf, message_,
+                                               max_receive_message_size);
   }
 
   ~DeserializeFuncType() GRPC_OVERRIDE {}
@@ -379,12 +382,13 @@ class CallOpGenericRecvMessage {
     op->data.recv_message = &recv_buf_;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (!deserialize_) return;
     if (recv_buf_) {
       if (*status) {
         got_message = true;
-        *status = deserialize_->Deserialize(recv_buf_, max_message_size).ok();
+        *status =
+            deserialize_->Deserialize(recv_buf_, max_receive_message_size).ok();
       } else {
         got_message = false;
         g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@@ -418,7 +422,7 @@ class CallOpClientSendClose {
     op->flags = 0;
     op->reserved = NULL;
   }
-  void FinishOp(bool* status, int max_message_size) { send_ = false; }
+  void FinishOp(bool* status, int max_receive_message_size) { send_ = false; }
 
  private:
   bool send_;
@@ -453,7 +457,7 @@ class CallOpServerSendStatus {
     op->reserved = NULL;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (!send_status_available_) return;
     g_core_codegen_interface->gpr_free(trailing_metadata_);
     send_status_available_ = false;
@@ -486,7 +490,7 @@ class CallOpRecvInitialMetadata {
     op->flags = 0;
     op->reserved = NULL;
   }
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (recv_initial_metadata_ == nullptr) return;
     FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_);
     recv_initial_metadata_ = nullptr;
@@ -525,7 +529,7 @@ class CallOpClientRecvStatus {
     op->reserved = NULL;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (recv_status_ == nullptr) return;
     FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_);
     *recv_status_ = Status(
@@ -562,13 +566,13 @@ class CallOpSetCollectionInterface
 /// API.
 class CallOpSetInterface : public CompletionQueueTag {
  public:
-  CallOpSetInterface() : max_message_size_(0) {}
+  CallOpSetInterface() : max_receive_message_size_(0) {}
   /// Fills in grpc_op, starting from ops[*nops] and moving
   /// upwards.
   virtual void FillOps(grpc_op* ops, size_t* nops) = 0;
 
-  void set_max_message_size(int max_message_size) {
-    max_message_size_ = max_message_size;
+  void set_max_receive_message_size(int max_receive_message_size) {
+    max_receive_message_size_ = max_receive_message_size;
   }
 
   /// Mark this as belonging to a collection if needed
@@ -577,7 +581,7 @@ class CallOpSetInterface : public CompletionQueueTag {
   }
 
  protected:
-  int max_message_size_;
+  int max_receive_message_size_;
   std::shared_ptr<CallOpSetCollectionInterface> collection_;
 };
 
@@ -609,12 +613,12 @@ class CallOpSet : public CallOpSetInterface,
   }
 
   bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
-    this->Op1::FinishOp(status, max_message_size_);
-    this->Op2::FinishOp(status, max_message_size_);
-    this->Op3::FinishOp(status, max_message_size_);
-    this->Op4::FinishOp(status, max_message_size_);
-    this->Op5::FinishOp(status, max_message_size_);
-    this->Op6::FinishOp(status, max_message_size_);
+    this->Op1::FinishOp(status, max_receive_message_size_);
+    this->Op2::FinishOp(status, max_receive_message_size_);
+    this->Op3::FinishOp(status, max_receive_message_size_);
+    this->Op4::FinishOp(status, max_receive_message_size_);
+    this->Op5::FinishOp(status, max_receive_message_size_);
+    this->Op6::FinishOp(status, max_receive_message_size_);
     *tag = return_tag_;
     collection_.reset();  // drop the ref at this point
     return true;
@@ -646,18 +650,21 @@ class Call GRPC_FINAL {
  public:
   /* call is owned by the caller */
   Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
-      : call_hook_(call_hook), cq_(cq), call_(call), max_message_size_(-1) {}
+      : call_hook_(call_hook),
+        cq_(cq),
+        call_(call),
+        max_receive_message_size_(-1) {}
 
   Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
-       int max_message_size)
+       int max_receive_message_size)
       : call_hook_(call_hook),
         cq_(cq),
         call_(call),
-        max_message_size_(max_message_size) {}
+        max_receive_message_size_(max_receive_message_size) {}
 
   void PerformOps(CallOpSetInterface* ops) {
-    if (max_message_size_ > 0) {
-      ops->set_max_message_size(max_message_size_);
+    if (max_receive_message_size_ > 0) {
+      ops->set_max_receive_message_size(max_receive_message_size_);
     }
     call_hook_->PerformOpsOnCall(ops, this);
   }
@@ -665,13 +672,13 @@ class Call GRPC_FINAL {
   grpc_call* call() const { return call_; }
   CompletionQueue* cq() const { return cq_; }
 
-  int max_message_size() const { return max_message_size_; }
+  int max_receive_message_size() { return max_receive_message_size_; }
 
  private:
   CallHook* call_hook_;
   CompletionQueue* cq_;
   grpc_call* call_;
-  int max_message_size_;
+  int max_receive_message_size_;
 };
 
 }  // namespace grpc
diff --git a/include/grpc++/impl/codegen/method_handler_impl.h b/include/grpc++/impl/codegen/method_handler_impl.h
index d989263252d715c8db6ca61373ffb4163d4c91b7..52f927631c9dbed00204e3788bc27a907526bee9 100644
--- a/include/grpc++/impl/codegen/method_handler_impl.h
+++ b/include/grpc++/impl/codegen/method_handler_impl.h
@@ -53,7 +53,7 @@ class RpcMethodHandler : public MethodHandler {
   void RunHandler(const HandlerParameter& param) GRPC_FINAL {
     RequestType req;
     Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request, &req, param.max_message_size);
+        param.request, &req, param.max_receive_message_size);
     ResponseType rsp;
     if (status.ok()) {
       status = func_(service_, param.server_context, &req, &rsp);
@@ -139,7 +139,7 @@ class ServerStreamingHandler : public MethodHandler {
   void RunHandler(const HandlerParameter& param) GRPC_FINAL {
     RequestType req;
     Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request, &req, param.max_message_size);
+        param.request, &req, param.max_receive_message_size);
 
     if (status.ok()) {
       ServerWriter<ResponseType> writer(param.call, param.server_context);
diff --git a/include/grpc++/impl/codegen/proto_utils.h b/include/grpc++/impl/codegen/proto_utils.h
index d2e0f6fcddd50ce903abdd01f687bc71a820342e..6f4786b87be0015d310bfbf7b7a7e5f9a981e643 100644
--- a/include/grpc++/impl/codegen/proto_utils.h
+++ b/include/grpc++/impl/codegen/proto_utils.h
@@ -204,7 +204,7 @@ class SerializationTraits<T, typename std::enable_if<std::is_base_of<
 
   static Status Deserialize(grpc_byte_buffer* buffer,
                             grpc::protobuf::Message* msg,
-                            int max_message_size) {
+                            int max_receive_message_size) {
     if (buffer == nullptr) {
       return Status(StatusCode::INTERNAL, "No payload");
     }
@@ -215,8 +215,9 @@ class SerializationTraits<T, typename std::enable_if<std::is_base_of<
         return reader.status();
       }
       ::grpc::protobuf::io::CodedInputStream decoder(&reader);
-      if (max_message_size > 0) {
-        decoder.SetTotalBytesLimit(max_message_size, max_message_size);
+      if (max_receive_message_size > 0) {
+        decoder.SetTotalBytesLimit(max_receive_message_size,
+                                   max_receive_message_size);
       }
       if (!msg->ParseFromCodedStream(&decoder)) {
         result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
diff --git a/include/grpc++/impl/codegen/rpc_service_method.h b/include/grpc++/impl/codegen/rpc_service_method.h
index 88da0965c8f881388b8e17a91bf5599a71284b82..78c54e377115f9eda3af939846a1d5177c9eb494 100644
--- a/include/grpc++/impl/codegen/rpc_service_method.h
+++ b/include/grpc++/impl/codegen/rpc_service_method.h
@@ -62,12 +62,12 @@ class MethodHandler {
         : call(c),
           server_context(context),
           request(req),
-          max_message_size(max_size) {}
+          max_receive_message_size(max_size) {}
     Call* call;
     ServerContext* server_context;
     // Handler required to grpc_byte_buffer_destroy this
     grpc_byte_buffer* request;
-    int max_message_size;
+    int max_receive_message_size;
   };
   virtual void RunHandler(const HandlerParameter& param) = 0;
 };
diff --git a/include/grpc++/impl/codegen/serialization_traits.h b/include/grpc++/impl/codegen/serialization_traits.h
index fa99dbfa9c3ffec2589dbfde5f95e78e8059e4a8..54e5d47632731597f58ab436e8f18f367c76d9ab 100644
--- a/include/grpc++/impl/codegen/serialization_traits.h
+++ b/include/grpc++/impl/codegen/serialization_traits.h
@@ -43,10 +43,10 @@ namespace grpc {
 /// functions:
 ///   static Status Serialize(const Message& msg,
 ///                           grpc_byte_buffer** buffer,
-//                            bool* own_buffer);
+///                           bool* own_buffer);
 ///   static Status Deserialize(grpc_byte_buffer* buffer,
 ///                             Message* msg,
-///                             int max_message_size);
+///                             int max_receive_message_size);
 ///
 /// Serialize is required to convert message to a grpc_byte_buffer, and
 /// to store a pointer to that byte buffer at *buffer. *own_buffer should
@@ -54,8 +54,8 @@ namespace grpc {
 /// ownership is retained elsewhere.
 ///
 /// Deserialize is required to convert buffer into the message stored at
-/// msg. max_message_size is passed in as a bound on the maximum number of
-/// message bytes Deserialize should accept.
+/// msg. max_receive_message_size is passed in as a bound on the maximum
+/// number of message bytes Deserialize should accept.
 ///
 /// Both functions return a Status, allowing them to explain what went
 /// wrong if required.
diff --git a/include/grpc++/impl/codegen/server_interface.h b/include/grpc++/impl/codegen/server_interface.h
index 1f1b714cbfc176bfa787c8e5df7e2dd66910be2b..4a00d7a3a13b4687ed38d46c7d7634717c8bc470 100644
--- a/include/grpc++/impl/codegen/server_interface.h
+++ b/include/grpc++/impl/codegen/server_interface.h
@@ -134,7 +134,7 @@ class ServerInterface : public CallHook {
 
   virtual void ShutdownInternal(gpr_timespec deadline) = 0;
 
-  virtual int max_message_size() const = 0;
+  virtual int max_receive_message_size() const = 0;
 
   virtual grpc_server* server() = 0;
 
@@ -205,8 +205,8 @@ class ServerInterface : public CallHook {
     bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
       bool serialization_status =
           *status && payload_ &&
-          SerializationTraits<Message>::Deserialize(payload_, request_,
-                                                    server_->max_message_size())
+          SerializationTraits<Message>::Deserialize(
+              payload_, request_, server_->max_receive_message_size())
               .ok();
       bool ret = RegisteredAsyncRequest::FinalizeResult(tag, status);
       *status = serialization_status && *status;
diff --git a/include/grpc++/impl/codegen/sync_stream.h b/include/grpc++/impl/codegen/sync_stream.h
index aba129811b5e98a16b115af30d83462dc9165bf1..e3c5a919b1e08b216ce683651d3bd50b7a6508d3 100644
--- a/include/grpc++/impl/codegen/sync_stream.h
+++ b/include/grpc++/impl/codegen/sync_stream.h
@@ -160,7 +160,7 @@ class ClientReader GRPC_FINAL : public ClientReaderInterface<R> {
   }
 
   bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
-    *sz = call_.max_message_size();
+    *sz = call_.max_receive_message_size();
     return true;
   }
 
@@ -310,7 +310,7 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
   }
 
   bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
-    *sz = call_.max_message_size();
+    *sz = call_.max_receive_message_size();
     return true;
   }
 
@@ -382,7 +382,7 @@ class ServerReader GRPC_FINAL : public ServerReaderInterface<R> {
   }
 
   bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
-    *sz = call_->max_message_size();
+    *sz = call_->max_receive_message_size();
     return true;
   }
 
@@ -474,7 +474,7 @@ class ServerReaderWriterBody GRPC_FINAL {
   }
 
   bool NextMessageSize(uint32_t* sz) {
-    *sz = call_->max_message_size();
+    *sz = call_->max_receive_message_size();
     return true;
   }
 
diff --git a/include/grpc++/impl/codegen/thrift_utils.h b/include/grpc++/impl/codegen/thrift_utils.h
index be558c57897bb2eec8e9cbc26d6703348dbec2f5..742d739703522f4fe5db83ec38d9812515647ce5 100644
--- a/include/grpc++/impl/codegen/thrift_utils.h
+++ b/include/grpc++/impl/codegen/thrift_utils.h
@@ -64,7 +64,7 @@ class SerializationTraits<T, typename std::enable_if<std::is_base_of<
   }
 
   static Status Deserialize(grpc_byte_buffer* buffer, T* msg,
-                            int max_message_size) {
+                            int max_receive_message_size) {
     if (!buffer) {
       return Status(StatusCode::INTERNAL, "No payload");
     }
diff --git a/include/grpc++/server.h b/include/grpc++/server.h
index 6876961e210fe6fa9cd64be5869c6fa1515a931d..6e150351255528482f0beb25c1fa7de6975d6d86 100644
--- a/include/grpc++/server.h
+++ b/include/grpc++/server.h
@@ -116,10 +116,10 @@ class Server GRPC_FINAL : public ServerInterface, private GrpcLibraryCodegen {
   ///
   /// \param thread_pool The threadpool instance to use for call processing.
   /// \param thread_pool_owned Does the server own the \a thread_pool instance?
-  /// \param max_message_size Maximum message length that the channel can
-  /// receive.
+  /// \param max_receive_message_size Maximum message length that the channel
+  /// can receive.
   Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
-         int max_message_size, ChannelArguments* args);
+         int max_receive_message_size, ChannelArguments* args);
 
   /// Register a service. This call does not take ownership of the service.
   /// The service must exist for the lifetime of the Server instance.
@@ -164,13 +164,15 @@ class Server GRPC_FINAL : public ServerInterface, private GrpcLibraryCodegen {
 
   void ShutdownInternal(gpr_timespec deadline) GRPC_OVERRIDE;
 
-  int max_message_size() const GRPC_OVERRIDE { return max_message_size_; };
+  int max_receive_message_size() const GRPC_OVERRIDE {
+    return max_receive_message_size_;
+  };
 
   grpc_server* server() GRPC_OVERRIDE { return server_; };
 
   ServerInitializer* initializer();
 
-  const int max_message_size_;
+  const int max_receive_message_size_;
 
   // Completion queue.
   CompletionQueue cq_;
diff --git a/include/grpc++/server_builder.h b/include/grpc++/server_builder.h
index b9c49f0b192946ec5ff4653adaed779b6be96e4a..37f1f8cb803cb7eca3064abbe7e2330e01bc99a4 100644
--- a/include/grpc++/server_builder.h
+++ b/include/grpc++/server_builder.h
@@ -78,12 +78,23 @@ class ServerBuilder {
   /// Only matches requests with :authority \a host
   ServerBuilder& RegisterService(const grpc::string& host, Service* service);
 
-  /// Set max message size in bytes.
-  ServerBuilder& SetMaxMessageSize(int max_message_size) {
-    max_message_size_ = max_message_size;
+  /// Set max receive message size in bytes.
+  ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) {
+    max_receive_message_size_ = max_receive_message_size;
+    return *this;
+  }
+
+  /// Set max send message size in bytes.
+  ServerBuilder& SetMaxSendMessageSize(int max_send_message_size) {
+    max_send_message_size_ = max_send_message_size;
     return *this;
   }
 
+  /// \deprecated For backward compatibility.
+  ServerBuilder& SetMaxMessageSize(int max_message_size) {
+    return SetMaxReceiveMessageSize(max_message_size);
+  }
+
   /// Set the support status for compression algorithms. All algorithms are
   /// enabled by default.
   ///
@@ -168,7 +179,8 @@ class ServerBuilder {
     Service* service;
   };
 
-  int max_message_size_;
+  int max_receive_message_size_;
+  int max_send_message_size_;
   std::vector<std::unique_ptr<ServerBuilderOption>> options_;
   std::vector<std::unique_ptr<NamedService>> services_;
   std::vector<Port> ports_;
diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h
index 01249a0b88bd0deb69b37589d4626a02d3e899d3..06f8969b7042c076b21a52dc7435fedb8d44a323 100644
--- a/include/grpc++/support/byte_buffer.h
+++ b/include/grpc++/support/byte_buffer.h
@@ -96,7 +96,7 @@ template <>
 class SerializationTraits<ByteBuffer, void> {
  public:
   static Status Deserialize(grpc_byte_buffer* byte_buffer, ByteBuffer* dest,
-                            int max_message_size) {
+                            int max_receive_message_size) {
     dest->set_buffer(byte_buffer);
     return Status::OK;
   }
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index b2c48406df798be811f89a85114d625bd1bc267a..a8e8ebe6ea92875441187a5ea7782bd902f19fb5 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -149,7 +149,11 @@ typedef struct {
     connection. Int valued. */
 #define GRPC_ARG_MAX_CONCURRENT_STREAMS "grpc.max_concurrent_streams"
 /** Maximum message length that the channel can receive. Int valued, bytes. */
-#define GRPC_ARG_MAX_MESSAGE_LENGTH "grpc.max_message_length"
+#define GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH "grpc.max_receive_message_length"
+/** \deprecated For backward compatibility. */
+#define GRPC_ARG_MAX_MESSAGE_LENGTH GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
+/** Maximum message length that the channel can send. Int valued, bytes. */
+#define GRPC_ARG_MAX_SEND_MESSAGE_LENGTH "grpc.max_send_message_length"
 /** Initial sequence number for http2 transports. Int valued. */
 #define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \
   "grpc.http2.initial_sequence_number"
diff --git a/package.xml b/package.xml
index 743283052042d89cb6daf4514c1f15c5f2739f7a..f3b244a03989b24c22b59211a312248a71bb7541 100644
--- a/package.xml
+++ b/package.xml
@@ -185,6 +185,7 @@
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_client_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_server_filter.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/message_size_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/algorithm_metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/trace.h" role="src" />
@@ -345,6 +346,7 @@
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_client_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_server_filter.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/message_size_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/compression.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/trace.c" role="src" />
diff --git a/src/core/ext/client_config/subchannel.c b/src/core/ext/client_config/subchannel.c
index 2c4364b259ffdf5b46c224e3f7e901ceaefb24d7..11418446347fed80fa20888e5e2c28e7b9a5036e 100644
--- a/src/core/ext/client_config/subchannel.c
+++ b/src/core/ext/client_config/subchannel.c
@@ -33,6 +33,7 @@
 
 #include "src/core/ext/client_config/subchannel.h"
 
+#include <limits.h>
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -347,21 +348,16 @@ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx,
       }
       if (0 ==
           strcmp(c->args->args[i].key, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) {
-        if (c->args->args[i].type == GRPC_ARG_INTEGER) {
-          if (c->args->args[i].value.integer >= 0) {
-            gpr_backoff_init(
-                &c->backoff_state, GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
-                GRPC_SUBCHANNEL_RECONNECT_JITTER,
-                GPR_MIN(c->args->args[i].value.integer,
-                        GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000),
-                c->args->args[i].value.integer);
-          } else {
-            gpr_log(GPR_ERROR, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS
-                    " : must be non-negative");
-          }
-        } else {
-          gpr_log(GPR_ERROR,
-                  GRPC_ARG_MAX_RECONNECT_BACKOFF_MS " : must be an integer");
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&c->args->args[i], options);
+        if (value >= 0) {
+          gpr_backoff_init(
+              &c->backoff_state, GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
+              GRPC_SUBCHANNEL_RECONNECT_JITTER,
+              GPR_MIN(value,
+                      GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000),
+              value);
         }
       }
     }
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 53da0e5d7098693e36ff2cf66e0399fa2b11bd84..ef731657af72741746a8ad757f4802f7c7e13a61 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -33,6 +33,7 @@
 
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 
+#include <limits.h>
 #include <math.h>
 #include <stdio.h>
 #include <string.h>
@@ -46,6 +47,7 @@
 #include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 #include "src/core/ext/transport/chttp2/transport/status_conversion.h"
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/workqueue.h"
 #include "src/core/lib/profiling/timers.h"
@@ -333,76 +335,65 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
         if (is_client) {
           gpr_log(GPR_ERROR, "%s: is ignored on the client",
                   GRPC_ARG_MAX_CONCURRENT_STREAMS);
-        } else if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_MAX_CONCURRENT_STREAMS);
         } else {
-          push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
-                       (uint32_t)channel_args->args[i].value.integer);
+          const grpc_integer_options options = {-1, 0, INT_MAX};
+          const int value =
+              grpc_channel_arg_get_integer(&channel_args->args[i], options);
+          if (value >= 0) {
+            push_setting(exec_ctx, t,
+                         GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
+                         (uint32_t)value);
+          }
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER);
-        } else if ((t->global.next_stream_id & 1) !=
-                   (channel_args->args[i].value.integer & 1)) {
-          gpr_log(GPR_ERROR, "%s: low bit must be %d on %s",
-                  GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER,
-                  t->global.next_stream_id & 1,
-                  is_client ? "client" : "server");
-        } else {
-          t->global.next_stream_id =
-              (uint32_t)channel_args->args[i].value.integer;
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
+          if ((t->global.next_stream_id & 1) != (value & 1)) {
+            gpr_log(GPR_ERROR, "%s: low bit must be %d on %s",
+                    GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER,
+                    t->global.next_stream_id & 1,
+                    is_client ? "client" : "server");
+          } else {
+            t->global.next_stream_id = (uint32_t)value;
+          }
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES);
-        } else if (channel_args->args[i].value.integer <= 5) {
-          gpr_log(GPR_ERROR, "%s: must be at least 5",
-                  GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES);
-        } else {
-          t->global.stream_lookahead =
-              (uint32_t)channel_args->args[i].value.integer;
+        const grpc_integer_options options = {-1, 5, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
+          t->global.stream_lookahead = (uint32_t)value;
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
-        } else if (channel_args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s: must be non-negative",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
-        } else {
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
           push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE,
-                       (uint32_t)channel_args->args[i].value.integer);
+                       (uint32_t)value);
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
-        } else if (channel_args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s: must be non-negative",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
-        } else {
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
           grpc_chttp2_hpack_compressor_set_max_usable_size(
-              &t->writing.hpack_compressor,
-              (uint32_t)channel_args->args[i].value.integer);
+              &t->writing.hpack_compressor, (uint32_t)value);
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_MAX_METADATA_SIZE)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_MAX_METADATA_SIZE);
-        } else if (channel_args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s: must be non-negative",
-                  GRPC_ARG_MAX_METADATA_SIZE);
-        } else {
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
           push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
-                       (uint32_t)channel_args->args[i].value.integer);
+                       (uint32_t)value);
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_MAX_FRAME_SIZE)) {
diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c
index 79ceeb66b381e55b591b359f884f48e250bae63f..3a56b1ff205ae995ae4fbb209a654c3560f40029 100644
--- a/src/core/lib/channel/channel_args.c
+++ b/src/core/lib/channel/channel_args.c
@@ -271,3 +271,21 @@ int grpc_channel_args_compare(const grpc_channel_args *a,
   }
   return 0;
 }
+
+int grpc_channel_arg_get_integer(grpc_arg *arg, grpc_integer_options options) {
+  if (arg->type != GRPC_ARG_INTEGER) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key);
+    return options.default_value;
+  }
+  if (arg->value.integer < options.min_value) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be >= %d", arg->key,
+            options.min_value);
+    return options.default_value;
+  }
+  if (arg->value.integer > options.max_value) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be <= %d", arg->key,
+            options.max_value);
+    return options.default_value;
+  }
+  return arg->value.integer;
+}
diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h
index aec61ee7c62f0e21bb54ba05c609dfb09751e945..586a296d1f66603ee54990455679c0408335f5c2 100644
--- a/src/core/lib/channel/channel_args.h
+++ b/src/core/lib/channel/channel_args.h
@@ -89,4 +89,12 @@ uint32_t grpc_channel_args_compression_algorithm_get_states(
 int grpc_channel_args_compare(const grpc_channel_args *a,
                               const grpc_channel_args *b);
 
+typedef struct grpc_integer_options {
+  int default_value;  // Return this if value is outside of expected bounds.
+  int min_value;
+  int max_value;
+} grpc_integer_options;
+/** Returns the value of \a arg, subject to the contraints in \a options. */
+int grpc_channel_arg_get_integer(grpc_arg *arg, grpc_integer_options options);
+
 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */
diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c
new file mode 100644
index 0000000000000000000000000000000000000000..6613785deaaa2450e58f2a100a450995d1850271
--- /dev/null
+++ b/src/core/lib/channel/message_size_filter.c
@@ -0,0 +1,165 @@
+//
+// Copyright 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "src/core/lib/channel/message_size_filter.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/channel/channel_args.h"
+
+// The protobuf library will (by default) start warning at 100 megs.
+#define DEFAULT_MAX_MESSAGE_LENGTH (4 * 1024 * 1024)
+
+typedef struct call_data {
+  // Receive closures are chained: we inject this closure as the
+  // recv_message_ready up-call on transport_stream_op, and remember to
+  // call our next_recv_message_ready member after handling it.
+  grpc_closure recv_message_ready;
+  // Used by recv_message_ready.
+  grpc_byte_stream** recv_message;
+  // Original recv_message_ready callback, invoked after our own.
+  grpc_closure* next_recv_message_ready;
+} call_data;
+
+typedef struct channel_data {
+  size_t max_send_size;
+  size_t max_recv_size;
+} channel_data;
+
+// Callback invoked when we receive a message.  Here we check the max
+// receive message size.
+static void recv_message_ready(grpc_exec_ctx* exec_ctx, void* user_data,
+                               grpc_error* error) {
+  grpc_call_element* elem = user_data;
+  call_data* calld = elem->call_data;
+  channel_data* chand = elem->channel_data;
+  if (*calld->recv_message != NULL &&
+      (*calld->recv_message)->length > chand->max_recv_size) {
+    char* message_string;
+    gpr_asprintf(
+        &message_string, "Received message larger than max (%u vs. %lu)",
+        (*calld->recv_message)->length, (unsigned long)chand->max_recv_size);
+    gpr_slice message = gpr_slice_from_copied_string(message_string);
+    gpr_free(message_string);
+    grpc_call_element_send_cancel_with_message(
+        exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, &message);
+  }
+  // Invoke the next callback.
+  grpc_exec_ctx_sched(exec_ctx, calld->next_recv_message_ready, error, NULL);
+}
+
+// Start transport op.
+static void start_transport_stream_op(grpc_exec_ctx* exec_ctx,
+                                      grpc_call_element* elem,
+                                      grpc_transport_stream_op* op) {
+  call_data* calld = elem->call_data;
+  channel_data* chand = elem->channel_data;
+  // Check max send message size.
+  if (op->send_message != NULL &&
+      op->send_message->length > chand->max_send_size) {
+    char* message_string;
+    gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %lu)",
+                 op->send_message->length, (unsigned long)chand->max_send_size);
+    gpr_slice message = gpr_slice_from_copied_string(message_string);
+    gpr_free(message_string);
+    grpc_call_element_send_cancel_with_message(
+        exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, &message);
+  }
+  // Inject callback for receiving a message.
+  if (op->recv_message_ready != NULL) {
+    calld->next_recv_message_ready = op->recv_message_ready;
+    calld->recv_message = op->recv_message;
+    op->recv_message_ready = &calld->recv_message_ready;
+  }
+  // Chain to the next filter.
+  grpc_call_next_op(exec_ctx, elem, op);
+}
+
+// Constructor for call_data.
+static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx,
+                                  grpc_call_element* elem,
+                                  grpc_call_element_args* args) {
+  call_data* calld = elem->call_data;
+  calld->next_recv_message_ready = NULL;
+  grpc_closure_init(&calld->recv_message_ready, recv_message_ready, elem);
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for call_data.
+static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              void* ignored) {}
+
+// Constructor for channel_data.
+static void init_channel_elem(grpc_exec_ctx* exec_ctx,
+                              grpc_channel_element* elem,
+                              grpc_channel_element_args* args) {
+  GPR_ASSERT(!args->is_last);
+  channel_data* chand = elem->channel_data;
+  memset(chand, 0, sizeof(*chand));
+  chand->max_send_size = DEFAULT_MAX_MESSAGE_LENGTH;
+  chand->max_recv_size = DEFAULT_MAX_MESSAGE_LENGTH;
+  const grpc_integer_options options = {DEFAULT_MAX_MESSAGE_LENGTH, 0, INT_MAX};
+  for (size_t i = 0; i < args->channel_args->num_args; ++i) {
+    if (strcmp(args->channel_args->args[i].key,
+               GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) == 0) {
+      chand->max_send_size = (size_t)grpc_channel_arg_get_integer(
+          &args->channel_args->args[i], options);
+    }
+    if (strcmp(args->channel_args->args[i].key,
+               GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) {
+      chand->max_recv_size = (size_t)grpc_channel_arg_get_integer(
+          &args->channel_args->args[i], options);
+    }
+  }
+}
+
+// Destructor for channel_data.
+static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
+                                 grpc_channel_element* elem) {}
+
+const grpc_channel_filter grpc_message_size_filter = {
+    start_transport_stream_op,
+    grpc_channel_next_op,
+    sizeof(call_data),
+    init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    destroy_call_elem,
+    sizeof(channel_data),
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_call_next_get_peer,
+    "message_size"};
diff --git a/src/core/lib/channel/message_size_filter.h b/src/core/lib/channel/message_size_filter.h
new file mode 100644
index 0000000000000000000000000000000000000000..a88ff7f81a1f9bc8119fa28af34d3dfdeec5a9dc
--- /dev/null
+++ b/src/core/lib/channel/message_size_filter.h
@@ -0,0 +1,39 @@
+//
+// Copyright 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef GRPC_CORE_LIB_CHANNEL_MESSAGE_SIZE_FILTER_H
+#define GRPC_CORE_LIB_CHANNEL_MESSAGE_SIZE_FILTER_H
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_message_size_filter;
+
+#endif /* GRPC_CORE_LIB_CHANNEL_MESSAGE_SIZE_FILTER_H */
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 119f5e82ab2fae7846f30b9f6552f1896735fa30..979e0aeaea8e168f78370da4453f119189015874 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -1163,17 +1163,6 @@ static void process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl,
     if (gpr_unref(&bctl->steps_to_complete)) {
       post_batch_completion(exec_ctx, bctl);
     }
-  } else if (call->receiving_stream->length >
-             grpc_channel_get_max_message_length(call->channel)) {
-    cancel_with_status(exec_ctx, call, GRPC_STATUS_INTERNAL,
-                       "Max message size exceeded");
-    grpc_byte_stream_destroy(exec_ctx, call->receiving_stream);
-    call->receiving_stream = NULL;
-    *call->receiving_buffer = NULL;
-    call->receiving_message = 0;
-    if (gpr_unref(&bctl->steps_to_complete)) {
-      post_batch_completion(exec_ctx, bctl);
-    }
   } else {
     call->test_only_last_message_flags = call->receiving_stream->flags;
     if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) &&
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index 52e78567bd89a296ddfa27abccb21262b4c0e414..6adb70a987c04fd87aa70d629c5607dccebecfc7 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -64,7 +64,6 @@ typedef struct registered_call {
 
 struct grpc_channel {
   int is_client;
-  uint32_t max_message_length;
   grpc_compression_options compression_options;
   grpc_mdelem *default_authority;
 
@@ -80,9 +79,6 @@ struct grpc_channel {
 #define CHANNEL_FROM_TOP_ELEM(top_elem) \
   CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem))
 
-/* the protobuf library will (by default) start warning at 100megs */
-#define DEFAULT_MAX_MESSAGE_LENGTH (4 * 1024 * 1024)
-
 static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg,
                             grpc_error *error);
 
@@ -114,21 +110,10 @@ grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
   gpr_mu_init(&channel->registered_call_mu);
   channel->registered_calls = NULL;
 
-  channel->max_message_length = DEFAULT_MAX_MESSAGE_LENGTH;
   grpc_compression_options_init(&channel->compression_options);
   if (args) {
     for (size_t i = 0; i < args->num_args; i++) {
-      if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_MESSAGE_LENGTH)) {
-        if (args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s ignored: it must be an integer",
-                  GRPC_ARG_MAX_MESSAGE_LENGTH);
-        } else if (args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s ignored: it must be >= 0",
-                  GRPC_ARG_MAX_MESSAGE_LENGTH);
-        } else {
-          channel->max_message_length = (uint32_t)args->args[i].value.integer;
-        }
-      } else if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
+      if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
         if (args->args[i].type != GRPC_ARG_STRING) {
           gpr_log(GPR_ERROR, "%s ignored: it must be a string",
                   GRPC_ARG_DEFAULT_AUTHORITY);
@@ -370,7 +355,3 @@ grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
   return grpc_mdelem_from_metadata_strings(GRPC_MDSTR_GRPC_STATUS,
                                            grpc_mdstr_from_string(tmp));
 }
-
-uint32_t grpc_channel_get_max_message_length(grpc_channel *channel) {
-  return channel->max_message_length;
-}
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 4c6297434628318dd376f4606cf37747adf161b3..23cc8656cabe419821e470fc22e7f3ca70f4ff8b 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -64,7 +64,6 @@ grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
     The returned elem is owned by the caller. */
 grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel,
                                                  int status_code);
-uint32_t grpc_channel_get_max_message_length(grpc_channel *channel);
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
 void grpc_channel_internal_ref(grpc_channel *channel, const char *reason);
diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c
index edda0c85fa93719e122902580278d91edcd05852..64bdfc344691b8cc51af3c57fe455a6af9ce35f6 100644
--- a/src/core/lib/surface/init.c
+++ b/src/core/lib/surface/init.c
@@ -45,6 +45,7 @@
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/channel/http_client_filter.h"
 #include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/channel/message_size_filter.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/combiner.h"
@@ -98,6 +99,15 @@ static bool maybe_add_http_filter(grpc_channel_stack_builder *builder,
 }
 
 static void register_builtin_channel_init() {
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      prepend_filter, (void *)&grpc_message_size_filter);
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      prepend_filter, (void *)&grpc_message_size_filter);
+  grpc_channel_init_register_stage(
+      GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, prepend_filter,
+      (void *)&grpc_message_size_filter);
   grpc_channel_init_register_stage(
       GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, prepend_filter,
       (void *)&grpc_compress_filter);
diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc
index 1a014da3b8ce80597e02295415325c325641e9d8..2980b16c56c1deab03925c7f99d8b645fbd11910 100644
--- a/src/cpp/server/server_builder.cc
+++ b/src/cpp/server/server_builder.cc
@@ -52,7 +52,9 @@ static void do_plugin_list_init(void) {
 }
 
 ServerBuilder::ServerBuilder()
-    : max_message_size_(-1), generic_service_(nullptr) {
+    : max_receive_message_size_(-1),
+      max_send_message_size_(-1),
+      generic_service_(nullptr) {
   gpr_once_init(&once_init_plugin_list, do_plugin_list_init);
   for (auto it = g_plugin_factory_list->begin();
        it != g_plugin_factory_list->end(); it++) {
@@ -160,8 +162,11 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
     }
     (*plugin)->UpdateChannelArguments(&args);
   }
-  if (max_message_size_ > 0) {
-    args.SetInt(GRPC_ARG_MAX_MESSAGE_LENGTH, max_message_size_);
+  if (max_receive_message_size_ >= 0) {
+    args.SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, max_receive_message_size_);
+  }
+  if (max_send_message_size_ >= 0) {
+    args.SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, max_send_message_size_);
   }
   args.SetInt(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET,
               enabled_compression_algorithms_bitset_);
@@ -173,8 +178,8 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
     args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM,
                 maybe_default_compression_algorithm_.algorithm);
   }
-  std::unique_ptr<Server> server(
-      new Server(thread_pool.release(), true, max_message_size_, &args));
+  std::unique_ptr<Server> server(new Server(thread_pool.release(), true,
+                                            max_receive_message_size_, &args));
   ServerInitializer* initializer = server->initializer();
 
   // If the server has atleast one sync methods, we know that this is a Sync
diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc
index af04fd4ca6477e3efde054ec80c77a4e770bc5c0..a693ce9b8eea730141b39af833b872cb5eb3c228 100644
--- a/src/cpp/server/server_cc.cc
+++ b/src/cpp/server/server_cc.cc
@@ -220,7 +220,7 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
    public:
     explicit CallData(Server* server, SyncRequest* mrd)
         : cq_(mrd->cq_),
-          call_(mrd->call_, server, &cq_, server->max_message_size_),
+          call_(mrd->call_, server, &cq_, server->max_receive_message_size_),
           ctx_(mrd->deadline_, mrd->request_metadata_.metadata,
                mrd->request_metadata_.count),
           has_request_payload_(mrd->has_request_payload_),
@@ -243,7 +243,7 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
       ctx_.BeginCompletionOp(&call_);
       global_callbacks->PreSynchronousRequest(&ctx_);
       method_->handler()->RunHandler(MethodHandler::HandlerParameter(
-          &call_, &ctx_, request_payload_, call_.max_message_size()));
+          &call_, &ctx_, request_payload_, call_.max_receive_message_size()));
       global_callbacks->PostSynchronousRequest(&ctx_);
       request_payload_ = nullptr;
       void* ignored_tag;
@@ -277,8 +277,8 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
 
 static internal::GrpcLibraryInitializer g_gli_initializer;
 Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
-               int max_message_size, ChannelArguments* args)
-    : max_message_size_(max_message_size),
+               int max_receive_message_size, ChannelArguments* args)
+    : max_receive_message_size_(max_receive_message_size),
       started_(false),
       shutdown_(false),
       shutdown_notified_(false),
@@ -514,7 +514,7 @@ bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
   grpc_metadata_array_destroy(&initial_metadata_array_);
   context_->set_call(call_);
   context_->cq_ = call_cq_;
-  Call call(call_, server_, call_cq_, server_->max_message_size());
+  Call call(call_, server_, call_cq_, server_->max_receive_message_size());
   if (*status && call_) {
     context_->BeginCompletionOp(&call);
   }
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
index 9cd9593d172cbdad5cb6fa8b456145b2c1a002d0..1159aac7a71268896fd51cddcee833010ea4ea6a 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.m
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -217,7 +217,7 @@ static NSMutableDictionary *kHostCache;
   }
 
   if (_responseSizeLimitOverride) {
-    args[@GRPC_ARG_MAX_MESSAGE_LENGTH] = _responseSizeLimitOverride;
+    args[@GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH] = _responseSizeLimitOverride;
   }
   return args;
 }
diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m
index f04a7e6441f86f4f8ae85b82dbac78477e66ba96..44f22c9e85c03acffc7a78e5b1ed0f73c266e1cb 100644
--- a/src/objective-c/tests/InteropTests.m
+++ b/src/objective-c/tests/InteropTests.m
@@ -180,7 +180,7 @@
     // - If you're developing the server, consider using response streaming, or let clients filter
     //   responses by setting a google.protobuf.FieldMask in the request:
     //   https://github.com/google/protobuf/blob/master/src/google/protobuf/field_mask.proto
-    XCTAssertEqualObjects(error.localizedDescription, @"Max message size exceeded");
+    XCTAssertEqualObjects(error.localizedDescription, @"Received message larger than max (4194305 vs. 4194304)");
     [expectation fulfill];
   }];
 
diff --git a/src/objective-c/tests/run_tests.sh b/src/objective-c/tests/run_tests.sh
index a265149f488a362971acd399fb3633e777bdefc2..81c2885320512e2d89db2663e8c0bfdf9bd45133 100755
--- a/src/objective-c/tests/run_tests.sh
+++ b/src/objective-c/tests/run_tests.sh
@@ -44,8 +44,8 @@ BINDIR=../../../bins/$CONFIG
              "interop_server before calling this script."
     exit 1
 }
-$BINDIR/interop_server --port=5050 &
-$BINDIR/interop_server --port=5051 --use_tls &
+$BINDIR/interop_server --port=5050 --max_send_message_size=8388608 &
+$BINDIR/interop_server --port=5051 --max_send_message_size=8388608 --use_tls &
 # Kill them when this script exits.
 trap 'kill -9 `jobs -p`' EXIT
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 42fced654570e182c50448f50fe5a71b80221dff..dae28e15f2cb9bea0766915fc0da6ad6cf781825 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -135,7 +135,7 @@ cdef extern from "grpc/grpc.h":
   const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
   const char *GRPC_ARG_ENABLE_CENSUS
   const char *GRPC_ARG_MAX_CONCURRENT_STREAMS
-  const char *GRPC_ARG_MAX_MESSAGE_LENGTH
+  const char *GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
   const char *GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
   const char *GRPC_ARG_DEFAULT_AUTHORITY
   const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
index 834a44123d49605d618d43a9ddd29e34dfb68407..f444e33cf078dd09f5e27fd704cf23b0c3dcb14a 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
@@ -39,7 +39,7 @@ class ConnectivityState:
 class ChannelArgKey:
   enable_census = GRPC_ARG_ENABLE_CENSUS
   max_concurrent_streams = GRPC_ARG_MAX_CONCURRENT_STREAMS
-  max_message_length = GRPC_ARG_MAX_MESSAGE_LENGTH
+  max_message_length = GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
   http2_initial_sequence_number = GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
   default_authority = GRPC_ARG_DEFAULT_AUTHORITY
   primary_user_agent_string = GRPC_ARG_PRIMARY_USER_AGENT_STRING
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index d53f46b18b4c3c514fda7e68277c5b32e0a091b3..a7eb6421a2ca515f1fbc1ecb34e09ec6487d04a4 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -86,6 +86,7 @@ CORE_SOURCE_FILES = [
   'src/core/lib/channel/handshaker.c',
   'src/core/lib/channel/http_client_filter.c',
   'src/core/lib/channel/http_server_filter.c',
+  'src/core/lib/channel/message_size_filter.c',
   'src/core/lib/compression/compression.c',
   'src/core/lib/compression/message_compress.c',
   'src/core/lib/debug/trace.c',
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index e6d30a174b8885d8c38b06d65ac02614f1dffa72..3b2b88eb7748231be2f4ae86feebb6904981a168 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -386,7 +386,7 @@ void Init_grpc_channel() {
   rb_define_const(grpc_rb_cChannel, "MAX_CONCURRENT_STREAMS",
                   ID2SYM(rb_intern(GRPC_ARG_MAX_CONCURRENT_STREAMS)));
   rb_define_const(grpc_rb_cChannel, "MAX_MESSAGE_LENGTH",
-                  ID2SYM(rb_intern(GRPC_ARG_MAX_MESSAGE_LENGTH)));
+                  ID2SYM(rb_intern(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH)));
   id_insecure_channel = rb_intern("this_channel_is_insecure");
   Init_grpc_propagate_masks();
   Init_grpc_connectivity_states();
diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c
index 43f71f51d157c7c7068df27e276fd869ba87a946..cdca3e674872bce661abc6824329b8c6d9b56400 100644
--- a/test/core/end2end/tests/max_message_length.c
+++ b/test/core/end2end/tests/max_message_length.c
@@ -98,19 +98,22 @@ static void end_test(grpc_end2end_test_fixture *f) {
   grpc_completion_queue_destroy(f->cq);
 }
 
-static void test_max_message_length(grpc_end2end_test_config config) {
+static void test_max_message_length(grpc_end2end_test_config config,
+                                    bool send_limit) {
+  gpr_log(GPR_INFO, "testing with send_limit=%d", send_limit);
+
   grpc_end2end_test_fixture f;
-  grpc_arg server_arg;
-  grpc_channel_args server_args;
-  grpc_call *c;
-  grpc_call *s;
+  grpc_arg channel_arg;
+  grpc_channel_args channel_args;
+  grpc_call *c = NULL;
+  grpc_call *s = NULL;
   cq_verifier *cqv;
   grpc_op ops[6];
   grpc_op *op;
   gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
   grpc_byte_buffer *request_payload =
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
-  grpc_byte_buffer *recv_payload;
+  grpc_byte_buffer *recv_payload = NULL;
   grpc_metadata_array initial_metadata_recv;
   grpc_metadata_array trailing_metadata_recv;
   grpc_metadata_array request_metadata_recv;
@@ -121,14 +124,17 @@ static void test_max_message_length(grpc_end2end_test_config config) {
   size_t details_capacity = 0;
   int was_cancelled = 2;
 
-  server_arg.key = GRPC_ARG_MAX_MESSAGE_LENGTH;
-  server_arg.type = GRPC_ARG_INTEGER;
-  server_arg.value.integer = 5;
+  channel_arg.key = send_limit ? GRPC_ARG_MAX_SEND_MESSAGE_LENGTH
+                               : GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH;
+  channel_arg.type = GRPC_ARG_INTEGER;
+  channel_arg.value.integer = 5;
 
-  server_args.num_args = 1;
-  server_args.args = &server_arg;
+  channel_args.num_args = 1;
+  channel_args.args = &channel_arg;
 
-  f = begin_test(config, "test_max_message_length", NULL, &server_args);
+  f = begin_test(config, "test_max_message_length",
+                 send_limit ? &channel_args : NULL,
+                 send_limit ? NULL : &channel_args);
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
@@ -173,6 +179,12 @@ static void test_max_message_length(grpc_end2end_test_config config) {
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
+  if (send_limit) {
+    CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+    cq_verify(cqv);
+    goto done;
+  }
+
   error =
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
@@ -199,11 +211,16 @@ static void test_max_message_length(grpc_end2end_test_config config) {
   CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
-  GPR_ASSERT(status != GRPC_STATUS_OK);
   GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
   GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr:1234"));
   GPR_ASSERT(was_cancelled == 1);
-  GPR_ASSERT(recv_payload == NULL);
+
+done:
+  GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT);
+  GPR_ASSERT(strcmp(details,
+                    send_limit
+                        ? "Sent message larger than max (11 vs. 5)"
+                        : "Received message larger than max (11 vs. 5)") == 0);
 
   gpr_free(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
@@ -211,9 +228,10 @@ static void test_max_message_length(grpc_end2end_test_config config) {
   grpc_metadata_array_destroy(&request_metadata_recv);
   grpc_call_details_destroy(&call_details);
   grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(recv_payload);
 
   grpc_call_destroy(c);
-  grpc_call_destroy(s);
+  if (s != NULL) grpc_call_destroy(s);
 
   cq_verifier_destroy(cqv);
 
@@ -222,7 +240,8 @@ static void test_max_message_length(grpc_end2end_test_config config) {
 }
 
 void max_message_length(grpc_end2end_test_config config) {
-  test_max_message_length(config);
+  test_max_message_length(config, true);
+  test_max_message_length(config, false);
 }
 
 void max_message_length_pre_init(void) {}
diff --git a/test/core/surface/invalid_channel_args_test.c b/test/core/surface/invalid_channel_args_test.c
index 1b1b8b8f9200716a780b74d0efefecb27b92a9eb..0640879866fad127de1302357d61ba4c208b9d80 100644
--- a/test/core/surface/invalid_channel_args_test.c
+++ b/test/core/surface/invalid_channel_args_test.c
@@ -84,38 +84,6 @@ static void one_test(grpc_channel_args *args, char *expected_error_message) {
 
 static void test_no_error_message(void) { one_test(NULL, NULL); }
 
-static void test_max_message_length_type(void) {
-  grpc_arg client_arg;
-  grpc_channel_args client_args;
-  char *expected_error_message;
-
-  client_arg.type = GRPC_ARG_STRING;
-  client_arg.key = GRPC_ARG_MAX_MESSAGE_LENGTH;
-  client_arg.value.string = NULL;
-
-  client_args.num_args = 1;
-  client_args.args = &client_arg;
-  expected_error_message = compose_error_string(
-      GRPC_ARG_MAX_MESSAGE_LENGTH, " ignored: it must be an integer");
-  one_test(&client_args, expected_error_message);
-}
-
-static void test_max_message_length_negative(void) {
-  grpc_arg client_arg;
-  grpc_channel_args client_args;
-  char *expected_error_message;
-
-  client_arg.type = GRPC_ARG_INTEGER;
-  client_arg.key = GRPC_ARG_MAX_MESSAGE_LENGTH;
-  client_arg.value.integer = -1;
-
-  client_args.num_args = 1;
-  client_args.args = &client_arg;
-  expected_error_message = compose_error_string(GRPC_ARG_MAX_MESSAGE_LENGTH,
-                                                " ignored: it must be >= 0");
-  one_test(&client_args, expected_error_message);
-}
-
 static void test_default_authority_type(void) {
   grpc_arg client_arg;
   grpc_channel_args client_args;
@@ -174,8 +142,6 @@ int main(int argc, char **argv) {
   gpr_set_log_function(log_error_sink);
 
   test_no_error_message();
-  test_max_message_length_type();
-  test_max_message_length_negative();
   test_default_authority_type();
   test_ssl_name_override_type();
   test_ssl_name_override_failed();
diff --git a/test/cpp/interop/interop_server.cc b/test/cpp/interop/interop_server.cc
index e5878bb248c8fecd75bac0cddafbe9bc59233368..c05eb5d1461be32918df80dfd59606f01510999b 100644
--- a/test/cpp/interop/interop_server.cc
+++ b/test/cpp/interop/interop_server.cc
@@ -56,6 +56,7 @@
 
 DEFINE_bool(use_tls, false, "Whether to use tls.");
 DEFINE_int32(port, 0, "Server port.");
+DEFINE_int32(max_send_message_size, -1, "The maximum send message size.");
 
 using grpc::Server;
 using grpc::ServerBuilder;
@@ -321,6 +322,9 @@ void grpc::testing::interop::RunServer(
   ServerBuilder builder;
   builder.RegisterService(&service);
   builder.AddListeningPort(server_address.str(), creds);
+  if (FLAGS_max_send_message_size >= 0) {
+    builder.SetMaxSendMessageSize(FLAGS_max_send_message_size);
+  }
   std::unique_ptr<Server> server(builder.BuildAndStart());
   gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());
   while (!g_got_sigint) {
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 873925430fd8b2ca4b69b66393e8ba67726329b2..fbe82c86165c333b3b25ee07265723830fcb93da 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -879,6 +879,7 @@ src/core/lib/channel/context.h \
 src/core/lib/channel/handshaker.h \
 src/core/lib/channel/http_client_filter.h \
 src/core/lib/channel/http_server_filter.h \
+src/core/lib/channel/message_size_filter.h \
 src/core/lib/compression/algorithm_metadata.h \
 src/core/lib/compression/message_compress.h \
 src/core/lib/debug/trace.h \
@@ -992,6 +993,7 @@ src/core/lib/channel/connected_channel.c \
 src/core/lib/channel/handshaker.c \
 src/core/lib/channel/http_client_filter.c \
 src/core/lib/channel/http_server_filter.c \
+src/core/lib/channel/message_size_filter.c \
 src/core/lib/compression/compression.c \
 src/core/lib/compression/message_compress.c \
 src/core/lib/debug/trace.c \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 7ffd07bc9602a66f7e620dfc9fa9d2db34157974..09f2bd5aa97ccfcabbbb25f695a6c185757ef58a 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -795,6 +795,7 @@ src/core/lib/channel/context.h \
 src/core/lib/channel/handshaker.h \
 src/core/lib/channel/http_client_filter.h \
 src/core/lib/channel/http_server_filter.h \
+src/core/lib/channel/message_size_filter.h \
 src/core/lib/compression/algorithm_metadata.h \
 src/core/lib/compression/message_compress.h \
 src/core/lib/debug/trace.h \
@@ -955,6 +956,7 @@ src/core/lib/channel/connected_channel.c \
 src/core/lib/channel/handshaker.c \
 src/core/lib/channel/http_client_filter.c \
 src/core/lib/channel/http_server_filter.c \
+src/core/lib/channel/message_size_filter.c \
 src/core/lib/compression/compression.c \
 src/core/lib/compression/message_compress.c \
 src/core/lib/debug/trace.c \
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 447dbe4f82c013e2fe3acf3e9929611223c95875..939f7b352a97f9aec21f3c45ecaddc26c076ed8f 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -5925,6 +5925,7 @@
       "src/core/lib/channel/handshaker.h", 
       "src/core/lib/channel/http_client_filter.h", 
       "src/core/lib/channel/http_server_filter.h", 
+      "src/core/lib/channel/message_size_filter.h", 
       "src/core/lib/compression/algorithm_metadata.h", 
       "src/core/lib/compression/message_compress.h", 
       "src/core/lib/debug/trace.h", 
@@ -6025,6 +6026,8 @@
       "src/core/lib/channel/http_client_filter.h", 
       "src/core/lib/channel/http_server_filter.c", 
       "src/core/lib/channel/http_server_filter.h", 
+      "src/core/lib/channel/message_size_filter.c", 
+      "src/core/lib/channel/message_size_filter.h", 
       "src/core/lib/compression/algorithm_metadata.h", 
       "src/core/lib/compression/compression.c", 
       "src/core/lib/compression/message_compress.c", 
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
index 0a317da7c78dfcddef8aaf949e21a3ef9773d1dc..1e5fb9b3cf0854c48d154d80140816ea84e6c8fe 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
@@ -379,6 +379,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
@@ -535,6 +536,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
index 4bf406513f0a8217ba7ec17082db2fee93eb4773..8da2b766199e0429ace812fad49901a53e771834 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
@@ -124,6 +124,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
       <Filter>src\core\lib\compression</Filter>
     </ClCompile>
@@ -734,6 +737,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
       <Filter>src\core\lib\compression</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
index 6b79f09b3b059576ca461bb78a242f7912430d66..e712c48fff558e69e594543e38ca18c7575f9d7e 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
@@ -375,6 +375,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
@@ -521,6 +522,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
index 5c3a2df68ed2baa9ae75302f4099e8b3788adac0..b2ccc64003bb10a6bca67051aebc9f572727df19 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
@@ -109,6 +109,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
       <Filter>src\core\lib\compression</Filter>
     </ClCompile>
@@ -707,6 +710,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
       <Filter>src\core\lib\compression</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 9ade6e38b543c18bccf19a3e2180d142b03f8fbd..229f608144e17bb1e14e39d40b43deb3efabdaf9 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -304,6 +304,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
@@ -475,6 +476,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 2bc1bb0279b41b7652eed18c579328ba07612474..a8e0b5c9615c6267c070c33c99f3122609487a5b 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -28,6 +28,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
       <Filter>src\core\lib\compression</Filter>
     </ClCompile>
@@ -683,6 +686,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
       <Filter>src\core\lib\compression</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
index d589009eb95582628c2b90ae585502ab1859643f..5718652ddea3df9878257d55760625879122be1b 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
@@ -195,6 +195,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
@@ -319,6 +320,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
index e47b003e7b5fc006d1237e11f1356d29fc4b921e..da80cac35068f98cddd094b1b35530751d0432a2 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
@@ -76,6 +76,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
       <Filter>src\core\lib\compression</Filter>
     </ClCompile>
@@ -464,6 +467,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
       <Filter>src\core\lib\compression</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index 13fca332547c42718905d9214475a6a8923984b2..9cd3d6e4e699924fc592fd66e6ffd69b2455e6f5 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -294,6 +294,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
@@ -443,6 +444,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 09007d4c82eeedca5b03903bf27ac21aa86c3566..c78e8292a36c36f63b372d15215d7cfa984a0522 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -31,6 +31,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
       <Filter>src\core\lib\compression</Filter>
     </ClCompile>
@@ -593,6 +596,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
       <Filter>src\core\lib\compression</Filter>
     </ClInclude>