@ondeinference / ed / commits / 8e12a74

ed-mcp: skip the child-process transport on tvOS

tvOS has no exec, so rmcp's transport-child-process feature pulls in nix, which does not compile there. Move that feature into a cfg(not(target_os = "tvos")) dependency and gate the stdio transport arm the same way. On tvOS a stdio MCP server now fails with a plain message instead of the crate refusing to build. cargo +nightly rustc -Z build-std -p ed-mcp --target aarch64-apple-tvos now compiles; the HTTP transport and the rest of the workspace are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: siGit Code v1.6.9 <noreply@sigit.si>

Seto Elkahfi committed Oct 14, 2026 at 22:20 UTC 8e12a74e7029ecde0bdb8f74dde455a499d10fbf
2 files changed +19 -2
crates/ed-mcp/Cargo.toml
+7 -1
@@ -16,7 +16,7 @@ agent-client-protocol = { workspace = true, optional = true }
16 ed-agent = { workspace = true, optional = true }
17 async-trait = { workspace = true, optional = true }
18 http = "1"
19 -rmcp = { workspace = true, features = ["client", "transport-child-process", "transport-streamable-http-client-reqwest", "reqwest"] }
19 +rmcp = { workspace = true, features = ["client", "transport-streamable-http-client-reqwest", "reqwest"] }
20 tokio = { workspace = true, features = ["sync", "time", "process", "macros", "io-std", "io-util", "rt"] }
21 tokio-util.workspace = true
22 futures.workspace = true
@@ -24,6 +24,12 @@ serde_json.workspace = true
24 anyhow.workspace = true
25 tracing.workspace = true
26
27 +# Spawning an MCP server as a child process needs `exec`, which Apple hides on tvOS (`libc`
28 +# has no `execv` there, so `nix` under `process-wrap` doesn't compile). The stdio transport
29 +# is everywhere else.
30 +[target.'cfg(not(target_os = "tvos"))'.dependencies]
31 +rmcp = { workspace = true, features = ["transport-child-process"] }
32 +
33 [features]
34 # Map ACP `session/new` MCP server lists (`McpToolset::connect`, `ServerSpec::from_acp`).
35 # Pulls in `agent-client-protocol`; `ed-acp` enables it.
crates/ed-mcp/src/lib.rs
+12 -1
@@ -34,8 +34,10 @@ use rmcp::model::{
34 Implementation, ProgressNotificationParam, Request, ServerResult, Tool,
35 };
36 use rmcp::service::{NotificationContext, PeerRequestOptions, RequestContext, RunningService};
37 +use rmcp::transport::StreamableHttpClientTransport;
38 +#[cfg(not(target_os = "tvos"))]
39 +use rmcp::transport::TokioChildProcess;
40 use rmcp::transport::streamable_http_client::StreamableHttpClientTransportConfig;
38 -use rmcp::transport::{StreamableHttpClientTransport, TokioChildProcess};
41 use rmcp::{ClientHandler, ErrorData as McpError, RoleClient, ServiceExt};
42 use serde_json::{Map, Value, json};
43 use tokio::sync::Mutex;
@@ -332,6 +334,7 @@ impl Connection {
334 elicitation,
335 };
336 let handshake = match &spec.transport {
337 + #[cfg(not(target_os = "tvos"))]
338 Transport::Stdio { command, args, env } => {
339 let mut cmd = tokio::process::Command::new(command);
340 cmd.args(args).envs(env.iter().map(|(k, v)| (k, v)));
@@ -339,6 +342,14 @@ impl Connection {
342 .with_context(|| format!("starting {}", command.display()))?;
343 tokio::time::timeout(timeout, handler.serve(transport)).await
344 }
345 + // tvOS can't start a process, so there is no stdio transport to offer.
346 + #[cfg(target_os = "tvos")]
347 + Transport::Stdio { command, .. } => {
348 + bail!(
349 + "can't start {}: this platform can't run MCP servers as processes",
350 + command.display()
351 + );
352 + }
353 Transport::Http {
354 url,
355 bearer,