diff --git a/BUILD b/BUILD
index 9f378ae990df2022557743b34097136023f666ca..091669917be2c9d923d942b67a863ad79e766d28 100644
--- a/BUILD
+++ b/BUILD
@@ -178,6 +178,7 @@ cc_library(
     "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h",
     "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h",
     "src/core/client_config/uri_parser.h",
+    "src/core/compression/algorithm_metadata.h",
     "src/core/compression/message_compress.h",
     "src/core/debug/trace.h",
     "src/core/httpcli/format_request.h",
@@ -470,6 +471,7 @@ cc_library(
     "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h",
     "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h",
     "src/core/client_config/uri_parser.h",
+    "src/core/compression/algorithm_metadata.h",
     "src/core/compression/message_compress.h",
     "src/core/debug/trace.h",
     "src/core/httpcli/format_request.h",
@@ -1269,6 +1271,7 @@ objc_library(
     "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h",
     "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h",
     "src/core/client_config/uri_parser.h",
+    "src/core/compression/algorithm_metadata.h",
     "src/core/compression/message_compress.h",
     "src/core/debug/trace.h",
     "src/core/httpcli/format_request.h",
diff --git a/build.yaml b/build.yaml
index 63d47ed84d9fa0d9e46215c20b9bc0bcc888e355..d157ace2f765903260bd8e8486386f3084fbd108 100644
--- a/build.yaml
+++ b/build.yaml
@@ -134,6 +134,7 @@ filegroups:
   - src/core/client_config/subchannel_factory_decorators/add_channel_arg.h
   - src/core/client_config/subchannel_factory_decorators/merge_channel_args.h
   - src/core/client_config/uri_parser.h
+  - src/core/compression/algorithm_metadata.h
   - src/core/compression/message_compress.h
   - src/core/debug/trace.h
   - src/core/httpcli/format_request.h
diff --git a/gRPC.podspec b/gRPC.podspec
index 1e248f58fdb324782648000b867df2b791d410e9..ac80ee02a9a71620741a99c0bd85fd47ae5ea09f 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -182,6 +182,7 @@ Pod::Spec.new do |s|
                       'src/core/client_config/subchannel_factory_decorators/add_channel_arg.h',
                       'src/core/client_config/subchannel_factory_decorators/merge_channel_args.h',
                       'src/core/client_config/uri_parser.h',
+                      'src/core/compression/algorithm_metadata.h',
                       'src/core/compression/message_compress.h',
                       'src/core/debug/trace.h',
                       'src/core/httpcli/format_request.h',
@@ -480,6 +481,7 @@ Pod::Spec.new do |s|
                               'src/core/client_config/subchannel_factory_decorators/add_channel_arg.h',
                               'src/core/client_config/subchannel_factory_decorators/merge_channel_args.h',
                               'src/core/client_config/uri_parser.h',
+                              'src/core/compression/algorithm_metadata.h',
                               'src/core/compression/message_compress.h',
                               'src/core/debug/trace.h',
                               'src/core/httpcli/format_request.h',
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index 020138bf15744fd93f00b502fc3b889f683aa70a..1abcd3b9cc87ccf2a70eb5bd1d372ea1d4b630e7 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -364,7 +364,8 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
 /* Constructor for call_data */
 static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                            grpc_call_element_args *args) {
-  grpc_subchannel_call_holder_init(elem->call_data, cc_pick_subchannel, elem);
+  grpc_subchannel_call_holder_init(elem->call_data, cc_pick_subchannel, elem,
+                                   args->metadata_context);
 }
 
 /* Destructor for call_data */
diff --git a/src/core/channel/client_uchannel.c b/src/core/channel/client_uchannel.c
index cf5e3bf482cff4f8ce6500553fb57a84563f743a..3276635625dc78dbf6a16b5c24ab94ed079eb234 100644
--- a/src/core/channel/client_uchannel.c
+++ b/src/core/channel/client_uchannel.c
@@ -140,7 +140,7 @@ static int cuc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg,
 static void cuc_init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                                grpc_call_element_args *args) {
   grpc_subchannel_call_holder_init(elem->call_data, cuc_pick_subchannel,
-                                   elem->channel_data);
+                                   elem->channel_data, args->metadata_context);
 }
 
 /* Destructor for call_data */
@@ -244,11 +244,11 @@ void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx,
 }
 
 grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel,
-                                          grpc_channel_args *args) {
+                                          grpc_channel_args *args,
+                                          grpc_mdctx *mdctx) {
   grpc_channel *channel = NULL;
 #define MAX_FILTERS 3
   const grpc_channel_filter *filters[MAX_FILTERS];
-  grpc_mdctx *mdctx = grpc_subchannel_get_mdctx(subchannel);
   grpc_channel *master = grpc_subchannel_get_master(subchannel);
   char *target = grpc_channel_get_target(master);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
diff --git a/src/core/channel/client_uchannel.h b/src/core/channel/client_uchannel.h
index dfe6695ae3eec2dbbbbbf4f828a1214cf908dc66..54fbea964c1588c2dce7c723037e354dd5c1fe7a 100644
--- a/src/core/channel/client_uchannel.h
+++ b/src/core/channel/client_uchannel.h
@@ -62,7 +62,8 @@ void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx,
                                                grpc_pollset *pollset);
 
 grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel,
-                                          grpc_channel_args *args);
+                                          grpc_channel_args *args,
+                                          grpc_mdctx *mdctx);
 
 void grpc_client_uchannel_set_subchannel(grpc_channel *uchannel,
                                          grpc_subchannel *subchannel);
diff --git a/src/core/channel/compress_filter.c b/src/core/channel/compress_filter.c
index 62219f5aa724b6062b01898e72e60c3b85e3b2cd..25d6e51281b864e87663f13d8ee5688872619665 100644
--- a/src/core/channel/compress_filter.c
+++ b/src/core/channel/compress_filter.c
@@ -42,6 +42,7 @@
 #include "src/core/channel/compress_filter.h"
 #include "src/core/channel/channel_args.h"
 #include "src/core/profiling/timers.h"
+#include "src/core/compression/algorithm_metadata.h"
 #include "src/core/compression/message_compress.h"
 #include "src/core/support/string.h"
 #include "src/core/transport/static_metadata.h"
@@ -65,6 +66,8 @@ typedef struct call_data {
   grpc_closure *post_send;
   grpc_closure send_done;
   grpc_closure got_slice;
+
+  grpc_mdctx *mdctx;
 } call_data;
 
 typedef struct channel_data {
@@ -72,6 +75,8 @@ typedef struct channel_data {
   grpc_compression_algorithm default_compression_algorithm;
   /** Compression options for the channel */
   grpc_compression_options compression_options;
+  /** Supported compression algorithms */
+  gpr_uint32 supported_compression_algorithms;
 } channel_data;
 
 /** For each \a md element from the incoming metadata, filter out the entry for
@@ -82,7 +87,7 @@ static grpc_mdelem *compression_md_filter(void *user_data, grpc_mdelem *md) {
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
 
-  if (md->key == GRPC_MDSTR_GRPC_ENCODING) {
+  if (md->key == GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST) {
     const char *md_c_str = grpc_mdstr_as_c_string(md->value);
     if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str),
                                           &calld->compression_algorithm)) {
@@ -138,14 +143,13 @@ static void process_send_initial_metadata(
   /* hint compression algorithm */
   grpc_metadata_batch_add_tail(
       initial_metadata, &calld->compression_algorithm_storage,
-      GRPC_MDELEM_REF(
-          channeld
-              ->mdelem_compression_algorithms[calld->compression_algorithm]));
+      grpc_compression_encoding_mdelem(calld->compression_algorithm));
 
   /* convey supported compression algorithms */
   grpc_metadata_batch_add_tail(
       initial_metadata, &calld->accept_encoding_storage,
-      GRPC_MDELEM_REF(channeld->mdelem_accept_encoding));
+      GRPC_MDELEM_REF(grpc_accept_encoding_mdelem_from_compression_algorithms(
+          calld->mdctx, channeld->supported_compression_algorithms)));
 }
 
 static void continue_send_message(grpc_exec_ctx *exec_ctx,
@@ -239,6 +243,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   /* initialize members */
   gpr_slice_buffer_init(&calld->slices);
   calld->has_compression_algorithm = 0;
+  calld->mdctx = args->metadata_context;
   grpc_closure_init(&calld->got_slice, got_slice, elem);
   grpc_closure_init(&calld->send_done, send_done, elem);
 }
@@ -257,10 +262,6 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
                               grpc_channel_element_args *args) {
   channel_data *channeld = elem->channel_data;
   grpc_compression_algorithm algo_idx;
-  const char *supported_algorithms_names[GRPC_COMPRESS_ALGORITHMS_COUNT - 1];
-  size_t supported_algorithms_idx = 0;
-  char *accept_encoding_str;
-  size_t accept_encoding_str_len;
 
   grpc_compression_options_init(&channeld->compression_options);
   channeld->compression_options.enabled_algorithms_bitset =
@@ -275,61 +276,22 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   channeld->compression_options.default_compression_algorithm =
       channeld->default_compression_algorithm;
 
-  channeld->mdstr_request_compression_algorithm_key = grpc_mdstr_from_string(
-      args->metadata_context, GRPC_COMPRESS_REQUEST_ALGORITHM_KEY);
-
-  channeld->mdstr_outgoing_compression_algorithm_key =
-      grpc_mdstr_from_string(args->metadata_context, "grpc-encoding");
-
-  channeld->mdstr_compression_capabilities_key =
-      grpc_mdstr_from_string(args->metadata_context, "grpc-accept-encoding");
-
+  channeld->supported_compression_algorithms = 0;
   for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) {
-    char *algorithm_name;
     /* skip disabled algorithms */
     if (grpc_compression_options_is_algorithm_enabled(
             &channeld->compression_options, algo_idx) == 0) {
       continue;
     }
-    GPR_ASSERT(grpc_compression_algorithm_name(algo_idx, &algorithm_name) != 0);
-    channeld->mdelem_compression_algorithms[algo_idx] =
-        grpc_mdelem_from_metadata_strings(
-            args->metadata_context,
-            GRPC_MDSTR_REF(channeld->mdstr_outgoing_compression_algorithm_key),
-            grpc_mdstr_from_string(args->metadata_context, algorithm_name));
-    if (algo_idx > 0) {
-      supported_algorithms_names[supported_algorithms_idx++] = algorithm_name;
-    }
+    channeld->supported_compression_algorithms |= 1u << algo_idx;
   }
 
