diff --git a/CI/generate-docs.sh b/CI/generate-docs.sh
index 54b07dd9..2afdaacb 100755
--- a/CI/generate-docs.sh
+++ b/CI/generate-docs.sh
@@ -1,4 +1,5 @@
 #!/bin/bash
+set -e
 echo "-- Generating documentation."
 echo "-- Node version: $(node -v)"
 echo "-- NPM version: $(npm -v)"
diff --git a/docs/comments.js b/docs/comments.js
index 5536a427..46a00fc2 100644
--- a/docs/comments.js
+++ b/docs/comments.js
@@ -1,6 +1,8 @@
 const fs = require('fs');
+const path = require('path');
 const glob = require('glob');
 const parseComments = require('parse-comments');
+const config = require('./config.json');
 
 /**
  * Read each file and call `parse-comments` on it.
@@ -25,9 +27,15 @@ const parseFiles = files => {
  */
 const processComments = comments => {
     let sorted = {};
+    let errors = [];
 
     comments.forEach(comment => {
         if (typeof comment.api === 'undefined') return;
+        let validationFailures = validateComment(comment);
+
+        if (validationFailures) {
+            errors.push(validationFailures);
+        }
 
         // Store the object based on its api (ie. requests, events) and category (ie. general, scenes, etc).
         comment.category = comment.category || 'miscellaneous';
@@ -44,9 +52,47 @@ const processComments = comments => {
         sorted[comment.api][comment.category].push(comment);
     });
 
+    if (errors.length) {
+        throw JSON.stringify(errors, null, 2);
+    }
+
     return sorted;
 };
 
-const files = glob.sync("./../src/*.@(cpp|h)");
+// Rudimentary validation of documentation content, returns an error object or undefined.
+const validateComment = comment => {
+    let errors = [];
+    [].concat(comment.params).concat(comment.returns).filter(Boolean).forEach(param => {
+        if (typeof param.name !== 'string' || param.name === '') {
+            errors.push({
+                description: `Invalid param or return value name`,
+                param: param
+            });
+        }
+
+        if (typeof param.type !== 'string' || param.type === '') {
+            errors.push({
+                description: `Invalid param or return value type`,
+                param: param
+            });
+        }
+    });
+
+    if (errors.length) {
+        return {
+            errors: errors,
+            fullContext: Object.assign({}, comment)
+        };
+    }
+
+    return;
+}
+
+const files = glob.sync(config.srcGlob);
 const comments = processComments(parseFiles(files));
-fs.writeFileSync('./generated/comments.json', JSON.stringify(comments, null, 2));
+
+if (!fs.existsSync(config.outDirectory)){
+    fs.mkdirSync(config.outDirectory);
+}
+
+fs.writeFileSync(path.join(config.outDirectory, 'comments.json'), JSON.stringify(comments, null, 2));
diff --git a/docs/config.json b/docs/config.json
new file mode 100644
index 00000000..ab2a2391
--- /dev/null
+++ b/docs/config.json
@@ -0,0 +1,5 @@
+{
+    "srcGlob": "./../src/**/*.@(cpp|h)",
+    "srcTemplate": "./protocol.hbs",
+    "outDirectory": "./generated"
+}
diff --git a/docs/docs.js b/docs/docs.js
index f0353091..b66cb0f3 100644
--- a/docs/docs.js
+++ b/docs/docs.js
@@ -1,6 +1,8 @@
 const fs = require('fs');
+const path = require('path');
 const toc = require('markdown-toc');
 const handlebars = require('handlebars');
+const config = require('./config.json');
 
 const helpers = require('handlebars-helpers')({
     handlebars: handlebars
@@ -8,10 +10,7 @@ const helpers = require('handlebars-helpers')({
 
 // Allows pipe characters to be used within markdown tables.
 handlebars.registerHelper('depipe', (text) => {
-    if (!text) {
-        return text;
-    }
-    return text.replace('|', `\\|`);
+    return typeof text === 'string' ? text.replace('|', '\\|') : text;
 });
 
 const insertHeader = (text) => {
@@ -29,6 +28,10 @@ const generateProtocol = (templatePath, data) => {
     return insertHeader(toc.insert(generated));
 };
 
-const comments = fs.readFileSync('./generated/comments.json', 'utf8');
-const markdown = generateProtocol('./protocol.hbs', JSON.parse(comments));
-fs.writeFileSync('./generated/protocol.md', markdown);
+if (!fs.existsSync(config.outDirectory)){
+    fs.mkdirSync(config.outDirectory);
+}
+
+const comments = fs.readFileSync(path.join(config.outDirectory, 'comments.json'), 'utf8');
+const markdown = generateProtocol(config.srcTemplate, JSON.parse(comments));
+fs.writeFileSync(path.join(config.outDirectory, 'protocol.md'), markdown);
diff --git a/docs/protocol.hbs b/docs/protocol.hbs
index 592d03b7..313a5442 100644
--- a/docs/protocol.hbs
+++ b/docs/protocol.hbs
@@ -33,7 +33,7 @@
 | Name | Type  | Description |
 | ---- | :---: | ------------|
 {{#each returns}}
-| `{{name}}` | _{{depipe type}}_ | {{{description}}} |
+| `{{name}}` | _{{depipe type}}_ | {{{depipe description}}} |
 {{/each}}
 
 {{else}}
@@ -73,7 +73,7 @@ _No additional response items._
 | Name | Type  | Description |
 | ---- | :---: | ------------|
 {{#each params}}
-| `{{name}}` | _{{depipe type}}_ | {{{description}}} |
+| `{{name}}` | _{{depipe type}}_ | {{{depipe description}}} |
 {{/each}}
 
 {{else}}
@@ -86,7 +86,7 @@ _No specified parameters._
 | Name | Type  | Description |
 | ---- | :---: | ------------|
 {{#each returns}}
-| `{{name}}` | _{{depipe type}}_ | {{{description}}} |
+| `{{name}}` | _{{depipe type}}_ | {{{depipe description}}} |
 {{/each}}
 
 {{else}}