diff --git a/WORKSPACE b/WORKSPACE
index 54d27f06a9e10d302343d2f1b0cea1196e8535d7..da63c5fea80f7ad08854eeca6d09a6d0312f760b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -72,6 +72,12 @@ local_repository(
     path = "third_party/gflags",
 )
 
+git_repository(
+    name   = "mongoose_repo",
+    commit = "4120a97945b41195a6223a600dae8e3b19bed19e",
+    remote = "https://github.com/makdharma/mongoose.git"
+)
+
 new_local_repository(
     name = "submodule_cares",
     path = "third_party/cares",
diff --git a/tools/distrib/check_copyright.py b/tools/distrib/check_copyright.py
index 2eb5b5b4a8ef1e0242ddd9705f94b1e0f2c155cc..710e8709e9972ab3c6c4c6f31e02649983c44248 100755
--- a/tools/distrib/check_copyright.py
+++ b/tools/distrib/check_copyright.py
@@ -111,6 +111,8 @@ _EXEMPT = frozenset((
 
   # An older file originally from outside gRPC.
   'src/php/tests/bootstrap.php',
+  # census.proto copied from github
+  'tools/grpcz/census.proto',
 ))
 
 
diff --git a/tools/grpcz/BUILD b/tools/grpcz/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..5e1faf7064f80b9e704f8ea0dab0d28fdd53ab18
--- /dev/null
+++ b/tools/grpcz/BUILD
@@ -0,0 +1,63 @@
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+licenses(["notice"])  # 3-clause BSD
+
+package(default_visibility = ["//visibility:public"])
+
+load("//:bazel/grpc_build_system.bzl", "grpc_proto_library")
+
+grpc_proto_library (
+    name = "monitoring_proto",
+    srcs = [
+        "monitoring.proto",
+    ],
+    deps = [
+        ":census_proto",
+    ],
+    well_known_protos = "@submodule_protobuf//:well_known_protos",
+)
+
+grpc_proto_library (
+    name = "census_proto",
+    srcs = [
+        "census.proto",
+    ],
+    well_known_protos = "@submodule_protobuf//:well_known_protos",
+)
+
+cc_binary(
+    name = "grpcz_client",
+    srcs = ["grpcz_client.cc",],
+    deps = [
+        "//external:gflags",
+        "monitoring_proto",
+        "@mongoose_repo//:mongoose_lib",
+    ],
+)
diff --git a/tools/grpcz/census.proto b/tools/grpcz/census.proto
new file mode 100644
index 0000000000000000000000000000000000000000..d1ff69400b05231382af485949ff0b408e1a11f5
--- /dev/null
+++ b/tools/grpcz/census.proto
@@ -0,0 +1,318 @@
+// Copyright 2017, Google Inc.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//     http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//TODO(ericgribkoff) Depend on this directly from the instrumentation-proto
+//repository.
+
+syntax = "proto3";
+
+package google.instrumentation;
+
+option java_package = "com.google.instrumentation.stats.proto";
+option java_outer_classname = "CensusProto";
+
+// All the census protos.
+//
+// Nomenclature notes:
+//   * Capitalized names below (like View) are protos.
+//   * Protos which describe types are named with a Descriptor suffix (e.g.
+//     MesurementDescriptor).
+//
+// Census lets you define the type and description of the data being measured
+// (e.g. the latency of an RPC or the number of CPU cycles spent on an
+// operation using MeasurementDescriptor. As individual measurements (a double
+// value) for are recorded, they are aggregated together into an
+// Aggregation. There are two Aggregation types available: Distribution
+// (describes the distribution of all measurements, possibly with a histogram)
+// and IntervalStats (the count and mean of measurements across specified time
+// periods). An Aggregation is described by an AggregationDescriptor.
+//
+// You can define how your measurements (described by a MeasurementDescriptor)
+// are broken down by Tag values and which Aggregations to use through a
+// ViewDescriptor. The output (all measurements broken down by tag values into
+// specific Aggregations) is called a View.
+
+
+// The following two types are copied from
+// google/protobuf/{duration,timestamp}.proto. Ideally, we would be able to
+// import them, but this causes compilation issues on C-based systems
+// (e.g. https://koti.kapsi.fi/jpa/nanopb/), which cannot process the C++
+// headers generated from the standard protobuf distribution. See the relevant
+// proto files for full documentation of these types.
+
+message Duration {
+  // Signed seconds of the span of time. Must be from -315,576,000,000
+  // to +315,576,000,000 inclusive.
+  int64 seconds = 1;
+
+  // Signed fractions of a second at nanosecond resolution of the span
+  // of time. Durations less than one second are represented with a 0
+  // `seconds` field and a positive or negative `nanos` field. For durations
+  // of one second or more, a non-zero value for the `nanos` field must be
+  // of the same sign as the `seconds` field. Must be from -999,999,999
+  // to +999,999,999 inclusive.
+  int32 nanos = 2;
+}
+
+message Timestamp {
+  // Represents seconds of UTC time since Unix epoch
+  // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
+  // 9999-12-31T23:59:59Z inclusive.
+  int64 seconds = 1;
+
+  // Non-negative fractions of a second at nanosecond resolution. Negative
+  // second values with fractions must still have non-negative nanos values
+  // that count forward in time. Must be from 0 to 999,999,999
+  // inclusive.
+  int32 nanos = 2;
+}
+
+// MeasurementDescriptor describes a data point (measurement) type.
+message MeasurementDescriptor {
+  // A descriptive name, e.g. rpc_latency, cpu. Must be unique.
+  string name = 1;
+
+  // More detailed description of the resource, used in documentation.
+  string description = 2;
+
+  // Fundamental units of measurement supported by Census
+  // TODO(aveitch): expand this to include other S.I. units?
+  enum BasicUnit {
+    UNKNOWN = 0;    // Implementations should not use this
+    SCALAR = 1;     // Dimensionless
+    BITS = 2;       // A single bit
+    BYTES = 3;      // An 8-bit byte
+    SECONDS = 4;    // S.I. unit
+    CORES = 5;      // CPU core usage
+    MAX_UNITS = 6;  // Last defined value; implementations should only use
+                    // this for validation.
+  }
+
+  // MeasurementUnit lets you build compound units of the form
+  //   10^n * (A * B * ...) / (X * Y * ...),
+  // where the elements in the numerator and denominator are all BasicUnits.  A
+  // MeasurementUnit must have at least one BasicUnit in its numerator.
+  //
+  // To specify multiplication in the numerator or denominator, simply specify
+  // multiple numerator or denominator fields.  For example:
+  //
+  // - byte-seconds (i.e. bytes * seconds):
+  //     numerator: BYTES
+  //     numerator: SECS
+  //
+  // - events/sec^2 (i.e. rate of change of events/sec):
+  //     numerator: SCALAR
+  //     denominator: SECS
+  //     denominator: SECS
+  //
+  // To specify multiples (in power of 10) of units, specify a non-zero
+  // 'power10' value, for example:
+  //
+  // - MB/s (i.e. megabytes / s):
+  //     power10: 6
+  //     numerator: BYTES
+  //     denominator: SECS
+  //
+  // - nanoseconds
+  //     power10: -9
+  //     numerator: SECS
+  message MeasurementUnit {
+    int32 power10 = 1;
+    repeated BasicUnit numerators = 2;
+    repeated BasicUnit denominators = 3;
+  }
+
+  // The units used by this type of measurement.
+  MeasurementUnit unit = 3;
+}
+
+// An aggregation summarizes a series of individual measurements. There are
+// two types of aggregation (IntervalAggregation and DistributionAggregation),
+// unique types of each can be set using descriptors for each.
+
+// DistributionAggregation contains summary statistics for a population of
+// values and, optionally, a histogram representing the distribution of those
+// values across a specified set of histogram buckets, as defined in
+// DistributionAggregationDescriptor.bucket_bounds.
+//
+// The summary statistics are the count, mean, minimum, and the maximum of the
+// set of population of values.
+//
+// Although it is not forbidden, it is generally a bad idea to include
+// non-finite values (infinities or NaNs) in the population of values, as this
+// will render the `mean` field meaningless.
+message DistributionAggregation {
+  // The number of values in the population. Must be non-negative.
+  int64 count = 1;
+
+  // The arithmetic mean of the values in the population. If `count` is zero
+  // then this field must be zero.
+  double mean = 2;
+
+  // The sum of the values in the population.  If `count` is zero then this
+  // field must be zero.
+  double sum = 3;
+
+  // Describes a range of population values.
+  message Range {
+    // The minimum of the population values.
+    double min = 1;
+    // The maximum of the population values.
+    double max = 2;
+  }
+
+  // The range of the population values. If `count` is zero, this field will not
+  // be defined.
+  Range range = 4;
+
+  // A Distribution may optionally contain a histogram of the values in the
+  // population. The histogram is given in `bucket_count` as counts of values
+  // that fall into one of a sequence of non-overlapping buckets, as described
+  // by `DistributionAggregationDescriptor.bucket_boundaries`. The sum of the
+  // values in `bucket_counts` must equal the value in `count`.
+  //
+  // Bucket counts are given in order under the numbering scheme described
+  // above (the underflow bucket has number 0; the finite buckets, if any,
+  // have numbers 1 through N-2; the overflow bucket has number N-1).
+  //
+  // The size of `bucket_count` must be no greater than N as defined in
+  // `bucket_boundaries`.
+  //
+  // Any suffix of trailing zero bucket_count fields may be omitted.
+  repeated int64 bucket_counts = 5;
+
+  // Tags associated with this DistributionAggregation. These will be filled
+  // in based on the View specification.
+  repeated Tag tags = 6;
+}
+
+message DistributionAggregationDescriptor {
+  // A Distribution may optionally contain a histogram of the values in the
+  // population. The bucket boundaries for that histogram are described by
+  // `bucket_bounds`. This defines `size(bucket_bounds) + 1` (= N)
+  // buckets. The boundaries for bucket index i are:
+  //
+  // [-infinity, bucket_bounds[i]) for i == 0
+  // [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-2
+  // [bucket_bounds[i-1], +infinity) for i == N-1
+  //
+  // i.e. an underflow bucket (number 0), zero or more finite buckets (1
+  // through N - 2, and an overflow bucket (N - 1), with inclusive lower
+  // bounds and exclusive upper bounds.
+  //
+  // If `bucket_bounds` has no elements (zero size), then there is no
+  // histogram associated with the Distribution. If `bucket_bounds` has only
+  // one element, there are no finite buckets, and that single element is the
+  // common boundary of the overflow and underflow buckets. The values must
+  // be monotonically increasing.
+  repeated double bucket_bounds = 1;
+}
+
+// An IntervalAggreation records summary stats over various time
+// windows. These stats are approximate, with the degree of accuracy
+// controlled by setting the n_sub_intervals parameter in the
+// IntervalAggregationDescriptor.
+message IntervalAggregation {
+  // Summary statistic over a single time interval.
+  message Interval {
+    // The interval duration. Must be positive.
+    Duration interval_size = 1;
+    // Approximate number of measurements recorded in this interval.
+    double count = 2;
+    // The cumulative sum of measurements in this interval.
+    double sum = 3;
+  }
+
+  // Full set of intervals for this aggregation.
+  repeated Interval intervals = 1;
+
+  // Tags associated with this IntervalAggregation. These will be filled in
+  // based on the View specification.
+  repeated Tag tags = 2;
+}
+
+// An IntervalAggreationDescriptor specifies time intervals for an
+// IntervalAggregation.
+message IntervalAggregationDescriptor {
+  // Number of internal sub-intervals to use when collecting stats for each
+  // interval. The max error in interval measurements will be approximately
+  // 1/n_sub_intervals (although in practice, this will only be approached in
+  // the presence of very large and bursty workload changes), and underlying
+  // memory usage will be roughly proportional to the value of this
+  // field. Must be in the range [2, 20]. A value of 5 will be used if this is
+  // unspecified.
+  int32 n_sub_intervals = 1;
+
+  // The size of each interval, as a time duration. Must have at least one
+  // element.
+  repeated Duration interval_sizes = 2;
+}
+
+// A Tag: key-value pair.
+message Tag {
+  string key = 1;
+  string value = 2;
+}
+
+// A ViewDescriptor specifies an AggregationDescriptor and a set of tag
+// keys. Views instantiated from this descriptor will contain Aggregations
+// broken down by the unique set of matching tag values for each measurement.
+message ViewDescriptor {
+  // Name of view. Must be unique.
+  string name = 1;
+
+  // More detailed description, for documentation purposes.
+  string description = 2;
+
+  // Name of a MeasurementDescriptor to be used for this view.
+  string measurement_descriptor_name = 3;
+
+  // Aggregation type to associate with View.
+  oneof aggregation {
+    IntervalAggregationDescriptor interval_aggregation = 4;
+    DistributionAggregationDescriptor distribution_aggregation = 5;
+  }
+
+  // Tag keys to match with a given measurement. If no keys are specified,
+  // then all stats are recorded. Keys must be unique.
+  repeated string tag_keys = 6;
+}
+
+// DistributionView contains all aggregations for a view specified using a
+// DistributionAggregationDescriptor.
+message DistributionView {
+  // Aggregations - each will have a unique set of tag values for the tag_keys
+  // associated with the corresponding View.
+  repeated DistributionAggregation aggregations = 1;
+
+  // Start and end timestamps over which aggregations was accumulated.
+  Timestamp start = 2;
+  Timestamp end = 3;
+}
+
+// IntervalView contains all aggregations for a view specified using a
+// IntervalAggregationDescriptor.
+message IntervalView {
+  // Aggregations - each will have a unique set of tag values for the tag_keys
+  // associated with the corresponding View.
+  repeated IntervalAggregation aggregations = 1;
+}
+
+// A View contains the aggregations based on a ViewDescriptor.
+message View {
+  // ViewDescriptor name associated with this set of View.
+  string view_name = 1;
+
+  oneof view {
+    DistributionView distribution_view = 2;
+    IntervalView interval_view = 3;
+  }
+}
diff --git a/tools/grpcz/grpcz_client.cc b/tools/grpcz/grpcz_client.cc
new file mode 100644
index 0000000000000000000000000000000000000000..47eec8dfc38b1e8877c1b4b2d879a7dc86e64d49
--- /dev/null
+++ b/tools/grpcz/grpcz_client.cc
@@ -0,0 +1,181 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <string>
+
+#include <google/protobuf/util/json_util.h>
+#include <grpc++/grpc++.h>
+#include <grpc/support/log.h>
+
+#include "gflags/gflags.h"
+#include "mongoose.h"
+
+// TODO (makdharma): remove local copies of these protos
+#include "tools/grpcz/census.grpc.pb.h"
+#include "tools/grpcz/monitoring.grpc.pb.h"
+
+DEFINE_string(
+    grpcz_server, "127.0.0.1:8080",
+    "Unix domain socket path (e.g. unix://tmp/grpcz.sock) or IP address"
+    "(host:port) where grpcz server is running.");
+DEFINE_string(http_port, "8000",
+              "Port id for accessing the HTTP server that renders /grpcz page");
+DEFINE_bool(print_to_console, false,
+            "print the JSON retreived from grpcz server and quit");
+
+using grpc::Channel;
+using grpc::ClientContext;
+using grpc::Status;
+
+using ::grpc::instrumentation::v1alpha::CanonicalRpcStats;
+using ::grpc::instrumentation::v1alpha::Monitoring;
+
+static const std::string static_html_header =
+    "<!DOCTYPE html> <html> <head> <style> \
+table { border-collapse: collapse; width: 100%; } \
+table, td, th { border: 1px solid black; } \
+</style> </head> <body>\
+<div id='stats' data-stats='";
+
+static const std::string static_html_footer =
+    "' class='hidden'></div>\
+<h1>GRPCZ Statistics</h1> <div id='table'> </div> \
+<script> \
+  var canonical_stats = JSON.parse(\
+            document.getElementById('stats').getAttribute('data-stats')); \
+  var table = document.createElement('table'); \
+  if (canonical_stats['Error Message'] != undefined) { \
+     document.getElementById('table').innerHTML = canonical_stats['Error Message']; } \
+  else {\
+  for (var key in canonical_stats) { \
+    name = canonical_stats[key]['view']['viewName']; \
+    distribution = canonical_stats[key]['view']['distributionView']; \
+    interval = canonical_stats[key]['view']['intervalView']; \
+    value = (interval == undefined) ? \
+      JSON.stringify(distribution, null, ' ') : \
+      JSON.stringify(interval, null, ' '); \
+    var row = table.insertRow(-1); \
+    var col1 = row.insertCell(0); \
+    var col2 = row.insertCell(1); \
+    col1.innerHTML = name; \
+    col2.innerHTML = '<pre>' + value + '</pre>'; \
+  } \
+  document.getElementById('table').appendChild(table); \
+  }\
+</script> </body> </html>";
+
+class GrpczClient {
+ public:
+  GrpczClient(std::shared_ptr<Channel> channel)
+      : stub_(Monitoring::NewStub(channel)) {}
+
+  std::string GetStatsAsJson() {
+    const ::google::protobuf::Empty request;
+    CanonicalRpcStats reply;
+    ClientContext context;
+    Status status = stub_->GetCanonicalRpcStats(&context, request, &reply);
+
+    if (status.ok()) {
+      std::string json_str;
+      ::google::protobuf::util::MessageToJsonString(reply, &json_str);
+      return json_str;
+    } else {
+      static const std::string error_message_json =
+          "{\"Error Message\":\"" + status.error_message() + "\"}";
+      gpr_log(GPR_DEBUG, "%d: %s", status.error_code(),
+              status.error_message().c_str());
+      return error_message_json;
+    }
+  }
+
+ private:
+  std::unique_ptr<Monitoring::Stub> stub_;
+};
+
+static struct mg_serve_http_opts s_http_server_opts;
+std::unique_ptr<GrpczClient> g_grpcz_client;
+
+static void ev_handler(struct mg_connection *nc, int ev, void *p) {
+  if (ev == MG_EV_HTTP_REQUEST) {
+    mg_serve_http(nc, (struct http_message *)p, s_http_server_opts);
+  }
+}
+
+static void grpcz_handler(struct mg_connection *nc, int ev, void *ev_data) {
+  (void)ev;
+  (void)ev_data;
+  gpr_log(GPR_INFO, "fetching grpcz stats from %s", FLAGS_grpcz_server.c_str());
+  std::string json_str = g_grpcz_client->GetStatsAsJson();
+  std::string rendered_html =
+      static_html_header + json_str + static_html_footer;
+  mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n%s", rendered_html.c_str());
+  nc->flags |= MG_F_SEND_AND_CLOSE;
+}
+
+int main(int argc, char **argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  // Create a client
+  g_grpcz_client.reset(new GrpczClient(grpc::CreateChannel(
+      FLAGS_grpcz_server, grpc::InsecureChannelCredentials())));
+  if (FLAGS_print_to_console) {
+    // using GPR_ERROR since this is the default verbosity. _DEBUG or _INFO
+    // won't print unless GRPC_VERBOSITY env var is set appropriately, which
+    // might confuse users of this utility.
+    gpr_log(GPR_ERROR, "%s\n", g_grpcz_client->GetStatsAsJson().c_str());
+    return 0;
+  }
+
+  // Set up a mongoose webserver handler
+  struct mg_mgr mgr;
+  mg_mgr_init(&mgr, NULL);
+  gpr_log(GPR_INFO, "Starting grpcz web server on port %s\n",
+          FLAGS_http_port.c_str());
+
+  struct mg_connection *nc = mg_bind(&mgr, FLAGS_http_port.c_str(), ev_handler);
+  if (nc == NULL) {
+    gpr_log(GPR_ERROR, "Failed to create listener on port %s\n",
+            FLAGS_http_port.c_str());
+    return -1;
+  }
+  mg_register_http_endpoint(nc, "/grpcz", grpcz_handler);
+  mg_set_protocol_http_websocket(nc);
+
+  // Poll in a loop and serve /grpcz pages
+  for (;;) {
+    static const int k_sleep_millis = 100;
+    mg_mgr_poll(&mgr, k_sleep_millis);
+  }
+  mg_mgr_free(&mgr);
+  return 0;
+}
diff --git a/tools/grpcz/monitoring.proto b/tools/grpcz/monitoring.proto
new file mode 100644
index 0000000000000000000000000000000000000000..fefcd7d22f17138b4aafb50a31a9592c70c9ed4e
--- /dev/null
+++ b/tools/grpcz/monitoring.proto
@@ -0,0 +1,156 @@
+// Copyright 2017, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file defines an interface for exporting monitoring information
+// out of gRPC servers.
+syntax = "proto3";
+
+// TODO(ericgribkoff) Figure out how to manage the external Census proto
+// dependency.
+import "tools/grpcz/census.proto";
+import "google/protobuf/any.proto";
+import "google/protobuf/empty.proto";
+
+package grpc.instrumentation.v1alpha;
+
+option java_multiple_files = true;
+option java_package = "io.grpc.instrumentation.v1alpha";
+option java_outer_classname = "MonitoringProto";
+
+service Monitoring {
+  // Return canonical RPC stats
+  rpc GetCanonicalRpcStats(google.protobuf.Empty) returns (CanonicalRpcStats) {
+  }
+
+  // Query the server for specific stats
+  rpc GetStats(StatsRequest) returns (StatsResponse) {
+  }
+
+  // Request the server to stream back snapshots of the requested stats
+  rpc WatchStats(StatsRequest) returns (stream StatsResponse) {
+  }
+
+
+  // Return request traces.
+  rpc GetRequestTraces(TraceRequest) returns(TraceResponse) {
+  // TODO(aveitch): Please define the messages here
+  }
+
+  // Return application-defined groups of monitoring data.
+  // This is a low level facility to allow extension of the monitoring API to
+  // application-specific monitoring data. Frameworks may use this to define
+  // additional groups of monitoring data made available by servers.
+  rpc GetCustomMonitoringData(MonitoringDataGroup)
+    returns (CustomMonitoringData) {
+  }
+
+}
+
+// Canonical RPC stats exported by gRPC.
+message CanonicalRpcStats {
+  StatsResponse rpc_client_errors = 1;
+  StatsResponse rpc_client_completed_rpcs = 2;
+  StatsResponse rpc_client_started_rpcs = 3;
+  StatsResponse rpc_client_elapsed_time = 4;
+  StatsResponse rpc_client_server_elapsed_time = 5;
+  StatsResponse rpc_client_request_bytes = 6;
+  StatsResponse rpc_client_response_bytes = 7;
+  StatsResponse rpc_client_request_count = 8;
+  StatsResponse rpc_client_response_count = 9;
+  StatsResponse rpc_server_errors = 10;
+  StatsResponse rpc_server_completed_rpcs = 11;
+  StatsResponse rpc_server_server_elapsed_time = 12;
+  StatsResponse rpc_server_request_bytes = 13;
+  StatsResponse rpc_server_response_bytes = 14;
+  StatsResponse rpc_server_request_count = 15;
+  StatsResponse rpc_server_response_count = 16;
+  StatsResponse rpc_server_elapsed_time = 17;
+  //TODO(ericgribkoff) Add minute-hour interval stats.
+}
+
+// This message is sent when requesting a set of stats (Census Views) from
+// a client system, as part of the MonitoringService API's.
+message StatsRequest {
+  // An optional set of ViewDescriptor names. Only Views using these
+  // descriptors will be sent back in the response. If no names are provided,
+  // then all Views present in the client system will be included in every
+  // response. If measurement_names is also provided, then Views matching the
+  // intersection of the two are returned.
+  // TODO(aveitch): Consider making this a list of regexes or prefix matches in
+  // the future.
+  repeated string view_names = 1;
+
+  // An optional set of MeasurementDescriptor names. Only Views using these
+  // descriptors will be sent back in the response. If no names are provided,
+  // then all Views present in the client system will be included in every
+  // response. If view_names is also provided, then Views matching the
+  // intersection of the two are returned.
+  // TODO(aveitch): Consider making this a list of regexes or prefix matches in
+  // the future.
+  repeated string measurement_names = 2;
+
+  // By default, the MeasurementDescriptors and ViewDescriptors corresponding to
+  // the Views that are returned in a StatsResponse will be included in the
+  // first such response. Set this to true to have them not sent.
+  bool dont_include_descriptors_in_first_response = 3;
+}
+
+// This message contains all information relevant to a single View. It is the
+// return type for GetStats and WatchStats, and used in CanonicalRpcStats.
+message StatsResponse {
+  // A StatsResponse can optionally contain the MeasurementDescriptor and
+  // ViewDescriptor for the View. These will be sent in the first WatchStats
+  // response, or all GetStats and GetCanonicalRpcStats responses. These will
+  // not be set for {Get,Watch}Stats if
+  // dont_include_descriptors_in_first_response is set to true in the
+  // StatsRequest.
+  google.instrumentation.MeasurementDescriptor measurement_descriptor = 1;
+  google.instrumentation.ViewDescriptor view_descriptor = 2;
+
+  // The View data.
+  google.instrumentation.View view = 3;
+}
+
+message TraceRequest {
+  // TODO(aveitch): Complete definition of this type
+}
+
+message TraceResponse {
+  // TODO(aveitch): Complete definition of this type
+}
+
+message MonitoringDataGroup {
+  string name = 1;  // name of a group of monitoring data
+}
+
+// The wrapper for custom monitoring data.
+message CustomMonitoringData {
+  // can be any application specific monitoring data
+  google.protobuf.Any contents = 1;
+}