diff --git a/amp.admin.inc b/amp.admin.inc
index 782db2a..93d95b1 100644
--- a/amp.admin.inc
+++ b/amp.admin.inc
@@ -15,6 +15,18 @@
 function amp_admin_form($form, &$form_state) {
   $form = array();
 
+  if (!module_exists('token')) {
+    // Provide message in case somebody has upgraded AMP module but has not
+    // installed Token.
+    drupal_set_message(t('The AMP module requires the <a href="@module">Token</a> module as a dependency. Please download and install Token to prevent errors with AMP.', array('@module' => 'https://www.drupal.org/project/token')), 'warning');
+  }
+
+  if (!module_exists('ctools')) {
+    // Provide message in case somebody has upgraded AMP module but has not
+    // installed ctools.
+    drupal_set_message(t('The AMP module requires the <a href="@module">ctools</a> module as a dependency. Please download and install ctools to prevent errors with AMP.', array('@module' => 'https://www.drupal.org/project/ctools')), 'warning');
+  }
+
   if (module_exists('field_ui')) {
     $form['amp_content_amp_status'] = array(
       '#title' => t('AMP Status by Content Type'),
@@ -270,3 +282,118 @@ function amp_get_formatted_status_list() {
   }
   return $node_status_list;
 }
+
+/**
+ * Form constructor for the AMP metadata form.
+ */
+function amp_admin_metadata_form($form, &$form_state) {
+  if (!module_exists('token')) {
+    // Provide message in case somebody has upgraded AMP module but has not
+    // installed Token.
+    drupal_set_message(t('The AMP module requires the <a href="@module">Token</a> module as a dependency. Please download and install Token to prevent errors with AMP.', array('@module' => 'https://www.drupal.org/project/token')), 'warning');
+  }
+
+  if (!module_exists('ctools')) {
+    // Provide message in case somebody has upgraded AMP module but has not
+    // installed ctools.
+    drupal_set_message(t('The AMP module requires the <a href="@module">ctools</a> module as a dependency. Please download and install ctools to prevent errors with AMP.', array('@module' => 'https://www.drupal.org/project/ctools')), 'warning');
+  }
+
+
+  $form['amp_metadata_organization'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Organization information'),
+  );
+
+  $form['amp_metadata_organization']['description'] = array(
+    '#type' => 'item',
+    '#description' => t('Provide information about your organization for use in search metadata.'),
+  );
+
+  $form['amp_metadata_organization']['amp_metadata_token_tree'] = array(
+    '#theme' => 'token_tree',
+    '#token_types' => array('site'),
+    '#dialog' => TRUE,
+  );
+
+  $form['amp_metadata_organization']['amp_metadata_organization_name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Organization name'),
+    '#description' => t('
+      <p>Name of the publisher of the content. Typically, this can be the same as the site name.</p>
+      <p>Suggested token: [site:name]</p>'),
+    '#required' => TRUE,
+    '#attributes' => ['placeholder' => '[site:name]'],
+    '#default_value' => variable_get('amp_metadata_organization_name', NULL)
+  );
+
+  $form['amp_metadata_organization']['amp_metadata_organization_logo'] = array(
+    '#type' => 'managed_file',
+    '#title' => t('Organization logo'),
+    '#description' => t('
+      <p>Upload a logo for your organization.</p>
+      <p>This logo must have a height of 60px and a width less than 600px. SVG logos are not allowed: please provide a JPG, JPEG, GIF or PNG.</p>
+      <p>See the AMP <a href="@logo_guidelines">logo guidelines</a>.</p>', array('@logo_guidelines' => 'https://developers.google.com/search/docs/data-types/articles#amp-logo-guidelines')),
+    '#upload_location' => 'public://',
+    '#upload_validators' => array(
+      'file_validate_extensions' => array('png jpg'),
+    ),
+    '#required' => TRUE,
+    '#default_value' => variable_get('amp_metadata_organization_logo', FALSE),
+  );
+
+  $enabled_types = amp_get_enabled_types();
+  if (isset($enabled_types)) {
+    $node_types = node_type_get_names();
+    $enabled_type_list = array();
+    foreach ($enabled_types as $type) {
+      $enabled_type_list[] = $node_types[$type] . t(': <a href="@configure">Edit AMP metadata settings</a>', array(
+          '@configure' => '/admin/structure/types/manage/' . $type . '?destination=/admin/config/content/amp/metadata',
+        ));
+    }
+
+    $form['amp_metadata_content_types'] = array(
+      '#title' => 'Content information from AMP-enabled content types',
+      '#theme' => 'item_list',
+      '#items' => $enabled_type_list,
+    );
+  }
+  else {
+    $form['amp_metadata_content_types'] = array(
+      '#markup' => t('No content types are currently enabled for AMP. <a href="@configure">Enable them here</a>.', array(
+        '@configure' => '/admin/config/content/amp',
+      )),
+    );
+  }
+
+  $form['#submit'] = array('amp_admin_metadata_form_submit');
+  return system_settings_form($form);
+}
+
+/**
+ * Submit handler for the amp_admin_metadata_form.
+ * Sets the logo file to permanent.
+ */
+function amp_admin_metadata_form_submit($form, &$form_state) {
+  $old_fid = variable_get('amp_metadata_organization_logo', 0);
+  if ($form_state['values']['amp_metadata_organization_logo'] != $old_fid) {
+
+    if ($form_state['values']['amp_metadata_organization_logo'] != 0) {
+      $file = file_load($form_state['values']['amp_metadata_organization_logo']);
+
+      // If this is a new file...
+      if (!$file->status) {
+        $file->status = FILE_STATUS_PERMANENT;
+        file_save($file);
+        file_usage_add($file, 'amp', 'logo', 1);
+      }
+    }
+
+    // Delete the old file
+    $old_file = $old_fid ? file_load($old_fid) : FALSE;
+    if ($old_file) {
+      file_usage_delete($old_file, 'amp', 'logo', 1);
+      file_delete($old_file);
+    }
+  }
+}
diff --git a/amp.info b/amp.info
index f0bab35..849af03 100644
--- a/amp.info
+++ b/amp.info
@@ -3,4 +3,6 @@ description = Google AMP integration
 core = 7.x
 configure = admin/config/content/amp
 php = 5.5
+dependencies[] = token:token
+dependencies[] = ctools:ctools
 files[] = amp.test
diff --git a/amp.install b/amp.install
index 7f162de..2a0acda 100644
--- a/amp.install
+++ b/amp.install
@@ -20,6 +20,22 @@ function amp_requirements($phase) {
         'severity' => REQUIREMENT_ERROR,
       );
     }
+    if (!module_exists('token')) {
+      $requirements['amp_token'] = array(
+        'title' => t('Token module required for AMP'),
+        'value' => t('Not installed'),
+        'description' => t('The AMP module requires the <a href="@module">Token</a> module as a dependency. Please download and install Token to prevent errors with AMP.', array('@module' => 'https://www.drupal.org/project/token')),
+        'severity' => REQUIREMENT_ERROR,
+      );
+    }
+    if (!module_exists('ctools')) {
+      $requirements['amp_ctools'] = array(
+        'title' => t('ctools module required for AMP'),
+        'value' => t('Not installed'),
+        'description' => t('The AMP module requires the <a href="@module">ctools</a> module as a dependency. Please download and install ctools to prevent errors with AMP.', array('@module' => 'https://www.drupal.org/project/ctools')),
+        'severity' => REQUIREMENT_ERROR,
+      );
+    }
     $themes = list_themes();
     if (empty($themes['amptheme']) || empty($themes['amptheme']->status)) {
       $requirements['amptheme'] = array(
diff --git a/amp.module b/amp.module
index dfbdab6..1133e25 100644
--- a/amp.module
+++ b/amp.module
@@ -46,6 +46,22 @@ function amp_menu() {
     'type' => MENU_NORMAL_ITEM,
     'file' => 'amp.admin.inc',
   );
+
+  $items['admin/config/content/amp/config'] = array(
+    'title' => 'AMP Configuration',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'file' => 'amp.admin.inc',
+  );
+
+  $items['admin/config/content/amp/metadata'] = array(
+    'title' => 'AMP Metadata',
+    'description' => 'Configure AMP Metadata',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('amp_admin_metadata_form'),
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'amp.admin.inc',
+  );
   return $items;
 }
 
@@ -424,6 +440,125 @@ function amp_node_form_submit_warnfix(&$form, $form_state) {
 }
 
 /**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function amp_form_node_type_form_alter(&$form, &$form_state, $form_id) {
+  $enabled_types = amp_get_enabled_types();
+  if (in_array($form['#node_type']->type, $enabled_types)) {
+    $form['amp_metadata'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('AMP Metadata'),
+      '#group' => 'additional_settings',
+      '#weight' => 100,
+    );
+
+    $form['amp_metadata']['description'] = array(
+      '#type' => 'item',
+      '#title' => 'Content information',
+      '#description' => t('Provide information about your content for use in the Top Stories carousel in Google Search.'),
+    );
+
+
+    $form['amp_metadata']['amp_metadata_options'] = array(
+      '#type' => 'container',
+      '#tree' => 'true',
+    );
+
+    $amp_metadata_fields = _amp_get_metadata_type_fields();
+
+    $settings = variable_get('amp_metadata_options_' . $form['#node_type']->type);
+
+    foreach ($amp_metadata_fields as $name => $field) {
+      $form_options['amp_metadata_config_' . $name] = array(
+        '#type' => 'textfield',
+        '#title' => t('@title', array('@title' => ucfirst($name))),
+        '#default_value' => isset($settings['amp_metadata_config_' . $name]) ? $settings['amp_metadata_config_' . $name] : $field['value'],
+      );
+      switch ($name) {
+        case 'schemaType':
+          $schema_type_options = array(
+            'Article' => 'Article',
+            'NewsArticle' => 'NewsArticle',
+            'BlogPosting' => 'BlogPosting',
+          );
+          $form_options['amp_metadata_config_' . $name]['#title'] = 'AMP schema type';
+          $form_options['amp_metadata_config_' . $name]['#type'] = 'select';
+          $form_options['amp_metadata_config_' . $name]['#options'] = $schema_type_options;
+          $form_options['amp_metadata_config_' . $name]['#description'] = t('The type of schema to use on AMP pages');
+          break;
+        case 'headline':
+          $form_options['amp_metadata_config_' . $name]['#title'] = 'Article headline';
+          $form_options['amp_metadata_config_' . $name]['#description'] = t('
+          <p>A short headline for an AMP article, using fewer than 110 characters and no HTML markup.</p>
+          <p>Use tokens to provide the correct headline for each article page. Suggested token: [node:title].'
+          );
+          $form_options['amp_metadata_config_' . $name]['#attributes'] = ['placeholder' => '[node:title]'];
+          break;
+        case 'datePublished':
+          $form_options['amp_metadata_config_' . $name]['#title'] = 'Date published';
+          $form_options['amp_metadata_config_' . $name]['#description'] = t('
+          <p>The date an article was published.</p>
+          <p>Use tokens to provide the correct published date for each article. Suggested token: [node:created].'
+          );
+          $form_options['amp_metadata_config_' . $name]['#attributes'] = ['placeholder' => '[node:created]'];
+          break;
+        case 'dateModified':
+          $form_options['amp_metadata_config_' . $name]['#title'] = 'Date modified';
+          $form_options['amp_metadata_config_' . $name]['#description'] = t('
+          <p>The date an article was most recently modified date.</p>
+          <p>Use tokens to provide the correct modification date for each article. Suggested token: [node:changed].'
+          );
+          $form_options['amp_metadata_config_' . $name]['#attributes'] = ['placeholder' => '[node:changed]'];
+          break;
+        case 'author':
+          $form_options['amp_metadata_config_' . $name]['#title'] = 'Author';
+          $form_options['amp_metadata_config_' . $name]['#description'] = t('
+          <p>The name of the author to use on AMP pages.</p>
+          <p>Use tokens to provide the correct author for each article. Token output should be text only, no HTML markup. Suggested token: [node:author:name].'
+          );
+          $form['amp_metadata_config_' . $name]['#attributes'] = ['placeholder' => '[node:author:name]'];
+          break;
+        case 'description':
+          $form_options['amp_metadata_config_' . $name]['#title'] = 'Article description';
+          $form_options['amp_metadata_config_' . $name]['#description'] = t('
+          <p>A short description of an AMP article, using fewer than 150 characters and no HTML markup.</p>
+          <p>Use tokens to provide the correct description for each article. Suggested token: [node:summary].'
+          );
+          $form_options['amp_metadata_config_' . $name]['#attributes'] = ['placeholder' => '[node:summary]'];
+          break;
+        case 'image':
+          $form_options['amp_metadata_config_' . $name]['#title'] = 'Article image for carousel';
+          $form_options['amp_metadata_config_' . $name]['#description'] = t('
+          <p>An article image to appear in the Top Stories carousel.</p>
+          <p>Images must be at least 696px wide: refer to <a href="@image_guidelines">article image guidelines</a> for further details.</p>
+          <p>Use tokens to provide the correct image for each article. Example token: [node:field_image]. The image field token likely varies by content type.',
+            array('@image_guidelines' => 'https://developers.google.com/search/docs/data-types/articles#article_types')
+          );
+          $form_options['amp_metadata_config_' . $name]['#attributes'] = ['placeholder' => '[node:field_image]'];
+          break;
+      }
+    }
+
+    $form['amp_metadata']['amp_metadata_options'] += $form_options;
+
+    $form['amp_metadata']['amp_metadata_token_tree'] = array(
+      '#theme' => 'token_tree',
+      '#token_types' => array('node'),
+      '#dialog' => TRUE,
+    );
+
+    $form['#submit'][] = 'amp_metadata_node_type_submit';
+  }
+}
+
+/**
+ * Additional submit handler for the node type form.
+ */
+function amp_metadata_node_type_submit(&$form, $form_state) {
+  variable_set('amp_metadata_options_' . $form['#node_type']->type, $form_state['values']['amp_metadata_options']);
+}
+
+/**
  * Implements hook_form_alter().
  */
 function amp_form_alter(&$form, &$form_state, $form_id) {
@@ -511,6 +646,97 @@ function amp_node_view($node, $view_mode, $langcode) {
     $uri['options']['absolute'] = TRUE;
     drupal_add_html_head_link(array('rel' => 'amphtml', 'href' => url($uri['path'], $uri['options'])), TRUE);
   }
+
+  // Add AMP metadata.
+  if ($view_mode == 'amp' && node_is_page($node)) {
+    $metadata = variable_get('amp_metadata_options_' . $node->type);
+    $amp_metadata_type_fields = _amp_get_metadata_type_fields();
+
+    $metadata_json['@context'] = 'http://schema.org';
+
+    $main_uri = entity_uri('node', $node);
+    $main_uri['options']['absolute'] = TRUE;
+    $metadata_json['mainEntityOfPage'] = url($main_uri['path'], $main_uri['options']);
+
+    foreach ($amp_metadata_type_fields as $name => $values) {
+      if ($values['type'] == 'schema') {
+        $metadata_json['@type'] = $metadata['amp_metadata_config_' . $name];
+      }
+      elseif ($values['type'] == 'text') {
+        $metadata_json[$name] = $metadata['amp_metadata_config_' . $name];
+      }
+      elseif ($values['type'] == 'date') {
+        $metadata_json[$name] = $metadata['amp_metadata_config_' . $name];
+      }
+      elseif ($values['type'] == 'Person') {
+        $metadata_json[$name] = array(
+          '@type' => 'Person',
+          'name' => $metadata['amp_metadata_config_' . $name],
+        );
+      }
+      elseif ($values['type'] == 'ImageObject') {
+        $image_url = token_replace($metadata['amp_metadata_config_' . $name], array('node' => $node));
+
+        // Provide backup parsing of image element if token does not output a URL.
+        if (strip_tags($image_url) != $image_url) {
+          // Force path to be absolute.
+          if (strpos($image_url, 'img src="/') !== FALSE) {
+            global $base_root;
+            $image_url = str_replace('img src="/', 'img src="' . $base_root . '/', $image_url);
+          }
+
+          $matches = array();
+          preg_match('/src="([^"]*)"/', $image_url, $matches);
+          if (!empty($matches[1])) {
+            $image_url = $matches[1];
+          }
+        }
+
+        if (!empty($image_url)) {
+          $image_info = getimagesize($image_url);
+          if ($image_info) {
+            $metadata_json[$name] = array(
+              '@type' => 'ImageObject',
+              'url' => $image_url,
+              'width' => $image_info[0],
+              'height' => $image_info[1],
+            );
+          }
+        }
+      }
+
+      // Add global publisher info.
+      $metadata_json['publisher'] = array(
+        '@type' => 'Organization',
+        'name' => variable_get('amp_metadata_organization_name', ''),
+      );
+
+      $logo_fid = variable_get('amp_metadata_organization_logo', NULL);
+      if ($logo_fid) {
+        $logo = file_load($logo_fid);
+        $logo_url = file_create_url($logo->uri);
+        $logo_info = getimagesize($logo_url);
+        $metadata_json['publisher']['logo'] = array(
+          '@type' => 'ImageObject',
+          'url' => $logo_url,
+          'width' => $logo_info[0],
+          'height' => $logo_info[1],
+        );
+      }
+    }
+
+    drupal_alter('amp_metadata', $metadata_json, $node, $type);
+
+    $element = array(
+      '#tag' => 'script',
+      '#type' => 'html_tag',
+      '#attributes' => array(
+        'type' => 'application/ld+json',
+      ),
+      '#value' => strip_tags(token_replace(json_encode($metadata_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT), array('node' => $node))),
+    );
+    drupal_add_html_head($element, 'amp_metadata');
+  }
 }
 
 /**
@@ -1214,3 +1440,21 @@ function amp_html_head_alter(&$head_elements) {
     }
   }
 }
+
+/**
+ * Helper function to return information about metadata node type fields.
+ *
+ * @return array
+ */
+function _amp_get_metadata_type_fields() {
+
+  return array(
+    'schemaType' => array('value' => 'NewsArticle', 'type' => 'schema'),
+    'headline' => array('value' => '[node:title]', 'type' => 'text'),
+    'datePublished' => array('value' => '[node:created]', 'type' => 'date'),
+    'dateModified' => array('value' => '[node:changed]', 'type' => 'date'),
+    'description' => array('value' => '[node:summary]', 'type' => 'text'),
+    'author' => array('value' => '[node:author:name]', 'type' => 'Person'),
+    'image' => array('value' => '', 'type' => 'ImageObject'),
+  );
+}