-  /* TODO(dgq): gpr_strjoin_sep could be made to work with statically allocated
-   * arrays, as to avoid the heap allocs */
-  accept_encoding_str =
-      gpr_strjoin_sep(supported_algorithms_names, supported_algorithms_idx, ",",
-                      &accept_encoding_str_len);
-
-  channeld->mdelem_accept_encoding = grpc_mdelem_from_metadata_strings(
-      args->metadata_context,
-      GRPC_MDSTR_REF(channeld->mdstr_compression_capabilities_key),
-      grpc_mdstr_from_string(args->metadata_context, accept_encoding_str));
-  gpr_free(accept_encoding_str);
-
   GPR_ASSERT(!args->is_last);
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
-  channel_data *channeld = elem->channel_data;
-  grpc_compression_algorithm algo_idx;
-
-  GRPC_MDSTR_UNREF(channeld->mdstr_request_compression_algorithm_key);
-  GRPC_MDSTR_UNREF(channeld->mdstr_outgoing_compression_algorithm_key);
-  GRPC_MDSTR_UNREF(channeld->mdstr_compression_capabilities_key);
-  for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) {
-    GRPC_MDELEM_UNREF(channeld->mdelem_compression_algorithms[algo_idx]);
-  }
-  GRPC_MDELEM_UNREF(channeld->mdelem_accept_encoding);
 }
 
 const grpc_channel_filter grpc_compress_filter = {
diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c
index 3a0f68f30fc32f9005ebf026d59315bc51c705da..ec7791656fae6264aeffc1602533fdccd772f505 100644
--- a/src/core/channel/http_client_filter.c
+++ b/src/core/channel/http_client_filter.c
@@ -37,6 +37,7 @@
 #include <grpc/support/string_util.h>
 #include "src/core/support/string.h"
 #include "src/core/profiling/timers.h"
+#include "src/core/transport/static_metadata.h"
 
 typedef struct call_data {
   grpc_linked_mdelem method;
@@ -54,17 +55,11 @@ typedef struct call_data {
       up-call on transport_op, and remember to call our on_done_recv member
       after handling it. */
   grpc_closure hc_on_recv;
+
+  grpc_mdctx *mdctx;
 } call_data;
 
-typedef struct channel_data {
-  grpc_mdelem *te_trailers;
-  grpc_mdelem *method;
-  grpc_mdelem *scheme;
-  grpc_mdelem *content_type;
-  grpc_mdelem *status;
-  /** complete user agent mdelem */
-  grpc_mdelem *user_agent;
-} channel_data;
+typedef struct channel_data { grpc_mdelem *static_scheme; } channel_data;
 
 typedef struct {
   grpc_call_element *elem;
@@ -73,14 +68,12 @@ typedef struct {
 
 static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
   client_recv_filter_args *a = user_data;
-  grpc_call_element *elem = a->elem;
-  channel_data *channeld = elem->channel_data;
-  if (md == channeld->status) {
+  if (md == GRPC_MDELEM_STATUS_200) {
     return NULL;
-  } else if (md->key == channeld->status->key) {
-    grpc_call_element_send_cancel(a->exec_ctx, elem);
+  } else if (md->key == GRPC_MDSTR_STATUS) {
+    grpc_call_element_send_cancel(a->exec_ctx, a->elem);
     return NULL;
-  } else if (md->key == channeld->content_type->key) {
+  } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
     return NULL;
   }
   return md;
@@ -98,14 +91,12 @@ static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) {
 }
 
 static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
-  grpc_call_element *elem = user_data;
-  channel_data *channeld = elem->channel_data;
   /* eat the things we'd like to set ourselves */
-  if (md->key == channeld->method->key) return NULL;
-  if (md->key == channeld->scheme->key) return NULL;
-  if (md->key == channeld->te_trailers->key) return NULL;
-  if (md->key == channeld->content_type->key) return NULL;
-  if (md->key == channeld->user_agent->key) return NULL;
+  if (md->key == GRPC_MDSTR_METHOD) return NULL;
+  if (md->key == GRPC_MDSTR_SCHEME) return NULL;
+  if (md->key == GRPC_MDSTR_TE) return NULL;
+  if (md->key == GRPC_MDSTR_CONTENT_TYPE) return NULL;
+  if (md->key == GRPC_MDSTR_USER_AGENT) return NULL;
   return md;
 }
 
@@ -120,16 +111,18 @@ static void hc_mutate_op(grpc_call_element *elem,
     /* Send : prefixed headers, which have to be before any application
        layer headers. */
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
-                                 GRPC_MDELEM_REF(channeld->method));
+                                 GRPC_MDELEM_METHOD_POST);
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
-                                 GRPC_MDELEM_REF(channeld->scheme));
+                                 channeld->static_scheme);
     grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
-                                 GRPC_MDELEM_REF(channeld->te_trailers));
-    grpc_metadata_batch_add_tail(op->send_initial_metadata,
-                                 &calld->content_type,
-                                 GRPC_MDELEM_REF(channeld->content_type));
-    grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
-                                 GRPC_MDELEM_REF(channeld->user_agent));
+                                 GRPC_MDELEM_TE_TRAILERS);
+    grpc_metadata_batch_add_tail(
+        op->send_initial_metadata, &calld->content_type,
+        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
+    grpc_metadata_batch_add_tail(
+        op->send_initial_metadata, &calld->user_agent,
+        GRPC_MDELEM_REF(grpc_mdelem_from_cache(calld->mdctx,
+                                               GRPC_MDELEM_CACHED_USER_AGENT)));
   }
 
   if (op->recv_initial_metadata != NULL) {
@@ -155,6 +148,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                            grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   calld->on_done_recv = NULL;
+  calld->mdctx = args->metadata_context;
   grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
 }
 
@@ -162,109 +156,39 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
                               grpc_call_element *elem) {}
 
-static const char *scheme_from_args(const grpc_channel_args *args) {
+static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
   unsigned i;
+  size_t j;
+  grpc_mdelem *valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
+                                  GRPC_MDELEM_SCHEME_HTTPS};
   if (args != NULL) {
     for (i = 0; i < args->num_args; ++i) {
       if (args->args[i].type == GRPC_ARG_STRING &&
           strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
-        return args->args[i].value.string;
+        for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
+          if (0 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value),
+                          args->args[i].value.string)) {
+            return valid_schemes[j];
+          }
+        }
       }
     }
   }
-  return "http";
-}
-
-static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
-                                        const grpc_channel_args *args) {
-  gpr_strvec v;
-  size_t i;
-  int is_first = 1;
-  char *tmp;
-  grpc_mdstr *result;
-
-  gpr_strvec_init(&v);
-
-  for (i = 0; args && i < args->num_args; i++) {
-    if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
-      if (args->args[i].type != GRPC_ARG_STRING) {
-        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
-                GRPC_ARG_PRIMARY_USER_AGENT_STRING);
-      } else {
-        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
-        is_first = 0;
-        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
-      }
-    }
-  }
-
-  gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
-               grpc_version_string(), GPR_PLATFORM_STRING);
-  is_first = 0;
-  gpr_strvec_add(&v, tmp);
-
-  for (i = 0; args && i < args->num_args; i++) {
-    if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
-      if (args->args[i].type != GRPC_ARG_STRING) {
-        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
-                GRPC_ARG_SECONDARY_USER_AGENT_STRING);
-      } else {
-        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
-        is_first = 0;
-        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
-      }
-    }
-  }
-
-  tmp = gpr_strvec_flatten(&v, NULL);
-  gpr_strvec_destroy(&v);
-  result = grpc_mdstr_from_string(mdctx, tmp);
-  gpr_free(tmp);
-
-  return result;
+  return GRPC_MDELEM_SCHEME_HTTP;
 }
 
 /* Constructor for channel_data */
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
                               grpc_channel_element *elem,
                               grpc_channel_element_args *args) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  /* The first and the last filters tend to be implemented differently to
-     handle the case that there's no 'next' filter to call on the up or down
-     path */
+  channel_data *chand = elem->channel_data;
   GPR_ASSERT(!args->is_last);
-
-  /* initialize members */
-  channeld->te_trailers =
-      grpc_mdelem_from_strings(args->metadata_context, "te", "trailers");
-  channeld->method =
-      grpc_mdelem_from_strings(args->metadata_context, ":method", "POST");
-  channeld->scheme = grpc_mdelem_from_strings(
-      args->metadata_context, ":scheme", scheme_from_args(args->channel_args));
-  channeld->content_type = grpc_mdelem_from_strings(
-      args->metadata_context, "content-type", "application/grpc");
-  channeld->status =
-      grpc_mdelem_from_strings(args->metadata_context, ":status", "200");
-  channeld->user_agent = grpc_mdelem_from_metadata_strings(
-      args->metadata_context,
-      grpc_mdstr_from_string(args->metadata_context, "user-agent"),
-      user_agent_from_args(args->metadata_context, args->channel_args));
+  chand->static_scheme = scheme_from_args(args->channel_args);
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  GRPC_MDELEM_UNREF(channeld->te_trailers);
-  GRPC_MDELEM_UNREF(channeld->method);
-  GRPC_MDELEM_UNREF(channeld->scheme);
-  GRPC_MDELEM_UNREF(channeld->content_type);
-  GRPC_MDELEM_UNREF(channeld->status);
-  GRPC_MDELEM_UNREF(channeld->user_agent);
 }
 
 const grpc_channel_filter grpc_http_client_filter = {
diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c
index 2adfe2bb61d0e7b244b9800ec7c32b9a62cc029c..c9db9470e064d60be03abe6059f9e820a8dd69f3 100644
--- a/src/core/channel/http_server_filter.c
+++ b/src/core/channel/http_server_filter.c
@@ -37,6 +37,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include "src/core/profiling/timers.h"
+#include "src/core/transport/static_metadata.h"
 
 typedef struct call_data {
   gpr_uint8 seen_path;
@@ -55,24 +56,10 @@ typedef struct call_data {
       up-call on transport_op, and remember to call our on_done_recv member
       after handling it. */
   grpc_closure hs_on_recv;
+  grpc_mdctx *mdctx;
 } call_data;
 
-typedef struct channel_data {
-  grpc_mdelem *te_trailers;
-  grpc_mdelem *method_post;
-  grpc_mdelem *http_scheme;
-  grpc_mdelem *https_scheme;
-  /* TODO(klempner): Remove this once we stop using it */
-  grpc_mdelem *grpc_scheme;
-  grpc_mdelem *content_type;
-  grpc_mdelem *status_ok;
-  grpc_mdelem *status_not_found;
-  grpc_mdstr *path_key;
-  grpc_mdstr *authority_key;
-  grpc_mdstr *host_key;
-
-  grpc_mdctx *mdctx;
-} channel_data;
+typedef struct channel_data { gpr_uint8 unused; } channel_data;
 
 typedef struct {
   grpc_call_element *elem;
@@ -82,25 +69,24 @@ typedef struct {
 static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
   server_filter_args *a = user_data;
   grpc_call_element *elem = a->elem;
-  channel_data *channeld = elem->channel_data;
   call_data *calld = elem->call_data;
 
   /* Check if it is one of the headers we care about. */
-  if (md == channeld->te_trailers || md == channeld->method_post ||
-      md == channeld->http_scheme || md == channeld->https_scheme ||
-      md == channeld->grpc_scheme || md == channeld->content_type) {
+  if (md == GRPC_MDELEM_TE_TRAILERS || md == GRPC_MDELEM_METHOD_POST ||
+      md == GRPC_MDELEM_SCHEME_HTTP || md == GRPC_MDELEM_SCHEME_HTTPS ||
+      md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
     /* swallow it */
-    if (md == channeld->method_post) {
+    if (md == GRPC_MDELEM_METHOD_POST) {
       calld->seen_post = 1;
-    } else if (md->key == channeld->http_scheme->key) {
+    } else if (md->key == GRPC_MDSTR_SCHEME) {
       calld->seen_scheme = 1;
-    } else if (md == channeld->te_trailers) {
+    } else if (md == GRPC_MDELEM_TE_TRAILERS) {
       calld->seen_te_trailers = 1;
     }
     /* TODO(klempner): Track that we've seen all the headers we should
        require */
     return NULL;
-  } else if (md->key == channeld->content_type->key) {
+  } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
     if (strncmp(grpc_mdstr_as_c_string(md->value), "application/grpc+", 17) ==
         0) {
       /* Although the C implementation doesn't (currently) generate them,
@@ -112,12 +98,11 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
       /* TODO(klempner): We're currently allowing this, but we shouldn't
          see it without a proxy so log for now. */
       gpr_log(GPR_INFO, "Unexpected content-type %s",
-              channeld->content_type->key);
+              grpc_mdstr_as_c_string(md->value));
     }
     return NULL;
-  } else if (md->key == channeld->te_trailers->key ||
-             md->key == channeld->method_post->key ||
-             md->key == channeld->http_scheme->key) {
+  } else if (md->key == GRPC_MDSTR_TE || md->key == GRPC_MDSTR_METHOD ||
+             md->key == GRPC_MDSTR_SCHEME) {
     gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
             grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value));
     /* swallow it and error everything out. */
@@ -125,22 +110,21 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
        on the wire here. */
     grpc_call_element_send_cancel(a->exec_ctx, elem);
     return NULL;
-  } else if (md->key == channeld->path_key) {
+  } else if (md->key == GRPC_MDSTR_PATH) {
     if (calld->seen_path) {
       gpr_log(GPR_ERROR, "Received :path twice");
       return NULL;
     }
     calld->seen_path = 1;
     return md;
-  } else if (md->key == channeld->authority_key) {
+  } else if (md->key == GRPC_MDSTR_AUTHORITY) {
     calld->seen_authority = 1;
     return md;
-  } else if (md->key == channeld->host_key) {
+  } else if (md->key == GRPC_MDSTR_HOST) {
     /* translate host to :authority since :authority may be
        omitted */
     grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
-        channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key),
-        GRPC_MDSTR_REF(md->value));
+        calld->mdctx, GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
     GRPC_MDELEM_UNREF(md);
     calld->seen_authority = 1;
     return authority;
@@ -191,15 +175,14 @@ static void hs_mutate_op(grpc_call_element *elem,
                          grpc_transport_stream_op *op) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
-  channel_data *channeld = elem->channel_data;
 
   if (op->send_initial_metadata != NULL && !calld->sent_status) {
     calld->sent_status = 1;
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->status,
-                                 GRPC_MDELEM_REF(channeld->status_ok));
-    grpc_metadata_batch_add_tail(op->send_initial_metadata,
-                                 &calld->content_type,
-                                 GRPC_MDELEM_REF(channeld->content_type));
+                                 GRPC_MDELEM_STATUS_200);
+    grpc_metadata_batch_add_tail(
+        op->send_initial_metadata, &calld->content_type,
+        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
   }
 
   if (op->recv_initial_metadata) {
@@ -228,6 +211,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   /* initialize members */
   memset(calld, 0, sizeof(*calld));
   grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem);
