mcp-beam is a MCP server (stdio transport) for casting local files and media URLs to Chromecast and DLNA/UPnP devices on your LAN.
It exposes four tools:
list_local_hardwarebeam_mediaseek_beamingstop_beaming
- One server for both Chromecast and DLNA/UPnP workflows.
- Stable device IDs for reliable follow-up calls.
- Protocol-aware direct play and transcoding decisions.
- Safe-by-default path, URL, and bind policies.
- Structured errors with practical remediation hints.
- Demo
- Quick Start
- Installation
- Runtime Dependencies
- Tool Reference
- Transcode Behavior
- Error Model
- Environment Variables
- Security
- Architecture
- Troubleshooting
- Development
Get beaming in a few minutes.
CLI one-liners:
# Claude Code
claude mcp add --scope user mcp-beam -- go run go2tv.app/mcp-beam@latest
# Codex
codex mcp add mcp-beam -- go run go2tv.app/mcp-beam@latest
# Gemini
gemini mcp add mcp-beam go run go2tv.app/mcp-beam@latestGeneric JSON configuration (for MCP hosts using mcpServers):
{
"mcpServers": {
"mcp-beam": {
"command": "go",
"args": [
"run",
"go2tv.app/mcp-beam@latest"
]
}
}
}Notes:
- Requires
goinPATH(Go1.25+). - The first run may be slower due to module download/build.
go run go2tv.app/mcp-beam@latest --version
go run go2tv.app/mcp-beam@latest --self-test- Call
list_local_hardwareand pick a deviceid. - Call
beam_mediawithsourceandtarget_device. - Call
seek_beamingas needed. - Call
stop_beamingwhen done.
Minimal example flow:
{
"name": "list_local_hardware",
"arguments": {
"timeout_ms": 3000,
"include_unreachable": false
}
}{
"name": "beam_media",
"arguments": {
"source": "/absolute/path/to/video.mp4",
"target_device": "dev_1234abcd",
"transcode": "auto"
}
}{
"name": "seek_beaming",
"arguments": {
"session_id": "sess_abcd1234",
"position_percent": 50
}
}{
"name": "stop_beaming",
"arguments": {
"session_id": "sess_abcd1234"
}
}go run go2tv.app/mcp-beam@latest --version
go run go2tv.app/mcp-beam@latest --self-testFrom repo root:
go run . --version
go run . --self-testUse this MCP config to run directly from source:
macOS/Linux:
{
"mcpServers": {
"mcp-beam": {
"command": "/bin/bash",
"args": [
"-lc",
"cd /absolute/path/to/mcp-beam && go run ."
]
}
}
}Windows PowerShell:
{
"mcpServers": {
"mcp-beam": {
"command": "powershell",
"args": [
"-NoProfile",
"-Command",
"Set-Location 'C:\\absolute\\path\\to\\mcp-beam'; go run ."
]
}
}
}Build locally:
go build -o ./bin/mcp-beam .
./bin/mcp-beam --version
./bin/mcp-beam --self-testOr install from releases:
https://github.com/alex/mcp-beam/releases
Linux/macOS checksum verify:
shasum -a 256 -c SHA256SUMSWindows checksum verify:
Get-FileHash .\mcp-beam_<version>_windows_amd64.zip -Algorithm SHA256Linux/macOS unpack:
tar -xzf mcp-beam_<version>_<os>_<arch>.tar.gz
./mcp-beam_<version>_<os>_<arch>/mcp-beam --version
./mcp-beam_<version>_<os>_<arch>/mcp-beam --self-testWindows unpack:
Expand-Archive .\mcp-beam_<version>_windows_amd64.zip -DestinationPath .
.\mcp-beam_<version>_windows_amd64\mcp-beam.exe --version
.\mcp-beam_<version>_windows_amd64\mcp-beam.exe --self-testMCP config for a local binary:
{
"mcpServers": {
"mcp-beam": {
"command": "/absolute/path/to/mcp-beam",
"args": []
}
}
}go(Go 1.25+) is required to run the server. See Go Installation below.ffmpegandffprobeare optional for non-transcoding paths, but recommended.- If transcoding is required and
ffmpegis unavailable, calls returnFFMPEG_NOT_FOUND.
- If transcoding is required and
Download and install from https://go.dev/dl/ or via package manager:
- Debian/Ubuntu:
sudo apt install golang-go - Fedora:
sudo dnf install golang - Arch:
sudo pacman -S go
Download and install from https://go.dev/dl/ or use Homebrew:
brew install go@1.25Download and install from https://go.dev/dl/
go versionShould output: go1.25.0 or higher.
Install examples:
- Linux: package manager (for example
sudo apt install ffmpeg) - macOS:
brew install ffmpeg - Windows: install FFmpeg and add
bintoPATH
Verify:
- Linux/macOS:
command -v ffmpeg && command -v ffprobe - Windows:
where ffmpegandwhere ffprobe
Discover Chromecast and DLNA/UPnP renderers on the local network.
Arguments:
timeout_ms(optional integer, minimum100, default5000)include_unreachable(optional boolean, defaultfalse)
Example:
{
"name": "list_local_hardware",
"arguments": {
"timeout_ms": 5000,
"include_unreachable": false
}
}On success, structuredContent includes:
countdevices[]entries:idnametypeaddressis_audio_onlyprotocol(chromecastordlna)capabilities.supports_file_sourcecapabilities.supports_url_sourcecapabilities.supports_hls_m3u8_urlcapabilities.limitations[]
Start playback on a selected discovered device.
Arguments:
source(required string): absolute local file path, orhttp/httpsURLtarget_device(required string): stable device ID preferred, exact name fallbacktranscode(optional string):auto(default),always,neversubtitles_path(optional string): absolute local subtitle file path (.srtor.vtt)start_seconds(optional integer, minimum0): start offset from the beginning of media
Example:
{
"name": "beam_media",
"arguments": {
"source": "/absolute/path/to/media.mp4",
"target_device": "dev_1234abcd",
"transcode": "auto",
"subtitles_path": "/absolute/path/to/subs.srt",
"start_seconds": 60
}
}On success, structuredContent includes:
oksession_iddevice_idmedia_urltranscodingwarnings[]
Protocol notes:
- Chromecast supports local files and URL sources.
- Chromecast supports direct
.m3u8HLS URL casting. - DLNA supports local files and URL sources with direct-first then proxy fallback behavior.
- DLNA
.m3u8URLs are rejected with structured limitation details. - When
subtitles_pathis omitted for local files, mcp-beam auto-detects sidecar subtitles using the same basename (.srt, then.vtt).
Stop an active beam session.
Arguments:
target_device(optional string)session_id(optional string)- At least one of
target_deviceorsession_idis required.
Example:
{
"name": "stop_beaming",
"arguments": {
"session_id": "sess_abcd1234"
}
}On success, structuredContent includes:
okstopped_session_iddevice_id
Seek an active beam session by absolute position, percentage, from-end offset, or relative delta.
Arguments:
target_device(optional string)session_id(optional string)- Exactly one of:
position_seconds(integer, minimum0)position_percent(number, range0to100)from_end_seconds(integer, minimum0)delta_seconds(integer): relative delta from the current playback position; negative values rewind- At least one of
target_deviceorsession_idis required.
Example:
{
"name": "seek_beaming",
"arguments": {
"session_id": "sess_abcd1234",
"from_end_seconds": 10
}
}On success, structuredContent includes:
oksession_iddevice_idposition_secondsrequested_moderesolved_position_seconds- optional
duration_seconds
Examples:
- Middle of media:
position_percent: 50 - Ten seconds from end:
from_end_seconds: 10 - Exact second:
position_seconds: 120 - Skip ahead 30 seconds:
delta_seconds: 30 - Rewind 10 seconds:
delta_seconds: -10
Note:
- Relative modes (
position_percent,from_end_seconds) require known media duration.
beam_media.arguments.transcode values:
auto(default)alwaysnever
Behavior summary:
never: do not transcode.always: force transcoding for video sources; ignored for non-video sources.auto: protocol-aware default behavior.- Chromecast local files: transcode only when codec compatibility requires it.
- Chromecast URL sources: direct stream by default.
- DLNA local files: transcode only with
alwaysfor video sources. - DLNA URL sources: direct-first then proxy fallback; transcode forced only with
alwaysfor video.
Edge cases:
- Invalid
transcodevalues return JSON-RPC-32602(invalid params). transcode=alwayswith direct Chromecast HLS (.m3u8) URLs is rejected.- If transcoding is required/requested and
ffmpegis unavailable, the call returnsFFMPEG_NOT_FOUND. - Results include
structuredContent.transcodingandwarnings[]so callers can verify what ran.
Input validation failures:
- JSON-RPC error
-32602(invalid params)
Tool failures:
isError=truestructuredContent.errorincludes:codemessage- optional
limitations[] - optional
suggested_fixes[] - optional
details
Common tool error codes:
DEVICE_NOT_FOUNDDEVICE_UNREACHABLEFILE_NOT_FOUNDFILE_NOT_READABLEUNSUPPORTED_MEDIAUNSUPPORTED_SOURCE_FOR_PROTOCOLUNSUPPORTED_URL_PATTERNTRANSCODE_REQUIREDFFMPEG_NOT_FOUNDPROTOCOL_ERRORINTERNAL_ERROR
| Variable | Default | Effect |
|---|---|---|
MCP_BEAM_STRICT_PATH_POLICY |
false |
Enables strict file/subtitle path allowlist enforcement. |
MCP_BEAM_ALLOWED_PATH_PREFIXES |
empty | Comma-separated absolute prefixes allowed in strict mode. |
MCP_BEAM_ALLOW_LOOPBACK_URLS |
false |
Allows localhost/loopback URL hosts when true. |
MCP_BEAM_ALLOW_WILDCARD_BIND |
false |
Allows wildcard bind addresses when true. |
MCP_BEAM_LOG_LEVEL |
info |
Server log level: debug, info, warn, error. |
Security controls:
- Local file paths must be absolute.
- Strict path mode enforces allowlisted prefixes and rejects path escapes.
- Only
httpandhttpsURLs are accepted. - Loopback hosts (
localhost,127.0.0.0/8,::1) are blocked by default. - Wildcard bind addresses (
0.0.0.0,::) are blocked by default. - Temporary media routes use random, unguessable tokens.
- Session ownership is process-local and in-memory.
Recommended production baseline:
- Keep
MCP_BEAM_ALLOW_LOOPBACK_URLS=falseunless explicitly needed for local-only testing. - Keep
MCP_BEAM_ALLOW_WILDCARD_BIND=false. - Enable
MCP_BEAM_STRICT_PATH_POLICY=truewith explicitMCP_BEAM_ALLOWED_PATH_PREFIXES. - Run
mcp-beamunder a least-privilege OS account.
Threat boundaries:
- MCP client input is untrusted and validated strictly.
- Source URL hosts are external trust boundaries.
- Media listeners are LAN-visible and should run only on trusted networks.
- Device control endpoints (Chromecast/DLNA) depend on LAN integrity.
MCP Host (MCP client)
|
| stdio JSON-RPC (MCP)
v
mcp-beam (single process)
- internal/mcpserver (initialize, tools/list, tools/call)
- internal/discovery (unified DLNA + Chromecast discovery)
- internal/beam (session manager + lifecycle + cleanup)
|
+--> go2tv castprotocol (Chromecast control)
+--> go2tv soapcalls (DLNA control)
+--> go2tv httphandlers (temporary HTTP media serving)
+--> go2tv utils (MIME/transcode/url helpers)
Runtime model:
- Single headless binary.
- MCP over
stdin/stdoutonly. - In-process session manager is the source of truth.
- One active session per target device.
Core flow:
list_local_hardware: discover, normalize, stable IDs, optional reachability filter.beam_media: validate source, resolve target, choose protocol, decide transcode, start playback, persist session.seek_beaming: seek active sessions bysession_idortarget_device.stop_beaming: resolve session/device, stop protocol playback, tear down runtime resources.
Session lifecycle defaults:
idle_cleanup_after = 10mpaused_cleanup_after = 90mmax_session_age = 24h- sweep interval
5s
State sources:
- Chromecast via status polling (
GetStatus) - DLNA hybrid monitoring (callbacks + polling fallback)
Quick diagnostics:
mcp-beam --version
mcp-beam --self-testVerbose logs:
MCP_BEAM_LOG_LEVEL=debug mcp-beamCommon issues:
FFMPEG_NOT_FOUND: installffmpeg/ffprobe, then verifyPATH.DEVICE_NOT_FOUND: runlist_local_hardwareand reuse returnedid.DEVICE_UNREACHABLE: verify the target is powered on and reachable.UNSUPPORTED_URL_PATTERN: source must be routablehttp/https; for local loopback testing only, setMCP_BEAM_ALLOW_LOOPBACK_URLS=true.UNSUPPORTED_SOURCE_FOR_PROTOCOL: target Chromecast for.m3u8.PROTOCOL_ERRORwith bind policy: use a concrete LAN bind address; setMCP_BEAM_ALLOW_WILDCARD_BIND=trueonly in controlled environments.invalid params: remove unknown fields and match exact argument names/types.
Discovery issues:
- If no devices are returned, increase
timeout_ms. - Set
include_unreachable=truefor debugging. - Verify firewall/network discovery access.
Startup issues:
- Verify command path and executable permissions.
- On Windows, use full path to
mcp-beam.exe. - In debug logs, check for
mcp_server_start,mcp_read_wait,mcp_message_received.
Common commands:
make test
make lint
make release
make clean
