diff --git a/core/modules/menu/lib/Drupal/menu/Controller/MenuController.php b/core/modules/menu/lib/Drupal/menu/Controller/MenuController.php
new file mode 100644
index 0000000..a2b69a8
--- /dev/null
+++ b/core/modules/menu/lib/Drupal/menu/Controller/MenuController.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu\Controller\MenuController.
+ */
+
+namespace Drupal\menu\Controller;
+
+use Drupal\Core\ControllerInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\system\Plugin\Core\Entity\Menu;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Returns responses for Menu routes.
+ */
+class MenuController implements ControllerInterface {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a new MenuController.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *  The entity manager.
+   */
+  public function __construct($entity_manager) {
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.entity')
+    );
+  }
+
+  /**
+   * Shows an overview page of all the custom menus and their descriptions.
+   *
+   * @return array
+   *   A render array of the listing page.
+   */
+  public function overview() {
+    return $this->entityManager->getListController('menu')->render();
+  }
+
+  /**
+   * Gets all the available menus and menu items as a JavaScript array.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request of the page.
+   *
+   * @return \Symfony\Component\HttpFoundation\JsonResponse
+   *   The available menu and menu items.
+   */
+  public function getParentOptions(Request $request) {
+    $available_menus = array();
+    if ($menus = $request->request->get('menus')) {
+      foreach ($menus as $menu) {
+        $available_menus[$menu] = $menu;
+      }
+    }
+    $options = _menu_get_options(menu_get_menus(), $available_menus, array('mlid' => 0));
+
+    return new JsonResponse($options);
+  }
+
+  /**
+   * Provides the menu link submission form.
+   *
+   * @param \Drupal\system\Plugin\Core\Entity\Menu $menu
+   *   An entity representing a custom menu.
+   *
+   * @return array
+   *   Returns the menu link submission form.
+   */
+  public function addLink(Menu $menu) {
+    drupal_set_title(t('Add menu link'));
+    $menu_link = $this->entityManager->getStorageController('menu_link')->create(array(
+      'mlid' => 0,
+      'plid' => 0,
+      'menu_name' => $menu->id(),
+    ));
+    return entity_get_form($menu_link);
+  }
+
+}
diff --git a/core/modules/menu/lib/Drupal/menu/MenuFormController.php b/core/modules/menu/lib/Drupal/menu/MenuFormController.php
index 4eeab27..e715ab0 100644
--- a/core/modules/menu/lib/Drupal/menu/MenuFormController.php
+++ b/core/modules/menu/lib/Drupal/menu/MenuFormController.php
@@ -7,18 +7,55 @@
 
 namespace Drupal\menu;
 
+use Drupal\Core\Controller\ControllerInterface;
+use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityFormController;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Base form controller for menu edit forms.
  */
