diff --git a/core/core.services.yml b/core/core.services.yml
index 3f68677200..53f3b1365f 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -656,6 +656,11 @@ services:
   plugin.cache_clearer:
     class: Drupal\Core\Plugin\CachedDiscoveryClearer
     lazy: true
+  plugin.discovery_filterer:
+    class: Drupal\Core\Plugin\DiscoveryFilterer
+    arguments: ['@module_handler']
+    tags:
+      - { name: service_collector, tag: discovery_filter, call: addFilter }
   paramconverter.menu_link:
     class: Drupal\Core\ParamConverter\MenuLinkPluginConverter
     tags:
diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextFilter.php b/core/lib/Drupal/Core/Plugin/Context/ContextFilter.php
new file mode 100644
index 0000000000..46656187c3
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextFilter.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\Core\Plugin\Context;
+
+use Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface;
+
+/**
+ * Provides methods to filter plugins with satisfied context requirements.
+ */
+class ContextFilter implements DiscoveryFilterInterface {
+
+  /**
+   * An array of contexts.
+   *
+   * @var array|\Drupal\Component\Plugin\Context\ContextInterface[]
+   */
+  protected $contexts;
+
+  /**
+   * ContextFilter constructor.
+   *
+   * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+   *   An array of contexts.
+   */
+  public function __construct(array $contexts) {
+    $this->contexts = $contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function filter($type, $consumer, array $definitions, array $context) {
+    return \Drupal::service('context.handler')->filterPluginDefinitionsByContexts($this->contexts, $definitions);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/DiscoveryFilterInterface.php b/core/lib/Drupal/Core/Plugin/Discovery/DiscoveryFilterInterface.php
new file mode 100644
index 0000000000..e6fee12340
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Discovery/DiscoveryFilterInterface.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\Core\Plugin\Discovery;
+
+/**
+ * @todo.
+ *
+ * @see \Drupal\Core\Plugin\DiscoveryFilterer
+ */
+interface DiscoveryFilterInterface {
+
+  /**
+   * Filters the plugin definitions for a specific type.
+   *
+   * @param string $type
+   *   A string identifying the plugin type.
+   * @param string $consumer
+   *   A string identifying the consumer of these plugin definitions.
+   * @param \Drupal\Component\Plugin\Definition\PluginDefinitionInterface[]|array[] $definitions
+   *   The array of plugin definitions.
+   * @param mixed[] $context
+   *   An associative array containing additional information provided by the
+   *   code requesting the filtered definitins.
+   *
+   * @return \Drupal\Component\Plugin\Definition\PluginDefinitionInterface[]|array[]
+   *   The array of plugin definitions.
+   */
+  public function filter($type, $consumer, array $definitions, array $context);
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/DiscoveryFilterer.php b/core/lib/Drupal/Core/Plugin/DiscoveryFilterer.php
new file mode 100644
index 0000000000..30022da79b
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/DiscoveryFilterer.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\Core\Plugin;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface;
+
+/**
+ * Provides methods to retrieve filtered plugin definitions.
+ *
+ * This allows modules to alter plugin definitions, which is useful for tasks
+ * like hiding definitions from user interfaces based on available contexts.
+ *
+ * @see \Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface
+ */
+class DiscoveryFilterer {
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a new PluginDefinitionRepository.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler) {
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * An array of discovery filters.
+   *
+   * @var \Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface[]
+   */
+  protected $filters = [];
+
+  /**
+   * Adds a discovery filter.
+   *
+   * @param \Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface $filter
+   *   A discovery filter.
+   */
+  public function addFilter(DiscoveryFilterInterface $filter) {
+    $this->filters[] = $filter;
+  }
+
+  /**
+   * Gets the plugin definitions for a given type and sorts and filters them.
+   *
+   * @param string $type
+   *   A string identifying the plugin type.
+   * @param string $consumer
+   *   A string identifying the consumer of these plugin definitions.
+   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
+   *   The plugin discovery, usually the plugin manager.
+   * @param \Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface[] $filters
+   *   (optional) An array of discovery filters.
+   * @param mixed[] $context
+   *   (optional) An associative array containing additional information
+   *   provided by the code requesting the filtered definitins.
+   *
+   * @return \Drupal\Component\Plugin\Definition\PluginDefinitionInterface[]|array[]
+   *   An array of plugin definitions that are sorted and filtered.
+   */
+  public function get($type, $consumer, DiscoveryInterface $discovery, array $filters = [], array $context = []) {
+    $definitions = $discovery->getDefinitions();
+
+    /** @var \Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface[] $filters */
+    $filters = array_merge($filters, $this->filters);
+    foreach ($filters as $filter) {
+      $definitions = $filter->filter($type, $consumer, $definitions, $context);
+    }
+    return $definitions;
+  }
+
+}
diff --git a/core/modules/layout_builder/src/Controller/ChooseBlockController.php b/core/modules/layout_builder/src/Controller/ChooseBlockController.php
index 5287be25fc..72a0ee87c1 100644
--- a/core/modules/layout_builder/src/Controller/ChooseBlockController.php
+++ b/core/modules/layout_builder/src/Controller/ChooseBlockController.php
@@ -4,6 +4,8 @@
 
 use Drupal\Core\Block\BlockManagerInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Plugin\Context\ContextFilter;
+use Drupal\Core\Plugin\DiscoveryFilterer;
 use Drupal\Core\Url;
 use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
 use Drupal\layout_builder\SectionStorageInterface;
@@ -26,14 +28,24 @@ class ChooseBlockController implements ContainerInjectionInterface {
    */
   protected $blockManager;
 
+  /**
+   * The discovery filterer.
+   *
+   * @var \Drupal\Core\Plugin\DiscoveryFilterer
+   */
+  protected $discoveryFilterer;
+
   /**
    * ChooseBlockController constructor.
    *
    * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
    *   The block manager.
+   * @param \Drupal\Core\Plugin\DiscoveryFilterer $discovery_filterer
+   *   The discovery filterer.
    */
-  public function __construct(BlockManagerInterface $block_manager) {
+  public function __construct(BlockManagerInterface $block_manager, DiscoveryFilterer $discovery_filterer) {
     $this->blockManager = $block_manager;
+    $this->discoveryFilterer = $discovery_filterer;
   }
 
   /**
@@ -41,7 +53,8 @@ public function __construct(BlockManagerInterface $block_manager) {
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('plugin.manager.block')
+      $container->get('plugin.manager.block'),
+      $container->get('plugin.discovery_filterer')
     );
   }
 
@@ -62,8 +75,10 @@ public function build(SectionStorageInterface $section_storage, $delta, $region)
     $build['#type'] = 'container';
     $build['#attributes']['class'][] = 'block-categories';
 
-    $definitions = $this->blockManager->getDefinitionsForContexts($this->getAvailableContexts($section_storage));
-    foreach ($this->blockManager->getGroupedDefinitions($definitions) as $category => $blocks) {
+    $filters = new ContextFilter($this->getAvailableContexts($section_storage));
+    $definitions = $this->discoveryFilterer->get('block', 'layout_builder', $this->blockManager, $filters, ['section_storage' => $section_storage]);
+    $definitions = $this->blockManager->getGroupedDefinitions($definitions);
+    foreach ($definitions as $category => $blocks) {
       $build[$category]['#type'] = 'details';
       $build[$category]['#open'] = TRUE;
       $build[$category]['#title'] = $category;
diff --git a/core/modules/layout_builder/src/Controller/ChooseSectionController.php b/core/modules/layout_builder/src/Controller/ChooseSectionController.php
index a04cdbb1c1..4649d3a79a 100644
--- a/core/modules/layout_builder/src/Controller/ChooseSectionController.php
+++ b/core/modules/layout_builder/src/Controller/ChooseSectionController.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Layout\LayoutPluginManagerInterface;
+use Drupal\Core\Plugin\DiscoveryFilterer;
 use Drupal\Core\Plugin\PluginFormInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Url;
@@ -27,14 +28,24 @@ class ChooseSectionController implements ContainerInjectionInterface {
    */
   protected $layoutManager;
 
+  /**
+   * The discovery filterer.
+   *
+   * @var \Drupal\Core\Plugin\DiscoveryFilterer
+   */
+  protected $discoveryFilterer;
+
   /**
    * ChooseSectionController constructor.
    *
    * @param \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_manager
    *   The layout manager.
+   * @param \Drupal\Core\Plugin\DiscoveryFilterer $discovery_filterer
+   *   The discovery filterer.
    */
-  public function __construct(LayoutPluginManagerInterface $layout_manager) {
+  public function __construct(LayoutPluginManagerInterface $layout_manager, DiscoveryFilterer $discovery_filterer) {
     $this->layoutManager = $layout_manager;
+    $this->discoveryFilterer = $discovery_filterer;
   }
 
   /**
@@ -42,7 +53,8 @@ public function __construct(LayoutPluginManagerInterface $layout_manager) {
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('plugin.manager.core.layout')
+      $container->get('plugin.manager.core.layout'),
+      $container->get('plugin.discovery_filterer')
     );
   }
 
@@ -61,7 +73,8 @@ public function build(SectionStorageInterface $section_storage, $delta) {
     $output['#title'] = $this->t('Choose a layout');
 
     $items = [];
-    foreach ($this->layoutManager->getDefinitions() as $plugin_id => $definition) {
+    $definitions = $this->discoveryFilterer->get('layout', 'layout_builder', $this->layoutManager, [], ['section_storage' => $section_storage]);
+    foreach ($definitions as $plugin_id => $definition) {
       $layout = $this->layoutManager->createInstance($plugin_id);
       $item = [
         '#type' => 'link',
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.info.yml b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.info.yml
new file mode 100644
index 0000000000..607877e780
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Layout Builder test'
+type: module
+description: 'Support module for testing layout building.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.services.yml b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.services.yml
new file mode 100644
index 0000000000..975f611221
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.services.yml
@@ -0,0 +1,5 @@
+services:
+  layout_builder_test.discovery_filter:
+    class: Drupal\layout_builder_test\Plugin\LayoutBuilderTestDiscoveryFilter
+    tags:
+      - { name: discovery_filter }
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/LayoutBuilderTestDiscoveryFilter.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/LayoutBuilderTestDiscoveryFilter.php
new file mode 100644
index 0000000000..9d52d58e91
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/LayoutBuilderTestDiscoveryFilter.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\layout_builder_test\Plugin;
+
+use Drupal\Core\Plugin\Discovery\DiscoveryFilterInterface;
+
+/**
+ * Provides a test discovery filter.
+ */
+class LayoutBuilderTestDiscoveryFilter implements DiscoveryFilterInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function filter($type, $consumer, array $definitions, array $context) {
+    if ($type !== 'block' || $consumer !== 'layout_builder') {
+      return $definitions;
+    }
+
+    // Explicitly remove the "Help" blocks from the list.
+    unset($definitions['help_block']);
+
+    // Explicitly remove the "Sticky at top of lists field_block".
+    $disallowed_fields = [
+      'sticky',
+    ];
+
+    foreach ($definitions as $plugin_id => $definition) {
+      // Field block IDs are in the form 'field_block:{entity}:{bundle}:{name}',
+      // for example 'field_block:node:article:revision_timestamp'.
+      preg_match('/field_block:.*:.*:(.*)/', $plugin_id, $parts);
+      if (isset($parts[1]) && in_array($parts[1], $disallowed_fields, TRUE)) {
+        // Unset any field blocks that match our predefined list.
+        unset($definitions[$plugin_id]);
+      }
+    }
+    return $definitions;
+  }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderAPITest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderAPITest.php
new file mode 100644
index 0000000000..a9ddbfd1a7
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderAPITest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Drupal\Tests\layout_builder\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the Layout Builder API.
+ *
+ * @group layout_builder
+ */
+class LayoutBuilderAPITest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'layout_builder',
+    'block',
+    'node',
+    'layout_builder_test',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create a content type.
+    $this->createContentType(['type' => 'bundle_with_section_field']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function testLayoutBuilderChooseBlocksAlter() {
+    // In this test, hook_plugin_definition_PLUGIN_TYPE_alter() will have been
+    // implemented by the layout_test module, instructing the "Help" blocks not
+    // to appear, as well as the "Sticky at top of lists" field_block.
+    $assert_session = $this->assertSession();
+
+    $this->drupalLogin($this->drupalCreateUser([
+      'configure any layout',
+      'administer node display',
+      'administer node fields',
+    ]));
+
+    $field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
+
+    // From the manage display page, go to manage the layout.
+    $this->drupalGet("$field_ui_prefix/display/default");
+    $assert_session->linkExists('Manage layout');
+    $this->clickLink('Manage layout');
+    $assert_session->addressEquals("$field_ui_prefix/display-layout/default");
+
+    // Add a new block.
+    $assert_session->linkExists('Add Block');
+    $this->clickLink('Add Block');
+    // Verify that blocks not modified by
+    // hook_plugin_definition_PLUGIN_TYPE_alter() are present.
+    // 1. A core block.
+    $assert_session->linkExists('Powered by Drupal');
+    // 2. A field_block.
+    $assert_session->linkExists('Default revision');
+
+    // Verify that blocks explicitly removed by
+    // hook_plugin_definition_PLUGIN_TYPE_alter() are not present.
+    // 1. A core block.
+    $assert_session->linkNotExists('Help');
+    // 2. A field_block.
+    $assert_session->linkNotExists('Sticky at top of lists');
+  }
+
+}
