diff --git a/peridot/bin/discovermgr/BUILD.gn b/peridot/bin/discovermgr/BUILD.gn
new file mode 100644
index 0000000000000000000000000000000000000000..7368f86db8277dd7eab93724881e9634db3c3725
--- /dev/null
+++ b/peridot/bin/discovermgr/BUILD.gn
@@ -0,0 +1,54 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/package.gni")
+import("//build/rust/rustc_binary.gni")
+import("//build/test/test_package.gni")
+
+rustc_binary("bin") {
+  name = "discovermgr"
+  edition = "2018"
+
+  with_unit_tests = true
+
+  deps = [
+    "//garnet/public/lib/fidl/rust/fidl",
+    "//garnet/public/rust/fuchsia-async",
+    "//garnet/public/rust/fuchsia-component",
+    "//garnet/public/rust/fuchsia-syslog",
+    "//sdk/fidl/fuchsia.app.discover:fuchsia.app.discover-rustc",
+    "//third_party/rust_crates:failure",
+    "//third_party/rust_crates:futures-preview",
+    "//third_party/rust_crates:maplit",
+  ]
+}
+
+package("discovermgr") {
+  deps = [
+    ":bin",
+  ]
+
+  binary = "discovermgr"
+
+  meta = [
+    {
+      path = rebase_path("meta/discovermgr.cmx")
+      dest = "discovermgr.cmx"
+    },
+  ]
+}
+
+package("discovermgr_tests") {
+  testonly = true
+
+  deps = [
+    ":bin",
+  ]
+
+  tests = [
+    {
+      name = "discovermgr_bin_test"
+    },
+  ]
+}
diff --git a/peridot/bin/discovermgr/OWNERS b/peridot/bin/discovermgr/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..ea3f4ae6efc70688d45b7f978f4d641e390b1e1c
--- /dev/null
+++ b/peridot/bin/discovermgr/OWNERS
@@ -0,0 +1,2 @@
+miguelfrde@google.com
+schilit@google.com
diff --git a/peridot/bin/discovermgr/README.md b/peridot/bin/discovermgr/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ec2820dbc5f16deff4e767dfe719baaee66b1547
--- /dev/null
+++ b/peridot/bin/discovermgr/README.md
@@ -0,0 +1,16 @@
+# discovermgr
+
+**Status: WIP**
+
+Provides functionality for module discovery:
+
+- Suggestions management
+- Context management
+
+## Tests
+
+Run:
+
+```
+fx run-test discovermgr_tests
+```
diff --git a/peridot/bin/discovermgr/meta/discovermgr.cmx b/peridot/bin/discovermgr/meta/discovermgr.cmx
new file mode 100644
index 0000000000000000000000000000000000000000..d222deedee6bc2beaa67a7cfb26b772f70b0b580
--- /dev/null
+++ b/peridot/bin/discovermgr/meta/discovermgr.cmx
@@ -0,0 +1,11 @@
+{
+    "program": {
+        "binary": "bin/app"
+    },
+    "sandbox": {
+        "features": [],
+        "services": [
+            "fuchsia.logger.LogSink"
+        ]
+    }
+}
diff --git a/peridot/bin/discovermgr/src/main.rs b/peridot/bin/discovermgr/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ff81146a0111ae822d2965eb340ed236b0885021
--- /dev/null
+++ b/peridot/bin/discovermgr/src/main.rs
@@ -0,0 +1,95 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![feature(async_await, await_macro)]
+
+use {
+    crate::module_output::ModuleOutputWriterService,
+    failure::{Error, ResultExt},
+    fidl_fuchsia_app_discover::{DiscoverRegistryRequest, DiscoverRegistryRequestStream},
+    fuchsia_async as fasync,
+    fuchsia_component::server::ServiceFs,
+    fuchsia_syslog as syslog,
+    futures::prelude::*,
+};
+
+mod module_output;
+
+// The directory name where the discovermgr FIDL services are exposed.
+static SERVICE_DIRECTORY: &str = "public";
+
+enum IncomingServices {
+    DiscoverRegistry(DiscoverRegistryRequestStream),
+    // TODO(miguelfrde): add additional services
+}
+
+/// Handle DiscoveryRegistry requests.
+async fn run_discover_registry_server(
+    mut stream: DiscoverRegistryRequestStream,
+) -> Result<(), Error> {
+    while let Some(request) =
+        await!(stream.try_next()).context("Error running discover registry")?
+    {
+        match request {
+            DiscoverRegistryRequest::RegisterModuleOutputWriter { module, request, .. } => {
+                let module_output_stream = request.into_stream()?;
+                ModuleOutputWriterService::new(module)?.spawn(module_output_stream);
+            }
+        }
+    }
+    Ok(())
+}
+
+#[fasync::run_singlethreaded]
+async fn main() -> Result<(), Error> {
+    syslog::init_with_tags(&["discovermgr"])?;
+
+    let mut fs = ServiceFs::new_local();
+    fs.dir(SERVICE_DIRECTORY).add_fidl_service(IncomingServices::DiscoverRegistry);
+
+    fs.take_and_serve_directory_handle()?;
+
+    const MAX_CONCURRENT: usize = 10_000;
+    let fut =
+        fs.for_each_concurrent(MAX_CONCURRENT, |IncomingServices::DiscoverRegistry(stream)| {
+            run_discover_registry_server(stream).unwrap_or_else(|e| syslog::fx_log_err!("{:?}", e))
+        });
+
+    await!(fut);
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use failure::ResultExt;
+    use fidl_fuchsia_app_discover::{
+        DiscoverRegistryMarker, ModuleIdentifier, ModuleOutputWriterMarker,
+    };
+    use fuchsia_component::client;
+
+    static COMPONENT_URL: &str = "fuchsia-pkg://fuchsia.com/discovermgr#meta/discovermgr.cmx";
+
+    #[fasync::run_singlethreaded(test)]
+    async fn test_module_output() -> Result<(), Error> {
+        let launcher = client::launcher().context("Failed to open launcher service")?;
+        let app = client::launch(&launcher, COMPONENT_URL.to_string(), None /* arguments */)
+            .context("Failed to launch discovermgr")?;
+        let discover_manager = app
+            .connect_to_service::<DiscoverRegistryMarker>()
+            .context("Failed to connect to DiscoverRegistry")?;
+
+        let mod_scope = ModuleIdentifier {
+            story_id: Some("test-story".to_string()),
+            module_path: Some(vec!["test-mod".to_string()]),
+        };
+        let (module_output_proxy, server_end) =
+            fidl::endpoints::create_proxy::<ModuleOutputWriterMarker>()?;
+        assert!(discover_manager.register_module_output_writer(mod_scope, server_end).is_ok());
+
+        let result = await!(module_output_proxy.write("test-param", Some("test-ref")))?;
+        assert!(result.is_ok());
+        Ok(())
+    }
+}
diff --git a/peridot/bin/discovermgr/src/module_output.rs b/peridot/bin/discovermgr/src/module_output.rs
new file mode 100644
index 0000000000000000000000000000000000000000..652adf34ce80dc147b41ff130a95fe51aed4ffa5
--- /dev/null
+++ b/peridot/bin/discovermgr/src/module_output.rs
@@ -0,0 +1,62 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use {
+    failure::{format_err, Error, ResultExt},
+    fidl_fuchsia_app_discover::{
+        ModuleIdentifier, ModuleOutputWriterRequest, ModuleOutputWriterRequestStream,
+    },
+    fuchsia_async as fasync,
+    fuchsia_syslog::macros::*,
+    futures::prelude::*,
+};
+
+/// The ModuleOutputWriter protocol implementation.
+pub struct ModuleOutputWriterService {
+    /// The story id to which the module belongs.
+    story_id: String,
+
+    /// The module id in story |story_id| to which the output belongs.
+    module_path: Vec<String>,
+}
+
+impl ModuleOutputWriterService {
+    /// Create a new module writer instance from an identifier.
+    pub fn new(module: ModuleIdentifier) -> Result<Self, Error> {
+        Ok(ModuleOutputWriterService {
+            story_id: module.story_id.ok_or(format_err!("expected story id"))?,
+            module_path: module.module_path.ok_or(format_err!("expected mod path"))?,
+        })
+    }
+
+    /// Handle a stream of ModuleOutputWriter requests.
+    pub fn spawn(self, mut stream: ModuleOutputWriterRequestStream) {
+        fasync::spawn(
+            async move {
+                while let Some(request) = await!(stream.try_next()).context(format!(
+                    "Error running module output for {:?} {:?}",
+                    self.story_id, self.module_path,
+                ))? {
+                    match request {
+                        ModuleOutputWriterRequest::Write {
+                            output_name,
+                            entity_reference: _,
+                            responder,
+                        } => {
+                            fx_log_info!(
+                                "Got write for parameter name:{}, story:{}, mod:{:?}",
+                                output_name,
+                                self.story_id,
+                                self.module_path,
+                            );
+                            responder.send(&mut Ok(()))?;
+                        }
+                    }
+                }
+                Ok(())
+            }
+                .unwrap_or_else(|e: Error| fx_log_err!("error serving module output {}", e)),
+        )
+    }
+}
diff --git a/peridot/packages/prod/BUILD.gn b/peridot/packages/prod/BUILD.gn
index e460ea8b5f73b386048f3e6f8c0972390fdcc470..363a8333bab90d1b318df2e2fc9ec1928bf98e69 100644
--- a/peridot/packages/prod/BUILD.gn
+++ b/peridot/packages/prod/BUILD.gn
@@ -73,8 +73,8 @@ group("integration_testing") {
   testonly = true
   public_deps = [
     "//garnet/packages/prod:test_runner",
-    "//peridot/bin/sessionmgr/story_runner:dev_story_shell",
     "//peridot/bin/sessionmgr:dev_session_shell",
+    "//peridot/bin/sessionmgr/story_runner:dev_story_shell",
     "//peridot/bin/test_driver:test_driver_module",
     "//peridot/packages/prod:dev_base_shell",
     "//peridot/packages/prod:modular",
@@ -102,8 +102,9 @@ group("modular") {
   testonly = true
   public_deps = [
     "//peridot/bin/basemgr",
+    "//peridot/bin/discovermgr",
     "//peridot/bin/sessionmgr",
-    "//peridot/packages/prod:auto_login_base_shell",
     "//peridot/cloud/go/src/remote_module_resolver:host",
+    "//peridot/packages/prod:auto_login_base_shell",
   ]
 }
diff --git a/peridot/packages/tests/BUILD.gn b/peridot/packages/tests/BUILD.gn
index a688d5371c921c23e4820bff957d70a569817bf1..448eaf0d946c83c5ca815ab4870047bf92f30e44 100644
--- a/peridot/packages/tests/BUILD.gn
+++ b/peridot/packages/tests/BUILD.gn
@@ -54,6 +54,7 @@ group("maxwell_unittests") {
   testonly = true
   public_deps = [
     "//peridot/bin/context_engine:context_engine_unittests",
+    "//peridot/bin/discovermgr:discovermgr_tests",
     "//peridot/bin/module_resolver:module_resolver_unittests",
     "//peridot/bin/suggestion_engine:suggestion_engine_unittests",
   ]
@@ -91,10 +92,10 @@ group("modular_integration_tests") {
     "//peridot/tests/intents:intent_test_parent_module",
     "//peridot/tests/last_focus_time:last_focus_time_test_session_shell",
     "//peridot/tests/modular_config:test_config",
+    "//peridot/tests/modular_login_tests:modular_login_tests",
     "//peridot/tests/module_context:module_context_test_entity_module",
     "//peridot/tests/module_context:module_context_test_module",
     "//peridot/tests/module_context:module_context_test_session_shell",
-    "//peridot/tests/modular_login_tests:modular_login_tests",
     "//peridot/tests/parent_child:parent_child_test_child_module1",
     "//peridot/tests/parent_child:parent_child_test_child_module2",
     "//peridot/tests/parent_child:parent_child_test_parent_module",