diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c
index c6a0ada0f1a4a0761f3a3d108edbbf383c9d4b87..7f7fbf420fb32c8003693e8374038482079dc66d 100644
--- a/src/core/channel/channel_stack.c
+++ b/src/core/channel/channel_stack.c
@@ -157,7 +157,6 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
                           grpc_iomgr_cb_func destroy, void *destroy_arg,
                           grpc_call_context_element *context,
                           const void *transport_server_data,
-                          grpc_mdctx *metadata_context,
                           grpc_call_stack *call_stack) {
   grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack);
   grpc_call_element_args args;
@@ -178,7 +177,6 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
     args.refcount = &call_stack->refcount;
     args.server_transport_data = transport_server_data;
     args.context = context;
-    args.metadata_context = metadata_context;
     call_elems[i].filter = channel_elems[i].filter;
     call_elems[i].channel_data = channel_elems[i].channel_data;
     call_elems[i].call_data = user_data;
diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h
index d8940675acc6ebbeafc6cdf11403257b926a43eb..1db12ead7ec3558c8821ed9391e29757341bfcff 100644
--- a/src/core/channel/channel_stack.h
+++ b/src/core/channel/channel_stack.h
@@ -60,7 +60,6 @@ typedef struct {
 
 typedef struct {
   grpc_stream_refcount *refcount;
-  grpc_mdctx *metadata_context;
   const void *server_transport_data;
   grpc_call_context_element *context;
 } grpc_call_element_args;
@@ -193,7 +192,6 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
                           grpc_iomgr_cb_func destroy, void *destroy_arg,
                           grpc_call_context_element *context,
                           const void *transport_server_data,
-                          grpc_mdctx *metadata_context,
                           grpc_call_stack *call_stack);
 /* Set a pollset for a call stack: must occur before the first op is started */
 void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index 1abcd3b9cc87ccf2a70eb5bd1d372ea1d4b630e7..020138bf15744fd93f00b502fc3b889f683aa70a 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -364,8 +364,7 @@ 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,
-                                   args->metadata_context);
+  grpc_subchannel_call_holder_init(elem->call_data, cc_pick_subchannel, elem);
 }
 
 /* Destructor for call_data */
diff --git a/src/core/channel/client_uchannel.c b/src/core/channel/client_uchannel.c
index 3276635625dc78dbf6a16b5c24ab94ed079eb234..456ffb737199fe8068f1bf052ff893a02457c6d9 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, args->metadata_context);
+                                   elem->channel_data);
 }
 
 /* Destructor for call_data */
@@ -244,8 +244,7 @@ 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_mdctx *mdctx) {
+                                          grpc_channel_args *args) {
   grpc_channel *channel = NULL;
 #define MAX_FILTERS 3
   const grpc_channel_filter *filters[MAX_FILTERS];
@@ -254,7 +253,6 @@ grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel,
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   size_t n = 0;
 
-  grpc_mdctx_ref(mdctx);
   if (grpc_channel_args_is_census_enabled(args)) {
     filters[n++] = &grpc_client_census_filter;
   }
@@ -262,8 +260,8 @@ grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel,
   filters[n++] = &grpc_client_uchannel_filter;
   GPR_ASSERT(n <= MAX_FILTERS);
 
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, n, args, 1);
 
   gpr_free(target);
   return channel;
diff --git a/src/core/channel/client_uchannel.h b/src/core/channel/client_uchannel.h
index 54fbea964c1588c2dce7c723037e354dd5c1fe7a..dfe6695ae3eec2dbbbbbf4f828a1214cf908dc66 100644
--- a/src/core/channel/client_uchannel.h
+++ b/src/core/channel/client_uchannel.h
@@ -62,8 +62,7 @@ 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_mdctx *mdctx);
+                                          grpc_channel_args *args);
 
 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 25d6e51281b864e87663f13d8ee5688872619665..fc8b425e473e1d070887b58b6002f301796d23a2 100644
--- a/src/core/channel/compress_filter.c
+++ b/src/core/channel/compress_filter.c
@@ -66,8 +66,6 @@ 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 {
@@ -146,10 +144,10 @@ static void process_send_initial_metadata(
       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(grpc_accept_encoding_mdelem_from_compression_algorithms(
-          calld->mdctx, channeld->supported_compression_algorithms)));
+  grpc_metadata_batch_add_tail(initial_metadata,
+                               &calld->accept_encoding_storage,
+                               GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(
+                                   channeld->supported_compression_algorithms));
 }
 
 static void continue_send_message(grpc_exec_ctx *exec_ctx,
@@ -243,7 +241,6 @@ 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);
 }
diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c
index ec7791656fae6264aeffc1602533fdccd772f505..b9a30cdaf218c593961adb979cc5f2ec1367ddcb 100644
--- a/src/core/channel/http_client_filter.c
+++ b/src/core/channel/http_client_filter.c
@@ -55,11 +55,12 @@ 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 *static_scheme; } channel_data;
+typedef struct channel_data {
+  grpc_mdelem *static_scheme;
+  grpc_mdelem *user_agent;
+} channel_data;
 
 typedef struct {
   grpc_call_element *elem;
@@ -119,10 +120,8 @@ static void hc_mutate_op(grpc_call_element *elem,
     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)));
+    grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
+                                 GRPC_MDELEM_REF(channeld->user_agent));
   }
 
   if (op->recv_initial_metadata != NULL) {
@@ -148,7 +147,6 @@ 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);
 }
 
@@ -177,6 +175,54 @@ static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
   return GRPC_MDELEM_SCHEME_HTTP;
 }
 
+static grpc_mdstr *user_agent_from_args(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(tmp);
+  gpr_free(tmp);
+
+  return result;
+}
+
 /* Constructor for channel_data */
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
                               grpc_channel_element *elem,
@@ -184,11 +230,15 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(!args->is_last);
   chand->static_scheme = scheme_from_args(args->channel_args);
+  chand->user_agent = grpc_mdelem_from_metadata_strings(
+      GRPC_MDSTR_USER_AGENT, user_agent_from_args(args->channel_args));
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
+  channel_data *chand = elem->channel_data;
+  GRPC_MDELEM_UNREF(chand->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 c9db9470e064d60be03abe6059f9e820a8dd69f3..c1645c2ba0777343348e1500230ec0209f9d9dc9 100644
--- a/src/core/channel/http_server_filter.c
+++ b/src/core/channel/http_server_filter.c
@@ -56,7 +56,6 @@ 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 { gpr_uint8 unused; } channel_data;
@@ -124,7 +123,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
     /* translate host to :authority since :authority may be
        omitted */
     grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
-        calld->mdctx, GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
+        GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
     GRPC_MDELEM_UNREF(md);
     calld->seen_authority = 1;
     return authority;
@@ -211,7 +210,6 @@ 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 */
diff --git a/src/core/channel/subchannel_call_holder.c b/src/core/channel/subchannel_call_holder.c
index c5340e0eaf85bd795d9141eb0dafc62644b17535..72517145191e371d65a6b311fff12fe36eb88cc0 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, grpc_mdctx *mdctx) {
+    void *pick_subchannel_arg) {
   gpr_atm_rel_store(&holder->subchannel_call, 0);
   holder->pick_subchannel = pick_subchannel;
   holder->pick_subchannel_arg = pick_subchannel_arg;
@@ -68,7 +68,6 @@ 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,
@@ -157,9 +156,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->mdctx,
-            &holder->subchannel_call, &holder->next_step)) {
+    if (grpc_subchannel_create_call(exec_ctx, holder->subchannel,
+                                    holder->pollset, &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);
@@ -185,9 +184,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->mdctx,
-            &holder->subchannel_call, &holder->next_step)) {
+    if (grpc_subchannel_create_call(exec_ctx, holder->subchannel,
+                                    holder->pollset, &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 a770be257c13a398953b69e56ffcc18b604df732..bda051c56605564bc0f3f5a306074aa38cc56697 100644
--- a/src/core/channel/subchannel_call_holder.h
+++ b/src/core/channel/subchannel_call_holder.h
@@ -68,8 +68,6 @@ 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;
@@ -86,7 +84,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, grpc_mdctx *mdctx);
+    void *pick_subchannel_arg);
 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 5f906a82fb123e8e0f2d8fd6e6e011e104e3aa83..8e1970199774741bbcb6233a07ab1e95c168b46e 100644
--- a/src/core/client_config/subchannel.c
+++ b/src/core/client_config/subchannel.c
@@ -73,7 +73,6 @@ typedef struct waiting_for_connect {
   grpc_pollset *pollset;
   gpr_atm *target;
   grpc_subchannel *subchannel;
-  grpc_mdctx *mdctx;
   grpc_closure continuation;
 } waiting_for_connect;
 
@@ -146,8 +145,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,
-                                         grpc_mdctx *mdctx);
+                                         connection *con,
+                                         grpc_pollset *pollset);
 static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx,
                                               grpc_subchannel *c,
                                               const char *reason);
@@ -394,9 +393,8 @@ 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->mdctx, w4c->target, w4c->notify);
+  call_creation_finished_ok = grpc_subchannel_create_call(
+      exec_ctx, w4c->subchannel, w4c->pollset, 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");
@@ -404,8 +402,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, grpc_mdctx *mdctx,
-                                gpr_atm *target, grpc_closure *notify) {
+                                grpc_pollset *pollset, gpr_atm *target,
+                                grpc_closure *notify) {
   connection *con;
   grpc_subchannel_call *call;
   GPR_TIMER_BEGIN("grpc_subchannel_create_call", 0);
@@ -415,7 +413,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, mdctx);
+    call = create_call(exec_ctx, con, pollset);
     if (!gpr_atm_rel_cas(target, 0, (gpr_atm)(gpr_uintptr)call)) {
       GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "failed to set");
     }
@@ -428,7 +426,6 @@ 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);
@@ -856,15 +853,15 @@ 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,
-                                         grpc_mdctx *mdctx) {
+                                         connection *con,
+                                         grpc_pollset *pollset) {
   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, mdctx, callstk);
+                       NULL, NULL, callstk);
   grpc_call_stack_set_pollset(exec_ctx, callstk, pollset);
   return call;
 }
diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h
index 02ff25eb21d1a06efe04c0dcf43c0e6278b99a30..85ea3739e4aca892ad9f310612637714adb8f550 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, grpc_mdctx *mdctx,
-                                gpr_atm *target, grpc_closure *notify);
+                                grpc_pollset *pollset, gpr_atm *target,
+                                grpc_closure *notify);
 
 /** cancel \a call in the waiting state. */
 void grpc_subchannel_cancel_create_call(grpc_exec_ctx *exec_ctx,
@@ -147,8 +147,6 @@ struct grpc_subchannel_args {
   /** Address to connect to */
   struct sockaddr *addr;
   size_t addr_len;
-  /** metadata context to use */
-  grpc_mdctx *mdctx;
   /** master channel */
   grpc_channel *master;
 };
diff --git a/src/core/security/client_auth_filter.c b/src/core/security/client_auth_filter.c
index 27727b52c9b565983b426b086a46ec85aa54bdc7..cd4b39fa52155e13a750065fbcdfce223a8574b6 100644
--- a/src/core/security/client_auth_filter.c
+++ b/src/core/security/client_auth_filter.c
@@ -63,7 +63,6 @@ typedef struct {
   gpr_uint8 security_context_set;
   grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
   char *service_url;
-  grpc_mdctx *md_ctx;
 } call_data;
 
 /* We can have a per-channel credentials. */
@@ -107,7 +106,7 @@ static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
   for (i = 0; i < num_md; i++) {
     grpc_metadata_batch_add_tail(
         mdb, &calld->md_links[i],
-        grpc_mdelem_from_slices(calld->md_ctx, gpr_slice_ref(md_elems[i].key),
+        grpc_mdelem_from_slices(gpr_slice_ref(md_elems[i].key),
                                 gpr_slice_ref(md_elems[i].value)));
   }
   grpc_call_next_op(exec_ctx, elem, op);
@@ -262,7 +261,6 @@ 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;
   memset(calld, 0, sizeof(*calld));
-  calld->md_ctx = args->metadata_context;
 }
 
 static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index 851e0cfab31f3111cacdc2c6685538c3f9f15778..872c0bd1acb639ad2470adff5ad959593ab340df 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -87,7 +87,7 @@ static void state_unref(grpc_server_secure_state *state) {
 }
 
 static void setup_transport(grpc_exec_ctx *exec_ctx, void *statep,
-                            grpc_transport *transport, grpc_mdctx *mdctx) {
+                            grpc_transport *transport) {
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_server_auth_filter, &grpc_http_server_filter};
   grpc_server_secure_state *state = statep;
@@ -99,7 +99,7 @@ static void setup_transport(grpc_exec_ctx *exec_ctx, void *statep,
       grpc_server_get_channel_args(state->server), args_to_add,
       GPR_ARRAY_SIZE(args_to_add));
   grpc_server_setup_transport(exec_ctx, state->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx, args_copy);
+                              GPR_ARRAY_SIZE(extra_filters), args_copy);
   grpc_channel_args_destroy(args_copy);
 }
 
@@ -130,16 +130,14 @@ static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *statep,
                                      grpc_endpoint *secure_endpoint) {
   grpc_server_secure_state *state = statep;
   grpc_transport *transport;
-  grpc_mdctx *mdctx;
   if (status == GRPC_SECURITY_OK) {
     gpr_mu_lock(&state->mu);
     remove_tcp_from_list_locked(state, wrapped_endpoint);
     if (!state->is_shutdown) {
-      mdctx = grpc_mdctx_create();
       transport = grpc_create_chttp2_transport(
           exec_ctx, grpc_server_get_channel_args(state->server),
-          secure_endpoint, mdctx, 0);
-      setup_transport(exec_ctx, state, transport, mdctx);
+          secure_endpoint, 0);
+      setup_transport(exec_ctx, state, transport);
       grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
     } else {
       /* We need to consume this here, because the server may already have gone
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index de0afb93b3093a1429b5710fbe8a1bcb2b1b5088..5b6c9dc69d8e3acf07c1cc9bc29994da8b13d2a3 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -137,7 +137,6 @@ struct grpc_call {
   grpc_channel *channel;
   grpc_call *parent;
   grpc_call *first_child;
-  grpc_mdctx *metadata_context;
   /* TODO(ctiller): share with cq if possible? */
   gpr_mu mu;
 
@@ -269,11 +268,9 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
   }
   call->send_deadline = send_deadline;
   GRPC_CHANNEL_INTERNAL_REF(channel, "call");
-  call->metadata_context = grpc_channel_get_metadata_context(channel);
   /* 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");
@@ -570,9 +567,8 @@ static int prepare_application_metadata(grpc_call *call, int count,
     grpc_metadata *md = &metadata[i];
     grpc_linked_mdelem *l = (grpc_linked_mdelem *)&md->internal_data;
     GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data));
-    l->md = grpc_mdelem_from_string_and_buffer(call->metadata_context, md->key,
-                                               (const gpr_uint8 *)md->value,
-                                               md->value_length);
+    l->md = grpc_mdelem_from_string_and_buffer(
+        md->key, (const gpr_uint8 *)md->value, md->value_length);
     if (!grpc_mdstr_is_legal_header(l->md->key)) {
       gpr_log(GPR_ERROR, "attempt to send invalid metadata key: %s",
               grpc_mdstr_as_c_string(l->md->key));
@@ -715,8 +711,7 @@ static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
                                           grpc_status_code status,
                                           const char *description) {
   grpc_mdstr *details =
-      description ? grpc_mdstr_from_string(c->metadata_context, description)
-                  : NULL;
+      description ? grpc_mdstr_from_string(description) : NULL;
   cancel_closure *cc = gpr_malloc(sizeof(*cc));
 
   GPR_ASSERT(status != GRPC_STATUS_OK);
@@ -1230,9 +1225,8 @@ 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_GRPC_MESSAGE,
+              GRPC_MDSTR_GRPC_MESSAGE,
               grpc_mdstr_from_string(
-                  call->metadata_context,
                   op->data.send_status_from_server.status_details));
           call->send_extra_metadata_count++;
           set_status_details(
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index 0ca877a394afb96c1f1da76289e0bae1a9e67a0b..859197412bb4ee9a95fde563bd804c1d743346bd 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -65,7 +65,6 @@ struct grpc_channel {
   int is_client;
   gpr_refcount refs;
   gpr_uint32 max_message_length;
-  grpc_mdctx *metadata_context;
   grpc_mdelem *default_authority;
 
   gpr_mu registered_call_mu;
@@ -82,59 +81,10 @@ 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,
-    const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client) {
+    const grpc_channel_args *args, int is_client) {
   size_t i;
   size_t size =
       sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
@@ -145,17 +95,9 @@ grpc_channel *grpc_channel_create_from_filters(
   channel->is_client = is_client;
   /* decremented by grpc_channel_destroy */
   gpr_ref_init(&channel->refs, 1);
-  channel->metadata_context = mdctx;
   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++) {
@@ -179,7 +121,7 @@ grpc_channel *grpc_channel_create_from_filters(
             GRPC_MDELEM_UNREF(channel->default_authority);
           }
           channel->default_authority = grpc_mdelem_from_strings(
-              mdctx, ":authority", args->args[i].value.string);
+              ":authority", args->args[i].value.string);
         }
       } else if (0 ==
                  strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) {
@@ -193,7 +135,7 @@ grpc_channel *grpc_channel_create_from_filters(
                     GRPC_ARG_DEFAULT_AUTHORITY);
           } else {
             channel->default_authority = grpc_mdelem_from_strings(
-                mdctx, ":authority", args->args[i].value.string);
+                ":authority", args->args[i].value.string);
           }
         }
       }
