diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index bf7969a0c..00e74b1da 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -551,22 +551,23 @@ class Runtime extends EventEmitter {
      */
     _buildMenuForScratchBlocks (menuName, menuItems, categoryInfo) {
         const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id);
-
-        /** @TODO: support dynamic menus when 'menuItems' is a method name string (see extension spec) */
-        if (typeof menuItems === 'string') {
-            throw new Error(`Dynamic extension menus are not yet supported. Menu name: ${menuName}`);
-        }
-        const options = menuItems.map(item => {
-            switch (typeof item) {
-            case 'string':
-                return [item, item];
-            case 'object':
-                return [item.text, item.value];
-            default:
-                throw new Error(`Can't interpret menu item: ${item}`);
+        var options = null;
+        if (typeof menuItems === 'function') {
+             options = function () {
+                return menuItems();
             }
-        });
-
+        } else {
+            options = menuItems.map(item => {
+                switch (typeof item) {
+                case 'string':
+                    return [item, item];
+                case 'object':
+                    return [item.text, item.value];
+                default:
+                    throw new Error(`Can't interpret menu item: ${item}`);
+                }
+            });
+        }
         return {
             json: {
                 message0: '%1',
diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js
index 3228f5c2a..1fb4795b2 100644
--- a/src/extension-support/extension-manager.js
+++ b/src/extension-support/extension-manager.js
@@ -242,8 +242,34 @@ class ExtensionManager {
             }
             return result;
         }, []);
+        extensionInfo.menus = extensionInfo.menus || [];
+        extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus);
         return extensionInfo;
     }
+    
+    /**
+     * Prepare extension menus. e.g. setup binding for dynamic menu functions.
+     * @param {string} serviceName - the name of the service hosting this extension block
+     * @param {Array.<MenuInfo>} menuInfo - the menu defined by the extension.
+     * @returns {Array.<MenuInfo>} - a menuInfo object with all preprocessing done.
+     * @private
+     */
+    _prepareMenuInfo (serviceName, menus) {
+        var menuNames = Object.getOwnPropertyNames(menus);
+        for (let i = 0; i < menuNames.length; i++) {
+            var item = menuNames[i];
+            // If the value is a string, it should be the name of a function in the
+            // extension object to call to populate the menu whenever it is opened.
+            // Set up the binding for the function object here so
+            // we can use it later when converting the menu for Scratch Blocks.
+            if (typeof menus[item] === 'string') {
+              const serviceObject = dispatch.services[serviceName];
+              const menuFunc = serviceObject[menus[item]].bind(serviceObject);
+              menus[item] = menuFunc;
+            }
+        }
+        return menus;
+    }
 
     /**
      * Apply defaults for optional block fields.