diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index b16e87cffbf60211823ad6b863e5e536bdcda04a..7bf91141d716fce246a06221858851b4f94a43a8 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -2052,6 +2052,7 @@ dependencies = [
  "hashbrown 0.13.2",
  "serde",
  "smallstring",
+ "tokio",
  "zbus",
 ]
 
diff --git a/src/rust/mpris/Cargo.toml b/src/rust/mpris/Cargo.toml
index 9701120a9c15b54cdeef3bc06948e3b84e398e90..bcd98aa5818e2a15b51016ded09ebb7d9f24980a 100644
--- a/src/rust/mpris/Cargo.toml
+++ b/src/rust/mpris/Cargo.toml
@@ -5,11 +5,16 @@ edition.workspace = true
 authors.workspace = true
 license.workspace = true
 
+[[bin]]
+name = "mpris"
+path = "src/main.rs"
+
 [lib]
 doctest = false
 
 [dependencies]
 hashbrown.workspace = true
+tokio.workspace = true
 serde.workspace = true
 zbus.workspace = true
 
diff --git a/src/rust/mpris/src/main.rs b/src/rust/mpris/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1e9858e9b21966001739fddcfe6a1051d13c2ff4
--- /dev/null
+++ b/src/rust/mpris/src/main.rs
@@ -0,0 +1,18 @@
+use std::future::pending;
+use zbus::ConnectionBuilder;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let (main, player) = mpris::server::build("mpris");
+
+    let _ = ConnectionBuilder::session()?
+        .name("org.mpris.MediaPlayer2.mpris")?
+        .serve_at("/org/mpris/MediaPlayer2", main)?
+        .serve_at("/org/mpris/MediaPlayer2", player)?
+        .build()
+        .await?;
+
+    // Do other things or go to wait forever
+    pending::<()>().await;
+    Ok(())
+}
diff --git a/src/rust/mpris/src/server.rs b/src/rust/mpris/src/server.rs
index 93be09338320f1f6ad13b63ad28cbc2c8da64e33..b748bd65d2ea709c4e1e82d79f639d98ae647976 100644
--- a/src/rust/mpris/src/server.rs
+++ b/src/rust/mpris/src/server.rs
@@ -5,6 +5,21 @@ use crate::types::*;
 use commons::log;
 use zbus::dbus_interface;
 
+macro_rules! str {
+    ($str: literal) => {
+        String::from($str)
+    };
+}
+
+pub fn build(identity: impl ToString) -> (MediaPlayer2, MediaPlayer2Player) {
+    (
+        MediaPlayer2 {
+            identity: identity.to_string(),
+        },
+        MediaPlayer2Player {},
+    )
+}
+
 /// Should be served at "/org/mpris/MediaPlayer2"
 pub struct MediaPlayer2 {
     identity: String,
@@ -44,7 +59,7 @@ impl MediaPlayer2 {
     /// DesktopEntry property
     #[dbus_interface(property, name = "DesktopEntry")]
     fn desktop_entry(&self) -> String {
-        todo!()
+        self.identity.clone()
     }
 
     /// Fullscreen property
@@ -70,16 +85,15 @@ impl MediaPlayer2 {
     /// SupportedMimeTypes property
     #[dbus_interface(property, name = "SupportedMimeTypes")]
     fn supported_mime_types(&self) -> Vec<String> {
-        todo!()
+        vec![str!("video/mkv")]
     }
 
-    /// SupportedUriSchemes property. It should be `id://` and `file://` for
-    /// lektord. Here we support `file://` for only files in the database to
-    /// enable the user to search the kara folder and drop the files to play
-    /// them imediatly.
+    /// SupportedUriSchemes property. It should be `id` and `file` for lektord.
+    /// Here we support `file` for only files in the database to enable the user
+    /// to search the kara folder and drop the files to play them imediatly.
     #[dbus_interface(property, name = "SupportedUriSchemes")]
     fn supported_uri_schemes(&self) -> Vec<String> {
-        vec![String::from("id://"), String::from("file://")]
+        vec![str!("id"), str!("file")]
     }
 }
 
@@ -136,13 +150,13 @@ impl MediaPlayer2Player {
     /// CanPause property
     #[dbus_interface(property)]
     fn can_pause(&self) -> bool {
-        todo!()
+        true
     }
 
     /// CanPlay property
     #[dbus_interface(property)]
     fn can_play(&self) -> bool {
-        todo!()
+        true
     }
 
     /// CanSeek property
@@ -153,11 +167,11 @@ impl MediaPlayer2Player {
 
     /// LoopStatus property
     #[dbus_interface(property, name = "LoopStatus")]
-    fn loop_status(&self) -> MediaPlayerObjectPath {
-        todo!()
+    fn loop_status(&self) -> MediaPlayerLoopStatus {
+        MediaPlayerLoopStatus::None
     }
     #[dbus_interface(property, name = "LoopStatus")]
-    fn set_loop_status(&self, loop_status: MediaPlayerObjectPath) {
+    fn set_loop_status(&self, loop_status: MediaPlayerLoopStatus) {
         todo!("handle loop status {loop_status:?}")
     }
 
@@ -171,7 +185,7 @@ impl MediaPlayer2Player {
     /// Shuffle property
     #[dbus_interface(property, name = "Shuffle")]
     fn shuffle(&self) -> bool {
-        todo!()
+        false
     }
     #[dbus_interface(property, name = "Shuffle")]
     fn set_shuffle(&self, shuffle: bool) {
@@ -181,7 +195,7 @@ impl MediaPlayer2Player {
     /// Volume property
     #[dbus_interface(property, name = "Volume")]
     fn volume(&self) -> f64 {
-        todo!()
+        100.0
     }
     #[dbus_interface(property, name = "Volume")]
     fn set_volume(&self, volume: f64) {
@@ -191,13 +205,13 @@ impl MediaPlayer2Player {
     /// PlaybackStatus property
     #[dbus_interface(property, name = "PlaybackStatus")]
     fn playback_status(&self) -> MediaPlayerPlaybackStatus {
-        todo!()
+        MediaPlayerPlaybackStatus::Stopped
     }
 
     /// Position property
     #[dbus_interface(property, name = "Position")]
     fn position(&self) -> TimeMicroSec {
-        todo!()
+        0.into()
     }
 
     /// Metadata property. If there is a current kara playing, there must be at
@@ -205,6 +219,6 @@ impl MediaPlayer2Player {
     /// the current kara id.
     #[dbus_interface(property, name = "Metadata")]
     fn metadata(&self) -> MediaPlayerMetadata {
-        todo!()
+        MediaPlayerMetadata::from_iter([])
     }
 }
diff --git a/src/rust/mpris/src/types.rs b/src/rust/mpris/src/types.rs
index 7e017d11cf85d9da04c60a4ddb08379d7cd60f69..d89d6e11fa74940cbac76eaab65a9380d02a51ed 100644
--- a/src/rust/mpris/src/types.rs
+++ b/src/rust/mpris/src/types.rs
@@ -52,6 +52,12 @@ pub enum MediaPlayerObjectPath {
 
 // Implementations
 
+impl FromIterator<(String, String)> for MediaPlayerMetadata {
+    fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
+        Self(iter.into_iter().collect())
+    }
+}
+
 impl From<i64> for TimeMicroSec {
     fn from(value: i64) -> Self {
         Self(value)
@@ -158,6 +164,6 @@ impl From<MediaPlayerMetadata> for ZValue<'_> {
             dict.append(key.into(), value.into())
                 .expect("invalid entry found")
         }
-        todo!()
+        dict.into()
     }
 }