@@ -204,8 +146,8 @@ grpc_channel *grpc_channel_create_from_filters(
       target != NULL) {
     char *default_authority = grpc_get_default_authority(target);
     if (default_authority) {
-      channel->default_authority = grpc_mdelem_from_strings(
-          channel->metadata_context, ":authority", default_authority);
+      channel->default_authority =
+          grpc_mdelem_from_strings(":authority", default_authority);
     }
     gpr_free(default_authority);
   }
@@ -259,12 +201,10 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
   GPR_ASSERT(!reserved);
   return grpc_channel_create_call_internal(
       channel, parent_call, propagation_mask, cq,
-      grpc_mdelem_from_metadata_strings(
-          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_AUTHORITY,
-                 grpc_mdstr_from_string(channel->metadata_context, host))
+      grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+                                        grpc_mdstr_from_string(method)),
+      host ? grpc_mdelem_from_metadata_strings(GRPC_MDSTR_AUTHORITY,
+                                               grpc_mdstr_from_string(host))
            : NULL,
       deadline);
 }
@@ -276,14 +216,11 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method,
       "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)",
       4, (channel, method, host, reserved));
   GPR_ASSERT(!reserved);
-  rc->path = grpc_mdelem_from_metadata_strings(
-      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_AUTHORITY,
-                 grpc_mdstr_from_string(channel->metadata_context, host))
-           : NULL;
+  rc->path = grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+                                               grpc_mdstr_from_string(method));
+  rc->authority = host ? grpc_mdelem_from_metadata_strings(
+                             GRPC_MDSTR_AUTHORITY, grpc_mdstr_from_string(host))
+                       : NULL;
   gpr_mu_lock(&channel->registered_call_mu);
   rc->next = channel->registered_calls;
   channel->registered_calls = rc;
@@ -336,8 +273,6 @@ 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);
   gpr_free(channel);
@@ -376,10 +311,6 @@ grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) {
   return CHANNEL_STACK_FROM_CHANNEL(channel);
 }
 
-grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel) {
-  return channel->metadata_context;
-}
-
 grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
   char tmp[GPR_LTOA_MIN_BUFSIZE];
   switch (i) {
@@ -391,9 +322,8 @@ grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
       return GRPC_MDELEM_GRPC_STATUS_2;
   }
   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));
+  return grpc_mdelem_from_metadata_strings(GRPC_MDSTR_GRPC_STATUS,
+                                           grpc_mdstr_from_string(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 8bafce4216a0ef4809c4e06530d83b8092e141f1..7dea609ebc8021b315ef5ca4a460785f8b47c715 100644
--- a/src/core/surface/channel.h
+++ b/src/core/surface/channel.h
@@ -40,14 +40,11 @@
 grpc_channel *grpc_channel_create_from_filters(
     grpc_exec_ctx *exec_ctx, const char *target,
     const grpc_channel_filter **filters, size_t count,
-    const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client);
+    const grpc_channel_args *args, int is_client);
 
 /** Get a (borrowed) pointer to this channels underlying channel stack */
 grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
 
-/** Get a (borrowed) pointer to the channel wide metadata context */
-grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel);
-
 /** Get a grpc_mdelem of grpc-status: X where X is the numeric value of
     status_code.
 
diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c
index 51d9130b63d5711989a12281a20a6077b3958f69..026592db951816224f12bc843e933baeb2ac654c 100644
--- a/src/core/surface/channel_create.c
+++ b/src/core/surface/channel_create.c
@@ -59,8 +59,6 @@ typedef struct {
 
   grpc_endpoint *tcp;
 
-  grpc_mdctx *mdctx;
-
   grpc_closure connected;
 } connector;
 
@@ -72,7 +70,6 @@ static void connector_ref(grpc_connector *con) {
 static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
   connector *c = (connector *)con;
   if (gpr_unref(&c->refs)) {
-    grpc_mdctx_unref(c->mdctx);
     gpr_free(c);
   }
 }
@@ -82,8 +79,8 @@ static void connected(grpc_exec_ctx *exec_ctx, void *arg, int success) {
   grpc_closure *notify;
   grpc_endpoint *tcp = c->tcp;
   if (tcp != NULL) {
-    c->result->transport = grpc_create_chttp2_transport(
-        exec_ctx, c->args.channel_args, tcp, c->mdctx, 1);
+    c->result->transport =
+        grpc_create_chttp2_transport(exec_ctx, c->args.channel_args, tcp, 1);
     grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL,
                                         0);
     GPR_ASSERT(c->result->transport);
@@ -123,7 +120,6 @@ static const grpc_connector_vtable connector_vtable = {
 typedef struct {
   grpc_subchannel_factory base;
   gpr_refcount refs;
-  grpc_mdctx *mdctx;
   grpc_channel_args *merge_args;
   grpc_channel *master;
 } subchannel_factory;
@@ -139,7 +135,6 @@ static void subchannel_factory_unref(grpc_exec_ctx *exec_ctx,
   if (gpr_unref(&f->refs)) {
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory");
     grpc_channel_args_destroy(f->merge_args);
-    grpc_mdctx_unref(f->mdctx);
     gpr_free(f);
   }
 }
@@ -154,10 +149,7 @@ static grpc_subchannel *subchannel_factory_create_subchannel(
   grpc_subchannel *s;
   memset(c, 0, sizeof(*c));
   c->base.vtable = &connector_vtable;
-  c->mdctx = f->mdctx;
-  grpc_mdctx_ref(c->mdctx);
   gpr_ref_init(&c->refs, 1);
-  args->mdctx = f->mdctx;
   args->args = final_args;
   args->master = f->master;
   s = grpc_subchannel_create(&c->base, args);
@@ -182,7 +174,6 @@ grpc_channel *grpc_insecure_channel_create(const char *target,
   const grpc_channel_filter *filters[MAX_FILTERS];
   grpc_resolver *resolver;
   subchannel_factory *f;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   size_t n = 0;
   GRPC_API_TRACE(
@@ -196,14 +187,12 @@ grpc_channel *grpc_insecure_channel_create(const char *target,
   filters[n++] = &grpc_client_channel_filter;
   GPR_ASSERT(n <= MAX_FILTERS);
 
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, n, args, 1);
 
   f = gpr_malloc(sizeof(*f));
   f->base.vtable = &subchannel_factory_vtable;
   gpr_ref_init(&f->refs, 1);
-  grpc_mdctx_ref(mdctx);
-  f->mdctx = mdctx;
   f->merge_args = grpc_channel_args_copy(args);
   f->master = channel;
   GRPC_CHANNEL_INTERNAL_REF(f->master, "subchannel_factory");
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index 96ad012addb89d5d9ab1a2e1b749bec029496838..0247116ebb97101271cc5cc3d2954803f4b91d86 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -46,7 +46,6 @@
 typedef struct {
   grpc_linked_mdelem status;
   grpc_linked_mdelem details;
-  grpc_mdctx *mdctx;
 } call_data;
 
 typedef struct {
@@ -60,9 +59,9 @@ 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(calld->mdctx, "grpc-status", tmp);
-  calld->details.md = grpc_mdelem_from_strings(calld->mdctx, "grpc-message",
-                                               chand->error_message);
+  calld->status.md = grpc_mdelem_from_strings("grpc-status", tmp);
+  calld->details.md =
+      grpc_mdelem_from_strings("grpc-message", chand->error_message);
   calld->status.prev = calld->details.next = NULL;
   calld->status.next = &calld->details;
   calld->details.prev = &calld->status;
@@ -105,8 +104,6 @@ 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) {
-  call_data *calld = elem->call_data;
-  calld->mdctx = args->metadata_context;
 }
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
@@ -141,8 +138,8 @@ grpc_channel *grpc_lame_client_channel_create(const char *target,
   channel_data *chand;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   static const grpc_channel_filter *filters[] = {&lame_filter};
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, 1,
-                                             NULL, grpc_mdctx_create(), 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, 1, NULL, 1);
   elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
   GRPC_API_TRACE(
       "grpc_lame_client_channel_create(target=%s, error_code=%d, "
diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c
index 0dd0a3116944a0707ce0088d6d6921f50b327a0c..471b5a71e73b355cc3c69731e177cffb470a87a0 100644
--- a/src/core/surface/secure_channel_create.c
+++ b/src/core/surface/secure_channel_create.c
@@ -67,8 +67,6 @@ typedef struct {
   grpc_endpoint *newly_connecting_endpoint;
 
   grpc_closure connected_closure;
-
-  grpc_mdctx *mdctx;
 } connector;
 
 static void connector_ref(grpc_connector *con) {
@@ -79,7 +77,6 @@ static void connector_ref(grpc_connector *con) {
 static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
   connector *c = (connector *)con;
   if (gpr_unref(&c->refs)) {
-    grpc_mdctx_unref(c->mdctx);
     gpr_free(c);
   }
 }
@@ -105,7 +102,7 @@ static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
     c->connecting_endpoint = NULL;
     gpr_mu_unlock(&c->mu);
     c->result->transport = grpc_create_chttp2_transport(
-        exec_ctx, c->args.channel_args, secure_endpoint, c->mdctx, 1);
+        exec_ctx, c->args.channel_args, secure_endpoint, 1);
     grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL,
                                         0);
     c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *) * 2);
@@ -174,7 +171,6 @@ static const grpc_connector_vtable connector_vtable = {
 typedef struct {
   grpc_subchannel_factory base;
   gpr_refcount refs;
-  grpc_mdctx *mdctx;
   grpc_channel_args *merge_args;
   grpc_channel_security_connector *security_connector;
   grpc_channel *master;
@@ -193,7 +189,6 @@ static void subchannel_factory_unref(grpc_exec_ctx *exec_ctx,
                                   "subchannel_factory");
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory");
     grpc_channel_args_destroy(f->merge_args);
-    grpc_mdctx_unref(f->mdctx);
     gpr_free(f);
   }
 }
@@ -209,13 +204,10 @@ static grpc_subchannel *subchannel_factory_create_subchannel(
   memset(c, 0, sizeof(*c));
   c->base.vtable = &connector_vtable;
   c->security_connector = f->security_connector;
-  c->mdctx = f->mdctx;
   gpr_mu_init(&c->mu);
-  grpc_mdctx_ref(c->mdctx);
   gpr_ref_init(&c->refs, 1);
   args->args = final_args;
   args->master = f->master;
-  args->mdctx = f->mdctx;
   s = grpc_subchannel_create(&c->base, args);
   grpc_connector_unref(exec_ctx, &c->base);
   grpc_channel_args_destroy(final_args);
@@ -239,7 +231,6 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds,
   grpc_channel_args *args_copy;
   grpc_channel_args *new_args_from_connector;
   grpc_channel_security_connector *security_connector;
-  grpc_mdctx *mdctx;
   grpc_resolver *resolver;
   subchannel_factory *f;
 #define MAX_FILTERS 3
@@ -269,7 +260,6 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds,
         target, GRPC_STATUS_INVALID_ARGUMENT,
         "Failed to create security connector.");
   }
-  mdctx = grpc_mdctx_create();
 
   connector_arg = grpc_security_connector_to_arg(&security_connector->base);
   args_copy = grpc_channel_args_copy_and_add(
@@ -283,13 +273,11 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds,
   GPR_ASSERT(n <= MAX_FILTERS);
 
   channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args_copy, mdctx, 1);
+                                             args_copy, 1);
 
   f = gpr_malloc(sizeof(*f));
   f->base.vtable = &subchannel_factory_vtable;
   gpr_ref_init(&f->refs, 1);
-  grpc_mdctx_ref(mdctx);
-  f->mdctx = mdctx;
   GRPC_SECURITY_CONNECTOR_REF(&security_connector->base, "subchannel_factory");
   f->security_connector = security_connector;
   f->merge_args = grpc_channel_args_copy(args_copy);
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index d33c42187702cb4a4d9b79a2b1b262467115ad45..cdbd542d9a527f06f692b1bbc242a5cd1eb5bcfc 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -887,7 +887,7 @@ void grpc_server_start(grpc_server *server) {
 void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s,
                                  grpc_transport *transport,
                                  grpc_channel_filter const **extra_filters,
-                                 size_t num_extra_filters, grpc_mdctx *mdctx,
+                                 size_t num_extra_filters,
                                  const grpc_channel_args *args) {
   size_t num_filters = s->channel_filter_count + num_extra_filters + 1;
   grpc_channel_filter const **filters =
@@ -922,7 +922,7 @@ void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s,
   }
 
   channel = grpc_channel_create_from_filters(exec_ctx, NULL, filters,
-                                             num_filters, args, mdctx, 0);
+                                             num_filters, args, 0);
   chand = (channel_data *)grpc_channel_stack_element(
               grpc_channel_get_channel_stack(channel), 0)->channel_data;
   chand->server = s;
@@ -941,8 +941,8 @@ void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s,
     chand->registered_methods = gpr_malloc(alloc);
     memset(chand->registered_methods, 0, alloc);
     for (rm = s->registered_methods; rm; rm = rm->next) {
-      host = rm->host ? grpc_mdstr_from_string(mdctx, rm->host) : NULL;
-      method = grpc_mdstr_from_string(mdctx, rm->method);
+      host = rm->host ? grpc_mdstr_from_string(rm->host) : NULL;
+      method = grpc_mdstr_from_string(rm->method);
       hash = GRPC_MDSTR_KV_HASH(host ? host->hash : 0, method->hash);
       for (probes = 0; chand->registered_methods[(hash + probes) % slots]
                                .server_registered_method != NULL;
diff --git a/src/core/surface/server.h b/src/core/surface/server.h
index 4c46d0767928e502702aa7802cd2db1e8e7532e9..a957fdb3605fb9f78958af6f78df442baedfb543 100644
--- a/src/core/surface/server.h
+++ b/src/core/surface/server.h
@@ -57,7 +57,7 @@ void grpc_server_add_listener(
 void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *server,
                                  grpc_transport *transport,
                                  grpc_channel_filter const **extra_filters,
-                                 size_t num_extra_filters, grpc_mdctx *mdctx,
+                                 size_t num_extra_filters,
                                  const grpc_channel_args *args);
 
 const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server);
diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c
index 580b91573c208c0ab8f36d8b1178fac3dda80b4b..9acc694b5b5a65d9a1202460b0e148018cfccd2b 100644
--- a/src/core/surface/server_chttp2.c
+++ b/src/core/surface/server_chttp2.c
@@ -44,11 +44,11 @@
 #include <grpc/support/useful.h>
 
 static void setup_transport(grpc_exec_ctx *exec_ctx, void *server,
-                            grpc_transport *transport, grpc_mdctx *mdctx) {
+                            grpc_transport *transport) {
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_server_setup_transport(exec_ctx, server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(server));
 }
 
@@ -61,10 +61,9 @@ static void new_transport(grpc_exec_ctx *exec_ctx, void *server,
    * (as in server_secure_chttp2.c) needs to add synchronization to avoid this
    * case.
    */
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport = grpc_create_chttp2_transport(
-      exec_ctx, grpc_server_get_channel_args(server), tcp, mdctx, 0);
-  setup_transport(exec_ctx, server, transport, mdctx);
+      exec_ctx, grpc_server_get_channel_args(server), tcp, 0);
+  setup_transport(exec_ctx, server, transport);
   grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
 }
 
