Use math test to test generated code
diff --git a/package.json b/package.json
index 72731c0..30d3251 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
     "lib": "src/node/src"
   },
   "scripts": {
-    "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js",
+    "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js --exclude-path=src/node/.jshintignore",
     "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
     "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
     "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test",
@@ -72,5 +72,25 @@
     "binding.gyp"
   ],
   "main": "src/node/index.js",
-  "license": "BSD-3-Clause"
+  "license": "BSD-3-Clause",
+  "jshintConfig" : {
+    "bitwise": true,
+    "curly": true,
+    "eqeqeq": true,
+    "esnext": true,
+    "freeze": true,
+    "immed": true,
+    "indent": 2,
+    "latedef": "nofunc",
+    "maxlen": 80,
+    "mocha": true,
+    "newcap": true,
+    "node": true,
+    "noarg": true,
+    "quotmark": "single",
+    "strict": true,
+    "trailing": true,
+    "undef": true,
+    "unused": "vars"
+  }
 }
diff --git a/src/compiler/node_generator.cc b/src/compiler/node_generator.cc
index 03e1314..822622c 100644
--- a/src/compiler/node_generator.cc
+++ b/src/compiler/node_generator.cc
@@ -138,7 +138,7 @@
   out->Print("}\n");
   out->Print("return new Buffer(arg.serializeBinary());\n");
   out->Outdent();
-  out->Print("}\n");
+  out->Print("}\n\n");
 
   // Print the deserializer
   out->Print(template_vars,
@@ -148,7 +148,7 @@
       template_vars,
       "return $node_name$.deserializeBinary(new Uint8Array(buffer_arg));\n");
   out->Outdent();
