{"_id":"@aliran/mcp","name":"@aliran/mcp","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aliran/mcp","version":"0.1.0","description":"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 ","license":"MIT","type":"module","main":"src/index.js","bin":{"aliran-mcp":"src/index.js"},"repository":{"type":"git","url":"git+https://github.com/AbueloSimpson/aliran.git","directory":"mcp"},"homepage":"https://abuelosimpson.github.io/aliran/mcp/","bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"keywords":["mcp","model-context-protocol","ott","streaming","p2p","aliran","operations"],"scripts":{"start":"node src/index.js","prepack":"node scripts/bundle-docs.mjs"},"publishConfig":{"access":"public"},"dependencies":{"@modelcontextprotocol/sdk":"^1.12.0","zod":"^4.4.3"},"engines":{"node":">=20"},"comment":"The SERVER side of MCP: it exposes Aliran admin operations to an AI client over local stdio. It does NOT call the Claude API, so it depends only on @modelcontextprotocol/sdk — no @anthropic-ai/* packages. Secrets (panel/broadcaster passwords, the SSH key) live in the operator's local config.json (0600) and never reach the model — the model only ever sees tool RESULTS. Runs on the operator's machine, NOT inside the docker-compose stack. prepack bundles the repo docs/ corpus into docs-bundle/ so a published install still serves the mcp://aliran/docs resources (config.js prefers a live repo checkout's docs/ when one is present).","_id":"@aliran/mcp@0.1.0","_nodeVersion":"24.17.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-6UMrw7McoZxzMcvPQoR0uu//0XjYYq3CRKbrqIPCWmtkA2IcPNknOI5UPvOSds7B6rT0BLXdU+ge1FMQ5VbQeA==","shasum":"8cce19133f3163ba2b23e6c3fd91d8d60b9af12b","tarball":"https://registry.npmjs.org/@aliran/mcp/-/mcp-0.1.0.tgz","fileCount":61,"unpackedSize":895047,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCi/HqAAbW0Vr4Hd/mO+b/q8xBQJSJlPLDraDLMkBIksQIgIllCRBWfhXICV7TPbxGHZsYXSwcU5D2evSF/ZlguIsk="}]},"_npmUser":{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"},"directories":{},"maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp_0.1.0_1784961499397_0.461392393149779"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-25T06:38:18.981Z","0.1.0":"2026-07-25T06:38:19.727Z","modified":"2026-07-25T06:38:19.985Z"},"maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"description":"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 ","homepage":"https://abuelosimpson.github.io/aliran/mcp/","keywords":["mcp","model-context-protocol","ott","streaming","p2p","aliran","operations"],"repository":{"type":"git","url":"git+https://github.com/AbueloSimpson/aliran.git","directory":"mcp"},"bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"license":"MIT","readme":"# @aliran/mcp — Aliran MCP server\r\n\r\nAn [MCP](https://modelcontextprotocol.io) **server** that lets an AI client (Claude\r\nDesktop, Claude Code, …) **install, configure, maintain and support** an Aliran\r\ndeployment — so a non-server-literate operator never needs a terminal.\r\n\r\nIt exposes Aliran's existing admin surfaces as MCP **tools** and the shipped docs as\r\nMCP **resources**:\r\n\r\n- **`panel_*`** — the panel admin API (`:3210`): viewer accounts, grants, channel\r\n  packages (bouquets), streams, stream art (uploaded from the operator's disk —\r\n  never base64 through the model), remote sources incl. per-channel exclusion,\r\n  categories (presentation + catalog-wide rename/merge), publishers,\r\n  status/observability, aggregate-only analytics, dashboard admins.\r\n- **`broadcaster_*`** — the broadcaster control API (`:3310`): channels\r\n  (create/start/stop/rotate), ffmpeg logs, the capability probe, incidents, health,\r\n  aggregate-only analytics, control admins.\r\n- **`reseller_*`** *(optional)* — the reseller control API (`:3330`): the OPERATOR's\r\n  oversight jobs — principals (enroll/limits/suspend), credit mints (the result\r\n  echoes the ledger line), ledger audit, accounts/trials views, sweep status.\r\n  Reseller daily driving (activate/renew) deliberately stays in the resellers' own\r\n  panel.\r\n- **`library_*`** *(optional)* — the VOD library control API (`:3320`): titles\r\n  list/get/add (one-shot ingest from a path ON the library box), operational\r\n  patches, re-ingest, ffmpeg logs, delete (the panel record is only marked\r\n  unavailable — purging it is a panel job).\r\n- **`server_*`** — an **SSH executor**: `preflight`, `install`, `update` (with a\r\n  `dryRun` preview of what would deploy), `status`, `logs`, `disk`, `set_env` (env\r\n  knobs, validated in-image via `config.js --check` and **reverted** on failure\r\n  before anything restarts), `restart`, `backup`, `list_backups`, `restore`\r\n  (refuses a non-empty volume without `force`), `sysctl`. **Multi-host:** name\r\n  extra boxes (repeaters, scale-out broadcasters) in `ssh.hosts` and every tool\r\n  takes `host:\"<name>\"` — `panel_add_publisher {host}` writes the minted site key\r\n  into the RIGHT box's `broadcaster/.env`.\r\n- **`repeater_status`** — SSH-shaped status for a repeater appliance (the repeater\r\n  has NO admin API by design): compose state, logs, and the opt-in loopback\r\n  `/metrics` when the box enables `STATUS_PORT` — honestly reported when it\r\n  doesn't.\r\n- **`diagnose_*`** — a `/healthz` sweep and a symptom → knowledge-base router.\r\n- **`docs_search`** + the `mcp://aliran/*` resources — the shipped documentation.\r\n- **6 MCP prompts** — guided runbooks (`new-site-install`, `onboard-a-reseller`,\r\n  `migrate-a-channel-source`, `monthly-maintenance`, `incident-triage`,\r\n  `expose-dashboards`) naming the exact tools per step.\r\n\r\n> This is the **server** side of MCP. It exposes tools/resources to an AI client; it\r\n> does **not** call the Claude API. Its only runtime dependency is\r\n> `@modelcontextprotocol/sdk` (+ `zod` for tool schemas). Transport is local **stdio**.\r\n\r\n## Security model\r\n\r\nThe **config file is the only place secrets live** — the panel/broadcaster admin\r\npasswords and the path to the SSH private key. The AI model driving this server sees\r\nonly tool **results**, never the config. Two rules follow:\r\n\r\n- **Secrets move server-side, never through the model.** `server_install` runs\r\n  `admin-cli init` on the box and writes the minted `PUBLISHER_KEY` straight into the\r\n  box's `broadcaster/.env`; only the panel **public** key is returned. Enrolling a\r\n  publisher works the same way.\r\n- **Keep the config `0600`.** It holds credentials; the server warns on startup if it\r\n  is group/other-readable.\r\n\r\n## Configure\r\n\r\n```bash\r\ncp config.example.json config.json\r\nchmod 600 config.json          # it holds credentials\r\n$EDITOR config.json\r\n```\r\n\r\n```jsonc\r\n{\r\n  \"panel\":       { \"url\": \"https://panel.example.com\", \"user\": \"admin\", \"pass\": \"…\" },\r\n  \"broadcaster\": { \"url\": \"https://broadcaster.example.com\", \"user\": \"admin\", \"pass\": \"…\" },\r\n  \"reseller\":    { \"user\": \"root-admin\", \"pass\": \"…\" },   // optional — no url → tunneled to :3330\r\n  \"library\":     { \"user\": \"admin\", \"pass\": \"…\" },        // optional — no url → tunneled to :3320\r\n  \"ssh\":         { \"host\": \"203.0.113.10\", \"user\": \"root\", \"keyPath\": \"~/.ssh/aliran_deploy\", \"port\": 22 },\r\n  \"install\":     { \"repoDir\": \"/opt/aliran\", \"composeProfiles\": [] }\r\n}\r\n```\r\n\r\n**Reachability.** Every service API binds loopback on the box. Give each an explicit\r\n`url` (a [Caddy TLS endpoint](../docs/kb/public-dashboards.md)), **or** omit `url`\r\nand this server opens an **SSH local-forward tunnel** to its loopback port (`:3210`\r\npanel / `:3310` broadcaster / `:3330` reseller / `:3320` library) using the same\r\nkey — no public dashboard required. `user`/`pass` are the **dashboard admin** logins\r\n(created by `add-admin`, or by `server_install`); the reseller login should be the\r\n**root admin principal**.\r\n\r\nAny of `panel`, `broadcaster`, `reseller`, `library`, `ssh` may be omitted; only the\r\ntools whose backend is configured are registered.\r\n\r\n**Multi-host.** More than one box? Grow `ssh` with named hosts —\r\n`\"hosts\": { \"edge-1\": { \"host\": \"…\", \"user\": \"root\", \"keyPath\": \"…\", \"repoDir\": \"/opt/aliran\" } }`\r\n— and pass `host:\"edge-1\"` on any `server_*` tool / `repeater_status` /\r\n`panel_add_publisher` (omitted = the default box; a single-host config is\r\nunchanged). Per-entry `keyPath`/`port`/`repoDir` are optional; a hosts-only shape\r\ntakes `\"default\": \"<name>\"`. The doctor probes every named host.\r\n\r\n## Check your setup (`--doctor`)\r\n\r\nThe onboarding self-check: validates the config (and its file mode), probes SSH and\r\nthe panel/broadcaster `/healthz` (add `--login` to also verify credentials with ONE\r\nreal login — the default never spends a login attempt, so a debugging loop cannot\r\ntrip the 10-per-15-min lockout), lists the tool groups the AI client will get, and\r\nprints the paste-ready `claude_desktop_config.json` snippet:\r\n\r\n```bash\r\nnode src/index.js --doctor --config ./config.json\r\n```\r\n\r\nExit codes: `0` all good · `1` a configured backend failed a probe · `2` the config\r\nis unusable. The full walkthrough (with sample output, Claude Desktop wiring per OS,\r\nfirst prompts, troubleshooting): [docs/mcp-quickstart.md](../docs/mcp-quickstart.md).\r\n\r\n## Run\r\n\r\n```bash\r\nnode src/index.js --config ./config.json      # or set ALIRAN_MCP_CONFIG\r\n```\r\n\r\n**Any MCP client works** — the server is client-agnostic. The `mcpServers` JSON shape\r\n(Claude Desktop, Cursor, Windsurf, Cline, Gemini CLI):\r\n\r\n```jsonc\r\n{\r\n  \"mcpServers\": {\r\n    \"aliran\": {\r\n      \"command\": \"node\",\r\n      \"args\": [\"/path/to/aliran/mcp/src/index.js\", \"--config\", \"/path/to/config.json\"]\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nCodex CLI takes the same two facts in `~/.codex/config.toml`\r\n(`[mcp_servers.aliran]`), VS Code agent mode in `.vscode/mcp.json`, Claude Code /\r\nCodex via `claude mcp add` / `codex mcp add` one-liners — `--doctor` prints every\r\nsnippet with your absolute paths filled in, and\r\n[docs/mcp-quickstart.md](../docs/mcp-quickstart.md) has the per-client walkthrough.\r\n⚠ Destructive-tool confirmations (`destructiveHint`) are advisory per the MCP spec —\r\nverify your client prompts before purge/stop/update tools.\r\n\r\nRun it from a **repo checkout** or from the **published package** — `npm pack` /\r\n`npm publish` bundle the docs corpus into `docs-bundle/` (the `prepack` script), and\r\nthe server falls back to it exactly when a repo checkout's live `docs/` is absent.\r\nOnce published: `npx @aliran/mcp --config <path>`, or\r\n`command: \"npx\", args: [\"-y\", \"@aliran/mcp\", \"--config\", …]` in the client config.\r\nSet `docsDir` in the config to override the docs location either way.\r\n\r\n## Test\r\n\r\n`npm run test:mcp` (from the repo root) boots an in-process panel + broadcaster, a\r\nREAL reseller service pointed at that panel, and a library control server (fake\r\nTitleManager — call shapes, no transcode), launches this server over a stdio pipe,\r\nand drives it as an MCP client — tools, resources, a write chain,\r\ndestructive-annotation presence, docs search, the re-login-on-401 path, the SSH\r\nexecutor against a command stub (which runs the REAL `config.js --check` for the\r\n`server_set_env` validate-then-revert path, and covers the `server_restore`\r\nrefusal), category/source/art curation, the reseller oversight set (the credit\r\nmint asserted against the real ledger), and the library title lifecycle. S49c adds:\r\na SECOND fake box through the same stub (multi-host routing, per-host repoDir, the\r\npublisher key landing on the named box), `repeater_status` in all three\r\nstatus-server states, the list filters + grant-summary compaction, hls bounds +\r\nthe feedKey/`key` redaction, the prompt runbooks with a tool-name drift guard,\r\n`server_update {dryRun}`, and an `npm pack` probe that runs the doctor from the\r\nunpacked tarball (docs resolving from `docs-bundle/`). It is in the required CI\r\nlane (deterministic, no DHT).\r\n","readmeFilename":"README.md","_rev":"1-2d98dcf4da960541e76e29ad6c151df4"}