diff --git a/src/core/transport/chttp2/hpack_encoder.c b/src/core/transport/chttp2/hpack_encoder.c
index 6c7c00b7c399660b4b90877fccce7db1def26dc2..fe03b89dc2b0353a531f809dda0552abe0e77b8d 100644
--- a/src/core/transport/chttp2/hpack_encoder.c
+++ b/src/core/transport/chttp2/hpack_encoder.c
@@ -42,6 +42,7 @@
 #include "src/core/transport/chttp2/hpack_table.h"
 #include "src/core/transport/chttp2/timeout_encoding.h"
 #include "src/core/transport/chttp2/varint.h"
+#include "src/core/transport/static_metadata.h"
 
 #define HASH_FRAGMENT_1(x) ((x)&255)
 #define HASH_FRAGMENT_2(x) ((x >> 8) & 255)
@@ -435,8 +436,7 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
   grpc_chttp2_encode_timeout(
       gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str);
   mdelem = grpc_mdelem_from_metadata_strings(
-      c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str),
-      grpc_mdstr_from_string(c->mdctx, timeout_str));
+      GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str));
   hpack_enc(c, mdelem, st);
   GRPC_MDELEM_UNREF(mdelem);
 }
@@ -447,11 +447,8 @@ gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) {
   return slice;
 }
 
-void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
-                                       grpc_mdctx *ctx) {
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c) {
   memset(c, 0, sizeof(*c));
-  c->mdctx = ctx;
-  c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout");
 }
 
 void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
@@ -460,7 +457,6 @@ void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
     if (c->entries_keys[i]) GRPC_MDSTR_UNREF(c->entries_keys[i]);
     if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]);
   }
-  GRPC_MDSTR_UNREF(c->timeout_key_str);
 }
 
 void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
diff --git a/src/core/transport/chttp2/hpack_encoder.h b/src/core/transport/chttp2/hpack_encoder.h
index 59b122dfdad8e39267d4ddf4f1b68acaaa91656b..dab6f66c7169501e0ee163c5d01f42bcf66e28dc 100644
--- a/src/core/transport/chttp2/hpack_encoder.h
+++ b/src/core/transport/chttp2/hpack_encoder.h
@@ -59,11 +59,6 @@ typedef struct {
      been seen. When that count reaches max (255), all values are halved. */
   gpr_uint8 filter_elems[GRPC_CHTTP2_HPACKC_NUM_FILTERS];
 
-  /* metadata context */
-  grpc_mdctx *mdctx;
-  /* the string 'grpc-timeout' */
-  grpc_mdstr *timeout_key_str;
-
   /* entry tables for keys & elems: these tables track values that have been
      seen and *may* be in the decompressor table */
   grpc_mdstr *entries_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES];
@@ -74,8 +69,7 @@ typedef struct {
   gpr_uint16 table_elem_size[GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS];
 } grpc_chttp2_hpack_compressor;
 
-void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
-                                       grpc_mdctx *mdctx);
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c);
 void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c);
 
 void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, gpr_uint32 id,
diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c
index 6eebfc3ce40584159dba604f8421cd9e48fa0b05..03a7d63b097ea103306d0320b1c769d7e94b7cdd 100644
--- a/src/core/transport/chttp2/hpack_parser.c
+++ b/src/core/transport/chttp2/hpack_parser.c
@@ -633,8 +633,7 @@ static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
 
 static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p,
                                grpc_chttp2_hpack_parser_string *str) {
-  grpc_mdstr *s = grpc_mdstr_from_buffer(p->table.mdctx, (gpr_uint8 *)str->str,
-                                         str->length);
+  grpc_mdstr *s = grpc_mdstr_from_buffer((gpr_uint8 *)str->str, str->length);
   str->length = 0;
   return s;
 }
@@ -742,8 +741,7 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
 static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
                                 const gpr_uint8 *cur, const gpr_uint8 *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
-  on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                              GRPC_MDSTR_REF(md->key),
+  on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
                                               take_string(p, &p->value)),
          1);
   return parse_begin(p, cur, end);
@@ -752,8 +750,7 @@ static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
 /* finish a literal header with incremental indexing with no index */
 static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
                                   const gpr_uint8 *cur, const gpr_uint8 *end) {
-  on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                              take_string(p, &p->key),
+  on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
                                               take_string(p, &p->value)),
          1);
   return parse_begin(p, cur, end);
@@ -795,8 +792,7 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
 static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
                                 const gpr_uint8 *cur, const gpr_uint8 *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
-  on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                              GRPC_MDSTR_REF(md->key),
+  on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
                                               take_string(p, &p->value)),
          0);
   return parse_begin(p, cur, end);
@@ -805,8 +801,7 @@ static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
 /* finish a literal header without incremental indexing with index = 0 */
 static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
                                   const gpr_uint8 *cur, const gpr_uint8 *end) {
-  on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                              take_string(p, &p->key),
+  on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
                                               take_string(p, &p->value)),
          0);
   return parse_begin(p, cur, end);
@@ -848,8 +843,7 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
 static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
                                 const gpr_uint8 *cur, const gpr_uint8 *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
-  on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                              GRPC_MDSTR_REF(md->key),
+  on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
                                               take_string(p, &p->value)),
          0);
   return parse_begin(p, cur, end);
@@ -858,8 +852,7 @@ static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
 /* finish a literal header that is never indexed with an extra value */
 static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
                                   const gpr_uint8 *cur, const gpr_uint8 *end) {
-  on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                              take_string(p, &p->key),
+  on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
                                               take_string(p, &p->value)),
          0);
   return parse_begin(p, cur, end);
@@ -1342,8 +1335,7 @@ static void on_header_not_set(void *user_data, grpc_mdelem *md) {
   abort();
 }
 
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
-                                   grpc_mdctx *mdctx) {
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) {
   p->on_header = on_header_not_set;
   p->on_header_user_data = NULL;
   p->state = parse_begin;
@@ -1353,7 +1345,7 @@ void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
   p->value.str = NULL;
   p->value.capacity = 0;
   p->value.length = 0;
-  grpc_chttp2_hptbl_init(&p->table, mdctx);
+  grpc_chttp2_hptbl_init(&p->table);
 }
 
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) {
diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h
index f56867016cfbcd06d59143a8eb7d7a1ee59e4bce..fb894b5735a35557a7865d5e0fe9be197f392bdd 100644
--- a/src/core/transport/chttp2/hpack_parser.h
+++ b/src/core/transport/chttp2/hpack_parser.h
@@ -95,8 +95,7 @@ struct grpc_chttp2_hpack_parser {
   grpc_chttp2_hptbl table;
 };
 
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
-                                   grpc_mdctx *mdctx);
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p);
 void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p);
 
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p);
diff --git a/src/core/transport/chttp2/hpack_table.c b/src/core/transport/chttp2/hpack_table.c
index c442c2c34139c50fd4ebfe6ae3b029a0c8fdca82..f66745f069a569f6f9207cc3d81a060b08b11191 100644
--- a/src/core/transport/chttp2/hpack_table.c
+++ b/src/core/transport/chttp2/hpack_table.c
@@ -169,15 +169,14 @@ static struct {
     {"www-authenticate", ""},
 };
 
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) {
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) {
   size_t i;
 
   memset(tbl, 0, sizeof(*tbl));
-  tbl->mdctx = mdctx;
   tbl->max_bytes = GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
   for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
-    tbl->static_ents[i - 1] = grpc_mdelem_from_strings(
-        mdctx, static_table[i].key, static_table[i].value);
+    tbl->static_ents[i - 1] =
+        grpc_mdelem_from_strings(static_table[i].key, static_table[i].value);
   }
 }
 
diff --git a/src/core/transport/chttp2/hpack_table.h b/src/core/transport/chttp2/hpack_table.h
index 4f882e2e03ba80f5b6b95911573556221a51672b..d2587e0758eae6765dd754c621f90859b87cbe29 100644
--- a/src/core/transport/chttp2/hpack_table.h
+++ b/src/core/transport/chttp2/hpack_table.h
@@ -57,7 +57,6 @@
 
 /* hpack decoder table */
 typedef struct {
-  grpc_mdctx *mdctx;
   /* the first used entry in ents */
   gpr_uint16 first_ent;
   /* the last used entry in ents */
@@ -77,7 +76,7 @@ typedef struct {
 } grpc_chttp2_hptbl;
 
 /* initialize a hpack table */
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx);
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl);
 void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl);
 
 /* lookup a table entry based on its hpack index */
diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h
index 2d0cb4abdbb6ebf75d2ac071457fe74f64dc4dc2..99b5187d460a6a1ea5d18805aa052226155caac3 100644
--- a/src/core/transport/chttp2/internal.h
+++ b/src/core/transport/chttp2/internal.h
@@ -232,9 +232,6 @@ struct grpc_chttp2_transport_parsing {
 
   /** data to write later - after parsing */
   gpr_slice_buffer qbuf;
-  /* metadata object cache */
-  grpc_mdstr *str_grpc_timeout;
-  grpc_mdelem *elem_grpc_status_ok;
   /** parser for headers */
   grpc_chttp2_hpack_parser hpack_parser;
   /** simple one shot parsers */
@@ -288,7 +285,6 @@ struct grpc_chttp2_transport_parsing {
 struct grpc_chttp2_transport {
   grpc_transport base; /* must be first */
   grpc_endpoint *ep;
-  grpc_mdctx *metadata_context;
   gpr_refcount refs;
   char *peer_string;
 
diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c
index 8cef8fbb772291dee6c1ec7b6f18cd0e92e2dfe2..2872a3a5c1bfc4a33a743b1ff91537cef4804d9d 100644
--- a/src/core/transport/chttp2/parsing.c
+++ b/src/core/transport/chttp2/parsing.c
@@ -35,14 +35,15 @@
 
 #include <string.h>
 
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
 #include "src/core/profiling/timers.h"
 #include "src/core/transport/chttp2/http2_errors.h"
 #include "src/core/transport/chttp2/status_conversion.h"
 #include "src/core/transport/chttp2/timeout_encoding.h"
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
+#include "src/core/transport/static_metadata.h"
 
 static int init_frame_parser(grpc_exec_ctx *exec_ctx,
                              grpc_chttp2_transport_parsing *transport_parsing);
@@ -588,13 +589,12 @@ static void on_initial_header(void *tp, grpc_mdelem *md) {
       transport_parsing->is_client ? "CLI" : "SVR",
       grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
 
-  if (md->key == transport_parsing->elem_grpc_status_ok->key &&
-      md != transport_parsing->elem_grpc_status_ok) {
+  if (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
     /* TODO(ctiller): check for a status like " 0" */
     stream_parsing->seen_error = 1;
   }
 
-  if (md->key == transport_parsing->str_grpc_timeout) {
+  if (md->key == GRPC_MDSTR_GRPC_TIMEOUT) {
     gpr_timespec *cached_timeout = grpc_mdelem_get_user_data(md, free_timeout);
     if (!cached_timeout) {
       /* not already parsed: parse it now, and store the result away */
@@ -635,8 +635,7 @@ static void on_trailing_header(void *tp, grpc_mdelem *md) {
       transport_parsing->is_client ? "CLI" : "SVR",
       grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
 
-  if (md->key == transport_parsing->elem_grpc_status_ok->key &&
-      md != transport_parsing->elem_grpc_status_ok) {
+  if (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
     /* TODO(ctiller): check for a status like " 0" */
     stream_parsing->seen_error = 1;
   }
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index 1545cf24ca50a35c1b63e55f6e1ddfa92da7917c..480fb042cba06832f2ec0c9d3d837e62c03f3486 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -49,6 +49,7 @@
 #include "src/core/transport/chttp2/internal.h"
 #include "src/core/transport/chttp2/status_conversion.h"
 #include "src/core/transport/chttp2/timeout_encoding.h"
+#include "src/core/transport/static_metadata.h"
 #include "src/core/transport/transport_impl.h"
 
 #define DEFAULT_WINDOW 65535
@@ -156,9 +157,6 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
   grpc_chttp2_hpack_parser_destroy(&t->parsing.hpack_parser);
   grpc_chttp2_goaway_parser_destroy(&t->parsing.goaway_parser);
 
-  GRPC_MDSTR_UNREF(t->parsing.str_grpc_timeout);
-  GRPC_MDELEM_UNREF(t->parsing.elem_grpc_status_ok);
-
   for (i = 0; i < STREAM_LIST_COUNT; i++) {
     GPR_ASSERT(t->lists[i].head == NULL);
     GPR_ASSERT(t->lists[i].tail == NULL);
@@ -184,8 +182,6 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
     gpr_free(ping);
   }
 
-  grpc_mdctx_unref(t->metadata_context);
-
   gpr_free(t->peer_string);
   gpr_free(t);
 }
@@ -220,8 +216,7 @@ static void ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); }
 
 static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                            const grpc_channel_args *channel_args,
-                           grpc_endpoint *ep, grpc_mdctx *mdctx,
-                           gpr_uint8 is_client) {
+                           grpc_endpoint *ep, gpr_uint8 is_client) {
   size_t i;
   int j;
 
@@ -237,9 +232,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
   /* ref is dropped at transport close() */
   gpr_ref_init(&t->shutdown_ep_refs, 1);
   gpr_mu_init(&t->mu);
-  grpc_mdctx_ref(mdctx);
   t->peer_string = grpc_endpoint_get_peer(ep);
-  t->metadata_context = mdctx;
   t->endpoint_reading = 1;
   t->global.next_stream_id = is_client ? 1 : 2;
   t->global.is_client = is_client;
@@ -249,10 +242,6 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
   t->global.ping_counter = 1;
   t->global.pings.next = t->global.pings.prev = &t->global.pings;
   t->parsing.is_client = is_client;
-  t->parsing.str_grpc_timeout =
-      grpc_mdstr_from_string(t->metadata_context, "grpc-timeout");
-  t->parsing.elem_grpc_status_ok =
-      grpc_mdelem_from_strings(t->metadata_context, "grpc-status", "0");
   t->parsing.deframe_state =
       is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0;
   t->writing.is_client = is_client;
@@ -263,12 +252,12 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
   gpr_slice_buffer_init(&t->global.qbuf);
 
   gpr_slice_buffer_init(&t->writing.outbuf);
-  grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor, mdctx);
+  grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor);
   grpc_closure_init(&t->writing_action, writing_action, t);
 
   gpr_slice_buffer_init(&t->parsing.qbuf);
   grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser);
-  grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser, t->metadata_context);
+  grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser);
 
   grpc_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing,
                     &t->writing);
@@ -724,11 +713,10 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
 static int contains_non_ok_status(
     grpc_chttp2_transport_global *transport_global,
     grpc_metadata_batch *batch) {
-  grpc_mdelem *ok_elem =
-      TRANSPORT_FROM_GLOBAL(transport_global)->parsing.elem_grpc_status_ok;
   grpc_linked_mdelem *l;
   for (l = batch->list.head; l; l = l->next) {
-    if (l->md->key == ok_elem->key && l->md != ok_elem) {
+    if (l->md->key == GRPC_MDSTR_GRPC_STATUS &&
+        l->md != GRPC_MDELEM_GRPC_STATUS_0) {
       return 1;
     }
   }
@@ -1035,19 +1023,18 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx,
      about the metadata yet */
   if (!stream_global->published_trailing_metadata ||
       stream_global->recv_trailing_metadata_finished != NULL) {
-    grpc_mdctx *mdctx =
-        TRANSPORT_FROM_GLOBAL(transport_global)->metadata_context;
     char status_string[GPR_LTOA_MIN_BUFSIZE];
     gpr_ltoa(status, status_string);
     grpc_chttp2_incoming_metadata_buffer_add(
         &stream_global->received_trailing_metadata,
-        grpc_mdelem_from_strings(mdctx, "grpc-status", status_string));
+        grpc_mdelem_from_metadata_strings(
+            GRPC_MDSTR_GRPC_STATUS, grpc_mdstr_from_string(status_string)));
     if (slice) {
       grpc_chttp2_incoming_metadata_buffer_add(
           &stream_global->received_trailing_metadata,
           grpc_mdelem_from_metadata_strings(
-              mdctx, grpc_mdstr_from_string(mdctx, "grpc-message"),
-              grpc_mdstr_from_slice(mdctx, gpr_slice_ref(*slice))));
+              GRPC_MDSTR_GRPC_MESSAGE,
+              grpc_mdstr_from_slice(gpr_slice_ref(*slice))));
     }
     stream_global->published_trailing_metadata = 1;
     grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
@@ -1597,9 +1584,9 @@ static const grpc_transport_vtable vtable = {
 
 grpc_transport *grpc_create_chttp2_transport(
     grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
-    grpc_endpoint *ep, grpc_mdctx *mdctx, int is_client) {
+    grpc_endpoint *ep, int is_client) {
   grpc_chttp2_transport *t = gpr_malloc(sizeof(grpc_chttp2_transport));
-  init_transport(exec_ctx, t, channel_args, ep, mdctx, is_client != 0);
+  init_transport(exec_ctx, t, channel_args, ep, is_client != 0);
   return &t->base;
 }
 
diff --git a/src/core/transport/chttp2_transport.h b/src/core/transport/chttp2_transport.h
index fce2b680fd913de30f5eff01da71493c230251e8..95520501edde98b3272d9a6e23f267b4402a69be 100644
--- a/src/core/transport/chttp2_transport.h
+++ b/src/core/transport/chttp2_transport.h
@@ -42,7 +42,7 @@ extern int grpc_flowctl_trace;
 
 grpc_transport *grpc_create_chttp2_transport(
     grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
-    grpc_endpoint *ep, grpc_mdctx *metadata_context, int is_client);
+    grpc_endpoint *ep, int is_client);
 
 void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx,
                                          grpc_transport *transport,
diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c
index cbec63c8686af8c90b1d0b928e7f614d4baeeff1..d031efc2264868e49895385620020ed56458bba6 100644
--- a/src/core/transport/metadata.c
+++ b/src/core/transport/metadata.c
@@ -64,33 +64,17 @@
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
 #define DEBUG_ARGS , const char *file, int line
 #define FWD_DEBUG_ARGS , file, line
-#define INTERNAL_STRING_REF(s)            \
-  if (is_mdstr_static((grpc_mdstr *)(s))) \
-    ;                                     \
-  else                                    \
-  internal_string_ref((s), __FILE__, __LINE__)
-#define INTERNAL_STRING_UNREF(s)          \
-  if (is_mdstr_static((grpc_mdstr *)(s))) \
-    ;                                     \
-  else                                    \
-  internal_string_unref((s), __FILE__, __LINE__)
-#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
+#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
 #else
 #define DEBUG_ARGS
 #define FWD_DEBUG_ARGS
-#define INTERNAL_STRING_REF(s)            \
-  if (is_mdstr_static((grpc_mdstr *)(s))) \
-    ;                                     \
-  else                                    \
-  internal_string_ref((s))
-#define INTERNAL_STRING_UNREF(s)          \
-  if (is_mdstr_static((grpc_mdstr *)(s))) \
-    ;                                     \
-  else                                    \
-  internal_string_unref((s))
-#define REF_MD_LOCKED(s) ref_md_locked((s))
+#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
 #endif
 
+#define TABLE_IDX(hash, log2_shards, capacity) \
+  (((hash) >> (log2_shards)) % (capacity))
+#define SHARD_IDX(hash, log2_shards) ((hash) & ((1 << (log2_shards)) - 1))
+
 typedef void (*destroy_user_data_func)(void *user_data);
 
 /* Shadow structure for grpc_mdstr for non-static values */
@@ -100,14 +84,13 @@ typedef struct internal_string {
   gpr_uint32 hash;
 
   /* private only data */
-  gpr_uint32 refs;
+  gpr_atm refcnt;
+
   gpr_uint8 has_base64_and_huffman_encoded;
   gpr_slice_refcount refcount;
 
   gpr_slice base64_and_huffman;
 
-  grpc_mdctx *context;
-
   struct internal_string *bucket_next;
 } internal_string;
 
@@ -117,69 +100,75 @@ typedef struct internal_metadata {
   internal_string *key;
   internal_string *value;
 
+  /* private only data */
   gpr_atm refcnt;
 
-  /* private only data */
   gpr_mu mu_user_data;
   gpr_atm destroy_user_data;
   gpr_atm user_data;
 
-  grpc_mdctx *context;
   struct internal_metadata *bucket_next;
 } internal_metadata;
 
-typedef struct static_string {
-  grpc_mdstr *mdstr;
-  gpr_uint32 hash;
-} static_string;
+typedef struct strtab_shard {
+  gpr_mu mu;
+  internal_string **strs;
+  size_t count;
+  size_t capacity;
+} strtab_shard;
 
-typedef struct static_mdelem {
-  grpc_mdelem *mdelem;
-  gpr_uint32 hash;
-} static_mdelem;
+typedef struct mdtab_shard {
+  gpr_mu mu;
+  internal_metadata **elems;
+  size_t count;
+  size_t capacity;
+  size_t free;
+} mdtab_shard;
 
-struct grpc_mdctx {
-  gpr_uint32 hash_seed;
-  int refs;
+#define LOG2_STRTAB_SHARD_COUNT 5
+#define LOG2_MDTAB_SHARD_COUNT 4
+#define STRTAB_SHARD_COUNT ((size_t)(1 << LOG2_STRTAB_SHARD_COUNT))
+#define MDTAB_SHARD_COUNT ((size_t)(1 << LOG2_MDTAB_SHARD_COUNT))
 
-  gpr_mu mu;
+/* hash seed: decided at initialization time */
+static gpr_uint32 g_hash_seed;
 
-  /* linearly probed hash tables for static element lookup */
-  static_string static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
-  static_mdelem static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
-  size_t static_strtab_maxprobe;
-  size_t static_mdtab_maxprobe;
+/* linearly probed hash tables for static element lookup */
+static grpc_mdstr *g_static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
+static grpc_mdelem *g_static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
+static size_t g_static_strtab_maxprobe;
+static size_t g_static_mdtab_maxprobe;
 
-  /* chained hash table of dynamically allocated strings */
-  internal_string **strtab;
-  size_t strtab_count;
-  size_t strtab_capacity;
+static strtab_shard g_strtab_shard[STRTAB_SHARD_COUNT];
+static mdtab_shard g_mdtab_shard[MDTAB_SHARD_COUNT];
 
-  /* chained hash table of dynamically allocated mdelems */
-  internal_metadata **mdtab;
-  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);
-static void internal_string_unref(internal_string *s DEBUG_ARGS);
-static void discard_metadata(grpc_mdctx *ctx);
-static void gc_mdtab(grpc_mdctx *ctx);
-static void metadata_context_destroy_locked(grpc_mdctx *ctx);
+static void discard_metadata(mdtab_shard *shard);
+static void gc_mdtab(mdtab_shard *shard);
 
 void grpc_mdctx_global_init(void) {
-  size_t i;
+  size_t i, j;
+  g_hash_seed = (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec;
+  g_static_strtab_maxprobe = 0;
+  g_static_mdtab_maxprobe = 0;
+  /* build static tables */
+  memset(g_static_mdtab, 0, sizeof(g_static_mdtab));
+  memset(g_static_strtab, 0, sizeof(g_static_strtab));
   for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
     grpc_mdstr *elem = &grpc_static_mdstr_table[i];
     const char *str = grpc_static_metadata_strings[i];
+    gpr_uint32 hash = gpr_murmur_hash3(str, strlen(str), g_hash_seed);
     *(gpr_slice *)&elem->slice = gpr_slice_from_static_string(str);
-    *(gpr_uint32 *)&elem->hash = gpr_murmur_hash3(str, strlen(str), 0);
+    *(gpr_uint32 *)&elem->hash = hash;
+    for (j = 0;; j++) {
+      size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_strtab);
+      if (g_static_strtab[idx] == NULL) {
+        g_static_strtab[idx] = &grpc_static_mdstr_table[i];
+        break;
+      }
+    }
+    if (j > g_static_strtab_maxprobe) {
+      g_static_strtab_maxprobe = j;
+    }
   }
   for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
     grpc_mdelem *elem = &grpc_static_mdelem_table[i];
@@ -187,12 +176,56 @@ void grpc_mdctx_global_init(void) {
         &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]];
+    gpr_uint32 hash = GRPC_MDSTR_KV_HASH(key->hash, value->hash);
     *(grpc_mdstr **)&elem->key = key;
     *(grpc_mdstr **)&elem->value = value;
+    for (j = 0;; j++) {
+      size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_mdtab);
+      if (g_static_mdtab[idx] == NULL) {
+        g_static_mdtab[idx] = elem;
+        break;
+      }
+    }
+    if (j > g_static_mdtab_maxprobe) {
+      g_static_mdtab_maxprobe = j;
+    }
+  }
+  /* initialize shards */
+  for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
+    strtab_shard *shard = &g_strtab_shard[i];
+    gpr_mu_init(&shard->mu);
+    shard->count = 0;
+    shard->capacity = INITIAL_STRTAB_CAPACITY;
+    shard->strs = gpr_malloc(sizeof(*shard->strs) * shard->capacity);
+    memset(shard->strs, 0, sizeof(*shard->strs) * shard->capacity);
+  }
+  for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
+    mdtab_shard *shard = &g_mdtab_shard[i];
+    gpr_mu_init(&shard->mu);
+    shard->count = 0;
+    shard->free = 0;
+    shard->capacity = INITIAL_MDTAB_CAPACITY;
+    shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
+    memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity);
   }
 }
 
-void grpc_mdctx_global_shutdown(void) {}
+void grpc_mdctx_global_shutdown(void) {
+  size_t i;
+  for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
+    mdtab_shard *shard = &g_mdtab_shard[i];
+    gpr_mu_destroy(&shard->mu);
+    discard_metadata(shard);
+    GPR_ASSERT(shard->count == 0);
+    gpr_free(shard->elems);
+  }
+  for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
+    strtab_shard *shard = &g_strtab_shard[i];
+    gpr_mu_destroy(&shard->mu);
+    GPR_ASSERT(shard->count == 0);
+    gpr_free(shard->strs);
+  }
+}
 
 static int is_mdstr_static(grpc_mdstr *s) {
   return s >= &grpc_static_mdstr_table[0] &&
@@ -204,39 +237,8 @@ static int is_mdelem_static(grpc_mdelem *e) {
          e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 }
 
-static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); }
-
-static void unlock(grpc_mdctx *ctx) {
-  /* If the context has been orphaned we'd like to delete it soon. We check
-     conditions in unlock as it signals the end of mutations on a context.
-
-     We need to ensure all grpc_mdelem and grpc_mdstr elements have been deleted
-     first. This is equivalent to saying that both tables have zero counts,
-     which is equivalent to saying that strtab_count is zero (as mdelem's MUST
-     reference an mdstr for their key and value slots).
-
-     To encourage that to happen, we start discarding zero reference count
-     mdelems on every unlock (instead of the usual 'I'm too loaded' trigger
-     case), since otherwise we can be stuck waiting for a garbage collection
-     that will never happen. */
-  if (ctx->refs == 0) {
-/* uncomment if you're having trouble diagnosing an mdelem leak to make
-   things clearer (slows down destruction a lot, however) */
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-    gc_mdtab(ctx);
-#endif
-    if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
-      discard_metadata(ctx);
-    }
-    if (ctx->strtab_count == 0) {
-      metadata_context_destroy_locked(ctx);
-      return;
-    }
-  }
-  gpr_mu_unlock(&ctx->mu);
-}
-
-static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
+static void ref_md_locked(mdtab_shard *shard,
+                          internal_metadata *md DEBUG_ARGS) {
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
           "ELM   REF:%p:%d->%d: '%s' = '%s'", md,
@@ -246,62 +248,32 @@ static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
 #endif
   if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
-    md->context->mdtab_free--;
+    shard->free--;
   } else {
     GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
   }
 }
 
+#if 0
 grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
   grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
   size_t i, j;
 
   memset(ctx, 0, sizeof(*ctx));
 
