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
b96380b331e822598888c9ec4b411d188a1c1e2a
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,