Skip to main content
Glama
README.md
# owncast

MCP connector for a self-hosted [Owncast](https://github.com/owncast/owncast) 0.3.0 server. It speaks the HTTP API over stdio and over stateless Streamable HTTP on localhost.

Owncast is MIT software. This connector does not copy that code. It only calls the HTTP API on the server you configure. It is not affiliated with Owncast.

`owncast.routes.json` maps every operation in Owncast 0.3.0 `openapi.yaml`. `scripts/gen-operations.mjs` writes `src/operations.ts` from that table. Do not edit `src/operations.ts` by hand. The spec lists 239 operations. 117 are tools. 122 are excluded (CORS OPTIONS, browser IndieAuth and Fediverse OTP, player metrics, Prometheus proxy, multipart uploads, and the public-page remote follow form).

`OWNCAST_URL` is required. There is no default origin.

## Setup

```bash
npm install
npm run ci
npm start
npm run start:http
```

`npm start` speaks stdio. `npm run start:http` serves Streamable HTTP at `http://127.0.0.1:3000/mcp` and `GET /health`. HTTP mode has no authentication of its own. Keep it on localhost.

```json
{
  "mcpServers": {
    "owncast": {
      "command": "node",
      "args": ["/workspace/connectors/owncast/dist/index.js"],
      "env": {
        "OWNCAST_URL": "http://127.0.0.1:8095",
        "OWNCAST_ADMIN_PASSWORD": "...",
        "OWNCAST_ACCESS_TOKEN": "...",
        "OWNCAST_ALLOW_WRITES": "false",
        "OWNCAST_TOOLSETS": "status,chat,config"
      }
    }
  }
}
```

You can set `OWNCAST_ADMIN_PASSWORD`, `OWNCAST_ACCESS_TOKEN`, or both. Public routes need neither. Admin routes need the password. Integration routes need the access token.

Docker (HTTP, no authentication, keep it on localhost):

```bash
docker build -t owncast-mcp .
docker run -p 127.0.0.1:3000:3000 \
  -e OWNCAST_URL=http://127.0.0.1:8080 \
  -e OWNCAST_ADMIN_PASSWORD=... \
  owncast-mcp
```

Do not put passwords or tokens in the image.

Regenerate the registry after an OpenAPI bump.

```bash
npm run gen:operations
```

`scripts/live-check.mjs` talks to a real server over stdio. It needs `OWNCAST_URL` and `OWNCAST_ADMIN_PASSWORD` (or `/workspace/tools/owncast/admin.pw`). It sets `OWNCAST_TOOLSETS=all` and unsets `DATABASE_URL`. Gated writes run only when `OWNCAST_ALLOW_WRITES=true`. It prints pass or fail lines only.

## Config

| Variable                 | Meaning                                                                                            |
| ------------------------ | -------------------------------------------------------------------------------------------------- |
| `OWNCAST_URL`            | Required. Origin, `http` or `https`, no userinfo, query, or hash. Example `http://127.0.0.1:8080`. |
| `OWNCAST_ADMIN_PASSWORD` | Admin password. Sent as HTTP basic auth with user `admin` on `/api/admin/*`. Redacted everywhere.  |
| `OWNCAST_ACCESS_TOKEN`   | Integration access token. Sent as `Authorization: Bearer` on `/api/integrations/*`.                |
| `OWNCAST_ALLOW_WRITES`   | Must be the literal `true` before a confirmed write is sent.                                       |
| `OWNCAST_TOOLSETS`       | Comma list of toolsets, or `all`. Default is the read-focused set below.                           |
| `OWNCAST_PER_MINUTE`     | Local request budget. Default 30. Cache hits do not spend it.                                      |
| `OWNCAST_TIMEOUT_MS`     | Upstream timeout. Default 20000.                                                                   |
| `OWNCAST_CACHE_TTL_SEC`  | Read cache TTL. Default 60. Writes clear the cache.                                                |
| `OWNCAST_CONTACT`        | Contact token in the User-Agent. Must not contain a secret.                                        |
| `MCP_TRANSPORT`          | `stdio` or `http`. `--http` overrides.                                                             |
| `PORT`                   | HTTP port. Default 3000.                                                                           |

Invalid values are reported by field name and issue code. The rejected value is not included. Passwords, access tokens, stream keys, webhook secrets, and authorization headers are redacted in logs, errors, tool results, thrown messages, and URLs.

Auth is chosen per route. Public paths send no credentials. `/api/admin` sends `Authorization: Basic` for user `admin`. `/api/integrations` sends `Authorization: Bearer`. The connector never puts credentials in the API URL.

## Safety

GET is a read. POST, PUT, PATCH, and DELETE are writes.

A write needs `confirm: true` as a boolean and `OWNCAST_ALLOW_WRITES=true`. The string `"true"` is rejected. Otherwise the tool returns a dry-run preview of method, path, query, and redacted body, and it sends nothing. Destructive actions add a warning on the tool and on the preview. That set covers disconnecting the inbound stream, changing stream keys or the admin password, banning or disabling users, and deleting webhooks or access tokens.

Access token create and webhook create return the secret once, with a shown-only-once note. Later list calls redact it. Admin config reads redact `streamKeys`, `key`, `adminPassword`, and webhook `secret` fields. Error text drops stack traces and filesystem paths.

HTTP 401 is unauthorized. HTTP 403 is forbidden. HTTP 404 is not found. HTTP 400 and 422 are invalid input. HTTP 429 includes Retry-After. Other failures are server, network, or timeout. Response bodies are capped at 1,000,000 bytes.

## Tools

Default `OWNCAST_TOOLSETS` publishes 3 tools. `OWNCAST_TOOLSETS=all` publishes 7 tools (117 operations, 39 reads and 78 writes). Each tool takes `action` plus optional `params`, `query`, `body`, and `confirm`.

`action` `help` with optional `target` returns the path, query, and body fields for one action. Descriptions list action names only, so the tool list stays small.

| Toolset      | Tool                   | Operations | Default |
| ------------ | ---------------------- | ---------- | ------- |
| status       | `owncast_status`       | 10         | yes     |
| chat         | `owncast_chat`         | 11         | yes     |
| config       | `owncast_config`       | 17         | yes     |
| moderation   | `owncast_moderation`   | 13         | no      |
| config_write | `owncast_config_write` | 55         | no      |
| webhooks     | `owncast_webhooks`     | 6          | no      |
| ops          | `owncast_ops`          | 5          | no      |

`owncast_status` covers public info (`/status`, `/config`, emoji, YP, ping, followers) plus integration status. `owncast_chat` reads chat and sends integration, system, and action messages. `owncast_config` is admin reads, including server config with stream keys redacted. `owncast_moderation` hides or shows messages, bans IPs, and manages moderators. `owncast_webhooks` covers webhooks and access tokens. `owncast_ops` disconnects the inbound stream and related server actions.

## What this is not

This is not the Owncast web UI, the admin dashboard, or an RTMP ingest. It does not start Owncast. It does not upload logos, favicons, or custom emoji files. It does not proxy Prometheus. It does not complete IndieAuth or Fediverse OTP in a browser.

## Compared with other Owncast MCP servers

A GitHub search for `owncast mcp` and `owncast mcp-server` on 3 Nov 2026 found no Owncast MCP server.

Nearest alternatives that talk to Owncast, without MCP:

- [SleepyLili/tlapbot](https://github.com/SleepyLili/tlapbot) is a Python Flask bot for Owncast channel points and redeems.
- [glyphscribe/owncastparser](https://github.com/glyphscribe/owncastparser) is a Home Assistant integration for Owncast status.
- [owncast/plugin-sdk](https://github.com/owncast/plugin-sdk) is the official plugin SDK.

This connector adds confirm-gated writes with dry-run previews, per-route public/admin/integration auth, stream-key redaction, toolset scoping, vitest coverage, and a live check against Owncast 0.3.0.

## License

MIT. Owncast itself is also MIT. See [owncast/owncast](https://github.com/owncast/owncast/blob/develop/LICENSE).