Skip to content

WASM Support for rmcp #379

@joulei

Description

@joulei

Hi! I'm working on integrating rmcp into a project that requires WASM compatibility and noticed that rmcp currently doesn't compile for wasm32-unknown-unknown due to several WASM-specific blockers, mostly Send requirements.

Current Issues:

  1. Send trait bounds incompatibility

    • traits likeSseClient requiree Send + Sync + 'static
    • Send is a no go in web:
      • JavaScript interop types contain PhantomData<*const u8> making them intentionally !Send
      • reqwest's WASM implementation uses JsValue types that are !Send by design
  2. Transport trait architecture

    • All transport implementations must return impl Future + Send
  3. Worker transport assumptions

    • Worker trait expects thread spawning capabilities
    • WASM is single-threaded, no std::thread support
  4. Service layer threading

    • ServiceRole, Service traits all require Send + Sync
    • Future return types across the codebase assume Send bounds

Questions:

  1. Is WASM support planned for rmcp in the near future?
  2. Is anyone currently working on this?

Proposal:

I'm willing to work on this if there's interest (I might do either way in a fork, as I'd really like to avoid making my own rmcp). We've recently had to deal with similar issues making some cross-platform async Rust projects wasm-compatible, there are some patterns we've used that could work here:

  1. Platform-conditional trait abstractions:
  cfg_if::cfg_if! {
      if #[cfg(target_arch = "wasm32")] {
          pub trait PlatformSend {}
          impl<T> PlatformSend for T {}
      } else {
          pub trait PlatformSend: Send {}
          impl<T> PlatformSend for T where T: Send {}
      }
  }

  pub trait SseClient: Clone + PlatformSend + 'static { ... }
  pub type BoxPlatformSendFuture<T> = Pin<Box<dyn Future<Output = T> + PlatformSend>>;
  1. ThreadToken pattern for WASM:
  • If needed, use thread-local storage for non-Send types that need to cross Send boundaries
  • ^ safe access to JavaScript objects in single-threaded WASM context
  1. Conditional spawn implementations:
  #[cfg(not(target_arch = "wasm32"))]
  fn spawn_worker(fut: impl Future + Send + 'static) { tokio::spawn(fut); }

  #[cfg(target_arch = "wasm32")]
  fn spawn_worker(fut: impl Future + 'static) { wasm_bindgen_futures::spawn_local(fut); }

I haven't dug in depth through rmcp (I've wrote this based on what I've seen from the client-side), but we've recently done similar things in other projects and have some experience in maintaining API compatibility while supporting both native threading and WASM.

Would love to hear your plans (if any) regarding WASM support, and if it's something you'd accept PRs on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions