@aliran/mcp
v0.1.0
Published
Aliran MCP server — exposes the panel admin API, broadcaster control API, reseller/library oversight, an SSH install/maintenance executor (multi-host), guided runbook prompts and the shipped docs as Model Context Protocol tools/resources, so an AI client
Maintainers
Readme
@aliran/mcp — Aliran MCP server
An MCP server that lets an AI client (Claude Desktop, Claude Code, …) install, configure, maintain and support an Aliran deployment — so a non-server-literate operator never needs a terminal.
It exposes Aliran's existing admin surfaces as MCP tools and the shipped docs as MCP resources:
panel_*— the panel admin API (:3210): viewer accounts, grants, channel packages (bouquets), streams, stream art (uploaded from the operator's disk — never base64 through the model), remote sources incl. per-channel exclusion, categories (presentation + catalog-wide rename/merge), publishers, status/observability, aggregate-only analytics, dashboard admins.broadcaster_*— the broadcaster control API (:3310): channels (create/start/stop/rotate), ffmpeg logs, the capability probe, incidents, health, aggregate-only analytics, control admins.reseller_*(optional) — the reseller control API (:3330): the OPERATOR's oversight jobs — principals (enroll/limits/suspend), credit mints (the result echoes the ledger line), ledger audit, accounts/trials views, sweep status. Reseller daily driving (activate/renew) deliberately stays in the resellers' own panel.library_*(optional) — the VOD library control API (:3320): titles list/get/add (one-shot ingest from a path ON the library box), operational patches, re-ingest, ffmpeg logs, delete (the panel record is only marked unavailable — purging it is a panel job).server_*— an SSH executor:preflight,install,update(with adryRunpreview of what would deploy),status,logs,disk,set_env(env knobs, validated in-image viaconfig.js --checkand reverted on failure before anything restarts),restart,backup,list_backups,restore(refuses a non-empty volume withoutforce),sysctl. Multi-host: name extra boxes (repeaters, scale-out broadcasters) inssh.hostsand every tool takeshost:"<name>"—panel_add_publisher {host}writes the minted site key into the RIGHT box'sbroadcaster/.env.repeater_status— SSH-shaped status for a repeater appliance (the repeater has NO admin API by design): compose state, logs, and the opt-in loopback/metricswhen the box enablesSTATUS_PORT— honestly reported when it doesn't.diagnose_*— a/healthzsweep and a symptom → knowledge-base router.docs_search+ themcp://aliran/*resources — the shipped documentation.- 6 MCP prompts — guided runbooks (
new-site-install,onboard-a-reseller,migrate-a-channel-source,monthly-maintenance,incident-triage,expose-dashboards) naming the exact tools per step.
This is the server side of MCP. It exposes tools/resources to an AI client; it does not call the Claude API. Its only runtime dependency is
@modelcontextprotocol/sdk(+zodfor tool schemas). Transport is local stdio.
Security model
The config file is the only place secrets live — the panel/broadcaster admin passwords and the path to the SSH private key. The AI model driving this server sees only tool results, never the config. Two rules follow:
- Secrets move server-side, never through the model.
server_installrunsadmin-cli initon the box and writes the mintedPUBLISHER_KEYstraight into the box'sbroadcaster/.env; only the panel public key is returned. Enrolling a publisher works the same way. - Keep the config
0600. It holds credentials; the server warns on startup if it is group/other-readable.
Configure
cp config.example.json config.json
chmod 600 config.json # it holds credentials
$EDITOR config.json{
"panel": { "url": "https://panel.example.com", "user": "admin", "pass": "…" },
"broadcaster": { "url": "https://broadcaster.example.com", "user": "admin", "pass": "…" },
"reseller": { "user": "root-admin", "pass": "…" }, // optional — no url → tunneled to :3330
"library": { "user": "admin", "pass": "…" }, // optional — no url → tunneled to :3320
"ssh": { "host": "203.0.113.10", "user": "root", "keyPath": "~/.ssh/aliran_deploy", "port": 22 },
"install": { "repoDir": "/opt/aliran", "composeProfiles": [] }
}Reachability. Every service API binds loopback on the box. Give each an explicit
url (a Caddy TLS endpoint), or omit url
and this server opens an SSH local-forward tunnel to its loopback port (:3210
panel / :3310 broadcaster / :3330 reseller / :3320 library) using the same
key — no public dashboard required. user/pass are the dashboard admin logins
(created by add-admin, or by server_install); the reseller login should be the
root admin principal.
Any of panel, broadcaster, reseller, library, ssh may be omitted; only the
tools whose backend is configured are registered.
Multi-host. More than one box? Grow ssh with named hosts —
"hosts": { "edge-1": { "host": "…", "user": "root", "keyPath": "…", "repoDir": "/opt/aliran" } }
— and pass host:"edge-1" on any server_* tool / repeater_status /
panel_add_publisher (omitted = the default box; a single-host config is
unchanged). Per-entry keyPath/port/repoDir are optional; a hosts-only shape
takes "default": "<name>". The doctor probes every named host.
Check your setup (--doctor)
The onboarding self-check: validates the config (and its file mode), probes SSH and
the panel/broadcaster /healthz (add --login to also verify credentials with ONE
real login — the default never spends a login attempt, so a debugging loop cannot
trip the 10-per-15-min lockout), lists the tool groups the AI client will get, and
prints the paste-ready claude_desktop_config.json snippet:
node src/index.js --doctor --config ./config.jsonExit codes: 0 all good · 1 a configured backend failed a probe · 2 the config
is unusable. The full walkthrough (with sample output, Claude Desktop wiring per OS,
first prompts, troubleshooting): docs/mcp-quickstart.md.
Run
node src/index.js --config ./config.json # or set ALIRAN_MCP_CONFIGAny MCP client works — the server is client-agnostic. The mcpServers JSON shape
(Claude Desktop, Cursor, Windsurf, Cline, Gemini CLI):
{
"mcpServers": {
"aliran": {
"command": "node",
"args": ["/path/to/aliran/mcp/src/index.js", "--config", "/path/to/config.json"]
}
}
}Codex CLI takes the same two facts in ~/.codex/config.toml
([mcp_servers.aliran]), VS Code agent mode in .vscode/mcp.json, Claude Code /
Codex via claude mcp add / codex mcp add one-liners — --doctor prints every
snippet with your absolute paths filled in, and
docs/mcp-quickstart.md has the per-client walkthrough.
⚠ Destructive-tool confirmations (destructiveHint) are advisory per the MCP spec —
verify your client prompts before purge/stop/update tools.
Run it from a repo checkout or from the published package — npm pack /
npm publish bundle the docs corpus into docs-bundle/ (the prepack script), and
the server falls back to it exactly when a repo checkout's live docs/ is absent.
Once published: npx @aliran/mcp --config <path>, or
command: "npx", args: ["-y", "@aliran/mcp", "--config", …] in the client config.
Set docsDir in the config to override the docs location either way.
Test
npm run test:mcp (from the repo root) boots an in-process panel + broadcaster, a
REAL reseller service pointed at that panel, and a library control server (fake
TitleManager — call shapes, no transcode), launches this server over a stdio pipe,
and drives it as an MCP client — tools, resources, a write chain,
destructive-annotation presence, docs search, the re-login-on-401 path, the SSH
executor against a command stub (which runs the REAL config.js --check for the
server_set_env validate-then-revert path, and covers the server_restore
refusal), category/source/art curation, the reseller oversight set (the credit
mint asserted against the real ledger), and the library title lifecycle. S49c adds:
a SECOND fake box through the same stub (multi-host routing, per-host repoDir, the
publisher key landing on the named box), repeater_status in all three
status-server states, the list filters + grant-summary compaction, hls bounds +
the feedKey/key redaction, the prompt runbooks with a tool-name drift guard,
server_update {dryRun}, and an npm pack probe that runs the doctor from the
unpacked tarball (docs resolving from docs-bundle/). It is in the required CI
lane (deterministic, no DHT).