+  calld->mdctx = args->metadata_context;
 }
 
 /* Destructor for call_data */
@@ -238,56 +222,12 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
                               grpc_channel_element *elem,
                               grpc_channel_element_args *args) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  /* The first and the last filters tend to be implemented differently to
-     handle the case that there's no 'next' filter to call on the up or down
-     path */
   GPR_ASSERT(!args->is_last);
-
-  /* initialize members */
-  channeld->te_trailers =
-      grpc_mdelem_from_strings(args->metadata_context, "te", "trailers");
-  channeld->status_ok =
-      grpc_mdelem_from_strings(args->metadata_context, ":status", "200");
-  channeld->status_not_found =
-      grpc_mdelem_from_strings(args->metadata_context, ":status", "404");
-  channeld->method_post =
-      grpc_mdelem_from_strings(args->metadata_context, ":method", "POST");
-  channeld->http_scheme =
-      grpc_mdelem_from_strings(args->metadata_context, ":scheme", "http");
-  channeld->https_scheme =
-      grpc_mdelem_from_strings(args->metadata_context, ":scheme", "https");
-  channeld->grpc_scheme =
-      grpc_mdelem_from_strings(args->metadata_context, ":scheme", "grpc");
-  channeld->path_key = grpc_mdstr_from_string(args->metadata_context, ":path");
-  channeld->authority_key =
-      grpc_mdstr_from_string(args->metadata_context, ":authority");
-  channeld->host_key = grpc_mdstr_from_string(args->metadata_context, "host");
-  channeld->content_type = grpc_mdelem_from_strings(
-      args->metadata_context, "content-type", "application/grpc");
-
-  channeld->mdctx = args->metadata_context;
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  GRPC_MDELEM_UNREF(channeld->te_trailers);
-  GRPC_MDELEM_UNREF(channeld->status_ok);
-  GRPC_MDELEM_UNREF(channeld->status_not_found);
-  GRPC_MDELEM_UNREF(channeld->method_post);
-  GRPC_MDELEM_UNREF(channeld->http_scheme);
-  GRPC_MDELEM_UNREF(channeld->https_scheme);
-  GRPC_MDELEM_UNREF(channeld->grpc_scheme);
-  GRPC_MDELEM_UNREF(channeld->content_type);
-  GRPC_MDSTR_UNREF(channeld->path_key);
-  GRPC_MDSTR_UNREF(channeld->authority_key);
-  GRPC_MDSTR_UNREF(channeld->host_key);
 }
 
 const grpc_channel_filter grpc_http_server_filter = {
diff --git a/src/core/channel/subchannel_call_holder.c b/src/core/channel/subchannel_call_holder.c
index 72517145191e371d65a6b311fff12fe36eb88cc0..c5340e0eaf85bd795d9141eb0dafc62644b17535 100644
--- a/src/core/channel/subchannel_call_holder.c
+++ b/src/core/channel/subchannel_call_holder.c
@@ -58,7 +58,7 @@ static void retry_waiting_locked(grpc_exec_ctx *exec_ctx,
 void grpc_subchannel_call_holder_init(
     grpc_subchannel_call_holder *holder,
     grpc_subchannel_call_holder_pick_subchannel pick_subchannel,
-    void *pick_subchannel_arg) {
+    void *pick_subchannel_arg, grpc_mdctx *mdctx) {
   gpr_atm_rel_store(&holder->subchannel_call, 0);
   holder->pick_subchannel = pick_subchannel;
   holder->pick_subchannel_arg = pick_subchannel_arg;
@@ -68,6 +68,7 @@ void grpc_subchannel_call_holder_init(
   holder->waiting_ops_count = 0;
   holder->waiting_ops_capacity = 0;
   holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+  holder->mdctx = mdctx;
 }
 
 void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx,
@@ -156,9 +157,9 @@ retry:
       holder->subchannel != NULL) {
     holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL;
     grpc_closure_init(&holder->next_step, call_ready, holder);
-    if (grpc_subchannel_create_call(exec_ctx, holder->subchannel,
-                                    holder->pollset, &holder->subchannel_call,
-                                    &holder->next_step)) {
+    if (grpc_subchannel_create_call(
+            exec_ctx, holder->subchannel, holder->pollset, holder->mdctx,
+            &holder->subchannel_call, &holder->next_step)) {
       /* got one immediately - continue the op (and any waiting ops) */
       holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
       retry_waiting_locked(exec_ctx, holder);
@@ -184,9 +185,9 @@ static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg, int success) {
     fail_locked(exec_ctx, holder);
   } else {
     grpc_closure_init(&holder->next_step, call_ready, holder);
-    if (grpc_subchannel_create_call(exec_ctx, holder->subchannel,
-                                    holder->pollset, &holder->subchannel_call,
-                                    &holder->next_step)) {
+    if (grpc_subchannel_create_call(
+            exec_ctx, holder->subchannel, holder->pollset, holder->mdctx,
+            &holder->subchannel_call, &holder->next_step)) {
       holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
       /* got one immediately - continue the op (and any waiting ops) */
       retry_waiting_locked(exec_ctx, holder);
diff --git a/src/core/channel/subchannel_call_holder.h b/src/core/channel/subchannel_call_holder.h
index bda051c56605564bc0f3f5a306074aa38cc56697..a770be257c13a398953b69e56ffcc18b604df732 100644
--- a/src/core/channel/subchannel_call_holder.h
+++ b/src/core/channel/subchannel_call_holder.h
@@ -68,6 +68,8 @@ typedef struct grpc_subchannel_call_holder {
   grpc_subchannel_call_holder_pick_subchannel pick_subchannel;
   void *pick_subchannel_arg;
 
+  grpc_mdctx *mdctx;
+
   gpr_mu mu;
 
   grpc_subchannel_call_holder_creation_phase creation_phase;
@@ -84,7 +86,7 @@ typedef struct grpc_subchannel_call_holder {
 void grpc_subchannel_call_holder_init(
     grpc_subchannel_call_holder *holder,
     grpc_subchannel_call_holder_pick_subchannel pick_subchannel,
-    void *pick_subchannel_arg);
+    void *pick_subchannel_arg, grpc_mdctx *mdctx);
 void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx,
                                          grpc_subchannel_call_holder *holder);
 
diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c
index b15a9033af803bd1261fbb083ddf5fea2fe19ede..5f906a82fb123e8e0f2d8fd6e6e011e104e3aa83 100644
--- a/src/core/client_config/subchannel.c
+++ b/src/core/client_config/subchannel.c
@@ -73,6 +73,7 @@ typedef struct waiting_for_connect {
   grpc_pollset *pollset;
   gpr_atm *target;
   grpc_subchannel *subchannel;
+  grpc_mdctx *mdctx;
   grpc_closure continuation;
 } waiting_for_connect;
 
@@ -87,8 +88,6 @@ struct grpc_subchannel {
   /** address to connect to */
   struct sockaddr *addr;
   size_t addr_len;
-  /** metadata context */
-  grpc_mdctx *mdctx;
   /** master channel - the grpc_channel instance that ultimately owns
       this channel_data via its channel stack.
       We occasionally use this to bump the refcount on the master channel
@@ -147,8 +146,8 @@ struct grpc_subchannel_call {
   (((grpc_subchannel_call *)(callstack)) - 1)
 
 static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx,
-                                         connection *con,
-                                         grpc_pollset *pollset);
+                                         connection *con, grpc_pollset *pollset,
+                                         grpc_mdctx *mdctx);
 static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx,
                                               grpc_subchannel *c,
                                               const char *reason);
@@ -267,7 +266,6 @@ static void subchannel_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
   gpr_free((void *)c->filters);
   grpc_channel_args_destroy(c->args);
   gpr_free(c->addr);
-  grpc_mdctx_unref(c->mdctx);
   grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker);
   grpc_connector_unref(exec_ctx, c->connector);
   gpr_free(c);
@@ -306,11 +304,9 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
   memcpy(c->addr, args->addr, args->addr_len);
   c->addr_len = args->addr_len;
   c->args = grpc_channel_args_copy(args->args);
-  c->mdctx = args->mdctx;
   c->master = args->master;
   c->pollset_set = grpc_client_channel_get_connecting_pollset_set(parent_elem);
   c->random = random_seed();
-  grpc_mdctx_ref(c->mdctx);
   grpc_closure_init(&c->connected, subchannel_connected, c);
   grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE,
                                "subchannel");
@@ -398,8 +394,9 @@ static void continue_creating_call(grpc_exec_ctx *exec_ctx, void *arg,
   int call_creation_finished_ok;
   waiting_for_connect *w4c = arg;
   grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel, w4c->pollset);
-  call_creation_finished_ok = grpc_subchannel_create_call(
-      exec_ctx, w4c->subchannel, w4c->pollset, w4c->target, w4c->notify);
+  call_creation_finished_ok =
+      grpc_subchannel_create_call(exec_ctx, w4c->subchannel, w4c->pollset,
+                                  w4c->mdctx, w4c->target, w4c->notify);
   GPR_ASSERT(call_creation_finished_ok == 1);
   w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success);
   GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect");
@@ -407,8 +404,8 @@ static void continue_creating_call(grpc_exec_ctx *exec_ctx, void *arg,
 }
 
 int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx, grpc_subchannel *c,
-                                grpc_pollset *pollset, gpr_atm *target,
-                                grpc_closure *notify) {
+                                grpc_pollset *pollset, grpc_mdctx *mdctx,
+                                gpr_atm *target, grpc_closure *notify) {
   connection *con;
   grpc_subchannel_call *call;
   GPR_TIMER_BEGIN("grpc_subchannel_create_call", 0);
@@ -418,7 +415,7 @@ int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx, grpc_subchannel *c,
     CONNECTION_REF_LOCKED(con, "call");
     gpr_mu_unlock(&c->mu);
 
-    call = create_call(exec_ctx, con, pollset);
+    call = create_call(exec_ctx, con, pollset, mdctx);
     if (!gpr_atm_rel_cas(target, 0, (gpr_atm)(gpr_uintptr)call)) {
       GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "failed to set");
     }
@@ -431,6 +428,7 @@ int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx, grpc_subchannel *c,
     w4c->pollset = pollset;
     w4c->target = target;
     w4c->subchannel = c;
+    w4c->mdctx = mdctx;
     /* released when clearing w4c */
     SUBCHANNEL_REF_LOCKED(c, "waiting_for_connect");
     grpc_closure_init(&w4c->continuation, continue_creating_call, w4c);
@@ -624,7 +622,7 @@ static void publish_transport(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
   con->refs = 0;
   con->subchannel = c;
   grpc_channel_stack_init(exec_ctx, filters, num_filters, c->master, c->args,
-                          c->mdctx, stk);
+                          stk);
   grpc_connected_channel_bind_transport(stk, c->connecting_result.transport);
   gpr_free((void *)c->connecting_result.filters);
   memset(&c->connecting_result, 0, sizeof(c->connecting_result));
@@ -858,23 +856,19 @@ void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx,
 }
 
 static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx,
-                                         connection *con,
-                                         grpc_pollset *pollset) {
+                                         connection *con, grpc_pollset *pollset,
+                                         grpc_mdctx *mdctx) {
   grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con);
   grpc_subchannel_call *call =
       gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size);
   grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call);
   call->connection = con;
   grpc_call_stack_init(exec_ctx, chanstk, 1, subchannel_call_destroy, call,
-                       NULL, NULL, callstk);
+                       NULL, NULL, mdctx, callstk);
   grpc_call_stack_set_pollset(exec_ctx, callstk, pollset);
   return call;
 }
 
-grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel) {
-  return subchannel->mdctx;
-}
-
 grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel) {
   return subchannel->master;
 }
diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h
index 381b7689b891d62a40125b055ccb5e8527158e3a..02ff25eb21d1a06efe04c0dcf43c0e6278b99a30 100644
--- a/src/core/client_config/subchannel.h
+++ b/src/core/client_config/subchannel.h
@@ -84,8 +84,8 @@ void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx,
  * asynchronously, invoking the \a notify callback upon completion. */
 int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx,
                                 grpc_subchannel *subchannel,
-                                grpc_pollset *pollset, gpr_atm *target,
-                                grpc_closure *notify);
+                                grpc_pollset *pollset, grpc_mdctx *mdctx,
+                                gpr_atm *target, grpc_closure *notify);
 
 /** cancel \a call in the waiting state. */
 void grpc_subchannel_cancel_create_call(grpc_exec_ctx *exec_ctx,
@@ -157,9 +157,6 @@ struct grpc_subchannel_args {
 grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
                                         grpc_subchannel_args *args);
 
-/** Return the metadata context associated with the subchannel */
-grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel);
-
 /** Return the master channel associated with the subchannel */
 grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel);
 
diff --git a/src/core/compression/algorithm.c b/src/core/compression/algorithm.c
index fd95a3c8912c354e86c19863e35d96b4447ca0f4..73d91fa8ea5fb111f54d071bd6e8f8ff4631f100 100644
--- a/src/core/compression/algorithm.c
+++ b/src/core/compression/algorithm.c
@@ -37,7 +37,9 @@
 #include <grpc/compression.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/compression/algorithm_metadata.h"
 #include "src/core/surface/api_trace.h"
+#include "src/core/transport/static_metadata.h"
 
 int grpc_compression_algorithm_parse(const char *name, size_t name_length,
                                      grpc_compression_algorithm *algorithm) {
@@ -72,17 +74,55 @@ int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm,
   switch (algorithm) {
     case GRPC_COMPRESS_NONE:
       *name = "identity";
-      break;
+      return 1;
     case GRPC_COMPRESS_DEFLATE:
       *name = "deflate";
-      break;
+      return 1;
     case GRPC_COMPRESS_GZIP:
       *name = "gzip";
-      break;
-    default:
+      return 1;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
       return 0;
   }
-  return 1;
+  return 0;
+}
+
+grpc_compression_algorithm grpc_compression_algorithm_from_mdstr(
+    grpc_mdstr *str) {
+  if (str == GRPC_MDSTR_IDENTITY) return GRPC_COMPRESS_NONE;
+  if (str == GRPC_MDSTR_DEFLATE) return GRPC_COMPRESS_DEFLATE;
+  if (str == GRPC_MDSTR_GZIP) return GRPC_COMPRESS_GZIP;
+  return GRPC_COMPRESS_ALGORITHMS_COUNT;
+}
+
+grpc_mdstr *grpc_compression_algorithm_mdstr(
+    grpc_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_COMPRESS_NONE:
+      return GRPC_MDSTR_IDENTITY;
+    case GRPC_COMPRESS_DEFLATE:
+      return GRPC_MDSTR_DEFLATE;
+    case GRPC_COMPRESS_GZIP:
+      return GRPC_MDSTR_GZIP;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
+      return NULL;
+  }
+  return NULL;
+}
+
+grpc_mdelem *grpc_compression_encoding_mdelem(
+    grpc_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_COMPRESS_NONE:
+      return GRPC_MDELEM_GRPC_ENCODING_IDENTITY;
+    case GRPC_COMPRESS_DEFLATE:
+      return GRPC_MDELEM_GRPC_ENCODING_DEFLATE;
+    case GRPC_COMPRESS_GZIP:
+      return GRPC_MDELEM_GRPC_ENCODING_GZIP;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
+      return NULL;
+  }
+  return NULL;
 }
 
 /* TODO(dgq): Add the ability to specify parameters to the individual
diff --git a/src/core/compression/algorithm_metadata.h b/src/core/compression/algorithm_metadata.h
new file mode 100644
index 0000000000000000000000000000000000000000..882633c3074ebd8dcd5ac5393b2097990aeeab30
--- /dev/null
+++ b/src/core/compression/algorithm_metadata.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2015, 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_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H
+#define GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H
+
+#include <grpc/compression.h>
+#include "src/core/transport/metadata.h"
+
+/** Return compression algorithm based metadata value */
+grpc_mdstr *grpc_compression_algorithm_mdstr(
+    grpc_compression_algorithm algorithm);
+
+/** Return compression algorithm based metadata element (grpc-encoding: xxx) */
+grpc_mdelem *grpc_compression_encoding_mdelem(
+    grpc_compression_algorithm algorithm);
+
+/** Find compression algorithm based on passed in mdstr - returns
+ * GRPC_COMPRESS_ALGORITHM_COUNT on failure */
+grpc_compression_algorithm grpc_compression_algorithm_from_mdstr(
+    grpc_mdstr *str);
+
+#endif /* GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H */
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index aa435d44d38c1ecb43ab898e512ddabf2fcca137..de0afb93b3093a1429b5710fbe8a1bcb2b1b5088 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -43,6 +43,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/channel/channel_stack.h"
+#include "src/core/compression/algorithm_metadata.h"
 #include "src/core/iomgr/timer.h"
 #include "src/core/profiling/timers.h"
 #include "src/core/support/string.h"
@@ -50,6 +51,7 @@
 #include "src/core/surface/call.h"
 #include "src/core/surface/channel.h"
 #include "src/core/surface/completion_queue.h"
