Packages

fnord

0.9.38

AI code archaeology

Current section

Files

Jump to
fnord lib mcp stdio_wrapper.ex
Raw

lib/mcp/stdio_wrapper.ex

defmodule MCP.STDIOWrapper do
@moduledoc """
Creates a small executable wrapper for stdio MCP servers.
Hermes' stdio transport reads JSON-RPC from the child's stdout. Many MCP servers
also write human-readable logs to stderr.
This module generates a temporary wrapper script that:
- Executes the real server command (passed as the wrapper's first argument)
- Captures the server's stderr to a temp file
- Replays captured stderr only when the server exits non-zero
This keeps successful runs quiet while preserving debugging output for failures.
"""
@type path :: binary()
@spec script_path() :: {:ok, path()} | {:error, term()}
def script_path do
with {:ok, path} <- Services.TempFile.mktemp(prefix: "fnord-mcp-stdio-", suffix: "-wrapper"),
:ok <- File.write(path, script_body()),
:ok <- File.chmod(path, 0o700) do
{:ok, path}
end
end
@spec script_path!() :: path()
def script_path! do
case script_path() do
{:ok, path} -> path
{:error, reason} -> raise "Failed to create MCP stdio wrapper: #{inspect(reason)}"
end
end
defp script_body do
"""
#!/usr/bin/env bash
set -eu -o pipefail
if [[ "$#" -lt 1 ]]; then
echo "fnord mcp stdio wrapper: missing command" >&2
exit 127
fi
cmd="$1"
shift
stderr_file=""
cleanup() {
if [[ -n "${stderr_file}" && -e "${stderr_file}" ]]; then
rm -f "${stderr_file}" || true
fi
}
trap cleanup EXIT
stderr_file="$(mktemp -t fnord-mcp-stderr.XXXXXX)"
set +e
"${cmd}" "$@" 2>"${stderr_file}"
status=$?
set -e
if [[ ${status} -ne 0 ]]; then
cat "${stderr_file}" >&2 || true
fi
exit ${status}
"""
end
end