# `ExMCP.Transport.Stdio`
[🔗](https://github.com/azmaveth/ex_mcp/blob/v1.0.0/lib/ex_mcp/transport/stdio.ex#L1)

This module implements the standard MCP specification.

stdio transport implementation for MCP.

This transport communicates with MCP servers over standard input/output,
typically by spawning a subprocess. This is one of the two official MCP
transports defined in the specification.

## Options

- `:command` - Command and arguments to spawn (required)
- `:cd` - Working directory for the process
- `:env` - Environment variables as a list of `{"KEY", "VALUE"}` tuples;
  use `{"KEY", false}` to remove an inherited variable from the child
- `:environment_policy` - `:isolated` (default) passes only a small runtime
  allowlist plus explicit `:env`; `:inherit` preserves the parent environment
  for explicitly trusted deployments
- `:max_frame_bytes` - maximum inbound or outbound JSON-RPC frame size
  (default: 1 MiB)

## Example

    {:ok, client} = ExMCP.Client.start_link(
      transport: :stdio,
      command: ["node", "my-mcp-server.js"],
      cd: "/path/to/server",
      env: [{"NODE_ENV", "production"}]
    )

# `receive_message`

```elixir
@spec receive_message(
  %ExMCP.Transport.Stdio{
    line_buffer: term(),
    max_frame_bytes: term(),
    os_pid: term(),
    port: term(),
    reader_pid: term(),
    subscriber: term()
  },
  timeout()
) ::
  {:ok, binary(),
   %ExMCP.Transport.Stdio{
     line_buffer: term(),
     max_frame_bytes: term(),
     os_pid: term(),
     port: term(),
     reader_pid: term(),
     subscriber: term()
   }}
  | {:error, any()}
```

Receives a single message, waiting at most `timeout` milliseconds.

Callers must run this in the process that owns the port (or in one that may
take ownership): port ownership is transferred to the caller, and an OTP port
is closed when its owner exits. Running it in a short-lived helper process
would therefore kill the spawned program — which is why the handshake path
uses this timeout-aware clause in-process instead of wrapping
`receive_message/1` in a task.

# `subscribe`

Subscribe to receive transport events (push model).

Spawns an internal reader process that takes over port ownership,
reads and parses JSON messages, and pushes them to the subscriber.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