-  ctx->refs = 1;
-  ctx->hash_seed = seed;
-  gpr_mu_init(&ctx->mu);
-  ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
-  memset(ctx->strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
-  ctx->strtab_count = 0;
-  ctx->strtab_capacity = INITIAL_STRTAB_CAPACITY;
-  ctx->mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
-  memset(ctx->mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
-  ctx->mdtab_count = 0;
-  ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY;
-  ctx->mdtab_free = 0;
+  g_refs = 1;
+  g_hash_seed = seed;
+  gpr_mu_init(&g_mu);
+  g_strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
+  memset(g_strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
+  g_strtab_count = 0;
+  g_strtab_capacity = INITIAL_STRTAB_CAPACITY;
+  g_mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
+  memset(g_mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
+  g_mdtab_count = 0;
+  g_mdtab_capacity = INITIAL_MDTAB_CAPACITY;
+  g_mdtab_free = 0;
 
-  for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
-    const char *str = grpc_static_metadata_strings[i];
-    gpr_uint32 lup_hash = gpr_murmur_hash3(str, strlen(str), seed);
-    for (j = 0;; j++) {
-      size_t idx = (lup_hash + j) % GPR_ARRAY_SIZE(ctx->static_strtab);
-      if (ctx->static_strtab[idx].mdstr == NULL) {
-        ctx->static_strtab[idx].mdstr = &grpc_static_mdstr_table[i];
-        ctx->static_strtab[idx].hash = lup_hash;
-        break;
-      }
-    }
-    if (j > ctx->static_strtab_maxprobe) {
-      ctx->static_strtab_maxprobe = j;
-    }
-  }
-
-  for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
-    grpc_mdelem *elem = &grpc_static_mdelem_table[i];
-    gpr_uint32 hash = GRPC_MDSTR_KV_HASH(elem->key->hash, elem->value->hash);
-    for (j = 0;; j++) {
-      size_t idx = (hash + j) % GPR_ARRAY_SIZE(ctx->static_mdtab);
-      if (ctx->static_mdtab[idx].mdelem == NULL) {
-        ctx->static_mdtab[idx].mdelem = elem;
-        ctx->static_mdtab[idx].hash = hash;
-        break;
-      }
-    }
-    if (j > ctx->static_mdtab_maxprobe) {
-      ctx->static_mdtab_maxprobe = j;
-    }
-  }
 
   return ctx;
 }
@@ -313,55 +285,20 @@ grpc_mdctx *grpc_mdctx_create(void) {
   return grpc_mdctx_create_with_seed(
       (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
 }
+#endif
 
-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) {
+static void discard_metadata(mdtab_shard *shard) {
   size_t i;
   internal_metadata *next, *cur;
 
-  for (i = 0; i < ctx->mdtab_capacity; i++) {
-    cur = ctx->mdtab[i];
+  for (i = 0; i < shard->capacity; i++) {
+    cur = shard->elems[i];
     while (cur) {
       void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data);
       GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
       next = cur->bucket_next;
-      INTERNAL_STRING_UNREF(cur->key);
-      INTERNAL_STRING_UNREF(cur->value);
+      GRPC_MDSTR_UNREF((grpc_mdstr *)cur->key);
+      GRPC_MDSTR_UNREF((grpc_mdstr *)cur->value);
       if (user_data != NULL) {
         ((destroy_user_data_func)gpr_atm_no_barrier_load(
             &cur->destroy_user_data))(user_data);
@@ -369,29 +306,30 @@ static void discard_metadata(grpc_mdctx *ctx) {
       gpr_mu_destroy(&cur->mu_user_data);
       gpr_free(cur);
       cur = next;
-      ctx->mdtab_free--;
-      ctx->mdtab_count--;
+      shard->free--;
+      shard->count--;
     }
-    ctx->mdtab[i] = NULL;
+    shard->elems[i] = NULL;
   }
 }
 
+#if 0
 static void metadata_context_destroy_locked(grpc_mdctx *ctx) {
-  GPR_ASSERT(ctx->strtab_count == 0);
-  GPR_ASSERT(ctx->mdtab_count == 0);
-  GPR_ASSERT(ctx->mdtab_free == 0);
-  gpr_free(ctx->strtab);
-  gpr_free(ctx->mdtab);
-  gpr_mu_unlock(&ctx->mu);
-  gpr_mu_destroy(&ctx->mu);
+  GPR_ASSERT(g_strtab_count == 0);
+  GPR_ASSERT(g_mdtab_count == 0);
+  GPR_ASSERT(g_mdtab_free == 0);
+  gpr_free(g_strtab);
+  gpr_free(g_mdtab);
+  gpr_mu_unlock(&g_mu);
+  gpr_mu_destroy(&g_mu);
   gpr_free(ctx);
 }
 
 void grpc_mdctx_ref(grpc_mdctx *ctx) {
   GPR_TIMER_BEGIN("grpc_mdctx_ref", 0);
   lock(ctx);
-  GPR_ASSERT(ctx->refs > 0);
-  ctx->refs++;
+  GPR_ASSERT(g_refs > 0);
+  g_refs++;
   unlock(ctx);
   GPR_TIMER_END("grpc_mdctx_ref", 0);
 }
@@ -399,14 +337,15 @@ void grpc_mdctx_ref(grpc_mdctx *ctx) {
 void grpc_mdctx_unref(grpc_mdctx *ctx) {
   GPR_TIMER_BEGIN("grpc_mdctx_unref", 0);
   lock(ctx);
-  GPR_ASSERT(ctx->refs > 0);
-  ctx->refs--;
+  GPR_ASSERT(g_refs > 0);
+  g_refs--;
   unlock(ctx);
   GPR_TIMER_END("grpc_mdctx_unref", 0);
 }
+#endif
 
-static void grow_strtab(grpc_mdctx *ctx) {
-  size_t capacity = ctx->strtab_capacity * 2;
+static void grow_strtab(strtab_shard *shard) {
+  size_t capacity = shard->capacity * 2;
   size_t i;
   internal_string **strtab;
   internal_string *s, *next;
@@ -416,117 +355,94 @@ static void grow_strtab(grpc_mdctx *ctx) {
   strtab = gpr_malloc(sizeof(internal_string *) * capacity);
   memset(strtab, 0, sizeof(internal_string *) * capacity);
 
-  for (i = 0; i < ctx->strtab_capacity; i++) {
-    for (s = ctx->strtab[i]; s; s = next) {
+  for (i = 0; i < shard->capacity; i++) {
+    for (s = shard->strs[i]; s; s = next) {
+      size_t idx = TABLE_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT, capacity);
       next = s->bucket_next;
-      s->bucket_next = strtab[s->hash % capacity];
-      strtab[s->hash % capacity] = s;
+      s->bucket_next = strtab[idx];
+      strtab[idx] = s;
     }
   }
 
-  gpr_free(ctx->strtab);
-  ctx->strtab = strtab;
-  ctx->strtab_capacity = capacity;
+  gpr_free(shard->strs);
+  shard->strs = strtab;
+  shard->capacity = capacity;
 
   GPR_TIMER_END("grow_strtab", 0);
 }
 
-static void internal_destroy_string(internal_string *is) {
+static void internal_destroy_string(strtab_shard *shard, internal_string *is) {
   internal_string **prev_next;
   internal_string *cur;
-  grpc_mdctx *ctx = is->context;
   GPR_TIMER_BEGIN("internal_destroy_string", 0);
   if (is->has_base64_and_huffman_encoded) {
     gpr_slice_unref(is->base64_and_huffman);
   }
-  for (prev_next = &ctx->strtab[is->hash % ctx->strtab_capacity],
+  for (prev_next = &shard->strs[TABLE_IDX(is->hash, LOG2_STRTAB_SHARD_COUNT,
+                                          shard->capacity)],
       cur = *prev_next;
        cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next)
     ;
   *prev_next = cur->bucket_next;
-  ctx->strtab_count--;
+  shard->count--;
   gpr_free(is);
   GPR_TIMER_END("internal_destroy_string", 0);
 }
 
-static void internal_string_ref(internal_string *s DEBUG_ARGS) {
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR   REF:%p:%d->%d: '%s'", s,
-          s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
-#endif
-  ++s->refs;
-}
-
-static void internal_string_unref(internal_string *s DEBUG_ARGS) {
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
-          s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
-#endif
-  GPR_ASSERT(s->refs > 0);
-  if (0 == --s->refs) {
-    internal_destroy_string(s);
-  }
-}
-
 static void slice_ref(void *p) {
   internal_string *is =
       (internal_string *)((char *)p - offsetof(internal_string, refcount));
-  grpc_mdctx *ctx = is->context;
-  GPR_TIMER_BEGIN("slice_ref", 0);
-  lock(ctx);
-  INTERNAL_STRING_REF(is);
-  unlock(ctx);
-  GPR_TIMER_END("slice_ref", 0);
+  GRPC_MDSTR_REF((grpc_mdstr *)(is));
 }
 
 static void slice_unref(void *p) {
   internal_string *is =
       (internal_string *)((char *)p - offsetof(internal_string, refcount));
-  grpc_mdctx *ctx = is->context;
-  GPR_TIMER_BEGIN("slice_unref", 0);
-  lock(ctx);
-  INTERNAL_STRING_UNREF(is);
-  unlock(ctx);
-  GPR_TIMER_END("slice_unref", 0);
+  GRPC_MDSTR_UNREF((grpc_mdstr *)(is));
 }
 
-grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str) {
-  return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str));
+grpc_mdstr *grpc_mdstr_from_string(const char *str) {
+  return grpc_mdstr_from_buffer((const gpr_uint8 *)str, strlen(str));
 }
 
-grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice) {
-  grpc_mdstr *result = grpc_mdstr_from_buffer(ctx, GPR_SLICE_START_PTR(slice),
+grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice) {
+  grpc_mdstr *result = grpc_mdstr_from_buffer(GPR_SLICE_START_PTR(slice),
                                               GPR_SLICE_LENGTH(slice));
   gpr_slice_unref(slice);
   return result;
 }
 
-grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
-                                   size_t length) {
-  gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed);
+grpc_mdstr *grpc_mdstr_from_buffer(const gpr_uint8 *buf, size_t length) {
+  gpr_uint32 hash = gpr_murmur_hash3(buf, length, g_hash_seed);
   internal_string *s;
+  strtab_shard *shard =
+      &g_strtab_shard[SHARD_IDX(hash, LOG2_STRTAB_SHARD_COUNT)];
   size_t i;
+  size_t idx;
 
   GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
 
   /* search for a static string */
-  for (i = 0; i <= ctx->static_strtab_maxprobe; i++) {
-    size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_strtab);
-    static_string *ss = &ctx->static_strtab[idx];
-    if (ss->hash == hash && GPR_SLICE_LENGTH(ss->mdstr->slice) == length &&
-        0 == memcmp(buf, GPR_SLICE_START_PTR(ss->mdstr->slice), length)) {
-      return ss->mdstr;
+  for (i = 0; i <= g_static_strtab_maxprobe; i++) {
+    grpc_mdstr *ss;
+    idx = (hash + i) % GPR_ARRAY_SIZE(g_static_strtab);
+    ss = g_static_strtab[idx];
+    if (ss == NULL) break;
+    if (ss->hash == hash && GPR_SLICE_LENGTH(ss->slice) == length &&
+        0 == memcmp(buf, GPR_SLICE_START_PTR(ss->slice), length)) {
+      return ss;
     }
   }
 
-  lock(ctx);
+  gpr_mu_lock(&shard->mu);
 
   /* search for an existing string */
-  for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
+  idx = TABLE_IDX(hash, LOG2_STRTAB_SHARD_COUNT, shard->capacity);
+  for (s = shard->strs[idx]; s; s = s->bucket_next) {
     if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
         0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
-      INTERNAL_STRING_REF(s);
-      unlock(ctx);
+      GRPC_MDSTR_REF((grpc_mdstr *)s);
+      gpr_mu_unlock(&shard->mu);
       GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
       return (grpc_mdstr *)s;
     }
@@ -536,7 +452,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
   if (length + 1 < GPR_SLICE_INLINED_SIZE) {
     /* string data goes directly into the slice */
     s = gpr_malloc(sizeof(internal_string));
-    s->refs = 1;
+    gpr_atm_rel_store(&s->refcnt, 2);
     s->slice.refcount = NULL;
     memcpy(s->slice.data.inlined.bytes, buf, length);
     s->slice.data.inlined.bytes[length] = 0;
@@ -545,7 +461,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
     /* string data goes after the internal_string header, and we +1 for null
        terminator */
     s = gpr_malloc(sizeof(internal_string) + length + 1);
-    s->refs = 1;
+    gpr_atm_rel_store(&s->refcnt, 2);
     s->refcount.ref = slice_ref;
     s->refcount.unref = slice_unref;
     s->slice.refcount = &s->refcount;
@@ -557,44 +473,43 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
   }
   s->has_base64_and_huffman_encoded = 0;
   s->hash = hash;
-  s->context = ctx;
-  s->bucket_next = ctx->strtab[hash % ctx->strtab_capacity];
-  ctx->strtab[hash % ctx->strtab_capacity] = s;
+  s->bucket_next = shard->strs[idx];
+  shard->strs[idx] = s;
 
-  ctx->strtab_count++;
+  shard->count++;
 
-  if (ctx->strtab_count > ctx->strtab_capacity * 2) {
-    grow_strtab(ctx);
+  if (shard->count > shard->capacity * 2) {
+    grow_strtab(shard);
   }
 
-  unlock(ctx);
+  gpr_mu_unlock(&shard->mu);
   GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
 
   return (grpc_mdstr *)s;
 }
 
-static void gc_mdtab(grpc_mdctx *ctx) {
+static void gc_mdtab(mdtab_shard *shard) {
   size_t i;
   internal_metadata **prev_next;
   internal_metadata *md, *next;
 
   GPR_TIMER_BEGIN("gc_mdtab", 0);
-  for (i = 0; i < ctx->mdtab_capacity; i++) {
-    prev_next = &ctx->mdtab[i];
-    for (md = ctx->mdtab[i]; md; md = next) {
+  for (i = 0; i < shard->capacity; i++) {
+    prev_next = &shard->elems[i];
+    for (md = shard->elems[i]; md; md = next) {
       void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
       next = md->bucket_next;
       if (gpr_atm_acq_load(&md->refcnt) == 0) {
-        INTERNAL_STRING_UNREF(md->key);
-        INTERNAL_STRING_UNREF(md->value);
+        GRPC_MDSTR_UNREF((grpc_mdstr *)md->key);
+        GRPC_MDSTR_UNREF((grpc_mdstr *)md->value);
         if (md->user_data) {
           ((destroy_user_data_func)gpr_atm_no_barrier_load(
               &md->destroy_user_data))(user_data);
         }
         gpr_free(md);
         *prev_next = next;
-        ctx->mdtab_free--;
-        ctx->mdtab_count--;
+        shard->free--;
+        shard->count--;
       } else {
         prev_next = &md->bucket_next;
       }
@@ -603,8 +518,8 @@ static void gc_mdtab(grpc_mdctx *ctx) {
   GPR_TIMER_END("gc_mdtab", 0);
 }
 
-static void grow_mdtab(grpc_mdctx *ctx) {
-  size_t capacity = ctx->mdtab_capacity * 2;
+static void grow_mdtab(mdtab_shard *shard) {
+  size_t capacity = shard->capacity * 2;
   size_t i;
   internal_metadata **mdtab;
   internal_metadata *md, *next;
@@ -615,64 +530,66 @@ static void grow_mdtab(grpc_mdctx *ctx) {
   mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
   memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
 
-  for (i = 0; i < ctx->mdtab_capacity; i++) {
-    for (md = ctx->mdtab[i]; md; md = next) {
+  for (i = 0; i < shard->capacity; i++) {
+    for (md = shard->elems[i]; md; md = next) {
+      size_t idx;
       hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
       next = md->bucket_next;
-      md->bucket_next = mdtab[hash % capacity];
-      mdtab[hash % capacity] = md;
+      idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, capacity);
+      md->bucket_next = mdtab[idx];
+      mdtab[idx] = md;
     }
   }
 
-  gpr_free(ctx->mdtab);
-  ctx->mdtab = mdtab;
-  ctx->mdtab_capacity = capacity;
+  gpr_free(shard->elems);
+  shard->elems = mdtab;
+  shard->capacity = capacity;
 
   GPR_TIMER_END("grow_mdtab", 0);
 }
 
-static void rehash_mdtab(grpc_mdctx *ctx) {
-  if (ctx->mdtab_free > ctx->mdtab_capacity / 4) {
-    gc_mdtab(ctx);
+static void rehash_mdtab(mdtab_shard *shard) {
+  if (shard->free > shard->capacity / 4) {
+    gc_mdtab(shard);
   } else {
-    grow_mdtab(ctx);
+    grow_mdtab(shard);
   }
 }
 
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
-                                               grpc_mdstr *mkey,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
                                                grpc_mdstr *mvalue) {
   internal_string *key = (internal_string *)mkey;
   internal_string *value = (internal_string *)mvalue;
   gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
   internal_metadata *md;
+  mdtab_shard *shard = &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
   size_t i;
-
-  GPR_ASSERT(is_mdstr_static(mkey) || key->context == ctx);
-  GPR_ASSERT(is_mdstr_static(mvalue) || value->context == ctx);
+  size_t idx;
 
   GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
 
   if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) {
-    for (i = 0; i <= ctx->static_mdtab_maxprobe; i++) {
-      size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_mdtab);
-      static_mdelem *smd = &ctx->static_mdtab[idx];
-      if (smd->hash == hash && smd->mdelem->key == mkey &&
-          smd->mdelem->value == mvalue) {
-        return smd->mdelem;
+    for (i = 0; i <= g_static_mdtab_maxprobe; i++) {
+      grpc_mdelem *smd;
+      idx = (hash + i) % GPR_ARRAY_SIZE(g_static_mdtab);
+      smd = g_static_mdtab[idx];
+      if (smd == NULL) break;
+      if (smd->key == mkey && smd->value == mvalue) {
+        return smd;
       }
     }
   }
 
-  lock(ctx);
+  gpr_mu_lock(&shard->mu);
 
+  idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, shard->capacity);
   /* search for an existing pair */
-  for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
+  for (md = shard->elems[idx]; md; md = md->bucket_next) {
     if (md->key == key && md->value == value) {
-      REF_MD_LOCKED(md);
-      INTERNAL_STRING_UNREF(key);
-      INTERNAL_STRING_UNREF(value);
-      unlock(ctx);
+      REF_MD_LOCKED(shard, md);
+      GRPC_MDSTR_UNREF((grpc_mdstr *)key);
+      GRPC_MDSTR_UNREF((grpc_mdstr *)value);
+      gpr_mu_unlock(&shard->mu);
       GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
       return (grpc_mdelem *)md;
     }
@@ -681,12 +598,12 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
   /* not found: create a new pair */
   md = gpr_malloc(sizeof(internal_metadata));
   gpr_atm_rel_store(&md->refcnt, 2);
-  md->context = ctx;
   md->key = key;
   md->value = value;
   md->user_data = 0;
   md->destroy_user_data = 0;
-  md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
+  md->bucket_next = shard->elems[idx];
+  shard->elems[idx] = md;
   gpr_mu_init(&md->mu_user_data);
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(GPR_DEBUG, "ELM   NEW:%p:%d: '%s' = '%s'", md,
@@ -694,40 +611,34 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
           grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
 #endif
-  ctx->mdtab[hash % ctx->mdtab_capacity] = md;
-  ctx->mdtab_count++;
+  shard->count++;
 
-  if (ctx->mdtab_count > ctx->mdtab_capacity * 2) {
-    rehash_mdtab(ctx);
+  if (shard->count > shard->capacity * 2) {
+    rehash_mdtab(shard);
   }
 
-  unlock(ctx);
+  gpr_mu_unlock(&shard->mu);
 
   GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
 
   return (grpc_mdelem *)md;
 }
 
-grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
-                                      const char *value) {
-  return grpc_mdelem_from_metadata_strings(ctx,
-                                           grpc_mdstr_from_string(ctx, key),
-                                           grpc_mdstr_from_string(ctx, value));
+grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value) {
+  return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_string(key),
+                                           grpc_mdstr_from_string(value));
 }
 
-grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
-                                     gpr_slice value) {
-  return grpc_mdelem_from_metadata_strings(ctx, grpc_mdstr_from_slice(ctx, key),
-                                           grpc_mdstr_from_slice(ctx, value));
+grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value) {
+  return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_slice(key),
+                                           grpc_mdstr_from_slice(value));
 }
 
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
-                                                const char *key,
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
                                                 const gpr_uint8 *value,
                                                 size_t value_length) {
   return grpc_mdelem_from_metadata_strings(
-      ctx, grpc_mdstr_from_string(ctx, key),
-      grpc_mdstr_from_buffer(ctx, value, value_length));
+      grpc_mdstr_from_string(key), grpc_mdstr_from_buffer(value, value_length));
 }
 
 grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
@@ -763,14 +674,16 @@ void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
 #endif
   if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
-    grpc_mdctx *ctx = md->context;
+    gpr_uint32 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
+    mdtab_shard *shard =
+        &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
     GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
-    lock(ctx);
+    gpr_mu_lock(&shard->mu);
     if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
-      ctx->mdtab_free++;
+      shard->free++;
       gpr_atm_no_barrier_store(&md->refcnt, 0);
     }
-    unlock(ctx);
+    gpr_mu_unlock(&shard->mu);
     GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
   }
 }
@@ -781,40 +694,31 @@ const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
 
 grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
-  grpc_mdctx *ctx;
   if (is_mdstr_static(gs)) return gs;
-  ctx = s->context;
-  lock(ctx);
-  internal_string_ref(s FWD_DEBUG_ARGS);
-  unlock(ctx);
+  GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) != 0);
   return gs;
 }
 
 void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
-  grpc_mdctx *ctx;
   if (is_mdstr_static(gs)) return;
-  ctx = s->context;
-  lock(ctx);
-  internal_string_unref(s FWD_DEBUG_ARGS);
-  unlock(ctx);
-}
-
-size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *ctx) {
-  return ctx->mdtab_capacity;
-}
-
-size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *ctx) {
-  return ctx->mdtab_count;
-}
-
-size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) {
-  return ctx->mdtab_free;
+  if (2 == gpr_atm_full_fetch_add(&s->refcnt, -1)) {
+    strtab_shard *shard =
+        &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
+    gpr_mu_lock(&shard->mu);
+    if (1 == gpr_atm_no_barrier_load(&s->refcnt)) {
+      internal_destroy_string(shard, s);
+    }
+    gpr_mu_unlock(&shard->mu);
+  }
 }
 
 void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
   internal_metadata *im = (internal_metadata *)md;
   void *result;
+  if (is_mdelem_static(md)) {
+    return (void *)grpc_static_mdelem_user_data[md - grpc_static_mdelem_table];
+  }
   if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
     return (void *)gpr_atm_no_barrier_load(&im->user_data);
   } else {
@@ -845,15 +749,16 @@ void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
 gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
   internal_string *s = (internal_string *)gs;
   gpr_slice slice;
-  grpc_mdctx *ctx = s->context;
-  lock(ctx);
+  strtab_shard *shard =
+      &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
+  gpr_mu_lock(&shard->mu);
   if (!s->has_base64_and_huffman_encoded) {
     s->base64_and_huffman =
         grpc_chttp2_base64_encode_and_huffman_compress(s->slice);
     s->has_base64_and_huffman_encoded = 1;
   }
   slice = s->base64_and_huffman;
-  unlock(ctx);
+  gpr_mu_unlock(&shard->mu);
   return slice;
 }
 
@@ -885,52 +790,6 @@ 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 0f12a7392c7d9d3335751f02a6b21bda6c4b392b..c1071e4e16faf3dc7de6522cf237500571a0bdf4 100644
--- a/src/core/transport/metadata.h
+++ b/src/core/transport/metadata.h
@@ -68,7 +68,6 @@
    declared here - in which case those functions are effectively no-ops. */
 
 /* Forward declarations */
-typedef struct grpc_mdctx grpc_mdctx;
 typedef struct grpc_mdstr grpc_mdstr;
 typedef struct grpc_mdelem grpc_mdelem;
 
@@ -87,27 +86,18 @@ struct grpc_mdelem {
   /* there is a private part to this in metadata.c */
 };
 
-/* Create/orphan a metadata context */
-grpc_mdctx *grpc_mdctx_create(void);
-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);
-size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *mdctx);
-size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *mdctx);
+size_t grpc_mdctx_get_mdtab_capacity_test_only(void);
+size_t grpc_mdctx_get_mdtab_count_test_only(void);
+size_t grpc_mdctx_get_mdtab_free_test_only(void);
 
 /* Constructors for grpc_mdstr instances; take a variety of data types that
    clients may have handy */
-grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str);
+grpc_mdstr *grpc_mdstr_from_string(const char *str);
 /* Unrefs the slice. */
-grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice);
-grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *str,
-                                   size_t length);
+grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice);
+grpc_mdstr *grpc_mdstr_from_buffer(const gpr_uint8 *str, size_t length);
 
 /* Returns a borrowed slice from the mdstr with its contents base64 encoded
    and huffman compressed */