+#include "src/core/transport/static_metadata.h"
 
 /** The maximum number of concurrent batches possible.
     Based upon the maximum number of individually queueable ops in the batch
@@ -271,6 +273,7 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
   /* initial refcount dropped by grpc_call_destroy */
   grpc_call_stack_init(&exec_ctx, channel_stack, 1, destroy_call, call,
                        call->context, server_transport_data,
+                       grpc_channel_get_metadata_context(channel),
                        CALL_STACK_FROM_CALL(call));
   if (cq != NULL) {
     GRPC_CQ_INTERNAL_REF(cq, "bind");
@@ -788,8 +791,12 @@ static void destroy_status(void *ignored) {}
 
 static gpr_uint32 decode_status(grpc_mdelem *md) {
   gpr_uint32 status;
-  void *user_data = grpc_mdelem_get_user_data(md, destroy_status);
-  if (user_data) {
+  void *user_data;
+  if (md == GRPC_MDELEM_GRPC_STATUS_0) return 0;
+  if (md == GRPC_MDELEM_GRPC_STATUS_1) return 1;
+  if (md == GRPC_MDELEM_GRPC_STATUS_2) return 2;
+  user_data = grpc_mdelem_get_user_data(md, destroy_status);
+  if (user_data != NULL) {
     status = ((gpr_uint32)(gpr_intptr)user_data) - STATUS_OFFSET;
   } else {
     if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value),
@@ -803,38 +810,23 @@ static gpr_uint32 decode_status(grpc_mdelem *md) {
   return status;
 }
 
-/* just as for status above, we need to offset: metadata userdata can't hold a
- * zero (null), which in this case is used to signal no compression */
-#define COMPRESS_OFFSET 1
-static void destroy_compression(void *ignored) {}
-
 static gpr_uint32 decode_compression(grpc_mdelem *md) {
-  grpc_compression_algorithm algorithm;
-  void *user_data = grpc_mdelem_get_user_data(md, destroy_compression);
-  if (user_data) {
-    algorithm =
-        ((grpc_compression_algorithm)(gpr_intptr)user_data) - COMPRESS_OFFSET;
-  } else {
+  grpc_compression_algorithm algorithm =
+      grpc_compression_algorithm_from_mdstr(md->value);
+  if (algorithm == GRPC_COMPRESS_ALGORITHMS_COUNT) {
     const char *md_c_str = grpc_mdstr_as_c_string(md->value);
-    if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str),
-                                          &algorithm)) {
-      gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'", md_c_str);
-      assert(0);
-    }
-    grpc_mdelem_set_user_data(
-        md, destroy_compression,
-        (void *)(gpr_intptr)(algorithm + COMPRESS_OFFSET));
+    gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'", md_c_str);
   }
   return algorithm;
 }
 
 static grpc_mdelem *recv_common_filter(grpc_call *call, grpc_mdelem *elem) {
-  if (elem->key == grpc_channel_get_status_string(call->channel)) {
+  if (elem->key == GRPC_MDSTR_GRPC_STATUS) {
     GPR_TIMER_BEGIN("status", 0);
     set_status_code(call, STATUS_FROM_WIRE, decode_status(elem));
     GPR_TIMER_END("status", 0);
     return NULL;
-  } else if (elem->key == grpc_channel_get_message_string(call->channel)) {
+  } else if (elem->key == GRPC_MDSTR_GRPC_MESSAGE) {
     GPR_TIMER_BEGIN("status-details", 0);
     set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(elem->value));
     GPR_TIMER_END("status-details", 0);
@@ -867,14 +859,12 @@ static grpc_mdelem *recv_initial_filter(void *callp, grpc_mdelem *elem) {
   elem = recv_common_filter(call, elem);
   if (elem == NULL) {
     return NULL;
-  } else if (elem->key ==
-             grpc_channel_get_compression_algorithm_string(call->channel)) {
+  } else if (elem->key == GRPC_MDSTR_GRPC_ENCODING) {
     GPR_TIMER_BEGIN("compression_algorithm", 0);
     set_compression_algorithm(call, decode_compression(elem));
     GPR_TIMER_END("compression_algorithm", 0);
     return NULL;
-  } else if (elem->key == grpc_channel_get_encodings_accepted_by_peer_string(
-                              call->channel)) {
+  } else if (elem->key == GRPC_MDSTR_GRPC_ACCEPT_ENCODING) {
     GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0);
     set_encodings_accepted_by_peer(call, elem);
     GPR_TIMER_END("encodings_accepted_by_peer", 0);
@@ -1240,8 +1230,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
             call->channel, op->data.send_status_from_server.status);
         if (op->data.send_status_from_server.status_details != NULL) {
           call->send_extra_metadata[1].md = grpc_mdelem_from_metadata_strings(
-              call->metadata_context,
-              GRPC_MDSTR_REF(grpc_channel_get_message_string(call->channel)),
+              call->metadata_context, GRPC_MDSTR_GRPC_MESSAGE,
               grpc_mdstr_from_string(
                   call->metadata_context,
                   op->data.send_status_from_server.status_details));
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index a9a5f828f27eca8e34b26fbbc45871abac554eb2..0ca877a394afb96c1f1da76289e0bae1a9e67a0b 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -46,6 +46,7 @@
 #include "src/core/surface/api_trace.h"
 #include "src/core/surface/call.h"
 #include "src/core/surface/init.h"
+#include "src/core/transport/static_metadata.h"
 
 /** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS.
  *  Avoids needing to take a metadata context lock for sending status
@@ -65,16 +66,7 @@ struct grpc_channel {
   gpr_refcount refs;
   gpr_uint32 max_message_length;
   grpc_mdctx *metadata_context;
-  /** mdstr for the grpc-status key */
-  grpc_mdstr *grpc_status_string;
-  grpc_mdstr *grpc_compression_algorithm_string;
-  grpc_mdstr *grpc_encodings_accepted_by_peer_string;
-  grpc_mdstr *grpc_message_string;
-  grpc_mdstr *path_string;
-  grpc_mdstr *authority_string;
   grpc_mdelem *default_authority;
-  /** mdelem for grpc-status: 0 thru grpc-status: 2 */
-  grpc_mdelem *grpc_status_elem[NUM_CACHED_STATUS_ELEMS];
 
   gpr_mu registered_call_mu;
   registered_call *registered_calls;
@@ -90,6 +82,55 @@ struct grpc_channel {
 /* the protobuf library will (by default) start warning at 100megs */
 #define DEFAULT_MAX_MESSAGE_LENGTH (100 * 1024 * 1024)
 
+static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
+                                        const grpc_channel_args *args) {
+  gpr_strvec v;
+  size_t i;
+  int is_first = 1;
+  char *tmp;
+  grpc_mdstr *result;
+
+  gpr_strvec_init(&v);
+
+  for (i = 0; args && i < args->num_args; i++) {
+    if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
+      if (args->args[i].type != GRPC_ARG_STRING) {
+        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
+                GRPC_ARG_PRIMARY_USER_AGENT_STRING);
+      } else {
+        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
+        is_first = 0;
+        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
+      }
+    }
+  }
+
+  gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
+               grpc_version_string(), GPR_PLATFORM_STRING);
+  is_first = 0;
+  gpr_strvec_add(&v, tmp);
+
+  for (i = 0; args && i < args->num_args; i++) {
+    if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
+      if (args->args[i].type != GRPC_ARG_STRING) {
+        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
+                GRPC_ARG_SECONDARY_USER_AGENT_STRING);
+      } else {
+        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
+        is_first = 0;
+        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
+      }
+    }
+  }
+
+  tmp = gpr_strvec_flatten(&v, NULL);
+  gpr_strvec_destroy(&v);
+  result = grpc_mdstr_from_string(mdctx, tmp);
+  gpr_free(tmp);
+
+  return result;
+}
+
 grpc_channel *grpc_channel_create_from_filters(
     grpc_exec_ctx *exec_ctx, const char *target,
     const grpc_channel_filter **filters, size_t num_filters,
@@ -105,24 +146,16 @@ grpc_channel *grpc_channel_create_from_filters(
   /* decremented by grpc_channel_destroy */
   gpr_ref_init(&channel->refs, 1);
   channel->metadata_context = mdctx;
-  channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status");
-  channel->grpc_compression_algorithm_string =
-      grpc_mdstr_from_string(mdctx, "grpc-encoding");
-  channel->grpc_encodings_accepted_by_peer_string =
-      grpc_mdstr_from_string(mdctx, "grpc-accept-encoding");
-  channel->grpc_message_string = grpc_mdstr_from_string(mdctx, "grpc-message");
-  for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
-    char buf[GPR_LTOA_MIN_BUFSIZE];
-    gpr_ltoa((long)i, buf);
-    channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings(
-        mdctx, GRPC_MDSTR_REF(channel->grpc_status_string),
-        grpc_mdstr_from_string(mdctx, buf));
-  }
-  channel->path_string = grpc_mdstr_from_string(mdctx, ":path");
-  channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority");
   gpr_mu_init(&channel->registered_call_mu);
   channel->registered_calls = NULL;
 
+  if (is_client) {
+    grpc_mdctx_set_mdelem_cache(
+        mdctx, GRPC_MDELEM_CACHED_USER_AGENT,
+        grpc_mdelem_from_metadata_strings(mdctx, GRPC_MDSTR_USER_AGENT,
+                                          user_agent_from_args(mdctx, args)));
+  }
+
   channel->max_message_length = DEFAULT_MAX_MESSAGE_LENGTH;
   if (args) {
     for (i = 0; i < args->num_args; i++) {
@@ -178,7 +211,6 @@ grpc_channel *grpc_channel_create_from_filters(
   }
 
   grpc_channel_stack_init(exec_ctx, filters, num_filters, channel, args,
-                          channel->metadata_context,
                           CHANNEL_STACK_FROM_CHANNEL(channel));
 
   return channel;
@@ -228,11 +260,10 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
   return grpc_channel_create_call_internal(
       channel, parent_call, propagation_mask, cq,
       grpc_mdelem_from_metadata_strings(
-          channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
+          channel->metadata_context, GRPC_MDSTR_PATH,
           grpc_mdstr_from_string(channel->metadata_context, method)),
       host ? grpc_mdelem_from_metadata_strings(
-                 channel->metadata_context,
-                 GRPC_MDSTR_REF(channel->authority_string),
+                 channel->metadata_context, GRPC_MDSTR_AUTHORITY,
                  grpc_mdstr_from_string(channel->metadata_context, host))
            : NULL,
       deadline);
@@ -246,12 +277,11 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method,
       4, (channel, method, host, reserved));
   GPR_ASSERT(!reserved);
   rc->path = grpc_mdelem_from_metadata_strings(
-      channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
+      channel->metadata_context, GRPC_MDSTR_PATH,
       grpc_mdstr_from_string(channel->metadata_context, method));
   rc->authority =
       host ? grpc_mdelem_from_metadata_strings(
-                 channel->metadata_context,
-                 GRPC_MDSTR_REF(channel->authority_string),
+                 channel->metadata_context, GRPC_MDSTR_AUTHORITY,
                  grpc_mdstr_from_string(channel->metadata_context, host))
            : NULL;
   gpr_mu_lock(&channel->registered_call_mu);
@@ -293,17 +323,7 @@ void grpc_channel_internal_ref(grpc_channel *c) {
 }
 
 static void destroy_channel(grpc_exec_ctx *exec_ctx, grpc_channel *channel) {
-  size_t i;
   grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel));
-  for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
-    GRPC_MDELEM_UNREF(channel->grpc_status_elem[i]);
-  }
-  GRPC_MDSTR_UNREF(channel->grpc_status_string);
-  GRPC_MDSTR_UNREF(channel->grpc_compression_algorithm_string);
-  GRPC_MDSTR_UNREF(channel->grpc_encodings_accepted_by_peer_string);
-  GRPC_MDSTR_UNREF(channel->grpc_message_string);
-  GRPC_MDSTR_UNREF(channel->path_string);
-  GRPC_MDSTR_UNREF(channel->authority_string);
   while (channel->registered_calls) {
     registered_call *rc = channel->registered_calls;
     channel->registered_calls = rc->next;
@@ -316,6 +336,7 @@ static void destroy_channel(grpc_exec_ctx *exec_ctx, grpc_channel *channel) {
   if (channel->default_authority != NULL) {
     GRPC_MDELEM_UNREF(channel->default_authority);
   }
+  grpc_mdctx_drop_caches(channel->metadata_context);
   grpc_mdctx_unref(channel->metadata_context);
   gpr_mu_destroy(&channel->registered_call_mu);
   gpr_free(channel->target);
@@ -359,34 +380,20 @@ grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel) {
   return channel->metadata_context;
 }
 
-grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel) {
-  return channel->grpc_status_string;
-}
-
-grpc_mdstr *grpc_channel_get_compression_algorithm_string(
-    grpc_channel *channel) {
-  return channel->grpc_compression_algorithm_string;
-}
-
-grpc_mdstr *grpc_channel_get_encodings_accepted_by_peer_string(
-    grpc_channel *channel) {
-  return channel->grpc_encodings_accepted_by_peer_string;
-}
-
 grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
-  if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) {
-    return GRPC_MDELEM_REF(channel->grpc_status_elem[i]);
-  } else {
-    char tmp[GPR_LTOA_MIN_BUFSIZE];
-    gpr_ltoa(i, tmp);
-    return grpc_mdelem_from_metadata_strings(
-        channel->metadata_context, GRPC_MDSTR_REF(channel->grpc_status_string),
-        grpc_mdstr_from_string(channel->metadata_context, tmp));
+  char tmp[GPR_LTOA_MIN_BUFSIZE];
+  switch (i) {
+    case 0:
+      return GRPC_MDELEM_GRPC_STATUS_0;
+    case 1:
+      return GRPC_MDELEM_GRPC_STATUS_1;
+    case 2:
+      return GRPC_MDELEM_GRPC_STATUS_2;
   }
-}
-
-grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel) {
-  return channel->grpc_message_string;
+  gpr_ltoa(i, tmp);
+  return grpc_mdelem_from_metadata_strings(
+      channel->metadata_context, GRPC_MDSTR_GRPC_STATUS,
+      grpc_mdstr_from_string(channel->metadata_context, tmp));
 }
 
 gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel) {
diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h
index e5030d52d2f53254cbabab0e1079a2d0dc4d0892..8bafce4216a0ef4809c4e06530d83b8092e141f1 100644
--- a/src/core/surface/channel.h
+++ b/src/core/surface/channel.h
@@ -54,12 +54,6 @@ grpc_mdctx *grpc_channel_get_metadata_context(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);
-grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel);
-grpc_mdstr *grpc_channel_get_compression_algorithm_string(
-    grpc_channel *channel);
-grpc_mdstr *grpc_channel_get_encodings_accepted_by_peer_string(
-    grpc_channel *channel);
-grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel);
 gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel);
 
 #ifdef GRPC_CHANNEL_REF_COUNT_DEBUG
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index fc458a603d3acd81ca57c42054fd1e2efad60a12..96ad012addb89d5d9ab1a2e1b749bec029496838 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -46,10 +46,10 @@
 typedef struct {
   grpc_linked_mdelem status;
   grpc_linked_mdelem details;
+  grpc_mdctx *mdctx;
 } call_data;
 
 typedef struct {
-  grpc_mdctx *mdctx;
   grpc_channel *master;
   grpc_status_code error_code;
   const char *error_message;
@@ -60,8 +60,8 @@ static void fill_metadata(grpc_call_element *elem, grpc_metadata_batch *mdb) {
   channel_data *chand = elem->channel_data;
   char tmp[GPR_LTOA_MIN_BUFSIZE];
   gpr_ltoa(chand->error_code, tmp);
-  calld->status.md = grpc_mdelem_from_strings(chand->mdctx, "grpc-status", tmp);
-  calld->details.md = grpc_mdelem_from_strings(chand->mdctx, "grpc-message",
+  calld->status.md = grpc_mdelem_from_strings(calld->mdctx, "grpc-status", tmp);
+  calld->details.md = grpc_mdelem_from_strings(calld->mdctx, "grpc-message",
                                                chand->error_message);
   calld->status.prev = calld->details.next = NULL;
   calld->status.next = &calld->details;
@@ -104,7 +104,10 @@ static void lame_start_transport_op(grpc_exec_ctx *exec_ctx,
 }
 
 static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {}
+                           grpc_call_element_args *args) {
+  call_data *calld = elem->call_data;
+  calld->mdctx = args->metadata_context;
+}
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
                               grpc_call_element *elem) {}
@@ -115,7 +118,6 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(args->is_first);
   GPR_ASSERT(args->is_last);
-  chand->mdctx = args->metadata_context;
   chand->master = args->master;
 }
 
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index e9f380083f8a3e110503fda0e3a22ffb9d9722f8..d33c42187702cb4a4d9b79a2b1b262467115ad45 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -54,6 +54,7 @@
 #include "src/core/surface/completion_queue.h"
 #include "src/core/surface/init.h"
 #include "src/core/transport/metadata.h"