-class MenuFormController extends EntityFormController {
+class MenuFormController extends EntityFormController implements EntityControllerInterface {
 
   /**
-   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   * The factory for entity queries.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryFactory
+   */
+  protected $entityQueryFactory;
+
+  /**
+   * Constructs a MenuFormController object.
+   *
+   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory
+   *   The factory for entity queries.
+   * @param string|null $operation
+   *   (optional) The name of the current operation, defaults to NULL.
+   */
+  public function __construct(QueryFactory $entity_query_factory, $operation = NULL) {
+    parent::__construct($operation);
+
+    $this->entityQueryFactory = $entity_query_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+    return new static($container->get('entity.query'));
+  }
+
+  /**
+   * {@inheritdoc}
    */
   public function form(array $form, array &$form_state) {
     $form = parent::form($form, $form_state);
+
+    if ($this->operation == 'edit') {
+      drupal_set_title(t('Edit menu %label', array('%label' => $this->entity->label())), PASS_THROUGH);
+    }
+
     $menu = $this->entity;
     $system_menus = menu_list_system_menus();
     $form_state['menu'] = &$menu;
@@ -36,7 +73,7 @@ public function form(array $form, array &$form_state) {
       '#maxlength' => MENU_MAX_MENU_NAME_LENGTH_UI,
       '#description' => t('A unique name to construct the URL for the menu. It must only contain lowercase letters, numbers and hyphens.'),
       '#machine_name' => array(
-        'exists' => 'menu_edit_menu_name_exists',
+        'exists' => array($this, 'menuNameExists'),
         'source' => array('label'),
         'replace_pattern' => '[^a-z0-9-]+',
         'replace' => '-',
@@ -53,6 +90,7 @@ public function form(array $form, array &$form_state) {
 
     // Add menu links administration form for existing menus.
     if (!$menu->isNew() || isset($system_menus[$menu->id()])) {
+      form_load_include($form_state, 'admin.inc', 'menu');
       // Form API supports constructing and validating self-contained sections
       // within forms, but does not allow to handle the form section's submission
       // equally separated yet. Therefore, we use a $form_state key to point to
@@ -67,7 +105,27 @@ public function form(array $form, array &$form_state) {
   }
 
   /**
-   * Overrides Drupal\Core\Entity\EntityFormController::actions().
+   * Returns whether a menu name already exists.
+   *
+   * @param string $value
+   *   The name of the menu.
+   *
+   * @return bool
+   *   Returns TRUE if the menu already exists.
+   *
+   * @see form_validate_machine_name()
+   */
+  public function menuNameExists($value) {
+    $custom_exists = entity_load('menu', $value);
+      // 'menu-' is added to the menu name to avoid name-space conflicts.
+    $value = 'menu-' . $value;
+    $link_exists = $this->entityQueryFactory->get('menu_link')->condition('menu_name', $value)->range(0,1)->count()->execute();
+
+    return $custom_exists || $link_exists;
+  }
+
+  /**
+   * {@inheritdoc}
    */
   protected function actions(array $form, array &$form_state) {
     $actions = parent::actions($form, $form_state);
@@ -80,7 +138,7 @@ protected function actions(array $form, array &$form_state) {
   }
 
   /**
-   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   * {@inheritdoc}
    */
   public function save(array $form, array &$form_state) {
     $menu = $this->entity;
@@ -111,7 +169,7 @@ public function save(array $form, array &$form_state) {
   }
 
   /**
-   * Overrides Drupal\Core\Entity\EntityFormController::delete().
+   * {@inheritdoc}
    */
   public function delete(array $form, array &$form_state) {
     $menu = $this->entity;
diff --git a/core/modules/menu/lib/Drupal/menu/MenuListController.php b/core/modules/menu/lib/Drupal/menu/MenuListController.php
index 1ed2200..c6b1245 100644
--- a/core/modules/menu/lib/Drupal/menu/MenuListController.php
+++ b/core/modules/menu/lib/Drupal/menu/MenuListController.php
@@ -48,7 +48,7 @@ public function getOperations(EntityInterface $entity) {
     $uri = $entity->uri();
 
     $operations['edit']['title'] = t('Edit menu');
-    $operatuins['edit']['href'] = $uri['path'];
+    $operations['edit']['href'] = $uri['path'];
     $operations['add'] = array(
       'title' => t('Add link'),
       'href' => $uri['path'] . '/add',
diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
index a5c5456..69c288c 100644
--- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
+++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
@@ -187,7 +187,7 @@ function deleteCustomMenu($menu) {
     $this->assertFalse($result, 'All menu links associated to the custom menu were deleted.');
 
     // Make sure there's no delete button on system menus.
-    $this->drupalGet('admin/structure/menu/manage/main/edit');
+    $this->drupalGet('admin/structure/menu/manage/main');
     $this->assertNoRaw('edit-delete', 'The delete button was not found');
 
     // Try to delete the main menu.
@@ -294,7 +294,7 @@ function testSystemMenuRename() {
     $edit = array(
       'label' => $this->randomName(16),
     );
-    $this->drupalPost('admin/structure/menu/manage/main/edit', $edit, t('Save'));
+    $this->drupalPost('admin/structure/menu/manage/main', $edit, t('Save'));
 
     // Make sure menu shows up with new name in block addition.
     $default_theme = variable_get('theme_default', 'stark');
diff --git a/core/modules/menu/menu.admin.inc b/core/modules/menu/menu.admin.inc
index f08ddbd..f6b4bfc 100644
--- a/core/modules/menu/menu.admin.inc
+++ b/core/modules/menu/menu.admin.inc
@@ -6,47 +6,7 @@
  */
 
 use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
-use Drupal\system\Plugin\Core\Entity\Menu;
 use Drupal\Component\Utility\NestedArray;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-
-/**
- * Menu callback which shows an overview page of all the custom menus and their descriptions.
- */
-function menu_overview_page() {
-  return Drupal::entityManager()
-    ->getListController('menu')
-    ->render();
-}
-
-/**
- * Page callback: Presents the menu creation form.
- *
- * @return array
- *   A form array as expected by drupal_render().
- *
- * @see menu_menu()
- */
-function menu_menu_add() {
-  $menu = entity_create('menu', array());
-  return entity_get_form($menu);
-}
-
-/**
- * Page callback: Presents the menu edit form.
- *
- * @param \Drupal\system\Plugin\Core\Entity\Menu $menu
- *   The menu to edit.
- *
- * @return array
- *   A form array as expected by drupal_render().
- *
- * @see menu_menu()
- */
-function menu_menu_edit(Menu $menu) {
-  drupal_set_title(t('Edit menu %label', array('%label' => $menu->label())), PASS_THROUGH);
-  return entity_get_form($menu);
-}
 
 /**
  * Form constructor to edit an entire menu tree at once.
@@ -94,20 +54,6 @@ function menu_overview_form($form, &$form_state) {
   menu_tree_check_access($tree, $node_links);
   $menu_admin = FALSE;
 
-  // Inline the "Add link" action so it displays right above the table of
-  // links. No access check needed, since this form has the same access
-  // restriction as adding menu items to the menu.
-  $form['inline_actions'] = array(
-    '#prefix' => '<ul class="action-links">',
-    '#suffix' => '</ul>',
-  );
-  $form['inline_actions']['add'] = array(
-    '#theme' => 'menu_local_action',
-    '#link' => array(
-      'href' => 'admin/structure/menu/manage/' . $form_state['menu']->id() . '/add',
-      'title' => t('Add link'),
-    ),
-  );
   $form = array_merge($form, _menu_overview_tree_form($tree, $delta));
   $form['#empty_text'] = t('There are no menu links yet. <a href="@link">Add link</a>.', array('@link' => url('admin/structure/menu/manage/' . $form_state['menu']->id() .'/add')));
 
@@ -310,21 +256,6 @@ function theme_menu_overview_form($variables) {
 }
 
 /**
- * Returns whether a menu name already exists.
- *
- * @see menu_edit_menu()
- * @see form_validate_machine_name()
- */
-function menu_edit_menu_name_exists($value) {
-  $custom_exists = entity_load('menu', $value);
-  // 'menu-' is added to the menu name to avoid name-space conflicts.
-  $value = 'menu-' . $value;
-  $link_exists = Drupal::entityQuery('menu_link')->condition('menu_name', $value)->range(0,1)->count()->execute();
-
-  return $custom_exists || $link_exists;
-}
-
-/**
  * Submit function for adding or editing a custom menu.
  */
 function menu_edit_menu_submit($form, &$form_state) {
@@ -355,22 +286,3 @@ function menu_edit_menu_submit($form, &$form_state) {
   drupal_set_message(t('Your configuration has been saved.'));
   $form_state['redirect'] = $path . $menu['id'];
 }
-
-/**
- * Menu callback: Provides the menu link submission form.
- *
- * @param \Drupal\system\Plugin\Core\Entity\Menu $menu
- *   An entity representing a custom menu.
- *
- * @return
- *   Returns the menu link submission form.
- */
-function menu_link_add(Menu $menu) {
-  $menu_link = entity_create('menu_link', array(
-    'mlid' => 0,
-    'plid' => 0,
-    'menu_name' => $menu->id(),
-  ));
-  drupal_set_title(t('Add menu link'));
-  return entity_get_form($menu_link);
-}
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index 14c41b0..d5c28d8 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -67,16 +67,7 @@ function menu_menu() {
   $items['admin/structure/menu'] = array(
     'title' => 'Menus',
     'description' => 'Add new menus to your site, edit existing menus, and rename and reorganize menu links.',
-    'page callback' => 'menu_overview_page',
-    'access callback' => 'user_access',
-    'access arguments' => array('administer menu'),
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/structure/menu/parents'] = array(
-    'title' => 'Parent menu items',
-    'page callback' => 'menu_parent_options_js',
-    'type' => MENU_CALLBACK,
-    'access arguments' => array(TRUE),
+    'route_name' => 'menu_overview_page',
   );
   $items['admin/structure/menu/list'] = array(
     'title' => 'List menus',
@@ -84,35 +75,25 @@ function menu_menu() {
   );
   $items['admin/structure/menu/add'] = array(
     'title' => 'Add menu',
-    'page callback' => 'menu_menu_add',
-    'access arguments' => array('administer menu'),
+    'route_name' => 'menu_add_menu',
     'type' => MENU_LOCAL_ACTION,
-    'file' => 'menu.admin.inc',
   );
   $items['admin/structure/menu/settings'] = array(
     'title' => 'Settings',
     'route_name' => 'menu_settings',
-    'access arguments' => array('administer menu'),
     'type' => MENU_LOCAL_TASK,
     'weight' => 100,
   );
   $items['admin/structure/menu/manage/%menu'] = array(
     'title' => 'Edit menu',
-    'page callback' => 'menu_menu_edit',
-    'page arguments' => array(4),
-    'title callback' => 'entity_page_label',
+    'title callback' => 'menu_page_label',
     'title arguments' => array(4),
-    'access arguments' => array('administer menu'),
-    'file' => 'menu.admin.inc',
+    'route_name' => 'menu_edit_menu',
   );
-  // Not officially a local action, but displayed as such in
-  // menu_overview_form().
   $items['admin/structure/menu/manage/%menu/add'] = array(
-    'title' => 'Add menu link',
-    'page callback' => 'menu_link_add',
-    'page arguments' => array(4),
-    'access arguments' => array('administer menu'),
-    'file' => 'menu.admin.inc',
+    'title' => 'Add link',
+    'type' => MENU_LOCAL_ACTION,
+    'route_name' => 'menu_link_add',
   );
   $items['admin/structure/menu/manage/%menu/edit'] = array(
     'title' => 'Edit menu',
@@ -126,9 +107,7 @@ function menu_menu() {
   );
   $items['admin/structure/menu/item/%menu_link/edit'] = array(
     'title' => 'Edit menu link',
-    'page callback' => 'entity_get_form',
-    'page arguments' => array(4),
-    'access arguments' => array('administer menu'),
+    'route_name' => 'menu_link_edit',
   );
   $items['admin/structure/menu/item/%menu_link/reset'] = array(
     'title' => 'Reset menu link',
@@ -142,13 +121,30 @@ function menu_menu() {
 }
 
 /**
+ * Title callback: Returns the label of the menu.
+ *
+ * @todo http://drupal.org/node/1981644
+ *
+ * @param int $menu_id
+ *   The entity ID of the menu.
+ *
+ * @return string
+ *   The menu's label.
+ */
+function menu_page_label($menu_id) {
+  $menu = entity_load('menu', $menu_id);
+  return $menu->label();
+}
+
+/**
  * Implements hook_entity_info_alter().
  */
 function menu_entity_info_alter(&$entity_info) {
   $entity_info['menu']['controllers']['list'] = 'Drupal\menu\MenuListController';
   $entity_info['menu']['uri_callback'] = 'menu_uri';
   $entity_info['menu']['controllers']['form'] = array(
-    'default' => 'Drupal\menu\MenuFormController',
+    'add' => 'Drupal\menu\MenuFormController',
+    'edit' => 'Drupal\menu\MenuFormController',
   );
 }
 
@@ -205,7 +201,7 @@ function menu_enable() {
 
   $base_link = entity_create('menu_link', array(
     'menu_name' => $system_link->menu_name,
-    'router_path' => 'admin/structure/menu/manage/%',
+    'router_path' => 'admin/structure/menu/manage/%menu',
     'module' => 'menu',
   ));
 
@@ -353,22 +349,6 @@ function menu_parent_options(array $menus, MenuLink $menu_link = NULL, $type = N
 }
 
 /**
- * Page callback.
- * Get all the available menus and menu items as a JavaScript array.
- */
-function menu_parent_options_js() {
-  $available_menus = array();
-  if (isset($_POST['menus']) && count($_POST['menus'])) {
-    foreach ($_POST['menus'] as $menu) {
-      $available_menus[$menu] = $menu;
-    }
-  }
-  $options = _menu_get_options(menu_get_menus(), $available_menus, array('mlid' => 0));
-
-  return new JsonResponse($options);
-}
-
-/**
  * Helper function to get the items of the given menu.
  */
 function _menu_get_options($menus, $available_menus, $item) {
diff --git a/core/modules/menu/menu.routing.yml b/core/modules/menu/menu.routing.yml
index aafe5c3..2d2c47f 100644
--- a/core/modules/menu/menu.routing.yml
+++ b/core/modules/menu/menu.routing.yml
@@ -5,6 +5,34 @@ menu_settings:
   requirements:
     _permission: 'administer menu'
 
+menu_overview_page:
+  pattern: '/admin/structure/menu'
+  defaults:
+    _content: '\Drupal\menu\Controller\MenuController::overview'
+  requirements:
+    _permission: 'administer menu'
+
+menu_parent_options_js:
+  pattern: '/admin/structure/menu/parents'
+  defaults:
+    _controller: '\Drupal\menu\Controller\MenuController::getParentOptions'
+  requirements:
+    _access: 'TRUE'
+
+menu_link_add:
+  pattern: '/admin/structure/menu/manage/{menu}/add'
+  defaults:
+    _content: '\Drupal\menu\Controller\MenuController::addLink'
+  requirements:
+    _permission: 'administer menu'
+
+menu_link_edit:
+  pattern: '/admin/structure/menu/item/{menu_link}/edit'
+  defaults:
+    _entity_form: 'menu_link'
+  requirements:
+    _permission: 'administer menu'
+
 menu_link_reset:
   pattern: 'admin/structure/menu/item/{menu_link}/reset'
   defaults:
@@ -19,6 +47,20 @@ menu_link_delete:
   requirements:
     _access_menu_delete_link: 'TRUE'
 
+menu_add_menu:
+  pattern: '/admin/structure/menu/add'
+  defaults:
+    _entity_form: 'menu.add'
+  requirements:
+    _permission: 'administer menu'
+
+menu_edit_menu:
+  pattern: '/admin/structure/menu/manage/{menu}'
+  defaults:
+    _entity_form: 'menu.edit'
+  requirements:
+    _permission: 'administer menu'
+
 menu_delete_menu:
   pattern: 'admin/structure/menu/manage/{menu}/delete'
   defaults:
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
index c2d6593..3c38883 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
@@ -114,7 +114,6 @@ function testBreadCrumbs() {
       'admin/structure/menu' => t('Menus'),
     );
     $this->assertBreadcrumb('admin/structure/menu/manage/tools', $trail);
-    $this->assertBreadcrumb('admin/structure/menu/manage/tools/edit', $trail);
 
     $mlid_node_add = db_query('SELECT mlid FROM {menu_links} WHERE link_path = :href AND module = :module', array(
       ':href' => 'node/add',
