diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs
index c3c83a8f000..dfbbe0b3918 100644
--- a/tooling/acvm_cli/src/cli/execute_cmd.rs
+++ b/tooling/acvm_cli/src/cli/execute_cmd.rs
@@ -74,8 +74,12 @@ pub(crate) fn execute_program_from_witness(
         &program,
         inputs_map,
         &Bn254BlackBoxSolver,
-        &mut DefaultForeignCallBuilder { output: PrintOutput::Stdout, ..Default::default() }
-            .build(),
+        &mut DefaultForeignCallBuilder {
+            output: PrintOutput::Stdout,
+            enable_mocks: false,
+            ..Default::default()
+        }
+        .build(),
     )
     .map_err(CliError::CircuitExecutionError)
 }
diff --git a/tooling/lsp/src/requests/test_run.rs b/tooling/lsp/src/requests/test_run.rs
index 1071866dfad..bd53526298e 100644
--- a/tooling/lsp/src/requests/test_run.rs
+++ b/tooling/lsp/src/requests/test_run.rs
@@ -93,6 +93,7 @@ fn on_test_run_request_inner(
                 |output, base| {
                     DefaultForeignCallBuilder {
                         output,
+                        enable_mocks: true,
                         resolver_url: None, // NB without this the root and package don't do anything.
                         root_path: Some(workspace.root_dir.clone()),
                         package_name: Some(package.name.to_string()),
diff --git a/tooling/nargo/src/foreign_calls/default.rs b/tooling/nargo/src/foreign_calls/default.rs
index ce4af3aa744..19928e89563 100644
--- a/tooling/nargo/src/foreign_calls/default.rs
+++ b/tooling/nargo/src/foreign_calls/default.rs
@@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
 use crate::PrintOutput;
 
 use super::{
-    layers::{self, Layer, Layering},
-    mocker::MockForeignCallExecutor,
+    layers::{self, Either, Layer, Layering},
+    mocker::{DisabledMockForeignCallExecutor, MockForeignCallExecutor},
     print::PrintForeignCallExecutor,
     ForeignCallExecutor,
 };
@@ -15,17 +15,38 @@ use super::rpc::RPCForeignCallExecutor;
 
 /// A builder for [DefaultForeignCallLayers] where we can enable fields based on feature flags,
 /// which is easier than providing different overrides for a `new` method.
-#[derive(Default)]
 pub struct DefaultForeignCallBuilder<'a> {
     pub output: PrintOutput<'a>,
+    pub enable_mocks: bool,
+
     #[cfg(feature = "rpc")]
     pub resolver_url: Option<String>,
+
     #[cfg(feature = "rpc")]
     pub root_path: Option<std::path::PathBuf>,
+
     #[cfg(feature = "rpc")]
     pub package_name: Option<String>,
 }
 
+impl<'a> Default for DefaultForeignCallBuilder<'a> {
+    fn default() -> Self {
+        Self {
+            output: PrintOutput::default(),
+            enable_mocks: true,
+
+            #[cfg(feature = "rpc")]
+            resolver_url: None,
+
+            #[cfg(feature = "rpc")]
+            root_path: None,
+
+            #[cfg(feature = "rpc")]
+            package_name: None,
+        }
+    }
+}
+
 impl<'a> DefaultForeignCallBuilder<'a> {
     /// Override the output.
     pub fn with_output(mut self, output: PrintOutput<'a>) -> Self {
@@ -33,6 +54,12 @@ impl<'a> DefaultForeignCallBuilder<'a> {
         self
     }
 
+    /// Enable or disable mocks.
+    pub fn with_mocks(mut self, enabled: bool) -> Self {
+        self.enable_mocks = enabled;
+        self
+    }
+
     /// Compose the executor layers with [layers::Empty] as the default handler.
     pub fn build<F>(self) -> DefaultForeignCallLayers<'a, layers::Empty, F>
     where
@@ -69,7 +96,11 @@ impl<'a> DefaultForeignCallBuilder<'a> {
         };
 
         executor
-            .add_layer(MockForeignCallExecutor::default())
+            .add_layer(if self.enable_mocks {
+                Either::Left(MockForeignCallExecutor::default())
+            } else {
+                Either::Right(DisabledMockForeignCallExecutor)
+            })
             .add_layer(PrintForeignCallExecutor::new(self.output))
     }
 }
@@ -78,11 +109,16 @@ impl<'a> DefaultForeignCallBuilder<'a> {
 #[cfg(feature = "rpc")]
 pub type DefaultForeignCallLayers<'a, B, F> = Layer<
     PrintForeignCallExecutor<'a>,
-    Layer<MockForeignCallExecutor<F>, Layer<Option<RPCForeignCallExecutor>, B>>,
+    Layer<
+        Either<MockForeignCallExecutor<F>, DisabledMockForeignCallExecutor>,
+        Layer<Option<RPCForeignCallExecutor>, B>,
+    >,
 >;
 #[cfg(not(feature = "rpc"))]
-pub type DefaultForeignCallLayers<'a, B, F> =
-    Layer<PrintForeignCallExecutor<'a>, Layer<MockForeignCallExecutor<F>, B>>;
+pub type DefaultForeignCallLayers<'a, B, F> = Layer<
+    PrintForeignCallExecutor<'a>,
+    Layer<Either<MockForeignCallExecutor<F>, DisabledMockForeignCallExecutor>, B>,
+>;
 
 /// Convenience constructor for code that used to create the executor this way.
 #[cfg(feature = "rpc")]
@@ -106,6 +142,7 @@ impl DefaultForeignCallExecutor {
     {
         DefaultForeignCallBuilder {
             output,
+            enable_mocks: true,
             resolver_url: resolver_url.map(|s| s.to_string()),
             root_path,
             package_name,
diff --git a/tooling/nargo/src/foreign_calls/layers.rs b/tooling/nargo/src/foreign_calls/layers.rs
index 25df4e904aa..bc0536ad4b5 100644
--- a/tooling/nargo/src/foreign_calls/layers.rs
+++ b/tooling/nargo/src/foreign_calls/layers.rs
@@ -122,6 +122,28 @@ impl<T> Layering for T {
     }
 }
 
+/// A case where we can have either this or that type of handler.
+pub enum Either<L, R> {
+    Left(L),
+    Right(R),
+}
+
+impl<L, R, F> ForeignCallExecutor<F> for Either<L, R>
+where
+    L: ForeignCallExecutor<F>,
+    R: ForeignCallExecutor<F>,
+{
+    fn execute(
+        &mut self,
+        foreign_call: &ForeignCallWaitInfo<F>,
+    ) -> Result<ForeignCallResult<F>, ForeignCallError> {
+        match self {
+            Either::Left(left) => left.execute(foreign_call),
+            Either::Right(right) => right.execute(foreign_call),
+        }
+    }
+}
+
 /// Support disabling a layer by making it optional.
 /// This way we can still have a known static type for a composition,
 /// because layers are always added, potentially wrapped in an `Option`.
diff --git a/tooling/nargo/src/foreign_calls/mocker.rs b/tooling/nargo/src/foreign_calls/mocker.rs
index 42d788fcfc6..ac821155eef 100644
--- a/tooling/nargo/src/foreign_calls/mocker.rs
+++ b/tooling/nargo/src/foreign_calls/mocker.rs
@@ -1,5 +1,3 @@
-use std::marker::PhantomData;
-
 use acvm::{
     acir::brillig::{ForeignCallParam, ForeignCallResult},
     pwg::ForeignCallWaitInfo,
@@ -178,12 +176,9 @@ where
 }
 
 /// Handler that panics if any of the mock functions are called.
-#[allow(dead_code)] // TODO: Make the mocker optional
-pub(crate) struct DisabledMockForeignCallExecutor<F> {
-    _field: PhantomData<F>,
-}
+pub struct DisabledMockForeignCallExecutor;
 
-impl<F> ForeignCallExecutor<F> for DisabledMockForeignCallExecutor<F> {
+impl<F> ForeignCallExecutor<F> for DisabledMockForeignCallExecutor {
     fn execute(
         &mut self,
         foreign_call: &ForeignCallWaitInfo<F>,
diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs
index 709caf71185..52b8e0fd59e 100644
--- a/tooling/nargo_cli/src/cli/execute_cmd.rs
+++ b/tooling/nargo_cli/src/cli/execute_cmd.rs
@@ -7,7 +7,7 @@ use clap::Args;
 
 use nargo::constants::PROVER_INPUT_FILE;
 use nargo::errors::try_to_diagnose_runtime_error;
-use nargo::foreign_calls::DefaultForeignCallExecutor;
+use nargo::foreign_calls::DefaultForeignCallBuilder;
 use nargo::package::Package;
 use nargo::PrintOutput;
 use nargo_toml::{get_package_manifest, resolve_workspace_from_toml};
@@ -141,12 +141,14 @@ pub(crate) fn execute_program(
         &compiled_program.program,
         initial_witness,
         &Bn254BlackBoxSolver,
-        &mut DefaultForeignCallExecutor::new(
-            PrintOutput::Stdout,
-            foreign_call_resolver_url,
+        &mut DefaultForeignCallBuilder {
+            output: PrintOutput::Stdout,
+            enable_mocks: false,
+            resolver_url: foreign_call_resolver_url.map(|s| s.to_string()),
             root_path,
             package_name,
-        ),
+        }
+        .build(),
     );
     match solved_witness_stack_err {
         Ok(solved_witness_stack) => Ok(solved_witness_stack),
diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs
index 43f1c306d88..9bf3ae9fedf 100644
--- a/tooling/nargo_cli/src/cli/test_cmd.rs
+++ b/tooling/nargo_cli/src/cli/test_cmd.rs
@@ -499,6 +499,7 @@ impl<'a> TestRunner<'a> {
             |output, base| {
                 DefaultForeignCallBuilder {
                     output,
+                    enable_mocks: true,
                     resolver_url: foreign_call_resolver_url.map(|s| s.to_string()),
                     root_path: root_path.clone(),
                     package_name: Some(package_name.clone()),
diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs
index 6aae94f6645..6215aae90e4 100644
--- a/tooling/nargo_cli/tests/stdlib-tests.rs
+++ b/tooling/nargo_cli/tests/stdlib-tests.rs
@@ -91,7 +91,7 @@ fn run_stdlib_tests(force_brillig: bool, inliner_aggressiveness: i64) {
                 PrintOutput::Stdout,
                 &CompileOptions { force_brillig, inliner_aggressiveness, ..Default::default() },
                 |output, base| {
-                    DefaultForeignCallBuilder { output, ..Default::default() }.build_with_base(base)
+                    DefaultForeignCallBuilder::default().with_output(output).build_with_base(base)
                 },
             );
             (test_name, status)