+#include "src/core/transport/static_metadata.h"
 
 typedef struct listener {
   void *arg;
@@ -108,8 +109,6 @@ struct channel_data {
   grpc_server *server;
   grpc_connectivity_state connectivity_state;
   grpc_channel *channel;
-  grpc_mdstr *path_key;
-  grpc_mdstr *authority_key;
   /* linked list of all channels on a server */
   channel_data *next;
   channel_data *prev;
@@ -558,12 +557,11 @@ static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
 
 static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
-  channel_data *chand = elem->channel_data;
   call_data *calld = elem->call_data;
-  if (md->key == chand->path_key) {
+  if (md->key == GRPC_MDSTR_PATH) {
     calld->path = GRPC_MDSTR_REF(md->value);
     return NULL;
-  } else if (md->key == chand->authority_key) {
+  } else if (md->key == GRPC_MDSTR_AUTHORITY) {
     calld->host = GRPC_MDSTR_REF(md->value);
     return NULL;
   }
@@ -718,9 +716,6 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(!args->is_last);
   chand->server = NULL;
   chand->channel = NULL;
-  chand->path_key = grpc_mdstr_from_string(args->metadata_context, ":path");
-  chand->authority_key =
-      grpc_mdstr_from_string(args->metadata_context, ":authority");
   chand->next = chand->prev = chand;
   chand->registered_methods = NULL;
   chand->connectivity_state = GRPC_CHANNEL_IDLE;
@@ -750,8 +745,6 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
     chand->next = chand->prev = chand;
     maybe_finish_shutdown(exec_ctx, chand->server);
     gpr_mu_unlock(&chand->server->mu_global);
-    GRPC_MDSTR_UNREF(chand->path_key);
-    GRPC_MDSTR_UNREF(chand->authority_key);
     server_unref(exec_ctx, chand->server);
   }
 }
diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c
index fa941a73c6d76c9c46d42468f95f6ebc27d715a1..cbec63c8686af8c90b1d0b928e7f614d4baeeff1 100644
--- a/src/core/transport/metadata.c
+++ b/src/core/transport/metadata.c
@@ -37,12 +37,15 @@
 #include <stddef.h>
 #include <string.h>
 
+#include <grpc/compression.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
 #include "src/core/profiling/timers.h"
 #include "src/core/support/murmur_hash.h"
+#include "src/core/support/string.h"
 #include "src/core/transport/chttp2/bin_encoder.h"
 #include "src/core/transport/static_metadata.h"
 
@@ -157,6 +160,11 @@ struct grpc_mdctx {
   size_t mdtab_count;
   size_t mdtab_free;
   size_t mdtab_capacity;
+
+  /* cache slots */
+  gpr_atm cache_slots[GRPC_MDELEM_CACHE_SLOT_COUNT];
+  /* compression algorithm mdelems: one per algorithm bitmask */
+  gpr_atm compression_algorithm_mdelem[1 << GRPC_COMPRESS_ALGORITHMS_COUNT];
 };
 
 static void internal_string_ref(internal_string *s DEBUG_ARGS);
@@ -175,8 +183,10 @@ void grpc_mdctx_global_init(void) {
   }
   for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
     grpc_mdelem *elem = &grpc_static_mdelem_table[i];
-    grpc_mdstr *key = &grpc_static_mdstr_table[2 * i + 0];
-    grpc_mdstr *value = &grpc_static_mdstr_table[2 * i + 1];
+    grpc_mdstr *key =
+        &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 0]];
+    grpc_mdstr *value =
+        &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 1]];
     *(grpc_mdstr **)&elem->key = key;
     *(grpc_mdstr **)&elem->value = value;
   }
@@ -304,6 +314,42 @@ grpc_mdctx *grpc_mdctx_create(void) {
       (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
 }
 
+static void drop_cached_elem(gpr_atm *slot) {
+  gpr_atm value = gpr_atm_no_barrier_load(slot);
+  gpr_atm_rel_store(slot, 0);
+  GRPC_MDELEM_UNREF((grpc_mdelem *)value);
+}
+
+void grpc_mdctx_drop_caches(grpc_mdctx *ctx) {
+  size_t i;
+  for (i = 0; i < GRPC_MDELEM_CACHE_SLOT_COUNT; i++) {
+    drop_cached_elem(&ctx->cache_slots[i]);
+  }
+  for (i = 0; i < GPR_ARRAY_SIZE(ctx->compression_algorithm_mdelem); i++) {
+    drop_cached_elem(&ctx->compression_algorithm_mdelem[i]);
+  }
+}
+
+static void set_cache(gpr_atm *slot, grpc_mdelem *elem) {
+  if (!gpr_atm_rel_cas(slot, 0, (gpr_atm)elem)) {
+    GRPC_MDELEM_UNREF(elem);
+  }
+}
+
+void grpc_mdctx_set_mdelem_cache(grpc_mdctx *ctx, grpc_mdelem_cache_slot slot,
+                                 grpc_mdelem *elem) {
+  set_cache(&ctx->cache_slots[slot], elem);
+}
+
+static grpc_mdelem *get_cache(gpr_atm *slot) {
+  return (grpc_mdelem *)gpr_atm_acq_load(slot);
+}
+
+grpc_mdelem *grpc_mdelem_from_cache(grpc_mdctx *ctx,
+                                    grpc_mdelem_cache_slot slot) {
+  return get_cache(&ctx->cache_slots[slot]);
+}
+
 static void discard_metadata(grpc_mdctx *ctx) {
   size_t i;
   internal_metadata *next, *cur;
@@ -780,6 +826,7 @@ void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
 void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
                                void *user_data) {
   internal_metadata *im = (internal_metadata *)md;
+  GPR_ASSERT(!is_mdelem_static(md));
   GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
   gpr_mu_lock(&im->mu_user_data);
   if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
@@ -838,6 +885,52 @@ int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) {
   return conforms_to(s, legal_header_bits);
 }
 
+static grpc_mdelem *make_accept_encoding_mdelem_for_compression_algorithms(
+    grpc_mdctx *mdctx, gpr_uint32 algorithms) {
+  gpr_strvec sv;
+  int i;
+  char *str;
+  grpc_mdelem *out;
+
+  gpr_strvec_init(&sv);
+  for (i = 0; algorithms != 0; i++, algorithms >>= 1) {
+    if (algorithms & 1) {
+      char *name;
+      GPR_ASSERT(grpc_compression_algorithm_name((grpc_compression_algorithm)i,
+                                                 &name));
+      if (sv.count) {
+        gpr_strvec_add(&sv, gpr_strdup(","));
+      }
+      gpr_strvec_add(&sv, gpr_strdup(name));
+    }
+  }
+  str = gpr_strvec_flatten(&sv, NULL);
+  out =
+      grpc_mdelem_from_metadata_strings(mdctx, GRPC_MDSTR_GRPC_ACCEPT_ENCODING,
+                                        grpc_mdstr_from_string(mdctx, str));
+  gpr_strvec_destroy(&sv);
+  gpr_free(str);
+  return out;
+}
+
+grpc_mdelem *grpc_accept_encoding_mdelem_from_compression_algorithms(
+    grpc_mdctx *ctx, gpr_uint32 algorithms) {
+  grpc_mdelem *ret;
+  gpr_atm *slot;
+  GPR_ASSERT(algorithms < GPR_ARRAY_SIZE(ctx->compression_algorithm_mdelem));
+
+  slot = &ctx->compression_algorithm_mdelem[algorithms];
+  ret = get_cache(slot);
+  if (ret == NULL) {
+    set_cache(slot, make_accept_encoding_mdelem_for_compression_algorithms(
+                        ctx, algorithms));
+    ret = get_cache(slot);
+    GPR_ASSERT(ret != NULL);
+  }
+
+  return ret;
+}
+
 int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s) {
   /* TODO(ctiller): consider caching this */
   return grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(s->slice),
diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h
index 7d70883fa48ad260a52cccb29d456cd24f9185e2..0f12a7392c7d9d3335751f02a6b21bda6c4b392b 100644
--- a/src/core/transport/metadata.h
+++ b/src/core/transport/metadata.h
@@ -93,6 +93,8 @@ grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed);
 void grpc_mdctx_ref(grpc_mdctx *mdctx);
 void grpc_mdctx_unref(grpc_mdctx *mdctx);
 
+void grpc_mdctx_drop_caches(grpc_mdctx *mdctx);
+
 /* Test only accessors to internal state - only for testing this code - do not
    rely on it outside of metadata_test.c */
 size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *mdctx);
@@ -161,6 +163,25 @@ int grpc_mdstr_is_legal_header(grpc_mdstr *s);
 int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s);
 int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s);
 
+/* Gross layering hack (that we seem to need):
+ * metadata context keeps a cache of algorithm bitset to
+ * 'accept-encoding: algorithm1,algorithm2' in order to accelerate sending
+ * compression metadata */
+grpc_mdelem *grpc_accept_encoding_mdelem_from_compression_algorithms(
+    grpc_mdctx *ctx, gpr_uint32 algorithm_mask);
+
+/* Cache-slots
+ * A metadata context can cache (on behalf of its owner) some small set of
+ * metadata elements. */
+typedef enum {
+  GRPC_MDELEM_CACHED_USER_AGENT = 0,
+  GRPC_MDELEM_CACHE_SLOT_COUNT
+} grpc_mdelem_cache_slot;
+void grpc_mdctx_set_mdelem_cache(grpc_mdctx *ctx, grpc_mdelem_cache_slot slot,
+                                 grpc_mdelem *elem);
+grpc_mdelem *grpc_mdelem_from_cache(grpc_mdctx *ctx,
+                                    grpc_mdelem_cache_slot slot);
+
 #define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
 
 void grpc_mdctx_global_init(void);
diff --git a/src/core/transport/static_metadata.c b/src/core/transport/static_metadata.c
index f359ce5c209594a2a4708133270cde84e0115b26..fb7c5006e11ced21d6d95279a0d80e4651a84990 100644
--- a/src/core/transport/static_metadata.c
+++ b/src/core/transport/static_metadata.c
@@ -47,18 +47,21 @@ grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 
 grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 
-const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT *
-                                                  2] = {
-    9,  29, 8,  29, 10, 29, 10, 42, 11, 29, 12, 29, 13, 29, 14, 29, 15, 29, 16,
-    29, 17, 29, 18, 29, 19, 29, 20, 29, 21, 29, 22, 29, 23, 29, 24, 29, 25, 29,
-    26, 29, 27, 29, 30, 29, 31, 29, 32, 29, 33, 29, 39, 0,  43, 29, 47, 29, 48,
-    29, 49, 29, 50, 29, 51, 29, 52, 29, 53, 29, 54, 29, 55, 29, 56, 34, 56, 58,
-    57, 68, 57, 69, 59, 29, 60, 29, 61, 29, 62, 29, 63, 29, 64, 29, 65, 35, 65,
-    44, 65, 45, 66, 29, 67, 29, 70, 1,  70, 2,  70, 3,  70, 4,  70, 5,  70, 6,
-    70, 7,  71, 29, 72, 73, 74, 29, 75, 29, 76, 29, 77, 29, 78, 29};
+const gpr_uint8
+    grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] = {
+        11, 32, 10, 32, 12, 32, 12, 46, 13, 32, 14, 32, 15, 32, 16, 32, 17, 32,
+        19, 32, 20, 32, 21, 32, 22, 32, 23, 32, 24, 32, 25, 32, 26, 32, 27, 32,
+        28, 18, 28, 32, 29, 32, 30, 32, 33, 32, 34, 32, 35, 32, 36, 32, 40, 31,
+        40, 45, 40, 50, 43, 0,  43, 1,  43, 2,  47, 32, 51, 32, 52, 32, 53, 32,
+        54, 32, 55, 32, 56, 32, 57, 32, 58, 32, 59, 32, 60, 37, 60, 62, 61, 72,
+        61, 73, 63, 32, 64, 32, 65, 32, 66, 32, 67, 32, 68, 32, 69, 38, 69, 48,
+        69, 49, 70, 32, 71, 32, 74, 3,  74, 4,  74, 5,  74, 6,  74, 7,  74, 8,
+        74, 9,  75, 32, 76, 77, 78, 32, 79, 32, 80, 32, 81, 32, 82, 32};
 
 const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "0",