@@ -115,15 +105,12 @@ gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *str);
 
 /* Constructors for grpc_mdelem instances; take a variety of data types that
    clients may have handy */
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, grpc_mdstr *key,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *key,
                                                grpc_mdstr *value);
-grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
-                                      const char *value);
+grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value);
 /* Unrefs the slices. */
-grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
-                                     gpr_slice value);
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
-                                                const char *key,
+grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value);
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
                                                 const gpr_uint8 *value,
                                                 size_t value_length);
 
@@ -163,25 +150,6 @@ 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 fb7c5006e11ced21d6d95279a0d80e4651a84990..e7aff325c29356a9cbc553f3f984f00327335d16 100644
--- a/src/core/transport/static_metadata.c
+++ b/src/core/transport/static_metadata.c
@@ -46,17 +46,23 @@
 grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 
 grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+gpr_uintptr grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 3, 7, 5, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 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};
+        11, 33, 10, 33, 12, 33, 12, 47, 13, 33, 14, 33, 15, 33, 16, 33, 17, 33,
+        19, 33, 20, 33, 21, 33, 22, 33, 23, 33, 24, 33, 25, 33, 26, 33, 27, 33,
+        28, 18, 28, 33, 29, 33, 30, 33, 34, 33, 35, 33, 36, 33, 37, 33, 40, 31,
+        40, 32, 40, 46, 40, 51, 40, 52, 40, 53, 40, 54, 41, 31, 41, 46, 41, 51,
+        44, 0,  44, 1,  44, 2,  48, 33, 55, 33, 56, 33, 57, 33, 58, 33, 59, 33,
+        60, 33, 61, 33, 62, 33, 63, 33, 64, 38, 64, 66, 65, 76, 65, 77, 67, 33,
+        68, 33, 69, 33, 70, 33, 71, 33, 72, 33, 73, 39, 73, 49, 73, 50, 74, 33,
+        75, 33, 78, 3,  78, 4,  78, 5,  78, 6,  78, 7,  78, 8,  78, 9,  79, 33,
+        80, 81, 82, 33, 83, 33, 84, 33, 85, 33, 86, 33};
 
 const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "0",
@@ -91,6 +97,7 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "cookie",
     "date",
     "deflate",
+    "deflate,gzip",
     "",
     "etag",
     "expect",
@@ -110,6 +117,9 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "http",
     "https",
     "identity",
+    "identity,deflate",
+    "identity,deflate,gzip",
+    "identity,gzip",
     "if-match",
     "if-modified-since",
     "if-none-match",
@@ -142,3 +152,6 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "vary",
     "via",
     "www-authenticate"};
+
+const gpr_uint8 grpc_static_accept_encoding_metadata[8] = {0,  29, 26, 30,
+                                                           28, 32, 27, 31};
diff --git a/src/core/transport/static_metadata.h b/src/core/transport/static_metadata.h
index 671801636b8f2c22417610f264b4bd0035238582..e9055fb45cdbbdb7e611f1079ccaa829abff3718 100644
--- a/src/core/transport/static_metadata.h
+++ b/src/core/transport/static_metadata.h
@@ -46,7 +46,7 @@
 
 #include "src/core/transport/metadata.h"
 
-#define GRPC_STATIC_MDSTR_COUNT 83
+#define GRPC_STATIC_MDSTR_COUNT 87
 extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 /* "0" */
 #define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0])
@@ -112,111 +112,121 @@ extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 #define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[30])
 /* "deflate" */
 #define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[31])
+/* "deflate,gzip" */
+#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[32])
 /* "" */
-#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[32])
+#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[33])
 /* "etag" */
-#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[33])
+#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[34])
 /* "expect" */
-#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[34])
+#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[35])
 /* "expires" */
-#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[35])
+#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[36])
 /* "from" */
-#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[36])
+#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[37])
 /* "GET" */
-#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[37])
+#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[38])
 /* "grpc" */
-#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[38])
+#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[39])
 /* "grpc-accept-encoding" */
-#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[39])
+#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[40])
 /* "grpc-encoding" */
-#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[40])
+#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[41])
 /* "grpc-internal-encoding-request" */
-#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[41])
+#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[42])
 /* "grpc-message" */
-#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[42])
+#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[43])
 /* "grpc-status" */
-#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[43])
+#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[44])
 /* "grpc-timeout" */
-#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[44])
+#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[45])
 /* "gzip" */
-#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[45])
+#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[46])
 /* "gzip, deflate" */
-#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[46])
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[47])
 /* "host" */
-#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[47])
+#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[48])
 /* "http" */
-#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[48])
+#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[49])
 /* "https" */
-#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[49])
+#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[50])
 /* "identity" */
-#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[50])
+#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[51])
+/* "identity,deflate" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[52])
+/* "identity,deflate,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
+  (&grpc_static_mdstr_table[53])
+/* "identity,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[54])
 /* "if-match" */
-#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[51])
+#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[55])
 /* "if-modified-since" */
-#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[52])
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[56])
 /* "if-none-match" */
-#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[53])
+#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[57])
 /* "if-range" */
-#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[54])
+#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[58])
 /* "if-unmodified-since" */
-#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[55])
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[59])
 /* "last-modified" */
-#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[56])
+#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[60])
 /* "link" */
-#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[57])
+#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[61])
 /* "location" */
-#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[58])
+#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[62])
 /* "max-forwards" */
-#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[59])
+#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[63])
 /* ":method" */
-#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[60])
+#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[64])
 /* ":path" */
-#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[61])
+#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[65])
 /* "POST" */
-#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[62])
+#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[66])
 /* "proxy-authenticate" */
-#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[63])
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[67])
 /* "proxy-authorization" */
-#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[64])
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[68])
 /* "range" */
-#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[65])
+#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[69])
 /* "referer" */
-#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[66])
+#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[70])
 /* "refresh" */
-#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[67])
+#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[71])
 /* "retry-after" */
-#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[68])
+#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[72])
 /* ":scheme" */
-#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[69])
+#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[73])
 /* "server" */
-#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[70])
+#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[74])
 /* "set-cookie" */
-#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[71])
+#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[75])
 /* "/" */
-#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[72])
+#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[76])
 /* "/index.html" */
-#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[73])
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[77])
 /* ":status" */
-#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[74])
+#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[78])
 /* "strict-transport-security" */
-#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[75])
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[79])
 /* "te" */
-#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[76])
+#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[80])
 /* "trailers" */
-#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[77])
+#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[81])
 /* "transfer-encoding" */
-#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[78])
+#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[82])
 /* "user-agent" */
-#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[79])
+#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[83])
 /* "vary" */
-#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[80])
+#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[84])
 /* "via" */
-#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[81])
+#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[85])
 /* "www-authenticate" */
-#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[82])
+#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[86])
 
-#define GRPC_STATIC_MDELEM_COUNT 71
+#define GRPC_STATIC_MDELEM_COUNT 78
 extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+extern gpr_uintptr grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
 /* "accept-charset": "" */
 #define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY (&grpc_static_mdelem_table[0])
 /* "accept": "" */
@@ -272,98 +282,121 @@ extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 #define GRPC_MDELEM_EXPIRES_EMPTY (&grpc_static_mdelem_table[24])
 /* "from": "" */
 #define GRPC_MDELEM_FROM_EMPTY (&grpc_static_mdelem_table[25])
+/* "grpc-accept-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE (&grpc_static_mdelem_table[26])
+/* "grpc-accept-encoding": "deflate,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE_COMMA_GZIP \
+  (&grpc_static_mdelem_table[27])
+/* "grpc-accept-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_GZIP (&grpc_static_mdelem_table[28])
+/* "grpc-accept-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY \
+  (&grpc_static_mdelem_table[29])
+/* "grpc-accept-encoding": "identity,deflate" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE \
+  (&grpc_static_mdelem_table[30])
+/* "grpc-accept-encoding": "identity,deflate,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
+  (&grpc_static_mdelem_table[31])
+/* "grpc-accept-encoding": "identity,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \
+  (&grpc_static_mdelem_table[32])
 /* "grpc-encoding": "deflate" */
-#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE (&grpc_static_mdelem_table[26])
+#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE (&grpc_static_mdelem_table[33])
 /* "grpc-encoding": "gzip" */
-#define GRPC_MDELEM_GRPC_ENCODING_GZIP (&grpc_static_mdelem_table[27])
+#define GRPC_MDELEM_GRPC_ENCODING_GZIP (&grpc_static_mdelem_table[34])
 /* "grpc-encoding": "identity" */
-#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY (&grpc_static_mdelem_table[28])
+#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY (&grpc_static_mdelem_table[35])
 /* "grpc-status": "0" */
-#define GRPC_MDELEM_GRPC_STATUS_0 (&grpc_static_mdelem_table[29])
+#define GRPC_MDELEM_GRPC_STATUS_0 (&grpc_static_mdelem_table[36])
 /* "grpc-status": "1" */
-#define GRPC_MDELEM_GRPC_STATUS_1 (&grpc_static_mdelem_table[30])
+#define GRPC_MDELEM_GRPC_STATUS_1 (&grpc_static_mdelem_table[37])
 /* "grpc-status": "2" */
-#define GRPC_MDELEM_GRPC_STATUS_2 (&grpc_static_mdelem_table[31])
+#define GRPC_MDELEM_GRPC_STATUS_2 (&grpc_static_mdelem_table[38])
 /* "host": "" */
-#define GRPC_MDELEM_HOST_EMPTY (&grpc_static_mdelem_table[32])
+#define GRPC_MDELEM_HOST_EMPTY (&grpc_static_mdelem_table[39])
 /* "if-match": "" */
-#define GRPC_MDELEM_IF_MATCH_EMPTY (&grpc_static_mdelem_table[33])
+#define GRPC_MDELEM_IF_MATCH_EMPTY (&grpc_static_mdelem_table[40])
 /* "if-modified-since": "" */
-#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[34])
+#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[41])
 /* "if-none-match": "" */
-#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY (&grpc_static_mdelem_table[35])
+#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY (&grpc_static_mdelem_table[42])
 /* "if-range": "" */
-#define GRPC_MDELEM_IF_RANGE_EMPTY (&grpc_static_mdelem_table[36])
+#define GRPC_MDELEM_IF_RANGE_EMPTY (&grpc_static_mdelem_table[43])
 /* "if-unmodified-since": "" */
-#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[37])
+#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[44])
 /* "last-modified": "" */
-#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[38])
+#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45])
 /* "link": "" */
-#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[39])
+#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[46])
 /* "location": "" */
-#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[40])
+#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[47])
 /* "max-forwards": "" */
-#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[41])
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[48])
 /* ":method": "GET" */
-#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[42])
+#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[49])
 /* ":method": "POST" */
-#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[43])
+#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[50])
 /* ":path": "/" */
-#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[44])
+#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[51])
 /* ":path": "/index.html" */
-#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[45])
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[52])
 /* "proxy-authenticate": "" */
-#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[46])
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[53])
 /* "proxy-authorization": "" */
-#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[47])
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[54])
 /* "range": "" */
-#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[48])
+#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[55])
 /* "referer": "" */
-#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[49])
+#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[56])
 /* "refresh": "" */
-#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[50])
+#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[57])
 /* "retry-after": "" */
-#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[51])
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[58])
 /* ":scheme": "grpc" */
-#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[52])
+#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[59])
 /* ":scheme": "http" */
-#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[53])
+#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[60])
 /* ":scheme": "https" */