-  out->Print("}\n");
+  out->Print("}\n\n");
 }
 
 void PrintMethod(const MethodDescriptor *method, Printer *out) {
@@ -212,6 +212,8 @@
 
     out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");
 
+    out.Print("'use strict';\n");
+
     out.Print("var grpc = require('grpc');\n");
     if (file->message_type_count() > 0) {
       grpc::string file_path = GetRelativePath(file->name(),
diff --git a/src/node/.jshintignore b/src/node/.jshintignore
new file mode 100644
index 0000000..0a73e1e
--- /dev/null
+++ b/src/node/.jshintignore
@@ -0,0 +1 @@
+**/*_pb.js
\ No newline at end of file
diff --git a/src/node/.jshintrc b/src/node/.jshintrc
deleted file mode 100644
index 8237e0d..0000000
--- a/src/node/.jshintrc
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "bitwise": true,
-  "curly": true,
-  "eqeqeq": true,
-  "esnext": true,
-  "freeze": true,
-  "immed": true,
-  "indent": 2,
-  "latedef": "nofunc",
-  "maxlen": 80,
-  "newcap": true,
-  "node": true,
-  "noarg": true,
-  "quotmark": "single",
-  "strict": true,
-  "trailing": true,
-  "undef": true,
-  "unused": "vars",
-  "globals": {
-    /* Mocha-provided globals */
-    "describe": false,
-    "it": false,
-    "before": false,
-    "beforeEach": false,
-    "after": false,
-    "afterEach": false
-  }
-}
diff --git a/src/node/test/math/math_grpc_pb.js b/src/node/test/math/math_grpc_pb.js
new file mode 100644
index 0000000..083ed66
--- /dev/null
+++ b/src/node/test/math/math_grpc_pb.js
@@ -0,0 +1,99 @@
+// GENERATED CODE -- DO NOT EDIT!
+
+'use strict';
+var grpc = require('grpc');
+var math_pb = require('./math_pb.js');
+
+function serialize_DivArgs(arg) {
+  if (!(arg instanceof math_pb.DivArgs)) {
+    throw new Error('Expected argument of type DivArgs');
+  }
+  return new Buffer(arg.serializeBinary());
+}
+
+function deserialize_DivArgs(buffer_arg) {
+  return math_pb.DivArgs.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
+function serialize_DivReply(arg) {
+  if (!(arg instanceof math_pb.DivReply)) {
+    throw new Error('Expected argument of type DivReply');
+  }
+  return new Buffer(arg.serializeBinary());
+}
+
+function deserialize_DivReply(buffer_arg) {
+  return math_pb.DivReply.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
+function serialize_FibArgs(arg) {
+  if (!(arg instanceof math_pb.FibArgs)) {
+    throw new Error('Expected argument of type FibArgs');
+  }
+  return new Buffer(arg.serializeBinary());
+}
+
+function deserialize_FibArgs(buffer_arg) {
+  return math_pb.FibArgs.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
+function serialize_Num(arg) {
+  if (!(arg instanceof math_pb.Num)) {
+    throw new Error('Expected argument of type Num');
+  }
+  return new Buffer(arg.serializeBinary());
+}
+
+function deserialize_Num(buffer_arg) {
+  return math_pb.Num.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
+
+var MathService = exports.MathService = {
+  div: {
+    path: '/math.Math/Div',
+    requestStream: false,
+    responseStream: false,
+    requestType: math_pb.DivArgs,
+    responseType: math_pb.DivReply,
+    requestSerialize: serialize_DivArgs,
+    requestDeserialize: deserialize_DivArgs,
+    responseSerialize: serialize_DivReply,
+    responseDeserialize: deserialize_DivReply,
+  },
+  divMany: {
+    path: '/math.Math/DivMany',
+    requestStream: true,
+    responseStream: true,
+    requestType: math_pb.DivArgs,
+    responseType: math_pb.DivReply,
+    requestSerialize: serialize_DivArgs,
+    requestDeserialize: deserialize_DivArgs,
+    responseSerialize: serialize_DivReply,
+    responseDeserialize: deserialize_DivReply,
+  },
+  fib: {
+    path: '/math.Math/Fib',
+    requestStream: false,
+    responseStream: true,
+    requestType: math_pb.FibArgs,
+    responseType: math_pb.Num,
+    requestSerialize: serialize_FibArgs,
+    requestDeserialize: deserialize_FibArgs,
+    responseSerialize: serialize_Num,
+    responseDeserialize: deserialize_Num,
+  },
+  sum: {
+    path: '/math.Math/Sum',
+    requestStream: true,
+    responseStream: false,
+    requestType: math_pb.Num,
+    responseType: math_pb.Num,
+    requestSerialize: serialize_Num,
+    requestDeserialize: deserialize_Num,
+    responseSerialize: serialize_Num,
+    responseDeserialize: deserialize_Num,
+  },
+};
+
+exports.MathClient = grpc.makeGenericClientConstructor(MathService);
diff --git a/src/node/test/math/math_pb.js b/src/node/test/math/math_pb.js
new file mode 100644
index 0000000..3489143
--- /dev/null
+++ b/src/node/test/math/math_pb.js
@@ -0,0 +1,866 @@
+/**
+ * @fileoverview
+ * @enhanceable
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = Function('return this')();
+
+goog.exportSymbol('proto.math.DivArgs', null, global);
+goog.exportSymbol('proto.math.DivReply', null, global);
+goog.exportSymbol('proto.math.FibArgs', null, global);
+goog.exportSymbol('proto.math.FibReply', null, global);
+goog.exportSymbol('proto.math.Num', null, global);
+
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.math.DivArgs = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.math.DivArgs, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  proto.math.DivArgs.displayName = 'proto.math.DivArgs';
+}
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ *     for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.math.DivArgs.prototype.toObject = function(opt_includeInstance) {
+  return proto.math.DivArgs.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ *     instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.math.DivArgs} msg The msg instance to transform.
+ * @return {!Object}
+ */
+proto.math.DivArgs.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    dividend: msg.getDividend(),
+    divisor: msg.getDivisor()
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.math.DivArgs}
+ */
+proto.math.DivArgs.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.math.DivArgs;
+  return proto.math.DivArgs.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.math.DivArgs} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.math.DivArgs}
+ */
+proto.math.DivArgs.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt64());
+      msg.setDividend(value);
+      break;
+    case 2:
+      var value = /** @type {number} */ (reader.readInt64());
+      msg.setDivisor(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Class method variant: serializes the given message to binary data
+ * (in protobuf wire format), writing to the given BinaryWriter.
+ * @param {!proto.math.DivArgs} message
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.DivArgs.serializeBinaryToWriter = function(message, writer) {
+  message.serializeBinaryToWriter(writer);
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.math.DivArgs.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  this.serializeBinaryToWriter(writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format),
+ * writing to the given BinaryWriter.
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.DivArgs.prototype.serializeBinaryToWriter = function (writer) {
+  var f = undefined;
+  f = this.getDividend();
+  if (f !== 0) {
+    writer.writeInt64(
+      1,
+      f
+    );
+  }
+  f = this.getDivisor();
+  if (f !== 0) {
+    writer.writeInt64(
+      2,
+      f
+    );
+  }
+};
+
+
+/**
+ * Creates a deep clone of this proto. No data is shared with the original.
+ * @return {!proto.math.DivArgs} The clone.
+ */
+proto.math.DivArgs.prototype.cloneMessage = function() {
+  return /** @type {!proto.math.DivArgs} */ (jspb.Message.cloneMessage(this));
+};
+
+
+/**
+ * optional int64 dividend = 1;
+ * @return {number}
+ */
+proto.math.DivArgs.prototype.getDividend = function() {
+  return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
+};
+
+
+/** @param {number} value  */
+proto.math.DivArgs.prototype.setDividend = function(value) {
+  jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * optional int64 divisor = 2;
+ * @return {number}
+ */
+proto.math.DivArgs.prototype.getDivisor = function() {
+  return /** @type {number} */ (jspb.Message.getFieldProto3(this, 2, 0));
+};
+
+
+/** @param {number} value  */
+proto.math.DivArgs.prototype.setDivisor = function(value) {
+  jspb.Message.setField(this, 2, value);
+};
+
+
+
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.math.DivReply = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.math.DivReply, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  proto.math.DivReply.displayName = 'proto.math.DivReply';
+}
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ *     for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.math.DivReply.prototype.toObject = function(opt_includeInstance) {
+  return proto.math.DivReply.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ *     instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.math.DivReply} msg The msg instance to transform.
+ * @return {!Object}
+ */
+proto.math.DivReply.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    quotient: msg.getQuotient(),
+    remainder: msg.getRemainder()
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.math.DivReply}
+ */
+proto.math.DivReply.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.math.DivReply;
+  return proto.math.DivReply.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.math.DivReply} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.math.DivReply}
+ */
+proto.math.DivReply.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt64());
+      msg.setQuotient(value);
+      break;
+    case 2:
+      var value = /** @type {number} */ (reader.readInt64());
+      msg.setRemainder(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Class method variant: serializes the given message to binary data
+ * (in protobuf wire format), writing to the given BinaryWriter.
+ * @param {!proto.math.DivReply} message
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.DivReply.serializeBinaryToWriter = function(message, writer) {
+  message.serializeBinaryToWriter(writer);
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.math.DivReply.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  this.serializeBinaryToWriter(writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format),
+ * writing to the given BinaryWriter.
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.DivReply.prototype.serializeBinaryToWriter = function (writer) {
+  var f = undefined;
+  f = this.getQuotient();
+  if (f !== 0) {
+    writer.writeInt64(
+      1,
+      f
+    );
+  }
+  f = this.getRemainder();
+  if (f !== 0) {
+    writer.writeInt64(
+      2,
+      f
+    );
+  }
+};
+
+
+/**
+ * Creates a deep clone of this proto. No data is shared with the original.
+ * @return {!proto.math.DivReply} The clone.
+ */
+proto.math.DivReply.prototype.cloneMessage = function() {
+  return /** @type {!proto.math.DivReply} */ (jspb.Message.cloneMessage(this));
+};
+
+
+/**
+ * optional int64 quotient = 1;
+ * @return {number}
+ */
+proto.math.DivReply.prototype.getQuotient = function() {
+  return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
+};
+
+
+/** @param {number} value  */
+proto.math.DivReply.prototype.setQuotient = function(value) {
+  jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * optional int64 remainder = 2;
+ * @return {number}
+ */
+proto.math.DivReply.prototype.getRemainder = function() {
+  return /** @type {number} */ (jspb.Message.getFieldProto3(this, 2, 0));
+};
+
+
+/** @param {number} value  */
+proto.math.DivReply.prototype.setRemainder = function(value) {
+  jspb.Message.setField(this, 2, value);
+};
+
+
+
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.math.FibArgs = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.math.FibArgs, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  proto.math.FibArgs.displayName = 'proto.math.FibArgs';
+}
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ *     for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.math.FibArgs.prototype.toObject = function(opt_includeInstance) {
+  return proto.math.FibArgs.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ *     instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.math.FibArgs} msg The msg instance to transform.
+ * @return {!Object}
+ */
+proto.math.FibArgs.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    limit: msg.getLimit()
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.math.FibArgs}
+ */
+proto.math.FibArgs.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.math.FibArgs;
+  return proto.math.FibArgs.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.math.FibArgs} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.math.FibArgs}
+ */
+proto.math.FibArgs.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt64());
+      msg.setLimit(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Class method variant: serializes the given message to binary data
+ * (in protobuf wire format), writing to the given BinaryWriter.
+ * @param {!proto.math.FibArgs} message
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.FibArgs.serializeBinaryToWriter = function(message, writer) {
+  message.serializeBinaryToWriter(writer);
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.math.FibArgs.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  this.serializeBinaryToWriter(writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format),
+ * writing to the given BinaryWriter.
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.FibArgs.prototype.serializeBinaryToWriter = function (writer) {
+  var f = undefined;
+  f = this.getLimit();
+  if (f !== 0) {
+    writer.writeInt64(
+      1,
+      f
+    );
+  }
+};
+
+
+/**
+ * Creates a deep clone of this proto. No data is shared with the original.
+ * @return {!proto.math.FibArgs} The clone.
+ */
+proto.math.FibArgs.prototype.cloneMessage = function() {
+  return /** @type {!proto.math.FibArgs} */ (jspb.Message.cloneMessage(this));
+};
+
+
+/**
+ * optional int64 limit = 1;
+ * @return {number}
+ */
+proto.math.FibArgs.prototype.getLimit = function() {
+  return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
+};
+
+
+/** @param {number} value  */
+proto.math.FibArgs.prototype.setLimit = function(value) {
+  jspb.Message.setField(this, 1, value);
+};
+
+
+
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.math.Num = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.math.Num, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  proto.math.Num.displayName = 'proto.math.Num';
+}
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ *     for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.math.Num.prototype.toObject = function(opt_includeInstance) {
+  return proto.math.Num.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ *     instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.math.Num} msg The msg instance to transform.
+ * @return {!Object}
+ */
+proto.math.Num.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    num: msg.getNum()
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.math.Num}
+ */
+proto.math.Num.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.math.Num;
+  return proto.math.Num.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.math.Num} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.math.Num}
+ */
+proto.math.Num.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt64());
+      msg.setNum(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Class method variant: serializes the given message to binary data
+ * (in protobuf wire format), writing to the given BinaryWriter.
+ * @param {!proto.math.Num} message
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.Num.serializeBinaryToWriter = function(message, writer) {
+  message.serializeBinaryToWriter(writer);
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.math.Num.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  this.serializeBinaryToWriter(writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format),
+ * writing to the given BinaryWriter.
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.Num.prototype.serializeBinaryToWriter = function (writer) {
+  var f = undefined;
+  f = this.getNum();
+  if (f !== 0) {
+    writer.writeInt64(
+      1,
+      f
+    );
+  }
+};
+
+
+/**
+ * Creates a deep clone of this proto. No data is shared with the original.
+ * @return {!proto.math.Num} The clone.
+ */
+proto.math.Num.prototype.cloneMessage = function() {
+  return /** @type {!proto.math.Num} */ (jspb.Message.cloneMessage(this));
+};
+
+
+/**
+ * optional int64 num = 1;
+ * @return {number}
+ */
+proto.math.Num.prototype.getNum = function() {
+  return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
+};
+
+
+/** @param {number} value  */
+proto.math.Num.prototype.setNum = function(value) {
+  jspb.Message.setField(this, 1, value);
+};
+
+
+
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.math.FibReply = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.math.FibReply, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  proto.math.FibReply.displayName = 'proto.math.FibReply';
+}
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ *     for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.math.FibReply.prototype.toObject = function(opt_includeInstance) {
+  return proto.math.FibReply.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ *     instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.math.FibReply} msg The msg instance to transform.
+ * @return {!Object}
+ */
+proto.math.FibReply.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    count: msg.getCount()
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.math.FibReply}
+ */
+proto.math.FibReply.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.math.FibReply;
+  return proto.math.FibReply.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.math.FibReply} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.math.FibReply}
+ */
+proto.math.FibReply.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt64());
+      msg.setCount(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Class method variant: serializes the given message to binary data
+ * (in protobuf wire format), writing to the given BinaryWriter.
+ * @param {!proto.math.FibReply} message
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.FibReply.serializeBinaryToWriter = function(message, writer) {
+  message.serializeBinaryToWriter(writer);
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.math.FibReply.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  this.serializeBinaryToWriter(writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format),
+ * writing to the given BinaryWriter.
+ * @param {!jspb.BinaryWriter} writer
+ */
+proto.math.FibReply.prototype.serializeBinaryToWriter = function (writer) {
+  var f = undefined;
+  f = this.getCount();
+  if (f !== 0) {
+    writer.writeInt64(
+      1,
+      f
+    );
+  }
+};
+
+
+/**
+ * Creates a deep clone of this proto. No data is shared with the original.
+ * @return {!proto.math.FibReply} The clone.
+ */
+proto.math.FibReply.prototype.cloneMessage = function() {
+  return /** @type {!proto.math.FibReply} */ (jspb.Message.cloneMessage(this));
+};
+
+
+/**
+ * optional int64 count = 1;
+ * @return {number}
+ */
+proto.math.FibReply.prototype.getCount = function() {
+  return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
+};
+
+
+/** @param {number} value  */
+proto.math.FibReply.prototype.setCount = function(value) {
+  jspb.Message.setField(this, 1, value);
+};
+
+
+goog.object.extend(exports, proto.math);
diff --git a/src/node/test/math/math_server.js b/src/node/test/math/math_server.js
index 9f67c52..fa05ed0 100644
--- a/src/node/test/math/math_server.js
+++ b/src/node/test/math/math_server.js
@@ -34,8 +34,8 @@
 'use strict';
 
 var grpc = require('../..');
-var math = grpc.load(__dirname + '/../../../proto/math/math.proto').math;
-
+var grpcMath = require('./math_grpc_pb');
+var math = require('./math_pb');
 
 /**
  * Server function for division. Provides the /Math/DivMany and /Math/Div
@@ -46,14 +46,16 @@
  */
 function mathDiv(call, cb) {
   var req = call.request;
+  var divisor = req.getDivisor();
+  var dividend = req.getDividend();
   // Unary + is explicit coersion to integer
-  if (+req.divisor === 0) {
+  if (req.getDivisor() === 0) {
     cb(new Error('cannot divide by zero'));
   } else {
-    cb(null, {
-      quotient: req.dividend / req.divisor,
-      remainder: req.dividend % req.divisor
-    });
+    var response = new math.DivReply();
+    response.setQuotient(Math.floor(dividend / divisor));
+    response.setRemainder(dividend % divisor);
+    cb(null, response);
   }
 }
 
@@ -67,7 +69,9 @@
   // Here, call is a standard writable Node object Stream
   var previous = 0, current = 1;
   for (var i = 0; i < stream.request.limit; i++) {
-    stream.write({num: current});
+    var response = new math.Num();
+    response.setNum(current);
+    stream.write(response);
     var temp = current;
     current += previous;
     previous = temp;
@@ -85,22 +89,26 @@
   // Here, call is a standard readable Node object Stream
   var sum = 0;
   call.on('data', function(data) {
-    sum += (+data.num);
+    sum += data.getNum();
   });
   call.on('end', function() {
-    cb(null, {num: sum});
+    var response = new math.Num();
+    response.setNum(sum);
+    cb(null, response);
   });
 }
 
 function mathDivMany(stream) {
   stream.on('data', function(div_args) {
-    if (+div_args.divisor === 0) {
+    var divisor = div_args.getDivisor();
+    var dividend = div_args.getDividend();
+    if (divisor === 0) {
       stream.emit('error', new Error('cannot divide by zero'));
     } else {
-      stream.write({
-        quotient: div_args.dividend / div_args.divisor,
-        remainder: div_args.dividend % div_args.divisor
-      });
+      var response = new math.DivReply();
+      response.setQuotient(Math.floor(dividend / divisor));
+      response.setRemainder(dividend % divisor);
+      stream.write(response);
     }
   });
   stream.on('end', function() {
@@ -110,7 +118,7 @@
 
 function getMathServer() {
   var server = new grpc.Server();
-  server.addProtoService(math.Math.service, {
+  server.addService(grpcMath.MathService, {
     div: mathDiv,
     fib: mathFib,
     sum: mathSum,
diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js
index 3d44610..34c16e0 100644
--- a/src/node/test/math_client_test.js
+++ b/src/node/test/math_client_test.js
@@ -36,7 +36,8 @@
 var assert = require('assert');
 
 var grpc = require('..');
-var math = grpc.load(__dirname + '/../../proto/math/math.proto').math;
+var math = require('./math/math_pb');
+var MathClient = require('./math/math_grpc_pb').MathClient;
 
 /**
  * Client to use to make requests to a running server.
@@ -55,35 +56,41 @@
     var port_num = server.bind('0.0.0.0:0',
                                grpc.ServerCredentials.createInsecure());
     server.start();
-    math_client = new math.Math('localhost:' + port_num,
-                                grpc.credentials.createInsecure());
+    math_client = new MathClient('localhost:' + port_num,
+                                 grpc.credentials.createInsecure());
     done();
   });
   after(function() {
     server.forceShutdown();
   });
   it('should handle a single request', function(done) {
-    var arg = {dividend: 7, divisor: 4};
+    var arg = new math.DivArgs();
+    arg.setDividend(7);
+    arg.setDivisor(4);
     math_client.div(arg, function handleDivResult(err, value) {
       assert.ifError(err);
-      assert.equal(value.quotient, 1);
-      assert.equal(value.remainder, 3);
+      assert.equal(value.getQuotient(), 1);
+      assert.equal(value.getRemainder(), 3);
       done();
     });
   });
   it('should handle an error from a unary request', function(done) {
-    var arg = {dividend: 7, divisor: 0};
+    var arg = new math.DivArgs();
+    arg.setDividend(7);
+    arg.setDivisor(0);
     math_client.div(arg, function handleDivResult(err, value) {
       assert(err);
       done();
     });
   });
   it('should handle a server streaming request', function(done) {
-    var call = math_client.fib({limit: 7});
+    var arg = new math.FibArgs();
+    arg.setLimit(7);
+    var call = math_client.fib(arg);
     var expected_results = [1, 1, 2, 3, 5, 8, 13];
     var next_expected = 0;
     call.on('data', function checkResponse(value) {
-      assert.equal(value.num, expected_results[next_expected]);
+      assert.equal(value.getNum(), expected_results[next_expected]);
       next_expected += 1;
     });
     call.on('status', function checkStatus(status) {
@@ -94,10 +101,12 @@
   it('should handle a client streaming request', function(done) {
     var call = math_client.sum(function handleSumResult(err, value) {
       assert.ifError(err);
-      assert.equal(value.num, 21);
+      assert.equal(value.getNum(), 21);
     });
     for (var i = 0; i < 7; i++) {
-      call.write({'num': i});
+      var arg = new math.Num();
+      arg.setNum(i);
+      call.write(arg);
     }
     call.end();
     call.on('status', function checkStatus(status) {
@@ -107,8 +116,8 @@
   });
   it('should handle a bidirectional streaming request', function(done) {
     function checkResponse(index, value) {
-      assert.equal(value.quotient, index);
-      assert.equal(value.remainder, 1);
+      assert.equal(value.getQuotient(), index);
+      assert.equal(value.getRemainder(), 1);
     }
     var call = math_client.divMany();
     var response_index = 0;
@@ -117,7 +126,10 @@
       response_index += 1;
     });
     for (var i = 0; i < 7; i++) {
-      call.write({dividend: 2 * i + 1, divisor: 2});
+      var arg = new math.DivArgs();
+      arg.setDividend(2 * i + 1);
+      arg.setDivisor(2);
+      call.write(arg);
     }
     call.end();
     call.on('status', function checkStatus(status) {
@@ -131,7 +143,10 @@
       assert.fail(value, undefined, 'Unexpected data response on failing call',
                   '!=');
     });
-    call.write({dividend: 7, divisor: 0});
+    var arg = new math.DivArgs();
+    arg.setDividend(7);
+    arg.setDivisor(0);
+    call.write(arg);
     call.end();
     call.on('error', function checkStatus(status) {
       done();
diff --git a/templates/package.json.template b/templates/package.json.template
index 5db2706..564df84 100644
--- a/templates/package.json.template
+++ b/templates/package.json.template
@@ -21,7 +21,7 @@
       "lib": "src/node/src"
     },
     "scripts": {
-      "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js",
+      "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js --exclude-path=src/node/.jshintignore",
       "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
       "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
       "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test",
@@ -74,5 +74,25 @@
       "binding.gyp"
     ],
     "main": "src/node/index.js",
-    "license": "BSD-3-Clause"
+    "license": "BSD-3-Clause",
+    "jshintConfig" : {
+      "bitwise": true,
+      "curly": true,
+      "eqeqeq": true,
+      "esnext": true,
+      "freeze": true,
+      "immed": true,
+      "indent": 2,
+      "latedef": "nofunc",
+      "maxlen": 80,
+      "mocha": true,
+      "newcap": true,
+      "node": true,
+      "noarg": true,
+      "quotmark": "single",
+      "strict": true,
+      "trailing": true,
+      "undef": true,
+      "unused": "vars"
+    }
   }