+    "1",
+    "2",
     "200",
     "204",
     "206",
@@ -74,6 +77,7 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "access-control-allow-origin",
     "age",
     "allow",
+    "application/grpc",
     ":authority",
     "authorization",
     "cache-control",
@@ -96,6 +100,7 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "grpc",
     "grpc-accept-encoding",
     "grpc-encoding",
+    "grpc-internal-encoding-request",
     "grpc-message",
     "grpc-status",
     "grpc-timeout",
diff --git a/src/core/transport/static_metadata.h b/src/core/transport/static_metadata.h
index 4a7bee1da8b8e1116876b16aea3cf740b3db61c3..671801636b8f2c22417610f264b4bd0035238582 100644
--- a/src/core/transport/static_metadata.h
+++ b/src/core/transport/static_metadata.h
@@ -46,168 +46,176 @@
 
 #include "src/core/transport/metadata.h"
 
-#define GRPC_STATIC_MDSTR_COUNT 79
+#define GRPC_STATIC_MDSTR_COUNT 83
 extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 /* "0" */
 #define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0])
+/* "1" */
+#define GRPC_MDSTR_1 (&grpc_static_mdstr_table[1])
+/* "2" */
+#define GRPC_MDSTR_2 (&grpc_static_mdstr_table[2])
 /* "200" */
-#define GRPC_MDSTR_200 (&grpc_static_mdstr_table[1])
+#define GRPC_MDSTR_200 (&grpc_static_mdstr_table[3])
 /* "204" */
-#define GRPC_MDSTR_204 (&grpc_static_mdstr_table[2])
+#define GRPC_MDSTR_204 (&grpc_static_mdstr_table[4])
 /* "206" */
-#define GRPC_MDSTR_206 (&grpc_static_mdstr_table[3])
+#define GRPC_MDSTR_206 (&grpc_static_mdstr_table[5])
 /* "304" */
-#define GRPC_MDSTR_304 (&grpc_static_mdstr_table[4])
+#define GRPC_MDSTR_304 (&grpc_static_mdstr_table[6])
 /* "400" */
-#define GRPC_MDSTR_400 (&grpc_static_mdstr_table[5])
+#define GRPC_MDSTR_400 (&grpc_static_mdstr_table[7])
 /* "404" */
-#define GRPC_MDSTR_404 (&grpc_static_mdstr_table[6])
+#define GRPC_MDSTR_404 (&grpc_static_mdstr_table[8])
 /* "500" */
-#define GRPC_MDSTR_500 (&grpc_static_mdstr_table[7])
+#define GRPC_MDSTR_500 (&grpc_static_mdstr_table[9])
 /* "accept" */
-#define GRPC_MDSTR_ACCEPT (&grpc_static_mdstr_table[8])
+#define GRPC_MDSTR_ACCEPT (&grpc_static_mdstr_table[10])
 /* "accept-charset" */
-#define GRPC_MDSTR_ACCEPT_CHARSET (&grpc_static_mdstr_table[9])
+#define GRPC_MDSTR_ACCEPT_CHARSET (&grpc_static_mdstr_table[11])
 /* "accept-encoding" */
-#define GRPC_MDSTR_ACCEPT_ENCODING (&grpc_static_mdstr_table[10])
+#define GRPC_MDSTR_ACCEPT_ENCODING (&grpc_static_mdstr_table[12])
 /* "accept-language" */
-#define GRPC_MDSTR_ACCEPT_LANGUAGE (&grpc_static_mdstr_table[11])
+#define GRPC_MDSTR_ACCEPT_LANGUAGE (&grpc_static_mdstr_table[13])
 /* "accept-ranges" */
-#define GRPC_MDSTR_ACCEPT_RANGES (&grpc_static_mdstr_table[12])
+#define GRPC_MDSTR_ACCEPT_RANGES (&grpc_static_mdstr_table[14])
 /* "access-control-allow-origin" */
-#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (&grpc_static_mdstr_table[13])
+#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (&grpc_static_mdstr_table[15])
 /* "age" */
-#define GRPC_MDSTR_AGE (&grpc_static_mdstr_table[14])
+#define GRPC_MDSTR_AGE (&grpc_static_mdstr_table[16])
 /* "allow" */
-#define GRPC_MDSTR_ALLOW (&grpc_static_mdstr_table[15])
+#define GRPC_MDSTR_ALLOW (&grpc_static_mdstr_table[17])
+/* "application/grpc" */
+#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (&grpc_static_mdstr_table[18])
 /* ":authority" */
-#define GRPC_MDSTR_AUTHORITY (&grpc_static_mdstr_table[16])
+#define GRPC_MDSTR_AUTHORITY (&grpc_static_mdstr_table[19])
 /* "authorization" */
-#define GRPC_MDSTR_AUTHORIZATION (&grpc_static_mdstr_table[17])
+#define GRPC_MDSTR_AUTHORIZATION (&grpc_static_mdstr_table[20])
 /* "cache-control" */
-#define GRPC_MDSTR_CACHE_CONTROL (&grpc_static_mdstr_table[18])
+#define GRPC_MDSTR_CACHE_CONTROL (&grpc_static_mdstr_table[21])
 /* "content-disposition" */
-#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[19])
+#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[22])
 /* "content-encoding" */
-#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[20])
+#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[23])
 /* "content-language" */
-#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[21])
+#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[24])
 /* "content-length" */
-#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[22])
+#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[25])
 /* "content-location" */
-#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[23])
+#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[26])
 /* "content-range" */
-#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[24])
+#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[27])
 /* "content-type" */
-#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[25])
+#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[28])
 /* "cookie" */
-#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[26])
+#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[29])
 /* "date" */
-#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[27])
+#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[30])
 /* "deflate" */
-#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[28])
+#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[31])
 /* "" */
-#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[29])
+#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[32])
 /* "etag" */
-#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[30])
+#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[33])
 /* "expect" */
-#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[31])
+#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[34])
 /* "expires" */
-#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[32])
+#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[35])
 /* "from" */
-#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[33])
+#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[36])
 /* "GET" */
-#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[34])
+#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[37])
 /* "grpc" */
-#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[35])
+#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[38])
 /* "grpc-accept-encoding" */
-#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[36])
+#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[39])
 /* "grpc-encoding" */
-#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[37])
+#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[40])
+/* "grpc-internal-encoding-request" */
+#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[41])
 /* "grpc-message" */
-#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[38])
+#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[42])
 /* "grpc-status" */
-#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[39])
+#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[43])
 /* "grpc-timeout" */
-#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[40])
+#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[44])
 /* "gzip" */
-#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[41])
+#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[45])
 /* "gzip, deflate" */
-#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[42])
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[46])
 /* "host" */
-#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[43])
+#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[47])
 /* "http" */
-#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[44])
+#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[48])
 /* "https" */
-#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[45])
+#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[49])
 /* "identity" */
-#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[46])
+#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[50])
 /* "if-match" */
-#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[47])
+#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[51])
 /* "if-modified-since" */
-#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[48])
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[52])
 /* "if-none-match" */
-#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[49])
+#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[53])
 /* "if-range" */
-#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[50])
+#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[54])
 /* "if-unmodified-since" */
-#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[51])
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[55])
 /* "last-modified" */
-#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[52])
+#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[56])
 /* "link" */
-#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[53])
+#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[57])
 /* "location" */
-#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[54])
+#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[58])
 /* "max-forwards" */
-#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[55])
+#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[59])
 /* ":method" */
-#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[56])
+#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[60])
 /* ":path" */
-#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[57])
+#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[61])
 /* "POST" */
-#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[58])
+#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[62])
 /* "proxy-authenticate" */
-#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[59])
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[63])
 /* "proxy-authorization" */
-#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[60])
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[64])
 /* "range" */
-#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[61])
+#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[65])
 /* "referer" */
-#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[62])
+#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[66])
 /* "refresh" */
-#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[63])
+#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[67])
 /* "retry-after" */
-#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[64])
+#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[68])
 /* ":scheme" */
-#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[65])
+#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[69])
 /* "server" */
-#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[66])
+#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[70])
 /* "set-cookie" */
-#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[67])
+#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[71])
 /* "/" */
-#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[68])
+#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[72])
 /* "/index.html" */
-#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[69])
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[73])
 /* ":status" */
-#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[70])
+#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[74])
 /* "strict-transport-security" */
-#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[71])
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[75])
 /* "te" */
-#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[72])
+#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[76])
 /* "trailers" */
-#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[73])
+#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[77])
 /* "transfer-encoding" */
-#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[74])
+#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[78])
 /* "user-agent" */
-#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[75])
+#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[79])
 /* "vary" */
-#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[76])
+#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[80])
 /* "via" */
-#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[77])
+#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[81])
 /* "www-authenticate" */
-#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[78])
+#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[82])
 
-#define GRPC_STATIC_MDELEM_COUNT 65
+#define GRPC_STATIC_MDELEM_COUNT 71
 extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 /* "accept-charset": "" */
 #define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY (&grpc_static_mdelem_table[0])
@@ -247,101 +255,114 @@ extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 #define GRPC_MDELEM_CONTENT_LOCATION_EMPTY (&grpc_static_mdelem_table[16])
 /* "content-range": "" */
 #define GRPC_MDELEM_CONTENT_RANGE_EMPTY (&grpc_static_mdelem_table[17])
+/* "content-type": "application/grpc" */
+#define GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC \
+  (&grpc_static_mdelem_table[18])
 /* "content-type": "" */
-#define GRPC_MDELEM_CONTENT_TYPE_EMPTY (&grpc_static_mdelem_table[18])
+#define GRPC_MDELEM_CONTENT_TYPE_EMPTY (&grpc_static_mdelem_table[19])
 /* "cookie": "" */
-#define GRPC_MDELEM_COOKIE_EMPTY (&grpc_static_mdelem_table[19])
+#define GRPC_MDELEM_COOKIE_EMPTY (&grpc_static_mdelem_table[20])
 /* "date": "" */
-#define GRPC_MDELEM_DATE_EMPTY (&grpc_static_mdelem_table[20])
+#define GRPC_MDELEM_DATE_EMPTY (&grpc_static_mdelem_table[21])
 /* "etag": "" */
-#define GRPC_MDELEM_ETAG_EMPTY (&grpc_static_mdelem_table[21])
+#define GRPC_MDELEM_ETAG_EMPTY (&grpc_static_mdelem_table[22])
 /* "expect": "" */
-#define GRPC_MDELEM_EXPECT_EMPTY (&grpc_static_mdelem_table[22])
+#define GRPC_MDELEM_EXPECT_EMPTY (&grpc_static_mdelem_table[23])
 /* "expires": "" */
-#define GRPC_MDELEM_EXPIRES_EMPTY (&grpc_static_mdelem_table[23])
+#define GRPC_MDELEM_EXPIRES_EMPTY (&grpc_static_mdelem_table[24])
 /* "from": "" */
-#define GRPC_MDELEM_FROM_EMPTY (&grpc_static_mdelem_table[24])
+#define GRPC_MDELEM_FROM_EMPTY (&grpc_static_mdelem_table[25])
+/* "grpc-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE (&grpc_static_mdelem_table[26])
+/* "grpc-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ENCODING_GZIP (&grpc_static_mdelem_table[27])
+/* "grpc-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY (&grpc_static_mdelem_table[28])
 /* "grpc-status": "0" */