-#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[54])
+#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[61])
 /* "server": "" */
-#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[55])
+#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[62])
 /* "set-cookie": "" */
-#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[56])
+#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[63])
 /* ":status": "200" */
-#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[57])
+#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[64])
 /* ":status": "204" */
-#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[58])
+#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[65])
 /* ":status": "206" */
-#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[59])
+#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[66])
 /* ":status": "304" */
-#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[60])
+#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[67])
 /* ":status": "400" */
-#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[61])
+#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[68])
 /* ":status": "404" */
-#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[62])
+#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[69])
 /* ":status": "500" */
-#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[63])
+#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[70])
 /* "strict-transport-security": "" */
 #define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
-  (&grpc_static_mdelem_table[64])
+  (&grpc_static_mdelem_table[71])
 /* "te": "trailers" */
-#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[65])
+#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[72])
 /* "transfer-encoding": "" */
-#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[66])
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[73])
 /* "user-agent": "" */
-#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[67])
+#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[74])
 /* "vary": "" */
-#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[68])
+#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[75])
 /* "via": "" */
-#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[69])
+#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[76])
 /* "www-authenticate": "" */
-#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[70])
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[77])
 
-const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
-const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];
+extern const gpr_uint8
+    grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
+extern const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];
+extern const gpr_uint8 grpc_static_accept_encoding_metadata[8];
+#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) \
+  (&grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]])
 #endif /* GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H */
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index fb2cd2d85b2273a6944cb340a8bfedfacd9f016d..e1a4b8ed909f4095756eb478796e25e698ba12ca 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -64,14 +64,13 @@ static void done_write(grpc_exec_ctx *exec_ctx, void *arg, int success) {
   gpr_event_set(&a->done_write, (void *)1);
 }
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   thd_args *a = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, a->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(a->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -84,7 +83,6 @@ void grpc_run_bad_client_test(grpc_bad_client_server_side_validator validator,
   gpr_thd_id id;
   char *hex;
   grpc_transport *transport;
-  grpc_mdctx *mdctx;
   gpr_slice slice =
       gpr_slice_from_copied_buffer(client_payload, client_payload_length);
   gpr_slice_buffer outgoing;
@@ -102,8 +100,6 @@ void grpc_run_bad_client_test(grpc_bad_client_server_side_validator validator,
   /* Init grpc */
   grpc_init();
 
-  mdctx = grpc_mdctx_create();
-
   /* Create endpoints */
   sfd = grpc_iomgr_create_endpoint_pair("fixture", 65536);
 
@@ -115,9 +111,8 @@ void grpc_run_bad_client_test(grpc_bad_client_server_side_validator validator,
   a.validator = validator;
   grpc_server_register_completion_queue(a.server, a.cq, NULL);
   grpc_server_start(a.server);
-  transport =
-      grpc_create_chttp2_transport(&exec_ctx, NULL, sfd.server, mdctx, 0);
-  server_setup_transport(&a, transport, mdctx);
+  transport = grpc_create_chttp2_transport(&exec_ctx, NULL, sfd.server, 0);
+  server_setup_transport(&a, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 2a05c608bb21978aa37e743852d0a078203c47bf..9dbb87930034c1d9f3c585fb0001b8470686da49 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -93,13 +93,10 @@ static void test_create_channel_stack(void) {
   grpc_call_element *call_elem;
   grpc_arg arg;
   grpc_channel_args chan_args;
-  grpc_mdctx *metadata_context;
   int *channel_data;
   int *call_data;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  metadata_context = grpc_mdctx_create();
-
   arg.type = GRPC_ARG_INTEGER;
   arg.key = "test_key";
   arg.value.integer = 42;
@@ -117,7 +114,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,
-                       metadata_context, call_stack);
+                       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);
@@ -133,8 +130,6 @@ static void test_create_channel_stack(void) {
   grpc_channel_stack_destroy(&exec_ctx, channel_stack);
   gpr_free(channel_stack);
 
-  grpc_mdctx_unref(metadata_context);
-
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
diff --git a/test/core/end2end/fixtures/h2_sockpair+trace.c b/test/core/end2end/fixtures/h2_sockpair+trace.c
index 1f5051f0aba91b133cf99cee2782e27dc4c28bf8..ccc8631d9419f9c4b41ab168ffcf70659064572e 100644
--- a/test/core/end2end/fixtures/h2_sockpair+trace.c
+++ b/test/core/end2end/fixtures/h2_sockpair+trace.c
@@ -57,14 +57,13 @@
 /* chttp2 transport that is immediately available (used for testing
    connected_channel without a client_channel */
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   grpc_end2end_test_fixture *f = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, f->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(f->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -75,17 +74,15 @@ typedef struct {
 } sp_client_setup;
 
 static void client_setup_transport(grpc_exec_ctx *exec_ctx, void *ts,
-                                   grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+                                   grpc_transport *transport) {
   sp_client_setup *cs = ts;
 
   const grpc_channel_filter *filters[] = {&grpc_http_client_filter,
                                           &grpc_compress_filter,
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
-  grpc_channel *channel =
-      grpc_channel_create_from_filters(exec_ctx, "socketpair-target", filters,
-                                       nfilters, cs->client_args, mdctx, 1);
+  grpc_channel *channel = grpc_channel_create_from_filters(
+      exec_ctx, "socketpair-target", filters, nfilters, cs->client_args, 1);
 
   cs->f->client = channel;
 
@@ -112,13 +109,12 @@ static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   sp_client_setup cs;
   cs.client_args = client_args;
   cs.f = f;
-  transport = grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client,
-                                           mdctx, 1);
-  client_setup_transport(&exec_ctx, &cs, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
+  client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -128,15 +124,14 @@ static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
                                           grpc_channel_args *server_args) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport;
   GPR_ASSERT(!f->server);
   f->server = grpc_server_create_from_filters(NULL, 0, server_args);
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   grpc_server_start(f->server);
-  transport = grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server,
-                                           mdctx, 0);
-  server_setup_transport(f, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
+  server_setup_transport(f, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/end2end/fixtures/h2_sockpair.c b/test/core/end2end/fixtures/h2_sockpair.c
index b61fe9861074a63cb5167e1ca4fa0b5b37058abc..a6a84c9b1a9af4d2feafc8044b1cc5e52003ee4c 100644
--- a/test/core/end2end/fixtures/h2_sockpair.c
+++ b/test/core/end2end/fixtures/h2_sockpair.c
@@ -56,14 +56,13 @@
 /* chttp2 transport that is immediately available (used for testing
    connected_channel without a client_channel */
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   grpc_end2end_test_fixture *f = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, f->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(f->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -74,17 +73,15 @@ typedef struct {
 } sp_client_setup;
 
 static void client_setup_transport(grpc_exec_ctx *exec_ctx, void *ts,
-                                   grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+                                   grpc_transport *transport) {
   sp_client_setup *cs = ts;
 
   const grpc_channel_filter *filters[] = {&grpc_http_client_filter,
                                           &grpc_compress_filter,
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
-  grpc_channel *channel =
-      grpc_channel_create_from_filters(exec_ctx, "socketpair-target", filters,
-                                       nfilters, cs->client_args, mdctx, 1);
+  grpc_channel *channel = grpc_channel_create_from_filters(
+      exec_ctx, "socketpair-target", filters, nfilters, cs->client_args, 1);
 
   cs->f->client = channel;
 
@@ -111,13 +108,12 @@ static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   sp_client_setup cs;
   cs.client_args = client_args;
   cs.f = f;
-  transport = grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client,
-                                           mdctx, 1);
-  client_setup_transport(&exec_ctx, &cs, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
+  client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -127,15 +123,14 @@ static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
                                           grpc_channel_args *server_args) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport;
   GPR_ASSERT(!f->server);
   f->server = grpc_server_create_from_filters(NULL, 0, server_args);
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   grpc_server_start(f->server);
-  transport = grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server,
-                                           mdctx, 0);
-  server_setup_transport(f, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
+  server_setup_transport(f, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/end2end/fixtures/h2_sockpair_1byte.c b/test/core/end2end/fixtures/h2_sockpair_1byte.c
index 9f0fd2ea9a510321f230ee94e69e5ff30c0e9664..4b8f9054ef5936970e819a5904deea84adbf5058 100644
--- a/test/core/end2end/fixtures/h2_sockpair_1byte.c
+++ b/test/core/end2end/fixtures/h2_sockpair_1byte.c
@@ -56,14 +56,13 @@
 /* chttp2 transport that is immediately available (used for testing
    connected_channel without a client_channel */
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   grpc_end2end_test_fixture *f = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, f->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(f->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -74,17 +73,15 @@ typedef struct {
 } sp_client_setup;
 
 static void client_setup_transport(grpc_exec_ctx *exec_ctx, void *ts,
-                                   grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+                                   grpc_transport *transport) {
   sp_client_setup *cs = ts;
 
   const grpc_channel_filter *filters[] = {&grpc_http_client_filter,
                                           &grpc_compress_filter,
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
-  grpc_channel *channel =
-      grpc_channel_create_from_filters(exec_ctx, "socketpair-target", filters,
-                                       nfilters, cs->client_args, mdctx, 1);
+  grpc_channel *channel = grpc_channel_create_from_filters(
+      exec_ctx, "socketpair-target", filters, nfilters, cs->client_args, 1);
 
   cs->f->client = channel;
 
@@ -111,13 +108,12 @@ static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   sp_client_setup cs;
   cs.client_args = client_args;
   cs.f = f;
-  transport = grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client,
-                                           mdctx, 1);
-  client_setup_transport(&exec_ctx, &cs, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
+  client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -127,15 +123,14 @@ static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
                                           grpc_channel_args *server_args) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport;
   GPR_ASSERT(!f->server);
   f->server = grpc_server_create_from_filters(NULL, 0, server_args);
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   grpc_server_start(f->server);
-  transport = grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server,
-                                           mdctx, 0);
-  server_setup_transport(f, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
+  server_setup_transport(f, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/end2end/fixtures/h2_uchannel.c b/test/core/end2end/fixtures/h2_uchannel.c
index 5fed175f9d5407faf3ebee60b5bb7d9c17a11ea0..ee4a60c29aae99a447a94657a5b4fe3018f91884 100644
--- a/test/core/end2end/fixtures/h2_uchannel.c
+++ b/test/core/end2end/fixtures/h2_uchannel.c
@@ -66,8 +66,6 @@ typedef struct {
 
   grpc_endpoint *tcp;
 
-  grpc_mdctx *mdctx;
-
   grpc_closure connected;
 } connector;
 
@@ -79,7 +77,6 @@ static void connector_ref(grpc_connector *con) {
 static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
   connector *c = (connector *)con;
   if (gpr_unref(&c->refs)) {
-    grpc_mdctx_unref(c->mdctx);
     gpr_free(c);
   }
 }
@@ -89,8 +86,8 @@ static void connected(grpc_exec_ctx *exec_ctx, void *arg, int success) {
   grpc_closure *notify;
   grpc_endpoint *tcp = c->tcp;
   if (tcp != NULL) {
-    c->result->transport = grpc_create_chttp2_transport(
-        exec_ctx, c->args.channel_args, tcp, c->mdctx, 1);
+    c->result->transport =
+        grpc_create_chttp2_transport(exec_ctx, c->args.channel_args, tcp, 1);
     grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL,
                                         0);
     GPR_ASSERT(c->result->transport);
@@ -130,7 +127,6 @@ static const grpc_connector_vtable connector_vtable = {
 typedef struct {
   grpc_subchannel_factory base;
   gpr_refcount refs;
-  grpc_mdctx *mdctx;
   grpc_channel_args *merge_args;
   grpc_channel *master;
   grpc_subchannel **sniffed_subchannel;
@@ -147,7 +143,6 @@ static void subchannel_factory_unref(grpc_exec_ctx *exec_ctx,
   if (gpr_unref(&f->refs)) {
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory");
     grpc_channel_args_destroy(f->merge_args);
-    grpc_mdctx_unref(f->mdctx);
     gpr_free(f);
   }
 }
@@ -162,10 +157,7 @@ static grpc_subchannel *subchannel_factory_create_subchannel(
   grpc_subchannel *s;
   memset(c, 0, sizeof(*c));
   c->base.vtable = &connector_vtable;
-  c->mdctx = f->mdctx;
-  grpc_mdctx_ref(c->mdctx);
   gpr_ref_init(&c->refs, 1);
-  args->mdctx = f->mdctx;
   args->args = final_args;
   args->master = f->master;
   s = grpc_subchannel_create(&c->base, args);
@@ -188,22 +180,19 @@ grpc_channel *channel_create(const char *target, const grpc_channel_args *args,
   const grpc_channel_filter *filters[MAX_FILTERS];
   grpc_resolver *resolver;
   subchannel_factory *f;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   size_t n = 0;
 
   filters[n++] = &grpc_client_channel_filter;
   GPR_ASSERT(n <= MAX_FILTERS);
 
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, n, args, 1);
 
   f = gpr_malloc(sizeof(*f));
   f->sniffed_subchannel = sniffed_subchannel;
   f->base.vtable = &test_subchannel_factory_vtable;
   gpr_ref_init(&f->refs, 1);
-  grpc_mdctx_ref(mdctx);
-  f->mdctx = mdctx;
   f->merge_args = grpc_channel_args_copy(args);
   f->master = channel;
   GRPC_CHANNEL_INTERNAL_REF(f->master, "test_subchannel_factory");
@@ -263,9 +252,7 @@ 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,
-      grpc_channel_get_metadata_context(ffd->master_channel));
+  f->client = grpc_client_uchannel_create(ffd->sniffed_subchannel, client_args);
   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_encoder_test.c b/test/core/transport/chttp2/hpack_encoder_test.c
index 30cb6c1d17674906c96035b29b1ca24b67854072..64ea1e3f1444d6b25fa6eea8034624862f266ca4 100644
--- a/test/core/transport/chttp2/hpack_encoder_test.c
+++ b/test/core/transport/chttp2/hpack_encoder_test.c
@@ -46,7 +46,6 @@
 
 #define TEST(x) run_test(x, #x)
 
-grpc_mdctx *g_mdctx;
 grpc_chttp2_hpack_compressor g_compressor;
 int g_failure = 0;
 
@@ -76,7 +75,7 @@ static void verify(size_t window_available, int eof, size_t expect_window_used,
       e[i - 1].next = &e[i];
       e[i].prev = &e[i - 1];
     }
-    e[i].md = grpc_mdelem_from_strings(g_mdctx, key, value);
+    e[i].md = grpc_mdelem_from_strings(key, value);
   }
   e[0].prev = NULL;
   e[nheaders - 1].next = NULL;
@@ -181,11 +180,9 @@ static void test_decode_table_overflow(void) {
 
 static void run_test(void (*test)(), const char *name) {
   gpr_log(GPR_INFO, "RUN TEST: %s", name);
-  g_mdctx = grpc_mdctx_create_with_seed(0);
-  grpc_chttp2_hpack_compressor_init(&g_compressor, g_mdctx);
+  grpc_chttp2_hpack_compressor_init(&g_compressor);
   test();
   grpc_chttp2_hpack_compressor_destroy(&g_compressor);
-  grpc_mdctx_unref(g_mdctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
index a790b461b680a378750de4a507202ebb6c224dc2..3acbc06349a16186b814ba7955101ad2065a9504 100644
--- a/test/core/transport/chttp2/hpack_parser_test.c
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -91,9 +91,8 @@ static void test_vector(grpc_chttp2_hpack_parser *parser,
 
 static void test_vectors(grpc_slice_split_mode mode) {
   grpc_chttp2_hpack_parser parser;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   /* D.2.1 */
   test_vector(&parser, mode,
               "400a 6375 7374 6f6d 2d6b 6579 0d63 7573"
@@ -111,7 +110,7 @@ static void test_vectors(grpc_slice_split_mode mode) {
   test_vector(&parser, mode, "82", ":method", "GET", NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   /* D.3.1 */
   test_vector(&parser, mode,
               "8286 8441 0f77 7777 2e65 7861 6d70 6c65"
@@ -131,7 +130,7 @@ static void test_vectors(grpc_slice_split_mode mode) {
               NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   /* D.4.1 */
   test_vector(&parser, mode,
               "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4"
@@ -151,7 +150,7 @@ static void test_vectors(grpc_slice_split_mode mode) {
               NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   parser.table.max_bytes = 256;
   /* D.5.1 */
   test_vector(&parser, mode,
@@ -184,7 +183,7 @@ static void test_vectors(grpc_slice_split_mode mode) {
               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   parser.table.max_bytes = 256;
   /* D.6.1 */
   test_vector(&parser, mode,
@@ -213,7 +212,6 @@ static void test_vectors(grpc_slice_split_mode mode) {
               "set-cookie",
               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
-  grpc_mdctx_unref(mdctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
index adc69bf31437f48d6ae57d3f1a9510af7f532e78..468e93b773ebbde0b900c5119610ba45a19789fa 100644
--- a/test/core/transport/chttp2/hpack_table_test.c
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -60,10 +60,8 @@ static void assert_index(const grpc_chttp2_hptbl *tbl, gpr_uint32 idx,
 
 static void test_static_lookup(void) {
   grpc_chttp2_hptbl tbl;
-  grpc_mdctx *mdctx;
 
-  mdctx = grpc_mdctx_create();
-  grpc_chttp2_hptbl_init(&tbl, mdctx);
+  grpc_chttp2_hptbl_init(&tbl);
 
   LOG_TEST("test_static_lookup");
   assert_index(&tbl, 1, ":authority", "");
@@ -129,7 +127,6 @@ static void test_static_lookup(void) {
   assert_index(&tbl, 61, "www-authenticate", "");
 
   grpc_chttp2_hptbl_destroy(&tbl);
-  grpc_mdctx_unref(mdctx);
 }
 
 static void test_many_additions(void) {
@@ -137,17 +134,15 @@ static void test_many_additions(void) {
   int i;
   char *key;
   char *value;
-  grpc_mdctx *mdctx;
 
   LOG_TEST("test_many_additions");
 
-  mdctx = grpc_mdctx_create();
-  grpc_chttp2_hptbl_init(&tbl, mdctx);
+  grpc_chttp2_hptbl_init(&tbl);
 
   for (i = 0; i < 1000000; i++) {
     gpr_asprintf(&key, "K:%d", i);
     gpr_asprintf(&value, "VALUE:%d", i);
-    grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, key, value));
+    grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(key, value));
     assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
     gpr_free(key);
     gpr_free(value);
@@ -161,13 +156,12 @@ static void test_many_additions(void) {
   }
 
   grpc_chttp2_hptbl_destroy(&tbl);
-  grpc_mdctx_unref(mdctx);
 }
 
 static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
                                                  const char *key,
                                                  const char *value) {
-  grpc_mdelem *md = grpc_mdelem_from_strings(tbl->mdctx, key, value);
+  grpc_mdelem *md = grpc_mdelem_from_strings(key, value);
   grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
   GRPC_MDELEM_UNREF(md);
   return r;
@@ -177,16 +171,14 @@ static void test_find(void) {
   grpc_chttp2_hptbl tbl;
   int i;
   char buffer[32];
-  grpc_mdctx *mdctx;
   grpc_chttp2_hptbl_find_result r;
 
   LOG_TEST("test_find");
 
-  mdctx = grpc_mdctx_create();
-  grpc_chttp2_hptbl_init(&tbl, mdctx);
-  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "xyz"));
-  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "123"));
-  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "x", "1"));
+  grpc_chttp2_hptbl_init(&tbl);
+  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings("abc", "xyz"));
+  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings("abc", "123"));
+  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings("x", "1"));
 
   r = find_simple(&tbl, "abc", "123");
   GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
@@ -235,8 +227,7 @@ static void test_find(void) {
   /* overflow the string buffer, check find still works */
   for (i = 0; i < 10000; i++) {
     gpr_ltoa(i, buffer);
-    grpc_chttp2_hptbl_add(&tbl,
-                          grpc_mdelem_from_strings(mdctx, "test", buffer));
+    grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings("test", buffer));
   }
 
   r = find_simple(&tbl, "abc", "123");
@@ -265,7 +256,6 @@ static void test_find(void) {
   GPR_ASSERT(r.has_value == 0);
 
   grpc_chttp2_hptbl_destroy(&tbl);
-  grpc_mdctx_unref(mdctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c
index 875ab3d77b4b0d3b755c38676b0c682b88948e19..9c1eae95528829028e4ced9279f23ff847a6406e 100644
--- a/test/core/transport/metadata_test.c
+++ b/test/core/transport/metadata_test.c
@@ -50,44 +50,39 @@
 #define MANY 10000
 
 static void test_no_op(void) {
-  grpc_mdctx *ctx;
-
   LOG_TEST("test_no_op");
-
-  ctx = grpc_mdctx_create();
-  grpc_mdctx_unref(ctx);
+  grpc_init();
+  grpc_shutdown();
 }
 
 static void test_create_string(void) {
-  grpc_mdctx *ctx;
   grpc_mdstr *s1, *s2, *s3;
 
   LOG_TEST("test_create_string");
 
-  ctx = grpc_mdctx_create();
-  s1 = grpc_mdstr_from_string(ctx, "hello");
-  s2 = grpc_mdstr_from_string(ctx, "hello");
-  s3 = grpc_mdstr_from_string(ctx, "very much not hello");
+  grpc_init();
+  s1 = grpc_mdstr_from_string("hello");
+  s2 = grpc_mdstr_from_string("hello");
+  s3 = grpc_mdstr_from_string("very much not hello");
   GPR_ASSERT(s1 == s2);
   GPR_ASSERT(s3 != s1);
   GPR_ASSERT(gpr_slice_str_cmp(s1->slice, "hello") == 0);
   GPR_ASSERT(gpr_slice_str_cmp(s3->slice, "very much not hello") == 0);
   GRPC_MDSTR_UNREF(s1);
   GRPC_MDSTR_UNREF(s2);
-  grpc_mdctx_unref(ctx);
   GRPC_MDSTR_UNREF(s3);
+  grpc_shutdown();
 }
 
 static void test_create_metadata(void) {
-  grpc_mdctx *ctx;
   grpc_mdelem *m1, *m2, *m3;
 
   LOG_TEST("test_create_metadata");
 
-  ctx = grpc_mdctx_create();
-  m1 = grpc_mdelem_from_strings(ctx, "a", "b");
-  m2 = grpc_mdelem_from_strings(ctx, "a", "b");
-  m3 = grpc_mdelem_from_strings(ctx, "a", "c");
+  grpc_init();
+  m1 = grpc_mdelem_from_strings("a", "b");
+  m2 = grpc_mdelem_from_strings("a", "b");
+  m3 = grpc_mdelem_from_strings("a", "c");
   GPR_ASSERT(m1 == m2);
   GPR_ASSERT(m3 != m1);
   GPR_ASSERT(m3->key == m1->key);
@@ -98,32 +93,25 @@ static void test_create_metadata(void) {
   GRPC_MDELEM_UNREF(m1);
   GRPC_MDELEM_UNREF(m2);
   GRPC_MDELEM_UNREF(m3);
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 static void test_create_many_ephemeral_metadata(void) {
-  grpc_mdctx *ctx;
   char buffer[GPR_LTOA_MIN_BUFSIZE];
   long i;
-  size_t mdtab_capacity_before;
 
   LOG_TEST("test_create_many_ephemeral_metadata");
 
-  ctx = grpc_mdctx_create();
-  mdtab_capacity_before = grpc_mdctx_get_mdtab_capacity_test_only(ctx);
+  grpc_init();
   /* add, and immediately delete a bunch of different elements */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", buffer));
+    GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", buffer));
   }
-  /* capacity should not grow */
-  GPR_ASSERT(mdtab_capacity_before ==
-             grpc_mdctx_get_mdtab_capacity_test_only(ctx));
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 static void test_create_many_persistant_metadata(void) {
-  grpc_mdctx *ctx;
   char buffer[GPR_LTOA_MIN_BUFSIZE];
   long i;
   grpc_mdelem **created = gpr_malloc(sizeof(grpc_mdelem *) * MANY);
@@ -131,16 +119,16 @@ static void test_create_many_persistant_metadata(void) {
 
   LOG_TEST("test_create_many_persistant_metadata");
 
-  ctx = grpc_mdctx_create();
+  grpc_init();
   /* add phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    created[i] = grpc_mdelem_from_strings(ctx, "a", buffer);
+    created[i] = grpc_mdelem_from_strings("a", buffer);
   }
   /* verify phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    md = grpc_mdelem_from_strings(ctx, "a", buffer);
+    md = grpc_mdelem_from_strings("a", buffer);
     GPR_ASSERT(md == created[i]);
     GRPC_MDELEM_UNREF(md);
   }
@@ -148,37 +136,22 @@ static void test_create_many_persistant_metadata(void) {
   for (i = 0; i < MANY; i++) {
     GRPC_MDELEM_UNREF(created[i]);
   }
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 
   gpr_free(created);
 }
 
 static void test_spin_creating_the_same_thing(void) {
-  grpc_mdctx *ctx;
-
   LOG_TEST("test_spin_creating_the_same_thing");
 
-  ctx = grpc_mdctx_create();
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 0);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 0);
-
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
-
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
-
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
-
-  grpc_mdctx_unref(ctx);
+  grpc_init();
+  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
+  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
+  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
+  grpc_shutdown();
 }
 
 static void test_things_stick_around(void) {
-  grpc_mdctx *ctx;
   size_t i, j;
   char *buffer;
   size_t nstrs = 1000;
@@ -188,11 +161,11 @@ static void test_things_stick_around(void) {
 
   LOG_TEST("test_things_stick_around");
 
-  ctx = grpc_mdctx_create();
+  grpc_init();
 
   for (i = 0; i < nstrs; i++) {
     gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", i);
-    strs[i] = grpc_mdstr_from_string(ctx, buffer);
+    strs[i] = grpc_mdstr_from_string(buffer);
     shuf[i] = i;
     gpr_free(buffer);
   }
@@ -214,65 +187,62 @@ static void test_things_stick_around(void) {
     GRPC_MDSTR_UNREF(strs[shuf[i]]);
     for (j = i + 1; j < nstrs; j++) {
       gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]);
-      test = grpc_mdstr_from_string(ctx, buffer);
+      test = grpc_mdstr_from_string(buffer);
       GPR_ASSERT(test == strs[shuf[j]]);
       GRPC_MDSTR_UNREF(test);
       gpr_free(buffer);
     }
   }
 
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
   gpr_free(strs);
   gpr_free(shuf);
 }
 
 static void test_slices_work(void) {
   /* ensure no memory leaks when switching representation from mdstr to slice */
-  grpc_mdctx *ctx;
   grpc_mdstr *str;
   gpr_slice slice;
 
   LOG_TEST("test_slices_work");
 
-  ctx = grpc_mdctx_create();
+  grpc_init();
 
   str = grpc_mdstr_from_string(
-      ctx, "123456789012345678901234567890123456789012345678901234567890");
+      "123456789012345678901234567890123456789012345678901234567890");
   slice = gpr_slice_ref(str->slice);
   GRPC_MDSTR_UNREF(str);
   gpr_slice_unref(slice);
 
   str = grpc_mdstr_from_string(
-      ctx, "123456789012345678901234567890123456789012345678901234567890");
+      "123456789012345678901234567890123456789012345678901234567890");
   slice = gpr_slice_ref(str->slice);
   gpr_slice_unref(slice);
   GRPC_MDSTR_UNREF(str);
 
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 static void test_base64_and_huffman_works(void) {
-  grpc_mdctx *ctx;
   grpc_mdstr *str;
   gpr_slice slice1;
   gpr_slice slice2;
 
   LOG_TEST("test_base64_and_huffman_works");
 
-  ctx = grpc_mdctx_create();
-  str = grpc_mdstr_from_string(ctx, "abcdefg");
+  grpc_init();
+  str = grpc_mdstr_from_string("abcdefg");
   slice1 = grpc_mdstr_as_base64_encoded_and_huffman_compressed(str);
   slice2 = grpc_chttp2_base64_encode_and_huffman_compress(str->slice);
   GPR_ASSERT(0 == gpr_slice_cmp(slice1, slice2));
 
   gpr_slice_unref(slice2);
   GRPC_MDSTR_UNREF(str);
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
-  grpc_init();
   test_no_op();
   test_create_string();
   test_create_metadata();
@@ -282,6 +252,5 @@ int main(int argc, char **argv) {
   test_things_stick_around();
   test_slices_work();
   test_base64_and_huffman_works();
-  grpc_shutdown();
   return 0;
 }
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
index d52e329023319c8a98bf4bd3465ea2573d4e9f89..86cb4143f8a94cbb6f2ced13b364bb403ef36bde 100755
--- a/tools/codegen/core/gen_static_metadata.py
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -50,9 +50,6 @@ CONFIG = [
     'host',
     'grpc-message',
     'grpc-status',
-    'gzip',
-    'deflate',
-    'identity',
     '',
     ('grpc-status', '0'),
     ('grpc-status', '1'),
@@ -127,6 +124,12 @@ CONFIG = [
     ('www-authenticate', ''),
 ]
 
+COMPRESSION_ALGORITHMS = [
+    'identity',
+    'deflate',
+    'gzip',
+]
+
 # utility: mangle the name of a config
 def mangle(elem):
   xl = {
@@ -174,6 +177,7 @@ def put_banner(files, banner):
 # build a list of all the strings we need
 all_strs = set()
 all_elems = set()
+static_userdata = {}
 for elem in CONFIG:
   if isinstance(elem, tuple):
     all_strs.add(elem[0])
@@ -181,6 +185,16 @@ for elem in CONFIG:
     all_elems.add(elem)
   else:
     all_strs.add(elem)
+compression_elems = []
+for mask in range(1, 1<<len(COMPRESSION_ALGORITHMS)):
+  val = ','.join(COMPRESSION_ALGORITHMS[alg]
+                 for alg in range(0, len(COMPRESSION_ALGORITHMS))
+                 if (1 << alg) & mask)
+  elem = ('grpc-accept-encoding', val)
+  all_strs.add(val)
+  all_elems.add(elem)
+  compression_elems.append(elem)
+  static_userdata[elem] = 1 + mask
 all_strs = sorted(list(all_strs), key=mangle)
 all_elems = sorted(list(all_elems), key=mangle)
 
@@ -248,11 +262,15 @@ print >>C
 
 print >>H, '#define GRPC_STATIC_MDELEM_COUNT %d' % len(all_elems)
 print >>H, 'extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];'
+print >>H, 'extern gpr_uintptr grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];'
 for i, elem in enumerate(all_elems):
   print >>H, '/* "%s": "%s" */' % elem
   print >>H, '#define %s (&grpc_static_mdelem_table[%d])' % (mangle(elem).upper(), i)
 print >>H
 print >>C, 'grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];'
+print >>C, 'gpr_uintptr grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {'
+print >>C, '  %s' % ','.join('%d' % static_userdata.get(elem, 0) for elem in all_elems)
+print >>C, '};'
 print >>C
 
 def str_idx(s):
@@ -260,18 +278,31 @@ def str_idx(s):
     if s == s2:
       return i
 
-print >>H, 'const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT*2];'
+def md_idx(m):
+  for i, m2 in enumerate(all_elems):
+    if m == m2:
+      return i
+
+print >>H, 'extern const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT*2];'
 print >>C, 'const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT*2] = {'
 print >>C, ','.join('%d' % str_idx(x) for x in itertools.chain.from_iterable([a,b] for a, b in all_elems))
 print >>C, '};'
 print >>C
 
-print >>H, 'const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];'
+print >>H, 'extern const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];'
 print >>C, 'const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {'
 print >>C, '%s' % ',\n'.join('  "%s"' % s for s in all_strs)
 print >>C, '};'
 print >>C
 
+print >>H, 'extern const gpr_uint8 grpc_static_accept_encoding_metadata[%d];' % (1 << len(COMPRESSION_ALGORITHMS))
+print >>C, 'const gpr_uint8 grpc_static_accept_encoding_metadata[%d] = {' % (1 << len(COMPRESSION_ALGORITHMS))
+print >>C, '0,%s' % ','.join('%d' % md_idx(elem) for elem in compression_elems)
+print >>C, '};'
+print >>C
+
+print >>H, '#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) (&grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]])'
+
 print >>H, '#endif /* GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H */'
 
 H.close()