-#define GRPC_MDELEM_GRPC_STATUS_0 (&grpc_static_mdelem_table[25])
+#define GRPC_MDELEM_GRPC_STATUS_0 (&grpc_static_mdelem_table[29])
+/* "grpc-status": "1" */
+#define GRPC_MDELEM_GRPC_STATUS_1 (&grpc_static_mdelem_table[30])
+/* "grpc-status": "2" */
+#define GRPC_MDELEM_GRPC_STATUS_2 (&grpc_static_mdelem_table[31])
 /* "host": "" */
-#define GRPC_MDELEM_HOST_EMPTY (&grpc_static_mdelem_table[26])
+#define GRPC_MDELEM_HOST_EMPTY (&grpc_static_mdelem_table[32])
 /* "if-match": "" */
-#define GRPC_MDELEM_IF_MATCH_EMPTY (&grpc_static_mdelem_table[27])
+#define GRPC_MDELEM_IF_MATCH_EMPTY (&grpc_static_mdelem_table[33])
 /* "if-modified-since": "" */
-#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[28])
+#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[34])
 /* "if-none-match": "" */
-#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY (&grpc_static_mdelem_table[29])
+#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY (&grpc_static_mdelem_table[35])
 /* "if-range": "" */
-#define GRPC_MDELEM_IF_RANGE_EMPTY (&grpc_static_mdelem_table[30])
+#define GRPC_MDELEM_IF_RANGE_EMPTY (&grpc_static_mdelem_table[36])
 /* "if-unmodified-since": "" */
-#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[31])
+#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[37])
 /* "last-modified": "" */
-#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[32])
+#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[38])
 /* "link": "" */
-#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[33])
+#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[39])
 /* "location": "" */
-#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[34])
+#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[40])
 /* "max-forwards": "" */
-#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[35])
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[41])
 /* ":method": "GET" */
-#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[36])
+#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[42])
 /* ":method": "POST" */
-#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[37])
+#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[43])
 /* ":path": "/" */
-#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[38])
+#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[44])
 /* ":path": "/index.html" */
-#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[39])
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[45])
 /* "proxy-authenticate": "" */
-#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[40])
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[46])
 /* "proxy-authorization": "" */
-#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[41])
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[47])
 /* "range": "" */
-#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[42])
+#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[48])
 /* "referer": "" */
-#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[43])
+#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[49])
 /* "refresh": "" */
-#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[44])
+#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[50])
 /* "retry-after": "" */
-#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[45])
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[51])
 /* ":scheme": "grpc" */
-#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[46])
+#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[52])
 /* ":scheme": "http" */
-#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[47])
+#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[53])
 /* ":scheme": "https" */
-#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[48])
+#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[54])
 /* "server": "" */
-#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[49])
+#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[55])
 /* "set-cookie": "" */
-#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[50])
+#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[56])
 /* ":status": "200" */
-#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[51])
+#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[57])
 /* ":status": "204" */
-#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[52])
+#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[58])
 /* ":status": "206" */
-#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[53])
+#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[59])
 /* ":status": "304" */
-#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[54])
+#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[60])
 /* ":status": "400" */
-#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[55])
+#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[61])
 /* ":status": "404" */
-#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[56])
+#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[62])
 /* ":status": "500" */
-#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[57])
+#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[63])
 /* "strict-transport-security": "" */
 #define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
-  (&grpc_static_mdelem_table[58])
+  (&grpc_static_mdelem_table[64])
 /* "te": "trailers" */
-#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[59])
+#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[65])
 /* "transfer-encoding": "" */
-#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[60])
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[66])
 /* "user-agent": "" */
-#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[61])
+#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[67])
 /* "vary": "" */
-#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[62])
+#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[68])
 /* "via": "" */
-#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[63])
+#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[69])
 /* "www-authenticate": "" */
-#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[64])
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[70])
 
 const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
 const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 08550b4934ec2307222c680c3b64c9a3b9222f93..2a05c608bb21978aa37e743852d0a078203c47bf 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -109,7 +109,7 @@ static void test_create_channel_stack(void) {
 
   channel_stack = gpr_malloc(grpc_channel_stack_size(&filters, 1));
   grpc_channel_stack_init(&exec_ctx, &filters, 1, NULL, &chan_args,
-                          metadata_context, channel_stack);
+                          channel_stack);
   GPR_ASSERT(channel_stack->count == 1);
   channel_elem = grpc_channel_stack_element(channel_stack, 0);
   channel_data = (int *)channel_elem->channel_data;
@@ -117,7 +117,7 @@ static void test_create_channel_stack(void) {
 
   call_stack = gpr_malloc(channel_stack->call_stack_size);
   grpc_call_stack_init(&exec_ctx, channel_stack, 0, NULL, NULL, NULL, NULL,
-                       call_stack);
+                       metadata_context, call_stack);
   GPR_ASSERT(call_stack->count == 1);
   call_elem = grpc_call_stack_element(call_stack, 0);
   GPR_ASSERT(call_elem->filter == channel_elem->filter);
diff --git a/test/core/end2end/fixtures/h2_uchannel.c b/test/core/end2end/fixtures/h2_uchannel.c
index d1f9d38b82b12f9383947d56ffd3673c01a5fd29..5fed175f9d5407faf3ebee60b5bb7d9c17a11ea0 100644
--- a/test/core/end2end/fixtures/h2_uchannel.c
+++ b/test/core/end2end/fixtures/h2_uchannel.c
@@ -263,7 +263,9 @@ static void chttp2_init_client_micro_fullstack(grpc_end2end_test_fixture *f,
   /* here sniffed_subchannel should be ready to use */
   GPR_ASSERT(conn_state == GRPC_CHANNEL_IDLE);
   GPR_ASSERT(ffd->sniffed_subchannel != NULL);
-  f->client = grpc_client_uchannel_create(ffd->sniffed_subchannel, client_args);
+  f->client = grpc_client_uchannel_create(
+      ffd->sniffed_subchannel, client_args,
+      grpc_channel_get_metadata_context(ffd->master_channel));
   grpc_client_uchannel_set_subchannel(f->client, ffd->sniffed_subchannel);
   gpr_log(GPR_INFO, "CHANNEL WRAPPING SUBCHANNEL: %p(%p)", f->client,
           ffd->sniffed_subchannel);
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
index 4e52b0e46688b9655abc56b2d6bcf9d7097cd304..a790b461b680a378750de4a507202ebb6c224dc2 100644
--- a/test/core/transport/chttp2/hpack_parser_test.c
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -35,6 +35,7 @@
 
 #include <stdarg.h>
 
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice.h>
diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c
index fc9c93a7742cebd3eb464968d0c65643884ee3ff..875ab3d77b4b0d3b755c38676b0c682b88948e19 100644
--- a/test/core/transport/metadata_test.c
+++ b/test/core/transport/metadata_test.c
@@ -35,11 +35,13 @@
 
 #include <stdio.h>
 
-#include "src/core/support/string.h"
-#include "src/core/transport/chttp2/bin_encoder.h"
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+
+#include "src/core/support/string.h"
+#include "src/core/transport/chttp2/bin_encoder.h"
 #include "test/core/util/test_config.h"
 
 #define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
index 0e84a73ea598d2c25a1613ccf98c9d1dd6523c82..d52e329023319c8a98bf4bd3465ea2573d4e9f89 100755
--- a/tools/codegen/core/gen_static_metadata.py
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -41,6 +41,7 @@ import sys
 
 CONFIG = [
     'grpc-timeout',
+    'grpc-internal-encoding-request',
     ':path',
     'grpc-encoding',
     'grpc-accept-encoding',
@@ -54,7 +55,13 @@ CONFIG = [
     'identity',
     '',
     ('grpc-status', '0'),
+    ('grpc-status', '1'),
+    ('grpc-status', '2'),
+    ('grpc-encoding', 'identity'),
+    ('grpc-encoding', 'gzip'),
+    ('grpc-encoding', 'deflate'),
     ('te', 'trailers'),
+    ('content-type', 'application/grpc'),
     (':method', 'POST'),
     (':status', '200'),
     (':status', '404'),
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index fbabae4238222ebc0f7558db8c68bf267966dfa6..da65d0024e1bb8bcb75a37a17416b71e3123e0c7 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -809,6 +809,7 @@ src/core/client_config/subchannel_factory.h \
 src/core/client_config/subchannel_factory_decorators/add_channel_arg.h \
 src/core/client_config/subchannel_factory_decorators/merge_channel_args.h \
 src/core/client_config/uri_parser.h \
+src/core/compression/algorithm_metadata.h \
 src/core/compression/message_compress.h \
 src/core/debug/trace.h \
 src/core/httpcli/format_request.h \
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index aa433372632fe8ab311d421501c02acbe9f1d7d1..7fd7cff8d618beec6cf7338779bac40f787ea415 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -485,10 +485,10 @@ _CONFIGS = {
     'msan': SimpleConfig('msan', timeout_multiplier=1.5),
     'ubsan': SimpleConfig('ubsan'),
     'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
-        'ASAN_OPTIONS': 'detect_leaks=1:color=always:suppressions=tools/tsan_suppressions.txt',
+        'ASAN_OPTIONS': 'detect_leaks=1:color=always',
         'LSAN_OPTIONS': 'report_objects=1'}),
     'asan-noleaks': SimpleConfig('asan', environ={
-        'ASAN_OPTIONS': 'detect_leaks=0:color=always:suppressions=tools/tsan_suppressions.txt'}),
+        'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
     'gcov': SimpleConfig('gcov'),
     'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
     'helgrind': ValgrindConfig('dbg', 'helgrind')
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 485fe870d0a13f6f654313ebb6dee5169616111d..cd9f630adf0ddeaac0e10a6ee0de9897e52c3b37 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -14032,6 +14032,7 @@
       "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h", 
       "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h", 
       "src/core/client_config/uri_parser.h", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.h", 
       "src/core/httpcli/format_request.h", 
@@ -14207,6 +14208,7 @@
       "src/core/client_config/uri_parser.c", 
       "src/core/client_config/uri_parser.h", 
       "src/core/compression/algorithm.c", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.c", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.c", 
@@ -14547,6 +14549,7 @@
       "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h", 
       "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h", 
       "src/core/client_config/uri_parser.h", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.h", 
       "src/core/httpcli/format_request.h", 
@@ -14708,6 +14711,7 @@
       "src/core/client_config/uri_parser.c", 
       "src/core/client_config/uri_parser.h", 
       "src/core/compression/algorithm.c", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.c", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.c", 
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 2345f8569e38cdd1baf4cc3aa22963711258a5fe..1311bfb6caf2bb27987c1bd217bd215722c9c026 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -295,6 +295,7 @@
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\add_channel_arg.h" />
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\merge_channel_args.h" />
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h" />
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h" />
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h" />
     <ClInclude Include="..\..\..\src\core\debug\trace.h" />
     <ClInclude Include="..\..\..\src\core\httpcli\format_request.h" />
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 16d7d8aaed4786850d32fa8e6d567753a3e0455e..b20bd9148643cde969efe381d8f2a15758ff8ae3 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -602,6 +602,9 @@
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h">
       <Filter>src\core\client_config</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h">
+      <Filter>src\core\compression</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h">
       <Filter>src\core\compression</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index efc99fee73c49137fbdcf5b494a044ced0af47ff..dba01c85df683e1c542a77e9ae229770726e0441 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -274,6 +274,7 @@
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\add_channel_arg.h" />
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\merge_channel_args.h" />
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h" />
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h" />
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h" />
     <ClInclude Include="..\..\..\src\core\debug\trace.h" />
     <ClInclude Include="..\..\..\src\core\httpcli\format_request.h" />
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 2eb7849bec5ef5e8de48b028ce2bff6edc37b256..cf12c96f13572a5fb5a6937f736899f070512dab 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -500,6 +500,9 @@
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h">
       <Filter>src\core\client_config</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h">
+      <Filter>src\core\compression</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h">
       <Filter>src\core\compression</Filter>
     </ClInclude>